Add primitive-functions, ability to work with Program
This commit is contained in:
parent
24c09c8fbe
commit
8b02e3e7d1
4 changed files with 158 additions and 49 deletions
|
|
@ -19,7 +19,7 @@ const C = {
|
|||
Bold: "\x1b[1m",
|
||||
};
|
||||
|
||||
const program = Program.makeEmpty();
|
||||
const program = Program.make();
|
||||
|
||||
function runSource(inputRaw: string, isRepl: boolean): boolean {
|
||||
const input = inputRaw.trim();
|
||||
|
|
|
|||
57
src/lang/primitive.ts
Normal file
57
src/lang/primitive.ts
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
import { Value } from "./eval/value";
|
||||
import { Implementation, Program } from "./program";
|
||||
import { ThrownRuntimeError } from "./eval/error";
|
||||
import { FunctionName } from "./expr";
|
||||
import { valueToString } from "./debug/value_show";
|
||||
|
||||
// TODO: Primitive functions like +, -, *, div, <, <=, ==, mod
|
||||
|
||||
export function installPrimitives(program: Program) {
|
||||
function R(name: FunctionName, implementation: Implementation) {
|
||||
Program.registerPrimitive(program, name, implementation);
|
||||
}
|
||||
|
||||
// addition
|
||||
R("+", (args: Value[]): Value => {
|
||||
let sum = 0;
|
||||
for (const arg of args) {
|
||||
if (arg.tag !== "number") {
|
||||
throw ThrownRuntimeError.error({ tag: "TypeMismatch", expected: "number", received: arg });
|
||||
}
|
||||
sum += arg.value;
|
||||
}
|
||||
return Value.number(sum);
|
||||
});
|
||||
|
||||
// multiplication
|
||||
R("*", (args: Value[]): Value => {
|
||||
let product = 1;
|
||||
for (const arg of args) {
|
||||
if (arg.tag !== "number") {
|
||||
throw ThrownRuntimeError.error({ tag: "TypeMismatch", expected: "number", received: arg });
|
||||
}
|
||||
product = product*arg.value;
|
||||
}
|
||||
return Value.number(product);
|
||||
});
|
||||
|
||||
// string concat
|
||||
R("++", (args: Value[]): Value => {
|
||||
let sum = "";
|
||||
for (const arg of args) {
|
||||
if (arg.tag !== "string") {
|
||||
throw ThrownRuntimeError.error({ tag: "TypeMismatch", expected: "number", received: arg });
|
||||
}
|
||||
sum += arg.value;
|
||||
}
|
||||
return Value.string(sum);
|
||||
});
|
||||
|
||||
R("log", (args: Value[]): Value => {
|
||||
for (const arg of args) {
|
||||
console.log(valueToString(arg));
|
||||
}
|
||||
return Value.tuple([]);
|
||||
});
|
||||
|
||||
}
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
import { ThrownRuntimeError } from "./eval/error";
|
||||
import { Value } from "./eval/value";
|
||||
import { Expr, FunctionName, ProductPattern } from "./expr";
|
||||
import { installPrimitives } from "./primitive";
|
||||
|
||||
export type Timestamp = number;
|
||||
|
||||
|
|
@ -51,39 +52,56 @@ export type UserFunctionDefinition = {
|
|||
|
||||
export type PrimitiveFunctionDefinition = {
|
||||
name: FunctionName,
|
||||
implementation: (args: Value[]) => Value,
|
||||
implementation: Implementation,
|
||||
}
|
||||
|
||||
export type Implementation = (args: Value[]) => Value
|
||||
|
||||
export namespace Program {
|
||||
|
||||
type Error =
|
||||
| { tag: "DuplicateFunctionName", name: FunctionName }
|
||||
| { tag: "FunctionNotFound", name: FunctionName };
|
||||
| { tag: "FunctionNotFound", name: FunctionName }
|
||||
| { tag: "CannotEditPrimitiveFunction", name: FunctionName }
|
||||
| { tag: "CannotDeletePrimitiveFunction", name: FunctionName }
|
||||
| { tag: "PrimitiveFunctionAlreadyExists", name: FunctionName }
|
||||
|
||||
type Result<T> =
|
||||
| { tag: "ok", value: T }
|
||||
| { tag: "error", error: Error };
|
||||
// | { tag: "ParseError", message: string } // TODO
|
||||
| { tag: "error", error: Error }
|
||||
|
||||
export namespace Result {
|
||||
export function ok<T>(value: T): Result<T> { return { tag: "ok", value } }
|
||||
export function error<T>(error: Error): Result<T> { return { tag: "error", error } }
|
||||
}
|
||||
|
||||
export function makeEmpty(): Program {
|
||||
return {
|
||||
function primitive(name: FunctionName, implementation: Implementation): FunctionDefinition {
|
||||
return { tag: "primitive", def: { name, implementation } };
|
||||
}
|
||||
|
||||
export function registerPrimitive(program: Program, name: FunctionName, implementation: Implementation): void {
|
||||
const fn = program.function_definitions.get(name);
|
||||
if (fn !== undefined) {
|
||||
throw { tag: "PrimitiveFunctionAlreadyExists", name };
|
||||
}
|
||||
program.function_definitions.set(name, primitive(name, implementation));
|
||||
return undefined;
|
||||
}
|
||||
|
||||
export function make(): Program {
|
||||
const program: Program = {
|
||||
function_definitions: new Map(),
|
||||
function_definition_order: [],
|
||||
};
|
||||
|
||||
installPrimitives(program);
|
||||
return program;
|
||||
}
|
||||
// TODO: Primitive functions like +, -, *, div, <, <=, ==, mod
|
||||
|
||||
// TODO: function to create initial program (with the above primitive functions otherwise empty)
|
||||
|
||||
// may throw `ThrownRuntimeError`
|
||||
// may throw `ThrownRuntimeError`. This is used by evaluator.
|
||||
export function lookup_function(program: Program, name: FunctionName): FunctionDefinition {
|
||||
const fn = program.function_definitions.get(name);
|
||||
if (!fn) {
|
||||
if (fn === undefined) {
|
||||
throw ThrownRuntimeError.error({
|
||||
tag: "FunctionLookupFailure",
|
||||
name,
|
||||
|
|
@ -93,44 +111,86 @@ export namespace Program {
|
|||
}
|
||||
|
||||
export type CreateFunction = {
|
||||
name: FunctionName,
|
||||
parameters: ProductPattern[],
|
||||
body: Expr,
|
||||
raw_parameters: string,
|
||||
raw_body: string,
|
||||
}
|
||||
|
||||
export function registerFunction(
|
||||
program: Program,
|
||||
{ name, body, parameters, raw_parameters, raw_body }: CreateFunction
|
||||
): Result<void> {
|
||||
if (program.function_definitions.has(name)) {
|
||||
return Result.error({ tag: "DuplicateFunctionName", name });
|
||||
}
|
||||
|
||||
const now: Timestamp = Date.now();
|
||||
|
||||
const newFunction: UserFunctionDefinition = {
|
||||
name,
|
||||
raw_parameters,
|
||||
raw_body,
|
||||
parameters,
|
||||
body,
|
||||
created_at: now,
|
||||
last_modified_at: now,
|
||||
};
|
||||
|
||||
program.function_definitions.set(name, { tag: "user", def: newFunction });
|
||||
program.function_definition_order.push(name);
|
||||
|
||||
return Result.ok(undefined);
|
||||
}
|
||||
|
||||
export type UpdateFunction = {
|
||||
parameters: ProductPattern[],
|
||||
body: Expr,
|
||||
raw_name: string,
|
||||
raw_parameters: string,
|
||||
raw_body: string,
|
||||
}
|
||||
|
||||
export type UpdateFunction = {
|
||||
raw_name?: string,
|
||||
raw_parameters?: string,
|
||||
raw_body?: string,
|
||||
export function updateFunction(
|
||||
program: Program,
|
||||
name: FunctionName,
|
||||
{ parameters, body, raw_parameters, raw_body }: UpdateFunction
|
||||
): Result<void> {
|
||||
const existingEntry = program.function_definitions.get(name);
|
||||
if (existingEntry === undefined) {
|
||||
return Result.error({ tag: "FunctionNotFound", name });
|
||||
}
|
||||
if (existingEntry.tag === "primitive") {
|
||||
return Result.error({ tag: "CannotEditPrimitiveFunction", name });
|
||||
}
|
||||
const def = existingEntry.def;
|
||||
def.parameters = parameters;
|
||||
def.body = body;
|
||||
def.raw_parameters = raw_parameters;
|
||||
def.raw_body = raw_body;
|
||||
|
||||
def.last_modified_at = Date.now();
|
||||
|
||||
return Result.ok(undefined);
|
||||
}
|
||||
|
||||
export function add_user_function(program: Program, description: CreateFunction): Result<void> {
|
||||
// TODO:
|
||||
// - parsing/validation
|
||||
// - raw_name (check if function already exists)
|
||||
// - raw_parameters
|
||||
// - raw_body
|
||||
// - compute timestamp for now
|
||||
return (0 as any);
|
||||
}
|
||||
export function deleteFunction(program: Program, name: FunctionName): Result<void> {
|
||||
const existingEntry = program.function_definitions.get(name);
|
||||
if (!existingEntry) {
|
||||
return Result.error({ tag: "FunctionNotFound", name });
|
||||
}
|
||||
if (existingEntry.tag === "primitive") {
|
||||
return Result.error({ tag: "CannotDeletePrimitiveFunction", name });
|
||||
}
|
||||
program.function_definitions.delete(name);
|
||||
|
||||
// TODO: What about result type? Should it on deletion return the original data of the function, and if there's a failure, how detailed should it be?
|
||||
export function delete_user_function(program: Program, name: FunctionName): Result<void> {
|
||||
// TODO:
|
||||
// - see if the user function exists
|
||||
// - if it does, delete it
|
||||
// - if it doesn't ???
|
||||
return (0 as any);
|
||||
}
|
||||
const orderIndex = program.function_definition_order.indexOf(name);
|
||||
if (orderIndex !== -1) {
|
||||
program.function_definition_order.splice(orderIndex, 1);
|
||||
}
|
||||
|
||||
export function update_user_function(program: Program, name: FunctionName): Result<void> {
|
||||
// TODO:
|
||||
return (0 as any);
|
||||
}
|
||||
|
||||
export function get_user_function(program: Program, name: FunctionName): Result<UserFunctionDefinition> {
|
||||
// TODO:
|
||||
return (0 as any);
|
||||
return Result.ok(undefined);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,14 +2,6 @@
|
|||
|
||||
npm run start
|
||||
|
||||
|
||||
npm install -D @electron-forge/cli @electron-forge/maker-deb @electron-forge/maker-rpm @electron-forge/maker-squirrel @electron-forge/maker-zip @electron-forge/plugin-auto-unpack-natives @electron-forge/plugin-fuses @electron-forge/plugin-vite @electron/fuses @types/electron-squirrel-startup @typescript-eslint/eslint-plugin @typescript-eslint/parser electron eslint eslint-plugin-import typescript vite
|
||||
|
||||
npm install electron-squirrel-startup
|
||||
|
||||
npm install -D sass-embedded
|
||||
|
||||
|
||||
# Tests
|
||||
|
||||
npx ts-node src/parser/cursor.test.ts
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue