add the function of the interpreter

This commit is contained in:
Tan, Kian-ting 2024-03-31 20:59:09 +08:00
parent 68a21c773d
commit 840dabc683
2 changed files with 540 additions and 168 deletions

View file

@ -8,14 +8,15 @@ var TokenKind;
TokenKind[TokenKind["Int"] = 1] = "Int";
TokenKind[TokenKind["Flo"] = 2] = "Flo";
TokenKind[TokenKind["Str"] = 3] = "Str";
TokenKind[TokenKind["LParen"] = 4] = "LParen";
TokenKind[TokenKind["RParen"] = 5] = "RParen";
TokenKind[TokenKind["LBrack"] = 6] = "LBrack";
TokenKind[TokenKind["RBrack"] = 7] = "RBrack";
TokenKind[TokenKind["SpaceNL"] = 8] = "SpaceNL";
TokenKind[TokenKind["BSlash"] = 9] = "BSlash";
TokenKind[TokenKind["Apos"] = 10] = "Apos";
TokenKind[TokenKind["Other"] = 11] = "Other";
TokenKind[TokenKind["Bool"] = 4] = "Bool";
TokenKind[TokenKind["LParen"] = 5] = "LParen";
TokenKind[TokenKind["RParen"] = 6] = "RParen";
TokenKind[TokenKind["LBrack"] = 7] = "LBrack";
TokenKind[TokenKind["RBrack"] = 8] = "RBrack";
TokenKind[TokenKind["SpaceNL"] = 9] = "SpaceNL";
TokenKind[TokenKind["BSlash"] = 10] = "BSlash";
TokenKind[TokenKind["Apos"] = 11] = "Apos";
TokenKind[TokenKind["Other"] = 12] = "Other";
})(TokenKind || (TokenKind = {}));
var ItemType;
(function (ItemType) {
@ -23,10 +24,13 @@ var ItemType;
ItemType[ItemType["Flo"] = 1] = "Flo";
ItemType[ItemType["Id"] = 2] = "Id";
ItemType[ItemType["Str"] = 3] = "Str";
ItemType[ItemType["Bool"] = 4] = "Bool";
})(ItemType || (ItemType = {}));
const tokenizer = (0, typescript_parsec_1.buildLexer)([
[true, /^\d+/g, TokenKind.Int],
[true, /^\d+\.\d+/g, TokenKind.Flo],
[true, /^true/g, TokenKind.Bool],
[true, /^false/g, TokenKind.Bool],
[true, /^[+\-*/a-zA-Z_][0-9+\-*/a-zA-Z_]*/g, TokenKind.Id],
[true, /^\"([^\"]|\\\")+\"/g, TokenKind.Str],
[true, /^[(]/g, TokenKind.LParen],
@ -55,58 +59,70 @@ const CON_STR_INNER = (0, typescript_parsec_1.rule)();
function tokenToStr(value) {
return {
type: ItemType.Str,
str: value.text
str: value.text,
};
}
function bSlashTokenToStr(value) {
return {
type: ItemType.Str,
str: value.text
str: value.text,
};
}
function applyId(value) {
return {
type: ItemType.Id,
id: value.text
id: value.text,
};
}
function applyInt(value) {
return {
type: ItemType.Int,
int: BigInt(value.text)
int: +value.text,
};
}
function applyFlo(value) {
return {
type: ItemType.Flo,
flo: +value.text
flo: +value.text,
};
}
function applyStr(value) {
return {
type: ItemType.Str,
str: value.text.slice(1, value.text.length - 1)
str: value.text.slice(1, value.text.length - 1),
};
}
function applyBool(value) {
if (value.text == "true") {
return {
type: ItemType.Bool,
bool: true,
};
}
else {
return {
type: ItemType.Bool,
bool: false,
};
}
}
function applyList(value) {
return value;
}
function applyQuoted(value) {
let head = { type: ItemType.Id,
id: "quote" };
let head = { type: ItemType.Id, id: "quote" };
let merged = [head, value];
return merged;
}
function applyStrings(value) {
let head = [{ type: ItemType.Id,
id: "%concat" }];
let head = [{ type: ItemType.Id, id: "%concat" }];
let merged = head.concat(value);
return merged;
}
/** for convinence to omit the spaces and newlines */
let __ = (0, typescript_parsec_2.opt)((0, typescript_parsec_2.tok)(TokenKind.SpaceNL));
LISP.setPattern((0, typescript_parsec_2.alt)((0, typescript_parsec_2.kleft)(SINGLE, __), (0, typescript_parsec_2.kleft)(LISPS, __), (0, typescript_parsec_2.kleft)(CON_STR, __)));
SINGLE.setPattern((0, typescript_parsec_2.alt)((0, typescript_parsec_2.apply)((0, typescript_parsec_2.tok)(TokenKind.Id), applyId), (0, typescript_parsec_2.apply)((0, typescript_parsec_2.tok)(TokenKind.Int), applyInt), (0, typescript_parsec_2.apply)((0, typescript_parsec_2.tok)(TokenKind.Flo), applyFlo), (0, typescript_parsec_2.apply)((0, typescript_parsec_2.tok)(TokenKind.Str), applyStr)));
SINGLE.setPattern((0, typescript_parsec_2.alt)((0, typescript_parsec_2.apply)((0, typescript_parsec_2.tok)(TokenKind.Id), applyId), (0, typescript_parsec_2.apply)((0, typescript_parsec_2.tok)(TokenKind.Int), applyInt), (0, typescript_parsec_2.apply)((0, typescript_parsec_2.tok)(TokenKind.Flo), applyFlo), (0, typescript_parsec_2.apply)((0, typescript_parsec_2.tok)(TokenKind.Str), applyStr), (0, typescript_parsec_2.apply)((0, typescript_parsec_2.tok)(TokenKind.Bool), applyBool)));
LISPS.setPattern((0, typescript_parsec_2.alt)((0, typescript_parsec_2.apply)((0, typescript_parsec_2.kmid)((0, typescript_parsec_2.seq)((0, typescript_parsec_2.str)("("), __), (0, typescript_parsec_2.rep_sc)(LISP), (0, typescript_parsec_2.str)(")")), applyList), (0, typescript_parsec_2.apply)((0, typescript_parsec_2.kright)((0, typescript_parsec_2.str)("'"), (0, typescript_parsec_2.kmid)((0, typescript_parsec_2.seq)((0, typescript_parsec_2.str)("("), __), (0, typescript_parsec_2.rep_sc)(LISP), (0, typescript_parsec_2.str)(")"))), applyQuoted)));
CON_STR_INNER.setPattern((0, typescript_parsec_2.alt)((0, typescript_parsec_2.apply)((0, typescript_parsec_2.tok)(TokenKind.Id), tokenToStr), (0, typescript_parsec_2.apply)((0, typescript_parsec_2.tok)(TokenKind.Int), tokenToStr), (0, typescript_parsec_2.apply)((0, typescript_parsec_2.tok)(TokenKind.Flo), tokenToStr), (0, typescript_parsec_2.apply)((0, typescript_parsec_2.tok)(TokenKind.Str), tokenToStr), (0, typescript_parsec_2.apply)((0, typescript_parsec_2.tok)(TokenKind.Other), tokenToStr), (0, typescript_parsec_2.apply)((0, typescript_parsec_2.tok)(TokenKind.SpaceNL), tokenToStr), (0, typescript_parsec_2.apply)((0, typescript_parsec_2.kright)((0, typescript_parsec_2.tok)(TokenKind.BSlash), (0, typescript_parsec_2.tok)(TokenKind.LParen)), tokenToStr), (0, typescript_parsec_2.apply)((0, typescript_parsec_2.kright)((0, typescript_parsec_2.tok)(TokenKind.BSlash), (0, typescript_parsec_2.tok)(TokenKind.RParen)), tokenToStr), (0, typescript_parsec_2.apply)((0, typescript_parsec_2.kright)((0, typescript_parsec_2.tok)(TokenKind.BSlash), (0, typescript_parsec_2.tok)(TokenKind.LBrack)), tokenToStr), (0, typescript_parsec_2.apply)((0, typescript_parsec_2.kright)((0, typescript_parsec_2.tok)(TokenKind.BSlash), (0, typescript_parsec_2.tok)(TokenKind.RBrack)), tokenToStr), (0, typescript_parsec_2.apply)((0, typescript_parsec_2.kright)((0, typescript_parsec_2.tok)(TokenKind.BSlash), (0, typescript_parsec_2.tok)(TokenKind.Apos)), tokenToStr), (0, typescript_parsec_2.apply)((0, typescript_parsec_2.kright)((0, typescript_parsec_2.tok)(TokenKind.BSlash), (0, typescript_parsec_2.tok)(TokenKind.BSlash)), bSlashTokenToStr), LISPS));
CON_STR.setPattern((0, typescript_parsec_2.apply)((0, typescript_parsec_2.kmid)((0, typescript_parsec_2.str)("["), (0, typescript_parsec_2.rep_sc)(CON_STR_INNER), (0, typescript_parsec_2.str)("]")), applyStrings));
@ -125,16 +141,181 @@ function printAST(ast) {
else if (ast.type == ItemType.Flo) {
return ast.flo.toString();
}
else if (ast.type == ItemType.Bool) {
return ast.bool.toString();
}
else {
return ast.int.toString();
}
}
}
function isItem(x) {
return !Array.isArray(x);
}
function interpBinary(op, argsMapped) {
// x + y
let fst = argsMapped[0];
let snd = argsMapped[1];
if (argsMapped.length == 2 && isItem(fst) && isItem(snd)) {
if (fst.type == ItemType.Flo && snd.type == ItemType.Flo) {
return {
type: ItemType.Flo,
flo: op(fst.flo, snd.flo),
};
}
else if (fst.type == ItemType.Int && snd.type == ItemType.Int) {
return {
type: ItemType.Int,
int: op(fst.int, snd.int),
};
}
else {
throw new Error("the type of add should be (int, int) or (flo, flo");
}
}
else {
throw new Error("the number of args should be 2.");
}
}
function add(x, y) {
return x + y;
}
function sub(x, y) {
return x - y;
}
function mul(x, y) {
return x * y;
}
function div(x, y) {
return x / y;
}
function extendEnv(env, vari, data) {
// add var
if (!(vari in env)) {
env[vari] = [data];
// update
}
else {
env[vari] = [data].concat(env[vari]);
}
return env;
}
var emptyEnv = {};
function interp(prog, env) {
if (Array.isArray(prog)) {
if (!Array.isArray(prog[0])) {
let op = prog[0];
if (op.type == ItemType.Id) {
if (op.id == "let") {
let bindings = prog[1];
if (prog.length != 3) {
throw new Error("the number of args for 'let' should be 2.");
}
else if (!Array.isArray(bindings)) {
throw new Error("the bindings should be array");
}
else {
var newEnv = env;
for (var i = 0; i < bindings.length; i++) {
let binding = bindings[i];
if (!Array.isArray(binding)
|| (binding).length != 2) {
throw new Error("mall formed of let.");
}
else {
let vari = binding[0];
if (vari.hasOwnProperty("id")) {
let variName = vari.id;
newEnv = extendEnv(newEnv, variName, binding[1]);
}
}
}
let body = prog[2];
return interp(body, newEnv);
}
}
else if (op.id == "if") {
if (prog.length != 4) {
throw new Error("the args of if should be 2.");
}
else {
let cond = interp(prog[1], env);
if (Array.isArray(cond)) {
throw new Error("cond can't be reduced to a constant");
}
else if (cond.type != ItemType.Bool) {
throw new Error("type error of cond, not a bool");
}
else if (cond.bool == true) {
return interp(prog[2], env);
// if cond is false
}
else {
return interp(prog[3], env);
}
}
}
else {
let argsMapped = prog.slice(1).map((x) => {
return interp(x, env);
});
// binary basic operator
if (op.id == "+") {
return interpBinary(add, argsMapped);
}
else if (op.id == "-") {
return interpBinary(sub, argsMapped);
}
else if (op.id == "*") {
return interpBinary(mul, argsMapped);
}
else if (op.id == "/") {
return interpBinary(div, argsMapped);
// other named function call
}
else {
throw new Error("todo for other id");
}
}
// the caller should not be a non-id constant
}
else {
throw new Error("the caller shouldn't be number or string");
}
// the caller which is a higher-function call
}
else {
throw new Error("todo for ((lambda arg ) arg)");
}
}
else {
// constant
if (prog.type != ItemType.Id) {
return prog;
// variable
}
else {
let varName = prog.id;
let value = env[varName][0];
return value;
}
}
}
function evaluate(expr) {
let a = (0, typescript_parsec_1.expectSingleResult)((0, typescript_parsec_1.expectEOF)(LISP.parse(tokenizer.parse(expr))));
const util = require('util');
console.log(printAST(a));
let interped = interp(a, emptyEnv);
console.log(printAST(interped));
return a;
}
evaluate(`(main '((text 12)) [ 快狐跳懶狗\\\\\\\[\\\]\\\(\\\)(italic "fox and dog") (bold [OK])])`);
//evaluate(`(main '((text 12)) [ 快狐跳懶狗\\\\\\\[\\\]\\\(\\\)(italic "fox and dog") (bold [OK])])`)
//evaluate("@(let (a 17) (+ a 10))@")
// eval print loop
const readline = require("node:readline");
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
});
rl.question(`What's your program?`, (prog) => {
console.log(evaluate(prog));
rl.close();
});

View file

@ -1,70 +1,96 @@
import { Token } from 'typescript-parsec';
import { buildLexer, expectEOF, expectSingleResult, rule } from 'typescript-parsec';
import { alt, apply, kright, kmid, kleft, rep_sc, seq, str, tok, opt } from 'typescript-parsec';
import { Token } from "typescript-parsec";
import {
buildLexer,
expectEOF,
expectSingleResult,
rule,
} from "typescript-parsec";
import {
alt,
apply,
kright,
kmid,
kleft,
rep_sc,
seq,
str,
tok,
opt,
} from "typescript-parsec";
enum TokenKind{
Id,
Int,
Flo,
Str,
LParen,
RParen,
LBrack,
RBrack,
SpaceNL,
BSlash,
Apos,
Other
enum TokenKind {
Id,
Int,
Flo,
Str,
Bool,
LParen,
RParen,
LBrack,
RBrack,
SpaceNL,
BSlash,
Apos,
Other,
}
enum ItemType{
Int,
Flo,
Id,
Str,
enum ItemType {
Int,
Flo,
Id,
Str,
Bool,
}
type Item = ItemStr | ItemInt | ItemId | ItemFlo;
type Item = ItemStr | ItemInt | ItemId | ItemFlo | ItemBool;
interface ItemStr{
type : ItemType.Str,
str : string,
interface ItemStr {
type: ItemType.Str;
str: string;
}
interface ItemInt{
type : ItemType.Int,
int : BigInt,
interface ItemInt {
type: ItemType.Int;
int: number;
}
interface ItemId{
type : ItemType.Id,
id : string,
interface ItemId {
type: ItemType.Id;
id: string;
}
interface ItemFlo {
type: ItemType.Flo;
flo: number;
}
interface ItemFlo{
type : ItemType.Flo,
flo : number,
interface ItemBool {
type: ItemType.Bool;
bool: boolean;
}
interface Env {
[Key: string]: AST[];
}
type AST = Item | AST[];
const tokenizer = buildLexer([
[true, /^\d+/g, TokenKind.Int],
[true, /^\d+\.\d+/g, TokenKind.Flo],
[true, /^[+\-*/a-zA-Z_][0-9+\-*/a-zA-Z_]*/g, TokenKind.Id],
[true, /^\"([^\"]|\\\")+\"/g, TokenKind.Str],
[true, /^[(]/g, TokenKind.LParen],
[true, /^[)]/g, TokenKind.RParen],
[true, /^\[/g, TokenKind.LBrack],
[true, /^\]/g, TokenKind.RBrack],
[true, /^'/g, TokenKind.Apos],
[true, /^(\s|\t|\r?\n)+/g, TokenKind.SpaceNL],
[true, /^\\/g, TokenKind.BSlash],
[true, /^([^+\-*/a-zA-Z_0-9\[\]()'\s\t\r\n\\]+)/g, TokenKind.Other],
[true, /^\d+/g, TokenKind.Int],
[true, /^\d+\.\d+/g, TokenKind.Flo],
[true, /^true/g, TokenKind.Bool],
[true, /^false/g, TokenKind.Bool],
[true, /^[+\-*/a-zA-Z_][0-9+\-*/a-zA-Z_]*/g, TokenKind.Id],
[true, /^\"([^\"]|\\\")+\"/g, TokenKind.Str],
[true, /^[(]/g, TokenKind.LParen],
[true, /^[)]/g, TokenKind.RParen],
[true, /^\[/g, TokenKind.LBrack],
[true, /^\]/g, TokenKind.RBrack],
[true, /^'/g, TokenKind.Apos],
[true, /^(\s|\t|\r?\n)+/g, TokenKind.SpaceNL],
[true, /^\\/g, TokenKind.BSlash],
[true, /^([^+\-*/a-zA-Z_0-9\[\]()'\s\t\r\n\\]+)/g, TokenKind.Other],
]);
/**
@ -83,143 +109,308 @@ const LISP = rule<TokenKind, AST>();
const CON_STR = rule<TokenKind, AST>();
const CON_STR_INNER = rule<TokenKind, AST>();
function tokenToStr(value: Token<TokenKind>): Item {
return {
type : ItemType.Str,
str : value.text};
return {
type: ItemType.Str,
str: value.text,
};
}
function bSlashTokenToStr(value: Token<TokenKind>): Item {
return {
type : ItemType.Str,
str : value.text};
return {
type: ItemType.Str,
str: value.text,
};
}
function applyId(value: Token<TokenKind.Id>): Item {
return {
type :ItemType.Id,
id : value.text};
return {
type: ItemType.Id,
id: value.text,
};
}
function applyInt(value: Token<TokenKind.Int>): Item {
return {
type : ItemType.Int,
int : BigInt(value.text)};
return {
type: ItemType.Int,
int: +value.text,
};
}
function applyFlo(value: Token<TokenKind.Flo>): Item {
return {
type : ItemType.Flo,
flo : +value.text};
return {
type: ItemType.Flo,
flo: +value.text,
};
}
function applyStr(value: Token<TokenKind.Str>): Item {
return {
type : ItemType.Str,
str : value.text.slice(1,value.text.length-1)};
return {
type: ItemType.Str,
str: value.text.slice(1, value.text.length - 1),
};
}
function applyList(value: AST[]):AST{
return value;
function applyBool(value: Token<TokenKind.Bool>): Item {
if (value.text == "true"){
return {
type: ItemType.Bool,
bool: true,
};
} else{
return {
type: ItemType.Bool,
bool: false,
};
}
}
function applyList(value: AST[]): AST {
return value;
}
function applyQuoted(value: AST[]):AST{
let head : Item = {type : ItemType.Id,
id:"quote"}
let merged = [head, value];
return merged;
function applyQuoted(value: AST[]): AST {
let head: Item = { type: ItemType.Id, id: "quote" };
let merged = [head, value];
return merged;
}
function applyStrings(value: AST[]):AST{
let head : AST[] = [{type : ItemType.Id,
id:"%concat"}]
let merged = head.concat(value);
return merged;
function applyStrings(value: AST[]): AST {
let head: AST[] = [{ type: ItemType.Id, id: "%concat" }];
let merged = head.concat(value);
return merged;
}
/** for convinence to omit the spaces and newlines */
let __ = opt(tok(TokenKind.SpaceNL))
let __ = opt(tok(TokenKind.SpaceNL));
LISP.setPattern(
alt(
kleft(SINGLE, __),
kleft(LISPS, __),
kleft(CON_STR, __)
))
LISP.setPattern(alt(kleft(SINGLE, __), kleft(LISPS, __), kleft(CON_STR, __)));
SINGLE.setPattern(
alt(
apply(tok(TokenKind.Id), applyId),
apply(tok(TokenKind.Int), applyInt),
apply(tok(TokenKind.Flo), applyFlo),
apply(tok(TokenKind.Str), applyStr),
))
alt(
apply(tok(TokenKind.Id), applyId),
apply(tok(TokenKind.Int), applyInt),
apply(tok(TokenKind.Flo), applyFlo),
apply(tok(TokenKind.Str), applyStr),
apply(tok(TokenKind.Bool), applyBool),
)
);
LISPS.setPattern(
alt(
apply(kmid(seq(str("("), __),rep_sc(LISP),str(")")), applyList),
apply(kright(str("'"),
kmid(seq(str("("), __),rep_sc(LISP),str(")"))), applyQuoted),
))
alt(
apply(kmid(seq(str("("), __), rep_sc(LISP), str(")")), applyList),
apply(
kright(str("'"), kmid(seq(str("("), __), rep_sc(LISP), str(")"))),
applyQuoted
)
)
);
CON_STR_INNER.setPattern(
alt(
apply(tok(TokenKind.Id),tokenToStr),
apply(tok(TokenKind.Int),tokenToStr),
apply(tok(TokenKind.Flo),tokenToStr),
apply(tok(TokenKind.Str),tokenToStr),
apply(tok(TokenKind.Other),tokenToStr),
apply(tok(TokenKind.SpaceNL), tokenToStr),
apply(kright(tok(TokenKind.BSlash),tok(TokenKind.LParen)), tokenToStr),
apply(kright(tok(TokenKind.BSlash),tok(TokenKind.RParen)), tokenToStr),
apply(kright(tok(TokenKind.BSlash),tok(TokenKind.LBrack)), tokenToStr),
apply(kright(tok(TokenKind.BSlash),tok(TokenKind.RBrack)), tokenToStr),
apply(kright(tok(TokenKind.BSlash),tok(TokenKind.Apos)), tokenToStr),
apply(kright(tok(TokenKind.BSlash),tok(TokenKind.BSlash)), bSlashTokenToStr),
LISPS
))
alt(
apply(tok(TokenKind.Id), tokenToStr),
apply(tok(TokenKind.Int), tokenToStr),
apply(tok(TokenKind.Flo), tokenToStr),
apply(tok(TokenKind.Str), tokenToStr),
apply(tok(TokenKind.Other), tokenToStr),
apply(tok(TokenKind.SpaceNL), tokenToStr),
apply(kright(tok(TokenKind.BSlash), tok(TokenKind.LParen)), tokenToStr),
apply(kright(tok(TokenKind.BSlash), tok(TokenKind.RParen)), tokenToStr),
apply(kright(tok(TokenKind.BSlash), tok(TokenKind.LBrack)), tokenToStr),
apply(kright(tok(TokenKind.BSlash), tok(TokenKind.RBrack)), tokenToStr),
apply(kright(tok(TokenKind.BSlash), tok(TokenKind.Apos)), tokenToStr),
apply(
kright(tok(TokenKind.BSlash), tok(TokenKind.BSlash)),
bSlashTokenToStr
),
LISPS
)
);
CON_STR.setPattern(
apply(kmid(str("["),
rep_sc(CON_STR_INNER),
str("]")), applyStrings)
)
apply(kmid(str("["), rep_sc(CON_STR_INNER), str("]")), applyStrings)
);
function printAST(ast : AST): string{
if (Array.isArray(ast)){
let ast2 = ast.map(printAST);
return "(" + ast2.join(" ") + ")";
}else{
if (ast.type==ItemType.Str){
return "`" + ast.str + "`";
}else if (ast.type==ItemType.Id){
return ast.id;
}else if (ast.type== ItemType.Flo){
return ast.flo.toString();
}else{
return ast.int.toString();
}
function printAST(ast: AST): string {
if (Array.isArray(ast)) {
let ast2 = ast.map(printAST);
return "(" + ast2.join(" ") + ")";
} else {
if (ast.type == ItemType.Str) {
return "`" + ast.str + "`";
} else if (ast.type == ItemType.Id) {
return ast.id;
} else if (ast.type == ItemType.Flo) {
return ast.flo.toString();
} else if (ast.type == ItemType.Bool) {
return ast.bool.toString();
} else {
return ast.int.toString();
}
}
}
function isItem(x: AST): x is Item {
return !Array.isArray(x);
}
function interpBinary(op: Function, argsMapped: AST[]): ItemFlo | ItemInt {
// x + y
let fst = argsMapped[0];
let snd = argsMapped[1];
if (argsMapped.length == 2 && isItem(fst) && isItem(snd)) {
if (fst.type == ItemType.Flo && snd.type == ItemType.Flo) {
return {
type: ItemType.Flo,
flo: op(fst.flo, snd.flo),
};
} else if (fst.type == ItemType.Int && snd.type == ItemType.Int) {
return {
type: ItemType.Int,
int: op(fst.int, snd.int),
};
} else {
throw new Error("the type of add should be (int, int) or (flo, flo");
}
} else {
throw new Error("the number of args should be 2.");
}
}
function add(x: number, y: number): number {
return x + y;
}
function sub(x: number, y: number): number {
return x - y;
}
function mul(x: number, y: number): number {
return x * y;
}
function div(x: number, y: number): number {
return x / y;
}
function extendEnv(env : Env, vari : string, data : AST) : Env{
// add var
if (!(vari in env)){
env[vari] = [data];
// update
}else{
env[vari] = [data].concat(env[vari]);
}
return env;
}
var emptyEnv: Env = {};
function interp(prog: AST, env: Env): AST {
if (Array.isArray(prog)) {
if (!Array.isArray(prog[0])) {
let op = prog[0];
if (op.type == ItemType.Id) {
if (op.id == "let"){
let bindings = prog[1];
if (prog.length != 3){
throw new Error("the number of args for 'let' should be 2.");
}
else if (!Array.isArray(bindings)){
throw new Error("the bindings should be array");
}else{
var newEnv = env;
for (var i=0;i<bindings.length;i++){
let binding = bindings[i];
if (!Array.isArray(binding)
|| (<AST[]>(binding)).length != 2){
throw new Error("mall formed of let.")
}else{
let vari = binding[0];
if (vari.hasOwnProperty("id")){
let variName = (<ItemId>vari).id;
newEnv = extendEnv(newEnv, variName , binding[1]);
}
}
}
let body = prog[2];
return interp(body, newEnv);
}
}
else if(op.id == "if"){
if (prog.length != 4){
throw new Error("the args of if should be 2.");
}else{
let cond = interp(prog[1], env);
if (Array.isArray(cond)){
throw new Error("cond can't be reduced to a constant");
}else if (cond.type != ItemType.Bool){
throw new Error("type error of cond, not a bool");
}else if (cond.bool == true){
return interp(prog[2], env);
// if cond is false
}else{
return interp(prog[3], env);
}
}
}
else{
let argsMapped = prog.slice(1).map((x) => {
return interp(x, env);
});
// binary basic operator
if (op.id == "+") {
return interpBinary(add, argsMapped);
} else if (op.id == "-") {
return interpBinary(sub, argsMapped);
} else if (op.id == "*") {
return interpBinary(mul, argsMapped);
} else if (op.id == "/") {
return interpBinary(div, argsMapped);
// other named function call
} else {
throw new Error("todo for other id");
}}
// the caller should not be a non-id constant
} else {
throw new Error("the caller shouldn't be number or string");
}
// the caller which is a higher-function call
} else {
throw new Error("todo for ((lambda arg ) arg)");
}
} else {
// constant
if (prog.type != ItemType.Id) {
return prog;
// variable
} else {
let varName = prog.id;
let value = env[varName][0];
return value;
}
}
}
function evaluate(expr: string): AST {
let a = expectSingleResult(expectEOF(LISP.parse(tokenizer.parse(expr))));
const util = require('util')
console.log(printAST(a))
return a;
let a = expectSingleResult(expectEOF(LISP.parse(tokenizer.parse(expr))));
let interped = interp(a, emptyEnv);
console.log(printAST(interped));
return a;
}
//evaluate(`(main '((text 12)) [ 快狐跳懶狗\\\\\\\[\\\]\\\(\\\)(italic "fox and dog") (bold [OK])])`)
//evaluate("@(let (a 17) (+ a 10))@")
evaluate(`(main '((text 12)) [ 快狐跳懶狗\\\\\\\[\\\]\\\(\\\)(italic "fox and dog") (bold [OK])])`)
//evaluate("@(let (a 17) (+ a 10))@")
// eval print loop
const readline = require("node:readline");
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
});
rl.question(`What's your program?`, (prog: string) => {
console.log(evaluate(prog));
rl.close();
});