@spellu/4op
Version:
Spellu example, four arithmetic operations.
163 lines (162 loc) • 6.75 kB
JavaScript
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;