Skip to content

Instantly share code, notes, and snippets.

@tlrobinson
Last active January 16, 2017 18:26
Show Gist options
  • Save tlrobinson/b63ab1efe5b79aba14c65e6495d3ba1d to your computer and use it in GitHub Desktop.
Save tlrobinson/b63ab1efe5b79aba14c65e6495d3ba1d to your computer and use it in GitHub Desktop.
Chevrotain + Webpack 2 mangling issue
{
"presets": [
["es2015", { "modules": false }]
]
}
import calculator from "./calculator";
console.log(calculator("1 + 1"));
import { Parser, Lexer } from "chevrotain";
import {
allTokens,
WhiteSpace,
Plus, Minus, Multi, Div,
LParen, RParen,
NumberLiteral,
AdditionOperator, MultiplicationOperator
} from "./tokens";
const CalculatorLexer = new Lexer(allTokens);
// ----------------- parser -----------------
function Calculator(input) {
Parser.call(this, input, allTokens);
var $ = this;
$.RULE("expression", function() {
return $.SUBRULE($.additionExpression)
});
// lowest precedence thus it is first in the rule chain
// The precedence of binary expressions is determined by how far down the Parse Tree
// The binary expression appears.
$.RULE("additionExpression", function() {
var value, op, rhsVal;
// parsing part
value = $.SUBRULE($.multiplicationExpression);
$.MANY(function() {
// consuming 'AdditionOperator' will consume either Plus or Minus as they are subclasses of AdditionOperator
op = $.CONSUME(AdditionOperator);
// the index "2" in SUBRULE2 is needed to identify the unique position in the grammar during runtime
rhsVal = $.SUBRULE2($.multiplicationExpression);
// interpreter part
if (op instanceof Plus) {
value += rhsVal
} else { // op instanceof Minus
value -= rhsVal
}
});
return value
});
$.RULE("multiplicationExpression", function() {
var value, op, rhsVal;
// parsing part
value = $.SUBRULE($.atomicExpression);
$.MANY(function() {
op = $.CONSUME(MultiplicationOperator);
// the index "2" in SUBRULE2 is needed to identify the unique position in the grammar during runtime
rhsVal = $.SUBRULE2($.atomicExpression);
// interpreter part
if (op instanceof Multi) {
value *= rhsVal
} else { // op instanceof Div
value /= rhsVal
}
});
return value
});
$.RULE("atomicExpression", function() {
// @formatter:off
return $.OR([
// parenthesisExpression has the highest precedence and thus it appears
// in the "lowest" leaf in the expression ParseTree.
{ALT: function(){ return $.SUBRULE($.parenthesisExpression)}},
{ALT: function(){ return parseInt($.CONSUME(NumberLiteral).image, 10)}}
], "a number or parenthesis expression");
// @formatter:on
});
$.RULE("parenthesisExpression", function() {
var expValue;
$.CONSUME(LParen);
expValue = $.SUBRULE($.expression);
$.CONSUME(RParen);
return expValue
});
// very important to call this after all the rules have been defined.
// otherwise the parser may not work correctly as it will lack information
// derived during the self analysis phase.
Parser.performSelfAnalysis(this);
}
// avoids inserting number literals as these can have multiple(and infinite) semantic values, thus it is unlikely
// we can choose the correct number value to insert.
Calculator.prototype.canTokenTypeBeInsertedInRecovery = function(tokClass) {
return tokClass !== NumberLiteral
};
Calculator.prototype = Object.create(Parser.prototype);
Calculator.prototype.constructor = Calculator;
// wrapping it all togater
// reuse the same parser instance.
var parser = new Calculator([]);
export default function(text) {
var lexResult = CalculatorLexer.tokenize(text);
// setting a new input will RESET the parser instance's state.
parser.input = lexResult.tokens;
// any top level rule may be used as an entry point
var value = parser.expression();
return {
value: value,
lexResult: lexResult,
parseErrors: parser.errors
};
};
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<script src="app.bundle.js" charset="utf-8"></script>
</head>
<body>
</body>
</html>
{
"name": "webpack2-chevrotain-test",
"version": "1.0.0",
"main": "index.js",
"devDependencies": {
"babel-core": "^6.21.0",
"babel-loader": "^6.2.10",
"babel-preset-es2015": "^6.18.0",
"webpack": "2.2.0-rc.7"
},
"dependencies": {
"chevrotain": "^0.21.0"
},
"scripts": {
"install": "./node_modules/.bin/webpack"
}
}
import { createToken, Lexer } from "chevrotain";
// using the NA pattern marks this Token class as 'irrelevant' for the Lexer.
// AdditionOperator defines a Tokens hierarchy but only the leafs in this hierarchy define
// actual Tokens that can appear in the text
export const AdditionOperator = createToken({name: "AdditionOperator", pattern: Lexer.NA});
export const Plus = createToken({name: "Plus", pattern: /\+/, parent: AdditionOperator});
export const Minus = createToken({name: "Minus", pattern: /-/, parent: AdditionOperator});
export const MultiplicationOperator = createToken({name: "MultiplicationOperator", pattern: Lexer.NA});
export const Multi = createToken({name: "Multi", pattern: /\*/, parent: MultiplicationOperator});
export const Div = createToken({name: "Div", pattern: /\//, parent: MultiplicationOperator});
export const LParen = createToken({name: "LParen", pattern: /\(/});
export const RParen = createToken({name: "RParen", pattern: /\)/});
export const NumberLiteral = createToken({name: "NumberLiteral", pattern: /[1-9]\d*/});
// marking WhiteSpace as 'SKIPPED' makes the lexer skip it.
export const WhiteSpace = createToken({name: "WhiteSpace", pattern: /\s+/, group: Lexer.SKIPPED});
export const allTokens = [WhiteSpace, // whitespace is normally very common so it should be placed first to speed up the lexer's performance
Plus, Minus, Multi, Div, LParen, RParen, NumberLiteral, AdditionOperator, MultiplicationOperator];
var config = module.exports = {
entry: {
app: './app.js'
},
output: {
filename: '[name].bundle.js?[hash]',
},
module: {
loaders: [
{
test: /\.js$/,
exclude: /node_modules/,
loader: "babel-loader"
},
],
},
devtool: "sourcemap"
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment