Comparing version 1.0.0-beta.4 to 1.0.0-beta.5
@@ -0,1 +1,16 @@ | ||
## 1.0.0-beta.5 (November 10, 2020) | ||
- Jora became a superset of JSON5 (see #23) | ||
- Added support for template literals | ||
- Added support for multi-line comments | ||
- Added support for hexadecimal numbers | ||
- Added support for hexadecimal escape sequence in strings, i.e. `'hello\x20world'` | ||
- Added support for multiple lines in strings by escaping new line characters | ||
- Added `Infinity` and `NaN` literals | ||
- Fixed string literal parsing to be ECMA-262 compliant | ||
- Changed parsing to recognize line terminators according to ECMA-262 (added `\r\n`, `\u2028` and `\u2029`) | ||
- Allowed a single trailing comma in object and array literals | ||
- Allowed unicode escape sequence in identifier name | ||
- Allowed object literal property names starting with `$` when a value is defined | ||
## 1.0.0-beta.4 (November 4, 2020) | ||
@@ -2,0 +17,0 @@ |
{ | ||
"name": "jora", | ||
"version": "1.0.0-beta.4", | ||
"version": "1.0.0-beta.5", | ||
"description": "JavaScript object query engine", | ||
@@ -43,3 +43,3 @@ "author": "Roman Dvornov <rdvornov@gmail.com> (https://github.com/lahmatiy)", | ||
"eslint": "^6.5.1", | ||
"jison": "^0.4.18", | ||
"@lahmatiy/jison": "^0.4.18-remastered.1", | ||
"mocha": "^6.2.3", | ||
@@ -51,3 +51,3 @@ "nyc": "^14.1.0", | ||
"engines": { | ||
"node": ">=8.0.0" | ||
"node": ">=8.10.0" | ||
}, | ||
@@ -54,0 +54,0 @@ "files": [ |
@@ -54,2 +54,3 @@ # Jora | ||
- [Syntax](#syntax) | ||
- [Comments](#comments) | ||
- [Primitives](#primitives) | ||
@@ -98,6 +99,4 @@ - [Keywords](#keywords) | ||
// or with custom methods | ||
const queryWithCustomMethods = jora('foo.myMethod()', { | ||
methods: { | ||
myMethod(current) { /* do something and return a new value */ } | ||
} | ||
const queryWithCustomMethods = jora.setup({ | ||
myMethod(current) { /* do something and return a new value */ } | ||
}); | ||
@@ -107,2 +106,3 @@ | ||
const result = query(data, context); | ||
const result = queryWithCustomMethods('foo.myMethod()')(data, context); | ||
``` | ||
@@ -204,2 +204,10 @@ | ||
### Comments | ||
``` | ||
// single-line comment | ||
/* multi-line | ||
comment */ | ||
``` | ||
### Primitives | ||
@@ -210,3 +218,5 @@ | ||
42<br>-123<br>4.22<br>1e3<br>1e-2 | Numbers | ||
0xdecaf<br>-0xC0FFEE | Hexadecimal numbers | ||
"string"<br>'string' | Strings | ||
\`template line1<br>template line2\`<br>\`template ${hello} ${world}` | Template | ||
/regexp/<br>/regexp/i | A JavaScript regexp, only `i` flag supported | ||
@@ -229,2 +239,4 @@ { } | Object initializer/literal syntax. Spread operator (`...`) can be used, e.g. `{ a: 1, ..., ...foo }` (`...` with no expression on right side the same as `...$`) | ||
- `undefined` | ||
- `Infinity` | ||
- `NaN` | ||
@@ -231,0 +243,0 @@ ### Operators |
@@ -24,6 +24,6 @@ const { isPlainObject } = require('../utils'); | ||
Object, | ||
ObjectEntry, | ||
Parentheses, | ||
Pick, | ||
Pipeline, | ||
ObjectEntry, | ||
Reference, | ||
@@ -33,2 +33,3 @@ SliceNotation, | ||
Spread, | ||
Template, | ||
Unary | ||
@@ -41,2 +42,3 @@ } = require('./nodes').build; | ||
const $1name = { name: '$1.name' }; | ||
const $$1name = { name: '"$" + $1.name' }; | ||
const $2 = { name: '$2' }; | ||
@@ -48,3 +50,3 @@ const $3 = { name: '$3' }; | ||
const $r1 = { name: '@1.range' }; | ||
const refs = new Set([$0, $1, $1name, $2, $3, $4, $5, $r0, $r1]); | ||
const refs = new Set([$0, $1, $1name, $$1name, $2, $3, $4, $5, $r0, $r1]); | ||
const asis = ''; | ||
@@ -108,6 +110,24 @@ | ||
const switchToPreventPrimitiveState = 'if (this._input) this.begin("preventPrimitive"); '; | ||
const openScope = 'this.fnOpenedStack.push(this.fnOpened); this.fnOpened = 0; '; | ||
const closeScope = 'this.fnOpened = this.fnOpenedStack.pop() || 0; '; | ||
const templateToken = (input, state) => { | ||
if (input[0] !== (state === 'template' ? '}' : '`')) { | ||
return null; | ||
} | ||
for (let i = 1; i < input.length; i++) { | ||
if (input[i] === '`') { | ||
return i + 1; | ||
} | ||
if (input[i] === '$' && input[i + 1] === '{') { | ||
return i + 2; | ||
} | ||
if (input[i] === '\\') { | ||
i++; | ||
} | ||
} | ||
return null; | ||
}; | ||
module.exports = { | ||
@@ -123,13 +143,14 @@ // Lexical tokens | ||
ws: '\\s+', // required whitespaces | ||
comment: '//.*?(\\r|\\n|$)', | ||
ident: '[a-zA-Z_][a-zA-Z_$0-9]*', | ||
comment: '//.*?(?:\\n|\\r\\n?|\\u2028|\\u2029|$)|/\\*(?:.|\\s)*?(?:\\*/|$)', | ||
ident: '(?:[a-zA-Z_]|\\\\u[0-9a-fA-F]{4})(?:[a-zA-Z_$0-9]|\\\\u[0-9a-fA-F]{4})*', | ||
rx: '/(?:\\\\.|[^/])+/i?' | ||
}, | ||
startConditions: { | ||
preventPrimitive: 0 | ||
preventPrimitive: 0, | ||
template: 1 | ||
}, | ||
rules: [ | ||
// ignore comments and whitespaces | ||
['{comment}', 'yy.commentRanges.push(yylloc.range); /* a comment */'], | ||
['{ws}', '/* a whitespace */'], | ||
['{comment}', 'yy.commentRanges.push(yylloc.range)'], | ||
['{ws}', ''], | ||
@@ -142,14 +163,49 @@ // hack to prevent primitive (i.e. regexp and function) consumption | ||
// should be fixed in `regexp-lexer` | ||
[['preventPrimitive'], '', 'this.done = false; this.popState();'], | ||
[['preventPrimitive'], '', () => { | ||
this.done = false; | ||
this.popState(); | ||
}], | ||
// template | ||
[templateToken, (yy, yytext) => { | ||
const token = yytext.endsWith('`') ? 'TEMPLATE' : 'TPL_START'; | ||
yytext = this.toStringLiteral(yytext, true, 1 + (token !== 'TEMPLATE')); | ||
if (token === 'TEMPLATE') { | ||
yy.pps(); | ||
} | ||
return token; | ||
}], | ||
[['template'], templateToken, (yy, yytext) => { | ||
const token = yytext.endsWith('`') ? 'TPL_END' : 'TPL_CONTINUE'; | ||
yytext = this.toStringLiteral(yytext, true, 1 + (token !== 'TPL_END')); | ||
this.popState(); | ||
if (token === 'TPL_END') { | ||
yy.pps(); | ||
} | ||
return token; | ||
}], | ||
[['template'], '', () => this.parseError('Unexpected end of input')], | ||
// braces | ||
['\\(', openScope + 'return "(";'], | ||
['\\)', closeScope + switchToPreventPrimitiveState + 'return ")";'], | ||
['\\[', openScope + 'return "[";'], | ||
['\\]', closeScope + switchToPreventPrimitiveState + 'return "]";'], | ||
['\\{', openScope + 'return "{";'], | ||
['\\}', closeScope + switchToPreventPrimitiveState + 'return "}";'], | ||
['\\(', 'return "(";'], | ||
['\\)', 'yy.pps(); return ")";'], | ||
['\\[', 'return "[";'], | ||
['\\]', 'yy.pps(); return "]";'], | ||
['\\{', 'return "{";'], | ||
['\\}', (yy) => { | ||
if (this.bracketStack[this.bracketStack.length - 1] !== 'TPL_END') { | ||
yy.pps(); | ||
return '}'; | ||
} | ||
this.unput('}'); | ||
this.begin('template'); | ||
}], | ||
// keywords (should goes before ident) | ||
['(true|false|null|undefined){wb}', 'yytext = this.toLiteral(yytext);return "LITERAL";'], | ||
// eslint-disable-next-line no-unused-vars | ||
['(true|false|null|undefined|Infinity|NaN){wb}', (yytext) => { | ||
yytext = this.toLiteral(yytext); | ||
return 'LITERAL'; | ||
}], | ||
@@ -167,18 +223,22 @@ // keyword operators (should goes before IDENT) | ||
// primitives | ||
['(\\d+\\.|\\.)?\\d+([eE][-+]?\\d+)?{wb}', switchToPreventPrimitiveState + 'yytext = Number(yytext); return "NUMBER";'], // 212.321 | ||
['"(?:\\\\.|[^"])*"', switchToPreventPrimitiveState + 'yytext = this.toStringLiteral(yytext); return "STRING";'], // "foo" "with \" escaped" | ||
["'(?:\\\\.|[^'])*'", switchToPreventPrimitiveState + 'yytext = this.toStringLiteral(yytext); return "STRING";'], // 'foo' 'with \' escaped' | ||
['{rx}', switchToPreventPrimitiveState + 'yytext = this.toRegExp(yytext); return "REGEXP";'], // /foo/i | ||
['{ident}', switchToPreventPrimitiveState + 'return "IDENT";'], // foo123 | ||
['\\${ident}', switchToPreventPrimitiveState + 'yytext = yytext.slice(1); return "$IDENT";'], // $foo123 | ||
['(\\d+\\.|\\.)?\\d+([eE][-+]?\\d+)?{wb}', 'yy.pps(); yytext = Number(yytext); return "NUMBER";'], // 212.321 | ||
['0[xX][0-9a-fA-F]+', 'yy.pps(); yytext = parseInt(yytext, 16); return "NUMBER";'], // 0x12ab | ||
['"(?:\\\\[\\\\"]|[^"])*"', 'yy.pps(); yytext = this.toStringLiteral(yytext); return "STRING";'], // "foo" "with \" escaped" | ||
["'(?:\\\\[\\\\']|[^'])*'", 'yy.pps(); yytext = this.toStringLiteral(yytext); return "STRING";'], // 'foo' 'with \' escaped' | ||
['{rx}', 'yy.pps(); yytext = this.toRegExp(yytext); return "REGEXP";'], // /foo/i | ||
['{ident}', 'yy.pps(); yytext = this.ident(yytext); return "IDENT";'], // foo123 | ||
['\\${ident}', 'yy.pps(); yytext = this.ident(yytext.slice(1)); return "$IDENT";'], // $foo123 | ||
// special vars | ||
['@', switchToPreventPrimitiveState + 'return "@";'], | ||
['#', switchToPreventPrimitiveState + 'return "#";'], | ||
['\\${2}', switchToPreventPrimitiveState + 'return "$$";'], | ||
['\\$', switchToPreventPrimitiveState + 'return "$";'], | ||
['@', 'yy.pps(); return "@";'], | ||
['#', 'yy.pps(); return "#";'], | ||
['\\$\\$', 'yy.pps(); return "$$";'], | ||
['\\$', 'yy.pps(); return "$";'], | ||
// functions | ||
['=>', 'return "FUNCTION";'], | ||
['<(?!=)', 'this.fnOpened++; return "FUNCTION_START"'], | ||
['<(?!=)', () => { | ||
this.fnOpened++; | ||
return 'FUNCTION_START'; | ||
}], | ||
@@ -192,15 +252,15 @@ // operators | ||
['<', 'return "<";'], | ||
['>', ` | ||
['>', () => { | ||
if (this.fnOpened) { | ||
this.fnOpened--; | ||
return "FUNCTION_END"; | ||
return 'FUNCTION_END'; | ||
} | ||
return ">"; | ||
`], | ||
['\\.\\.\\(', openScope + 'return "..(";'], | ||
['\\.\\(', openScope + 'return ".(";'], | ||
['\\.\\[', openScope + 'return ".[";'], | ||
return '>'; | ||
}], | ||
['\\.\\.\\(', 'return "..(";'], | ||
['\\.\\(', 'return ".(";'], | ||
['\\.\\[', 'return ".[";'], | ||
['\\.\\.\\.', 'return "...";'], | ||
['\\.\\.', switchToPreventPrimitiveState + 'return "..";'], | ||
['\\.', switchToPreventPrimitiveState + 'return ".";'], | ||
['\\.\\.', 'yy.pps(); return "..";'], | ||
['\\.', 'yy.pps(); return ".";'], | ||
['\\?', 'return "?";'], | ||
@@ -336,2 +396,3 @@ [',', 'return ",";'], | ||
['LITERAL', $$(Literal($1))], | ||
['template', $$(Template($1))], | ||
['object', asis], | ||
@@ -373,13 +434,30 @@ ['array', asis], | ||
template: [ | ||
['templateString', $$([$1])], | ||
['templateStart templateTail', '$$=[$1, ...$2]'] | ||
], | ||
templateTail: [ | ||
['templateEnd', $$([null, $1])], | ||
['e templateEnd', $$([$1, $2])], | ||
['templateContinue templateTail', '$$=[null, $1, ...$2]'], | ||
['e templateContinue templateTail', '$$=[$1, $2, ...$3]'] | ||
], | ||
templateString: [['TEMPLATE', $$(Literal($1))]], | ||
templateStart: [['TPL_START', $$(Literal($1))]], | ||
templateContinue: [['TPL_CONTINUE', $$(Literal($1))]], | ||
templateEnd: [['TPL_END', $$(Literal($1))]], | ||
object: [ | ||
['{ }', $$(Object([]))], | ||
['{ properties }', $$(Object($2))], | ||
['{ properties , }', $$(Object($2))], | ||
['{ definitions }', $$(Object([]))], | ||
['{ definitions properties }', $$(Block($2, Object($3)))] | ||
['{ definitions properties }', $$(Block($2, Object($3)))], | ||
['{ definitions properties , }', $$(Block($2, Object($3)))] | ||
], | ||
properties: createCommaList('properties', 'property'), | ||
property: [ | ||
['ident', $$(ObjectEntry($1, null))], | ||
['$', $$(ObjectEntry(Current(), null))], // do nothing, but collect stat (suggestions) | ||
['$ident', $$(ObjectEntry(Reference($1), null))], | ||
['ident', $$(ObjectEntry($1, null))], | ||
['ident : e', $$(ObjectEntry($1, $3))], | ||
@@ -389,2 +467,3 @@ ['STRING : e', $$(ObjectEntry(Literal($1), $3))], | ||
['LITERAL : e', $$(ObjectEntry(Literal($1), $3))], | ||
['$ident : e', $$(ObjectEntry(Identifier($$1name), $3))], | ||
['[ e ] : e', $$(ObjectEntry($2, $5))], | ||
@@ -403,3 +482,4 @@ ['...', $$(Spread(null))], | ||
['[ ]', $$(Array([]))], | ||
['[ arrayElements ]', $$(Array($2))] | ||
['[ arrayElements ]', $$(Array($2))], | ||
['[ arrayElements , ]', $$(Array($2))] | ||
], | ||
@@ -406,0 +486,0 @@ |
@@ -31,2 +31,3 @@ const nodes = { | ||
Spread: require('./Spread'), | ||
Template: require('./Template'), | ||
Unary: require('./Unary') | ||
@@ -33,0 +34,0 @@ }; |
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
421483
52
8630
358