Skip to content
This repository has been archived by the owner on May 28, 2019. It is now read-only.

Commit

Permalink
lex: Switch to character codes. Closes #27; see #23.
Browse files Browse the repository at this point in the history
  • Loading branch information
Kit Cambridge committed Mar 23, 2013
1 parent 65d583c commit 2662169
Showing 1 changed file with 81 additions and 82 deletions.
163 changes: 81 additions & 82 deletions lib/json3.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,10 +45,10 @@
// bracket notation. IE 8 only supports this for primitives.
return "A"[0] != "A";
}
var value, serialized = '{"A":[1,true,false,null,"\\u0000\\b\\n\\f\\r\\t"]}', all = name == "json";
if (all || name == "json-stringify" || name == "json-parse") {
var value, serialized = '{"A":[1,true,false,null,"\\u0000\\b\\n\\f\\r\\t"]}', isAll = name == "json";
if (isAll || name == "json-stringify" || name == "json-parse") {
// Test `JSON.stringify`.
if (name == "json-stringify" || all) {
if (name == "json-stringify" || isAll) {
var stringify = JSON3.stringify, stringifySupported = typeof stringify == "function" && isExtended;
if (stringifySupported) {
// A test function object with a custom `toJSON` method.
Expand Down Expand Up @@ -114,12 +114,12 @@
stringifySupported = false;
}
}
if (!all) {
if (!isAll) {
return stringifySupported;
}
}
// Test `JSON.parse`.
if (name == "json-parse" || all) {
if (name == "json-parse" || isAll) {
var parse = JSON3.parse;
if (typeof parse == "function") {
try {
Expand Down Expand Up @@ -149,7 +149,7 @@
parseSupported = false;
}
}
if (!all) {
if (!isAll) {
return parseSupported;
}
}
Expand Down Expand Up @@ -328,7 +328,7 @@
// Internal: Recursively serializes an object. Implements the
// `Str(key, holder)`, `JO(value)`, and `JA(value)` operations.
var serialize = function (property, object, callback, properties, whitespace, indentation, stack) {
var value = object[property], className, year, month, date, time, hours, minutes, seconds, milliseconds, results, element, index, length, prefix, any, result;
var value = object[property], className, year, month, date, time, hours, minutes, seconds, milliseconds, results, element, index, length, prefix, hasMembers, result;
if (typeof value == "object" && value) {
className = getClass.call(value);
if (className == "[object Date]" && !isProperty.call(value, "toJSON")) {
Expand Down Expand Up @@ -421,11 +421,11 @@
indentation += whitespace;
if (className == "[object Array]") {
// Recursively serialize array elements.
for (index = 0, length = value.length; index < length; any || (any = true), index++) {
for (index = 0, length = value.length; index < length; hasMembers || (hasMembers = true), index++) {
element = serialize(index, value, callback, properties, whitespace, indentation, stack);
results.push(element === undef ? "null" : element);
}
result = any ? (whitespace ? "[\n" + indentation + results.join(",\n" + indentation) + "\n" + prefix + "]" : ("[" + results.join(",") + "]")) : "[]";
result = hasMembers ? (whitespace ? "[\n" + indentation + results.join(",\n" + indentation) + "\n" + prefix + "]" : ("[" + results.join(",") + "]")) : "[]";
} else {
// Recursively serialize object members. Members are selected from
// either a user-specified list of property names, or the object
Expand All @@ -441,9 +441,9 @@
// `JSON.stringify`.
results.push(quote(charIndexBuggy ? property.split("") : property) + ":" + (whitespace ? " " : "") + element);
}
any || (any = true);
hasMembers || (hasMembers = true);
});
result = any ? (whitespace ? "{\n" + indentation + results.join(",\n" + indentation) + "\n" + prefix + "}" : ("{" + results.join(",") + "}")) : "{}";
result = hasMembers ? (whitespace ? "{\n" + indentation + results.join(",\n" + indentation) + "\n" + prefix + "}" : ("{" + results.join(",") + "}")) : "{}";
}
// Remove the object from the traversed object stack.
stack.pop();
Expand Down Expand Up @@ -511,60 +511,65 @@
// the end of the source string. A token may be a string, number, `null`
// literal, or Boolean literal.
var lex = function () {
var source = Source, length = source.length, symbol, value, begin, position, sign, charCode;
var source = Source, length = source.length, value, begin, position, isSigned, charCode;
while (Index < length) {
charCode = source.charCodeAt(Index);
symbol = source[Index];
switch (charCode) {
case 9: case 10: case 13: case 32:
// Skip whitespace tokens, including tabs, carriage returns, line
// feeds, and space characters.
Index++;
break;
case 123: case 125: case 91: case 93: case 58: case 44:
// Parse a punctuator token at the current position. One of `{`,
// `}`, `[`, `]`, `:`, `,`
// Parse a punctuator token (`{`, `}`, `[`, `]`, `:`, or `,`) at
// the current position.
value = charIndexBuggy ? source.charAt(Index) : source[Index];
Index++;
return symbol;
return value;
case 34:
// `"` marks a JSON string, advance to the next character and
// parse the string at the current position. String tokens are
// prefixed with the sentinel `@` character to distinguish them
// from punctuators.
// `"` delimits a JSON string; advance to the next character and
// begin parsing the string. String tokens are prefixed with the
// sentinel `@` character to distinguish them from punctuators.
for (value = "@", Index++; Index < length;) {
symbol = source[Index];
if (symbol < " ") {
// Unescaped ASCII control characters are not permitted.
charCode = source.charCodeAt(Index);
if (charCode < 32) {
// Unescaped ASCII control characters (those with a code unit
// less than the space character) are not permitted.
abort();
} else if (symbol == "\\") {
// Parse escaped JSON control characters, `"`, `\`, `/`, and
// Unicode escape sequences.
symbol = source[++Index];
if ('\\"/btnfr'.indexOf(symbol) > -1) {
// Revive escaped control characters.
value += Unescapes[symbol];
Index++;
} else if (symbol == "u") {
// Advance to the first character of the escape sequence.
begin = ++Index;
// Validate the Unicode escape sequence.
for (position = Index + 4; Index < position; Index++) {
symbol = source[Index];
// A valid sequence comprises four hexdigits that form a
// single hexadecimal value.
if (!(symbol >= "0" && symbol <= "9" || symbol >= "a" && symbol <= "f" || symbol >= "A" && symbol <= "F")) {
// Invalid Unicode escape sequence.
abort();
} else if (charCode == 92) {
// A reverse solidus (`\`) marks the beginning of an escaped
// control character (including `"`, `\`, and `/`) or Unicode
// escape sequence.
charCode = source.charCodeAt(++Index);
switch (charCode) {
case 92: case 34: case 47: case 98: case 116: case 110: case 102: case 114:
// Revive escaped control characters.
value += Unescapes[charIndexBuggy ? source.charAt(Index) : source[Index]];
Index++;
break;
case 117:
// `\u` marks the beginning of a Unicode escape sequence.
// Advance to the first character and validate the
// four-digit code point.
begin = ++Index;
for (position = Index + 4; Index < position; Index++) {
charCode = source.charCodeAt(Index);
// A valid sequence comprises four hexdigits (case-
// insensitive) that form a single hexadecimal value.
if (!(charCode >= 48 && charCode <= 57 || charCode >= 97 && charCode <= 102 || charCode >= 65 && charCode <= 70)) {
// Invalid Unicode escape sequence.
abort();
}
}
}
// Revive the escaped character.
value += fromCharCode("0x" + source.slice(begin, Index));
} else {
// Invalid escape sequence.
abort();
// Revive the escaped character.
value += fromCharCode("0x" + source.slice(begin, Index));
break;
default:
// Invalid escape sequence.
abort();
}
} else {
if (symbol == '"') {
if (charCode == 34) {
// An unescaped double-quote character marks the end of the
// string.
break;
Expand All @@ -579,56 +584,55 @@
value += source.slice(begin, Index);
}
}
if (source[Index] == '"') {
if (source.charCodeAt(Index) == 34) {
// Advance to the next character and return the revived string.
Index++;
// Return the revived string.
return value;
}
// Unterminated string.
abort();
break;
default:
// Parse numbers and literals.
begin = Index;
// Advance the scanner's position past the sign, if one is
// specified.
if (symbol == "-") {
sign = true;
symbol = source[++Index];
// Advance past the negative sign, if one is specified.
if (charCode == 45) {
isSigned = true;
charCode = source.charCodeAt(++Index);
}
// Parse an integer or floating-point value.
if (symbol >= "0" && symbol <= "9") {
if (charCode >= 48 && charCode <= 57) {
// Leading zeroes are interpreted as octal literals.
if (symbol == "0" && (symbol = source[Index + 1], symbol >= "0" && symbol <= "9")) {
if (charCode == 48 && ((charCode = source.charCodeAt(Index + 1)), charCode >= 48 && charCode <= 57)) {
// Illegal octal literal.
abort();
}
sign = false;
isSigned = false;
// Parse the integer component.
for (; Index < length && (symbol = source[Index], symbol >= "0" && symbol <= "9"); Index++);
for (; Index < length && ((charCode = source.charCodeAt(Index)), charCode >= 48 && charCode <= 57); Index++);
// Floats cannot contain a leading decimal point; however, this
// case is already accounted for by the parser.
if (source[Index] == ".") {
if (source.charCodeAt(Index) == 46) {
position = ++Index;
// Parse the decimal component.
for (; position < length && (symbol = source[position], symbol >= "0" && symbol <= "9"); position++);
for (; position < length && ((charCode = source.charCodeAt(position)), charCode >= 48 && charCode <= 57); position++);
if (position == Index) {
// Illegal trailing decimal.
abort();
}
Index = position;
}
// Parse exponents.
symbol = source[Index];
if (symbol == "e" || symbol == "E") {
// Parse exponents. The `e` denoting the exponent is
// case-insensitive.
charCode = source.charCodeAt(Index);
if (charCode == 101 || charCode == 69) {
charCode = source.charCodeAt(++Index);
// Skip past the sign following the exponent, if one is
// specified.
symbol = source[++Index];
if (symbol == "+" || symbol == "-") {
if (charCode == 43 || charCode == 45) {
Index++;
}
// Parse the exponential component.
for (position = Index; position < length && (symbol = source[position], symbol >= "0" && symbol <= "9"); position++);
for (position = Index; position < length && ((charCode = source.charCodeAt(position)), charCode >= 48 && charCode <= 57); position++);
if (position == Index) {
// Illegal empty exponent.
abort();
Expand All @@ -639,7 +643,7 @@
return +source.slice(begin, Index);
}
// A negative sign may only precede numbers.
if (sign) {
if (isSigned) {
abort();
}
// `true`, `false`, and `null` literals.
Expand All @@ -655,7 +659,6 @@
}
// Unrecognized token.
abort();
break;
}
}
// Return the sentinel `$` character if the parser has reached the end
Expand All @@ -665,7 +668,7 @@

// Internal: Parses a JSON `value` token.
var get = function (value) {
var results, any, key;
var results, hasMembers, key;
if (value == "$") {
// Unexpected end of input.
abort();
Expand All @@ -679,7 +682,7 @@
if (value == "[") {
// Parses a JSON array, returning a new JavaScript array.
results = [];
for (;; any || (any = true)) {
for (;; hasMembers || (hasMembers = true)) {
value = lex();
// A closing square bracket marks the end of the array literal.
if (value == "]") {
Expand All @@ -688,7 +691,7 @@
// If the array literal contains elements, the current token
// should be a comma separating the previous element from the
// next.
if (any) {
if (hasMembers) {
if (value == ",") {
value = lex();
if (value == "]") {
Expand All @@ -704,21 +707,21 @@
if (value == ",") {
abort();
}
results.push(get(typeof value == "string" && charIndexBuggy ? value.split("") : value));
results.push(get(value));
}
return results;
} else if (value == "{") {
// Parses a JSON object, returning a new JavaScript object.
results = {};
for (;; any || (any = true)) {
for (;; hasMembers || (hasMembers = true)) {
value = lex();
// A closing curly brace marks the end of the object literal.
if (value == "}") {
break;
}
// If the object literal contains members, the current token
// should be a comma separator.
if (any) {
if (hasMembers) {
if (value == ",") {
value = lex();
if (value == "}") {
Expand All @@ -736,8 +739,7 @@
if (value == "," || typeof value != "string" || value[0] != "@" || lex() != ":") {
abort();
}
var result = lex();
results[value.slice(1)] = get(typeof result == "string" && charIndexBuggy ? result.split("") : result);
results[value.slice(1)] = get(lex());
}
return results;
}
Expand Down Expand Up @@ -784,9 +786,6 @@
var result, value;
Index = 0;
Source = "" + source;
if (charIndexBuggy) {
Source = source.split("");
}
result = get(lex());
// If a JSON string contains multiple tokens, it is invalid.
if (lex() != "$") {
Expand Down

0 comments on commit 2662169

Please sign in to comment.