]> git.kianting.info Git - anotherTypesetter/commitdiff
add the function of the interpreter
authorTan Kian-ting <chenjt30@gmail.com>
Sun, 31 Mar 2024 12:59:09 +0000 (20:59 +0800)
committerTan Kian-ting <chenjt30@gmail.com>
Sun, 31 Mar 2024 12:59:09 +0000 (20:59 +0800)
src/index.js
src/index.ts

index 3c0e3a148b6789160dacae9da1eb836b33b4a96c..54f2775798587525b3f253f3d3e1091dc555db9f 100644 (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();
+});
index 46a318ad91db323c4c908424efeaa107eba07dc9..1a61ccd22fada3fde5a9e84d367df3d464b3b72e 100644 (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';
-
-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 ItemI{
+  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<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
+  )
+);
 
+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<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))@")
+
+// 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();
+});