Comparing version 0.1.5 to 0.1.6
@@ -23,8 +23,24 @@ "use strict"; | ||
const { | ||
length | ||
} = expr.args; | ||
const body = compile(expr.body, options, compile); | ||
const isConst = (0, _.hasConstValue)(body); | ||
const isAsync = (0, _.hasAsyncValue)(body); | ||
let isConst = (0, _.hasConstValue)(body); | ||
let isAsync = (0, _.hasAsyncValue)(body); | ||
const args = expr.args.map(arg => { | ||
let defaultValue; | ||
if (arg.variadic) { | ||
defaultValue = () => []; | ||
} else if (arg.default) { | ||
defaultValue = compile(arg.default, options, compile); | ||
isConst = !isConst && (0, _.hasConstValue)(defaultValue); | ||
isAsync = !isAsync && (0, _.hasAsyncValue)(defaultValue); | ||
} else { | ||
defaultValue = () => undefined; | ||
} | ||
return { | ||
name: arg.name, | ||
variadic: !!arg.variadic, | ||
defaultValue | ||
}; | ||
}); | ||
return (0, _.mark)({ | ||
@@ -34,7 +50,11 @@ isAsync, | ||
}, (context, stack) => { | ||
return (...args) => { | ||
return (...params) => { | ||
const invocationContext = _objectSpread({}, context); | ||
for (let i = 0; i < length; i++) { | ||
invocationContext[expr.args[i].name] = i < length ? args[i] : undefined; | ||
for (let i = 0; i < args.length; i++) { | ||
if (args[i].variadic) { | ||
invocationContext[args[i].name] = params.slice(i); | ||
} else { | ||
invocationContext[args[i].name] = i < params.length ? params[i] : args[i].defaultValue(invocationContext, stack); | ||
} | ||
} | ||
@@ -41,0 +61,0 @@ |
@@ -40,2 +40,6 @@ "use strict"; | ||
var _Group = require("./Compiler/Evaluator/Group"); | ||
var _Spread = require("./Compiler/Evaluator/Spread"); | ||
class ExpressionCompiler extends _Compiler.Compiler { | ||
@@ -65,2 +69,4 @@ constructor() { | ||
this.setCompiler(_Parser.ExpressionType.Lambda, _Lambda.Lambda); | ||
this.setCompiler(_Parser.ExpressionType.Group, _Group.Group); | ||
this.setCompiler(_Parser.ExpressionType.Spread, _Spread.Spread); | ||
} | ||
@@ -67,0 +73,0 @@ |
@@ -29,2 +29,3 @@ "use strict"; | ||
this.appendScanner(_NumberLiteral.NumberLiteral); | ||
this.appendScanner((0, _Punctuation.makePunctuationScanner)('...', _Lexer.TokenType.Ellipsis)); | ||
this.appendScanner((0, _Punctuation.makePunctuationScanner)('.', _Lexer.TokenType.Dot)); | ||
@@ -31,0 +32,0 @@ this.appendScanner((0, _Punctuation.makePunctuationScanner)('(', _Lexer.TokenType.LParen)); |
@@ -40,2 +40,4 @@ "use strict"; | ||
var _Spread = require("./Parser/Parselet/Spread"); | ||
function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } } | ||
@@ -102,4 +104,4 @@ | ||
this.setInfix(_Lexer.TokenType.QuestionDot, _Conditional.NullConditional); | ||
this.setInfix(_Lexer.TokenType.RightArrow, _Lambda.LambdaInfix); | ||
this.setPrefix(_Lexer.TokenType.LParen, _Lambda.LambdaPrefix); | ||
this.setPrefix(_Lexer.TokenType.Ellipsis, _Spread.Spread); | ||
this.setInfix(_Lexer.TokenType.RightArrow, _Lambda.Lambda); | ||
this.setPrefix(_Lexer.TokenType.LParen, _Group.Group); | ||
@@ -106,0 +108,0 @@ this.setPrefix(_Lexer.TokenType.Identifier, _Value.Identifier); |
@@ -6,3 +6,3 @@ "use strict"; | ||
}); | ||
exports.RightArrow = exports.Await = exports.In = exports.Colon = exports.QuestionQuestionEq = exports.QuestionQuestion = exports.QuestionDot = exports.Question = exports.GtGtGtEq = exports.GtGtGt = exports.GtGtEq = exports.GtGt = exports.GtEq = exports.Gt = exports.LtLtEq = exports.LtLt = exports.LtEq = exports.Lt = exports.PercentEq = exports.Percent = exports.SlashEq = exports.Slash = exports.StarStarEq = exports.StarStar = exports.StarEq = exports.Star = exports.TildeEq = exports.Tilde = exports.CaretEq = exports.Caret = exports.PipePipe = exports.PipeEq = exports.Pipe = exports.AmpAmp = exports.AmpEq = exports.Amp = exports.BangEqEq = exports.BangEq = exports.Bang = exports.EqEqEq = exports.EqEq = exports.Eq = exports.MinusMinus = exports.MinusEq = exports.Minus = exports.PlusPlus = exports.PlusEq = exports.Plus = exports.Dot = exports.Comma = exports.RBracket = exports.LBracket = exports.RBrace = exports.LBrace = exports.RParen = exports.LParen = exports.LineTerminator = exports.Whitespace = exports.Identifier = exports.UndefinedLiteral = exports.NullLiteral = exports.BooleanLiteral = exports.StringLiteral = exports.NumberLiteral = exports.EOF = exports.Invalid = exports.Unknown = void 0; | ||
exports.Ellipsis = exports.RightArrow = exports.Await = exports.In = exports.Colon = exports.QuestionQuestionEq = exports.QuestionQuestion = exports.QuestionDot = exports.Question = exports.GtGtGtEq = exports.GtGtGt = exports.GtGtEq = exports.GtGt = exports.GtEq = exports.Gt = exports.LtLtEq = exports.LtLt = exports.LtEq = exports.Lt = exports.PercentEq = exports.Percent = exports.SlashEq = exports.Slash = exports.StarStarEq = exports.StarStar = exports.StarEq = exports.Star = exports.TildeEq = exports.Tilde = exports.CaretEq = exports.Caret = exports.PipePipe = exports.PipeEq = exports.Pipe = exports.AmpAmp = exports.AmpEq = exports.Amp = exports.BangEqEq = exports.BangEq = exports.Bang = exports.EqEqEq = exports.EqEq = exports.Eq = exports.MinusMinus = exports.MinusEq = exports.Minus = exports.PlusPlus = exports.PlusEq = exports.Plus = exports.Dot = exports.Comma = exports.RBracket = exports.LBracket = exports.RBrace = exports.LBrace = exports.RParen = exports.LParen = exports.LineTerminator = exports.Whitespace = exports.Identifier = exports.UndefinedLiteral = exports.NullLiteral = exports.BooleanLiteral = exports.StringLiteral = exports.NumberLiteral = exports.EOF = exports.Invalid = exports.Unknown = void 0; | ||
const Unknown = Symbol.for('numenor:tok:unknown'); | ||
@@ -141,2 +141,4 @@ exports.Unknown = Unknown; | ||
const RightArrow = Symbol.for('numenor:tok:=>'); | ||
exports.RightArrow = RightArrow; | ||
exports.RightArrow = RightArrow; | ||
const Ellipsis = Symbol.for('numenor:tok:...'); | ||
exports.Ellipsis = Ellipsis; |
@@ -6,3 +6,3 @@ "use strict"; | ||
}); | ||
exports.InvalidLeftHandSide = exports.UnexpectedToken = exports.UnknownToken = void 0; | ||
exports.InvalidArgumentList = exports.InvalidLeftHandSide = exports.UnexpectedToken = exports.UnknownToken = void 0; | ||
@@ -30,2 +30,4 @@ function str(tokenType) { | ||
const InvalidLeftHandSide = `Invalid left-hand side in assignment`; | ||
exports.InvalidLeftHandSide = InvalidLeftHandSide; | ||
exports.InvalidLeftHandSide = InvalidLeftHandSide; | ||
const InvalidArgumentList = `Invalid argument list in lambda expression`; | ||
exports.InvalidArgumentList = InvalidArgumentList; |
@@ -6,3 +6,3 @@ "use strict"; | ||
}); | ||
exports.Lambda = exports.ComputedMemberAccess = exports.MemberAccess = exports.Sequence = exports.Await = exports.Call = exports.PostfixOperation = exports.PrefixOperation = exports.BinaryOperation = exports.Conditional = exports.ObjectLiteral = exports.ArrayLiteral = exports.Assignment = exports.UndefinedLiteral = exports.NullLiteral = exports.BooleanLiteral = exports.StringLiteral = exports.NumberLiteral = exports.StackRef = exports.StackPop = exports.StackPush = exports.Identifier = void 0; | ||
exports.Spread = exports.Group = exports.Lambda = exports.ComputedMemberAccess = exports.MemberAccess = exports.Sequence = exports.Await = exports.Call = exports.PostfixOperation = exports.PrefixOperation = exports.BinaryOperation = exports.Conditional = exports.ObjectLiteral = exports.ArrayLiteral = exports.Assignment = exports.UndefinedLiteral = exports.NullLiteral = exports.BooleanLiteral = exports.StringLiteral = exports.NumberLiteral = exports.StackRef = exports.StackPop = exports.StackPush = exports.Identifier = void 0; | ||
const Identifier = Symbol.for('numenor:expr:ident'); | ||
@@ -51,2 +51,6 @@ exports.Identifier = Identifier; | ||
const Lambda = Symbol.for('numenor:expr:lambda'); | ||
exports.Lambda = Lambda; | ||
exports.Lambda = Lambda; | ||
const Group = Symbol.for('numenor:expr:group'); | ||
exports.Group = Group; | ||
const Spread = Symbol.for('numenor:expr:spread'); | ||
exports.Spread = Spread; |
@@ -185,6 +185,7 @@ "use strict"; | ||
this.ignored = new Set(); | ||
this.lexerState = this.lexer.currentState; | ||
} | ||
get state() { | ||
return this.lexer.currentState; | ||
return this.lexerState; | ||
} | ||
@@ -205,2 +206,12 @@ | ||
const { | ||
offset, | ||
line, | ||
col | ||
} = parserContext.shift(); | ||
this.lexerState = { | ||
offset, | ||
line, | ||
col | ||
}; | ||
return expression; | ||
@@ -207,0 +218,0 @@ } |
@@ -8,2 +8,4 @@ "use strict"; | ||
var _ = require("../"); | ||
var _Lexer = require("../../Lexer"); | ||
@@ -18,7 +20,16 @@ | ||
if (parser.accept(_Lexer.TokenType.RParen)) { | ||
return { | ||
type: _.ExpressionType.Group | ||
}; | ||
} | ||
const expression = parser.parse(); | ||
parser.expect(_Lexer.TokenType.RParen); | ||
return expression; | ||
return { | ||
type: _.ExpressionType.Group, | ||
expression | ||
}; | ||
}; | ||
exports.Group = Group; |
@@ -6,3 +6,3 @@ "use strict"; | ||
}); | ||
exports.LambdaInfix = exports.LambdaPrefix = void 0; | ||
exports.Lambda = void 0; | ||
@@ -17,94 +17,54 @@ var _ = require("./"); | ||
var _Value = require("./Value"); | ||
var _Precedence = require("../Precedence"); | ||
const prefix = (parser, token) => { | ||
if (token.type !== _Lexer.TokenType.LParen) { | ||
throw new SyntaxError((0, _Error.UnknownToken)(token)); | ||
const lambda = (parser, lhs, token) => { | ||
if (token.type !== _Lexer.TokenType.RightArrow) { | ||
throw new SyntaxError((0, _Error.UnexpectedToken)(_Lexer.TokenType.RightArrow, token)); | ||
} | ||
const args = []; | ||
let paramList = [lhs]; | ||
if (!parser.accept(_Lexer.TokenType.RParen)) { | ||
do { | ||
// support trailing comma | ||
if (parser.match(_Lexer.TokenType.RParen)) { | ||
// but not (,) | ||
if (args.length === 0) { | ||
throw new SyntaxError((0, _Error.UnknownToken)(parser.token)); | ||
} | ||
if (lhs.type === _2.ExpressionType.Group) { | ||
if (!lhs.expression) { | ||
paramList = []; | ||
} else if (lhs.expression.type === _2.ExpressionType.Sequence) { | ||
paramList = lhs.expression.expressions; | ||
} else { | ||
paramList = [lhs.expression]; | ||
} | ||
} else if (lhs.type !== _2.ExpressionType.Identifier) { | ||
throw new SyntaxError(_Error.InvalidArgumentList); | ||
} | ||
break; | ||
} | ||
const args = paramList.map((expr, index) => { | ||
if (expr.type === _2.ExpressionType.Identifier) { | ||
return { | ||
name: expr.name | ||
}; | ||
} | ||
args.push((0, _Value.Identifier)(parser, parser.shift())); | ||
} while (parser.accept(_Lexer.TokenType.Comma)); | ||
if (expr.type === _2.ExpressionType.Assignment && expr.lhs.type === _2.ExpressionType.Identifier) { | ||
return { | ||
name: expr.lhs.name, | ||
default: expr.rhs | ||
}; | ||
} | ||
parser.expect(_Lexer.TokenType.RParen); | ||
} | ||
if (expr.type === _2.ExpressionType.Spread && expr.rhs.type === _2.ExpressionType.Identifier && index === paramList.length - 1) { | ||
return { | ||
name: expr.rhs.name, | ||
variadic: true | ||
}; | ||
} | ||
parser.expect(_Lexer.TokenType.RightArrow); | ||
throw new SyntaxError(_Error.InvalidArgumentList); | ||
}); | ||
return { | ||
type: _2.ExpressionType.Lambda, | ||
args, | ||
body: parser.parse() | ||
body: parser.parse(_Precedence.Assignment - 1) | ||
}; | ||
}; | ||
const infix = (parser, lhs, token) => { | ||
if (lhs.type !== _2.ExpressionType.Identifier || token.type !== _Lexer.TokenType.RightArrow) { | ||
throw new SyntaxError((0, _Error.UnexpectedToken)(_Lexer.TokenType.RightArrow, token)); | ||
} | ||
return { | ||
type: _2.ExpressionType.Lambda, | ||
args: [lhs], | ||
body: parser.parse() | ||
}; | ||
}; // returns true if following grammar is matched: | ||
// * ::= '(' IdentifierList ')' '=>' | ||
// IdentifierList ::= Identifier | Identifier ',' IdentifierList | <empty> | ||
const matches = (parser, token) => { | ||
let offset = 0; | ||
const peek = () => parser.peek(offset++).type; | ||
if (token.type !== _Lexer.TokenType.LParen) { | ||
return false; | ||
} | ||
list: while (true) { | ||
switch (peek()) { | ||
case _Lexer.TokenType.RParen: | ||
{ | ||
break list; | ||
} | ||
case _Lexer.TokenType.Identifier: | ||
{ | ||
const next = peek(); | ||
if (next === _Lexer.TokenType.RParen) { | ||
break list; | ||
} else if (next === _Lexer.TokenType.Comma) { | ||
continue; | ||
} | ||
return false; | ||
} | ||
default: | ||
{ | ||
return false; | ||
} | ||
} | ||
} | ||
return peek() === _Lexer.TokenType.RightArrow; | ||
}; | ||
const LambdaPrefix = (0, _.makePrefix)(prefix, matches); | ||
exports.LambdaPrefix = LambdaPrefix; | ||
const LambdaInfix = (0, _.makeInfix)(infix, _2.Precedence.Primary); | ||
exports.LambdaInfix = LambdaInfix; | ||
const Lambda = (0, _.makeInfix)(lambda, _2.Precedence.Primary); | ||
exports.Lambda = Lambda; |
@@ -374,2 +374,16 @@ "use strict"; | ||
}); | ||
it('Evaluates lambda expressions with optional arguments', () => { | ||
const lambda = $eval('(x, y = 2) => x * y'); | ||
expect(lambda(16)).toBe(32); | ||
expect(lambda(16, 3)).toBe(48); | ||
expect($eval('(x, y = x * 2) => x * y')(16)).toBe(512); | ||
}); | ||
it('Evaluates lambda expressions with rest arguments', () => { | ||
const lambda = $compile('(n, ...numbers) => numbers.reduce((x, sum) => sum + x * n, 0)', { | ||
NoProtoAccess: false | ||
})(); | ||
expect(lambda(2, 1, 2, 3)).toBe(11); | ||
expect(lambda(2, 42)).toBe(42); | ||
expect(lambda(2)).toBe(0); | ||
}); | ||
}); |
@@ -187,2 +187,3 @@ "use strict"; | ||
it('Parses punctuation operators', () => { | ||
lex('...', token => expect(token.type).toBe(_Lexer.TokenType.Ellipsis)); | ||
lex('.', token => expect(token.type).toBe(_Lexer.TokenType.Dot)); | ||
@@ -189,0 +190,0 @@ lex('(', token => expect(token.type).toBe(_Lexer.TokenType.LParen)); |
@@ -146,3 +146,2 @@ "use strict"; | ||
args: [{ | ||
type: ExpressionType.Identifier, | ||
name: 'x' | ||
@@ -165,3 +164,2 @@ }], | ||
expect(parser.parse('(x) => x ** 2')).toEqual(squareAST); | ||
expect(parser.parse('(x,) => x ** 2')).toEqual(squareAST); | ||
expect(parser.parse('() => 2')).toEqual({ | ||
@@ -178,6 +176,4 @@ type: ExpressionType.Lambda, | ||
args: [{ | ||
type: ExpressionType.Identifier, | ||
name: 'fn' | ||
}, { | ||
type: ExpressionType.Identifier, | ||
name: 'x' | ||
@@ -188,3 +184,2 @@ }], | ||
args: [{ | ||
type: ExpressionType.Identifier, | ||
name: 'y' | ||
@@ -208,6 +203,5 @@ }], | ||
}); | ||
expect(parser.parse('x => y => (a, b, c) => x, a, b, c, y')).toEqual({ | ||
expect(parser.parse('x => y => (a, b, c) => (x, a, b, c, y)')).toEqual({ | ||
type: ExpressionType.Lambda, | ||
args: [{ | ||
type: ExpressionType.Identifier, | ||
name: 'x' | ||
@@ -218,3 +212,2 @@ }], | ||
args: [{ | ||
type: ExpressionType.Identifier, | ||
name: 'y' | ||
@@ -225,29 +218,29 @@ }], | ||
args: [{ | ||
type: ExpressionType.Identifier, | ||
name: 'a' | ||
}, { | ||
type: ExpressionType.Identifier, | ||
name: 'b' | ||
}, { | ||
type: ExpressionType.Identifier, | ||
name: 'c' | ||
}], | ||
body: { | ||
type: ExpressionType.Sequence, | ||
expressions: [{ | ||
type: ExpressionType.Identifier, | ||
name: 'x' | ||
}, { | ||
type: ExpressionType.Identifier, | ||
name: 'a' | ||
}, { | ||
type: ExpressionType.Identifier, | ||
name: 'b' | ||
}, { | ||
type: ExpressionType.Identifier, | ||
name: 'c' | ||
}, { | ||
type: ExpressionType.Identifier, | ||
name: 'y' | ||
}] | ||
type: ExpressionType.Group, | ||
expression: { | ||
type: ExpressionType.Sequence, | ||
expressions: [{ | ||
type: ExpressionType.Identifier, | ||
name: 'x' | ||
}, { | ||
type: ExpressionType.Identifier, | ||
name: 'a' | ||
}, { | ||
type: ExpressionType.Identifier, | ||
name: 'b' | ||
}, { | ||
type: ExpressionType.Identifier, | ||
name: 'c' | ||
}, { | ||
type: ExpressionType.Identifier, | ||
name: 'y' | ||
}] | ||
} | ||
} | ||
@@ -258,2 +251,45 @@ } | ||
}); | ||
it('Parses lambda expression with default argument values', () => { | ||
expect(parser.parse('(x = 2) => x')).toEqual({ | ||
type: ExpressionType.Lambda, | ||
args: [{ | ||
name: 'x', | ||
default: { | ||
type: ExpressionType.NumberLiteral, | ||
value: 2 | ||
} | ||
}], | ||
body: { | ||
type: ExpressionType.Identifier, | ||
name: 'x' | ||
} | ||
}); | ||
}); | ||
it('Parses lambda expression with the rest argument', () => { | ||
expect(parser.parse('(...numbers) => numbers.reduce(sum, 0)')).toEqual({ | ||
type: ExpressionType.Lambda, | ||
args: [{ | ||
name: 'numbers', | ||
variadic: true | ||
}], | ||
body: { | ||
type: ExpressionType.Call, | ||
lhs: { | ||
type: ExpressionType.MemberAccess, | ||
lhs: { | ||
type: ExpressionType.Identifier, | ||
name: 'numbers' | ||
}, | ||
name: 'reduce' | ||
}, | ||
args: [{ | ||
type: ExpressionType.Identifier, | ||
name: 'sum' | ||
}, { | ||
type: ExpressionType.NumberLiteral, | ||
value: 0 | ||
}] | ||
} | ||
}); | ||
}); | ||
it('Supports scope:enter and scope:leave events', () => { | ||
@@ -260,0 +296,0 @@ const args = []; |
{ | ||
"name": "numenor", | ||
"version": "0.1.5", | ||
"version": "0.1.6", | ||
"description": "Customizable, safe evaluator of JavaScript-like expressions.", | ||
@@ -5,0 +5,0 @@ "keywords": [ |
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
163668
64
4145