Skip to content

Commit

Permalink
dev dev dev
Browse files Browse the repository at this point in the history
  • Loading branch information
adrianschubek committed May 31, 2024
1 parent cdffbca commit fa6ba3a
Show file tree
Hide file tree
Showing 11 changed files with 641 additions and 178 deletions.
160 changes: 83 additions & 77 deletions src/ast.ts
Original file line number Diff line number Diff line change
@@ -1,170 +1,176 @@
import { ASTNodeType } from "./common";
import { ASTNodeType, Indexable } from "./common";
import { Visitor } from "./visitor";

export abstract class ASTNode {
export abstract class ASTNode implements Indexable {
type: ASTNodeType;
constructor(type: ASTNodeType) {
row: number;
col: number;
constructor(type: ASTNodeType, row: number, col: number) {
this.type = type;
this.row = row;
this.col = col;
}
visit(): void {
// check for interrupt
}
abstract accept<T>(visitor: Visitor<T>): T; // check for interrupt
}
export class Program extends ASTNode {
accept<T>(visitor: Visitor<T>): T {
return visitor.visitProgram(this);
}
body: ASTNode[];
constructor(body: ASTNode[]) {
super(ASTNodeType.PROG);
constructor(body: ASTNode[], row: number, col: number) {
super(ASTNodeType.PROG, row, col);
this.body = body;
}
visit(): void {
super.visit();
throw new Error("Method not implemented.");
}
}
export class Raw extends ASTNode {
accept<T>(visitor: Visitor<T>): T {
return visitor.visitRaw(this);
}
value: string;
constructor(value: string) {
super(ASTNodeType.RAW);
constructor(value: string, row: number, col: number) {
super(ASTNodeType.RAW, row, col);
this.value = value;
}
visit(): void {
super.visit();
throw new Error("Method not implemented.");
}
}
/**
* \foobar[...]{...}...
* Not a built-in function
*/
export class FunctionCall extends ASTNode {
accept<T>(visitor: Visitor<T>): T {
return visitor.visitFunctionCall(this);
}
name: string;
params: Params;
args: ASTNode[];
constructor(name: string, params?: Params, args?: ASTNode[]) {
super(ASTNodeType.F_CALL);
constructor(name: string, params: Params | null, args: ASTNode[], row: number, col: number) {
super(ASTNodeType.F_CALL, row, col);
this.name = name;
this.params = params ?? new Params({});
this.params = params ?? new Params({}, row, col);
this.args = args ?? [];
}

visit(): void {
super.visit();
throw new Error("Method not implemented.");
}
}
/**
* \fn[x,y]{add}{\${x + y}}
* x und y sind automatisch verfügbar in JS eval
*/
export class FunctionDefinition extends ASTNode {
accept<T>(visitor: Visitor<T>): T {
return visitor.visitFunctionDefinition(this);
}
name: ASTNode;
// arguments [x,y] only key is relevant maybe later use =string =int for type checking
fnArgs: Params;
body: ASTNode;
constructor(name: ASTNode, fnArgs: Params, body: ASTNode) {
super(ASTNodeType.F_DEF);
constructor(name: ASTNode, fnArgs: Params, body: ASTNode, row: number, col: number) {
super(ASTNodeType.F_DEF, row, col);
this.name = name;
this.fnArgs = fnArgs;
this.body = body;
}
visit(): void {
super.visit();
throw new Error("Method not implemented.");
}
}
/**
* [a=b,c=d,...] and [arg1,arg2,..]
*/
export class Params extends ASTNode {
accept<T>(visitor: Visitor<T>): T {
return visitor.visitParams(this);
}
kv: { [key: string]: ASTNode | null };
constructor(kv: { [key: string]: ASTNode | null }) {
super(ASTNodeType.PARAMS);
constructor(kv: { [key: string]: ASTNode | null }, row: number, col: number) {
super(ASTNodeType.PARAMS, row, col);
this.kv = kv;
}
visit(): void {
super.visit();
throw new Error("Method not implemented.");
}
}
export class IfStatement extends ASTNode {
accept<T>(visitor: Visitor<T>): T {
return visitor.visitIfStatement(this);
}
condition: ASTNode;
trueBranch: ASTNode;
falseBranch: ASTNode;
constructor(condition: ASTNode, trueBranch: ASTNode, falseBranch: ASTNode) {
super(ASTNodeType._IF);
constructor(condition: ASTNode, trueBranch: ASTNode, falseBranch: ASTNode, row: number, col: number) {
super(ASTNodeType._IF, row, col);
this.condition = condition;
this.trueBranch = trueBranch;
this.falseBranch = falseBranch;
}
visit(): void {
super.visit();
throw new Error("Method not implemented.");
}
}
export class LoopStatement extends ASTNode {
accept<T>(visitor: Visitor<T>): T {
return visitor.visitLoopStatement(this);
}
init: ASTNode;
condition: ASTNode;
increment: ASTNode;
body: ASTNode;
constructor(init: ASTNode, condition: ASTNode, increment: ASTNode, body: ASTNode) {
super(ASTNodeType._LOOP);
constructor(init: ASTNode, condition: ASTNode, increment: ASTNode, body: ASTNode, row: number, col: number) {
super(ASTNodeType._LOOP, row, col);
this.init = init;
this.condition = condition;
this.increment = increment;
this.body = body;
}
}
export class URLStatement extends ASTNode {
accept<T>(visitor: Visitor<T>): T {
return visitor.visitURLStatement(this);
}
url: ASTNode;
constructor(url: ASTNode) {
super(ASTNodeType._URL);
constructor(url: ASTNode, row: number, col: number) {
super(ASTNodeType._URL, row, col);
this.url = url;
}
visit(): void {
super.visit();
throw new Error("Method not implemented.");
}
}
export class FileStatement extends ASTNode {
accept<T>(visitor: Visitor<T>): T {
return visitor.visitFileStatement(this);
}
path: ASTNode;
constructor(path: ASTNode) {
super(ASTNodeType._FILE);
constructor(path: ASTNode, row: number, col: number) {
super(ASTNodeType._FILE, row, col);
this.path = path;
}
visit(): void {
super.visit();
throw new Error("Method not implemented.");
}
}
export class UseStatement extends ASTNode {
accept<T>(visitor: Visitor<T>): T {
return visitor.visitUseStatement(this);
}
code: ASTNode;
constructor(code: ASTNode) {
super(ASTNodeType._USE);
constructor(code: ASTNode, row: number, col: number) {
super(ASTNodeType._USE, row, col);
this.code = code;
}
visit(): void {
super.visit();
throw new Error("Method not implemented.");
}
}
export class EvalExpr extends ASTNode {
export class EvalStatement extends ASTNode {
accept<T>(visitor: Visitor<T>): T {
return visitor.visitEvalStatement(this);
}
expr: string;
constructor(expr: string) {
super(ASTNodeType.EVAL);
constructor(expr: string, row: number, col: number) {
super(ASTNodeType.EVAL, row, col);
this.expr = expr;
}
visit(): void {
super.visit();
throw new Error("Method not implemented.");
}
}
export class Comment extends ASTNode {
accept<T>(visitor: Visitor<T>): T {
return visitor.visitComment(this);
}
value: string;
constructor(value: string) {
super(ASTNodeType.COMMENT);
constructor(value: string, row: number, col: number) {
super(ASTNodeType.COMMENT, row, col);
this.value = value;
}
visit(): void {
super.visit();
throw new Error("Method not implemented.");
}
}

// export function converter<I extends ASTNode, O extends ASTNode>(inputAST: I, inputASTNodeType: ASTNodeType, outputFactory: (input: I) => O): O {
// if (inputAST.type === inputASTNodeType) return inputAST as unknown as O;
// return new outputClass(inputAST.row, inputAST.col);
// }
export function convertRawToEval(input: Raw | EvalStatement): EvalStatement {
if (input.type === ASTNodeType.EVAL) return input as EvalStatement;
return new EvalStatement((input as Raw).value, input.row, input.col);
}
export function convertEvalToRaw(input: EvalStatement): Raw {
return new Raw(input.expr, input.row, input.col);
}
58 changes: 51 additions & 7 deletions src/common.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import chalk from "chalk";
import { ASTNode, Params } from "./ast";

export enum TokenType {
T_RAW = "T_RAW",
Expand Down Expand Up @@ -31,31 +32,64 @@ export enum ASTNodeType {
_USE = "_USE" /* imports. must be top level. parse file then interpret */,
}

// built in cannot be overriden
export enum BuiltInFunction {
LOOP = "loop",
IF = "if",
EVAL = "$",
ENV = "env",
F = "f" /* fucntion definition */,
F = "f" /* function definition */,
RPL = "rpl" /* replace function */,
RN = "rn" /* rename function */,
DEL = "del" /* remove function */,
TOPARENT = "toparent" /* copy toparent function */,
TOCHILD = "tochild" /* copy tochild function */,
COMMENT = "#",
URL = "url",
FILE = "file",
USE = "use",
VAR = "var",
TRUE = "$true",
FALSE = "$false",
HALT = "halt" /* stop exec immediate */,
ASSERT = "assert" /* assert function */,
}

export interface Token {
export interface Token extends Indexable {
type: TokenType;
value: string;
row: number;
col: number;
}

export interface Indexable {
row: number;
col: number;
}

export interface Visitor {
visit(node: ASTNode): void;
}

/**
* Check RAW for truthy values
*
*/
export function truthy(value: string): boolean {
const val = value.toLowerCase().trim();
return /* val !== "false" && val !== "0" && */ val !== "$false";
}

export function warn(msg: string, row?: number, col?: number): void {
// treat warning as errors config?
console.log("⚠️ " + chalk.yellow(` ${msg} ${row !== undefined && col !== undefined ? `on line ${row}:${col}.` : ""}\n`));
}

export function err(msg: string, row?: number, col?: number): never {
if (row === undefined || col === undefined) throw new Error(msg);
throw new Error(chalk.red(`${msg} on line ${row}:${col}.`));
throw new Error("🔥 " + chalk.red(`${msg} ${row !== undefined && col !== undefined ? `on line ${row}:${col}.` : ""}\n`));
}

export function assertCount<T>(text: string, details: string, thisToken: Token, count: number, args?: T[]) {
export function assertCount<T>(text: string, details: string, thisToken: Indexable, count: number, args?: T[]) {
let actual = args?.length ?? 0;
if (actual !== count) {
err(
Expand All @@ -65,7 +99,7 @@ export function assertCount<T>(text: string, details: string, thisToken: Token,
);
}
}
export function assertRange<T>(text: string, details: string, thisToken: Token, min: number, max: number, args?: T[]) {
export function assertRange<T>(text: string, details: string, thisToken: Indexable, min: number, max: number, args?: T[]) {
let actual = args?.length ?? 0;
if (actual < min || actual > max) {
err(
Expand All @@ -76,7 +110,17 @@ export function assertRange<T>(text: string, details: string, thisToken: Token,
}
}

export function assertType<T>(details: string, thisToken: Token, expected: T, actual: T) {
export function assertFnArgCount<T>(thisToken: Indexable, fnName: string, count: number, args?: T[]) {
assertCount("arguments", `in function \\${fnName}`, thisToken, count, args);
}
export function assertFnArgRange<T>(thisToken: Indexable, fnName: string, min: number, max: number, args?: T[]) {
assertRange("arguments", `in function \\${fnName}`, thisToken, min, max, args);
}
export function assertParamCount(thisToken: Indexable, fnName: string, count: number, params: Params | null) {
assertCount("parameters", `in function \\${fnName}`, thisToken, count, params?.kv ? Object.values(params.kv) : undefined);
}

export function assertType<T>(details: string, thisToken: Indexable, expected: T, actual: T) {
if (actual !== expected) {
err(`Expected ${chalk.redBright(expected)} but got ${chalk.redBright(actual)} ${details}`, thisToken.row, thisToken.col);
}
Expand Down
Loading

0 comments on commit fa6ba3a

Please sign in to comment.