From 840dabc683386a66f3d3e9f790e2ce24f3e0b04b Mon Sep 17 00:00:00 2001 From: Tan Kian-ting Date: Sun, 31 Mar 2024 20:59:09 +0800 Subject: [PATCH] add the function of the interpreter --- src/index.js | 225 +++++++++++++++++++++--- src/index.ts | 481 +++++++++++++++++++++++++++++++++++---------------- 2 files changed, 539 insertions(+), 167 deletions(-) diff --git a/src/index.js b/src/index.js index 3c0e3a1..54f2775 100644 --- a/src/index.js +++ b/src/index.js @@ -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(); +}); diff --git a/src/index.ts b/src/index.ts index 46a318a..1a61ccd 100644 --- a/src/index.ts +++ b/src/index.ts @@ -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'; - -enum TokenKind{ - Id, - Int, - Flo, - Str, - LParen, - RParen, - LBrack, - RBrack, - SpaceNL, - BSlash, - Apos, - Other - +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, + 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: number; } -interface ItemInt{ - type : ItemType.Int, - int : BigInt, +interface ItemId { + type: ItemType.Id; + id: string; } -interface ItemId{ - type : ItemType.Id, - id : string, +interface ItemFlo { + type: ItemType.Flo; + flo: number; } +interface ItemBool { + type: ItemType.Bool; + bool: boolean; + } + -interface ItemFlo{ - type : ItemType.Flo, - flo : number, +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(); const CON_STR = rule(); const CON_STR_INNER = rule(); - - function tokenToStr(value: Token): Item { - return { - type : ItemType.Str, - str : value.text}; + return { + type: ItemType.Str, + str: value.text, + }; } - function bSlashTokenToStr(value: Token): Item { - return { - type : ItemType.Str, - str : value.text}; + return { + type: ItemType.Str, + str: value.text, + }; } - function applyId(value: Token): Item { - return { - type :ItemType.Id, - id : value.text}; + return { + type: ItemType.Id, + id: value.text, + }; } function applyInt(value: Token): Item { - return { - type : ItemType.Int, - int : BigInt(value.text)}; + return { + type: ItemType.Int, + int: +value.text, + }; } function applyFlo(value: Token): Item { - return { - type : ItemType.Flo, - flo : +value.text}; + return { + type: ItemType.Flo, + flo: +value.text, + }; } function applyStr(value: Token): 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): 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 + ) +); +CON_STR.setPattern( + 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 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."); + } +} -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 - - )) +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; +} -CON_STR.setPattern( - 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(" ") + ")"; +function extendEnv(env : Env, vari : string, data : AST) : Env{ + // add var + if (!(vari in env)){ + env[vari] = [data]; + // update }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(); + 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(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: 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))@") + +// eval print loop +const readline = require("node:readline"); + +const rl = readline.createInterface({ + input: process.stdin, + output: process.stdout, +}); -evaluate(`(main '((text 12)) [ 快狐跳懶狗\\\\\\\[\\\]\\\(\\\)(italic "fox and dog") (bold [OK])])`) -//evaluate("@(let (a 17) (+ a 10))@") \ No newline at end of file +rl.question(`What's your program?`, (prog: string) => { + console.log(evaluate(prog)); + rl.close(); +}); -- 2.39.2