@firehammer/jexl
Version:
Javascript Expression Language: Powerful context-based expression parser and evaluator
228 lines (225 loc) • 5.12 kB
JavaScript
"use strict";
/*
* Jexl
* Copyright 2020 Tom Shawver
*/
var h = require("./handlers");
/**
* A mapping of all states in the finite state machine to a set of instructions
* for handling or transitioning into other states. Each state can be handled
* in one of two schemes: a tokenType map, or a subHandler.
*
* Standard expression elements are handled through the tokenType object. This
* is an object map of all legal token types to encounter in this state (and
* any unexpected token types will generate a thrown error) to an options
* object that defines how they're handled. The available options are:
*
* {string} toState: The name of the state to which to transition
* immediately after handling this token
* {string} handler: The handler function to call when this token type is
* encountered in this state. If omitted, the default handler
* matching the token's "type" property will be called. If the handler
* function does not exist, no call will be made and no error will be
* generated. This is useful for tokens whose sole purpose is to
* transition to other states.
*
* States that consume a subexpression should define a subHandler, the
* function to be called with an expression tree argument when the
* subexpression is complete. Completeness is determined through the
* endStates object, which maps tokens on which an expression should end to the
* state to which to transition once the subHandler function has been called.
*
* Additionally, any state in which it is legal to mark the AST as completed
* should have a 'completable' property set to boolean true. Attempting to
* call {@link Parser#complete} in any state without this property will result
* in a thrown Error.
*
* @type {{}}
*/
exports.states = {
expectOperand: {
tokenTypes: {
literal: {
toState: "expectBinOp"
},
identifier: {
toState: "identifier"
},
unaryOp: {},
openParen: {
toState: "subExpression"
},
openCurl: {
toState: "expectObjKey",
handler: h.objStart
},
dot: {
toState: "traverse"
},
openBracket: {
toState: "arrayVal",
handler: h.arrayStart
}
}
},
expectBinOp: {
tokenTypes: {
binaryOp: {
toState: "expectOperand"
},
pipe: {
toState: "expectTransform"
},
dot: {
toState: "traverse"
},
question: {
toState: "ternaryMid",
handler: h.ternaryStart
}
},
completable: true
},
expectTransform: {
tokenTypes: {
identifier: {
toState: "postTransform",
handler: h.transform
}
}
},
expectObjKey: {
tokenTypes: {
literal: {
toState: "expectKeyValSep",
handler: h.objKey
},
identifier: {
toState: "expectKeyValSep",
handler: h.objKey
},
closeCurl: {
toState: "expectBinOp"
}
}
},
expectKeyValSep: {
tokenTypes: {
colon: {
toState: "objVal"
}
}
},
postTransform: {
tokenTypes: {
openParen: {
toState: "argVal"
},
binaryOp: {
toState: "expectOperand"
},
dot: {
toState: "traverse"
},
openBracket: {
toState: "filter"
},
pipe: {
toState: "expectTransform"
}
},
completable: true
},
postArgs: {
tokenTypes: {
binaryOp: {
toState: "expectOperand"
},
dot: {
toState: "traverse"
},
openBracket: {
toState: "filter"
},
pipe: {
toState: "expectTransform"
}
},
completable: true
},
identifier: {
tokenTypes: {
binaryOp: {
toState: "expectOperand"
},
dot: {
toState: "traverse"
},
openBracket: {
toState: "filter"
},
openParen: {
toState: "argVal",
handler: h.functionCall
},
pipe: {
toState: "expectTransform"
},
question: {
toState: "ternaryMid",
handler: h.ternaryStart
}
},
completable: true
},
traverse: {
tokenTypes: {
identifier: {
toState: "identifier"
}
}
},
filter: {
subHandler: h.filter,
endStates: {
closeBracket: "identifier"
}
},
subExpression: {
subHandler: h.subExpression,
endStates: {
closeParen: "expectBinOp"
}
},
argVal: {
subHandler: h.argVal,
endStates: {
comma: "argVal",
closeParen: "postArgs"
}
},
objVal: {
subHandler: h.objVal,
endStates: {
comma: "expectObjKey",
closeCurl: "expectBinOp"
}
},
arrayVal: {
subHandler: h.arrayVal,
endStates: {
comma: "arrayVal",
closeBracket: "expectBinOp"
}
},
ternaryMid: {
subHandler: h.ternaryMid,
endStates: {
colon: "ternaryEnd"
}
},
ternaryEnd: {
subHandler: h.ternaryEnd,
completable: true
}
};