From: Tan Kian-ting Date: Wed, 10 Apr 2024 14:56:29 +0000 (+0800) Subject: add map and add pdf page X-Git-Url: https://git.kianting.info/?a=commitdiff_plain;h=3359c7909878b10d8a136b62d55e08007494dde7;p=anotherTypesetter add map and add pdf page --- diff --git a/README.md b/README.md index b77e925..217e3fa 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,12 @@ - [v] car - 240403 - [v] cdr and cons - [v] concat string (++) - - [ ] create pdf + - [v] set! + - [v] list ref by index + - [ ] dict ref by id + - [v] map 20240410 + - [v] add pdf page 240410 (addPDFPages) + - [v] create pdf 240410 - [ ] close pdf - [ ] add character - [ ] add path diff --git a/package-lock.json b/package-lock.json index dcfd9a6..09be7cc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,6 +9,7 @@ "version": "0.0.1", "license": "MIT", "dependencies": { + "pdf-lib": "^1.17.1", "typescript-parsec": "^0.3.4" }, "devDependencies": { @@ -157,6 +158,22 @@ "node": ">= 8" } }, + "node_modules/@pdf-lib/standard-fonts": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@pdf-lib/standard-fonts/-/standard-fonts-1.0.0.tgz", + "integrity": "sha512-hU30BK9IUN/su0Mn9VdlVKsWBS6GyhVfqjwl1FjZN4TxP6cCw0jP2w7V3Hf5uX7M0AZJ16vey9yE0ny7Sa59ZA==", + "dependencies": { + "pako": "^1.0.6" + } + }, + "node_modules/@pdf-lib/upng": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@pdf-lib/upng/-/upng-1.0.1.tgz", + "integrity": "sha512-dQK2FUMQtowVP00mtIksrlZhdFXQZPC+taih1q4CvPZ5vqdxR/LKBaFg0oAfzd1GlHZXXSPdQfzQnt+ViGvEIQ==", + "dependencies": { + "pako": "^1.0.10" + } + }, "node_modules/@types/json-schema": { "version": "7.0.15", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", @@ -1306,6 +1323,11 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/pako": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", + "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==" + }, "node_modules/parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -1358,6 +1380,17 @@ "node": ">=8" } }, + "node_modules/pdf-lib": { + "version": "1.17.1", + "resolved": "https://registry.npmjs.org/pdf-lib/-/pdf-lib-1.17.1.tgz", + "integrity": "sha512-V/mpyJAoTsN4cnP31vc0wfNA1+p20evqqnap0KLoRUN0Yk/p3wN52DOEsL4oBFcLdb76hlpKPtzJIgo67j/XLw==", + "dependencies": { + "@pdf-lib/standard-fonts": "^1.0.0", + "@pdf-lib/upng": "^1.0.1", + "pako": "^1.0.11", + "tslib": "^1.11.1" + } + }, "node_modules/picomatch": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", @@ -1586,6 +1619,11 @@ "typescript": ">=4.2.0" } }, + "node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", @@ -1789,6 +1827,22 @@ "fastq": "^1.6.0" } }, + "@pdf-lib/standard-fonts": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@pdf-lib/standard-fonts/-/standard-fonts-1.0.0.tgz", + "integrity": "sha512-hU30BK9IUN/su0Mn9VdlVKsWBS6GyhVfqjwl1FjZN4TxP6cCw0jP2w7V3Hf5uX7M0AZJ16vey9yE0ny7Sa59ZA==", + "requires": { + "pako": "^1.0.6" + } + }, + "@pdf-lib/upng": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@pdf-lib/upng/-/upng-1.0.1.tgz", + "integrity": "sha512-dQK2FUMQtowVP00mtIksrlZhdFXQZPC+taih1q4CvPZ5vqdxR/LKBaFg0oAfzd1GlHZXXSPdQfzQnt+ViGvEIQ==", + "requires": { + "pako": "^1.0.10" + } + }, "@types/json-schema": { "version": "7.0.15", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", @@ -2638,6 +2692,11 @@ "p-limit": "^3.0.2" } }, + "pako": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", + "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==" + }, "parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -2675,6 +2734,17 @@ "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", "dev": true }, + "pdf-lib": { + "version": "1.17.1", + "resolved": "https://registry.npmjs.org/pdf-lib/-/pdf-lib-1.17.1.tgz", + "integrity": "sha512-V/mpyJAoTsN4cnP31vc0wfNA1+p20evqqnap0KLoRUN0Yk/p3wN52DOEsL4oBFcLdb76hlpKPtzJIgo67j/XLw==", + "requires": { + "@pdf-lib/standard-fonts": "^1.0.0", + "@pdf-lib/upng": "^1.0.1", + "pako": "^1.0.11", + "tslib": "^1.11.1" + } + }, "picomatch": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", @@ -2815,6 +2885,11 @@ "dev": true, "requires": {} }, + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + }, "type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", diff --git a/package.json b/package.json index e945b64..0f9f047 100644 --- a/package.json +++ b/package.json @@ -16,6 +16,7 @@ "author": "Tan Kian-ting", "license": "MIT", "dependencies": { + "pdf-lib": "^1.17.1", "typescript-parsec": "^0.3.4" }, "devDependencies": { diff --git a/src/index.js b/src/index.js index dfbc12c..4c125a1 100644 --- a/src/index.js +++ b/src/index.js @@ -1,7 +1,31 @@ "use strict"; +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); + __setModuleDefault(result, mod); + return result; +}; Object.defineProperty(exports, "__esModule", { value: true }); +const fs = __importStar(require("fs")); +const pdf_lib_1 = require("pdf-lib"); const typescript_parsec_1 = require("typescript-parsec"); const typescript_parsec_2 = require("typescript-parsec"); +/** input lisp file */ +const filename = "./text.lisp"; +let pdfDoc; var TokenKind; (function (TokenKind) { TokenKind[TokenKind["Id"] = 0] = "Id"; @@ -27,13 +51,14 @@ var ItemType; ItemType[ItemType["Bool"] = 4] = "Bool"; ItemType[ItemType["Clos"] = 5] = "Clos"; ItemType[ItemType["Ls"] = 6] = "Ls"; + ItemType[ItemType["Unit"] = 7] = "Unit"; })(ItemType || (ItemType = {})); const tokenizer = (0, typescript_parsec_1.buildLexer)([ - [true, /^\d+/g, TokenKind.Int], - [true, /^\d+\.\d+/g, TokenKind.Flo], + [true, /^-?\d+/g, TokenKind.Int], + [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], @@ -146,6 +171,9 @@ function astToString(ast, isInQuoted) { else if (ast.type === ItemType.Bool) { return ast.bool.toString(); } + else if (ast.type === ItemType.Unit) { + return "#unit"; // mark for unit + } else if (ast.type === ItemType.Clos) { const binding = astToString(ast.vars); const body = astToString(ast.body); @@ -185,7 +213,7 @@ function interpBinary(op, argsMapped) { }; } else { - throw new Error("the type of add should be (int, int) or (flo, flo)"); + throw new Error(`the type of ${op.toString()} should be (int, int) or (flo, flo)`); } } else { @@ -209,7 +237,7 @@ function interpBinaryBool(op, argsMapped) { }; } else { - throw new Error("the type of add should be (int, int) or (flo, flo)"); + throw new Error(`the type of ${op.toString()} should be (int, int) or (flo, flo)`); } } else { @@ -246,6 +274,13 @@ function ne(x, y) { function ge(x, y) { return x >= y; } +function otherNe(x, y) { + return astToString(x) !== astToString(y); +} +function otherEq(x, y) { + return astToString(x) === astToString(y); +} +// string manipulations function concatString(l, r) { const rtn = { type: ItemType.Str, @@ -253,6 +288,43 @@ function concatString(l, r) { }; return rtn; } +/** + * get string `s`'s substring from ith-char to (j-1)th-char. + * @param s the string + * @param i beginning index + * @param j ending index (excluded) + * @returns the substring + */ +function subString(s, i, j) { + const realI = i.int; + const realStr = s.str; + if (realI >= realStr.length || realI < 0) { + throw new Error("the 2nd argument of `listRef` should between 0..(length of string `s` - 1)"); + } + else if (j === undefined) { + const rtn = { + type: ItemType.Str, + str: realStr.substring(realI) + }; + return rtn; + } + else { + const realJ = j.int; + if (realJ >= realStr.length || realJ < 0) { + throw new Error("the 3rd argument of `listRef` should between 0..(length of string `s` - 1)"); + } + else if (realI > realJ) { + throw new Error("the 2nd argument should not larger than the 3rd arg."); + } + else { + const rtn = { + type: ItemType.Str, + str: realStr.substring(realI, realJ), + }; + return rtn; + } + } +} /** list manipulation */ function car(x) { const fst = x.list[0]; @@ -286,6 +358,16 @@ function cons(h, t) { }; return rtnList; } +function listRef(l, i) { + const realI = i.int; + if (realI >= l.list.length || realI < 0) { + throw new Error("the argument of `listRef` should between 0..(length of l - 1)"); + } + else { + const rtn = l.list[realI]; + return rtn; + } +} function extendEnv(env, vari, isRec, data) { // add var if (!(vari in env)) { @@ -447,10 +529,48 @@ function interp(prog, env) { return interpBinaryBool(le, argsMapped); } else if (op.id === "=") { - return interpBinaryBool(eq, argsMapped); + if (argsMapped[1].type === ItemType.Flo || + argsMapped[1].type === ItemType.Int) { + return interpBinaryBool(eq, argsMapped); + } + else { + if (prog.length !== 3) { + throw invalidLengthException('=', 2); + } + else if (!isItem(argsMapped[0]) + || !isItem(argsMapped[1])) { + throw new Error("Either 1st or 2nd arg of '=' is not a item."); + } + else { + return { + type: ItemType.Bool, + bool: otherEq(argsMapped[0], argsMapped[1]), + }; + } + } } else if (op.id === "!=") { - return interpBinaryBool(ne, argsMapped); + if ((argsMapped[0].type === ItemType.Flo && + argsMapped[0].type === argsMapped[1].type) || + (argsMapped[0].type === ItemType.Int) && + (argsMapped[0].type === argsMapped[1].type)) { + return interpBinaryBool(ne, argsMapped); + } + else { + if (prog.length !== 3) { + throw invalidLengthException('!=', 2); + } + else if (!isItem(argsMapped[1]) + || !isItem(argsMapped[2])) { + throw new Error("Either 1st or 2nd arg of '!=' is not a item."); + } + else { + return { + type: ItemType.Bool, + bool: otherNe(argsMapped[0], argsMapped[1]), + }; + } + } } else if (op.id === "car") { const arg = argsMapped[0]; @@ -487,7 +607,23 @@ function interp(prog, env) { else { return cons(arg[0], arg[1]); } - } // string manipulations + } + else if (op.id === "listRef") { + const arg = argsMapped; + if (prog.length !== 3) { + throw invalidLengthException('listRef', 2); + } + else if (!arg[0].hasOwnProperty('type') || arg[0].type !== ItemType.Ls) { + throw new Error("the 1st arg of 'listRef' is not a list."); + } + else if (!arg[1].hasOwnProperty('type') || arg[1].type !== ItemType.Int) { + throw new Error("the 2nd arg of 'listRef' is not a number."); + } + else { + return listRef(arg[0], arg[1]); + } + } + // string manipulations else if (op.id === "++") { const lhs = prog[1]; const rhs = prog[2]; @@ -502,6 +638,63 @@ function interp(prog, env) { return concatString(lhs, rhs); } } + else if (op.id === "subString") { + const str = prog[1]; + const i = prog[2]; + if (prog.length !== 3 && prog.length !== 4) { + throw new Error(`the number of args for 'subString' should be 2 or 3.`); + } + else if (!isItem(str) || str.type != ItemType.Str) { + throw new Error("the 1st item of the arg for 'subString' should be a string."); + } + else { + if (prog.length == 3) { + // str.substring(i) + return subString(str, i); + } + else { + // str.substring(i,j) + return subString(str, i, prog[3]); + } + } + } + // set manipulations + else if (op.id === "set!") { + const vari = prog[1]; + const replacer = prog[2]; + if (prog.length !== 3) { + throw invalidLengthException('set!', 2); + } + else if (!isItem(vari) || !isItem(replacer) + || env[vari.id][0].value.type != replacer.type) { + throw new Error("the type of replace and variable should be the same."); + } + else { + env[vari.id][0].value = prog[2]; + return { type: ItemType.Unit }; + } + } + else if (op.id === "addPDFPage") { + if (prog.length !== 2) { + throw invalidLengthException('addPDFPage', 1); + } + else if (astToString(argsMapped[0]) !== "'()") { + throw new Error("the arg of addPdfPage should be a empty string '()"); + } + else { + const page = pdfDoc.addPage(); + return { + type: ItemType.Unit, + }; + } + const rtn = argsMapped[argsMapped.length - 1]; + return rtn; + } + // procedures returning the last called command + else if (op.id === "begin") { + const rtn = argsMapped[argsMapped.length - 1]; + return rtn; + } // other named function call else { const caller = interp(prog[0], env); @@ -509,7 +702,7 @@ function interp(prog, env) { const varArgLen = varArgs.length; const argsMappedLen = argsMapped.length; if (argsMappedLen !== varArgLen) { - throw new Error("the number of the arguments is" + throw new Error("the number of the arguments of the caller is" + " not the same of that of the input vars."); } else { @@ -604,11 +797,17 @@ function evaluate(expr) { // evaluate("@(let (a 17) (+ a 10))@") // eval print loop const readline = require("node:readline"); +const node_process_1 = require("node:process"); const rl = readline.createInterface({ input: process.stdin, output: process.stdout, }); -rl.question(`What's your program?`, (prog) => { +async function run() { + pdfDoc = await pdf_lib_1.PDFDocument.create(); + const prog = fs.readFileSync(filename, { encoding: 'utf8' }); console.log(evaluate(prog)); - rl.close(); -}); + const pdfBytes = await pdfDoc.save(); + fs.writeFileSync(filename + '.pdf', pdfBytes, 'binary'); + (0, node_process_1.exit)(0); +} +run(); diff --git a/src/index.ts b/src/index.ts index b2ce069..cf96017 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,4 +1,5 @@ -import { validateHeaderName } from "http"; +import * as fs from 'fs'; +import { PDFDocument } from 'pdf-lib' import { Token } from "typescript-parsec"; import { buildLexer, @@ -19,6 +20,12 @@ import { opt, } from "typescript-parsec"; + + +/** input lisp file */ +const filename = "./text.lisp"; +let pdfDoc : PDFDocument; + enum TokenKind { Id, Int, @@ -43,15 +50,21 @@ enum ItemType { Bool, Clos, Ls, + Unit, } -type Item = ItemStr | ItemInt | ItemId | ItemFlo | ItemBool | Closure | List; +type Item = ItemStr | ItemInt | ItemId | ItemFlo | ItemBool | ItemUnit | Closure | List; interface ItemStr { type: ItemType.Str; str: string; } +// returned type for input or print, etc. #unit for representation +interface ItemUnit { + type: ItemType.Unit; +} + interface ItemInt { type: ItemType.Int; int: number; @@ -97,11 +110,11 @@ interface Closure{ type AST = Item | AST[]; const tokenizer = buildLexer([ - [true, /^\d+/g, TokenKind.Int], - [true, /^\d+\.\d+/g, TokenKind.Flo], + [true, /^-?\d+/g, TokenKind.Int], + [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], @@ -264,6 +277,8 @@ function astToString(ast: AST, isInQuoted? : boolean): string { return ast.flo.toString(); } else if (ast.type === ItemType.Bool) { return ast.bool.toString(); + }else if (ast.type === ItemType.Unit) { + return "#unit"; // mark for unit }else if (ast.type === ItemType.Clos){ const binding = astToString(ast.vars); const body = astToString(ast.body); @@ -302,7 +317,8 @@ function interpBinary(op: (a : number, b : number) => number, argsMapped: AST[]) 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 ${op.toString()} should be (int, int) or (flo, flo)`); } } else { throw new Error(`the number of args of ${op} should be 2, but it's ${argsMapped}`); @@ -325,7 +341,7 @@ function interpBinaryBool(op: (a : number, b : number) => boolean, argsMapped: A bool: op(fst.int, snd.int) as boolean, }; } else { - throw new Error("the type of add should be (int, int) or (flo, flo)"); + throw new Error(`the type of ${op.toString()} should be (int, int) or (flo, flo)`); } } else { throw new Error("the number of args should be 2."); @@ -362,6 +378,16 @@ function ne(x: number, y: number): boolean { function ge(x: number, y: number): boolean { return x >= y; } + +function otherNe(x: any, y: any): boolean { + return astToString(x) !== astToString(y); +} +function otherEq(x: any, y: any): boolean { + return astToString(x) === astToString(y); +} + + +// string manipulations function concatString(l: ItemStr, r : ItemStr) : ItemStr { const rtn : ItemStr = { type: ItemType.Str, @@ -369,6 +395,41 @@ function concatString(l: ItemStr, r : ItemStr) : ItemStr { } return rtn; } +/** + * get string `s`'s substring from ith-char to (j-1)th-char. + * @param s the string + * @param i beginning index + * @param j ending index (excluded) + * @returns the substring + */ +function subString(s: ItemStr, i: ItemInt, j? : ItemInt): ItemStr { + const realI = i.int; + const realStr = s.str; + if (realI >= realStr.length || realI < 0){ + throw new Error("the 2nd argument of `listRef` should between 0..(length of string `s` - 1)"); + } + else if(j === undefined){ + const rtn : ItemStr = { + type:ItemType.Str, + str:realStr.substring(realI) + }; + return rtn; + } + else{ + + const realJ = j.int; + if (realJ >= realStr.length || realJ < 0){ + throw new Error("the 3rd argument of `listRef` should between 0..(length of string `s` - 1)"); + }else if (realI > realJ){ + throw new Error("the 2nd argument should not larger than the 3rd arg."); + }else{ + const rtn : ItemStr = { + type:ItemType.Str, + str:realStr.substring(realI,realJ), + }; + return rtn; + }} +} /** list manipulation */ @@ -404,6 +465,17 @@ function cons(h: AST, t : List) : List { return rtnList; } +function listRef(l: List, i: ItemInt): AST { + const realI = i.int; + if (realI >= l.list.length || realI < 0){ + throw new Error("the argument of `listRef` should between 0..(length of l - 1)"); + }else{ + const rtn = l.list[realI]; + return rtn; + } +} + + function extendEnv(env : Env, vari : string, isRec: boolean, data : AST) : Env{ // add var @@ -437,7 +509,6 @@ function isClosure(x: any): x is Closure { return x.hasOwnProperty('type') && x.hasOwnProperty('vars'); } - function interp(prog: AST, env: Env): AST { if (Array.isArray(prog)) { if (!Array.isArray(prog[0])) { @@ -556,9 +627,42 @@ function interp(prog: AST, env: Env): AST { } else if (op.id === "<=") { return interpBinaryBool(le, argsMapped); } else if (op.id === "=") { - return interpBinaryBool(eq, argsMapped); + if ((argsMapped[1] as Item).type === ItemType.Flo || + (argsMapped[1] as Item).type === ItemType.Int){ + return interpBinaryBool(eq, argsMapped); + }else{ + if (prog.length !== 3){ + throw invalidLengthException('=', 2); + }else if(!isItem(argsMapped[0]) + ||!isItem(argsMapped[1])){ + throw new Error("Either 1st or 2nd arg of '=' is not a item.") + }else{ + return { + type:ItemType.Bool, + bool:otherEq(argsMapped[0], argsMapped[1]), + }; + } + } } else if (op.id === "!=") { + if ( + ((argsMapped[0] as Item).type === ItemType.Flo && + (argsMapped[0] as Item).type === (argsMapped[1] as Item).type)|| + ((argsMapped[0] as Item).type === ItemType.Int) && + ((argsMapped[0] as Item).type === (argsMapped[1] as Item).type)){ return interpBinaryBool(ne, argsMapped); + }else{ + if (prog.length !== 3){ + throw invalidLengthException('!=', 2); + }else if(!isItem(argsMapped[1]) + ||!isItem(argsMapped[2])){ + throw new Error("Either 1st or 2nd arg of '!=' is not a item.") + }else{ + return { + type:ItemType.Bool, + bool:otherNe(argsMapped[0], argsMapped[1]), + }; + } + } } else if (op.id === "car") { const arg = argsMapped[0]; if (prog.length !== 2){ @@ -587,7 +691,23 @@ function interp(prog: AST, env: Env): AST { }else{ return cons(arg[0], (arg[1] as List)); } - } // string manipulations + } + else if (op.id === "listRef"){ + const arg = argsMapped; + if (prog.length !== 3){ + throw invalidLengthException('listRef', 2); + }else if (!arg[0].hasOwnProperty('type') || (arg[0] as Item).type !== ItemType.Ls){ + throw new Error("the 1st arg of 'listRef' is not a list.") + }else if (!arg[1].hasOwnProperty('type') || (arg[1] as Item).type !== ItemType.Int){ + throw new Error("the 2nd arg of 'listRef' is not a number.") + }else{ + return listRef(arg[0] as List, arg[1] as ItemInt); + } + } + + + + // string manipulations else if (op.id === "++") { const lhs = prog[1]; const rhs = prog[2]; @@ -599,7 +719,57 @@ function interp(prog: AST, env: Env): AST { }else{ return concatString(lhs, rhs); }} + else if (op.id === "subString") { + const str = prog[1]; + const i = prog[2]; + if (prog.length !== 3 && prog.length !== 4){ + throw new Error(`the number of args for 'subString' should be 2 or 3.`); + }else if (!isItem(str) || str.type != ItemType.Str ){ + throw new Error("the 1st item of the arg for 'subString' should be a string.") + }else{ + if (prog.length == 3){ + // str.substring(i) + return subString(str, i as ItemInt);} + else{ + // str.substring(i,j) + return subString(str, i as ItemInt, prog[3] as ItemInt);} + } + } + // set manipulations + else if (op.id === "set!") { + const vari : ItemId = prog[1] as ItemId; + const replacer = prog[2]; + if (prog.length !== 3){ + throw invalidLengthException('set!', 2); + }else if (!isItem(vari) || !isItem(replacer) + || (env[vari.id][0].value as Item).type != replacer.type ){ + throw new Error("the type of replace and variable should be the same.") + }else{ + env[vari.id][0].value = prog[2]; + return {type:ItemType.Unit}; + } + } + else if (op.id === "addPDFPage"){ + if (prog.length !== 2){ + throw invalidLengthException('addPDFPage', 1); + }else if(astToString(argsMapped[0]) !== "'()"){ + throw new Error("the arg of addPdfPage should be a empty string '()") + }else{ + const page = pdfDoc.addPage(); + return { + type:ItemType.Unit, + } + } + + const rtn = argsMapped[argsMapped.length-1]; + return rtn; + } + // procedures returning the last called command + else if (op.id === "begin"){ + const rtn = argsMapped[argsMapped.length-1]; + return rtn; + } // other named function call else { @@ -611,7 +781,7 @@ function interp(prog: AST, env: Env): AST { const varArgLen = varArgs.length; const argsMappedLen = argsMapped.length; if (argsMappedLen !== varArgLen){ - throw new Error("the number of the arguments is" + throw new Error("the number of the arguments of the caller is" +" not the same of that of the input vars."); }else{ let newEnv = structuredClone((caller as Closure).env); @@ -717,13 +887,25 @@ function evaluate(expr: string): string { // eval print loop import readline = require("node:readline"); +import { exit } from "node:process"; const rl = readline.createInterface({ input: process.stdin, output: process.stdout, }); -rl.question(`What's your program?`, (prog: string) => { + +async function run(){ + pdfDoc = await PDFDocument.create(); + + + const prog = fs.readFileSync(filename, { encoding: 'utf8' }); + console.log(evaluate(prog)); - rl.close(); -}); + + const pdfBytes = await pdfDoc.save(); + fs.writeFileSync(filename+'.pdf', pdfBytes, 'binary'); + exit(0); +} + +run(); \ No newline at end of file diff --git a/text.lisp b/text.lisp new file mode 100644 index 0000000..637a51d --- /dev/null +++ b/text.lisp @@ -0,0 +1,12 @@ +(letrec (( + map (lambda (f l) + (if (!= l '()) + (cons (f (car l)) (map f (cdr l))) + '())) +)) +(begin +(addPDFPage '()) +(addPDFPage '()) +(map (lambda (x) (+ x 2)) '(8 9 10)) + +)) \ No newline at end of file diff --git a/text.lisp.pdf b/text.lisp.pdf new file mode 100644 index 0000000..a64d82f Binary files /dev/null and b/text.lisp.pdf differ diff --git a/tsconfig.json b/tsconfig.json index 5679481..a3a4c99 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -11,7 +11,7 @@ // "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */ /* Language and Environment */ - "target": "es2016", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */ + "target": "es2017", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */ // "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */ // "jsx": "preserve", /* Specify what JSX code is generated. */ // "experimentalDecorators": true, /* Enable experimental support for TC39 stage 2 draft decorators. */