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) {
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],
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));
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();
+});
-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],
]);
/**
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();
+});