UNPKG

@spellu/4op

Version:

Spellu example, four arithmetic operations.

163 lines (162 loc) 6.75 kB
import spellu from '@spellu/core'; const { $$, $0, $1, $_, $, _ } = spellu; /// <source path="sources/10_type.ts"> var spellu$calculator$4op; (function (spellu$calculator$4op) { let SyntaxType; (function (SyntaxType) { SyntaxType["Number"] = "Number+"; SyntaxType["BinaryExpression"] = "BinaryExpression"; SyntaxType["TokenNumber"] = "Number"; SyntaxType["TokenOpMultiple"] = "*"; SyntaxType["TokenOpDivide"] = "/"; SyntaxType["TokenOpAdd"] = "+"; SyntaxType["TokenOpSubtract"] = "-"; SyntaxType["TokenParenOpen"] = "("; SyntaxType["TokenParenClose"] = ")"; })(SyntaxType = spellu$calculator$4op.SyntaxType || (spellu$calculator$4op.SyntaxType = {})); })(spellu$calculator$4op || (spellu$calculator$4op = {})); /// </source> /// <source path="sources/11_factory.ts"> (function (spellu$calculator$4op) { function Node(type, data, range) { return $.node(type, data, range); } spellu$calculator$4op.Node = Node; })(spellu$calculator$4op || (spellu$calculator$4op = {})); /// </source> /// <source path="sources/20_grammar.ts"> /** * EBNF * expr = term ( '+' term | '-' term ) * term = factor ( '*' factor | '/' factor ) * factor = number | '(' expr ')' * number = ( '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' )+ */ (function (spellu$calculator$4op) { function grammar() { return $.start($(expr)); } spellu$calculator$4op.grammar = grammar; // line = expr @ recover function line() { return $0($(expr).recover(circuit => { // TODO: 行末まで読み飛ばす })); } // expr = term ( '+' term | '-' term )* function expr() { return $$($(term), $0(token(spellu$calculator$4op.SyntaxType.TokenOpAdd), $(term), '|', token(spellu$calculator$4op.SyntaxType.TokenOpSubtract), $(term))) .accept((result) => { let node = result[0]; for (const opAndRight of result[1]) { const left = node; const right = opAndRight[1]; node = spellu$calculator$4op.Node(spellu$calculator$4op.SyntaxType.BinaryExpression, { operator: opAndRight[0].value, left: node, right: opAndRight[1], }, $.range(left, right)); } return node; }); } // term = factor ( '*' factor | '/' factor )* function term() { return $$($(factor), $0(token(spellu$calculator$4op.SyntaxType.TokenOpMultiple), $(factor), '|', token(spellu$calculator$4op.SyntaxType.TokenOpDivide), $(factor))) .accept((result) => { let node = result[0]; for (const opAndRight of result[1]) { const left = node; const right = opAndRight[1]; node = spellu$calculator$4op.Node(spellu$calculator$4op.SyntaxType.BinaryExpression, { operator: opAndRight[0].value, left, right, }, $.range(left, right)); } return node; }); } // factor = number | '(' expr ')' function factor() { const parenExpression = $$(token(spellu$calculator$4op.SyntaxType.TokenParenOpen), $(expr), token(spellu$calculator$4op.SyntaxType.TokenParenClose)) .accept(result => result[1]); return $$(number(), '|', parenExpression); } // number = ( '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' )+ function number() { return _(token, spellu$calculator$4op.SyntaxType.TokenNumber) .accept(result => spellu$calculator$4op.Node(spellu$calculator$4op.SyntaxType.Number, { value: Number.parseFloat(result.value), }, result.range)); } function token(type, label) { return new spellu.Parser(circuit => { const token = scanners[type](circuit.input); if (token) { return circuit.success(token); } else { return circuit.expect(label || type); } }); } const scanners = { [spellu$calculator$4op.SyntaxType.TokenNumber]: _ => _.scan(spellu$calculator$4op.SyntaxType.TokenNumber, /[0-9]+(?:\.[0-9]+)?/, 0), [spellu$calculator$4op.SyntaxType.TokenOpMultiple]: _ => _.scan(spellu$calculator$4op.SyntaxType.TokenOpMultiple, '*', 0), [spellu$calculator$4op.SyntaxType.TokenOpDivide]: _ => _.scan(spellu$calculator$4op.SyntaxType.TokenOpDivide, '/', 0), [spellu$calculator$4op.SyntaxType.TokenOpAdd]: _ => _.scan(spellu$calculator$4op.SyntaxType.TokenOpAdd, '+', 0), [spellu$calculator$4op.SyntaxType.TokenOpSubtract]: _ => _.scan(spellu$calculator$4op.SyntaxType.TokenOpSubtract, '-', 0), [spellu$calculator$4op.SyntaxType.TokenParenOpen]: _ => _.scan(spellu$calculator$4op.SyntaxType.TokenParenOpen, '(', 0), [spellu$calculator$4op.SyntaxType.TokenParenClose]: _ => _.scan(spellu$calculator$4op.SyntaxType.TokenParenClose, ')', 0), }; })(spellu$calculator$4op || (spellu$calculator$4op = {})); /// </source> /// <source path="sources/30_runner.ts"> (function (spellu$calculator$4op) { function parse(source) { return spellu.parse(source, spellu$calculator$4op.grammar()); } spellu$calculator$4op.parse = parse; function trace(source) { return spellu.trace(source, spellu$calculator$4op.grammar()); } spellu$calculator$4op.trace = trace; function evaluate(node) { return visit(node); } spellu$calculator$4op.evaluate = evaluate; function visit(node) { switch (node.type) { case spellu$calculator$4op.SyntaxType.Number: return visitNumber(node); case spellu$calculator$4op.SyntaxType.BinaryExpression: return visitBinaryExpression(node); default: return Number.NaN; } } function visitNumber(node) { return node.value; } function visitBinaryExpression(node) { switch (node.operator) { case '+': return round(visit(node.left) + visit(node.right)); case '-': return round(visit(node.left) - visit(node.right)); case '*': return round(visit(node.left) * visit(node.right)); case '/': return round(visit(node.left) / visit(node.right)); default: return Number.NaN; } } function round(n) { return Math.round(n * 100) / 100; } })(spellu$calculator$4op || (spellu$calculator$4op = {})); /// </source> export default spellu$calculator$4op;