add recursion

This commit is contained in:
Tan, Kian-ting 2024-04-06 16:13:44 +08:00
parent 47e862a2f3
commit 15735fcae5
3 changed files with 331 additions and 145 deletions

View file

@ -1,10 +1,10 @@
## Just another interpretation of typesetting
## TODO
- [v] apply - 240403
- [v] letrec - 240403
- [v] letrec - 240405
- [v] car - 240403
- [ ] cdr and cons
- [ ] concat string (++)
- [v] cdr and cons
- [v] concat string (++)
- [ ] create pdf
- [ ] close pdf
- [ ] add character

View file

@ -33,7 +33,7 @@ const tokenizer = (0, typescript_parsec_1.buildLexer)([
[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, /^([+\-*/a-zA-Z_][0-9+\-*/a-zA-Z_]*|[<>]=?|!?=)/g, TokenKind.Id],
[true, /^\"([^\"]|\\\")+\"/g, TokenKind.Str],
[true, /^[(]/g, TokenKind.LParen],
[true, /^[)]/g, TokenKind.RParen],
@ -44,7 +44,7 @@ const tokenizer = (0, typescript_parsec_1.buildLexer)([
[true, /^\\/g, TokenKind.BSlash],
[true, /^([^+\-*/a-zA-Z_0-9\[\]()'\s\t\r\n\\]+)/g, TokenKind.Other],
]);
/**
/*
* ## BNF
LISP = UNIT | LISPS | CON_STR
LISPS = "(" LISP ")" | "'" "(" LISP ")"
@ -95,7 +95,7 @@ function applyStr(value) {
};
}
function applyBool(value) {
if (value.text == "true") {
if (value.text === "true") {
return {
type: ItemType.Bool,
bool: true,
@ -112,48 +112,53 @@ function applyList(value) {
return value;
}
function applyQuoted(value) {
let head = { type: ItemType.Id, id: "quote" };
let merged = [head, value];
const head = { type: ItemType.Id, id: "quote" };
const merged = [head, value];
return merged;
}
function applyStrings(value) {
let head = [{ type: ItemType.Id, id: "%concat" }];
let merged = head.concat(value);
const head = [{ type: ItemType.Id, id: "%concat" }];
const 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));
const __ = (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), (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));
function astToString(ast) {
function astToString(ast, isInQuoted) {
if (Array.isArray(ast)) {
let ast2 = ast.map(astToString);
const ast2 = ast.map((x) => astToString(x, isInQuoted));
return "(" + ast2.join(" ") + ")";
}
else {
if (ast.type == ItemType.Str) {
if (ast.type === ItemType.Str) {
return "`" + ast.str + "`";
}
else if (ast.type == ItemType.Id) {
else if (ast.type === ItemType.Id) {
return ast.id;
}
else if (ast.type == ItemType.Flo) {
else if (ast.type === ItemType.Flo) {
return ast.flo.toString();
}
else if (ast.type == ItemType.Bool) {
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);
else if (ast.type === ItemType.Clos) {
const binding = astToString(ast.vars);
const body = astToString(ast.body);
return `<closure; binding : ${binding}, body : ${body}>`;
}
else if (ast.type == ItemType.Ls) {
let body = astToString(ast.list);
return "'" + body;
else if (ast.type === ItemType.Ls) {
const body = astToString(ast.list, true);
if (isInQuoted) {
return body;
}
else {
return "'" + body;
}
}
else {
return ast.int.toString();
@ -163,36 +168,48 @@ function astToString(ast) {
function isItem(x) {
return !Array.isArray(x);
}
function interpBinary(op, argsMapped, isBool) {
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) {
if (isBool == true) {
return {
type: ItemType.Bool,
bool: op(fst.flo, snd.flo),
};
}
function interpBinary(op, argsMapped) {
const fst = argsMapped[0];
const 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) {
if (isBool == true) {
return {
type: ItemType.Bool,
bool: op(fst.int, snd.int),
};
}
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");
throw new Error("the type of add should be (int, int) or (flo, flo)");
}
}
else {
throw new Error(`the number of args of ${op} should be 2, but it's ${argsMapped}`);
}
}
function interpBinaryBool(op, argsMapped) {
const fst = argsMapped[0];
const snd = argsMapped[1];
if (argsMapped.length === 2 && isItem(fst) && isItem(snd)) {
if (fst.type === ItemType.Flo && snd.type === ItemType.Flo) {
return {
type: ItemType.Bool,
bool: op(fst.flo, snd.flo),
};
}
else if (fst.type === ItemType.Int && snd.type === ItemType.Int) {
return {
type: ItemType.Bool,
bool: op(fst.int, snd.int),
};
}
else {
throw new Error("the type of add should be (int, int) or (flo, flo)");
}
}
else {
@ -218,19 +235,29 @@ function gt(x, y) {
return x > y;
}
function eq(x, y) {
return x == y;
return x === y;
}
function le(x, y) {
return x <= y;
}
function ne(x, y) {
return x !== y;
}
function ge(x, y) {
return x >= y;
}
function concatString(l, r) {
const rtn = {
type: ItemType.Str,
str: l.str + r.str,
};
return rtn;
}
/** list manipulation */
function car(x) {
let fst = x.list[0];
const fst = x.list[0];
if (Array.isArray(fst)) {
let rtnList = {
const rtnList = {
type: ItemType.Ls,
list: fst,
};
@ -240,18 +267,37 @@ function car(x) {
return fst;
}
}
function cdr(x) {
if (x.list.length == 0) {
throw new Error("the argument of 'cdr' can't be a empty list.");
}
const remained = x.list.slice(1);
const rtnList = {
type: ItemType.Ls,
list: remained,
};
return rtnList;
}
function cons(h, t) {
const inner = [h].concat(t.list);
const rtnList = {
type: ItemType.Ls,
list: inner,
};
return rtnList;
}
function extendEnv(env, vari, isRec, data) {
// add var
if (!(vari in env)) {
env[vari] = [{ isRec: isRec, value: data }];
env[vari] = [{ isRec, value: data }];
// update
}
else {
env[vari] = [{ isRec: isRec, value: data }].concat(env[vari]);
env[vari] = [{ isRec, value: data }].concat(env[vari]);
}
return env;
}
var emptyEnv = {};
const emptyEnv = {};
/**
* @throws {Error}
*/
@ -270,11 +316,11 @@ function isClosure(x) {
function interp(prog, env) {
if (Array.isArray(prog)) {
if (!Array.isArray(prog[0])) {
let op = prog[0];
if (op.type == ItemType.Id) {
const op = prog[0];
if (op.type === ItemType.Id) {
// a list
if (op.id == "quote") {
let body = prog[1];
if (op.id === "quote") {
const body = prog[1];
if (!Array.isArray(body)) {
throw new Error("the argument of quote, aka: " + body + ", is not a list.");
}
@ -285,10 +331,10 @@ function interp(prog, env) {
};
}
}
/**lambda */
else if (op.id == "lambda") {
let vars = prog[1];
if (prog.length != 3) {
/* lambda */
else if (op.id === "lambda") {
const vars = prog[1];
if (prog.length !== 3) {
throw invalidLengthException('lambda', 2);
}
else if (!isItemArray(vars)) {
@ -297,17 +343,17 @@ function interp(prog, env) {
else {
return {
type: ItemType.Clos,
env: env,
vars: vars,
env,
vars,
body: prog[2],
};
}
}
/** let function */
else if (op.id == "let" || op.id == "letrec") {
let bindings = prog[1];
if (prog.length != 3) {
if (op.id == "let") {
else if (op.id === "let" || op.id === "letrec") {
const bindings = prog[1];
if (prog.length !== 3) {
if (op.id === "let") {
throw invalidLengthException('let', 2);
}
else {
@ -318,12 +364,12 @@ function interp(prog, env) {
throw new Error("the bindings should be array");
}
else {
var newEnv = structuredClone(env);
for (var i = 0; i < bindings.length; i++) {
let binding = bindings[i];
let newEnv = structuredClone(env);
for (let i = 0; i < bindings.length; i++) {
const binding = bindings[i];
if (!Array.isArray(binding)
|| (binding).length != 2) {
if (op.id == "let") {
|| binding.length !== 2) {
if (op.id === "let") {
throw new Error("malformed of let.");
}
else {
@ -331,11 +377,11 @@ function interp(prog, env) {
}
}
else {
let vari = binding[0];
const vari = binding[0];
if (vari.hasOwnProperty("id")) {
let variName = vari.id;
let data = interp(binding[1], env);
if (op.id == "letrec") {
const variName = vari.id;
const data = interp(binding[1], env);
if (op.id === "letrec") {
newEnv = extendEnv(newEnv, variName, true, data);
}
else {
@ -344,24 +390,24 @@ function interp(prog, env) {
}
}
}
let body = prog[2];
const body = prog[2];
return interp(body, newEnv);
}
}
// end of let
else if (op.id == "if") {
if (prog.length != 4) {
else if (op.id === "if") {
if (prog.length !== 4) {
throw invalidLengthException('if', 3);
}
else {
let cond = interp(prog[1], env);
const 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) {
else if (cond.type !== ItemType.Bool) {
throw new Error("type error of cond, not a bool");
}
else if (cond.bool == true) {
else if (cond.bool === true) {
return interp(prog[2], env);
// if cond is false
}
@ -371,74 +417,116 @@ function interp(prog, env) {
}
}
else {
let argsMapped = prog.slice(1).map((x) => {
const argsMapped = prog.slice(1).map((x) => {
return interp(x, env);
});
// binary basic operator
if (op.id == "+") {
if (op.id === "+") {
return interpBinary(add, argsMapped);
}
else if (op.id == "-") {
else if (op.id === "-") {
return interpBinary(sub, argsMapped);
}
else if (op.id == "*") {
else if (op.id === "*") {
return interpBinary(mul, argsMapped);
}
else if (op.id == "/") {
else if (op.id === "/") {
return interpBinary(div, argsMapped);
// bool calculation
}
else if (op.id == ">") {
return interpBinary(gt, argsMapped, true);
else if (op.id === ">") {
return interpBinaryBool(gt, argsMapped);
}
else if (op.id == "<") {
return interpBinary(lt, argsMapped, true);
else if (op.id === "<") {
return interpBinaryBool(lt, argsMapped);
}
else if (op.id == ">=") {
return interpBinary(ge, argsMapped, true);
else if (op.id === ">=") {
return interpBinaryBool(ge, argsMapped);
}
else if (op.id == "<=") {
return interpBinary(le, argsMapped, true);
else if (op.id === "<=") {
return interpBinaryBool(le, argsMapped);
}
else if (op.id == "==") {
return interpBinary(eq, argsMapped, true);
else if (op.id === "=") {
return interpBinaryBool(eq, argsMapped);
}
else if (op.id == "car") {
let arg = argsMapped[0];
if (prog.length != 2) {
else if (op.id === "!=") {
return interpBinaryBool(ne, argsMapped);
}
else if (op.id === "car") {
const arg = argsMapped[0];
if (prog.length !== 2) {
throw invalidLengthException('car', 1);
}
else if (!arg.hasOwnProperty('type') || arg.type != ItemType.Ls) {
else if (!arg.hasOwnProperty('type') || arg.type !== ItemType.Ls) {
throw new Error("the arg of 'car' is not a list.");
}
else {
return car(arg);
}
// other named function call
}
else if (op.id === "cdr") {
const arg = argsMapped[0];
if (prog.length !== 2) {
throw invalidLengthException('cdr', 1);
}
else if (!arg.hasOwnProperty('type') || arg.type !== ItemType.Ls) {
throw new Error("the arg of 'cdr' is not a list.");
}
else {
return cdr(arg);
}
}
else if (op.id === "cons") {
const arg = argsMapped;
if (prog.length !== 3) {
throw invalidLengthException('cdr', 2);
}
else if (!arg[1].hasOwnProperty('type') || arg[1].type !== ItemType.Ls) {
throw new Error("the 2nd arg of 'cons' is not a list.");
}
else {
return cons(arg[0], arg[1]);
}
} // string manipulations
else if (op.id === "++") {
const lhs = prog[1];
const rhs = prog[2];
if (prog.length !== 3) {
throw invalidLengthException('++', 2);
}
else if (!isItem(lhs) || !isItem(rhs)
|| lhs.type != ItemType.Str || rhs.type != ItemType.Str) {
throw new Error("at least one of the arg. of '++' is not a str.");
}
else {
return concatString(lhs, rhs);
}
}
// other named function call
else {
let caller = interp(prog[0], env);
let varArgs = caller.vars;
let varArgLen = varArgs.length;
let argsMappedLen = argsMapped.length;
if (argsMappedLen != varArgLen) {
const caller = interp(prog[0], env);
const varArgs = caller.vars;
const varArgLen = varArgs.length;
const argsMappedLen = argsMapped.length;
if (argsMappedLen !== varArgLen) {
throw new Error("the number of the arguments is"
+ " not the same of that of the input vars.");
}
else {
var newEnv = structuredClone(caller.env);
for (var i = 0; i < Object.keys(env).length; i++) {
let currentKey = Object.keys(env)[i];
let currentValue = env[currentKey];
if (currentValue[0].isRec !== undefined && currentValue[0].isRec == true) {
newEnv = extendEnv(newEnv, currentKey, true, currentValue[0].value);
}
}
var fuBody = caller.body;
for (var i = 0; i < argsMapped.length; i++) {
let varArg = varArgs[i];
var varArgIsRec = false;
if (varArg.isRec !== undefined && varArg.isRec == true) {
let newEnv = structuredClone(caller.env);
// for recursion function usage
/*for(const key in env){
const currentKey = key;
const currentValue = env[currentKey];
if (currentValue[0].isRec !== undefined && currentValue[0].isRec === true){
newEnv = extendEnv(newEnv, currentKey, true, currentValue[0].value);
}
}*/
const fuBody = caller.body;
for (let i = 0; i < argsMapped.length; i++) {
const varArg = varArgs[i];
let varArgIsRec = false;
if (varArg.isRec !== undefined && varArg.isRec === true) {
varArgIsRec = true;
}
newEnv = extendEnv(newEnv, varArgs[i].id, varArgIsRec, argsMapped[i]);
@ -455,24 +543,24 @@ function interp(prog, env) {
// the caller which is a higher-function call
}
else {
let argsMapped = prog.slice(1).map((x) => {
const argsMapped = prog.slice(1).map((x) => {
return interp(x, env);
});
let caller = interp(prog[0], env);
let varArgs = caller.vars;
let varArgLen = varArgs.length;
let argsMappedLen = argsMapped.length;
if (argsMappedLen != varArgLen) {
const caller = interp(prog[0], env);
const varArgs = caller.vars;
const varArgLen = varArgs.length;
const argsMappedLen = argsMapped.length;
if (argsMappedLen !== varArgLen) {
throw new Error("the number of the arguments is"
+ " not the same of that of the input vars.");
}
else {
var fuBody = caller.body;
var newEnv = structuredClone(env);
const fuBody = caller.body;
let newEnv = structuredClone(env);
// for recursion function usage
for (var i = 0; i < argsMapped.length; i++) {
var varArgIsRec = false;
if (varArgs[i].isRec !== undefined && varArgs[i].isRec == true) {
for (let i = 0; i < argsMapped.length; i++) {
let varArgIsRec = false;
if (varArgs[i].isRec !== undefined && varArgs[i].isRec === true) {
varArgIsRec = true;
}
newEnv = extendEnv(newEnv, varArgs[i].id, varArgIsRec, argsMapped[i]);
@ -483,24 +571,37 @@ function interp(prog, env) {
}
else {
// constant
if (prog.type != ItemType.Id) {
if (prog.type !== ItemType.Id) {
return prog;
}
// other variable
else {
let varName = prog.id;
let isRecAndVal = env[varName][0];
const varName = prog.id;
const isRecAndVal = env[varName][0];
// for letrec's usage
if (isRecAndVal.isRec === true) {
let value = isRecAndVal.value;
if (isClosure(value)) {
for (const key in env) {
const valueOfKey = env[key][0];
if (valueOfKey.isRec == true) {
value.env = extendEnv(value.env, key, true, valueOfKey.value);
}
}
return value;
}
}
return isRecAndVal.value;
}
}
}
function evaluate(expr) {
let input = (0, typescript_parsec_1.expectSingleResult)((0, typescript_parsec_1.expectEOF)(LISP.parse(tokenizer.parse(expr))));
let interped = interp(input, emptyEnv);
const input = (0, typescript_parsec_1.expectSingleResult)((0, typescript_parsec_1.expectEOF)(LISP.parse(tokenizer.parse(expr))));
const interped = interp(input, emptyEnv);
return astToString(interped);
}
//evaluate(`(main '((text 12)) [ 快狐跳懶狗\\\\\\\[\\\]\\\(\\\)(italic "fox and dog") (bold [OK])])`)
//evaluate("@(let (a 17) (+ a 10))@")
// 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({

View file

@ -101,7 +101,7 @@ const tokenizer = buildLexer([
[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, /^([+\-*/a-zA-Z_][0-9+\-*/a-zA-Z_]*|[<>]=?|!?=)/g, TokenKind.Id],
[true, /^\"([^\"]|\\\")+\"/g, TokenKind.Str],
[true, /^[(]/g, TokenKind.LParen],
[true, /^[)]/g, TokenKind.RParen],
@ -251,9 +251,9 @@ CON_STR.setPattern(
apply(kmid(str("["), rep_sc(CON_STR_INNER), str("]")), applyStrings)
);
function astToString(ast: AST): string {
function astToString(ast: AST, isInQuoted? : boolean): string {
if (Array.isArray(ast)) {
const ast2 = ast.map(astToString);
const ast2 = ast.map((x)=>astToString(x, isInQuoted));
return "(" + ast2.join(" ") + ")";
} else {
if (ast.type === ItemType.Str) {
@ -269,8 +269,12 @@ function astToString(ast: AST): string {
const body = astToString(ast.body);
return `<closure; binding : ${binding}, body : ${body}>`;
}else if (ast.type === ItemType.Ls){
const body = astToString(ast.list);
return "'"+body;
const body = astToString(ast.list, true);
if (isInQuoted){
return body;
}else{
return "'"+body;
}
}
else {
return ast.int.toString();
@ -301,7 +305,7 @@ function interpBinary(op: (a : number, b : number) => number, argsMapped: AST[])
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.");
throw new Error(`the number of args of ${op} should be 2, but it's ${argsMapped}`);
}
}
@ -352,9 +356,20 @@ function eq(x: number, y: number): boolean {
function le(x: number, y: number): boolean {
return x <= y;
}
function ne(x: number, y: number): boolean {
return x !== y;
}
function ge(x: number, y: number): boolean {
return x >= y;
}
function concatString(l: ItemStr, r : ItemStr) : ItemStr {
const rtn : ItemStr = {
type: ItemType.Str,
str: l.str + r.str,
}
return rtn;
}
/** list manipulation */
function car(x : List) : Item {
@ -365,10 +380,30 @@ function car(x : List) : Item {
list: fst,
}
return rtnList;
}else{
return fst;
}else{
return fst;
}
}
function cdr(x : List) : Item {
if (x.list.length == 0){
throw new Error("the argument of 'cdr' can't be a empty list.")
}
const remained = (x.list as AST[]).slice(1);
const rtnList : List = {
type: ItemType.Ls,
list: remained,
}
return rtnList;
}
function cons(h: AST, t : List) : List {
const inner = [h].concat(t.list);
const rtnList : List = {
type: ItemType.Ls,
list: inner,
}
return rtnList;
}
function extendEnv(env : Env, vari : string, isRec: boolean, data : AST) : Env{
// add var
@ -520,8 +555,10 @@ function interp(prog: AST, env: Env): AST {
return interpBinaryBool(ge, argsMapped);
} else if (op.id === "<=") {
return interpBinaryBool(le, argsMapped);
} else if (op.id === "==") {
} else if (op.id === "=") {
return interpBinaryBool(eq, argsMapped);
} else if (op.id === "!=") {
return interpBinaryBool(ne, argsMapped);
} else if (op.id === "car") {
const arg = argsMapped[0];
if (prog.length !== 2){
@ -531,8 +568,41 @@ function interp(prog: AST, env: Env): AST {
}else{
return car((arg as List));
}
}
else if (op.id === "cdr") {
const arg = argsMapped[0];
if (prog.length !== 2){
throw invalidLengthException('cdr', 1);
}else if (!arg.hasOwnProperty('type') || (arg as Item).type !== ItemType.Ls){
throw new Error("the arg of 'cdr' is not a list.")
}else{
return cdr((arg as List));
}
}else if (op.id === "cons") {
const arg = argsMapped;
if (prog.length !== 3){
throw invalidLengthException('cdr', 2);
}else if (!arg[1].hasOwnProperty('type') || (arg[1] as Item).type !== ItemType.Ls){
throw new Error("the 2nd arg of 'cons' is not a list.")
}else{
return cons(arg[0], (arg[1] as List));
}
} // string manipulations
else if (op.id === "++") {
const lhs = prog[1];
const rhs = prog[2];
if (prog.length !== 3){
throw invalidLengthException('++', 2);
}else if (!isItem(lhs) || !isItem(rhs)
|| lhs.type != ItemType.Str || rhs.type != ItemType.Str){
throw new Error("at least one of the arg. of '++' is not a str.")
}else{
return concatString(lhs, rhs);
}}
// other named function call
} else {
else {
const caller = interp(prog[0],env);
@ -548,13 +618,13 @@ function interp(prog: AST, env: Env): AST {
// for recursion function usage
for(const key in env){
/*for(const key in env){
const currentKey = key;
const currentValue = env[currentKey];
if (currentValue[0].isRec !== undefined && currentValue[0].isRec === true){
newEnv = extendEnv(newEnv, currentKey, true, currentValue[0].value);
}
}
}*/
const fuBody = (caller as Closure).body;
for(let i=0;i<argsMapped.length;i++){
@ -616,6 +686,21 @@ function interp(prog: AST, env: Env): AST {
else{
const varName = prog.id;
const isRecAndVal = env[varName][0];
// for letrec's usage
if (isRecAndVal.isRec === true){
let value = isRecAndVal.value;
if (isClosure(value)){
for (const key in env){
const valueOfKey = env[key][0];
if (valueOfKey.isRec == true){
value.env = extendEnv(value.env, key, true, valueOfKey.value);
}
}
return value;
}
}
return isRecAndVal.value;
}
}