This commit is contained in:
Tan, Kian-ting 2024-04-03 22:02:45 +08:00
parent 34335f97e1
commit 3bb1609bbe
5 changed files with 5413 additions and 52 deletions

13
.eslintrc.json Normal file
View file

@ -0,0 +1,13 @@
{
"env": {
"browser": true,
"es2021": true
},
"extends": "standard-with-typescript",
"parserOptions": {
"ecmaVersion": "latest",
"sourceType": "module"
},
"rules": {
}
}

5283
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -16,10 +16,17 @@
"author": "Tan Kian-ting",
"license": "MIT",
"dependencies": {
"typescript": "^5.4.3",
"typescript-parsec": "^0.3.4"
},
"devDependencies": {
"@types/node": "^20.11.30"
"@types/node": "^20.11.30",
"@typescript-eslint/eslint-plugin": "^6.21.0",
"eslint": "^8.57.0",
"eslint-config-standard-with-typescript": "^43.0.1",
"eslint-plugin-import": "^2.29.1",
"eslint-plugin-n": "^16.6.2",
"eslint-plugin-promise": "^6.1.1",
"eslint-plugin-typescript": "^0.14.0",
"typescript": "^5.4.3"
}
}

View file

@ -32,7 +32,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],
@ -158,18 +158,29 @@ function astToString(ast) {
function isItem(x) {
return !Array.isArray(x);
}
function interpBinary(op, argsMapped) {
// x + y
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),
};
}
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),
};
}
return {
type: ItemType.Int,
int: op(fst.int, snd.int),
@ -195,6 +206,21 @@ function mul(x, y) {
function div(x, y) {
return x / y;
}
function lt(x, y) {
return x < y;
}
function gt(x, y) {
return x > y;
}
function eq(x, y) {
return x == y;
}
function le(x, y) {
return x <= y;
}
function ge(x, y) {
return x >= y;
}
function extendEnv(env, vari, data) {
// add var
if (!(vari in env)) {
@ -217,6 +243,7 @@ function isItemArray(x) {
return x[0].hasOwnProperty('type');
}
function interp(prog, env) {
console.log(astToString(prog));
if (Array.isArray(prog)) {
if (!Array.isArray(prog[0])) {
let op = prog[0];
@ -257,7 +284,7 @@ function interp(prog, env) {
let vari = binding[0];
if (vari.hasOwnProperty("id")) {
let variName = vari.id;
newEnv = extendEnv(newEnv, variName, binding[1]);
newEnv = extendEnv(newEnv, variName, interp(binding[1], env));
}
}
}
@ -302,10 +329,42 @@ function interp(prog, env) {
}
else if (op.id == "/") {
return interpBinary(div, argsMapped);
// bool calculation
}
else if (op.id == ">") {
return interpBinary(gt, argsMapped, true);
}
else if (op.id == "<") {
return interpBinary(lt, argsMapped, true);
}
else if (op.id == ">=") {
return interpBinary(ge, argsMapped, true);
}
else if (op.id == "<=") {
return interpBinary(le, argsMapped, true);
}
else if (op.id == "==") {
return interpBinary(eq, argsMapped, true);
// other named function call
}
else {
throw new Error("todo for other id");
let caller = interp(prog[0], env);
let varArgs = caller.vars;
let varArgLen = varArgs.length;
let 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 = env;
var fuBody = caller.body;
for (var i = 0; i < argsMapped.length; i++) {
newEnv = extendEnv(env, varArgs[i].id, argsMapped[i]);
}
return interp(fuBody, newEnv);
}
throw new Error("aaaa");
}
}
// the caller should not be a non-id constant
@ -333,10 +392,9 @@ 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(astToString(interped));
return a;
let input = (0, typescript_parsec_1.expectSingleResult)((0, typescript_parsec_1.expectEOF)(LISP.parse(tokenizer.parse(expr))));
let interped = interp(input, emptyEnv);
return astToString(interped);
}
//evaluate(`(main '((text 12)) [ 快狐跳懶狗\\\\\\\[\\\]\\\(\\\)(italic "fox and dog") (bold [OK])])`)
//evaluate("@(let (a 17) (+ a 10))@")

View file

@ -1,3 +1,4 @@
import { validateHeaderName } from "http";
import { Token } from "typescript-parsec";
import {
buildLexer,
@ -88,7 +89,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],
@ -266,17 +267,29 @@ function isItem(x: AST): x is Item {
return !Array.isArray(x);
}
function interpBinary(op: Function, argsMapped: AST[]): ItemFlo | ItemInt {
// x + y
function interpBinary(op: Function, argsMapped: AST[], isBool? : boolean): ItemFlo | ItemInt | ItemBool {
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),
};
}
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),
};
}
return {
type: ItemType.Int,
int: op(fst.int, snd.int),
@ -301,6 +314,21 @@ function mul(x: number, y: number): number {
function div(x: number, y: number): number {
return x / y;
}
function lt(x: number, y: number): boolean {
return x < y;
}
function gt(x: number, y: number): boolean {
return x > y;
}
function eq(x: number, y: number): boolean {
return x == y;
}
function le(x: number, y: number): boolean {
return x <= y;
}
function ge(x: number, y: number): boolean {
return x >= y;
}
function extendEnv(env : Env, vari : string, data : AST) : Env{
// add var
@ -326,10 +354,15 @@ function isItemArray(x: any): x is Item[] {
}
function interp(prog: AST, env: Env): AST {
console.log(astToString(prog));
if (Array.isArray(prog)) {
if (!Array.isArray(prog[0])) {
let op = prog[0];
if (op.type == ItemType.Id) {
// a list
if (op.id == "quote"){
}
if (op.id == "lambda"){
let vars = prog[1];
if (prog.length != 3){
@ -366,7 +399,8 @@ function interp(prog: AST, env: Env): AST {
let vari = binding[0];
if (vari.hasOwnProperty("id")){
let variName = (<ItemId>vari).id;
newEnv = extendEnv(newEnv, variName , binding[1]);
newEnv = extendEnv(newEnv, variName , interp(binding[1], env));
}
}
@ -406,9 +440,38 @@ function interp(prog: AST, env: Env): AST {
return interpBinary(mul, argsMapped);
} else if (op.id == "/") {
return interpBinary(div, argsMapped);
// bool calculation
} else if (op.id == ">") {
return interpBinary(gt, argsMapped,true);
} else if (op.id == "<") {
return interpBinary(lt, argsMapped,true);
} else if (op.id == ">=") {
return interpBinary(ge, argsMapped,true);
} else if (op.id == "<=") {
return interpBinary(le, argsMapped,true);
} else if (op.id == "==") {
return interpBinary(eq, argsMapped,true);
// other named function call
} else {
throw new Error("todo for other id");
let caller = interp(prog[0],env);
let varArgs = (<ItemId[]>(<Closure>caller).vars);
let varArgLen = varArgs.length;
let 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 = env;
var fuBody = (<Closure>caller).body;
for(var i=0;i<argsMapped.length;i++){
newEnv = extendEnv(env, varArgs[i].id, argsMapped[i]);
}
return interp(fuBody, newEnv);
}
throw new Error("aaaa");
}}
// the caller should not be a non-id constant
} else {
@ -431,11 +494,10 @@ 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(astToString(interped));
return a;
function evaluate(expr: string): string {
let input = expectSingleResult(expectEOF(LISP.parse(tokenizer.parse(expr))));
let interped = interp(input, emptyEnv);
return astToString(interped);
}
//evaluate(`(main '((text 12)) [ 快狐跳懶狗\\\\\\\[\\\]\\\(\\\)(italic "fox and dog") (bold [OK])])`)