-
Notifications
You must be signed in to change notification settings - Fork 0
/
js.ts
112 lines (104 loc) · 5.08 KB
/
js.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 {
Literal, Op, OpType, VarDefinition,
LetPattern, MatchPattern, Expr,
Readability, TopLevel, exprToString
} from "./ast.ts";
import {BUILT_IN_NAMES} from "./builtIns.ts"
function argPatternToJs(expr: LetPattern): string {
if(expr.type == "Record") {
const xs = expr.val
return "{" + xs.map(([[prop, _], val]) => `${prop}: ${argPatternToJs(val)}`).join(", ") + "}"
} else if (expr.type == "Var") return expr.val
else throw "Impossible!"
}
const matchPatternToJs = (pat: MatchPattern, expr: Expr): string => {
if(pat.type == "Case") {
const [constructor, variable] = pat.val
return `[ (constructor_) => constructor_.$constructor == ${JSON.stringify(constructor)}, ` +
`(${variable}) => { return ${exprToJS(expr)} } ]`
} else return `[_ => true, (${pat.val}) => { return ${exprToJS(expr)} }]`
}
const dispatchOperator = (op: Op, opType: OpType, [a, b]: [Expr, Expr]) => {
const [str1, str2] = [exprToJS(a), exprToJS(b)]
if(opType == "IntOp" || opType == "FloatOp" || opType == "StrOp") {
if(op == "Add") return `(${str1} + ${str2})`
else if(op == "Sub") return `(${str1} - ${str2})`
else if(op == "Mult") return `(${str1} * ${str2})`
else if(op == "Div") return `(${str1} / ${str2})`
else if(op == "Rem") return `(${str1} % ${str2})`
} else if(opType == "IntOrFloatCmp") {
if(op == "Lt") return `(${str1} < ${str2})`
else if(op == "Lte") return `(${str1} <= ${str2})`
else if(op == "Gt") return `(${str1} > ${str2})`
else if(op == "Gte") return `(${str1} >= ${str2})`
}
return `${op}__${opType}(${str1}, ${str2})`
}
function exprToJS(expr: Expr): string {
if (expr.type == "BinOp") {
const [[expr1, _1], [expr2, _2], opType, op, _] = expr.fields
return dispatchOperator(op, opType, [expr1, expr2])
} else if (expr.type == "Call") {
const [expr1, expr2, _] = expr.fields
return `((${exprToJS(expr1)})(${exprToJS(expr2)}))`
} else if (expr.type == "Case") {
const [[str, _], newExpr] = expr.fields
return `makeCase(${JSON.stringify(str)}, ${exprToJS(newExpr)})`
} else if (expr.type == "FieldAccess") {
const [expr1, str, _] = expr.fields
return `(${exprToJS(expr1)}[${JSON.stringify(str)}])`
} else if (expr.type == "FuncDef") {
const [[argPattern, bodyExpr], _] = expr.fields
return `((${argPatternToJs(argPattern)}) => { return ${exprToJS(bodyExpr)} })`
} else if (expr.type == "If") {
const [[condExpr, _], thenExpr, elseExpr] = expr.fields
return `ifThenElse(${exprToJS(condExpr)}, () => ${exprToJS(thenExpr)}, () => ${exprToJS(elseExpr)})`
} else if (expr.type == "Let") {
const [[id, valExpr], expr1] = expr.fields
return `(((${id}) => { return ${exprToJS(expr1)} })(${exprToJS(valExpr)}))`
} else if (expr.type == "LetRec") {
const [varDefinitions, expr1] = expr.fields
const decls = varDefinitions.map(([[id, _1], _2]) => `let ${id};\n`).join("")
const defs = varDefinitions.map(([[id, val], _]) => `${id} = ${exprToJS(val)};\n`).join("")
const all = `${decls}\n${defs}\nreturn ${exprToJS(expr1)}`.split("\n").map(x => "\t" + x).join("\n")
return `(() => {\n${all}\n})()`
} else if (expr.type == "Literal") {
const [lit, [str, _2]] = expr.fields
if(lit=="Str") return JSON.stringify(str)
else return str
} else if (expr.type == "Match") {
const [expr1, xs, _] = expr.fields
const cs = "[" +
xs.map(([[pat, _], expr]) => matchPatternToJs(pat, expr)).join(", ") +
"]"
return `matchCases(${exprToJS(expr1)}, ${cs})`
} else if (expr.type == "NewRef") {
const [expr1, _] = expr.fields
return `{$val: ${exprToJS(expr1)}}`
} else if (expr.type == "Record") {
const [maybeExpr, xs, _] = expr.fields
const props = xs.map(([[str, _], expr]) => str + ": " + exprToJS(expr)).join(", ")
return `createPrototype(${maybeExpr == null ? "{}" : exprToJS(maybeExpr)}, {${props}})`
} else if (expr.type == "RefGet") {
const [expr1, _] = expr.field
return `(${exprToJS(expr1)}.$val)`
} else if (expr.type == "RefSet") {
const [[expr1, _], expr2] = expr.fields
return `(${exprToJS(expr1)}.$val = ${exprToJS(expr2)})`
} else {
return expr.field[0]
}
}
const topLevelToJs = (topLevel: TopLevel): string => {
if(topLevel.type == "Expr") return exprToJS(topLevel.val)
else if(topLevel.type == "LetDef") {
const [id, expr] = topLevel.val
return `const ${id} = ${exprToJS(expr)}`
} else
return topLevel.val.map(([[id, _1], _2]) => `let ${id};\n`).join("") +
topLevel.val.map(([[id, expr], _]) => `${id} = ${exprToJS(expr)};\n`).join("")
}
const topLevelsToJs = (topLevels: TopLevel[]) =>
`import { ${[...BUILT_IN_NAMES].join(", ")}, ifThenElse, createPrototype, makeCase, matchCases, Eq__AnyCmp, Neq_AnyCmp } from \"./runtime.js\";\n\n` +
topLevels.map(topLevelToJs).map(x => x + ";\n").join("")
export { topLevelsToJs }