add closure

This commit is contained in:
Tan, Kian-ting 2024-03-31 21:23:38 +08:00
parent 840dabc683
commit 34335f97e1
3 changed files with 92 additions and 16 deletions

6
README.md Normal file
View file

@ -0,0 +1,6 @@
## Just another interpretation of typesetting
## TODO
- [ ] apply
- [ ] letrec
- [ ] concat string
- [ ] basic typesetting format

View file

@ -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 `<closure; binding : ${binding}, body : ${body}>`;
}
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])])`)

View file

@ -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 `<closure; binding : ${binding}, body : ${body}>`;
}
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)
|| (<AST[]>(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;
}