From 34335f97e1b1900bb72ea0e468086c2ea98dbba6 Mon Sep 17 00:00:00 2001 From: Tan Kian-ting Date: Sun, 31 Mar 2024 21:23:38 +0800 Subject: [PATCH] add closure --- README.md | 6 ++++++ src/index.js | 45 ++++++++++++++++++++++++++++++++++------- src/index.ts | 57 +++++++++++++++++++++++++++++++++++++++++++--------- 3 files changed, 92 insertions(+), 16 deletions(-) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 0000000..a122d82 --- /dev/null +++ b/README.md @@ -0,0 +1,6 @@ +## Just another interpretation of typesetting +## TODO + - [ ] apply + - [ ] letrec + - [ ] concat string + - [ ] basic typesetting format \ No newline at end of file diff --git a/src/index.js b/src/index.js index 54f2775..0240760 100644 --- a/src/index.js +++ b/src/index.js @@ -25,6 +25,7 @@ var ItemType; ItemType[ItemType["Id"] = 2] = "Id"; ItemType[ItemType["Str"] = 3] = "Str"; ItemType[ItemType["Bool"] = 4] = "Bool"; + ItemType[ItemType["Clos"] = 5] = "Clos"; })(ItemType || (ItemType = {})); const tokenizer = (0, typescript_parsec_1.buildLexer)([ [true, /^\d+/g, TokenKind.Int], @@ -126,9 +127,9 @@ SINGLE.setPattern((0, typescript_parsec_2.alt)((0, typescript_parsec_2.apply)((0 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)); -function printAST(ast) { +function astToString(ast) { if (Array.isArray(ast)) { - let ast2 = ast.map(printAST); + let ast2 = ast.map(astToString); return "(" + ast2.join(" ") + ")"; } else { @@ -144,6 +145,11 @@ function printAST(ast) { else if (ast.type == ItemType.Bool) { return ast.bool.toString(); } + else if (ast.type == ItemType.Clos) { + let binding = astToString(ast.vars); + let body = astToString(ast.body); + return ``; + } else { return ast.int.toString(); } @@ -201,15 +207,40 @@ function extendEnv(env, vari, data) { return env; } var emptyEnv = {}; +/** + * @throws {Error} + */ +function invalidLengthException(id, no) { + return new Error(`the number of args for ${id} should be ${no}.`); +} +function isItemArray(x) { + return x[0].hasOwnProperty('type'); +} 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") { + if (op.id == "lambda") { + let vars = prog[1]; + if (prog.length != 3) { + throw invalidLengthException('lambda', 3); + } + else if (!isItemArray(vars)) { + throw new Error("the vars of lambda should be a list of items"); + } + else { + return { + type: ItemType.Clos, + vars: vars, + body: prog[2], + }; + } + } + else if (op.id == "let") { let bindings = prog[1]; if (prog.length != 3) { - throw new Error("the number of args for 'let' should be 2."); + throw invalidLengthException('let', 3); } else if (!Array.isArray(bindings)) { throw new Error("the bindings should be array"); @@ -220,7 +251,7 @@ function interp(prog, env) { let binding = bindings[i]; if (!Array.isArray(binding) || (binding).length != 2) { - throw new Error("mall formed of let."); + throw new Error("malformed of let."); } else { let vari = binding[0]; @@ -236,7 +267,7 @@ function interp(prog, env) { } else if (op.id == "if") { if (prog.length != 4) { - throw new Error("the args of if should be 2."); + throw invalidLengthException('if', 4); } else { let cond = interp(prog[1], env); @@ -304,7 +335,7 @@ function interp(prog, env) { function evaluate(expr) { let a = (0, typescript_parsec_1.expectSingleResult)((0, typescript_parsec_1.expectEOF)(LISP.parse(tokenizer.parse(expr)))); let interped = interp(a, emptyEnv); - console.log(printAST(interped)); + console.log(astToString(interped)); return a; } //evaluate(`(main '((text 12)) [ 快狐跳懶狗\\\\\\\[\\\]\\\(\\\)(italic "fox and dog") (bold [OK])])`) diff --git a/src/index.ts b/src/index.ts index 1a61ccd..37ed863 100644 --- a/src/index.ts +++ b/src/index.ts @@ -40,9 +40,10 @@ enum ItemType { Id, Str, Bool, + Clos, } -type Item = ItemStr | ItemInt | ItemId | ItemFlo | ItemBool; +type Item = ItemStr | ItemInt | ItemId | ItemFlo | ItemBool | Closure; interface ItemStr { type: ItemType.Str; @@ -74,6 +75,12 @@ interface Env { [Key: string]: AST[]; } +interface Closure{ + type: ItemType.Clos; + vars : Item[]; + body : AST; +} + type AST = Item | AST[]; const tokenizer = buildLexer([ @@ -231,9 +238,9 @@ CON_STR.setPattern( apply(kmid(str("["), rep_sc(CON_STR_INNER), str("]")), applyStrings) ); -function printAST(ast: AST): string { +function astToString(ast: AST): string { if (Array.isArray(ast)) { - let ast2 = ast.map(printAST); + let ast2 = ast.map(astToString); return "(" + ast2.join(" ") + ")"; } else { if (ast.type == ItemType.Str) { @@ -244,7 +251,12 @@ function printAST(ast: AST): string { return ast.flo.toString(); } else if (ast.type == ItemType.Bool) { return ast.bool.toString(); - } else { + }else if (ast.type == ItemType.Clos){ + let binding = astToString(ast.vars); + let body = astToString(ast.body); + return ``; + } + else { return ast.int.toString(); } } @@ -302,15 +314,42 @@ function extendEnv(env : Env, vari : string, data : AST) : Env{ } var emptyEnv: Env = {}; +/** + * @throws {Error} + */ +function invalidLengthException( id : string, no : number) : Error{ + return new Error(`the number of args for ${id} should be ${no}.`); +} + +function isItemArray(x: any): x is Item[] { + return x[0].hasOwnProperty('type'); + } + 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"){ + if (op.id == "lambda"){ + let vars = prog[1]; + if (prog.length != 3){ + throw invalidLengthException('lambda', 3); + } + else if (!isItemArray(vars)){ + throw new Error("the vars of lambda should be a list of items"); + } + else{ + return { + type: ItemType.Clos, + vars: vars, + body: prog[2], + } + } + } + else if (op.id == "let"){ let bindings = prog[1]; if (prog.length != 3){ - throw new Error("the number of args for 'let' should be 2."); + throw invalidLengthException('let', 3); } else if (!Array.isArray(bindings)){ throw new Error("the bindings should be array"); @@ -322,7 +361,7 @@ function interp(prog: AST, env: Env): AST { let binding = bindings[i]; if (!Array.isArray(binding) || ((binding)).length != 2){ - throw new Error("mall formed of let.") + throw new Error("malformed of let.") }else{ let vari = binding[0]; if (vari.hasOwnProperty("id")){ @@ -338,7 +377,7 @@ function interp(prog: AST, env: Env): AST { } else if(op.id == "if"){ if (prog.length != 4){ - throw new Error("the args of if should be 2."); + throw invalidLengthException('if', 4); }else{ let cond = interp(prog[1], env); if (Array.isArray(cond)){ @@ -395,7 +434,7 @@ function interp(prog: AST, env: Env): AST { function evaluate(expr: string): AST { let a = expectSingleResult(expectEOF(LISP.parse(tokenizer.parse(expr)))); let interped = interp(a, emptyEnv); - console.log(printAST(interped)); + console.log(astToString(interped)); return a; } -- 2.39.2