-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathseshatToJavascript.ts
112 lines (95 loc) Β· 4.48 KB
/
seshatToJavascript.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
import { CharStreams } from 'antlr4ts/CharStreams';
import { CommonTokenStream } from 'antlr4ts/CommonTokenStream';
import { AbstractParseTreeVisitor } from 'antlr4ts/tree/AbstractParseTreeVisitor';
import assert from 'assert';
import { EgyptianNumber } from './egyptianNumber';
import { SeshatLexer } from './grammar/seshat/SeshatLexer';
import {
Addition_statementContext, Assert_equal_statementContext, Division_statementContext,
Function_call_statementContext, IntegerContext, Multiplication_statementContext, NumberContext,
Reverse_subtraction_statementContext, SeshatParser, Single_fractionContext,
String_literalContext, TermContext
} from './grammar/seshat/SeshatParser';
import { SeshatParserVisitor } from './grammar/seshat/SeshatParserVisitor';
export class SeshatToJavascript extends AbstractParseTreeVisitor<string> implements SeshatParserVisitor<string> {
static transpile(script: string): string {
const chars = CharStreams.fromString(script);
const lexer = new SeshatLexer(chars);
const tokens = new CommonTokenStream(lexer);
const parser = new SeshatParser(tokens);
const tree = parser.seshat_start_rule();
const visitor = new SeshatToJavascript();
const javascriptCode: string = visitor.visit(tree);
return javascriptCode;
}
visitAddition_statement(ctx: Addition_statementContext): string {
const args = ctx._summands.map(arg => this.visit(arg)).join(', ');
return `temp = seshatEnvironment.EgyptianNumber.addAll(${args});`;
}
visitReverse_subtraction_statement(ctx: Reverse_subtraction_statementContext): string {
return `temp = seshatEnvironment.EgyptianNumber.subtract(${this.visit(ctx._minuend)}, ${this.visit(ctx._subtrahend)});`;
}
visitMultiplication_statement(ctx: Multiplication_statementContext): string {
const args = ctx._factors.map(arg => this.visit(arg)).join(', ');
return `temp = seshatEnvironment.EgyptianNumber.multiply(${args});`;
}
visitDivision_statement(ctx: Division_statementContext): string {
return `temp = seshatEnvironment.EgyptianNumber.divide(${this.visit(ctx._dividend)}, ${this.visit(ctx._divisor)});`;
}
visitAssert_equal_statement(ctx: Assert_equal_statementContext): string {
return `seshatEnvironment.assertEqual(temp, ${this.visit(ctx._value)});`;
}
visitFunction_call_statement(ctx: Function_call_statementContext): string {
const functionName = ctx._name.text;
assert(! functionName.includes('"'));
const args = ctx._args.map(arg => this.visitTerm(arg)).join(', ');
if (functionName == 'πππ‘') {
return `seshatEnvironment.print(${args});`;
}
return `${functionName}(${args});`;
// `seshatCall("${functionName}", context?, ${args});`;
}
visitTerm(ctx: TermContext): string {
if (ctx.WHAT_HAS_BEEN_FOUND()) return 'temp';
if (ctx.string_literal()) return this.visitString_literal(ctx.string_literal());
if (ctx.number()) return this.visitNumber(ctx.number());
return '';
}
visitString_literal(ctx: String_literalContext | undefined): string {
if (! ctx) return '';
return JSON.stringify(ctx.text.replace(/^ππ(.*)π’$/, '$1'));
}
visitNumber(ctx: NumberContext | undefined): string {
if (! ctx) return '';
const parsed = this.parseNumber(ctx);
return `(new seshatEnvironment.EgyptianNumber(${parsed.numerator}, ${parsed.denominator}))`;
}
parseNumber(ctx: NumberContext): EgyptianNumber {
return ctx.children?.map(child => {
if (child instanceof IntegerContext) return this.parseInteger(child);
else if (child instanceof Single_fractionContext) return this.parseSingle_fraction(child);
return new EgyptianNumber(0);
}).reduce(EgyptianNumber.add) || new EgyptianNumber(0);
}
parseSingle_fraction(ctx: Single_fractionContext): EgyptianNumber {
const tokenToFraction:{[key: string]: EgyptianNumber} = {
'π': new EgyptianNumber(1, 2),
'π': new EgyptianNumber(2, 3),
'π': new EgyptianNumber(3, 4),
'π΄': new EgyptianNumber(1, 4),
}
if (ctx.text in tokenToFraction) {
return tokenToFraction[ctx.text];
}
return new EgyptianNumber(1, EgyptianNumber.parseIntegerFromString(ctx._denominator.text));
}
parseInteger(ctx: IntegerContext): EgyptianNumber {
return new EgyptianNumber(EgyptianNumber.parseIntegerFromString(ctx.text));
}
protected defaultResult(): string {
return '';
}
protected aggregateResult(aggregate: string, nextResult: string): string {
return aggregate + '\n' + nextResult;
}
}