pax_global_header00006660000000000000000000000064127716123030014514gustar00rootroot0000000000000052 comment=486dcca7a5366e174fdc29de070aeac981713ec7 cson-parser-1.3.4/000077500000000000000000000000001277161230300137555ustar00rootroot00000000000000cson-parser-1.3.4/.editorconfig000066400000000000000000000002531277161230300164320ustar00rootroot00000000000000root = true [*] indent_style = space indent_size = 2 charset = utf-8 trim_trailing_whitespace = true insert_final_newline = true [*.md] trim_trailing_whitespace = false cson-parser-1.3.4/.gitignore000066400000000000000000000000411277161230300157400ustar00rootroot00000000000000node_modules/ npm-debug.log /tmp cson-parser-1.3.4/.npmrc000066400000000000000000000000441277161230300150730ustar00rootroot00000000000000registry=https://registry.npmjs.org cson-parser-1.3.4/.travis.yml000066400000000000000000000005341277161230300160700ustar00rootroot00000000000000language: node_js node_js: - '0.10' - '4' before_install: - npm install -g npm@latest-2 before_deploy: - 'git config --global user.email "opensource@groupon.com"' - 'git config --global user.name "Groupon"' deploy: provider: script script: ./node_modules/.bin/nlm release skip_cleanup: true 'on': branch: master node: '4' cson-parser-1.3.4/CHANGELOG.md000066400000000000000000000061461277161230300155750ustar00rootroot00000000000000### 1.3.4 * Compatible with coffee-script 1.11.0 - **[@jkrems](https://github.com/jkrems)** [#57](https://github.com/groupon/cson-parser/pull/57) - [`ed54c9a`](https://github.com/groupon/cson-parser/commit/ed54c9a89b3afb933c2eee1281e17fd6d78e8dba) **fix:** Compatible with coffee-script 1.11.0 - see: [#56](https://github.com/groupon/cson-parser/issues/56) ### 1.3.3 * Apply latest nlm generator - **[@i-tier-bot](https://github.com/i-tier-bot)** [#55](https://github.com/groupon/cson-parser/pull/55) - [`9905e06`](https://github.com/groupon/cson-parser/commit/9905e064f85f2cad7c656821195ea4afcd37f11f) **chore:** Apply latest nlm generator ### 1.3.2 * Parser no longer requires support for constructor.name - **[@jkrems](https://github.com/jkrems)** [#52](https://github.com/groupon/cson-parser/pull/52) - [`c5591b3`](https://github.com/groupon/cson-parser/commit/c5591b3a8ce0ba88a3e7738f940263ef053e7145) **fix:** Handle missing fn.name ### 1.3.1 * chore: Move to nlm for publishing - **[@jkrems](https://github.com/jkrems)** [#50](https://github.com/groupon/cson-parser/pull/50) - [`3c704a4`](https://github.com/groupon/cson-parser/commit/3c704a4e796b6d997a4aea499ac7d85bfe9fffe6) **chore:** Move to nlm for publishing 1.3.0 ----- * Add support for regular expressions - @jkrems https://github.com/groupon/cson-parser/pull/49 1.2.0 ----- * Use flexible coffee-script version - @jkrems https://github.com/groupon/cson-parser/pull/47 1.1.1 ----- * Support key with empty object - @charlierudolph https://github.com/groupon/cson-parser/pull/42 1.1.0 ----- * Add fs-cson to readme - @charlierudolph https://github.com/groupon/cson-parser/pull/39 * Proper CSON single-line formatting - @charlierudolph https://github.com/groupon/cson-parser/pull/37 1.0.9 ----- * Escape backslash in multi-line string - @jkrems https://github.com/groupon/cson-parser/pull/35 * Pin coffee-script version to 1.9.0 - @jkrems https://github.com/groupon/cson-parser/pull/33 1.0.8 ----- * Run against iojs - @jkrems #27 1.0.7 ----- * Added CSON package which now uses cson-parser - @balupton https://github.com/groupon/cson-parser/pull/25 1.0.6 ----- * Rename to cson-parser - @jkrems https://github.com/groupon/cson-parser/pull/23 1.0.5 ----- * Be explicit about registry for publish - @jkrems https://github.com/groupon/cson-safe/pull/19 * Unify `''`/`""` handling, str.charAt(0) -> str[0] - @jkrems https://github.com/groupon/cson-safe/pull/22 1.0.4 ----- * Use `vm.runInThisContext` instead of eval - @jkrems https://github.com/groupon/cson-safe/pull/18 1.0.3 ----- * Add license SPDX ID to package.json - @jkrems https://github.com/groupon/cson-safe/pull/16 1.0.2 ----- * Upgrade npub - @abloom https://github.com/groupon/cson-safe/pull/14 v1.0.1 ------ * Even nicer stringify with less noise - @jkrems https://github.com/groupon/cson-safe/pull/13 v1.0.0 ------ * Switch to coffee-script for parsing - @jkrems https://github.com/groupon/cson-safe/pull/10 v0.2.0 ------ * A nicer CSON.stringify - @johan https://github.com/groupon/cson-safe/pull/9 v0.1.1 ------ * Fix package meta data v0.1.0 ------ * Initial public release cson-parser-1.3.4/CONTRIBUTING.md000066400000000000000000000013061277161230300162060ustar00rootroot00000000000000# Contribution Guide Please follow this guide when creating issues or pull requests. ## Reporting a Bug Before reporting a bug, make sure you are using the latest versions of the code. When reporting a bug with this library, please provide a minimal test case. This can be a gist, inline in the description, or in the form of a pull request that includes a failing test. If you are contributing a bug fix, make sure it has a passing test in your pull request. ## Adding a Feature Before implementing features, try to make sure that (1) no one else is currently working on that and (2) you have checked with the maintainers that this is something they would like to see. All features should have tests. cson-parser-1.3.4/LICENSE000066400000000000000000000026661277161230300147740ustar00rootroot00000000000000Copyright (c) 2014, Groupon, Inc. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. Neither the name of GROUPON nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. cson-parser-1.3.4/README.md000066400000000000000000000045171277161230300152430ustar00rootroot00000000000000# cson-parser A minimalistic CSON parser. Offers: * A strict subset of CSON that allows only data * Interface is identical to JSON.{parse,stringify} * Does not run the code, free of intermediate string representations * Sane parse error messages with line/column * Regular Expressions are considered data and will be accepted as well In addition of pure data it allows for simple arithmetic expressions like addition and multiplication. This allows more readable configuration of numbers, the following is a valid strict CSON file: ```coffee cachedData: refreshIntervalMs: 5 * 60 * 1000 ``` ## Install `npm install --save cson-parser` ## Usage ```coffee CSON = require 'cson-parser' # This will print { a: '123' } console.log CSON.parse "a: '123'" ``` ## High-level APIs `cson-parser` only offers basic parsing and serialization. But there are some great tools if you want more than that: * [`fs-cson`](https://github.com/charlierudolph/fs-cson), read and write CSON files * [`CSON`](https://github.com/bevry/cson), provides file, coffeescript, javascript handling and a CLI * [`season`](https://www.npmjs.org/package/season), atom.io's CSON package. Includes CLI tool to convert CSON to JSON * [`grunt-cson`](https://www.npmjs.org/package/grunt-cson), converts CSON to JSON as a grunt task * [`load-grunt-configs`](https://www.npmjs.org/package/load-grunt-configs), loads grunt config from CSON files (among other formats) * [`fetcher`](https://www.npmjs.org/package/fetcher), a declarative way to download (frontend) libraries, supports CSON configs * [`csonschema`](https://www.npmjs.org/package/csonschema), parses [JSON Schema](http://json-schema.org) files written in CSON You can find more on the [npm website](https://preview.npmjs.com/browse/depended/cson-parser). ## FAQ ### Why not just use YAML? YAML allows for some pretty complex constructs like anchor and alias, which can behave in unexpected ways, especially with nested objects. CSON is simpler while still offering most of the niceties of YAML. ### Why not just use JSON? JSON doesn't offer multi-line strings and is generally a little noisier. Also sometimes it can be nice to have comments in config files. ### Why not just use CoffeeScript directly? You don't want data files being able to run arbitrary code. Even when ran in a proper sandbox, `while(true)` is still possible. cson-parser-1.3.4/lib/000077500000000000000000000000001277161230300145235ustar00rootroot00000000000000cson-parser-1.3.4/lib/cson-parser.js000066400000000000000000000031411277161230300173140ustar00rootroot00000000000000 /* Copyright (c) 2014, Groupon, Inc. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. Neither the name of GROUPON nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ var CSON, parse, stringify; stringify = require('./stringify'); parse = require('./parse'); module.exports = CSON = { stringify: stringify, parse: parse }; cson-parser-1.3.4/lib/parse.js000066400000000000000000000163351277161230300162030ustar00rootroot00000000000000 /* Copyright (c) 2014, Groupon, Inc. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. Neither the name of GROUPON nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ var defaultReviver, getFunctionNameIE, nodeTypeString, nodes, parse, parseRegExpLiteral, parseStringLiteral, runInThisContext, syntaxErrorMessage; runInThisContext = require('vm').runInThisContext; nodes = require('coffee-script').nodes; defaultReviver = function(key, value) { return value; }; getFunctionNameIE = function(fn) { return csNode.constructor.toString().match(/^function\s*([^( ]+)/)[1]; }; nodeTypeString = function(csNode) { var ref; return (ref = csNode.constructor.name) != null ? ref : getFunctionNameIE(csNode.constructor); }; syntaxErrorMessage = function(csNode, msg) { var column, columnIdx, line, lineIdx, ref; ref = csNode.locationData, lineIdx = ref.first_line, columnIdx = ref.first_column; if (lineIdx != null) { line = lineIdx + 1; } if (columnIdx != null) { column = columnIdx + 1; } return "Syntax error on line " + line + ", column " + column + ": " + msg; }; parseStringLiteral = function(literal) { return runInThisContext(literal); }; parseRegExpLiteral = function(literal) { return runInThisContext(literal); }; parse = function(source, reviver) { var coffeeAst, contextObj, isLiteral, nodeTransforms, parsed, transformKey, transformNode; if (reviver == null) { reviver = defaultReviver; } nodeTransforms = { Block: function(node) { var expressions; expressions = node.expressions; if (!expressions || expressions.length !== 1) { throw new SyntaxError(syntaxErrorMessage(node, 'One top level value expected')); } return transformNode(expressions[0]); }, Value: function(node) { return transformNode(node.base); }, Bool: function(node) { return node.val === 'true'; }, BooleanLiteral: function(node) { return node.value === 'true'; }, Null: function() { return null; }, NullLiteral: function() { return null; }, Literal: function(node) { var err, value; value = node.value; try { switch (value.charAt(0)) { case "'": case '"': return parseStringLiteral(value); case '/': return parseRegExpLiteral(value); default: return JSON.parse(value); } } catch (error) { err = error; throw new SyntaxError(syntaxErrorMessage(node, err.message)); } }, NumberLiteral: function(node) { return Number(node.value); }, StringLiteral: function(node) { return parseStringLiteral(node.value); }, RegexLiteral: function(node) { return parseRegExpLiteral(node.value); }, Arr: function(node) { return node.objects.map(transformNode); }, Obj: function(node) { return node.properties.reduce(function(outObject, property) { var keyName, value, variable; variable = property.variable, value = property.value; if (!variable) { return outObject; } keyName = transformKey(variable); value = transformNode(value); outObject[keyName] = reviver.call(outObject, keyName, value); return outObject; }, {}); }, Op: function(node) { var left, right; if (node.second != null) { left = transformNode(node.first); right = transformNode(node.second); switch (node.operator) { case '-': return left - right; case '+': return left + right; case '*': return left * right; case '/': return left / right; case '%': return left % right; case '&': return left & right; case '|': return left | right; case '^': return left ^ right; case '<<': return left << right; case '>>>': return left >>> right; case '>>': return left >> right; default: throw new SyntaxError(syntaxErrorMessage(node, "Unknown binary operator " + node.operator)); } } else { switch (node.operator) { case '-': return -transformNode(node.first); case '~': return ~transformNode(node.first); default: throw new SyntaxError(syntaxErrorMessage(node, "Unknown unary operator " + node.operator)); } } }, Parens: function(node) { var expressions; expressions = node.body.expressions; if (!expressions || expressions.length !== 1) { throw new SyntaxError(syntaxErrorMessage(node, 'Parenthesis may only contain one expression')); } return transformNode(expressions[0]); } }; isLiteral = function(csNode) { return LiteralTypes.some(function(LiteralType) { return csNode instanceof LiteralType; }); }; transformKey = function(csNode) { var type, value; type = nodeTypeString(csNode); if (type !== 'Value') { throw new SyntaxError(syntaxErrorMessage(csNode, type + " used as key")); } value = csNode.base.value; switch (value.charAt(0)) { case "'": case '"': return parseStringLiteral(value); default: return value; } }; transformNode = function(csNode) { var transform, type; type = nodeTypeString(csNode); transform = nodeTransforms[type]; if (!transform) { throw new SyntaxError(syntaxErrorMessage(csNode, "Unexpected " + type)); } return transform(csNode); }; if (typeof reviver !== 'function') { throw new TypeError("reviver has to be a function"); } coffeeAst = nodes(source.toString('utf8')); parsed = transformNode(coffeeAst); if (reviver === defaultReviver) { return parsed; } contextObj = {}; contextObj[''] = parsed; return reviver.call(contextObj, '', parsed); }; module.exports = parse; cson-parser-1.3.4/lib/stringify.js000066400000000000000000000123001277161230300170730ustar00rootroot00000000000000 /* Copyright (c) 2014, Groupon, Inc. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. Neither the name of GROUPON nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ var SPACES, isObject, jsIdentifierRE, newlineWrap, tripleQuotesRE, indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; }; jsIdentifierRE = /^[a-z_$][a-z0-9_$]*$/i; tripleQuotesRE = new RegExp("'''", 'g'); SPACES = ' '; newlineWrap = function(str) { return str && ("\n" + str + "\n"); }; isObject = function(obj) { return typeof obj === 'object' && obj !== null && !Array.isArray(obj); }; module.exports = function(data, visitor, indent) { var indentLine, indentLines, n, normalized, ref, visitArray, visitNode, visitObject, visitString; if ((ref = typeof data) === 'undefined' || ref === 'function') { return void 0; } indent = (function() { switch (typeof indent) { case 'string': return indent.slice(0, 10); case 'number': n = Math.min(10, Math.floor(indent)); if (indexOf.call([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], n) < 0) { n = 0; } return SPACES.slice(0, n); default: return 0; } })(); indentLine = function(line) { return indent + line; }; indentLines = function(str) { if (str === '') { return str; } return str.split('\n').map(indentLine).join('\n'); }; normalized = JSON.parse(JSON.stringify(data, visitor)); visitString = function(str) { var string; if (str.indexOf('\n') === -1 || !indent) { return JSON.stringify(str); } else { string = str.replace(/\\/g, '\\\\').replace(tripleQuotesRE, "\\'''"); return "'''" + (newlineWrap(indentLines(string))) + "'''"; } }; visitArray = function(arr) { var items, serializedItems; items = arr.map(function(value) { return visitNode(value, { bracesRequired: true }); }); serializedItems = indent ? newlineWrap(indentLines(items.join('\n'))) : items.join(','); return "[" + serializedItems + "]"; }; visitObject = function(obj, arg) { var bracesRequired, key, keypairs, serializedKeyPairs, serializedValue, value; bracesRequired = arg.bracesRequired; keypairs = (function() { var results; results = []; for (key in obj) { value = obj[key]; if (!key.match(jsIdentifierRE)) { key = JSON.stringify(key); } serializedValue = visitNode(value, { bracesRequired: !indent }); if (indent) { serializedValue = isObject(value) && Object.keys(value).length > 0 ? "\n" + (indentLines(serializedValue)) : " " + serializedValue; } results.push(key + ":" + serializedValue); } return results; })(); if (keypairs.length === 0) { return '{}'; } else if (indent) { serializedKeyPairs = keypairs.join('\n'); if (bracesRequired) { return "{" + (newlineWrap(indentLines(serializedKeyPairs))) + "}"; } else { return serializedKeyPairs; } } else { serializedKeyPairs = keypairs.join(','); if (bracesRequired) { return "{" + serializedKeyPairs + "}"; } else { return serializedKeyPairs; } } }; visitNode = function(node, options) { if (options == null) { options = {}; } switch (typeof node) { case 'boolean': return "" + node; case 'number': if (isFinite(node)) { return "" + node; } else { return 'null'; } break; case 'string': return visitString(node, options); case 'object': if (node === null) { return 'null'; } else if (Array.isArray(node)) { return visitArray(node, options); } else { return visitObject(node, options); } } }; return visitNode(normalized); }; cson-parser-1.3.4/package.json000066400000000000000000000022011277161230300162360ustar00rootroot00000000000000{ "name": "cson-parser", "version": "1.3.4", "description": "Safe parsing of CSON files", "license": "BSD-3-Clause", "main": "lib/cson-parser.js", "homepage": "https://github.com/groupon/cson-parser", "repository": { "type": "git", "url": "git+ssh://git@github.com/groupon/cson-parser" }, "bugs": { "url": "https://github.com/groupon/cson-parser/issues" }, "scripts": { "build": "rm -rf lib && coffee --no-header -cbo lib src", "pretest": "npm run build", "test": "mocha", "posttest": "nlm verify", "watch": "coffee --no-header -wcbo lib src & nodemon -w lib -w test -e coffee,js,json -x \"mocha\"" }, "nlm": { "license": { "files": [ "src" ] } }, "dependencies": { "coffee-script": "^1.10.0" }, "devDependencies": { "assertive": "^2.0.0", "mocha": "^2.0.0", "nlm": "^2.0.0", "nodemon": "^1.0.0" }, "author": { "name": "Groupon", "email": "opensource@groupon.com" }, "keywords": [ "cson", "parser" ], "files": [ "*.js", "lib" ], "publishConfig": { "registry": "https://registry.npmjs.org" } } cson-parser-1.3.4/src/000077500000000000000000000000001277161230300145445ustar00rootroot00000000000000cson-parser-1.3.4/src/cson-parser.coffee000066400000000000000000000030511277161230300201500ustar00rootroot00000000000000### Copyright (c) 2014, Groupon, Inc. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. Neither the name of GROUPON nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ### stringify = require './stringify' parse = require './parse' module.exports = CSON = { stringify, parse } cson-parser-1.3.4/src/parse.coffee000066400000000000000000000146351277161230300170400ustar00rootroot00000000000000### Copyright (c) 2014, Groupon, Inc. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. Neither the name of GROUPON nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ### {runInThisContext} = require 'vm' {nodes} = require 'coffee-script' defaultReviver = (key, value) -> value getFunctionNameIE = (fn) -> # fn.name is only standard in ES2015+ and won't work in IE # This takes the source of the function and extracts the name, # e.g. 'function fooBar ()' becomes fooBar. csNode.constructor.toString() .match(/^function\s*([^( ]+)/)[1] nodeTypeString = (csNode) -> csNode.constructor.name ? getFunctionNameIE(csNode.constructor) syntaxErrorMessage = (csNode, msg) -> { first_line: lineIdx first_column: columnIdx } = csNode.locationData line = lineIdx + 1 if lineIdx? column = columnIdx + 1 if columnIdx? "Syntax error on line #{line}, column #{column}: #{msg}" parseStringLiteral = (literal) -> # In theory this could be replaced by properly resolving # escape sequences etc. # We trust the coffee-script lexer to make sure it's just # a strings and running it has no side effects. runInThisContext literal parseRegExpLiteral = (literal) -> # In theory this could be replaced by properly resolving # escape sequences etc. # We trust the coffee-script lexer to make sure it's just # a regular expression and running it has no side effects. runInThisContext literal # See: # http://www.ecma-international.org/ecma-262/5.1/#sec-15.12.2 parse = (source, reviver = defaultReviver) -> nodeTransforms = Block: (node) -> {expressions} = node if !expressions || expressions.length != 1 throw new SyntaxError syntaxErrorMessage(node, 'One top level value expected') transformNode expressions[0] Value: (node) -> transformNode node.base Bool: (node) -> # coffee-script@1.10.0 node.val == 'true' BooleanLiteral: (node) -> # coffee-script@1.11.0 node.value == 'true' Null: -> null # coffee-script@1.10.0 NullLiteral: -> null # coffee-script@1.11.0 Literal: (node) -> # coffee-script@1.10.0 {value} = node try switch value.charAt 0 when "'", '"' then parseStringLiteral value when '/' then parseRegExpLiteral value else JSON.parse value catch err throw new SyntaxError syntaxErrorMessage(node, err.message) NumberLiteral: (node) -> # coffee-script@1.11.0 Number(node.value) StringLiteral: (node) -> # coffee-script@1.11.0 parseStringLiteral node.value RegexLiteral: (node) -> # coffee-script@1.11.0 parseRegExpLiteral node.value Arr: (node) -> node.objects.map transformNode Obj: (node) -> node.properties.reduce( (outObject, property) -> {variable, value} = property return outObject unless variable keyName = transformKey variable value = transformNode value outObject[keyName] = reviver.call outObject, keyName, value outObject {} ) Op: (node) -> if node.second? left = transformNode node.first right = transformNode node.second switch node.operator when '-' then left - right when '+' then left + right when '*' then left * right when '/' then left / right when '%' then left % right when '&' then left & right when '|' then left | right when '^' then left ^ right when '<<' then left << right when '>>>' then left >>> right when '>>' then left >> right else throw new SyntaxError syntaxErrorMessage( node, "Unknown binary operator #{node.operator}" ) else switch node.operator when '-' then -transformNode(node.first) when '~' then ~transformNode(node.first) else throw new SyntaxError syntaxErrorMessage( node, "Unknown unary operator #{node.operator}" ) Parens: (node) -> {expressions} = node.body if !expressions || expressions.length != 1 throw new SyntaxError syntaxErrorMessage( node, 'Parenthesis may only contain one expression' ) transformNode expressions[0] isLiteral = (csNode) -> LiteralTypes.some (LiteralType) -> csNode instanceof LiteralType transformKey = (csNode) -> type = nodeTypeString csNode if type != 'Value' throw new SyntaxError syntaxErrorMessage(csNode, "#{type} used as key") {value} = csNode.base switch value.charAt 0 when "'", '"' then parseStringLiteral value else value transformNode = (csNode) -> type = nodeTypeString csNode transform = nodeTransforms[type] unless transform throw new SyntaxError syntaxErrorMessage(csNode, "Unexpected #{type}") transform csNode if typeof reviver != 'function' throw new TypeError "reviver has to be a function" coffeeAst = nodes source.toString 'utf8' parsed = transformNode(coffeeAst) return parsed if reviver == defaultReviver contextObj = {} contextObj[''] = parsed reviver.call contextObj, '', parsed module.exports = parse cson-parser-1.3.4/src/stringify.coffee000066400000000000000000000106571277161230300177440ustar00rootroot00000000000000### Copyright (c) 2014, Groupon, Inc. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. Neither the name of GROUPON nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ### # There are multiple ways to express the same thing in CSON, so trying to # make `CSON.stringify(CSON.parse(str)) == str` work is doomed to fail # but we can at least make output look a lot nicer than JSON.stringify's. jsIdentifierRE = /^[a-z_$][a-z0-9_$]*$/i tripleQuotesRE = new RegExp "'''", 'g' # some syntax hilighters hate on /'''/g SPACES = ' ' # 10 spaces newlineWrap = (str) -> str and "\n#{ str }\n" isObject = (obj) -> typeof obj == 'object' && obj != null && !Array.isArray(obj) # See: # http://www.ecma-international.org/ecma-262/5.1/#sec-15.12.3 module.exports = (data, visitor, indent) -> return undefined if typeof data in ['undefined', 'function'] # pick an indent style much as JSON.stringify does indent = switch typeof indent when 'string' then indent.slice 0, 10 when 'number' n = Math.min 10, Math.floor indent n = 0 unless n in [1..10] # do not bail on NaN and similar SPACES.slice(0, n) else 0 indentLine = (line) -> indent + line indentLines = (str) -> return str if str == '' str.split('\n').map(indentLine).join('\n') # have the native JSON serializer do visitor transforms & normalization for us normalized = JSON.parse JSON.stringify data, visitor visitString = (str) -> if str.indexOf('\n') == -1 or !indent JSON.stringify str else string = str .replace /\\/g, '\\\\' # escape backslashes in source string .replace tripleQuotesRE, "\\'''" "'''#{ newlineWrap indentLines string }'''" visitArray = (arr) -> items = arr.map (value) -> visitNode value, bracesRequired: true serializedItems = if indent newlineWrap indentLines items.join '\n' else items.join ',' "[#{ serializedItems }]" visitObject = (obj, {bracesRequired}) -> keypairs = for key, value of obj key = JSON.stringify key unless key.match jsIdentifierRE serializedValue = visitNode value, bracesRequired: !indent if indent serializedValue = if isObject(value) and Object.keys(value).length > 0 "\n#{ indentLines serializedValue }" else " #{ serializedValue }" "#{ key }:#{ serializedValue }" if keypairs.length is 0 '{}' else if indent serializedKeyPairs = keypairs.join '\n' if bracesRequired "{#{ newlineWrap indentLines serializedKeyPairs }}" else serializedKeyPairs else serializedKeyPairs = keypairs.join ',' if bracesRequired "{#{ serializedKeyPairs }}" else serializedKeyPairs visitNode = (node, options = {}) -> switch typeof node when 'boolean' then "#{node}" when 'number' if isFinite node then "#{node}" else 'null' # NaN, Infinity and -Infinity when 'string' then visitString node, options when 'object' if node == null then 'null' else if Array.isArray(node) then visitArray node, options else visitObject node, options visitNode normalized cson-parser-1.3.4/test/000077500000000000000000000000001277161230300147345ustar00rootroot00000000000000cson-parser-1.3.4/test/mocha.opts000066400000000000000000000000731277161230300167320ustar00rootroot00000000000000--compilers test.coffee:coffee-script/register --recursive cson-parser-1.3.4/test/parse.test.coffee000066400000000000000000000133451277161230300202030ustar00rootroot00000000000000 CSON = require '../' assert = require 'assertive' os = require 'os' compilesTo = (source, expected) -> assert.deepEqual expected, CSON.parse(source) describe 'CSON.parse', -> it 'parses an empty object', -> compilesTo '{}', {} it 'parses boolean values', -> compilesTo 'true', true compilesTo 'yes', true compilesTo 'on', true compilesTo 'false', false compilesTo 'no', false compilesTo 'off', false it 'parses numbers', -> compilesTo '0.42', 0.42 compilesTo '42', 42 compilesTo '1.2e+4', 1.2e+4 it 'parses arrays', -> compilesTo '[ 1, 2, a: "str" ]', [ 1, 2, a: 'str' ] compilesTo( """ [ 1 2 a: 'str' ] """ [ 1, 2, a: 'str' ] ) it 'parses null', -> compilesTo 'null', null it 'does not allow undefined', -> err = assert.throws -> CSON.parse 'undefined' assert.match /^Syntax error on line 1, column 1: Unexpected Undefined/, err.message it 'allows line comments', -> compilesTo 'true # line comment', true it 'allows multi-line comments', -> compilesTo( """ a: ### This is a comment spanning multiple lines ### c: 3 """ { a: { c: 3 } } ) it 'allows multi-line strings', -> compilesTo( """ \"""Some long string \""" """ "Some long#{os.EOL}string" ) it 'does not allow using assignments', -> err = assert.throws -> CSON.parse 'a = 3' assert.equal 'Syntax error on line 1, column 1: Unexpected Assign', err.message err = assert.throws -> CSON.parse 'a ?= 3' assert.equal 'Syntax error on line 1, column 1: Unexpected Assign', err.message it 'does not allow referencing variables', -> err = assert.throws -> CSON.parse 'a: foo' assert.match /Syntax error on line 1, column 4: Unexpected (token o|IdentifierLiteral)/, err.message err = assert.throws -> CSON.parse 'a: process.env.NODE_ENV' assert.match /Syntax error on line 1, column 4: Unexpected (token p|IdentifierLiteral)/, err.message it 'does not allow Infinity or -Infinity', -> err = assert.throws -> CSON.parse 'a: Infinity' assert.match /^Syntax error on line 1, column 4: Unexpected (token I|InfinityLiteral)/, err.message err = assert.throws -> CSON.parse 'a: -Infinity' assert.match /Syntax error on line 1, column 5: Unexpected (token I|InfinityLiteral)/, err.message it 'does allow simple mathematical operations', -> compilesTo '(2 + 3) * 4', ((2 + 3) * 4) compilesTo '2 + 3 * 4', (2 + 3 * 4) compilesTo 'fetchIntervalMs: 1000 * 60 * 5', fetchIntervalMs: (1000 * 60 * 5) compilesTo '2 / 4', (2 / 4) compilesTo '5 - 1', (5 - 1) compilesTo '3 % 2', (3 % 2) it 'allows bit operations', -> compilesTo '5 & 6', (5 & 6) compilesTo '1 | 2', (1 | 2) compilesTo '~0', (~0) compilesTo '3 ^ 5', (3 ^ 5) compilesTo '1 << 3', (1 << 3) compilesTo '8 >> 3', (8 >> 3) compilesTo '-9 >>> 2', (-9 >>> 2) it 'allows hard tabs in strings', -> compilesTo 'a: "x\ty"', a: 'x\ty' it 'parses simple regular expressions', -> compilesTo 'a: /^[a-d]*/g', a: /^[a-d]*/g it 'parses complex multi-line regular expressions', -> compilesTo ''' syntax: identifier: /\\b[a-z_][a-z_0-9]*\\b/i operator: /// ^ ( ?: [-=]> # function | [-+*/%<>&|^!?=]= # compound assign / compare | >>>=? # zero-fill right shift | ([-+:]) # doubles | ([&|<>]) # logic / shift | \\?\\. # soak access | \\.{2,3} # range or splat ) /// ''', { syntax: identifier: /\b[a-z_][a-z_0-9]*\b/i operator: /// ^ ( ?: [-=]> # function | [-+*/%<>&|^!?=]= # compound assign / compare | >>>=? # zero-fill right shift | ([-+:]) # doubles | ([&|<>]) # logic / shift | \?\. # soak access | \.{2,3} # range or splat ) /// } it 'parses nested objects', -> compilesTo( """ a: b: c: false "d": 44 3: "t" e: 'str' """ { a: { b: { c: false }, d: 44, 3: 't' }, e: 'str' } ) it 'parses nested objects in arrays', -> compilesTo( """ o: [ a: 'x' b: 'y' c: d: 'z' , a: 'x' b: 'y' ] """ o: [ { a: 'x', b: 'y', c: { d: 'z' } } { a: 'x', b: 'y' } ] ) describe 'reviver functions', -> calls = expected = source = reviver = null beforeEach -> calls = [] reviver = (key, value) -> # Test: called on parent object @x = 'magic' if key == '4' calls.push key if typeof value == 'number' then value * 2 else value source = JSON.stringify { "1": 1, "2": 2,"3": {"4": 4, "5": {"6": 6}} } expected = 1: 2 2: 4 3: { x: 'magic', 4: 8, 5: { 6: 12 } } it 'supports them', -> assert.deepEqual( expected, CSON.parse(source, reviver) ) # See: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/parse assert.deepEqual calls, [ '1', '2', '4', '6', '5', '3', '' ] it 'works just like JSON.parse', -> assert.deepEqual( expected, JSON.parse(source, reviver) ) # See: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/parse assert.deepEqual calls, [ '1', '2', '4', '6', '5', '3', '' ] cson-parser-1.3.4/test/stringify.test.coffee000066400000000000000000000072061277161230300211060ustar00rootroot00000000000000 { equal } = require 'assertive' CSON = require '../' cson = (obj, visitor, space = 2) -> CSON.stringify obj, visitor, space describe 'CSON.stringify', -> it 'handles null', -> equal 'null', cson null it 'handles boolean values', -> equal 'true', cson true equal 'false', cson false it 'handles the empty object', -> equal '{}', cson {} it 'handles the empty array', -> equal '[]', cson [] it 'handles numbers', -> equal '0.42', cson 0.42 equal '42', cson 42 equal '1.2e+90', cson 1.2e+90 it 'handles single-line strings', -> equal '"hello!"', cson 'hello!' it 'handles multi-line strings', -> equal """ ''' I am your average multi-line string, and I have a sneaky \\''' in here, too ''' """, cson """ I am your average multi-line string, and I have a sneaky ''' in here, too """ it 'handles multi-line strings (with 0 indentation)', -> equal """ "I am your average multi-line string,\\nand I have a sneaky ''' in here, too" """, cson """ I am your average multi-line string, and I have a sneaky ''' in here, too """, null, 0 it 'handles multi-line strings w/ backslash', -> test = '\\\n\\' expected = "'''\n \\\\\n \\\\\n'''" equal test, CSON.parse(cson test) equal expected, cson test it 'handles arrays', -> equal ''' [ [ 1 ] null [] { a: "str" } {} ] ''', cson [ [1], null, [], a: 'str', {} ] it 'handles arrays (with 0 indentation)', -> equal ''' [[1],null,[],{a:"str"},{}] ''', cson [ [1], null, [], a: 'str', {} ], null, 0 it 'handles objects', -> equal ''' "": "empty" "non\\nidentifier": true default: false emptyObject: {} nested: string: "too" array: [ {} [] ] ''', cson { '': 'empty' "non\nidentifier": true default: false emptyObject: {} nested: { string: 'too' } array: [ {} [] ] } it 'handles objects (with 0 indentation)', -> equal ''' "":"empty","non\\nidentifier":true,default:false,nested:{string:"too"},array:[{},[]] ''', cson { '': 'empty' "non\nidentifier": true default: false nested: { string: 'too' } array: [ {} [] ] }, null, 0 it 'handles NaN and +/-Infinity like JSON.stringify does', -> equal 'null', cson NaN equal 'null', cson +Infinity equal 'null', cson -Infinity it 'handles undefined like JSON.stringify does', -> equal undefined, cson undefined it 'handles functions like JSON.stringify does', -> equal undefined, cson -> it 'accepts no more than ten indentation steps, just like JSON.stringify', -> equal ''' x: "don't": "be silly, will'ya?" ''', cson { x: { "don't": "be silly, will'ya?" } }, null, Infinity it 'lets people that really want to indent with tabs', -> equal ''' x: \t\t"super-tabby": true ''', cson { x: { 'super-tabby': yes } }, null, '\t\t' it 'handles indentation by NaN', -> equal '[1]', cson([ 1 ], null, NaN) it 'handles indentation by floating point numbers', -> equal '[\n 1\n]', cson([ 1 ], null, 3.9) it 'is bug compatible with JSON.stringify for non-whitespace indention', -> equal ''' x: ecma-262strange: true ''', cson { x: { strange: yes } }, null, 'ecma-262' it 'handles visitor functions', -> equal ''' keep: 1 ''', cson {filter: 'me', keep: 1}, (k, v) -> v unless typeof v is 'string'