compile test program
This commit is contained in:
parent
d084959281
commit
b9b479fd63
110
compile.phi
110
compile.phi
@ -1,9 +1,15 @@
|
||||
|
||||
(fn Emitter (ast) (do
|
||||
(fn Emitter (ast filename) (do
|
||||
(let output ())
|
||||
|
||||
(let (enter_scope leave_scope define_sym get_sym) (call Syms))
|
||||
|
||||
(fn generate () (do
|
||||
(call emit "#!/usr/bin/env node\n")
|
||||
(call emit "import { Runtime } from \"./runtime.js\"\n")
|
||||
(call emit "const runtime = new Runtime(\"")
|
||||
(call emit filename)
|
||||
(call emit "\")\n")
|
||||
(call emit_exprs ast)
|
||||
(return (call strings_join output))
|
||||
))
|
||||
@ -11,7 +17,7 @@
|
||||
(fn emit_exprs (exprs) (do
|
||||
(for expr exprs (do
|
||||
(call emit_expr expr)
|
||||
(call emit "\n")
|
||||
(call emit ";\n")
|
||||
))
|
||||
))
|
||||
|
||||
@ -21,13 +27,33 @@
|
||||
(call emit_list expr)
|
||||
) (if (== ty "int") (
|
||||
(let (_ _ value) expr)
|
||||
(call emit (call format "%" value))
|
||||
(call emit (call format "{ type: \"int\", value: % }" value))
|
||||
) (if (== ty "string") (
|
||||
(let (_ _ value) expr)
|
||||
(call emit (call format "\"%\"" (call string_escape value)))
|
||||
(call emit (call format "{ type: \"string\", value: \"%\" }" (call string_escape value)))
|
||||
) (if (== ty "ident") (
|
||||
(let (_ _ value) expr)
|
||||
(call emit (call format "_%" value))
|
||||
|
||||
(let sym (call get_sym value))
|
||||
(if (== sym null) (do
|
||||
(call panic "undefined symbol '%'" value)
|
||||
))
|
||||
|
||||
(let (sym_ty) sym)
|
||||
(if (== sym_ty "builtin") (do
|
||||
(let (_ id) sym)
|
||||
(if (== id "println") (do
|
||||
(call emit (call format
|
||||
"((...args) => (runtime.setLine(%), runtime.builtinPrintln(...args)))"
|
||||
line))
|
||||
) (do
|
||||
(call panic "unknown builtin '%'" id)
|
||||
))
|
||||
) (if (== sym_ty "fn") (do
|
||||
(call emit (call format "_%" value))
|
||||
) (do
|
||||
(call panic "not implemented '%'" sym_ty)
|
||||
)))
|
||||
) (do
|
||||
(call panic "unknown expr type '%' on line %" ty line)
|
||||
)))))
|
||||
@ -36,7 +62,7 @@
|
||||
(fn emit_list (expr) (do
|
||||
(let (ty line s) expr)
|
||||
(if (== (call len s) 0) (do
|
||||
(call emit "[]")
|
||||
(call emit "{ type: \"list\", values: [] }")
|
||||
(return)
|
||||
))
|
||||
(let ((_ _ id)) s)
|
||||
@ -53,6 +79,9 @@
|
||||
|
||||
(call emit (call format "_%" name))
|
||||
))
|
||||
|
||||
(call define_sym name ("fn" name line))
|
||||
|
||||
(call emit (call format ") {\n" name))
|
||||
(call emit_expr body)
|
||||
(call emit "}")
|
||||
@ -85,7 +114,7 @@
|
||||
|
||||
(call emit ")")
|
||||
) (do
|
||||
(call emit "[")
|
||||
(call emit "{ type: \"list\", values: [")
|
||||
(let first true)
|
||||
(for e s (do
|
||||
(if (not first) (do
|
||||
@ -95,7 +124,7 @@
|
||||
|
||||
(call emit_expr e)
|
||||
))
|
||||
(call emit "]")
|
||||
(call emit "] }")
|
||||
)))))
|
||||
))
|
||||
|
||||
@ -106,6 +135,65 @@
|
||||
(return (generate))
|
||||
))
|
||||
|
||||
(fn Syms () (do
|
||||
(let syms (null (
|
||||
("println" ("builtin" "println"))
|
||||
)))
|
||||
|
||||
(fn enter_scope () (do
|
||||
(= syms (syms ()))
|
||||
))
|
||||
|
||||
(fn leave_scope () (do
|
||||
(let (parent _) syms)
|
||||
(= syms parent)
|
||||
))
|
||||
|
||||
(fn define (ident sym) (do
|
||||
(let (_ syms) syms)
|
||||
(let i 0)
|
||||
(loop (do
|
||||
(if (>= i (call len syms)) (break))
|
||||
(let (s_ident s_sym) (call at syms i))
|
||||
(if (== ident s_ident) (do
|
||||
(call set s_syms i sym)
|
||||
(return)
|
||||
))
|
||||
(+= i 1)
|
||||
))
|
||||
(call push syms (ident sym))
|
||||
))
|
||||
|
||||
(fn find_sym (syms ident) (do
|
||||
(let (parent map) syms)
|
||||
(let i 0)
|
||||
(loop (do
|
||||
(if (>= i (call len map)) (break))
|
||||
(let (s_ident s_sym) (call at map i))
|
||||
(if (== ident s_ident) (do
|
||||
(return s_sym)
|
||||
))
|
||||
(+= i 1)
|
||||
))
|
||||
(if (!= parent null) (do
|
||||
(return (call find_syms parent ident))
|
||||
))
|
||||
(return null)
|
||||
))
|
||||
|
||||
(fn get (ident) (do
|
||||
(return (call find_sym syms ident))
|
||||
))
|
||||
|
||||
(return (
|
||||
enter_scope
|
||||
leave_scope
|
||||
define
|
||||
get
|
||||
))
|
||||
))
|
||||
|
||||
|
||||
(fn string_escape (str) (do
|
||||
(let str_len (call len str))
|
||||
(let i 0)
|
||||
@ -357,8 +445,10 @@
|
||||
))
|
||||
|
||||
|
||||
(let input_filename "program.phi")
|
||||
|
||||
(call println "reading file...")
|
||||
(let text (call read_text_file "program.phi"))
|
||||
(let text (call read_text_file input_filename))
|
||||
|
||||
//(call println "=== text ===")
|
||||
//(call println text)
|
||||
@ -382,7 +472,7 @@
|
||||
//))
|
||||
|
||||
(call println "emitting...")
|
||||
(let emitter (call Emitter ast))
|
||||
(let emitter (call Emitter ast input_filename))
|
||||
(let (emit) emitter)
|
||||
(let js_code (call emit))
|
||||
|
||||
|
236
phi.js
236
phi.js
@ -1,7 +1,8 @@
|
||||
"use strict";
|
||||
|
||||
import * as fs from "node:fs";
|
||||
import fs from "node:fs";
|
||||
import process from "node:process";
|
||||
import { Runtime } from "./runtime.js";
|
||||
|
||||
function main() {
|
||||
const filename = process.argv[2];
|
||||
@ -20,7 +21,7 @@ function main() {
|
||||
lastValue = result.value;
|
||||
}
|
||||
if (lastValue !== null) {
|
||||
console.log(valueToJs(lastValue));
|
||||
console.log(Runtime.valueToJs(lastValue));
|
||||
}
|
||||
}
|
||||
|
||||
@ -28,8 +29,8 @@ class Evaluator {
|
||||
constructor(filename) {
|
||||
this.syms = { parent: undefined, map: new Map(this.builtins) };
|
||||
this.currentLine = 0;
|
||||
this.callStack = [{ name: "<file>", line: 0 }];
|
||||
this.filename = filename;
|
||||
this.runtime = new Runtime(filename);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -37,9 +38,13 @@ class Evaluator {
|
||||
*/
|
||||
eval(expr) {
|
||||
if (!expr) {
|
||||
console.log(this.callStack)
|
||||
throw new Error()
|
||||
this.runtime.printPanic(
|
||||
"expression could not be evaluated",
|
||||
this.currentLine,
|
||||
);
|
||||
throw new Error("expression could not be evaluated");
|
||||
}
|
||||
|
||||
this.currentLine = expr.line;
|
||||
if (expr.type === "list") {
|
||||
return this.evalList(expr, expr.line);
|
||||
@ -129,8 +134,13 @@ class Evaluator {
|
||||
} else if (id in this.artithmeticOps) {
|
||||
const left = this.evalToValue(s[1]);
|
||||
const right = this.evalToValue(s[2]);
|
||||
if (id === "+" && left.type === "string" && right.type === "string") {
|
||||
return { type: "value", value: { type: "string", value: left.value + right.value } };
|
||||
if (
|
||||
id === "+" && left.type === "string" && right.type === "string"
|
||||
) {
|
||||
return {
|
||||
type: "value",
|
||||
value: { type: "string", value: left.value + right.value },
|
||||
};
|
||||
}
|
||||
return {
|
||||
type: "value",
|
||||
@ -139,6 +149,26 @@ class Evaluator {
|
||||
value: this.artithmeticOps[id](left.value, right.value),
|
||||
},
|
||||
};
|
||||
} else if (id === "==") {
|
||||
const left = this.evalToValue(s[1]);
|
||||
const right = this.evalToValue(s[2]);
|
||||
return {
|
||||
type: "value",
|
||||
value: {
|
||||
type: "bool",
|
||||
value: this.runtime.eq(left, right),
|
||||
},
|
||||
};
|
||||
} else if (id === "!=") {
|
||||
const left = this.evalToValue(s[1]);
|
||||
const right = this.evalToValue(s[2]);
|
||||
return {
|
||||
type: "value",
|
||||
value: {
|
||||
type: "bool",
|
||||
value: !this.runtime.eq(left, right),
|
||||
},
|
||||
};
|
||||
} else if (id in this.comparisonOps) {
|
||||
const left = this.evalToValue(s[1]);
|
||||
const right = this.evalToValue(s[2]);
|
||||
@ -235,7 +265,7 @@ class Evaluator {
|
||||
}
|
||||
}
|
||||
this.syms = outerSyms;
|
||||
return { type: "value", value: { type: "null" } }
|
||||
return { type: "value", value: { type: "null" } };
|
||||
}
|
||||
|
||||
evalDo(expr) {
|
||||
@ -268,21 +298,15 @@ class Evaluator {
|
||||
throw new Error("cannot call non-function");
|
||||
}
|
||||
|
||||
if (this.callStack.length > 100) {
|
||||
this.panic("stack overflow")
|
||||
this.printCallStack()
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
this.callStack.push({ name: fnValue.name, line: expr.line });
|
||||
this.runtime.pushCall(fnValue.name, expr.line);
|
||||
const callerSyms = this.syms;
|
||||
this.syms = {
|
||||
parent: fnValue.syms,
|
||||
map: new Map(),
|
||||
};
|
||||
if (fnValue.params.length !== args.length) {
|
||||
throw new Error(
|
||||
`incorrect amount of arguments for function '${fnValue.name}' on line ${expr.line}`,
|
||||
this.panic(
|
||||
`incorrect amount of arguments for function '${fnValue.name}'`,
|
||||
);
|
||||
}
|
||||
for (let i = 0; i < fnValue.params.length; ++i) {
|
||||
@ -299,7 +323,7 @@ class Evaluator {
|
||||
}
|
||||
|
||||
this.syms = callerSyms;
|
||||
this.callStack.pop()
|
||||
this.runtime.popCall();
|
||||
return { type: "value", value: returnValue };
|
||||
}
|
||||
|
||||
@ -342,10 +366,10 @@ class Evaluator {
|
||||
});
|
||||
} else if (pattern.type === "list") {
|
||||
if (value.type !== "list") {
|
||||
throw new Error(`expected list on line ${pattern.line}`);
|
||||
this.panic(`expected list`);
|
||||
}
|
||||
for (const [i, p] of pattern.values.entries()) {
|
||||
this.assignPattern(p, value.values[i] ?? { type: "null" })
|
||||
this.assignPattern(p, value.values[i] ?? { type: "null" });
|
||||
}
|
||||
} else {
|
||||
throw new Error(`cannot assign to pattern on line ${pattern.line}`);
|
||||
@ -363,89 +387,8 @@ class Evaluator {
|
||||
}
|
||||
|
||||
panic(msg) {
|
||||
console.error(`\x1b[1;91mpanic\x1b[1;97m: ${msg}\x1b[0m"`);
|
||||
this.printCallStack();
|
||||
}
|
||||
|
||||
printCallStack() {
|
||||
const last = this.callStack[this.callStack.length - 1];
|
||||
console.error(` \x1b[90mat \x1b[37m${last.name} \x1b[90m(${this.filename}:${this.currentLine})\x1b[0m`);
|
||||
for (let i = this.callStack.length - 2; i >= 0; --i) {
|
||||
const name = this.callStack[i].name;
|
||||
const line = this.callStack[i + 1].line;
|
||||
console.error(` \x1b[90mat \x1b[37m${name} \x1b[90m(${this.filename}:${line})\x1b[0m`);
|
||||
}
|
||||
}
|
||||
|
||||
builtinFormat(msg, ...args) {
|
||||
let value = valueToPrint(msg);
|
||||
for (const arg of args) {
|
||||
value = value.replace("%", valueToPrint(arg));
|
||||
}
|
||||
return { type: "string", value };
|
||||
}
|
||||
builtinPrintln(msg, ...args) {
|
||||
let text = valueToPrint(msg);
|
||||
|
||||
for (const arg of args) {
|
||||
text = text.replace("%", valueToPrint(arg));
|
||||
}
|
||||
|
||||
console.log(text);
|
||||
return { type: "null" };
|
||||
}
|
||||
builtinPanic(msg, ...args) {
|
||||
let text = valueToPrint(msg);
|
||||
|
||||
for (const arg of args) {
|
||||
text = text.replace("%", valueToPrint(arg));
|
||||
}
|
||||
|
||||
this.panic(text);
|
||||
process.exit(1);
|
||||
return { type: "null" };
|
||||
}
|
||||
builtinReadTextFile(path) {
|
||||
const text = fs.readFileSync(path.value).toString();
|
||||
return { type: "string", value: text };
|
||||
}
|
||||
builtinWriteTextFile(path, text) {
|
||||
fs.writeFileSync(path.value, text.value);
|
||||
return { type: "null" };
|
||||
}
|
||||
builtinPush(list, value) {
|
||||
if (list.type === "string") {
|
||||
list.value += value.value;
|
||||
return list;
|
||||
}
|
||||
list.values.push(value);
|
||||
return list;
|
||||
}
|
||||
builtinAt(value, index) {
|
||||
if (value.type === "string") {
|
||||
return { type: "string", value: value.value[index.value] };
|
||||
}
|
||||
return value.values[index.value];
|
||||
}
|
||||
builtinLen(value) {
|
||||
if (value.type === "string") {
|
||||
return { type: "int", value: value.value.length };
|
||||
}
|
||||
return { type: "int", value: value.values.length };
|
||||
}
|
||||
builtinStringToInt(value) {
|
||||
return { type: "int", value: Number(value.value) };
|
||||
}
|
||||
builtinCharCode(value) {
|
||||
return { type: "int", value: value.value.charCodeAt(0) };
|
||||
}
|
||||
builtinStringsJoin(value) {
|
||||
return {
|
||||
type: "string",
|
||||
value: value.values
|
||||
.map(value => value.value)
|
||||
.join("")
|
||||
};
|
||||
this.runtime.setLine(this.currentLine);
|
||||
this.runtime.panic(msg);
|
||||
}
|
||||
|
||||
artithmeticOps = {
|
||||
@ -453,8 +396,6 @@ class Evaluator {
|
||||
"-": (left, right) => right - left,
|
||||
};
|
||||
comparisonOps = {
|
||||
"==": (left, right) => left === right,
|
||||
"!=": (left, right) => left !== right,
|
||||
"<": (left, right) => left < right,
|
||||
">": (left, right) => left > right,
|
||||
"<=": (left, right) => left <= right,
|
||||
@ -463,24 +404,33 @@ class Evaluator {
|
||||
assignOps = {
|
||||
"=": (_, right) => right,
|
||||
"+=": (left, right) => ({
|
||||
type: left.type === "string" && left.type === right.type ? "string" : "int",
|
||||
value: left.value + right.value
|
||||
type: left.type === "string" && left.type === right.type
|
||||
? "string"
|
||||
: "int",
|
||||
value: left.value + right.value,
|
||||
}),
|
||||
"-=": (left, right) => ({
|
||||
type: "int",
|
||||
value: left.value - right.value,
|
||||
}),
|
||||
"-=": (left, right) => ({ type: "int", value: left.value - right.value }),
|
||||
};
|
||||
|
||||
builtinFns = {
|
||||
"format": (...args) => this.builtinFormat(...args),
|
||||
"println": (...args) => this.builtinPrintln(...args),
|
||||
"panic": (...args) => this.builtinPanic(...args),
|
||||
"read_text_file": (...args) => this.builtinReadTextFile(...args),
|
||||
"write_text_file": (...args) => this.builtinWriteTextFile(...args),
|
||||
"push": (...args) => this.builtinPush(...args),
|
||||
"at": (...args) => this.builtinAt(...args),
|
||||
"len": (...args) => this.builtinLen(...args),
|
||||
"string_to_int": (...args) => this.builtinStringToInt(...args),
|
||||
"char_code": (...args) => this.builtinCharCode(...args),
|
||||
"strings_join": (...args) => this.builtinStringsJoin(...args),
|
||||
"format": (...args) => this.runtime.builtinFormat(...args),
|
||||
"print": (...args) => this.runtime.builtinPrint(...args),
|
||||
"println": (...args) => this.runtime.builtinPrintln(...args),
|
||||
"panic": (...args) =>
|
||||
this.runtime.builtinPanic(this.currentLine, ...args),
|
||||
"read_text_file": (...args) =>
|
||||
this.runtime.builtinReadTextFile(...args),
|
||||
"write_text_file": (...args) =>
|
||||
this.runtime.builtinWriteTextFile(...args),
|
||||
"push": (...args) => this.runtime.builtinPush(...args),
|
||||
"at": (...args) => this.runtime.builtinAt(...args),
|
||||
"len": (...args) => this.runtime.builtinLen(...args),
|
||||
"string_to_int": (...args) => this.runtime.builtinStringToInt(...args),
|
||||
"char_code": (...args) => this.runtime.builtinCharCode(...args),
|
||||
"strings_join": (...args) => this.runtime.builtinStringsJoin(...args),
|
||||
};
|
||||
|
||||
consts = {
|
||||
@ -496,54 +446,6 @@ class Evaluator {
|
||||
];
|
||||
}
|
||||
|
||||
function valueToPrint(value) {
|
||||
if (value.type === "null") {
|
||||
return "null";
|
||||
} else if (value.type === "bool") {
|
||||
return `${value.value}`;
|
||||
} else if (value.type === "int") {
|
||||
return `${value.value}`;
|
||||
} else if (value.type === "string") {
|
||||
return `${value.value}`;
|
||||
} else if (value.type === "list") {
|
||||
return `(${value.values.map((v) => valueToString(v)).join(" ")})`;
|
||||
} else {
|
||||
throw new Error(`unknown value type ${value.type}`);
|
||||
}
|
||||
}
|
||||
|
||||
function valueToString(value) {
|
||||
if (value.type === "null") {
|
||||
return "null";
|
||||
} else if (value.type === "bool") {
|
||||
return `${value.value}`;
|
||||
} else if (value.type === "int") {
|
||||
return `${value.value}`;
|
||||
} else if (value.type === "string") {
|
||||
return `"${value.value}"`;
|
||||
} else if (value.type === "list") {
|
||||
return `(${value.values.map((v) => valueToString(v)).join(" ")})`;
|
||||
} else {
|
||||
throw new Error(`unknown value type ${value.type}`);
|
||||
}
|
||||
}
|
||||
|
||||
function valueToJs(value) {
|
||||
if (value.type === "null") {
|
||||
return null;
|
||||
} else if (value.type === "bool") {
|
||||
return value.value;
|
||||
} else if (value.type === "int") {
|
||||
return value.value;
|
||||
} else if (value.type === "string") {
|
||||
return value.value;
|
||||
} else if (value.type === "list") {
|
||||
return value.values.map((v) => valueToJs(v));
|
||||
} else {
|
||||
throw new Error(`unknown value type ${value.type}`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Expr} expr
|
||||
* @returns {string}
|
||||
|
210
runtime.js
Normal file
210
runtime.js
Normal file
@ -0,0 +1,210 @@
|
||||
import fs from "node:fs";
|
||||
import process from "node:process";
|
||||
|
||||
export class Runtime {
|
||||
constructor(filename) {
|
||||
this.syms = { parent: undefined, map: new Map(this.builtins) };
|
||||
this.callStack = [{ name: "<entry>", line: 0 }];
|
||||
this.filename = filename;
|
||||
this.currentLine = 0;
|
||||
}
|
||||
|
||||
setLine(line) {
|
||||
this.currentLine = line;
|
||||
}
|
||||
|
||||
pushCall(name, line) {
|
||||
this.callStack.push({ name, line });
|
||||
}
|
||||
|
||||
popCall() {
|
||||
this.callStack.pop();
|
||||
}
|
||||
|
||||
panic(msg) {
|
||||
this.printPanic(msg);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
printPanic(msg) {
|
||||
console.error(`\x1b[1;91mpanic\x1b[1;97m: ${msg}\x1b[0m`);
|
||||
this.printCallStack();
|
||||
}
|
||||
|
||||
printCallStack() {
|
||||
const last = this.callStack[this.callStack.length - 1];
|
||||
console.error(
|
||||
` \x1b[90mat \x1b[37m${last.name} \x1b[90m(${this.filename}:${this.currentLine})\x1b[0m`,
|
||||
);
|
||||
for (let i = this.callStack.length - 2; i >= 0; --i) {
|
||||
const name = this.callStack[i].name;
|
||||
const line = this.callStack[i + 1].line;
|
||||
console.error(
|
||||
` \x1b[90mat \x1b[37m${name} \x1b[90m(${this.filename}:${line})\x1b[0m`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
format(msg, ...args) {
|
||||
let value = Runtime.valueToPrint(msg);
|
||||
for (const arg of args) {
|
||||
value = value.replace("%", Runtime.valueToPrint(arg));
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
eq(left, right) {
|
||||
if (left.type === "null") {
|
||||
return right.type === "null";
|
||||
}
|
||||
if (right.type === "null") {
|
||||
return left.type === "null";
|
||||
}
|
||||
|
||||
if (left.type !== right.type) {
|
||||
this.panic(`cannot compare ${left.type} with ${right.type}`);
|
||||
}
|
||||
const type = left.type;
|
||||
if (["bool", "int", "string"].includes(type)) {
|
||||
return left.value === right.value;
|
||||
} else if (type === "list") {
|
||||
if (left.values.length !== right.values.length) {
|
||||
return false;
|
||||
}
|
||||
for (let i = 0; i < left.values.length; ++i) {
|
||||
if (!this.eq(left.values[i], right.values[i])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
this.panic(`equality not implemented for ${type}`);
|
||||
}
|
||||
}
|
||||
|
||||
builtinFormat(msg, ...args) {
|
||||
return { type: "string", value: this.format(msg, ...args) };
|
||||
}
|
||||
|
||||
builtinPrint(msg, ...args) {
|
||||
process.stdout.write(this.format(msg, ...args));
|
||||
return { type: "null" };
|
||||
}
|
||||
|
||||
builtinPrintln(msg, ...args) {
|
||||
console.log(this.format(msg, ...args));
|
||||
return { type: "null" };
|
||||
}
|
||||
|
||||
builtinPanic(currentLine, msg, ...args) {
|
||||
this.panic(this.format(msg, ...args), currentLine);
|
||||
return { type: "null" };
|
||||
}
|
||||
|
||||
builtinReadTextFile(path) {
|
||||
const text = fs.readFileSync(path.value).toString();
|
||||
return { type: "string", value: text };
|
||||
}
|
||||
|
||||
builtinWriteTextFile(path, text) {
|
||||
fs.writeFileSync(path.value, text.value);
|
||||
return { type: "null" };
|
||||
}
|
||||
|
||||
builtinPush(list, value) {
|
||||
if (list.type === "string") {
|
||||
list.value += value.value;
|
||||
return list;
|
||||
}
|
||||
list.values.push(value);
|
||||
return list;
|
||||
}
|
||||
|
||||
builtinAt(value, index) {
|
||||
if (value.type === "string") {
|
||||
return { type: "string", value: value.value[index.value] };
|
||||
}
|
||||
return value.values[index.value] ?? { type: "null" };
|
||||
}
|
||||
|
||||
builtinSet(subject, index, value) {
|
||||
subject.values[index.value] = value;
|
||||
return { type: "null" };
|
||||
}
|
||||
|
||||
builtinLen(value) {
|
||||
if (value.type === "string") {
|
||||
return { type: "int", value: value.value.length };
|
||||
}
|
||||
return { type: "int", value: value.values.length };
|
||||
}
|
||||
|
||||
builtinStringToInt(value) {
|
||||
return { type: "int", value: Number(value.value) };
|
||||
}
|
||||
|
||||
builtinCharCode(value) {
|
||||
return { type: "int", value: value.value.charCodeAt(0) };
|
||||
}
|
||||
|
||||
builtinStringsJoin(value) {
|
||||
return {
|
||||
type: "string",
|
||||
value: value.values
|
||||
.map((value) => value.value)
|
||||
.join(""),
|
||||
};
|
||||
}
|
||||
|
||||
static valueToPrint(value) {
|
||||
if (value.type === "null") {
|
||||
return "null";
|
||||
} else if (value.type === "bool") {
|
||||
return `${value.value}`;
|
||||
} else if (value.type === "int") {
|
||||
return `${value.value}`;
|
||||
} else if (value.type === "string") {
|
||||
return `${value.value}`;
|
||||
} else if (value.type === "list") {
|
||||
return `(${
|
||||
value.values.map((v) => Runtime.valueToString(v)).join(" ")
|
||||
})`;
|
||||
} else {
|
||||
throw new Error(`unknown value type ${value.type}`);
|
||||
}
|
||||
}
|
||||
|
||||
static valueToString(value) {
|
||||
if (value.type === "null") {
|
||||
return "null";
|
||||
} else if (value.type === "bool") {
|
||||
return `${value.value}`;
|
||||
} else if (value.type === "int") {
|
||||
return `${value.value}`;
|
||||
} else if (value.type === "string") {
|
||||
return `"${value.value}"`;
|
||||
} else if (value.type === "list") {
|
||||
return `(${
|
||||
value.values.map((v) => Runtime.valueToString(v)).join(" ")
|
||||
})`;
|
||||
} else {
|
||||
throw new Error(`unknown value type ${value.type}`);
|
||||
}
|
||||
}
|
||||
|
||||
static valueToJs(value) {
|
||||
if (value.type === "null") {
|
||||
return null;
|
||||
} else if (value.type === "bool") {
|
||||
return value.value;
|
||||
} else if (value.type === "int") {
|
||||
return value.value;
|
||||
} else if (value.type === "string") {
|
||||
return value.value;
|
||||
} else if (value.type === "list") {
|
||||
return value.values.map((v) => Runtime.valueToJs(v));
|
||||
} else {
|
||||
throw new Error(`unknown value type ${value.type}`);
|
||||
}
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user