[Pkg-javascript-commits] [node-cson-parser] 01/02: Imported Upstream version 1.3.4
Ross Gammon
ross-guest at moszumanska.debian.org
Tue Dec 20 18:51:57 UTC 2016
This is an automated email from the git hooks/post-receive script.
ross-guest pushed a commit to branch master
in repository node-cson-parser.
commit d33dac59253bc468ed725930d513cb4e8ff7803d
Author: Ross Gammon <rossgammon at mail.dk>
Date: Sat Nov 19 17:30:43 2016 +0100
Imported Upstream version 1.3.4
---
.editorconfig | 11 +++
.gitignore | 3 +
.npmrc | 1 +
.travis.yml | 16 +++
CHANGELOG.md | 111 +++++++++++++++++++++
CONTRIBUTING.md | 30 ++++++
LICENSE | 29 ++++++
README.md | 71 ++++++++++++++
lib/cson-parser.js | 42 ++++++++
lib/parse.js | 237 +++++++++++++++++++++++++++++++++++++++++++++
lib/stringify.js | 164 +++++++++++++++++++++++++++++++
package.json | 53 ++++++++++
src/cson-parser.coffee | 36 +++++++
src/parse.coffee | 196 +++++++++++++++++++++++++++++++++++++
src/stringify.coffee | 134 +++++++++++++++++++++++++
test/mocha.opts | 2 +
test/parse.test.coffee | 215 ++++++++++++++++++++++++++++++++++++++++
test/stringify.test.coffee | 154 +++++++++++++++++++++++++++++
18 files changed, 1505 insertions(+)
diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 0000000..beffa30
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,11 @@
+root = true
+
+[*]
+indent_style = space
+indent_size = 2
+charset = utf-8
+trim_trailing_whitespace = true
+insert_final_newline = true
+
+[*.md]
+trim_trailing_whitespace = false
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..c78d32f
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,3 @@
+node_modules/
+npm-debug.log
+/tmp
diff --git a/.npmrc b/.npmrc
new file mode 100644
index 0000000..38f11c6
--- /dev/null
+++ b/.npmrc
@@ -0,0 +1 @@
+registry=https://registry.npmjs.org
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 0000000..b1a0247
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,16 @@
+language: node_js
+node_js:
+ - '0.10'
+ - '4'
+before_install:
+ - npm install -g npm at latest-2
+before_deploy:
+ - 'git config --global user.email "opensource at 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'
diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
index 0000000..39a9b77
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,111 @@
+### 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
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
new file mode 100644
index 0000000..82c3e4e
--- /dev/null
+++ b/CONTRIBUTING.md
@@ -0,0 +1,30 @@
+# 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.
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..e97f18f
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,29 @@
+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.
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..c75c15e
--- /dev/null
+++ b/README.md
@@ -0,0 +1,71 @@
+# 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.
diff --git a/lib/cson-parser.js b/lib/cson-parser.js
new file mode 100644
index 0000000..130a80f
--- /dev/null
+++ b/lib/cson-parser.js
@@ -0,0 +1,42 @@
+
+/*
+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
+};
diff --git a/lib/parse.js b/lib/parse.js
new file mode 100644
index 0000000..1ac92c1
--- /dev/null
+++ b/lib/parse.js
@@ -0,0 +1,237 @@
+
+/*
+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;
diff --git a/lib/stringify.js b/lib/stringify.js
new file mode 100644
index 0000000..145302f
--- /dev/null
+++ b/lib/stringify.js
@@ -0,0 +1,164 @@
+
+/*
+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);
+};
diff --git a/package.json b/package.json
new file mode 100644
index 0000000..2760e9a
--- /dev/null
+++ b/package.json
@@ -0,0 +1,53 @@
+{
+ "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 at groupon.com"
+ },
+ "keywords": [
+ "cson",
+ "parser"
+ ],
+ "files": [
+ "*.js",
+ "lib"
+ ],
+ "publishConfig": {
+ "registry": "https://registry.npmjs.org"
+ }
+}
diff --git a/src/cson-parser.coffee b/src/cson-parser.coffee
new file mode 100644
index 0000000..4f250b8
--- /dev/null
+++ b/src/cson-parser.coffee
@@ -0,0 +1,36 @@
+###
+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 }
diff --git a/src/parse.coffee b/src/parse.coffee
new file mode 100644
index 0000000..b0c29c7
--- /dev/null
+++ b/src/parse.coffee
@@ -0,0 +1,196 @@
+###
+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 at 1.10.0
+ node.val == 'true'
+ BooleanLiteral: (node) -> # coffee-script at 1.11.0
+ node.value == 'true'
+
+ Null: -> null # coffee-script at 1.10.0
+ NullLiteral: -> null # coffee-script at 1.11.0
+
+ Literal: (node) -> # coffee-script at 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 at 1.11.0
+ Number(node.value)
+ StringLiteral: (node) -> # coffee-script at 1.11.0
+ parseStringLiteral node.value
+ RegexLiteral: (node) -> # coffee-script at 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
diff --git a/src/stringify.coffee b/src/stringify.coffee
new file mode 100644
index 0000000..6e3dbd5
--- /dev/null
+++ b/src/stringify.coffee
@@ -0,0 +1,134 @@
+###
+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
diff --git a/test/mocha.opts b/test/mocha.opts
new file mode 100644
index 0000000..1083fea
--- /dev/null
+++ b/test/mocha.opts
@@ -0,0 +1,2 @@
+--compilers test.coffee:coffee-script/register
+--recursive
diff --git a/test/parse.test.coffee b/test/parse.test.coffee
new file mode 100644
index 0000000..e998086
--- /dev/null
+++ b/test/parse.test.coffee
@@ -0,0 +1,215 @@
+
+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', '' ]
diff --git a/test/stringify.test.coffee b/test/stringify.test.coffee
new file mode 100644
index 0000000..a09c467
--- /dev/null
+++ b/test/stringify.test.coffee
@@ -0,0 +1,154 @@
+
+{ 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'
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-javascript/node-cson-parser.git
More information about the Pkg-javascript-commits
mailing list