├── test ├── fails │ ├── 29.json │ ├── 30.json │ ├── 16.json │ ├── 31.json │ ├── 33.json │ ├── 4.json │ ├── 8.json │ ├── 2.json │ ├── 23.json │ ├── 24.json │ ├── 27.json │ ├── 28.json │ ├── 19.json │ ├── 20.json │ ├── 34.json │ ├── 5.json │ ├── 6.json │ ├── 7.json │ ├── 9.json │ ├── 11.json │ ├── 25.json │ ├── 36.json │ ├── 12.json │ ├── 14.json │ ├── 15.json │ ├── 17.json │ ├── 21.json │ ├── 22.json │ ├── 26.json │ ├── 3.json │ ├── 32.json │ ├── 13.json │ ├── 10.json │ └── 35.json ├── passes │ ├── 2.json │ ├── 3.json │ └── 1.json └── all-tests.js ├── .gitignore ├── bower.json ├── scripts └── bundle.js ├── Makefile ├── src ├── jsonlint.l └── jsonlint.y ├── package.json ├── web ├── jsonlint.html ├── json2.js └── jsonlint.js ├── README.md └── lib ├── formatter.js ├── cli.js ├── doug-json-parse.js ├── nomnom.js └── jsonlint.js /test/fails/29.json: -------------------------------------------------------------------------------- 1 | [0e] -------------------------------------------------------------------------------- /test/fails/30.json: -------------------------------------------------------------------------------- 1 | [0e+] -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /test/fails/16.json: -------------------------------------------------------------------------------- 1 | [\naked] -------------------------------------------------------------------------------- /test/fails/31.json: -------------------------------------------------------------------------------- 1 | [0e+-1] -------------------------------------------------------------------------------- /test/fails/33.json: -------------------------------------------------------------------------------- 1 | ["mismatch"} -------------------------------------------------------------------------------- /test/fails/4.json: -------------------------------------------------------------------------------- 1 | ["extra comma",] -------------------------------------------------------------------------------- /test/fails/8.json: -------------------------------------------------------------------------------- 1 | ["Extra close"]] -------------------------------------------------------------------------------- /test/fails/2.json: -------------------------------------------------------------------------------- 1 | ["Unclosed array" -------------------------------------------------------------------------------- /test/fails/23.json: -------------------------------------------------------------------------------- 1 | ["Bad value", truth] -------------------------------------------------------------------------------- /test/fails/24.json: -------------------------------------------------------------------------------- 1 | ['single quote'] -------------------------------------------------------------------------------- /test/fails/27.json: -------------------------------------------------------------------------------- 1 | ["line 2 | break"] -------------------------------------------------------------------------------- /test/fails/28.json: -------------------------------------------------------------------------------- 1 | ["line\ 2 | break"] -------------------------------------------------------------------------------- /test/fails/19.json: -------------------------------------------------------------------------------- 1 | {"Missing colon" null} -------------------------------------------------------------------------------- /test/fails/20.json: -------------------------------------------------------------------------------- 1 | {"Double colon":: null} -------------------------------------------------------------------------------- /test/fails/34.json: -------------------------------------------------------------------------------- 1 | {"extra brace": 1}} 2 | -------------------------------------------------------------------------------- /test/fails/5.json: -------------------------------------------------------------------------------- 1 | ["double extra comma",,] -------------------------------------------------------------------------------- /test/fails/6.json: -------------------------------------------------------------------------------- 1 | [ , "<-- missing value"] -------------------------------------------------------------------------------- /test/fails/7.json: -------------------------------------------------------------------------------- 1 | ["Comma after the close"], -------------------------------------------------------------------------------- /test/fails/9.json: -------------------------------------------------------------------------------- 1 | {"Extra comma": true,} -------------------------------------------------------------------------------- /test/fails/11.json: -------------------------------------------------------------------------------- 1 | {"Illegal expression": 1 + 2} -------------------------------------------------------------------------------- /test/fails/25.json: -------------------------------------------------------------------------------- 1 | [" tab character in string "] -------------------------------------------------------------------------------- /test/fails/36.json: -------------------------------------------------------------------------------- 1 | {"x":1, 2 | "x":2} 3 | -------------------------------------------------------------------------------- /test/fails/12.json: -------------------------------------------------------------------------------- 1 | {"Illegal invocation": alert()} -------------------------------------------------------------------------------- /test/fails/14.json: -------------------------------------------------------------------------------- 1 | {"Numbers cannot be hex": 0x14} -------------------------------------------------------------------------------- /test/fails/15.json: -------------------------------------------------------------------------------- 1 | ["Illegal backslash escape: \x15"] -------------------------------------------------------------------------------- /test/fails/17.json: -------------------------------------------------------------------------------- 1 | ["Illegal backslash escape: \017"] -------------------------------------------------------------------------------- /test/fails/21.json: -------------------------------------------------------------------------------- 1 | {"Comma instead of colon", null} -------------------------------------------------------------------------------- /test/fails/22.json: -------------------------------------------------------------------------------- 1 | ["Colon instead of comma": false] -------------------------------------------------------------------------------- /test/fails/26.json: -------------------------------------------------------------------------------- 1 | ["tab\ character\ in\ string\ "] -------------------------------------------------------------------------------- /test/fails/3.json: -------------------------------------------------------------------------------- 1 | {unquoted_key: "keys must be quoted"} -------------------------------------------------------------------------------- /test/fails/32.json: -------------------------------------------------------------------------------- 1 | {"Comma instead if closing brace": true, -------------------------------------------------------------------------------- /test/fails/13.json: -------------------------------------------------------------------------------- 1 | {"Numbers cannot have leading zeroes": 013} -------------------------------------------------------------------------------- /test/passes/2.json: -------------------------------------------------------------------------------- 1 | [[[[[[[[[[[[[[[[[[["Not too deep"]]]]]]]]]]]]]]]]]]] -------------------------------------------------------------------------------- /test/fails/10.json: -------------------------------------------------------------------------------- 1 | {"Extra value after close": true} "misplaced quoted value" -------------------------------------------------------------------------------- /test/passes/3.json: -------------------------------------------------------------------------------- 1 | { 2 | "JSON Test Pattern pass3": { 3 | "The outermost value": "must be an object or array.", 4 | "In this test": "It is an object." 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "jsonlint", 3 | "version": "1.6.0", 4 | "main": "lib/jsonlint.js", 5 | "ignore": [ 6 | "**/.*", 7 | "node_modules", 8 | "scripts", 9 | "src", 10 | "test", 11 | "web", 12 | "package.json", 13 | "Makefile", 14 | "README.md" 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /scripts/bundle.js: -------------------------------------------------------------------------------- 1 | var fs = require('fs'); 2 | 3 | var source = "var jsonlint = (function(){var require=true,module=false;var exports={};" + 4 | fs.readFileSync(__dirname+'/../lib/doug-json-parse.js', 'utf8') + 5 | fs.readFileSync(__dirname+'/../lib/jsonlint.js', 'utf8') + 6 | "return exports;})();if(typeof module === 'object' && module.exports) module.exports = jsonlint;"; 7 | 8 | console.log(source); 9 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | 2 | all: build test site 3 | 4 | build: 5 | ./node_modules/jison/lib/cli.js src/jsonlint.y src/jsonlint.l 6 | mv jsonlint.js lib/jsonlint.js 7 | node scripts/bundle.js | ./node_modules/uglify-js/bin/uglifyjs > web/jsonlint.js 8 | 9 | site: 10 | cp web/jsonlint.js ../jsonlint-pages/jsonlint.js 11 | 12 | deploy: site 13 | cd ../jsonlint-pages && git commit -a -m 'deploy site updates' && git push origin gh-pages 14 | 15 | test: lib/jsonlint.js test/all-tests.js 16 | node test/all-tests.js 17 | 18 | -------------------------------------------------------------------------------- /src/jsonlint.l: -------------------------------------------------------------------------------- 1 | int "-"?([0-9]|[1-9][0-9]+) 2 | exp [eE][-+]?[0-9]+ 3 | frac "."[0-9]+ 4 | 5 | %% 6 | \s+ /* skip whitespace */ 7 | 8 | {int}{frac}?{exp}?\b return 'NUMBER' 9 | \"(?:'\\'[\\"bfnrt/]|'\\u'[a-fA-F0-9]{4}|[^\\\0-\x09\x0a-\x1f"])*\" yytext = yytext.substr(1,yyleng-2); return 'STRING' 10 | 11 | "{" return '{' 12 | "}" return '}' 13 | "[" return '[' 14 | "]" return ']' 15 | "," return ',' 16 | ":" return ':' 17 | "true" return 'TRUE' 18 | "false" return 'FALSE' 19 | "null" return 'NULL' 20 | <> return 'EOF' 21 | . return 'INVALID' 22 | 23 | %% 24 | 25 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "author": "Zach Carter (http://zaa.ch)", 3 | "name": "jsonlint-mod", 4 | "description": "Validate JSON", 5 | "keywords": [ 6 | "json", 7 | "validation", 8 | "lint", 9 | "jsonlint" 10 | ], 11 | "version": "1.7.6", 12 | "preferGlobal": true, 13 | "repository": { 14 | "type": "git", 15 | "url": "git://github.com/circlecell/jsonlint.git" 16 | }, 17 | "bugs": { 18 | "url": "http://github.com/circlecell/jsonlint/issues" 19 | }, 20 | "main": "web/jsonlint.js", 21 | "bin": { 22 | "jsonlint": "lib/cli.js" 23 | }, 24 | "engines": { 25 | "node": ">= 0.6" 26 | }, 27 | "dependencies": { 28 | "JSV": "^4.0.2", 29 | "chalk": "^2.4.2", 30 | "underscore": "^1.9.1" 31 | }, 32 | "devDependencies": { 33 | "test": "*", 34 | "jison": "*", 35 | "uglify-js": "*" 36 | }, 37 | "scripts": { 38 | "test": "node test/all-tests.js", 39 | "bundle": "node scripts/bundle > web/jsonlint.js", 40 | "prepublish": "npm run bundle" 41 | }, 42 | "homepage": "http://jsonlint.com", 43 | "optionalDependencies": {} 44 | } 45 | -------------------------------------------------------------------------------- /test/passes/1.json: -------------------------------------------------------------------------------- 1 | [ 2 | "JSON Test Pattern pass1", 3 | {"object with 1 member":["array with 1 element"]}, 4 | {}, 5 | [], 6 | -42, 7 | true, 8 | false, 9 | null, 10 | { 11 | "integer": 1234567890, 12 | "real": -9876.543210, 13 | "e": 0.123456789e-12, 14 | "E": 1.234567890E+34, 15 | "": 23456789012E66, 16 | "zero": 0, 17 | "one": 1, 18 | "space": " ", 19 | "quote": "\"", 20 | "backslash": "\\", 21 | "controls": "\b\f\n\r\t", 22 | "slash": "/ & \/", 23 | "alpha": "abcdefghijklmnopqrstuvwyz", 24 | "ALPHA": "ABCDEFGHIJKLMNOPQRSTUVWYZ", 25 | "digit": "0123456789", 26 | "0123456789": "digit", 27 | "special": "`1~!@#$%^&*()_+-={':[,]}|;.?", 28 | "hex": "\u0123\u4567\u89AB\uCDEF\uabcd\uef4A", 29 | "true": true, 30 | "false": false, 31 | "null": null, 32 | "array":[ ], 33 | "object":{ }, 34 | "address": "50 St. James Street", 35 | "url": "http://www.JSON.org/", 36 | "comment": "// /* */": " ", 38 | " s p a c e d " :[1,2 , 3 39 | 40 | , 41 | 42 | 4 , 5 , 6 ,7 ],"compact":[1,2,3,4,5,6,7], 43 | "jsontext": "{\"object with 1 member\":[\"array with 1 element\"]}", 44 | "quotes": "" \u0022 %22 0x22 034 "", 45 | "\/\\\"\uCAFE\uBABE\uAB98\uFCDE\ubcda\uef4A\b\f\n\r\t`1~!@#$%^&*()_+-=[]{}|;:',./<>?" 46 | : "A key can be any string" 47 | }, 48 | 0.5 ,98.6 49 | , 50 | 99.44 51 | , 52 | 53 | 1066, 54 | 1e1, 55 | 0.1e1, 56 | 1e-1, 57 | 1e00,2e+00,2e-00 58 | ,"rosebud"] -------------------------------------------------------------------------------- /src/jsonlint.y: -------------------------------------------------------------------------------- 1 | %start JSONText 2 | 3 | /* 4 | ECMA-262 5th Edition, 15.12.1 The JSON Grammar. 5 | */ 6 | 7 | 8 | %% 9 | 10 | JSONString 11 | : STRING 12 | { // replace escaped characters with actual character 13 | $$ = yytext.replace(/\\(\\|")/g, "$"+"1") 14 | .replace(/\\n/g,'\n') 15 | .replace(/\\r/g,'\r') 16 | .replace(/\\t/g,'\t') 17 | .replace(/\\v/g,'\v') 18 | .replace(/\\f/g,'\f') 19 | .replace(/\\b/g,'\b'); 20 | } 21 | ; 22 | 23 | JSONNumber 24 | : NUMBER 25 | {$$ = Number(yytext);} 26 | ; 27 | 28 | JSONNullLiteral 29 | : NULL 30 | {$$ = null;} 31 | ; 32 | 33 | JSONBooleanLiteral 34 | : TRUE 35 | {$$ = true;} 36 | | FALSE 37 | {$$ = false;} 38 | ; 39 | 40 | JSONText 41 | : JSONValue EOF 42 | {return $$ = $1;} 43 | ; 44 | 45 | JSONValue 46 | : JSONNullLiteral 47 | | JSONBooleanLiteral 48 | | JSONString 49 | | JSONNumber 50 | | JSONObject 51 | | JSONArray 52 | ; 53 | 54 | JSONObject 55 | : '{' '}' 56 | {{$$ = {};}} 57 | | '{' JSONMemberList '}' 58 | {$$ = $2;} 59 | ; 60 | 61 | JSONMember 62 | : JSONString ':' JSONValue 63 | {$$ = [$1, $3];} 64 | ; 65 | 66 | JSONMemberList 67 | : JSONMember 68 | {{$$ = {}; $$[$1[0]] = $1[1];}} 69 | | JSONMemberList ',' JSONMember 70 | {$$ = $1; $1[$3[0]] = $3[1];} 71 | ; 72 | 73 | JSONArray 74 | : '[' ']' 75 | {$$ = [];} 76 | | '[' JSONElementList ']' 77 | {$$ = $2;} 78 | ; 79 | 80 | JSONElementList 81 | : JSONValue 82 | {$$ = [$1];} 83 | | JSONElementList ',' JSONValue 84 | {$$ = $1; $1.push($3);} 85 | ; 86 | 87 | -------------------------------------------------------------------------------- /test/fails/35.json: -------------------------------------------------------------------------------- 1 | { 2 |    "chapters":[ 3 |         { 4 |             "title":"Unit Opener", 5 |             "file":"index1.html" 6 |         }, 7 |         { 8 |             "title":"Reading", 9 |             "file":"index2.html" 10 |         }, 11 |         { 12 |             "title":"Reading Comprehension", 13 |             "file":"index3.html" 14 |         }, 15 |         { 16 |             "title":"Vocabulary List", 17 |             "file":"index4.html" 18 |         }, 19 |         { 20 |             "title":"Mind Map", 21 |             "file":"index5.html" 22 |         }, 23 |         { 24 |             "title":"Vocabulary and Spelling", 25 |             "file":"index6.html" 26 |         }, 27 |         { 28 |             "title":"Language Arts", 29 |             "file":"index7.html" 30 |         }, 31 |         { 32 |             "title":"Grammar", 33 |             "file":"index8.html" 34 |         }, 35 | { 36 |             "title":"Reading 2", 37 |             "file":"index9.html" 38 |         }, 39 |         { 40 |             "title":"Reading Comprehension and Vocabulary", 41 |             "file":"index10.html" 42 |         }, 43 |         { 44 |             "title":"Let's Play", 45 |             "file":"index11.html" 46 |         }, 47 |         { 48 |             "title":"Investigate and Report", 49 |             "file":"index12.html" 50 |         }, 51 |         { 52 |             "title":"Grammar Review", 53 |             "file":"index13.html" 54 |         }, 55 |         { 56 |             "title":"Everyday English", 57 |             "file":"index14.html" 58 |         }, 59 |         { 60 |             "title":"Test Yourself: Reading", 61 |             "file":"index15.html" 62 |         }, 63 |         { 64 |             "title":"Test Yourself: Writing", 65 |             "file":"index16.html" 66 |         }, 67 | { 68 |             "title":"Logic: Allegory", 69 |             "file":"index16.html" 70 |         } 71 |     ] 72 | } -------------------------------------------------------------------------------- /web/jsonlint.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | JSON Lint 6 | 7 | 8 | 27 | 44 | 45 | 46 |

JSON Lint

47 |

A pure JavaScript version of the service provided at jsonlint.com.

48 | 51 |

52 | 53 | 54 |

55 |

Results

56 |

57 |   

project on github

58 | 59 | 60 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | JSON Lint [![npm version](https://badge.fury.io/js/jsonlint-mod.svg)](https://badge.fury.io/js/jsonlint-mod) 2 | ========= 3 | 4 | The fork is a modified version of [jsonlint](http://zaach.github.com/jsonlint/) enhanced by the original Douglas Crockford's JSON parser which brought the following features: 5 | 6 | - Handle hidden chars 7 | - Handle key duplicates 8 | - Web version exported as CJS module to use with Webpack and other bundlers 9 | 10 | A modified description below. 11 | 12 | ## Command line interface 13 | Install jsonlint with npm to use the command line interface: 14 | 15 | npm install jsonlint-mod -g 16 | 17 | Validate a file like so: 18 | 19 | jsonlint myfile.json 20 | 21 | or pipe input into stdin: 22 | 23 | cat myfile.json | jsonlint 24 | 25 | jsonlint will either report a syntax error with details or pretty print the source if it is valid. 26 | 27 | ### Options 28 | 29 | $ jsonlint -h 30 | 31 | Usage: jsonlint [file] [options] 32 | 33 | file file to parse; otherwise uses stdin 34 | 35 | Options: 36 | -v, --version print version and exit 37 | -s, --sort-keys sort object keys 38 | -i, --in-place overwrite the file 39 | -t CHAR, --indent CHAR character(s) to use for indentation [ ] 40 | -c, --compact compact error display 41 | -V, --validate a JSON schema to use for validation 42 | -e, --environment which specification of JSON Schema the validation file uses [json-schema-draft-03] 43 | -q, --quiet do not print the parsed json to STDOUT [false] 44 | -p, --pretty-print force pretty printing even if invalid 45 | 46 | 47 | ## Module interface 48 | 49 | var jsonlint = require("jsonlint-mod"); 50 | 51 | jsonlint.parse('{"creative?": false}'); 52 | 53 | It returns the parsed object or throws an `Error`. 54 | 55 | ## Vim Plugins 56 | 57 | * [Syntastic](http://www.vim.org/scripts/script.php?script_id=2736) 58 | * [sourcebeautify](http://www.vim.org/scripts/script.php?script_id=4079) 59 | 60 | ## MIT License 61 | 62 | Copyright (C) 2012 Zachary Carter 63 | 64 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 65 | 66 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 67 | 68 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 69 | -------------------------------------------------------------------------------- /lib/formatter.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | /** 4 | * Manual formatter taken straight from https://github.com/umbrae/jsonlintdotcom 5 | **/ 6 | 7 | /*jslint white: true, devel: true, onevar: true, browser: true, undef: true, nomen: true, regexp: true, plusplus: false, bitwise: true, newcap: true, maxerr: 50, indent: 4 */ 8 | 9 | /** 10 | * jsl.format - Provide json reformatting in a character-by-character approach, so that even invalid JSON may be reformatted (to the best of its ability). 11 | * 12 | **/ 13 | var formatter = (function () { 14 | 15 | function repeat(s, count) { 16 | return new Array(count + 1).join(s); 17 | } 18 | 19 | function formatJson(json, indentChars) { 20 | var i = 0, 21 | il = 0, 22 | tab = (typeof indentChars !== "undefined") ? indentChars : " ", 23 | newJson = "", 24 | indentLevel = 0, 25 | inString = false, 26 | currentChar = null; 27 | 28 | for (i = 0, il = json.length; i < il; i += 1) { 29 | currentChar = json.charAt(i); 30 | 31 | switch (currentChar) { 32 | case '{': 33 | case '[': 34 | if (!inString) { 35 | newJson += currentChar + "\n" + repeat(tab, indentLevel + 1); 36 | indentLevel += 1; 37 | } else { 38 | newJson += currentChar; 39 | } 40 | break; 41 | case '}': 42 | case ']': 43 | if (!inString) { 44 | indentLevel -= 1; 45 | newJson += "\n" + repeat(tab, indentLevel) + currentChar; 46 | } else { 47 | newJson += currentChar; 48 | } 49 | break; 50 | case ',': 51 | if (!inString) { 52 | newJson += ",\n" + repeat(tab, indentLevel); 53 | } else { 54 | newJson += currentChar; 55 | } 56 | break; 57 | case ':': 58 | if (!inString) { 59 | newJson += ": "; 60 | } else { 61 | newJson += currentChar; 62 | } 63 | break; 64 | case ' ': 65 | case "\n": 66 | case "\t": 67 | if (inString) { 68 | newJson += currentChar; 69 | } 70 | break; 71 | case '"': 72 | if (i > 0 && json.charAt(i - 1) !== '\\') { 73 | inString = !inString; 74 | } 75 | newJson += currentChar; 76 | break; 77 | default: 78 | newJson += currentChar; 79 | break; 80 | } 81 | } 82 | 83 | return newJson; 84 | } 85 | 86 | return { "formatJson": formatJson }; 87 | 88 | }()); 89 | 90 | if (typeof require !== 'undefined' && typeof exports !== 'undefined') { 91 | exports.formatter = formatter; 92 | } -------------------------------------------------------------------------------- /lib/cli.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | var fs = require("fs"); 4 | var path = require("path"); 5 | var parser = require("./jsonlint").parser; 6 | var JSV = require("JSV").JSV; 7 | var formatter = require("./formatter.js").formatter; 8 | 9 | var options = require("./nomnom") 10 | .script("jsonlint") 11 | .options({ 12 | file: { 13 | position: 0, 14 | help: "file to parse; otherwise uses stdin" 15 | }, 16 | version: { 17 | flag : true, 18 | string: '-v, --version', 19 | help: 'print version and exit', 20 | callback: function() { 21 | return require("../package").version; 22 | } 23 | }, 24 | sort : { 25 | flag : true, 26 | string: '-s, --sort-keys', 27 | help: 'sort object keys' 28 | }, 29 | inplace : { 30 | flag : true, 31 | string: '-i, --in-place', 32 | help: 'overwrite the file' 33 | }, 34 | indent : { 35 | string: '-t CHAR, --indent CHAR', 36 | "default": " ", 37 | help: 'character(s) to use for indentation' 38 | }, 39 | compact : { 40 | flag : true, 41 | string: '-c, --compact', 42 | help : 'compact error display' 43 | }, 44 | validate : { 45 | string: '-V, --validate', 46 | help : 'a JSON schema to use for validation' 47 | }, 48 | env : { 49 | string: '-e, --environment', 50 | "default": "json-schema-draft-03", 51 | help: 'which specification of JSON Schema the validation file uses' 52 | }, 53 | quiet: { 54 | flag: true, 55 | key: "value", 56 | string: '-q, --quiet', 57 | "default": false, 58 | help: 'do not print the parsed json to STDOUT' 59 | }, 60 | forcePrettyPrint: { 61 | flag: true, 62 | string: '-p, --pretty-print', 63 | help: 'force pretty printing even if invalid' 64 | } 65 | }).parse(); 66 | 67 | if (options.compact) { 68 | var fileName = options.file? options.file + ': ' : ''; 69 | parser.parseError = parser.lexer.parseError = function(str, hash) { 70 | // error from dougJSONParse 71 | if (hash.message) { 72 | console.error(fileName + 'line '+ hash.line +', col '+ hash.col +',', hash.message + '.'); 73 | } else { 74 | console.error(fileName + 'line '+ hash.loc.first_line +', col '+ hash.loc.last_column +', found: \''+ hash.token +'\' - expected: '+ hash.expected.join(', ') +'.'); 75 | } 76 | throw new Error(str); 77 | }; 78 | } 79 | 80 | function parse (source) { 81 | var parsed, 82 | formatted; 83 | 84 | try { 85 | parsed = options.sort ? 86 | sortObject(parser.parse(source)) : 87 | parser.parse(source); 88 | 89 | if (options.validate) { 90 | var env = JSV.createEnvironment(options.env); 91 | var schema = JSON.parse(fs.readFileSync(path.normalize(options.validate), "utf8")); 92 | var report = env.validate(parsed, schema); 93 | if (report.errors.length) { 94 | throw report.errors.reduce(schemaError, 'Validation Errors:'); 95 | } 96 | } 97 | 98 | return JSON.stringify(parsed, null, options.indent); 99 | } catch (e) { 100 | if (options.forcePrettyPrint) { 101 | /* From https://github.com/umbrae/jsonlintdotcom: 102 | * If we failed to validate, run our manual formatter and then re-validate so that we 103 | * can get a better line number. On a successful validate, we don't want to run our 104 | * manual formatter because the automatic one is faster and probably more reliable. 105 | */ 106 | 107 | try { 108 | formatted = formatter.formatJson(source, options.indent); 109 | // Re-parse so exception output gets better line numbers 110 | parsed = parser.parse(formatted); 111 | } catch (e) { 112 | if (! options.compact) { 113 | console.error(e); 114 | } 115 | // force the pretty print before exiting 116 | console.log(formatted); 117 | } 118 | } else { 119 | if (! options.compact) { 120 | console.error(e); 121 | } 122 | } 123 | process.exit(1); 124 | } 125 | } 126 | 127 | function schemaError (str, err) { 128 | return str + 129 | "\n\n"+err.message + 130 | "\nuri: " + err.uri + 131 | "\nschemaUri: " + err.schemaUri + 132 | "\nattribute: " + err.attribute + 133 | "\ndetails: " + JSON.stringify(err.details); 134 | } 135 | 136 | function main (args) { 137 | var source = ''; 138 | if (options.file) { 139 | var json = path.normalize(options.file); 140 | source = parse(fs.readFileSync(json, "utf8")); 141 | if (options.inplace) { 142 | fs.writeSync(fs.openSync(json,'w+'), source, 0, "utf8"); 143 | } else { 144 | if (! options.quiet) { console.log(source)}; 145 | } 146 | } else { 147 | var stdin = process.openStdin(); 148 | stdin.setEncoding('utf8'); 149 | 150 | stdin.on('data', function (chunk) { 151 | source += chunk.toString('utf8'); 152 | }); 153 | stdin.on('end', function () { 154 | if (! options.quiet) {console.log(parse(source))}; 155 | }); 156 | } 157 | } 158 | 159 | // from http://stackoverflow.com/questions/1359761/sorting-a-json-object-in-javascript 160 | function sortObject(o) { 161 | if (Array.isArray(o)) { 162 | return o.map(sortObject); 163 | } else if (Object.prototype.toString.call(o) !== '[object Object]') { 164 | return o; 165 | } 166 | 167 | var sorted = {}, 168 | key, a = []; 169 | 170 | for (key in o) { 171 | if (o.hasOwnProperty(key)) { 172 | a.push(key); 173 | } 174 | } 175 | 176 | a.sort(); 177 | 178 | for (key = 0; key < a.length; key++) { 179 | sorted[a[key]] = sortObject(o[a[key]]); 180 | } 181 | return sorted; 182 | } 183 | 184 | main(process.argv.slice(1)); 185 | -------------------------------------------------------------------------------- /test/all-tests.js: -------------------------------------------------------------------------------- 1 | var fs = require("fs"), 2 | assert = require("assert"), 3 | parser = require("../lib/jsonlint").parser; 4 | 5 | exports["test object"] = function () { 6 | var json = '{"foo": "bar"}'; 7 | assert.deepEqual(parser.parse(json), {"foo": "bar"}); 8 | }; 9 | 10 | exports["test escaped backslash"] = function () { 11 | var json = '{"foo": "\\\\"}'; 12 | assert.deepEqual(parser.parse(json), {"foo": "\\"}); 13 | }; 14 | 15 | exports["test escaped chars"] = function () { 16 | var json = '{"foo": "\\\\\\\""}'; 17 | assert.deepEqual(parser.parse(json), {"foo": '\\\"'}); 18 | }; 19 | 20 | exports["test escaped \\n"] = function () { 21 | var json = '{"foo": "\\\\\\n"}'; 22 | assert.deepEqual(parser.parse(json), {"foo": '\\\n'}); 23 | }; 24 | 25 | exports["test string with escaped line break"] = function () { 26 | var json = '{"foo": "bar\\nbar"}'; 27 | assert.deepEqual(parser.parse(json), {"foo": "bar\nbar"}); 28 | assert.equal(JSON.stringify(parser.parse(json)).length, 18); 29 | }; 30 | 31 | exports["test string with line break"] = function () { 32 | var json = '{"foo": "bar\nbar"}'; 33 | assert["throws"](function () {parser.parse(json)}, "should throw error"); 34 | }; 35 | 36 | exports["test string literal"] = function () { 37 | var json = '"foo"'; 38 | assert.equal(parser.parse(json), "foo"); 39 | }; 40 | 41 | exports["test number literal"] = function () { 42 | var json = '1234'; 43 | assert.equal(parser.parse(json), 1234); 44 | }; 45 | 46 | exports["test null literal"] = function () { 47 | var json = '1234'; 48 | assert.equal(parser.parse(json), 1234); 49 | }; 50 | 51 | exports["test boolean literal"] = function () { 52 | var json = 'true'; 53 | assert.equal(parser.parse(json), true); 54 | }; 55 | 56 | exports["test unclosed array"] = function () { 57 | var json = fs.readFileSync(__dirname + "/fails/2.json").toString(); 58 | assert["throws"](function () {parser.parse(json)}, "should throw error"); 59 | }; 60 | 61 | exports["test unquotedkey keys must be quoted"] = function () { 62 | var json = fs.readFileSync(__dirname + "/fails/3.json").toString(); 63 | assert["throws"](function () {parser.parse(json)}, "should throw error"); 64 | }; 65 | 66 | exports["test extra comma"] = function () { 67 | var json = fs.readFileSync(__dirname + "/fails/4.json").toString(); 68 | assert["throws"](function () {parser.parse(json)}, "should throw error"); 69 | }; 70 | 71 | exports["test double extra comma"] = function () { 72 | var json = fs.readFileSync(__dirname + "/fails/5.json").toString(); 73 | assert["throws"](function () {parser.parse(json)}, "should throw error"); 74 | }; 75 | 76 | exports["test missing value"] = function () { 77 | var json = fs.readFileSync(__dirname + "/fails/6.json").toString(); 78 | assert["throws"](function () {parser.parse(json)}, "should throw error"); 79 | }; 80 | 81 | exports["test comma after the close"] = function () { 82 | var json = fs.readFileSync(__dirname + "/fails/7.json").toString(); 83 | assert["throws"](function () {parser.parse(json)}, "should throw error"); 84 | }; 85 | 86 | exports["test extra close"] = function () { 87 | var json = fs.readFileSync(__dirname + "/fails/8.json").toString(); 88 | assert["throws"](function () {parser.parse(json)}, "should throw error"); 89 | }; 90 | 91 | exports["test extra comma after value"] = function () { 92 | var json = fs.readFileSync(__dirname + "/fails/9.json").toString(); 93 | assert["throws"](function () {parser.parse(json)}, "should throw error"); 94 | }; 95 | 96 | exports["test extra value after close with misplaced quotes"] = function () { 97 | var json = fs.readFileSync(__dirname + "/fails/10.json").toString(); 98 | assert["throws"](function () {parser.parse(json)}, "should throw error"); 99 | }; 100 | 101 | exports["test illegal expression addition"] = function () { 102 | var json = fs.readFileSync(__dirname + "/fails/11.json").toString(); 103 | assert["throws"](function () {parser.parse(json)}, "should throw error"); 104 | }; 105 | 106 | exports["test illegal invocation of alert"] = function () { 107 | var json = fs.readFileSync(__dirname + "/fails/12.json").toString(); 108 | assert["throws"](function () {parser.parse(json)}, "should throw error"); 109 | }; 110 | 111 | exports["test numbers cannot have leading zeroes"] = function () { 112 | var json = fs.readFileSync(__dirname + "/fails/13.json").toString(); 113 | assert["throws"](function () {parser.parse(json)}, "should throw error"); 114 | }; 115 | 116 | exports["test numbers cannot be hex"] = function () { 117 | var json = fs.readFileSync(__dirname + "/fails/14.json").toString(); 118 | assert["throws"](function () {parser.parse(json)}, "should throw error"); 119 | }; 120 | 121 | exports["test illegal backslash escape \\0"] = function () { 122 | var json = fs.readFileSync(__dirname + "/fails/15.json").toString(); 123 | assert["throws"](function () {parser.parse(json)}, "should throw error"); 124 | }; 125 | 126 | exports["test unquoted text"] = function () { 127 | var json = fs.readFileSync(__dirname + "/fails/16.json").toString(); 128 | assert["throws"](function () {parser.parse(json)}, "should throw error"); 129 | }; 130 | 131 | exports["test illegal backslash escape \\x"] = function () { 132 | var json = fs.readFileSync(__dirname + "/fails/17.json").toString(); 133 | assert["throws"](function () {parser.parse(json)}, "should throw error"); 134 | }; 135 | 136 | exports["test missing colon"] = function () { 137 | var json = fs.readFileSync(__dirname + "/fails/19.json") 138 | assert["throws"](function () {parser.parse(json)}, "should throw error"); 139 | }; 140 | 141 | exports["test double colon"] = function () { 142 | var json = fs.readFileSync(__dirname + "/fails/20.json").toString(); 143 | assert["throws"](function () {parser.parse(json)}, "should throw error"); 144 | }; 145 | 146 | exports["test comma instead of colon"] = function () { 147 | var json = fs.readFileSync(__dirname + "/fails/21.json").toString(); 148 | assert["throws"](function () {parser.parse(json)}, "should throw error"); 149 | }; 150 | 151 | exports["test colon instead of comma"] = function () { 152 | var json = fs.readFileSync(__dirname + "/fails/22.json").toString(); 153 | assert["throws"](function () {parser.parse(json)}, "should throw error"); 154 | }; 155 | 156 | exports["test bad raw value"] = function () { 157 | var json = fs.readFileSync(__dirname + "/fails/23.json").toString(); 158 | assert["throws"](function () {parser.parse(json)}, "should throw error"); 159 | }; 160 | 161 | exports["test single quotes"] = function () { 162 | var json = fs.readFileSync(__dirname + "/fails/24.json").toString(); 163 | assert["throws"](function () {parser.parse(json)}, "should throw error"); 164 | }; 165 | 166 | exports["test tab character in string"] = function () { 167 | var json = fs.readFileSync(__dirname + "/fails/25.json").toString(); 168 | assert["throws"](function () {parser.parse(json)}, "should throw error"); 169 | }; 170 | 171 | exports["test tab character in string 2"] = function () { 172 | var json = fs.readFileSync(__dirname + "/fails/26.json").toString(); 173 | assert["throws"](function () {parser.parse(json)}, "should throw error"); 174 | }; 175 | 176 | exports["test line break in string"] = function () { 177 | var json = fs.readFileSync(__dirname + "/fails/27.json").toString(); 178 | assert["throws"](function () {parser.parse(json)}, "should throw error"); 179 | }; 180 | 181 | exports["test line break in string in array"] = function () { 182 | var json = fs.readFileSync(__dirname + "/fails/28.json").toString(); 183 | assert["throws"](function () {parser.parse(json)}, "should throw error"); 184 | }; 185 | 186 | exports["test 0e"] = function () { 187 | var json = fs.readFileSync(__dirname + "/fails/29.json").toString(); 188 | assert["throws"](function () {parser.parse(json)}, "should throw error"); 189 | }; 190 | 191 | exports["test 0e+"] = function () { 192 | var json = fs.readFileSync(__dirname + "/fails/30.json").toString(); 193 | assert["throws"](function () {parser.parse(json)}, "should throw error"); 194 | }; 195 | 196 | exports["test 0e+ 1"] = function () { 197 | var json = fs.readFileSync(__dirname + "/fails/31.json").toString(); 198 | assert["throws"](function () {parser.parse(json)}, "should throw error"); 199 | }; 200 | 201 | exports["test comma instead of closing brace"] = function () { 202 | var json = fs.readFileSync(__dirname + "/fails/32.json").toString(); 203 | assert["throws"](function () {parser.parse(json)}, "should throw error"); 204 | }; 205 | 206 | exports["test bracket mismatch"] = function () { 207 | var json = fs.readFileSync(__dirname + "/fails/33.json").toString(); 208 | assert["throws"](function () {parser.parse(json)}, "should throw error"); 209 | } 210 | 211 | exports["test extra brace"] = function () { 212 | var json = fs.readFileSync(__dirname + "/fails/34.json").toString(); 213 | assert["throws"](function () {parser.parse(json)}, "should throw error"); 214 | } 215 | 216 | exports["test hidden characters"] = function () { 217 | var json = fs.readFileSync(__dirname + "/fails/35.json").toString(); 218 | assert["throws"](function () {parser.parse(json)}, "should throw error"); 219 | } 220 | 221 | exports["test key duplicates"] = function () { 222 | var json = fs.readFileSync(__dirname + "/fails/36.json").toString(); 223 | assert["throws"](function () {parser.parse(json)}, "should throw error"); 224 | } 225 | 226 | exports["test pass-1"] = function () { 227 | var json = fs.readFileSync(__dirname + "/passes/1.json").toString(); 228 | assert.doesNotThrow(function () {parser.parse(json)}, "should pass"); 229 | } 230 | 231 | exports["test pass-2"] = function () { 232 | var json = fs.readFileSync(__dirname + "/passes/2.json").toString(); 233 | assert.doesNotThrow(function () {parser.parse(json)}, "should pass"); 234 | } 235 | 236 | exports["test pass-3"] = function () { 237 | var json = fs.readFileSync(__dirname + "/passes/3.json").toString(); 238 | assert.doesNotThrow(function () {parser.parse(json)}, "should pass"); 239 | } 240 | 241 | if (require.main === module) 242 | require("test").run(exports); 243 | -------------------------------------------------------------------------------- /lib/doug-json-parse.js: -------------------------------------------------------------------------------- 1 | /* 2 | json_parse.js 3 | 2016-05-02 4 | 5 | Public Domain. 6 | 7 | NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK. 8 | 9 | This file creates a json_parse function. 10 | 11 | json_parse(text, reviver) 12 | This method parses a JSON text to produce an object or array. 13 | It can throw a SyntaxError exception. 14 | 15 | The optional reviver parameter is a function that can filter and 16 | transform the results. It receives each of the keys and values, 17 | and its return value is used instead of the original value. 18 | If it returns what it received, then the structure is not modified. 19 | If it returns undefined then the member is deleted. 20 | 21 | Example: 22 | 23 | // Parse the text. Values that look like ISO date strings will 24 | // be converted to Date objects. 25 | 26 | myData = json_parse(text, function (key, value) { 27 | var a; 28 | if (typeof value === "string") { 29 | a = 30 | /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)Z$/.exec(value); 31 | if (a) { 32 | return new Date(Date.UTC(+a[1], +a[2] - 1, +a[3], +a[4], 33 | +a[5], +a[6])); 34 | } 35 | } 36 | return value; 37 | }); 38 | 39 | This is a reference implementation. You are free to copy, modify, or 40 | redistribute. 41 | 42 | This code should be minified before deployment. 43 | See http://javascript.crockford.com/jsmin.html 44 | 45 | USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD CODE FROM SERVERS YOU DO 46 | NOT CONTROL. 47 | */ 48 | 49 | /*jslint for */ 50 | 51 | /*property 52 | at, b, call, charAt, f, fromCharCode, hasOwnProperty, message, n, name, 53 | prototype, push, r, t, text 54 | */ 55 | 56 | var ___dougJSONParse = (function () { 57 | "use strict"; 58 | 59 | // This is a function that can parse a JSON text, producing a JavaScript 60 | // data structure. It is a simple, recursive descent parser. It does not use 61 | // eval or regular expressions, so it can be used as a model for implementing 62 | // a JSON parser in other languages. 63 | 64 | // We are defining the function inside of another function to avoid creating 65 | // global variables. 66 | 67 | var at; // The index of the current character 68 | var ch; // The current character 69 | var escapee = { 70 | "\"": "\"", 71 | "\\": "\\", 72 | "/": "/", 73 | b: "\b", 74 | f: "\f", 75 | n: "\n", 76 | r: "\r", 77 | t: "\t" 78 | }; 79 | var text; 80 | 81 | var error = function (m) { 82 | 83 | // Call error when something is wrong. 84 | 85 | throw { 86 | name: "SyntaxError", 87 | message: m, 88 | at: at, 89 | text: text 90 | }; 91 | }; 92 | 93 | var next = function (c) { 94 | 95 | // If a c parameter is provided, verify that it matches the current character. 96 | 97 | if (c && c !== ch) { 98 | error("Expected '" + c + "' instead of '" + ch + "'"); 99 | } 100 | 101 | // Get the next character. When there are no more characters, 102 | // return the empty string. 103 | 104 | ch = text.charAt(at); 105 | at += 1; 106 | return ch; 107 | }; 108 | 109 | var number = function () { 110 | 111 | // Parse a number value. 112 | 113 | var value; 114 | var string = ""; 115 | 116 | if (ch === "-") { 117 | string = "-"; 118 | next("-"); 119 | } 120 | while (ch >= "0" && ch <= "9") { 121 | string += ch; 122 | next(); 123 | } 124 | if (ch === ".") { 125 | string += "."; 126 | while (next() && ch >= "0" && ch <= "9") { 127 | string += ch; 128 | } 129 | } 130 | if (ch === "e" || ch === "E") { 131 | string += ch; 132 | next(); 133 | if (ch === "-" || ch === "+") { 134 | string += ch; 135 | next(); 136 | } 137 | while (ch >= "0" && ch <= "9") { 138 | string += ch; 139 | next(); 140 | } 141 | } 142 | value = +string; 143 | if (!isFinite(value)) { 144 | error("Bad number"); 145 | } else { 146 | return value; 147 | } 148 | }; 149 | 150 | var string = function () { 151 | 152 | // Parse a string value. 153 | 154 | var hex; 155 | var i; 156 | var value = ""; 157 | var uffff; 158 | 159 | // When parsing for string values, we must look for " and \ characters. 160 | 161 | if (ch === "\"") { 162 | while (next()) { 163 | if (ch === "\"") { 164 | next(); 165 | return value; 166 | } 167 | if (ch === "\\") { 168 | next(); 169 | if (ch === "u") { 170 | uffff = 0; 171 | for (i = 0; i < 4; i += 1) { 172 | hex = parseInt(next(), 16); 173 | if (!isFinite(hex)) { 174 | break; 175 | } 176 | uffff = uffff * 16 + hex; 177 | } 178 | value += String.fromCharCode(uffff); 179 | } else if (typeof escapee[ch] === "string") { 180 | value += escapee[ch]; 181 | } else { 182 | break; 183 | } 184 | } else { 185 | value += ch; 186 | } 187 | } 188 | } 189 | error("Bad string"); 190 | }; 191 | 192 | var white = function () { 193 | 194 | // Skip whitespace. 195 | 196 | while (ch && ch <= " ") { 197 | next(); 198 | } 199 | }; 200 | 201 | var word = function () { 202 | 203 | // true, false, or null. 204 | 205 | switch (ch) { 206 | case "t": 207 | next("t"); 208 | next("r"); 209 | next("u"); 210 | next("e"); 211 | return true; 212 | case "f": 213 | next("f"); 214 | next("a"); 215 | next("l"); 216 | next("s"); 217 | next("e"); 218 | return false; 219 | case "n": 220 | next("n"); 221 | next("u"); 222 | next("l"); 223 | next("l"); 224 | return null; 225 | } 226 | error("Unexpected '" + ch + "'"); 227 | }; 228 | 229 | var value; // Place holder for the value function. 230 | 231 | var array = function () { 232 | 233 | // Parse an array value. 234 | 235 | var arr = []; 236 | 237 | if (ch === "[") { 238 | next("["); 239 | white(); 240 | if (ch === "]") { 241 | next("]"); 242 | return arr; // empty array 243 | } 244 | while (ch) { 245 | arr.push(value()); 246 | white(); 247 | if (ch === "]") { 248 | next("]"); 249 | return arr; 250 | } 251 | next(","); 252 | white(); 253 | } 254 | } 255 | error("Bad array"); 256 | }; 257 | 258 | var object = function () { 259 | 260 | // Parse an object value. 261 | 262 | var key; 263 | var obj = {}; 264 | 265 | if (ch === "{") { 266 | next("{"); 267 | white(); 268 | if (ch === "}") { 269 | next("}"); 270 | return obj; // empty object 271 | } 272 | while (ch) { 273 | key = string(); 274 | white(); 275 | next(":"); 276 | if (Object.hasOwnProperty.call(obj, key)) { 277 | error("Duplicate key '" + key + "'"); 278 | } 279 | obj[key] = value(); 280 | white(); 281 | if (ch === "}") { 282 | next("}"); 283 | return obj; 284 | } 285 | next(","); 286 | white(); 287 | } 288 | } 289 | error("Bad object"); 290 | }; 291 | 292 | value = function () { 293 | 294 | // Parse a JSON value. It could be an object, an array, a string, a number, 295 | // or a word. 296 | 297 | white(); 298 | switch (ch) { 299 | case "{": 300 | return object(); 301 | case "[": 302 | return array(); 303 | case "\"": 304 | return string(); 305 | case "-": 306 | return number(); 307 | default: 308 | return (ch >= "0" && ch <= "9") 309 | ? number() 310 | : word(); 311 | } 312 | }; 313 | 314 | // Return the json_parse function. It will have access to all of the above 315 | // functions and variables. 316 | 317 | return function (source, reviver) { 318 | var result; 319 | 320 | text = source; 321 | at = 0; 322 | ch = " "; 323 | result = value(); 324 | white(); 325 | if (ch) { 326 | error("Syntax error"); 327 | } 328 | 329 | // If there is a reviver function, we recursively walk the new structure, 330 | // passing each name/value pair to the reviver function for possible 331 | // transformation, starting with a temporary root object that holds the result 332 | // in an empty key. If there is not a reviver function, we simply return the 333 | // result. 334 | 335 | return (typeof reviver === "function") 336 | ? (function walk(holder, key) { 337 | var k; 338 | var v; 339 | var val = holder[key]; 340 | if (val && typeof val === "object") { 341 | for (k in val) { 342 | if (Object.prototype.hasOwnProperty.call(val, k)) { 343 | v = walk(val, k); 344 | if (v !== undefined) { 345 | val[k] = v; 346 | } else { 347 | delete val[k]; 348 | } 349 | } 350 | } 351 | } 352 | return reviver.call(holder, key, val); 353 | }({"": result}, "")) 354 | : result; 355 | }; 356 | }()); 357 | 358 | if(typeof module === 'object' && module.exports) { 359 | module.exports = ___dougJSONParse; 360 | } 361 | -------------------------------------------------------------------------------- /web/json2.js: -------------------------------------------------------------------------------- 1 | 2 | /*jslint evil: true, strict: false */ 3 | 4 | /*members "", "\b", "\t", "\n", "\f", "\r", "\"", JSON, "\\", apply, 5 | call, charCodeAt, getUTCDate, getUTCFullYear, getUTCHours, 6 | getUTCMinutes, getUTCMonth, getUTCSeconds, hasOwnProperty, join, 7 | lastIndex, length, parse, prototype, push, replace, slice, stringify, 8 | test, toJSON, toString, valueOf 9 | */ 10 | 11 | 12 | // Create a JSON object only if one does not already exist. We create the 13 | // methods in a closure to avoid creating global variables. 14 | 15 | if (!this.JSON) { 16 | this.JSON = {}; 17 | } 18 | 19 | (function () { 20 | 21 | function f(n) { 22 | // Format integers to have at least two digits. 23 | return n < 10 ? '0' + n : n; 24 | } 25 | 26 | if (typeof Date.prototype.toJSON !== 'function') { 27 | 28 | Date.prototype.toJSON = function (key) { 29 | 30 | return isFinite(this.valueOf()) ? 31 | this.getUTCFullYear() + '-' + 32 | f(this.getUTCMonth() + 1) + '-' + 33 | f(this.getUTCDate()) + 'T' + 34 | f(this.getUTCHours()) + ':' + 35 | f(this.getUTCMinutes()) + ':' + 36 | f(this.getUTCSeconds()) + 'Z' : null; 37 | }; 38 | 39 | String.prototype.toJSON = 40 | Number.prototype.toJSON = 41 | Boolean.prototype.toJSON = function (key) { 42 | return this.valueOf(); 43 | }; 44 | } 45 | 46 | var cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g, 47 | escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g, 48 | gap, 49 | indent, 50 | meta = { // table of character substitutions 51 | '\b': '\\b', 52 | '\t': '\\t', 53 | '\n': '\\n', 54 | '\f': '\\f', 55 | '\r': '\\r', 56 | '"' : '\\"', 57 | '\\': '\\\\' 58 | }, 59 | rep; 60 | 61 | 62 | function quote(string) { 63 | 64 | // If the string contains no control characters, no quote characters, and no 65 | // backslash characters, then we can safely slap some quotes around it. 66 | // Otherwise we must also replace the offending characters with safe escape 67 | // sequences. 68 | 69 | escapable.lastIndex = 0; 70 | return escapable.test(string) ? 71 | '"' + string.replace(escapable, function (a) { 72 | var c = meta[a]; 73 | return typeof c === 'string' ? c : 74 | '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4); 75 | }) + '"' : 76 | '"' + string + '"'; 77 | } 78 | 79 | 80 | function str(key, holder) { 81 | 82 | // Produce a string from holder[key]. 83 | 84 | var i, // The loop counter. 85 | k, // The member key. 86 | v, // The member value. 87 | length, 88 | mind = gap, 89 | partial, 90 | value = holder[key]; 91 | 92 | // If the value has a toJSON method, call it to obtain a replacement value. 93 | 94 | if (value && typeof value === 'object' && 95 | typeof value.toJSON === 'function') { 96 | value = value.toJSON(key); 97 | } 98 | 99 | // If we were called with a replacer function, then call the replacer to 100 | // obtain a replacement value. 101 | 102 | if (typeof rep === 'function') { 103 | value = rep.call(holder, key, value); 104 | } 105 | 106 | // What happens next depends on the value's type. 107 | 108 | switch (typeof value) { 109 | case 'string': 110 | return quote(value); 111 | 112 | case 'number': 113 | 114 | // JSON numbers must be finite. Encode non-finite numbers as null. 115 | 116 | return isFinite(value) ? String(value) : 'null'; 117 | 118 | case 'boolean': 119 | case 'null': 120 | 121 | // If the value is a boolean or null, convert it to a string. Note: 122 | // typeof null does not produce 'null'. The case is included here in 123 | // the remote chance that this gets fixed someday. 124 | 125 | return String(value); 126 | 127 | // If the type is 'object', we might be dealing with an object or an array or 128 | // null. 129 | 130 | case 'object': 131 | 132 | // Due to a specification blunder in ECMAScript, typeof null is 'object', 133 | // so watch out for that case. 134 | 135 | if (!value) { 136 | return 'null'; 137 | } 138 | 139 | // Make an array to hold the partial results of stringifying this object value. 140 | 141 | gap += indent; 142 | partial = []; 143 | 144 | // Is the value an array? 145 | 146 | if (Object.prototype.toString.apply(value) === '[object Array]') { 147 | 148 | // The value is an array. Stringify every element. Use null as a placeholder 149 | // for non-JSON values. 150 | 151 | length = value.length; 152 | for (i = 0; i < length; i += 1) { 153 | partial[i] = str(i, value) || 'null'; 154 | } 155 | 156 | // Join all of the elements together, separated with commas, and wrap them in 157 | // brackets. 158 | 159 | v = partial.length === 0 ? '[]' : 160 | gap ? '[\n' + gap + 161 | partial.join(',\n' + gap) + '\n' + 162 | mind + ']' : 163 | '[' + partial.join(',') + ']'; 164 | gap = mind; 165 | return v; 166 | } 167 | 168 | // If the replacer is an array, use it to select the members to be stringified. 169 | 170 | if (rep && typeof rep === 'object') { 171 | length = rep.length; 172 | for (i = 0; i < length; i += 1) { 173 | k = rep[i]; 174 | if (typeof k === 'string') { 175 | v = str(k, value); 176 | if (v) { 177 | partial.push(quote(k) + (gap ? ': ' : ':') + v); 178 | } 179 | } 180 | } 181 | } else { 182 | 183 | // Otherwise, iterate through all of the keys in the object. 184 | 185 | for (k in value) { 186 | if (Object.hasOwnProperty.call(value, k)) { 187 | v = str(k, value); 188 | if (v) { 189 | partial.push(quote(k) + (gap ? ': ' : ':') + v); 190 | } 191 | } 192 | } 193 | } 194 | 195 | // Join all of the member texts together, separated with commas, 196 | // and wrap them in braces. 197 | 198 | v = partial.length === 0 ? '{}' : 199 | gap ? '{\n' + gap + partial.join(',\n' + gap) + '\n' + 200 | mind + '}' : '{' + partial.join(',') + '}'; 201 | gap = mind; 202 | return v; 203 | } 204 | } 205 | 206 | // If the JSON object does not yet have a stringify method, give it one. 207 | 208 | if (typeof JSON.stringify !== 'function') { 209 | JSON.stringify = function (value, replacer, space) { 210 | 211 | // The stringify method takes a value and an optional replacer, and an optional 212 | // space parameter, and returns a JSON text. The replacer can be a function 213 | // that can replace values, or an array of strings that will select the keys. 214 | // A default replacer method can be provided. Use of the space parameter can 215 | // produce text that is more easily readable. 216 | 217 | var i; 218 | gap = ''; 219 | indent = ''; 220 | 221 | // If the space parameter is a number, make an indent string containing that 222 | // many spaces. 223 | 224 | if (typeof space === 'number') { 225 | for (i = 0; i < space; i += 1) { 226 | indent += ' '; 227 | } 228 | 229 | // If the space parameter is a string, it will be used as the indent string. 230 | 231 | } else if (typeof space === 'string') { 232 | indent = space; 233 | } 234 | 235 | // If there is a replacer, it must be a function or an array. 236 | // Otherwise, throw an error. 237 | 238 | rep = replacer; 239 | if (replacer && typeof replacer !== 'function' && 240 | (typeof replacer !== 'object' || 241 | typeof replacer.length !== 'number')) { 242 | throw new Error('JSON.stringify'); 243 | } 244 | 245 | // Make a fake root object containing our value under the key of ''. 246 | // Return the result of stringifying the value. 247 | 248 | return str('', {'': value}); 249 | }; 250 | } 251 | 252 | 253 | // If the JSON object does not yet have a parse method, give it one. 254 | 255 | if (typeof JSON.parse !== 'function') { 256 | JSON.parse = function (text, reviver) { 257 | 258 | // The parse method takes a text and an optional reviver function, and returns 259 | // a JavaScript value if the text is a valid JSON text. 260 | 261 | var j; 262 | 263 | function walk(holder, key) { 264 | 265 | // The walk method is used to recursively walk the resulting structure so 266 | // that modifications can be made. 267 | 268 | var k, v, value = holder[key]; 269 | if (value && typeof value === 'object') { 270 | for (k in value) { 271 | if (Object.hasOwnProperty.call(value, k)) { 272 | v = walk(value, k); 273 | if (v !== undefined) { 274 | value[k] = v; 275 | } else { 276 | delete value[k]; 277 | } 278 | } 279 | } 280 | } 281 | return reviver.call(holder, key, value); 282 | } 283 | 284 | 285 | // Parsing happens in four stages. In the first stage, we replace certain 286 | // Unicode characters with escape sequences. JavaScript handles many characters 287 | // incorrectly, either silently deleting them, or treating them as line endings. 288 | 289 | cx.lastIndex = 0; 290 | if (cx.test(text)) { 291 | text = text.replace(cx, function (a) { 292 | return '\\u' + 293 | ('0000' + a.charCodeAt(0).toString(16)).slice(-4); 294 | }); 295 | } 296 | 297 | // In the second stage, we run the text against regular expressions that look 298 | // for non-JSON patterns. We are especially concerned with '()' and 'new' 299 | // because they can cause invocation, and '=' because it can cause mutation. 300 | // But just to be safe, we want to reject all unexpected forms. 301 | 302 | // We split the second stage into 4 regexp operations in order to work around 303 | // crippling inefficiencies in IE's and Safari's regexp engines. First we 304 | // replace the JSON backslash pairs with '@' (a non-JSON character). Second, we 305 | // replace all simple value tokens with ']' characters. Third, we delete all 306 | // open brackets that follow a colon or comma or that begin the text. Finally, 307 | // we look to see that the remaining characters are only whitespace or ']' or 308 | // ',' or ':' or '{' or '}'. If that is so, then the text is safe for eval. 309 | 310 | if (/^[\],:{}\s]*$/. 311 | test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@'). 312 | replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']'). 313 | replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) { 314 | 315 | // In the third stage we use the eval function to compile the text into a 316 | // JavaScript structure. The '{' operator is subject to a syntactic ambiguity 317 | // in JavaScript: it can begin a block or an object literal. We wrap the text 318 | // in parens to eliminate the ambiguity. 319 | 320 | j = eval('(' + text + ')'); 321 | 322 | // In the optional fourth stage, we recursively walk the new structure, passing 323 | // each name/value pair to a reviver function for possible transformation. 324 | 325 | return typeof reviver === 'function' ? 326 | walk({'': j}, '') : j; 327 | } 328 | 329 | // If the text is not JSON parseable, then a SyntaxError is thrown. 330 | 331 | throw new SyntaxError('JSON.parse'); 332 | }; 333 | } 334 | }()); 335 | -------------------------------------------------------------------------------- /lib/nomnom.js: -------------------------------------------------------------------------------- 1 | /* 2 | nomnom is an option parser for node. It noms your args and gives them back to you in a hash. 3 | https://github.com/harthur/nomnom 4 | 5 | 6 | Copyright (c) 2010 Heather Arthur 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining 9 | a copy of this software and associated documentation files (the 10 | "Software"), to deal in the Software without restriction, including 11 | without limitation the rights to use, copy, modify, merge, publish, 12 | distribute, sublicense, and/or sell copies of the Software, and to 13 | permit persons to whom the Software is furnished to do so, subject to 14 | the following conditions: 15 | 16 | The above copyright notice and this permission notice shall be 17 | included in all copies or substantial portions of the Software. 18 | 19 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 20 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 21 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 22 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 23 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 24 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 25 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 26 | */ 27 | 28 | var _ = require("underscore"), chalk = require('chalk'); 29 | 30 | 31 | function ArgParser() { 32 | this.commands = {}; // expected commands 33 | this.specs = {}; // option specifications 34 | } 35 | 36 | ArgParser.prototype = { 37 | /* Add a command to the expected commands */ 38 | command : function(name) { 39 | var command; 40 | if (name) { 41 | command = this.commands[name] = { 42 | name: name, 43 | specs: {} 44 | }; 45 | } 46 | else { 47 | command = this.fallback = { 48 | specs: {} 49 | }; 50 | } 51 | 52 | // facilitates command('name').options().cb().help() 53 | var chain = { 54 | options : function(specs) { 55 | command.specs = specs; 56 | return chain; 57 | }, 58 | opts : function(specs) { 59 | // old API 60 | return this.options(specs); 61 | }, 62 | option : function(name, spec) { 63 | command.specs[name] = spec; 64 | return chain; 65 | }, 66 | callback : function(cb) { 67 | command.cb = cb; 68 | return chain; 69 | }, 70 | help : function(help) { 71 | command.help = help; 72 | return chain; 73 | }, 74 | usage : function(usage) { 75 | command._usage = usage; 76 | return chain; 77 | } 78 | }; 79 | return chain; 80 | }, 81 | 82 | nocommand : function() { 83 | return this.command(); 84 | }, 85 | 86 | options : function(specs) { 87 | this.specs = specs; 88 | return this; 89 | }, 90 | 91 | opts : function(specs) { 92 | // old API 93 | return this.options(specs); 94 | }, 95 | 96 | globalOpts : function(specs) { 97 | // old API 98 | return this.options(specs); 99 | }, 100 | 101 | option : function(name, spec) { 102 | this.specs[name] = spec; 103 | return this; 104 | }, 105 | 106 | usage : function(usage) { 107 | this._usage = usage; 108 | return this; 109 | }, 110 | 111 | printer : function(print) { 112 | this.print = print; 113 | return this; 114 | }, 115 | 116 | script : function(script) { 117 | this._script = script; 118 | return this; 119 | }, 120 | 121 | scriptName : function(script) { 122 | // old API 123 | return this.script(script); 124 | }, 125 | 126 | help : function(help) { 127 | this._help = help; 128 | return this; 129 | }, 130 | 131 | colors: function() { 132 | // deprecated - colors are on by default now 133 | return this; 134 | }, 135 | 136 | nocolors : function() { 137 | this._nocolors = true; 138 | return this; 139 | }, 140 | 141 | parseArgs : function(argv) { 142 | // old API 143 | return this.parse(argv); 144 | }, 145 | 146 | nom : function(argv) { 147 | return this.parse(argv); 148 | }, 149 | 150 | parse : function(argv) { 151 | this.print = this.print || function(str, code) { 152 | console.log(str); 153 | process.exit(code || 0); 154 | }; 155 | this._help = this._help || ""; 156 | this._script = this._script || process.argv[0] + " " 157 | + require('path').basename(process.argv[1]); 158 | this.specs = this.specs || {}; 159 | 160 | var argv = argv || process.argv.slice(2); 161 | 162 | var arg = Arg(argv[0]).isValue && argv[0], 163 | command = arg && this.commands[arg], 164 | commandExpected = !_(this.commands).isEmpty(); 165 | 166 | if (commandExpected) { 167 | if (command) { 168 | _(this.specs).extend(command.specs); 169 | this._script += " " + command.name; 170 | if (command.help) { 171 | this._help = command.help; 172 | } 173 | this.command = command; 174 | } 175 | else if (arg) { 176 | return this.print(this._script + ": no such command '" + arg + "'", 1); 177 | } 178 | else { 179 | // no command but command expected e.g. 'git -v' 180 | var helpStringBuilder = { 181 | list : function() { 182 | return 'one of: ' + _(this.commands).keys().join(", "); 183 | }, 184 | twoColumn : function() { 185 | // find the longest command name to ensure horizontal alignment 186 | var maxLength = _(this.commands).max(function (cmd) { 187 | return cmd.name.length; 188 | }).name.length; 189 | 190 | // create the two column text strings 191 | var cmdHelp = _.map(this.commands, function(cmd, name) { 192 | var diff = maxLength - name.length; 193 | var pad = new Array(diff + 4).join(" "); 194 | return " " + [ name, pad, cmd.help ].join(" "); 195 | }); 196 | return "\n" + cmdHelp.join("\n"); 197 | } 198 | }; 199 | 200 | // if there are a small number of commands and all have help strings, 201 | // display them in a two column table; otherwise use the brief version. 202 | // The arbitrary choice of "20" comes from the number commands git 203 | // displays as "common commands" 204 | var helpType = 'list'; 205 | if (_(this.commands).size() <= 20) { 206 | if (_(this.commands).every(function (cmd) { return cmd.help; })) { 207 | helpType = 'twoColumn'; 208 | } 209 | } 210 | 211 | this.specs.command = { 212 | position: 0, 213 | help: helpStringBuilder[helpType].call(this) 214 | } 215 | 216 | if (this.fallback) { 217 | _(this.specs).extend(this.fallback.specs); 218 | this._help = this.fallback.help; 219 | } else { 220 | this.specs.command.required = true; 221 | } 222 | } 223 | } 224 | 225 | if (this.specs.length === undefined) { 226 | // specs is a hash not an array 227 | this.specs = _(this.specs).map(function(opt, name) { 228 | opt.name = name; 229 | return opt; 230 | }); 231 | } 232 | this.specs = this.specs.map(function(opt) { 233 | return Opt(opt); 234 | }); 235 | 236 | if (argv.indexOf("--help") >= 0 || argv.indexOf("-h") >= 0) { 237 | return this.print(this.getUsage()); 238 | } 239 | 240 | var options = {}; 241 | var args = argv.map(function(arg) { 242 | return Arg(arg); 243 | }) 244 | .concat(Arg()); 245 | 246 | var positionals = []; 247 | 248 | /* parse the args */ 249 | var that = this; 250 | args.reduce(function(arg, val) { 251 | /* positional */ 252 | if (arg.isValue) { 253 | positionals.push(arg.value); 254 | } 255 | else if (arg.chars) { 256 | var last = arg.chars.pop(); 257 | 258 | /* -cfv */ 259 | (arg.chars).forEach(function(ch) { 260 | that.setOption(options, ch, true); 261 | }); 262 | 263 | /* -v key */ 264 | if (!that.opt(last).flag) { 265 | if (val.isValue) { 266 | that.setOption(options, last, val.value); 267 | return Arg(); // skip next turn - swallow arg 268 | } 269 | else { 270 | that.print("'-" + (that.opt(last).name || last) + "'" 271 | + " expects a value\n\n" + that.getUsage(), 1); 272 | } 273 | } 274 | else { 275 | /* -v */ 276 | that.setOption(options, last, true); 277 | } 278 | 279 | } 280 | else if (arg.full) { 281 | var value = arg.value; 282 | 283 | /* --key */ 284 | if (value === undefined) { 285 | /* --key value */ 286 | if (!that.opt(arg.full).flag) { 287 | if (val.isValue) { 288 | that.setOption(options, arg.full, val.value); 289 | return Arg(); 290 | } 291 | else { 292 | that.print("'--" + (that.opt(arg.full).name || arg.full) + "'" 293 | + " expects a value\n\n" + that.getUsage(), 1); 294 | } 295 | } 296 | else { 297 | /* --flag */ 298 | value = true; 299 | } 300 | } 301 | that.setOption(options, arg.full, value); 302 | } 303 | return val; 304 | }); 305 | 306 | positionals.forEach(function(pos, index) { 307 | this.setOption(options, index, pos); 308 | }, this); 309 | 310 | options._ = positionals; 311 | 312 | this.specs.forEach(function(opt) { 313 | if (opt.default !== undefined && options[opt.name] === undefined) { 314 | options[opt.name] = opt.default; 315 | } 316 | }, this); 317 | 318 | // exit if required arg isn't present 319 | this.specs.forEach(function(opt) { 320 | if (opt.required && options[opt.name] === undefined) { 321 | var msg = opt.name + " argument is required"; 322 | msg = this._nocolors ? msg : chalk.red(msg); 323 | 324 | this.print("\n" + msg + "\n" + this.getUsage(), 1); 325 | } 326 | }, this); 327 | 328 | if (command && command.cb) { 329 | command.cb(options); 330 | } 331 | else if (this.fallback && this.fallback.cb) { 332 | this.fallback.cb(options); 333 | } 334 | 335 | return options; 336 | }, 337 | 338 | getUsage : function() { 339 | if (this.command && this.command._usage) { 340 | return this.command._usage; 341 | } 342 | else if (this.fallback && this.fallback._usage) { 343 | return this.fallback._usage; 344 | } 345 | if (this._usage) { 346 | return this._usage; 347 | } 348 | 349 | // todo: use a template 350 | var str = "\n" 351 | if (!this._nocolors) { 352 | str += chalk.bold("Usage:"); 353 | } 354 | else { 355 | str += "Usage:"; 356 | } 357 | str += " " + this._script; 358 | 359 | var positionals = _(this.specs).select(function(opt) { 360 | return opt.position != undefined; 361 | }) 362 | positionals = _(positionals).sortBy(function(opt) { 363 | return opt.position; 364 | }); 365 | var options = _(this.specs).select(function(opt) { 366 | return opt.position === undefined; 367 | }); 368 | 369 | // assume there are no gaps in the specified pos. args 370 | positionals.forEach(function(pos) { 371 | str += " "; 372 | var posStr = pos.string; 373 | if (!posStr) { 374 | posStr = pos.name || "arg" + pos.position; 375 | if (pos.required) { 376 | posStr = "<" + posStr + ">"; 377 | } else { 378 | posStr = "[" + posStr + "]"; 379 | } 380 | if (pos.list) { 381 | posStr += "..."; 382 | } 383 | } 384 | str += posStr; 385 | }); 386 | 387 | if (options.length) { 388 | if (!this._nocolors) { 389 | // must be a better way to do this 390 | str += chalk.blue(" [options]"); 391 | } 392 | else { 393 | str += " [options]"; 394 | } 395 | } 396 | 397 | if (options.length || positionals.length) { 398 | str += "\n\n"; 399 | } 400 | 401 | function spaces(length) { 402 | var spaces = ""; 403 | for (var i = 0; i < length; i++) { 404 | spaces += " "; 405 | } 406 | return spaces; 407 | } 408 | var longest = positionals.reduce(function(max, pos) { 409 | return pos.name.length > max ? pos.name.length : max; 410 | }, 0); 411 | 412 | positionals.forEach(function(pos) { 413 | var posStr = pos.string || pos.name; 414 | str += posStr + spaces(longest - posStr.length) + " "; 415 | if (!this._nocolors) { 416 | str += chalk.grey(pos.help || "") 417 | } 418 | else { 419 | str += (pos.help || "") 420 | } 421 | str += "\n"; 422 | }, this); 423 | if (positionals.length && options.length) { 424 | str += "\n"; 425 | } 426 | 427 | if (options.length) { 428 | if (!this._nocolors) { 429 | str += chalk.blue("Options:"); 430 | } 431 | else { 432 | str += "Options:"; 433 | } 434 | str += "\n" 435 | 436 | var longest = options.reduce(function(max, opt) { 437 | return opt.string.length > max && !opt.hidden ? opt.string.length : max; 438 | }, 0); 439 | 440 | options.forEach(function(opt) { 441 | if (!opt.hidden) { 442 | str += " " + opt.string + spaces(longest - opt.string.length) + " "; 443 | 444 | var defaults = (opt.default != null ? " [" + opt.default + "]" : ""); 445 | var help = opt.help ? opt.help + defaults : ""; 446 | str += this._nocolors ? help: chalk.grey(help); 447 | 448 | str += "\n"; 449 | } 450 | }, this); 451 | } 452 | 453 | if (this._help) { 454 | str += "\n" + this._help; 455 | } 456 | return str; 457 | } 458 | }; 459 | 460 | ArgParser.prototype.opt = function(arg) { 461 | // get the specified opt for this parsed arg 462 | var match = Opt({}); 463 | this.specs.forEach(function(opt) { 464 | if (opt.matches(arg)) { 465 | match = opt; 466 | } 467 | }); 468 | return match; 469 | }; 470 | 471 | ArgParser.prototype.setOption = function(options, arg, value) { 472 | var option = this.opt(arg); 473 | if (option.callback) { 474 | var message = option.callback(value); 475 | 476 | if (typeof message == "string") { 477 | this.print(message, 1); 478 | } 479 | } 480 | 481 | if (option.type != "string") { 482 | try { 483 | // infer type by JSON parsing the string 484 | value = JSON.parse(value) 485 | } 486 | catch(e) {} 487 | } 488 | 489 | if (option.transform) { 490 | value = option.transform(value); 491 | } 492 | 493 | var name = option.name || arg; 494 | if (option.choices && option.choices.indexOf(value) == -1) { 495 | this.print(name + " must be one of: " + option.choices.join(", "), 1); 496 | } 497 | 498 | if (option.list) { 499 | if (!options[name]) { 500 | options[name] = [value]; 501 | } 502 | else { 503 | options[name].push(value); 504 | } 505 | } 506 | else { 507 | options[name] = value; 508 | } 509 | }; 510 | 511 | 512 | /* an arg is an item that's actually parsed from the command line 513 | e.g. "-l", "log.txt", or "--logfile=log.txt" */ 514 | var Arg = function(str) { 515 | var abbrRegex = /^\-(\w+?)$/, 516 | fullRegex = /^\-\-(no\-)?(.+?)(?:=(.+))?$/, 517 | valRegex = /^[^\-].*/; 518 | 519 | var charMatch = abbrRegex.exec(str), 520 | chars = charMatch && charMatch[1].split(""); 521 | 522 | var fullMatch = fullRegex.exec(str), 523 | full = fullMatch && fullMatch[2]; 524 | 525 | var isValue = str !== undefined && (str === "" || valRegex.test(str)); 526 | var value; 527 | if (isValue) { 528 | value = str; 529 | } 530 | else if (full) { 531 | value = fullMatch[1] ? false : fullMatch[3]; 532 | } 533 | 534 | return { 535 | str: str, 536 | chars: chars, 537 | full: full, 538 | value: value, 539 | isValue: isValue 540 | } 541 | } 542 | 543 | 544 | /* an opt is what's specified by the user in opts hash */ 545 | var Opt = function(opt) { 546 | var strings = (opt.string || "").split(","), 547 | abbr, full, metavar; 548 | for (var i = 0; i < strings.length; i++) { 549 | var string = strings[i].trim(), 550 | matches; 551 | if (matches = string.match(/^\-([^-])(?:\s+(.*))?$/)) { 552 | abbr = matches[1]; 553 | metavar = matches[2]; 554 | } 555 | else if (matches = string.match(/^\-\-(.+?)(?:[=\s]+(.+))?$/)) { 556 | full = matches[1]; 557 | metavar = metavar || matches[2]; 558 | } 559 | } 560 | 561 | matches = matches || []; 562 | var abbr = opt.abbr || abbr, // e.g. v from -v 563 | full = opt.full || full, // e.g. verbose from --verbose 564 | metavar = opt.metavar || metavar; // e.g. PATH from '--config=PATH' 565 | 566 | var string; 567 | if (opt.string) { 568 | string = opt.string; 569 | } 570 | else if (opt.position === undefined) { 571 | string = ""; 572 | if (abbr) { 573 | string += "-" + abbr; 574 | if (metavar) 575 | string += " " + metavar 576 | string += ", "; 577 | } 578 | string += "--" + (full || opt.name); 579 | if (metavar) { 580 | string += " " + metavar; 581 | } 582 | } 583 | 584 | opt = _(opt).extend({ 585 | name: opt.name || full || abbr, 586 | string: string, 587 | abbr: abbr, 588 | full: full, 589 | metavar: metavar, 590 | matches: function(arg) { 591 | return opt.full == arg || opt.abbr == arg || opt.position == arg 592 | || opt.name == arg || (opt.list && arg >= opt.position); 593 | } 594 | }); 595 | return opt; 596 | } 597 | 598 | 599 | var createParser = function() { 600 | return new ArgParser(); 601 | } 602 | 603 | var nomnom = createParser(); 604 | 605 | for (var i in nomnom) { 606 | if (typeof nomnom[i] == "function") { 607 | createParser[i] = _(nomnom[i]).bind(nomnom); 608 | } 609 | } 610 | 611 | module.exports = createParser; 612 | -------------------------------------------------------------------------------- /lib/jsonlint.js: -------------------------------------------------------------------------------- 1 | /* Jison generated parser */ 2 | var jsonlint = (function(){ 3 | var parser = {trace: function trace() { }, 4 | yy: {}, 5 | symbols_: {"error":2,"JSONString":3,"STRING":4,"JSONNumber":5,"NUMBER":6,"JSONNullLiteral":7,"NULL":8,"JSONBooleanLiteral":9,"TRUE":10,"FALSE":11,"JSONText":12,"JSONValue":13,"EOF":14,"JSONObject":15,"JSONArray":16,"{":17,"}":18,"JSONMemberList":19,"JSONMember":20,":":21,",":22,"[":23,"]":24,"JSONElementList":25,"$accept":0,"$end":1}, 6 | terminals_: {2:"error",4:"STRING",6:"NUMBER",8:"NULL",10:"TRUE",11:"FALSE",14:"EOF",17:"{",18:"}",21:":",22:",",23:"[",24:"]"}, 7 | productions_: [0,[3,1],[5,1],[7,1],[9,1],[9,1],[12,2],[13,1],[13,1],[13,1],[13,1],[13,1],[13,1],[15,2],[15,3],[20,3],[19,1],[19,3],[16,2],[16,3],[25,1],[25,3]], 8 | performAction: function anonymous(yytext,yyleng,yylineno,yy,yystate,$$,_$) { 9 | 10 | var $0 = $$.length - 1; 11 | switch (yystate) { 12 | case 1: // replace escaped characters with actual character 13 | this.$ = yytext.replace(/\\(\\|")/g, "$"+"1") 14 | .replace(/\\n/g,'\n') 15 | .replace(/\\r/g,'\r') 16 | .replace(/\\t/g,'\t') 17 | .replace(/\\v/g,'\v') 18 | .replace(/\\f/g,'\f') 19 | .replace(/\\b/g,'\b'); 20 | 21 | break; 22 | case 2:this.$ = Number(yytext); 23 | break; 24 | case 3:this.$ = null; 25 | break; 26 | case 4:this.$ = true; 27 | break; 28 | case 5:this.$ = false; 29 | break; 30 | case 6:return this.$ = $$[$0-1]; 31 | break; 32 | case 13:this.$ = {}; 33 | break; 34 | case 14:this.$ = $$[$0-1]; 35 | break; 36 | case 15:this.$ = [$$[$0-2], $$[$0]]; 37 | break; 38 | case 16:this.$ = {}; this.$[$$[$0][0]] = $$[$0][1]; 39 | break; 40 | case 17:this.$ = $$[$0-2]; $$[$0-2][$$[$0][0]] = $$[$0][1]; 41 | break; 42 | case 18:this.$ = []; 43 | break; 44 | case 19:this.$ = $$[$0-1]; 45 | break; 46 | case 20:this.$ = [$$[$0]]; 47 | break; 48 | case 21:this.$ = $$[$0-2]; $$[$0-2].push($$[$0]); 49 | break; 50 | } 51 | }, 52 | table: [{3:5,4:[1,12],5:6,6:[1,13],7:3,8:[1,9],9:4,10:[1,10],11:[1,11],12:1,13:2,15:7,16:8,17:[1,14],23:[1,15]},{1:[3]},{14:[1,16]},{14:[2,7],18:[2,7],22:[2,7],24:[2,7]},{14:[2,8],18:[2,8],22:[2,8],24:[2,8]},{14:[2,9],18:[2,9],22:[2,9],24:[2,9]},{14:[2,10],18:[2,10],22:[2,10],24:[2,10]},{14:[2,11],18:[2,11],22:[2,11],24:[2,11]},{14:[2,12],18:[2,12],22:[2,12],24:[2,12]},{14:[2,3],18:[2,3],22:[2,3],24:[2,3]},{14:[2,4],18:[2,4],22:[2,4],24:[2,4]},{14:[2,5],18:[2,5],22:[2,5],24:[2,5]},{14:[2,1],18:[2,1],21:[2,1],22:[2,1],24:[2,1]},{14:[2,2],18:[2,2],22:[2,2],24:[2,2]},{3:20,4:[1,12],18:[1,17],19:18,20:19},{3:5,4:[1,12],5:6,6:[1,13],7:3,8:[1,9],9:4,10:[1,10],11:[1,11],13:23,15:7,16:8,17:[1,14],23:[1,15],24:[1,21],25:22},{1:[2,6]},{14:[2,13],18:[2,13],22:[2,13],24:[2,13]},{18:[1,24],22:[1,25]},{18:[2,16],22:[2,16]},{21:[1,26]},{14:[2,18],18:[2,18],22:[2,18],24:[2,18]},{22:[1,28],24:[1,27]},{22:[2,20],24:[2,20]},{14:[2,14],18:[2,14],22:[2,14],24:[2,14]},{3:20,4:[1,12],20:29},{3:5,4:[1,12],5:6,6:[1,13],7:3,8:[1,9],9:4,10:[1,10],11:[1,11],13:30,15:7,16:8,17:[1,14],23:[1,15]},{14:[2,19],18:[2,19],22:[2,19],24:[2,19]},{3:5,4:[1,12],5:6,6:[1,13],7:3,8:[1,9],9:4,10:[1,10],11:[1,11],13:31,15:7,16:8,17:[1,14],23:[1,15]},{18:[2,17],22:[2,17]},{18:[2,15],22:[2,15]},{22:[2,21],24:[2,21]}], 53 | defaultActions: {16:[2,6]}, 54 | parseError: function parseError(str, hash) { 55 | throw new Error(str); 56 | }, 57 | parse: function parse(input) { 58 | var self = this, 59 | stack = [0], 60 | vstack = [null], // semantic value stack 61 | lstack = [], // location stack 62 | table = this.table, 63 | yytext = '', 64 | yylineno = 0, 65 | yyleng = 0, 66 | recovering = 0, 67 | TERROR = 2, 68 | EOF = 1; 69 | 70 | //this.reductionCount = this.shiftCount = 0; 71 | 72 | this.lexer.setInput(input); 73 | this.lexer.yy = this.yy; 74 | this.yy.lexer = this.lexer; 75 | if (typeof this.lexer.yylloc == 'undefined') 76 | this.lexer.yylloc = {}; 77 | var yyloc = this.lexer.yylloc; 78 | lstack.push(yyloc); 79 | 80 | if (typeof this.yy.parseError === 'function') 81 | this.parseError = this.yy.parseError; 82 | 83 | function popStack (n) { 84 | stack.length = stack.length - 2*n; 85 | vstack.length = vstack.length - n; 86 | lstack.length = lstack.length - n; 87 | } 88 | 89 | function lex() { 90 | var token; 91 | token = self.lexer.lex() || 1; // $end = 1 92 | // if token isn't its numeric value, convert 93 | if (typeof token !== 'number') { 94 | token = self.symbols_[token] || token; 95 | } 96 | return token; 97 | } 98 | 99 | var symbol, preErrorSymbol, state, action, a, r, yyval={},p,len,newState, expected; 100 | while (true) { 101 | // retreive state number from top of stack 102 | state = stack[stack.length-1]; 103 | 104 | // use default actions if available 105 | if (this.defaultActions[state]) { 106 | action = this.defaultActions[state]; 107 | } else { 108 | if (symbol == null) 109 | symbol = lex(); 110 | // read action for current state and first input 111 | action = table[state] && table[state][symbol]; 112 | } 113 | 114 | // handle parse error 115 | _handle_error: 116 | if (typeof action === 'undefined' || !action.length || !action[0]) { 117 | 118 | if (!recovering) { 119 | // Report error 120 | expected = []; 121 | for (p in table[state]) if (this.terminals_[p] && p > 2) { 122 | expected.push("'"+this.terminals_[p]+"'"); 123 | } 124 | var errStr = ''; 125 | if (this.lexer.showPosition) { 126 | errStr = 'Parse error on line '+(yylineno+1)+":\n"+this.lexer.showPosition()+"\nExpecting "+expected.join(', ') + ", got '" + this.terminals_[symbol]+ "'"; 127 | } else { 128 | errStr = 'Parse error on line '+(yylineno+1)+": Unexpected " + 129 | (symbol == 1 /*EOF*/ ? "end of input" : 130 | ("'"+(this.terminals_[symbol] || symbol)+"'")); 131 | } 132 | this.parseError(errStr, 133 | {text: this.lexer.match, token: this.terminals_[symbol] || symbol, line: this.lexer.yylineno, loc: yyloc, expected: expected}); 134 | } 135 | 136 | // just recovered from another error 137 | if (recovering == 3) { 138 | if (symbol == EOF) { 139 | throw new Error(errStr || 'Parsing halted.'); 140 | } 141 | 142 | // discard current lookahead and grab another 143 | yyleng = this.lexer.yyleng; 144 | yytext = this.lexer.yytext; 145 | yylineno = this.lexer.yylineno; 146 | yyloc = this.lexer.yylloc; 147 | symbol = lex(); 148 | } 149 | 150 | // try to recover from error 151 | while (1) { 152 | // check for error recovery rule in this state 153 | if ((TERROR.toString()) in table[state]) { 154 | break; 155 | } 156 | if (state == 0) { 157 | throw new Error(errStr || 'Parsing halted.'); 158 | } 159 | popStack(1); 160 | state = stack[stack.length-1]; 161 | } 162 | 163 | preErrorSymbol = symbol; // save the lookahead token 164 | symbol = TERROR; // insert generic error symbol as new lookahead 165 | state = stack[stack.length-1]; 166 | action = table[state] && table[state][TERROR]; 167 | recovering = 3; // allow 3 real symbols to be shifted before reporting a new error 168 | } 169 | 170 | // this shouldn't happen, unless resolve defaults are off 171 | if (action[0] instanceof Array && action.length > 1) { 172 | throw new Error('Parse Error: multiple actions possible at state: '+state+', token: '+symbol); 173 | } 174 | 175 | switch (action[0]) { 176 | 177 | case 1: // shift 178 | //this.shiftCount++; 179 | 180 | stack.push(symbol); 181 | vstack.push(this.lexer.yytext); 182 | lstack.push(this.lexer.yylloc); 183 | stack.push(action[1]); // push state 184 | symbol = null; 185 | if (!preErrorSymbol) { // normal execution/no error 186 | yyleng = this.lexer.yyleng; 187 | yytext = this.lexer.yytext; 188 | yylineno = this.lexer.yylineno; 189 | yyloc = this.lexer.yylloc; 190 | if (recovering > 0) 191 | recovering--; 192 | } else { // error just occurred, resume old lookahead f/ before error 193 | symbol = preErrorSymbol; 194 | preErrorSymbol = null; 195 | } 196 | break; 197 | 198 | case 2: // reduce 199 | //this.reductionCount++; 200 | 201 | len = this.productions_[action[1]][1]; 202 | 203 | // perform semantic action 204 | yyval.$ = vstack[vstack.length-len]; // default to $$ = $1 205 | // default location, uses first token for firsts, last for lasts 206 | yyval._$ = { 207 | first_line: lstack[lstack.length-(len||1)].first_line, 208 | last_line: lstack[lstack.length-1].last_line, 209 | first_column: lstack[lstack.length-(len||1)].first_column, 210 | last_column: lstack[lstack.length-1].last_column 211 | }; 212 | r = this.performAction.call(yyval, yytext, yyleng, yylineno, this.yy, action[1], vstack, lstack); 213 | 214 | if (typeof r !== 'undefined') { 215 | return r; 216 | } 217 | 218 | // pop off stack 219 | if (len) { 220 | stack = stack.slice(0,-1*len*2); 221 | vstack = vstack.slice(0, -1*len); 222 | lstack = lstack.slice(0, -1*len); 223 | } 224 | 225 | stack.push(this.productions_[action[1]][0]); // push nonterminal (reduce) 226 | vstack.push(yyval.$); 227 | lstack.push(yyval._$); 228 | // goto new state = table[STATE][NONTERMINAL] 229 | newState = table[stack[stack.length-2]][stack[stack.length-1]]; 230 | stack.push(newState); 231 | break; 232 | 233 | case 3: // accept 234 | return true; 235 | } 236 | 237 | } 238 | 239 | return true; 240 | }}; 241 | /* Jison generated lexer */ 242 | var lexer = (function(){ 243 | var lexer = ({EOF:1, 244 | parseError:function parseError(str, hash) { 245 | if (this.yy.parseError) { 246 | this.yy.parseError(str, hash); 247 | } else { 248 | throw new Error(str); 249 | } 250 | }, 251 | setInput:function (input) { 252 | this._input = input; 253 | this._more = this._less = this.done = false; 254 | this.yylineno = this.yyleng = 0; 255 | this.yytext = this.matched = this.match = ''; 256 | this.conditionStack = ['INITIAL']; 257 | this.yylloc = {first_line:1,first_column:0,last_line:1,last_column:0}; 258 | return this; 259 | }, 260 | input:function () { 261 | var ch = this._input[0]; 262 | this.yytext+=ch; 263 | this.yyleng++; 264 | this.match+=ch; 265 | this.matched+=ch; 266 | var lines = ch.match(/\n/); 267 | if (lines) this.yylineno++; 268 | this._input = this._input.slice(1); 269 | return ch; 270 | }, 271 | unput:function (ch) { 272 | this._input = ch + this._input; 273 | return this; 274 | }, 275 | more:function () { 276 | this._more = true; 277 | return this; 278 | }, 279 | less:function (n) { 280 | this._input = this.match.slice(n) + this._input; 281 | }, 282 | pastInput:function () { 283 | var past = this.matched.substr(0, this.matched.length - this.match.length); 284 | return (past.length > 20 ? '...':'') + past.substr(-20).replace(/\n/g, ""); 285 | }, 286 | upcomingInput:function () { 287 | var next = this.match; 288 | if (next.length < 20) { 289 | next += this._input.substr(0, 20-next.length); 290 | } 291 | return (next.substr(0,20)+(next.length > 20 ? '...':'')).replace(/\n/g, ""); 292 | }, 293 | showPosition:function () { 294 | var pre = this.pastInput(); 295 | var c = new Array(pre.length + 1).join("-"); 296 | return pre + this.upcomingInput() + "\n" + c+"^"; 297 | }, 298 | next:function () { 299 | if (this.done) { 300 | return this.EOF; 301 | } 302 | if (!this._input) this.done = true; 303 | 304 | var token, 305 | match, 306 | tempMatch, 307 | index, 308 | col, 309 | lines; 310 | if (!this._more) { 311 | this.yytext = ''; 312 | this.match = ''; 313 | } 314 | var rules = this._currentRules(); 315 | for (var i=0;i < rules.length; i++) { 316 | tempMatch = this._input.match(this.rules[rules[i]]); 317 | if (tempMatch && (!match || tempMatch[0].length > match[0].length)) { 318 | match = tempMatch; 319 | index = i; 320 | if (!this.options.flex) break; 321 | } 322 | } 323 | if (match) { 324 | lines = match[0].match(/\n.*/g); 325 | if (lines) this.yylineno += lines.length; 326 | this.yylloc = {first_line: this.yylloc.last_line, 327 | last_line: this.yylineno+1, 328 | first_column: this.yylloc.last_column, 329 | last_column: lines ? lines[lines.length-1].length-1 : this.yylloc.last_column + match[0].length} 330 | this.yytext += match[0]; 331 | this.match += match[0]; 332 | this.yyleng = this.yytext.length; 333 | this._more = false; 334 | this._input = this._input.slice(match[0].length); 335 | this.matched += match[0]; 336 | token = this.performAction.call(this, this.yy, this, rules[index],this.conditionStack[this.conditionStack.length-1]); 337 | if (this.done && this._input) this.done = false; 338 | if (token) return token; 339 | else return; 340 | } 341 | if (this._input === "") { 342 | return this.EOF; 343 | } else { 344 | this.parseError('Lexical error on line '+(this.yylineno+1)+'. Unrecognized text.\n'+this.showPosition(), 345 | {text: "", token: null, line: this.yylineno}); 346 | } 347 | }, 348 | lex:function lex() { 349 | var r = this.next(); 350 | if (typeof r !== 'undefined') { 351 | return r; 352 | } else { 353 | return this.lex(); 354 | } 355 | }, 356 | begin:function begin(condition) { 357 | this.conditionStack.push(condition); 358 | }, 359 | popState:function popState() { 360 | return this.conditionStack.pop(); 361 | }, 362 | _currentRules:function _currentRules() { 363 | return this.conditions[this.conditionStack[this.conditionStack.length-1]].rules; 364 | }, 365 | topState:function () { 366 | return this.conditionStack[this.conditionStack.length-2]; 367 | }, 368 | pushState:function begin(condition) { 369 | this.begin(condition); 370 | }}); 371 | lexer.options = {}; 372 | lexer.performAction = function anonymous(yy,yy_,$avoiding_name_collisions,YY_START) { 373 | 374 | var YYSTATE=YY_START 375 | switch($avoiding_name_collisions) { 376 | case 0:/* skip whitespace */ 377 | break; 378 | case 1:return 6 379 | break; 380 | case 2:yy_.yytext = yy_.yytext.substr(1,yy_.yyleng-2); return 4 381 | break; 382 | case 3:return 17 383 | break; 384 | case 4:return 18 385 | break; 386 | case 5:return 23 387 | break; 388 | case 6:return 24 389 | break; 390 | case 7:return 22 391 | break; 392 | case 8:return 21 393 | break; 394 | case 9:return 10 395 | break; 396 | case 10:return 11 397 | break; 398 | case 11:return 8 399 | break; 400 | case 12:return 14 401 | break; 402 | case 13:return 'INVALID' 403 | break; 404 | } 405 | }; 406 | lexer.rules = [/^(?:\s+)/,/^(?:(-?([0-9]|[1-9][0-9]+))(\.[0-9]+)?([eE][-+]?[0-9]+)?\b)/,/^(?:"(?:\\[\\"bfnrt/]|\\u[a-fA-F0-9]{4}|[^\\\0-\x09\x0a-\x1f"])*")/,/^(?:\{)/,/^(?:\})/,/^(?:\[)/,/^(?:\])/,/^(?:,)/,/^(?::)/,/^(?:true\b)/,/^(?:false\b)/,/^(?:null\b)/,/^(?:$)/,/^(?:.)/]; 407 | lexer.conditions = {"INITIAL":{"rules":[0,1,2,3,4,5,6,7,8,9,10,11,12,13],"inclusive":true}}; 408 | 409 | 410 | ; 411 | return lexer;})() 412 | parser.lexer = lexer; 413 | return parser; 414 | })(); 415 | 416 | var origParse = jsonlint.parse; 417 | 418 | jsonlint.parse = function(input) { 419 | var result = origParse.call(jsonlint, input); 420 | var dougJSONParse = typeof ___dougJSONParse === 'undefined' ? require('./doug-json-parse') : ___dougJSONParse; 421 | try { 422 | dougJSONParse(input); 423 | } catch(e) { 424 | if(/Duplicate key|Bad string|Unexpected/.test(e.message)) { 425 | var linesUntilError = input.substring(0, e.at).split('\n'); 426 | var line = linesUntilError.length; 427 | var col = linesUntilError[line - 1].length - 1; 428 | 429 | this.parseError(e.message, {line: line, col: col, message: e.message.replace(/./, function(l) { return l.toLowerCase(); })}); 430 | throw SyntaxError(e.message + ' on line ' + line); 431 | } 432 | } 433 | 434 | return result; 435 | } 436 | 437 | if (typeof require !== 'undefined' && typeof exports !== 'undefined') { 438 | exports.parser = jsonlint; 439 | exports.parse = function () { return jsonlint.parse.apply(jsonlint, arguments); } 440 | exports.main = function commonjsMain(args) { 441 | if (!args[1]) 442 | throw new Error('Usage: '+args[0]+' FILE'); 443 | if (typeof process !== 'undefined') { 444 | var source = require('fs').readFileSync(require('path').join(process.cwd(), args[1]), "utf8"); 445 | } else { 446 | var cwd = require("file").path(require("file").cwd()); 447 | var source = cwd.join(args[1]).read({charset: "utf-8"}); 448 | } 449 | return exports.parser.parse(source); 450 | } 451 | if (typeof module !== 'undefined' && require.main === module) { 452 | exports.main(typeof process !== 'undefined' ? process.argv.slice(1) : require("system").args); 453 | } 454 | } 455 | -------------------------------------------------------------------------------- /web/jsonlint.js: -------------------------------------------------------------------------------- 1 | var jsonlint = (function(){var require=true,module=false;var exports={};/* 2 | json_parse.js 3 | 2016-05-02 4 | 5 | Public Domain. 6 | 7 | NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK. 8 | 9 | This file creates a json_parse function. 10 | 11 | json_parse(text, reviver) 12 | This method parses a JSON text to produce an object or array. 13 | It can throw a SyntaxError exception. 14 | 15 | The optional reviver parameter is a function that can filter and 16 | transform the results. It receives each of the keys and values, 17 | and its return value is used instead of the original value. 18 | If it returns what it received, then the structure is not modified. 19 | If it returns undefined then the member is deleted. 20 | 21 | Example: 22 | 23 | // Parse the text. Values that look like ISO date strings will 24 | // be converted to Date objects. 25 | 26 | myData = json_parse(text, function (key, value) { 27 | var a; 28 | if (typeof value === "string") { 29 | a = 30 | /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)Z$/.exec(value); 31 | if (a) { 32 | return new Date(Date.UTC(+a[1], +a[2] - 1, +a[3], +a[4], 33 | +a[5], +a[6])); 34 | } 35 | } 36 | return value; 37 | }); 38 | 39 | This is a reference implementation. You are free to copy, modify, or 40 | redistribute. 41 | 42 | This code should be minified before deployment. 43 | See http://javascript.crockford.com/jsmin.html 44 | 45 | USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD CODE FROM SERVERS YOU DO 46 | NOT CONTROL. 47 | */ 48 | 49 | /*jslint for */ 50 | 51 | /*property 52 | at, b, call, charAt, f, fromCharCode, hasOwnProperty, message, n, name, 53 | prototype, push, r, t, text 54 | */ 55 | 56 | var ___dougJSONParse = (function () { 57 | "use strict"; 58 | 59 | // This is a function that can parse a JSON text, producing a JavaScript 60 | // data structure. It is a simple, recursive descent parser. It does not use 61 | // eval or regular expressions, so it can be used as a model for implementing 62 | // a JSON parser in other languages. 63 | 64 | // We are defining the function inside of another function to avoid creating 65 | // global variables. 66 | 67 | var at; // The index of the current character 68 | var ch; // The current character 69 | var escapee = { 70 | "\"": "\"", 71 | "\\": "\\", 72 | "/": "/", 73 | b: "\b", 74 | f: "\f", 75 | n: "\n", 76 | r: "\r", 77 | t: "\t" 78 | }; 79 | var text; 80 | 81 | var error = function (m) { 82 | 83 | // Call error when something is wrong. 84 | 85 | throw { 86 | name: "SyntaxError", 87 | message: m, 88 | at: at, 89 | text: text 90 | }; 91 | }; 92 | 93 | var next = function (c) { 94 | 95 | // If a c parameter is provided, verify that it matches the current character. 96 | 97 | if (c && c !== ch) { 98 | error("Expected '" + c + "' instead of '" + ch + "'"); 99 | } 100 | 101 | // Get the next character. When there are no more characters, 102 | // return the empty string. 103 | 104 | ch = text.charAt(at); 105 | at += 1; 106 | return ch; 107 | }; 108 | 109 | var number = function () { 110 | 111 | // Parse a number value. 112 | 113 | var value; 114 | var string = ""; 115 | 116 | if (ch === "-") { 117 | string = "-"; 118 | next("-"); 119 | } 120 | while (ch >= "0" && ch <= "9") { 121 | string += ch; 122 | next(); 123 | } 124 | if (ch === ".") { 125 | string += "."; 126 | while (next() && ch >= "0" && ch <= "9") { 127 | string += ch; 128 | } 129 | } 130 | if (ch === "e" || ch === "E") { 131 | string += ch; 132 | next(); 133 | if (ch === "-" || ch === "+") { 134 | string += ch; 135 | next(); 136 | } 137 | while (ch >= "0" && ch <= "9") { 138 | string += ch; 139 | next(); 140 | } 141 | } 142 | value = +string; 143 | if (!isFinite(value)) { 144 | error("Bad number"); 145 | } else { 146 | return value; 147 | } 148 | }; 149 | 150 | var string = function () { 151 | 152 | // Parse a string value. 153 | 154 | var hex; 155 | var i; 156 | var value = ""; 157 | var uffff; 158 | 159 | // When parsing for string values, we must look for " and \ characters. 160 | 161 | if (ch === "\"") { 162 | while (next()) { 163 | if (ch === "\"") { 164 | next(); 165 | return value; 166 | } 167 | if (ch === "\\") { 168 | next(); 169 | if (ch === "u") { 170 | uffff = 0; 171 | for (i = 0; i < 4; i += 1) { 172 | hex = parseInt(next(), 16); 173 | if (!isFinite(hex)) { 174 | break; 175 | } 176 | uffff = uffff * 16 + hex; 177 | } 178 | value += String.fromCharCode(uffff); 179 | } else if (typeof escapee[ch] === "string") { 180 | value += escapee[ch]; 181 | } else { 182 | break; 183 | } 184 | } else { 185 | value += ch; 186 | } 187 | } 188 | } 189 | error("Bad string"); 190 | }; 191 | 192 | var white = function () { 193 | 194 | // Skip whitespace. 195 | 196 | while (ch && ch <= " ") { 197 | next(); 198 | } 199 | }; 200 | 201 | var word = function () { 202 | 203 | // true, false, or null. 204 | 205 | switch (ch) { 206 | case "t": 207 | next("t"); 208 | next("r"); 209 | next("u"); 210 | next("e"); 211 | return true; 212 | case "f": 213 | next("f"); 214 | next("a"); 215 | next("l"); 216 | next("s"); 217 | next("e"); 218 | return false; 219 | case "n": 220 | next("n"); 221 | next("u"); 222 | next("l"); 223 | next("l"); 224 | return null; 225 | } 226 | error("Unexpected '" + ch + "'"); 227 | }; 228 | 229 | var value; // Place holder for the value function. 230 | 231 | var array = function () { 232 | 233 | // Parse an array value. 234 | 235 | var arr = []; 236 | 237 | if (ch === "[") { 238 | next("["); 239 | white(); 240 | if (ch === "]") { 241 | next("]"); 242 | return arr; // empty array 243 | } 244 | while (ch) { 245 | arr.push(value()); 246 | white(); 247 | if (ch === "]") { 248 | next("]"); 249 | return arr; 250 | } 251 | next(","); 252 | white(); 253 | } 254 | } 255 | error("Bad array"); 256 | }; 257 | 258 | var object = function () { 259 | 260 | // Parse an object value. 261 | 262 | var key; 263 | var obj = {}; 264 | 265 | if (ch === "{") { 266 | next("{"); 267 | white(); 268 | if (ch === "}") { 269 | next("}"); 270 | return obj; // empty object 271 | } 272 | while (ch) { 273 | key = string(); 274 | white(); 275 | next(":"); 276 | if (Object.hasOwnProperty.call(obj, key)) { 277 | error("Duplicate key '" + key + "'"); 278 | } 279 | obj[key] = value(); 280 | white(); 281 | if (ch === "}") { 282 | next("}"); 283 | return obj; 284 | } 285 | next(","); 286 | white(); 287 | } 288 | } 289 | error("Bad object"); 290 | }; 291 | 292 | value = function () { 293 | 294 | // Parse a JSON value. It could be an object, an array, a string, a number, 295 | // or a word. 296 | 297 | white(); 298 | switch (ch) { 299 | case "{": 300 | return object(); 301 | case "[": 302 | return array(); 303 | case "\"": 304 | return string(); 305 | case "-": 306 | return number(); 307 | default: 308 | return (ch >= "0" && ch <= "9") 309 | ? number() 310 | : word(); 311 | } 312 | }; 313 | 314 | // Return the json_parse function. It will have access to all of the above 315 | // functions and variables. 316 | 317 | return function (source, reviver) { 318 | var result; 319 | 320 | text = source; 321 | at = 0; 322 | ch = " "; 323 | result = value(); 324 | white(); 325 | if (ch) { 326 | error("Syntax error"); 327 | } 328 | 329 | // If there is a reviver function, we recursively walk the new structure, 330 | // passing each name/value pair to the reviver function for possible 331 | // transformation, starting with a temporary root object that holds the result 332 | // in an empty key. If there is not a reviver function, we simply return the 333 | // result. 334 | 335 | return (typeof reviver === "function") 336 | ? (function walk(holder, key) { 337 | var k; 338 | var v; 339 | var val = holder[key]; 340 | if (val && typeof val === "object") { 341 | for (k in val) { 342 | if (Object.prototype.hasOwnProperty.call(val, k)) { 343 | v = walk(val, k); 344 | if (v !== undefined) { 345 | val[k] = v; 346 | } else { 347 | delete val[k]; 348 | } 349 | } 350 | } 351 | } 352 | return reviver.call(holder, key, val); 353 | }({"": result}, "")) 354 | : result; 355 | }; 356 | }()); 357 | 358 | if(typeof module === 'object' && module.exports) { 359 | module.exports = ___dougJSONParse; 360 | } 361 | /* Jison generated parser */ 362 | var jsonlint = (function(){ 363 | var parser = {trace: function trace() { }, 364 | yy: {}, 365 | symbols_: {"error":2,"JSONString":3,"STRING":4,"JSONNumber":5,"NUMBER":6,"JSONNullLiteral":7,"NULL":8,"JSONBooleanLiteral":9,"TRUE":10,"FALSE":11,"JSONText":12,"JSONValue":13,"EOF":14,"JSONObject":15,"JSONArray":16,"{":17,"}":18,"JSONMemberList":19,"JSONMember":20,":":21,",":22,"[":23,"]":24,"JSONElementList":25,"$accept":0,"$end":1}, 366 | terminals_: {2:"error",4:"STRING",6:"NUMBER",8:"NULL",10:"TRUE",11:"FALSE",14:"EOF",17:"{",18:"}",21:":",22:",",23:"[",24:"]"}, 367 | productions_: [0,[3,1],[5,1],[7,1],[9,1],[9,1],[12,2],[13,1],[13,1],[13,1],[13,1],[13,1],[13,1],[15,2],[15,3],[20,3],[19,1],[19,3],[16,2],[16,3],[25,1],[25,3]], 368 | performAction: function anonymous(yytext,yyleng,yylineno,yy,yystate,$$,_$) { 369 | 370 | var $0 = $$.length - 1; 371 | switch (yystate) { 372 | case 1: // replace escaped characters with actual character 373 | this.$ = yytext.replace(/\\(\\|")/g, "$"+"1") 374 | .replace(/\\n/g,'\n') 375 | .replace(/\\r/g,'\r') 376 | .replace(/\\t/g,'\t') 377 | .replace(/\\v/g,'\v') 378 | .replace(/\\f/g,'\f') 379 | .replace(/\\b/g,'\b'); 380 | 381 | break; 382 | case 2:this.$ = Number(yytext); 383 | break; 384 | case 3:this.$ = null; 385 | break; 386 | case 4:this.$ = true; 387 | break; 388 | case 5:this.$ = false; 389 | break; 390 | case 6:return this.$ = $$[$0-1]; 391 | break; 392 | case 13:this.$ = {}; 393 | break; 394 | case 14:this.$ = $$[$0-1]; 395 | break; 396 | case 15:this.$ = [$$[$0-2], $$[$0]]; 397 | break; 398 | case 16:this.$ = {}; this.$[$$[$0][0]] = $$[$0][1]; 399 | break; 400 | case 17:this.$ = $$[$0-2]; $$[$0-2][$$[$0][0]] = $$[$0][1]; 401 | break; 402 | case 18:this.$ = []; 403 | break; 404 | case 19:this.$ = $$[$0-1]; 405 | break; 406 | case 20:this.$ = [$$[$0]]; 407 | break; 408 | case 21:this.$ = $$[$0-2]; $$[$0-2].push($$[$0]); 409 | break; 410 | } 411 | }, 412 | table: [{3:5,4:[1,12],5:6,6:[1,13],7:3,8:[1,9],9:4,10:[1,10],11:[1,11],12:1,13:2,15:7,16:8,17:[1,14],23:[1,15]},{1:[3]},{14:[1,16]},{14:[2,7],18:[2,7],22:[2,7],24:[2,7]},{14:[2,8],18:[2,8],22:[2,8],24:[2,8]},{14:[2,9],18:[2,9],22:[2,9],24:[2,9]},{14:[2,10],18:[2,10],22:[2,10],24:[2,10]},{14:[2,11],18:[2,11],22:[2,11],24:[2,11]},{14:[2,12],18:[2,12],22:[2,12],24:[2,12]},{14:[2,3],18:[2,3],22:[2,3],24:[2,3]},{14:[2,4],18:[2,4],22:[2,4],24:[2,4]},{14:[2,5],18:[2,5],22:[2,5],24:[2,5]},{14:[2,1],18:[2,1],21:[2,1],22:[2,1],24:[2,1]},{14:[2,2],18:[2,2],22:[2,2],24:[2,2]},{3:20,4:[1,12],18:[1,17],19:18,20:19},{3:5,4:[1,12],5:6,6:[1,13],7:3,8:[1,9],9:4,10:[1,10],11:[1,11],13:23,15:7,16:8,17:[1,14],23:[1,15],24:[1,21],25:22},{1:[2,6]},{14:[2,13],18:[2,13],22:[2,13],24:[2,13]},{18:[1,24],22:[1,25]},{18:[2,16],22:[2,16]},{21:[1,26]},{14:[2,18],18:[2,18],22:[2,18],24:[2,18]},{22:[1,28],24:[1,27]},{22:[2,20],24:[2,20]},{14:[2,14],18:[2,14],22:[2,14],24:[2,14]},{3:20,4:[1,12],20:29},{3:5,4:[1,12],5:6,6:[1,13],7:3,8:[1,9],9:4,10:[1,10],11:[1,11],13:30,15:7,16:8,17:[1,14],23:[1,15]},{14:[2,19],18:[2,19],22:[2,19],24:[2,19]},{3:5,4:[1,12],5:6,6:[1,13],7:3,8:[1,9],9:4,10:[1,10],11:[1,11],13:31,15:7,16:8,17:[1,14],23:[1,15]},{18:[2,17],22:[2,17]},{18:[2,15],22:[2,15]},{22:[2,21],24:[2,21]}], 413 | defaultActions: {16:[2,6]}, 414 | parseError: function parseError(str, hash) { 415 | throw new Error(str); 416 | }, 417 | parse: function parse(input) { 418 | var self = this, 419 | stack = [0], 420 | vstack = [null], // semantic value stack 421 | lstack = [], // location stack 422 | table = this.table, 423 | yytext = '', 424 | yylineno = 0, 425 | yyleng = 0, 426 | recovering = 0, 427 | TERROR = 2, 428 | EOF = 1; 429 | 430 | //this.reductionCount = this.shiftCount = 0; 431 | 432 | this.lexer.setInput(input); 433 | this.lexer.yy = this.yy; 434 | this.yy.lexer = this.lexer; 435 | if (typeof this.lexer.yylloc == 'undefined') 436 | this.lexer.yylloc = {}; 437 | var yyloc = this.lexer.yylloc; 438 | lstack.push(yyloc); 439 | 440 | if (typeof this.yy.parseError === 'function') 441 | this.parseError = this.yy.parseError; 442 | 443 | function popStack (n) { 444 | stack.length = stack.length - 2*n; 445 | vstack.length = vstack.length - n; 446 | lstack.length = lstack.length - n; 447 | } 448 | 449 | function lex() { 450 | var token; 451 | token = self.lexer.lex() || 1; // $end = 1 452 | // if token isn't its numeric value, convert 453 | if (typeof token !== 'number') { 454 | token = self.symbols_[token] || token; 455 | } 456 | return token; 457 | } 458 | 459 | var symbol, preErrorSymbol, state, action, a, r, yyval={},p,len,newState, expected; 460 | while (true) { 461 | // retreive state number from top of stack 462 | state = stack[stack.length-1]; 463 | 464 | // use default actions if available 465 | if (this.defaultActions[state]) { 466 | action = this.defaultActions[state]; 467 | } else { 468 | if (symbol == null) 469 | symbol = lex(); 470 | // read action for current state and first input 471 | action = table[state] && table[state][symbol]; 472 | } 473 | 474 | // handle parse error 475 | _handle_error: 476 | if (typeof action === 'undefined' || !action.length || !action[0]) { 477 | 478 | if (!recovering) { 479 | // Report error 480 | expected = []; 481 | for (p in table[state]) if (this.terminals_[p] && p > 2) { 482 | expected.push("'"+this.terminals_[p]+"'"); 483 | } 484 | var errStr = ''; 485 | if (this.lexer.showPosition) { 486 | errStr = 'Parse error on line '+(yylineno+1)+":\n"+this.lexer.showPosition()+"\nExpecting "+expected.join(', ') + ", got '" + this.terminals_[symbol]+ "'"; 487 | } else { 488 | errStr = 'Parse error on line '+(yylineno+1)+": Unexpected " + 489 | (symbol == 1 /*EOF*/ ? "end of input" : 490 | ("'"+(this.terminals_[symbol] || symbol)+"'")); 491 | } 492 | this.parseError(errStr, 493 | {text: this.lexer.match, token: this.terminals_[symbol] || symbol, line: this.lexer.yylineno, loc: yyloc, expected: expected}); 494 | } 495 | 496 | // just recovered from another error 497 | if (recovering == 3) { 498 | if (symbol == EOF) { 499 | throw new Error(errStr || 'Parsing halted.'); 500 | } 501 | 502 | // discard current lookahead and grab another 503 | yyleng = this.lexer.yyleng; 504 | yytext = this.lexer.yytext; 505 | yylineno = this.lexer.yylineno; 506 | yyloc = this.lexer.yylloc; 507 | symbol = lex(); 508 | } 509 | 510 | // try to recover from error 511 | while (1) { 512 | // check for error recovery rule in this state 513 | if ((TERROR.toString()) in table[state]) { 514 | break; 515 | } 516 | if (state == 0) { 517 | throw new Error(errStr || 'Parsing halted.'); 518 | } 519 | popStack(1); 520 | state = stack[stack.length-1]; 521 | } 522 | 523 | preErrorSymbol = symbol; // save the lookahead token 524 | symbol = TERROR; // insert generic error symbol as new lookahead 525 | state = stack[stack.length-1]; 526 | action = table[state] && table[state][TERROR]; 527 | recovering = 3; // allow 3 real symbols to be shifted before reporting a new error 528 | } 529 | 530 | // this shouldn't happen, unless resolve defaults are off 531 | if (action[0] instanceof Array && action.length > 1) { 532 | throw new Error('Parse Error: multiple actions possible at state: '+state+', token: '+symbol); 533 | } 534 | 535 | switch (action[0]) { 536 | 537 | case 1: // shift 538 | //this.shiftCount++; 539 | 540 | stack.push(symbol); 541 | vstack.push(this.lexer.yytext); 542 | lstack.push(this.lexer.yylloc); 543 | stack.push(action[1]); // push state 544 | symbol = null; 545 | if (!preErrorSymbol) { // normal execution/no error 546 | yyleng = this.lexer.yyleng; 547 | yytext = this.lexer.yytext; 548 | yylineno = this.lexer.yylineno; 549 | yyloc = this.lexer.yylloc; 550 | if (recovering > 0) 551 | recovering--; 552 | } else { // error just occurred, resume old lookahead f/ before error 553 | symbol = preErrorSymbol; 554 | preErrorSymbol = null; 555 | } 556 | break; 557 | 558 | case 2: // reduce 559 | //this.reductionCount++; 560 | 561 | len = this.productions_[action[1]][1]; 562 | 563 | // perform semantic action 564 | yyval.$ = vstack[vstack.length-len]; // default to $$ = $1 565 | // default location, uses first token for firsts, last for lasts 566 | yyval._$ = { 567 | first_line: lstack[lstack.length-(len||1)].first_line, 568 | last_line: lstack[lstack.length-1].last_line, 569 | first_column: lstack[lstack.length-(len||1)].first_column, 570 | last_column: lstack[lstack.length-1].last_column 571 | }; 572 | r = this.performAction.call(yyval, yytext, yyleng, yylineno, this.yy, action[1], vstack, lstack); 573 | 574 | if (typeof r !== 'undefined') { 575 | return r; 576 | } 577 | 578 | // pop off stack 579 | if (len) { 580 | stack = stack.slice(0,-1*len*2); 581 | vstack = vstack.slice(0, -1*len); 582 | lstack = lstack.slice(0, -1*len); 583 | } 584 | 585 | stack.push(this.productions_[action[1]][0]); // push nonterminal (reduce) 586 | vstack.push(yyval.$); 587 | lstack.push(yyval._$); 588 | // goto new state = table[STATE][NONTERMINAL] 589 | newState = table[stack[stack.length-2]][stack[stack.length-1]]; 590 | stack.push(newState); 591 | break; 592 | 593 | case 3: // accept 594 | return true; 595 | } 596 | 597 | } 598 | 599 | return true; 600 | }}; 601 | /* Jison generated lexer */ 602 | var lexer = (function(){ 603 | var lexer = ({EOF:1, 604 | parseError:function parseError(str, hash) { 605 | if (this.yy.parseError) { 606 | this.yy.parseError(str, hash); 607 | } else { 608 | throw new Error(str); 609 | } 610 | }, 611 | setInput:function (input) { 612 | this._input = input; 613 | this._more = this._less = this.done = false; 614 | this.yylineno = this.yyleng = 0; 615 | this.yytext = this.matched = this.match = ''; 616 | this.conditionStack = ['INITIAL']; 617 | this.yylloc = {first_line:1,first_column:0,last_line:1,last_column:0}; 618 | return this; 619 | }, 620 | input:function () { 621 | var ch = this._input[0]; 622 | this.yytext+=ch; 623 | this.yyleng++; 624 | this.match+=ch; 625 | this.matched+=ch; 626 | var lines = ch.match(/\n/); 627 | if (lines) this.yylineno++; 628 | this._input = this._input.slice(1); 629 | return ch; 630 | }, 631 | unput:function (ch) { 632 | this._input = ch + this._input; 633 | return this; 634 | }, 635 | more:function () { 636 | this._more = true; 637 | return this; 638 | }, 639 | less:function (n) { 640 | this._input = this.match.slice(n) + this._input; 641 | }, 642 | pastInput:function () { 643 | var past = this.matched.substr(0, this.matched.length - this.match.length); 644 | return (past.length > 20 ? '...':'') + past.substr(-20).replace(/\n/g, ""); 645 | }, 646 | upcomingInput:function () { 647 | var next = this.match; 648 | if (next.length < 20) { 649 | next += this._input.substr(0, 20-next.length); 650 | } 651 | return (next.substr(0,20)+(next.length > 20 ? '...':'')).replace(/\n/g, ""); 652 | }, 653 | showPosition:function () { 654 | var pre = this.pastInput(); 655 | var c = new Array(pre.length + 1).join("-"); 656 | return pre + this.upcomingInput() + "\n" + c+"^"; 657 | }, 658 | next:function () { 659 | if (this.done) { 660 | return this.EOF; 661 | } 662 | if (!this._input) this.done = true; 663 | 664 | var token, 665 | match, 666 | tempMatch, 667 | index, 668 | col, 669 | lines; 670 | if (!this._more) { 671 | this.yytext = ''; 672 | this.match = ''; 673 | } 674 | var rules = this._currentRules(); 675 | for (var i=0;i < rules.length; i++) { 676 | tempMatch = this._input.match(this.rules[rules[i]]); 677 | if (tempMatch && (!match || tempMatch[0].length > match[0].length)) { 678 | match = tempMatch; 679 | index = i; 680 | if (!this.options.flex) break; 681 | } 682 | } 683 | if (match) { 684 | lines = match[0].match(/\n.*/g); 685 | if (lines) this.yylineno += lines.length; 686 | this.yylloc = {first_line: this.yylloc.last_line, 687 | last_line: this.yylineno+1, 688 | first_column: this.yylloc.last_column, 689 | last_column: lines ? lines[lines.length-1].length-1 : this.yylloc.last_column + match[0].length} 690 | this.yytext += match[0]; 691 | this.match += match[0]; 692 | this.yyleng = this.yytext.length; 693 | this._more = false; 694 | this._input = this._input.slice(match[0].length); 695 | this.matched += match[0]; 696 | token = this.performAction.call(this, this.yy, this, rules[index],this.conditionStack[this.conditionStack.length-1]); 697 | if (this.done && this._input) this.done = false; 698 | if (token) return token; 699 | else return; 700 | } 701 | if (this._input === "") { 702 | return this.EOF; 703 | } else { 704 | this.parseError('Lexical error on line '+(this.yylineno+1)+'. Unrecognized text.\n'+this.showPosition(), 705 | {text: "", token: null, line: this.yylineno}); 706 | } 707 | }, 708 | lex:function lex() { 709 | var r = this.next(); 710 | if (typeof r !== 'undefined') { 711 | return r; 712 | } else { 713 | return this.lex(); 714 | } 715 | }, 716 | begin:function begin(condition) { 717 | this.conditionStack.push(condition); 718 | }, 719 | popState:function popState() { 720 | return this.conditionStack.pop(); 721 | }, 722 | _currentRules:function _currentRules() { 723 | return this.conditions[this.conditionStack[this.conditionStack.length-1]].rules; 724 | }, 725 | topState:function () { 726 | return this.conditionStack[this.conditionStack.length-2]; 727 | }, 728 | pushState:function begin(condition) { 729 | this.begin(condition); 730 | }}); 731 | lexer.options = {}; 732 | lexer.performAction = function anonymous(yy,yy_,$avoiding_name_collisions,YY_START) { 733 | 734 | var YYSTATE=YY_START 735 | switch($avoiding_name_collisions) { 736 | case 0:/* skip whitespace */ 737 | break; 738 | case 1:return 6 739 | break; 740 | case 2:yy_.yytext = yy_.yytext.substr(1,yy_.yyleng-2); return 4 741 | break; 742 | case 3:return 17 743 | break; 744 | case 4:return 18 745 | break; 746 | case 5:return 23 747 | break; 748 | case 6:return 24 749 | break; 750 | case 7:return 22 751 | break; 752 | case 8:return 21 753 | break; 754 | case 9:return 10 755 | break; 756 | case 10:return 11 757 | break; 758 | case 11:return 8 759 | break; 760 | case 12:return 14 761 | break; 762 | case 13:return 'INVALID' 763 | break; 764 | } 765 | }; 766 | lexer.rules = [/^(?:\s+)/,/^(?:(-?([0-9]|[1-9][0-9]+))(\.[0-9]+)?([eE][-+]?[0-9]+)?\b)/,/^(?:"(?:\\[\\"bfnrt/]|\\u[a-fA-F0-9]{4}|[^\\\0-\x09\x0a-\x1f"])*")/,/^(?:\{)/,/^(?:\})/,/^(?:\[)/,/^(?:\])/,/^(?:,)/,/^(?::)/,/^(?:true\b)/,/^(?:false\b)/,/^(?:null\b)/,/^(?:$)/,/^(?:.)/]; 767 | lexer.conditions = {"INITIAL":{"rules":[0,1,2,3,4,5,6,7,8,9,10,11,12,13],"inclusive":true}}; 768 | 769 | 770 | ; 771 | return lexer;})() 772 | parser.lexer = lexer; 773 | return parser; 774 | })(); 775 | 776 | var origParse = jsonlint.parse; 777 | 778 | jsonlint.parse = function(input) { 779 | var result = origParse.call(jsonlint, input); 780 | var dougJSONParse = typeof ___dougJSONParse === 'undefined' ? require('./doug-json-parse') : ___dougJSONParse; 781 | try { 782 | dougJSONParse(input); 783 | } catch(e) { 784 | if(/Duplicate key|Bad string|Unexpected/.test(e.message)) { 785 | var linesUntilError = input.substring(0, e.at).split('\n'); 786 | var line = linesUntilError.length; 787 | var col = linesUntilError[line - 1].length - 1; 788 | 789 | this.parseError(e.message, {line: line, col: col, message: e.message.replace(/./, function(l) { return l.toLowerCase(); })}); 790 | throw SyntaxError(e.message + ' on line ' + line); 791 | } 792 | } 793 | 794 | return result; 795 | } 796 | 797 | if (typeof require !== 'undefined' && typeof exports !== 'undefined') { 798 | exports.parser = jsonlint; 799 | exports.parse = function () { return jsonlint.parse.apply(jsonlint, arguments); } 800 | exports.main = function commonjsMain(args) { 801 | if (!args[1]) 802 | throw new Error('Usage: '+args[0]+' FILE'); 803 | if (typeof process !== 'undefined') { 804 | var source = require('fs').readFileSync(require('path').join(process.cwd(), args[1]), "utf8"); 805 | } else { 806 | var cwd = require("file").path(require("file").cwd()); 807 | var source = cwd.join(args[1]).read({charset: "utf-8"}); 808 | } 809 | return exports.parser.parse(source); 810 | } 811 | if (typeof module !== 'undefined' && require.main === module) { 812 | exports.main(typeof process !== 'undefined' ? process.argv.slice(1) : require("system").args); 813 | } 814 | } 815 | return exports;})();if(typeof module === 'object' && module.exports) module.exports = jsonlint; 816 | --------------------------------------------------------------------------------