mirror of
https://github.com/circlecell/jsonlint-mod.git
synced 2026-01-21 05:07:49 +08:00
Compare commits
23 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fca1f5f846 | ||
|
|
f019341e15 | ||
|
|
85bf286c79 | ||
|
|
79b553fb65 | ||
|
|
59c36ad370 | ||
|
|
92ac817d17 | ||
|
|
0f3202370f | ||
|
|
1d7ed8ab13 | ||
|
|
5e02f8b8e0 | ||
|
|
f2e1de3039 | ||
|
|
cdb7737033 | ||
|
|
5c5ad5acf0 | ||
|
|
6af4143235 | ||
|
|
cb82cc205d | ||
|
|
3899996991 | ||
|
|
14a108f3a4 | ||
|
|
c07fb3db3b | ||
|
|
f88c4842ff | ||
|
|
eca60bb22b | ||
|
|
4f24c338fa | ||
|
|
f4d68f8fd1 | ||
|
|
9eb4bd8260 | ||
|
|
11e7192a72 |
8
Makefile
8
Makefile
@@ -1,14 +1,16 @@
|
||||
|
||||
all: build test
|
||||
all: build test site
|
||||
|
||||
build:
|
||||
jison src/jsonlint.y src/jsonlint.l
|
||||
mv jsonlint.js lib/jsonlint.js
|
||||
node scripts/bundle.js | uglifyjs > web/jsonlint.js
|
||||
|
||||
deploy:
|
||||
site:
|
||||
cp web/jsonlint.js ../jsonlint-pages/jsonlint.js
|
||||
cd ../jsolint-pages && git commit -a -m 'deploy site updates' && git push origin gh-pages
|
||||
|
||||
deploy: site
|
||||
cd ../jsonlint-pages && git commit -a -m 'deploy site updates' && git push origin gh-pages
|
||||
|
||||
test: lib/jsonlint.js test/all-tests.js
|
||||
node test/all-tests.js
|
||||
|
||||
23
README.md
23
README.md
@@ -21,15 +21,21 @@ jsonlint will either report a syntax error with details or pretty print the sour
|
||||
### Options
|
||||
|
||||
$ jsonlint -h
|
||||
Usage: jsonlint <file> [options]
|
||||
|
||||
<file> file to parse; otherwise uses stdin
|
||||
usage: jsonlint <file> [options]
|
||||
|
||||
file file to parse; otherwise uses stdin
|
||||
|
||||
options:
|
||||
-v, --version print version and exit
|
||||
-s, --sort-keys sort object keys
|
||||
-i, --in-place overwrite the file
|
||||
-t CHAR, --indent CHAR character(s) to use for indentation
|
||||
-v, --version print version and exit
|
||||
-s, --sort-keys sort object keys
|
||||
-i, --in-place overwrite the file
|
||||
-t CHAR, --indent CHAR character(s) to use for indentation
|
||||
-c, --compact compact error display
|
||||
-V, --validate a JSON schema to use for validation
|
||||
-e, --environment which specification of JSON Schema the validation file uses
|
||||
-q, --quiet do not print the parsed json to STDOUT
|
||||
|
||||
|
||||
## Module interface
|
||||
|
||||
@@ -41,6 +47,11 @@ I'm not sure why you wouldn't use the built in `JSON.parse` but you can use json
|
||||
|
||||
It returns the parsed object or throws an `Error`.
|
||||
|
||||
## Vim Plugins
|
||||
|
||||
* [Syntastic](http://www.vim.org/scripts/script.php?script_id=2736)
|
||||
* [sourcebeautify](http://www.vim.org/scripts/script.php?script_id=4079)
|
||||
|
||||
## MIT License
|
||||
|
||||
Copyright (C) 2012 Zachary Carter
|
||||
|
||||
113
lib/cli.js
113
lib/cli.js
@@ -1,10 +1,14 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
var fs = require("fs");
|
||||
var path = require("path");
|
||||
var parser = require("./jsonlint").parser;
|
||||
var JSV = require("JSV").JSV;
|
||||
var formatter = require("./formatter.js").formatter;
|
||||
|
||||
var options = require("nomnom")
|
||||
.scriptName("jsonlint")
|
||||
.opts({
|
||||
.script("jsonlint")
|
||||
.options({
|
||||
file: {
|
||||
position: 0,
|
||||
help: "file to parse; otherwise uses stdin"
|
||||
@@ -14,7 +18,7 @@ var options = require("nomnom")
|
||||
string: '-v, --version',
|
||||
help: 'print version and exit',
|
||||
callback: function() {
|
||||
return JSON.parse(fs.readFileSync(__dirname + "/../package.json", "utf8")).version;
|
||||
return require("../package").version;
|
||||
}
|
||||
},
|
||||
sort : {
|
||||
@@ -36,41 +40,104 @@ var options = require("nomnom")
|
||||
flag : true,
|
||||
string: '-c, --compact',
|
||||
help : 'compact error display'
|
||||
},
|
||||
validate : {
|
||||
string: '-V, --validate',
|
||||
help : 'a JSON schema to use for validation'
|
||||
},
|
||||
env : {
|
||||
string: '-e, --environment',
|
||||
"default": "json-schema-draft-03",
|
||||
help: 'which specification of JSON Schema the validation file uses'
|
||||
},
|
||||
quiet: {
|
||||
flag: true,
|
||||
key: "value",
|
||||
string: '-q, --quiet',
|
||||
"default": false,
|
||||
help: 'do not print the parsed json to STDOUT'
|
||||
},
|
||||
forcePrettyPrint: {
|
||||
flag: true,
|
||||
string: '-p, --pretty-print',
|
||||
help: 'force pretty printing even if invalid'
|
||||
}
|
||||
})
|
||||
.parseArgs();
|
||||
}).parse();
|
||||
|
||||
if (options.compact) {
|
||||
var fileName = options.file? options.file + ': ' : '';
|
||||
parser.parseError = parser.lexer.parseError = function(str, hash) {
|
||||
console.error(fileName + 'line '+ hash.loc.first_line +', col '+ hash.loc.last_column +', found: \''+ hash.token +'\' - expected: '+ hash.expected.join(', ') +'.');
|
||||
throw new Error(str);
|
||||
};
|
||||
var fileName = options.file? options.file + ': ' : '';
|
||||
parser.parseError = parser.lexer.parseError = function(str, hash) {
|
||||
console.error(fileName + 'line '+ hash.loc.first_line +', col '+ hash.loc.last_column +', found: \''+ hash.token +'\' - expected: '+ hash.expected.join(', ') +'.');
|
||||
throw new Error(str);
|
||||
};
|
||||
}
|
||||
|
||||
function parse (source) {
|
||||
var parsed,
|
||||
formatted;
|
||||
|
||||
try {
|
||||
var parsed = options.sort ?
|
||||
sortObject(parser.parse(source)) :
|
||||
parser.parse(source);
|
||||
parsed = options.sort ?
|
||||
sortObject(parser.parse(source)) :
|
||||
parser.parse(source);
|
||||
|
||||
if (options.validate) {
|
||||
var env = JSV.createEnvironment(options.env);
|
||||
var schema = JSON.parse(fs.readFileSync(path.normalize(options.validate), "utf8"));
|
||||
var report = env.validate(parsed, schema);
|
||||
if (report.errors.length) {
|
||||
throw report.errors.reduce(schemaError, 'Validation Errors:');
|
||||
}
|
||||
}
|
||||
|
||||
return JSON.stringify(parsed,null,options.indent);
|
||||
} catch (e) {
|
||||
if (! options.compact) {
|
||||
console.log(e);
|
||||
if ( options.forcePrettyPrint ) {
|
||||
/* From https://github.com/umbrae/jsonlintdotcom:
|
||||
* If we failed to validate, run our manual formatter and then re-validate so that we
|
||||
* can get a better line number. On a successful validate, we don't want to run our
|
||||
* manual formatter because the automatic one is faster and probably more reliable.
|
||||
*/
|
||||
|
||||
try {
|
||||
formatted = formatter.formatJson(source, options.indent);
|
||||
// Re-parse so exception output gets better line numbers
|
||||
parsed = parser.parse(formatted);
|
||||
} catch (e) {
|
||||
if (! options.compact) {
|
||||
console.error(e);
|
||||
}
|
||||
return formatted;
|
||||
process.exit(1);
|
||||
}
|
||||
} else {
|
||||
if (! options.compact) {
|
||||
console.error(e);
|
||||
}
|
||||
|
||||
process.exit(1);
|
||||
}
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
function schemaError (str, err) {
|
||||
return str +
|
||||
"\n\n"+err.message +
|
||||
"\nuri: " + err.uri +
|
||||
"\nschemaUri: " + err.schemaUri +
|
||||
"\nattribute: " + err.attribute +
|
||||
"\ndetails: " + JSON.stringify(err.details);
|
||||
}
|
||||
|
||||
function main (args) {
|
||||
var source = '';
|
||||
if (options.file) {
|
||||
var path = require('path').normalize(options.file);
|
||||
source = parse(fs.readFileSync(path, "utf8"));
|
||||
var json = path.normalize(options.file);
|
||||
source = parse(fs.readFileSync(json, "utf8"));
|
||||
if (options.inplace) {
|
||||
fs.writeSync(fs.openSync(path,'w+'), source, 0, "utf8");
|
||||
fs.writeSync(fs.openSync(json,'w+'), source, 0, "utf8");
|
||||
} else {
|
||||
console.log(source);
|
||||
if (! options.quiet) { console.log(source)};
|
||||
}
|
||||
} else {
|
||||
var stdin = process.openStdin();
|
||||
@@ -80,15 +147,17 @@ function main (args) {
|
||||
source += chunk.toString('utf8');
|
||||
});
|
||||
stdin.on('end', function () {
|
||||
console.log(parse(source));
|
||||
if (! options.quiet) {console.log(parse(source))};
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// from http://stackoverflow.com/questions/1359761/sorting-a-json-object-in-javascript
|
||||
function sortObject(o) {
|
||||
if (Object.prototype.toString.call(o) != '[object Object]')
|
||||
if (Object.prototype.toString.call(o) !== '[object Object]') {
|
||||
return o;
|
||||
}
|
||||
|
||||
var sorted = {},
|
||||
key, a = [];
|
||||
|
||||
|
||||
92
lib/formatter.js
Normal file
92
lib/formatter.js
Normal file
@@ -0,0 +1,92 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
/**
|
||||
* Manual formatter taken straight from https://github.com/umbrae/jsonlintdotcom
|
||||
**/
|
||||
|
||||
/*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 */
|
||||
|
||||
/**
|
||||
* 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).
|
||||
*
|
||||
**/
|
||||
var formatter = (function () {
|
||||
|
||||
function repeat(s, count) {
|
||||
return new Array(count + 1).join(s);
|
||||
}
|
||||
|
||||
function formatJson(json, indentChars) {
|
||||
var i = 0,
|
||||
il = 0,
|
||||
tab = (typeof indentChars !== "undefined") ? indentChars : " ",
|
||||
newJson = "",
|
||||
indentLevel = 0,
|
||||
inString = false,
|
||||
currentChar = null;
|
||||
|
||||
for (i = 0, il = json.length; i < il; i += 1) {
|
||||
currentChar = json.charAt(i);
|
||||
|
||||
switch (currentChar) {
|
||||
case '{':
|
||||
case '[':
|
||||
if (!inString) {
|
||||
newJson += currentChar + "\n" + repeat(tab, indentLevel + 1);
|
||||
indentLevel += 1;
|
||||
} else {
|
||||
newJson += currentChar;
|
||||
}
|
||||
break;
|
||||
case '}':
|
||||
case ']':
|
||||
if (!inString) {
|
||||
indentLevel -= 1;
|
||||
newJson += "\n" + repeat(tab, indentLevel) + currentChar;
|
||||
} else {
|
||||
newJson += currentChar;
|
||||
}
|
||||
break;
|
||||
case ',':
|
||||
if (!inString) {
|
||||
newJson += ",\n" + repeat(tab, indentLevel);
|
||||
} else {
|
||||
newJson += currentChar;
|
||||
}
|
||||
break;
|
||||
case ':':
|
||||
if (!inString) {
|
||||
newJson += ": ";
|
||||
} else {
|
||||
newJson += currentChar;
|
||||
}
|
||||
break;
|
||||
case ' ':
|
||||
case "\n":
|
||||
case "\t":
|
||||
if (inString) {
|
||||
newJson += currentChar;
|
||||
}
|
||||
break;
|
||||
case '"':
|
||||
if (i > 0 && json.charAt(i - 1) !== '\\') {
|
||||
inString = !inString;
|
||||
}
|
||||
newJson += currentChar;
|
||||
break;
|
||||
default:
|
||||
newJson += currentChar;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return newJson;
|
||||
}
|
||||
|
||||
return { "formatJson": formatJson };
|
||||
|
||||
}());
|
||||
|
||||
if (typeof require !== 'undefined' && typeof exports !== 'undefined') {
|
||||
exports.formatter = formatter;
|
||||
}
|
||||
@@ -9,7 +9,15 @@ performAction: function anonymous(yytext,yyleng,yylineno,yy,yystate,$$,_$) {
|
||||
|
||||
var $0 = $$.length - 1;
|
||||
switch (yystate) {
|
||||
case 1:this.$ = yytext;
|
||||
case 1: // replace escaped characters with actual character
|
||||
this.$ = yytext.replace(/\\(\\|")/g, "$"+"1")
|
||||
.replace(/\\n/g,'\n')
|
||||
.replace(/\\r/g,'\r')
|
||||
.replace(/\\t/g,'\t')
|
||||
.replace(/\\v/g,'\v')
|
||||
.replace(/\\f/g,'\f')
|
||||
.replace(/\\b/g,'\b');
|
||||
|
||||
break;
|
||||
case 2:this.$ = Number(yytext);
|
||||
break;
|
||||
@@ -86,7 +94,7 @@ parse: function parse(input) {
|
||||
token = self.symbols_[token] || token;
|
||||
}
|
||||
return token;
|
||||
};
|
||||
}
|
||||
|
||||
var symbol, preErrorSymbol, state, action, a, r, yyval={},p,len,newState, expected;
|
||||
while (true) {
|
||||
@@ -104,6 +112,7 @@ parse: function parse(input) {
|
||||
}
|
||||
|
||||
// handle parse error
|
||||
_handle_error:
|
||||
if (typeof action === 'undefined' || !action.length || !action[0]) {
|
||||
|
||||
if (!recovering) {
|
||||
@@ -114,7 +123,7 @@ parse: function parse(input) {
|
||||
}
|
||||
var errStr = '';
|
||||
if (this.lexer.showPosition) {
|
||||
errStr = 'Parse error on line '+(yylineno+1)+":\n"+this.lexer.showPosition()+'\nExpecting '+expected.join(', ');
|
||||
errStr = 'Parse error on line '+(yylineno+1)+":\n"+this.lexer.showPosition()+"\nExpecting "+expected.join(', ') + ", got '" + this.terminals_[symbol]+ "'";
|
||||
} else {
|
||||
errStr = 'Parse error on line '+(yylineno+1)+": Unexpected " +
|
||||
(symbol == 1 /*EOF*/ ? "end of input" :
|
||||
@@ -228,8 +237,10 @@ parse: function parse(input) {
|
||||
}
|
||||
|
||||
return true;
|
||||
}};/* Jison generated lexer */
|
||||
var lexer = (function(){var lexer = ({EOF:1,
|
||||
}};
|
||||
/* Jison generated lexer */
|
||||
var lexer = (function(){
|
||||
var lexer = ({EOF:1,
|
||||
parseError:function parseError(str, hash) {
|
||||
if (this.yy.parseError) {
|
||||
this.yy.parseError(str, hash);
|
||||
@@ -265,6 +276,9 @@ more:function () {
|
||||
this._more = true;
|
||||
return this;
|
||||
},
|
||||
less:function (n) {
|
||||
this._input = this.match.slice(n) + this._input;
|
||||
},
|
||||
pastInput:function () {
|
||||
var past = this.matched.substr(0, this.matched.length - this.match.length);
|
||||
return (past.length > 20 ? '...':'') + past.substr(-20).replace(/\n/g, "");
|
||||
@@ -289,6 +303,8 @@ next:function () {
|
||||
|
||||
var token,
|
||||
match,
|
||||
tempMatch,
|
||||
index,
|
||||
col,
|
||||
lines;
|
||||
if (!this._more) {
|
||||
@@ -297,26 +313,31 @@ next:function () {
|
||||
}
|
||||
var rules = this._currentRules();
|
||||
for (var i=0;i < rules.length; i++) {
|
||||
match = this._input.match(this.rules[rules[i]]);
|
||||
if (match) {
|
||||
lines = match[0].match(/\n.*/g);
|
||||
if (lines) this.yylineno += lines.length;
|
||||
this.yylloc = {first_line: this.yylloc.last_line,
|
||||
last_line: this.yylineno+1,
|
||||
first_column: this.yylloc.last_column,
|
||||
last_column: lines ? lines[lines.length-1].length-1 : this.yylloc.last_column + match[0].length}
|
||||
this.yytext += match[0];
|
||||
this.match += match[0];
|
||||
this.matches = match;
|
||||
this.yyleng = this.yytext.length;
|
||||
this._more = false;
|
||||
this._input = this._input.slice(match[0].length);
|
||||
this.matched += match[0];
|
||||
token = this.performAction.call(this, this.yy, this, rules[i],this.conditionStack[this.conditionStack.length-1]);
|
||||
if (token) return token;
|
||||
else return;
|
||||
tempMatch = this._input.match(this.rules[rules[i]]);
|
||||
if (tempMatch && (!match || tempMatch[0].length > match[0].length)) {
|
||||
match = tempMatch;
|
||||
index = i;
|
||||
if (!this.options.flex) break;
|
||||
}
|
||||
}
|
||||
if (match) {
|
||||
lines = match[0].match(/\n.*/g);
|
||||
if (lines) this.yylineno += lines.length;
|
||||
this.yylloc = {first_line: this.yylloc.last_line,
|
||||
last_line: this.yylineno+1,
|
||||
first_column: this.yylloc.last_column,
|
||||
last_column: lines ? lines[lines.length-1].length-1 : this.yylloc.last_column + match[0].length}
|
||||
this.yytext += match[0];
|
||||
this.match += match[0];
|
||||
this.yyleng = this.yytext.length;
|
||||
this._more = false;
|
||||
this._input = this._input.slice(match[0].length);
|
||||
this.matched += match[0];
|
||||
token = this.performAction.call(this, this.yy, this, rules[index],this.conditionStack[this.conditionStack.length-1]);
|
||||
if (this.done && this._input) this.done = false;
|
||||
if (token) return token;
|
||||
else return;
|
||||
}
|
||||
if (this._input === "") {
|
||||
return this.EOF;
|
||||
} else {
|
||||
@@ -340,20 +361,27 @@ popState:function popState() {
|
||||
},
|
||||
_currentRules:function _currentRules() {
|
||||
return this.conditions[this.conditionStack[this.conditionStack.length-1]].rules;
|
||||
},
|
||||
topState:function () {
|
||||
return this.conditionStack[this.conditionStack.length-2];
|
||||
},
|
||||
pushState:function begin(condition) {
|
||||
this.begin(condition);
|
||||
}});
|
||||
lexer.options = {};
|
||||
lexer.performAction = function anonymous(yy,yy_,$avoiding_name_collisions,YY_START) {
|
||||
|
||||
var YYSTATE=YY_START
|
||||
switch($avoiding_name_collisions) {
|
||||
case 0:/* skip whitespace */
|
||||
break;
|
||||
case 1:return 6;
|
||||
case 1:return 6
|
||||
break;
|
||||
case 2:yy_.yytext = yy_.yytext.substr(1,yy_.yyleng-2); return 4;
|
||||
case 2:yy_.yytext = yy_.yytext.substr(1,yy_.yyleng-2); return 4
|
||||
break;
|
||||
case 3: return 17
|
||||
case 3:return 17
|
||||
break;
|
||||
case 4: return 18
|
||||
case 4:return 18
|
||||
break;
|
||||
case 5:return 23
|
||||
break;
|
||||
@@ -375,8 +403,12 @@ case 13:return 'INVALID'
|
||||
break;
|
||||
}
|
||||
};
|
||||
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/,/^$/,/^./];
|
||||
lexer.conditions = {"INITIAL":{"rules":[0,1,2,3,4,5,6,7,8,9,10,11,12,13],"inclusive":true}};return lexer;})()
|
||||
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)/,/^(?:$)/,/^(?:.)/];
|
||||
lexer.conditions = {"INITIAL":{"rules":[0,1,2,3,4,5,6,7,8,9,10,11,12,13],"inclusive":true}};
|
||||
|
||||
|
||||
;
|
||||
return lexer;})()
|
||||
parser.lexer = lexer;
|
||||
return parser;
|
||||
})();
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
"lint",
|
||||
"jsonlint"
|
||||
],
|
||||
"version": "1.3.2",
|
||||
"version": "1.6.0",
|
||||
"preferGlobal": true,
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@@ -25,7 +25,8 @@
|
||||
"node": ">= 0.6"
|
||||
},
|
||||
"dependencies": {
|
||||
"nomnom": ">= 1.x.x"
|
||||
"nomnom": ">= 1.5.x",
|
||||
"JSV": ">= 4.0.x"
|
||||
},
|
||||
"devDependencies": {
|
||||
"test": "*",
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
var fs = require('fs');
|
||||
var sys = require('sys');
|
||||
|
||||
var source = "var jsonlint = (function(){var require=true,module=false;var exports={};" +
|
||||
var source = "var jsonlint = (function(){var require=true,module=false;var exports={};" +
|
||||
fs.readFileSync(__dirname+'/../lib/jsonlint.js', 'utf8') +
|
||||
"return exports;})()";
|
||||
|
||||
sys.puts(source);
|
||||
console.log(source);
|
||||
|
||||
|
||||
@@ -1,23 +1,24 @@
|
||||
esc "\\"
|
||||
int "-"?([0-9]|[1-9][0-9]+)
|
||||
exp ([eE][-+]?[0-9]+)
|
||||
frac ("."[0-9]+)
|
||||
exp [eE][-+]?[0-9]+
|
||||
frac "."[0-9]+
|
||||
|
||||
%%
|
||||
\s+ {/* skip whitespace */}
|
||||
{int}{frac}?{exp}?\b {return 'NUMBER';}
|
||||
'"'("\\"["bfnrt/{esc}]|"\\u"[a-fA-F0-9]{4}|[^\0-\x09\x0a-\x1f"{esc}])*'"' {yytext = yytext.substr(1,yyleng-2); return 'STRING';}
|
||||
"{" %{ return '{' %}
|
||||
"}" %{ return '}' %}
|
||||
"[" {return '['}
|
||||
"]" {return ']'}
|
||||
"," {return ','}
|
||||
":" {return ':'}
|
||||
"true" {return 'TRUE'}
|
||||
"false" {return 'FALSE'}
|
||||
"null" {return 'NULL'}
|
||||
<<EOF>> {return 'EOF'}
|
||||
. {return 'INVALID'}
|
||||
\s+ /* skip whitespace */
|
||||
|
||||
{int}{frac}?{exp}?\b return 'NUMBER'
|
||||
\"(?:'\\'[\\"bfnrt/]|'\\u'[a-fA-F0-9]{4}|[^\\\0-\x09\x0a-\x1f"])*\" yytext = yytext.substr(1,yyleng-2); return 'STRING'
|
||||
|
||||
"{" return '{'
|
||||
"}" return '}'
|
||||
"[" return '['
|
||||
"]" return ']'
|
||||
"," return ','
|
||||
":" return ':'
|
||||
"true" return 'TRUE'
|
||||
"false" return 'FALSE'
|
||||
"null" return 'NULL'
|
||||
<<EOF>> return 'EOF'
|
||||
. return 'INVALID'
|
||||
|
||||
%%
|
||||
|
||||
|
||||
@@ -1,18 +1,23 @@
|
||||
%start JSONText
|
||||
|
||||
/*
|
||||
ECMA-262 5th Edition, 15.12.1 The JSON Grammar.
|
||||
*/
|
||||
|
||||
|
||||
/* author: Zach Carter */
|
||||
|
||||
|
||||
%start JSONText
|
||||
|
||||
%%
|
||||
|
||||
JSONString
|
||||
: STRING
|
||||
{$$ = yytext;}
|
||||
{ // replace escaped characters with actual character
|
||||
$$ = yytext.replace(/\\(\\|")/g, "$"+"1")
|
||||
.replace(/\\n/g,'\n')
|
||||
.replace(/\\r/g,'\r')
|
||||
.replace(/\\t/g,'\t')
|
||||
.replace(/\\v/g,'\v')
|
||||
.replace(/\\f/g,'\f')
|
||||
.replace(/\\b/g,'\b');
|
||||
}
|
||||
;
|
||||
|
||||
JSONNumber
|
||||
|
||||
@@ -7,9 +7,25 @@ exports["test object"] = function () {
|
||||
assert.deepEqual(parser.parse(json), {"foo": "bar"});
|
||||
};
|
||||
|
||||
exports["test escaped backslash"] = function () {
|
||||
var json = '{"foo": "\\\\"}';
|
||||
assert.deepEqual(parser.parse(json), {"foo": "\\"});
|
||||
};
|
||||
|
||||
exports["test escaped chars"] = function () {
|
||||
var json = '{"foo": "\\\\\\\""}';
|
||||
assert.deepEqual(parser.parse(json), {"foo": '\\\"'});
|
||||
};
|
||||
|
||||
exports["test escaped \\n"] = function () {
|
||||
var json = '{"foo": "\\\\\\n"}';
|
||||
assert.deepEqual(parser.parse(json), {"foo": '\\\n'});
|
||||
};
|
||||
|
||||
exports["test string with escaped line break"] = function () {
|
||||
var json = '{"foo": "bar\\nbar"}';
|
||||
assert.deepEqual(parser.parse(json), {"foo": "bar\\nbar"});
|
||||
assert.deepEqual(parser.parse(json), {"foo": "bar\nbar"});
|
||||
assert.equal(JSON.stringify(parser.parse(json)).length, 18);
|
||||
};
|
||||
|
||||
exports["test string with line break"] = function () {
|
||||
|
||||
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user