add recursion
This commit is contained in:
parent
47e862a2f3
commit
15735fcae5
3 changed files with 331 additions and 145 deletions
|
@ -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
|
||||
|
|
361
src/index.js
361
src/index.js
|
@ -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({
|
||||
|
|
109
src/index.ts
109
src/index.ts
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue