add map and add pdf page

This commit is contained in:
Tan, Kian-ting 2024-04-10 22:56:29 +08:00
parent 15735fcae5
commit 3359c79098
8 changed files with 502 additions and 28 deletions

View file

@ -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

75
package-lock.json generated
View file

@ -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",

View file

@ -16,6 +16,7 @@
"author": "Tan Kian-ting",
"license": "MIT",
"dependencies": {
"pdf-lib": "^1.17.1",
"typescript-parsec": "^0.3.4"
},
"devDependencies": {

View file

@ -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();

View file

@ -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();

12
text.lisp Normal file
View file

@ -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))
))

BIN
text.lisp.pdf Normal file

Binary file not shown.

View file

@ -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. */