diff --git a/README.md b/README.md index 5127d47..dbb43e3 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,13 @@ -

- logo -
-

- -
-

Zep Language

-

Compiles to WebAssembly.
Tuned for WebAssembly.

-
+

+
+███████╗███████╗██████╗ 
+╚══███╔╝██╔════╝██╔══██╗
+  ███╔╝ █████╗  ██████╔╝
+ ███╔╝  ██╔══╝  ██╔═══╝ 
+███████╗███████╗██║     
+╚══════╝╚══════╝╚═╝     
+                        
+v0.0.0-wip

## Capabilities - ✅ Tokenizer diff --git a/src/ast/nodes/EnumDeclaration.ts b/src/ast/nodes/EnumDeclaration.ts new file mode 100644 index 0000000..468190f --- /dev/null +++ b/src/ast/nodes/EnumDeclaration.ts @@ -0,0 +1,14 @@ +import { EnumElement } from "./EnumElement.js"; +import { Identifier } from "./Identifier.js"; +import { Statement } from "./Statement.js"; + +export class EnumDeclaration extends Statement { + public nameOf: string = "EnumDeclaration"; + public name: Identifier; + public elements: EnumElement[]; + constructor(name: Identifier, elements: EnumElement[] = []) { + super(); + this.name = name; + this.elements = elements; + } +} diff --git a/src/ast/nodes/EnumElement.ts b/src/ast/nodes/EnumElement.ts new file mode 100644 index 0000000..51b11a9 --- /dev/null +++ b/src/ast/nodes/EnumElement.ts @@ -0,0 +1,14 @@ +import { Identifier } from "./Identifier.js"; +import { NumberLiteral } from "./NumberLiteral.js"; +import { Statement } from "./Statement.js"; + +export class EnumElement extends Statement { + public nameOf: string = "EnumElement"; + public name: Identifier; + public value: NumberLiteral; + constructor(name: Identifier, value: NumberLiteral) { + super(); + this.name = name; + this.value = value; + } +} diff --git a/src/ast/nodes/ReferenceExpression.ts b/src/ast/nodes/ReferenceExpression.ts index 4eb40b5..ca493bd 100644 --- a/src/ast/nodes/ReferenceExpression.ts +++ b/src/ast/nodes/ReferenceExpression.ts @@ -3,7 +3,7 @@ import { Statement } from "./Statement"; export class ReferenceExpression extends Expression { public nameOf: string = "ReferenceExpression"; - constructor(public referencing: Statement) { + constructor(public name: string, public referencing: Statement) { super(); } } diff --git a/src/parser/index.ts b/src/parser/index.ts index c4ff962..5595142 100644 --- a/src/parser/index.ts +++ b/src/parser/index.ts @@ -32,6 +32,8 @@ import { BooleanLiteral } from "../ast/nodes/BooleanLiteral.js"; import { Node } from "../ast/nodes/Node.js"; import { BranchStatement } from "../ast/nodes/BranchStatement.js"; import { BranchToStatement } from "../ast/nodes/BranchToStatement.js"; +import { EnumDeclaration } from "../ast/nodes/EnumDeclaration.js"; +import { EnumElement } from "../ast/nodes/EnumElement.js"; export class Parser { public program: Program = new Program("test.zp"); @@ -56,6 +58,8 @@ export class Parser { state.resume(); if ((node = this.parseVariableDeclaration(scope))) return node; state.resume(); + if ((node = this.parseEnumDeclaration(scope))) return node; + state.resume(); return null; } parseStatement(scope: Scope = this.program.globalScope): Statement | null { @@ -161,6 +165,115 @@ export class Parser { scope.add(name.text, node); return node; } + parseFunctionDeclaration( + scope: Scope = this.program.globalScope, + ): FunctionDeclaration | null { + const state = this.tokenizer.createState(); + + let exported = false; + + const exp = this.parseModifierExpression(); + if (!exp) state.resume(); + else if (exp.tag.data == "export") exported = true; + const fn = this.tokenizer.getToken(); + if (!isIdentifier(fn) || fn.text !== "fn") return null; + + const name = this.tokenizer.getToken(); + if (!isIdentifier(name)) return null; + if (this.tokenizer.getToken().token !== Token.LeftParen) return null; + const params: ParameterExpression[] = []; + const blockScope = new Scope(scope); + while (true) { + const param = this.parseParameterExpression(blockScope); + if (!param) break; + params.push(param); + const tok = this.tokenizer.getToken().token; + if (tok !== Token.Comma) break; + } + if (this.tokenizer.getToken().token !== Token.Sub) return null; + if (this.tokenizer.getToken().token !== Token.GreaterThan) return null; + const returnType = this.tokenizer.getToken(); + if (!isBuiltinType(returnType)) return null; + const block = this.parseBlockExpression(blockScope); + if (!block) return null; + + const node = new FunctionDeclaration( + new Identifier(name.text, name.range), + params, + new TypeExpression([returnType.text], false), + block, + new Scope(scope), + exported + ); + + if (scope.parentScope) { + if (exported) { + new SyntaxError( + this.program, + "Warn", "Exported functions must occur at the global scope! Not exporting function and moving on.", + 0x1, + fn.range, + "WARN" + ); + } else { + new SyntaxError( + this.program, + "Error", "Closures not yet supported!", + 0x2, + fn.range, + "FAIL" + ); + } + } + this.program.globalScope.add(name.text, node); + this.program.topLevelStatements.push(node); + return node; + } + parseEnumDeclaration( + scope: Scope = this.program.globalScope, + ): EnumDeclaration | null { + this.tokenizer.createState(); + if (this.tokenizer.getToken().text != "enum") return null; + const name = this.tokenizer.getToken(); + if (this.tokenizer.getToken().text != "{") return null; + + const elements: EnumElement[] = []; + + let index = 0; + while (true) { + const name = this.tokenizer.getToken(); + if (name.token !== Token.Identifier) return null; + const trailing = this.tokenizer.getToken(); + if (trailing.text === "}" || trailing.text === ",") { + const element = new EnumElement( + new Identifier( + name.text, + name.range + ), + new NumberLiteral( + index.toString() + ) + ); + + index++; + elements.push(element); + if (trailing.text === "}") break; + } + } + + const node = new EnumDeclaration( + new Identifier( + name.text, + name.range + ), + elements + ); + + this.program.globalScope.add(name.text, node); + this.program.topLevelStatements.push(node); + + return node; + } parseIfStatement( scope: Scope = this.program.globalScope, ): IfStatement | null { @@ -235,71 +348,6 @@ export class Parser { ); return node; } - - parseFunctionDeclaration( - scope: Scope = this.program.globalScope, - ): FunctionDeclaration | null { - const state = this.tokenizer.createState(); - - let exported = false; - - const exp = this.parseModifierExpression(); - if (!exp) state.resume(); - else if (exp.tag.data == "export") exported = true; - const fn = this.tokenizer.getToken(); - if (!isIdentifier(fn) || fn.text !== "fn") return null; - - const name = this.tokenizer.getToken(); - if (!isIdentifier(name)) return null; - if (this.tokenizer.getToken().token !== Token.LeftParen) return null; - const params: ParameterExpression[] = []; - const blockScope = new Scope(scope); - while (true) { - const param = this.parseParameterExpression(blockScope); - if (!param) break; - params.push(param); - const tok = this.tokenizer.getToken().token; - if (tok !== Token.Comma) break; - } - if (this.tokenizer.getToken().token !== Token.Sub) return null; - if (this.tokenizer.getToken().token !== Token.GreaterThan) return null; - const returnType = this.tokenizer.getToken(); - if (!isBuiltinType(returnType)) return null; - const block = this.parseBlockExpression(blockScope); - if (!block) return null; - - const node = new FunctionDeclaration( - new Identifier(name.text, name.range), - params, - new TypeExpression([returnType.text], false), - block, - new Scope(scope), - exported - ); - - if (scope.parentScope) { - if (exported) { - new SyntaxError( - this.program, - "Warn", "Exported functions must occur at the global scope! Not exporting function and moving on.", - 0x1, - fn.range, - "WARN" - ); - } else { - new SyntaxError( - this.program, - "Error", "Closures not yet supported!", - 0x2, - fn.range, - "FAIL" - ); - } - } - this.program.globalScope.add(name.text, node); - this.program.topLevelStatements.push(node); - return node; - } parseFunctionImport( scope: Scope = this.program.globalScope, ): FunctionImport | null { @@ -490,7 +538,6 @@ export class Parser { args, ); - this.program.statements.push(node); return node; } parseReferenceExpression( @@ -498,7 +545,7 @@ export class Parser { ): ReferenceExpression | null { const id = this.tokenizer.getToken(); if (!scope.has(id.text)) return null; - return new ReferenceExpression(scope.get(id.text)! as Statement); + return new ReferenceExpression(id.text, scope.get(id.text)! as Statement); } parseReturnStatement( scope: Scope = this.program.globalScope, @@ -551,7 +598,7 @@ export class Parser { } if (left instanceof Identifier) { if (scope.has(left.data)) { - left = new ReferenceExpression(left); + left = new ReferenceExpression(left.data, left); } else { new TokenMismatchError( `Cannot find name ${left.data} in scope`, @@ -562,7 +609,6 @@ export class Parser { } } const node = new BinaryExpression(left, op, right); - this.program.statements.push(node); // Check scope return node; } diff --git a/src/test.ts b/src/test.ts index bea53be..c2cc7bc 100644 --- a/src/test.ts +++ b/src/test.ts @@ -1,20 +1,18 @@ +import { writeFileSync } from "fs"; import { Parser } from "./parser"; import { Tokenizer } from "./tokenizer"; -import { TreeObject, asTree } from "treeify"; -import { Generator } from "./generator/index.js"; -import { readFileSync, writeFileSync } from "fs"; -import { execSync } from "child_process"; -import { FunctionImport } from "./ast/nodes/FunctionImport.js"; -import { VariableDeclaration } from "./ast/nodes/VariableDeclaration.js"; -import { FunctionDeclaration } from "./ast/nodes/Function.js"; import { Transpile } from "./transpiler/transpiler"; const start = Date.now(); const tokenizer = new Tokenizer(` +enum Axis { + X, + Y, + Z +} + #[export]: add fn add(a: i32, b: i32) -> i32 { - #[export]: main - fn main() -> none {} i32? c = a + b rt c } @@ -25,4 +23,8 @@ const parser = new Parser(tokenizer, "test.zp"); const program = parser.parseProgram(); //console.dir(program, { depth: null }); console.log(program); -console.log("Transpiled:\n" + Transpile.from(program)); + +const transpiled = Transpile.from(program); +console.log("Transpiled:\n" + transpiled); + +writeFileSync("./test.ts", transpiled); \ No newline at end of file diff --git a/src/transpiler/transpiler.ts b/src/transpiler/transpiler.ts index e967db9..182ffb4 100644 --- a/src/transpiler/transpiler.ts +++ b/src/transpiler/transpiler.ts @@ -1,6 +1,7 @@ import { Program } from "../ast/Program"; import { BinaryExpression } from "../ast/nodes/BinaryExpression"; import { BooleanLiteral } from "../ast/nodes/BooleanLiteral"; +import { EnumDeclaration } from "../ast/nodes/EnumDeclaration"; import { FunctionDeclaration } from "../ast/nodes/Function"; import { Identifier } from "../ast/nodes/Identifier"; import { Node } from "../ast/nodes/Node"; @@ -22,27 +23,28 @@ export class Transpile { } return out; } - if (node instanceof NumberLiteral) return Transpile.num(node); - if (node instanceof StringLiteral) return Transpile.str(node); - if (node instanceof VariableDeclaration) return Transpile.var(node); - if (node instanceof FunctionDeclaration) return Transpile.fn(node); - if (node instanceof BinaryExpression) return Transpile.binxp(node); - if (node instanceof ReturnStatement) return Transpile.rt(node); - if (node instanceof ReferenceExpression) return Transpile.ref(node); - if (node instanceof ParameterExpression) return Transpile.param(node); - if (node instanceof Identifier) return Transpile.id(node); + if (node instanceof NumberLiteral) return Transpile.NumberLiteral(node); + if (node instanceof StringLiteral) return Transpile.StringLiteral(node); + if (node instanceof VariableDeclaration) return Transpile.VariableDeclaration(node); + if (node instanceof FunctionDeclaration) return Transpile.FunctionDeclaration(node); + if (node instanceof EnumDeclaration) return Transpile.EnumDeclaration(node); + if (node instanceof BinaryExpression) return Transpile.BinaryExpression(node); + if (node instanceof ReturnStatement) return Transpile.ReturnStatement(node); + if (node instanceof ReferenceExpression) return Transpile.ReferenceExpression(node); + if (node instanceof ParameterExpression) return Transpile.ParameterExpression(node); + if (node instanceof Identifier) return Transpile.Identifier(node); return "nop"; } - static num(node: NumberLiteral) { + static NumberLiteral(node: NumberLiteral) { return node.data; } - static str(node: StringLiteral) { + static StringLiteral(node: StringLiteral) { return node.data; } - static var(node: VariableDeclaration) { - return `${depth}${node.mutable ? "let" : "const"} ${node.name.data} = ${Transpile.from(node.value)};`; + static VariableDeclaration(node: VariableDeclaration) { + return `${depth}${node.mutable ? "let" : "const"} ${node.name.data} = ${Transpile.from(node.value)}`; } - static fn(node: FunctionDeclaration) { + static FunctionDeclaration(node: FunctionDeclaration) { let params = ""; let body = ""; for (const param of node.parameters) { @@ -59,19 +61,36 @@ export class Transpile { // Can be done more efficiently with a loop - 1 return `${depth}${node.exported ? "export " : ""}function ${node.name.data}(${params}) {${body ? "\n" + body : ""}}`; } - static binxp(node: BinaryExpression) { + static EnumDeclaration(node: EnumDeclaration) { + let body = ""; + const end = node.elements.length - 1; + + depth += " "; + for (let i = 0; i < end; i++) { + const element = node.elements[i]; + body += `${depth}${element.name.data} = ${element.value.data},\n`; + } + const lastElement = node.elements[end]; + body += `${depth}${lastElement.name.data} = ${lastElement.value.data}\n`; + + depth = depth.slice(0, depth.length - 2); + + return `enum ${node.name.data} {\n${body}}`; + } + static BinaryExpression(node: BinaryExpression) { return Transpile.from(node.left) + " " + node.operand + " " + Transpile.from(node.right); } - static rt(node: ReturnStatement) { - return depth + Transpile.from(node.returning); + static ReturnStatement(node: ReturnStatement) { + // @ts-ignore + return depth + "return " + node.returning.name; } - static ref(node: ReferenceExpression) { + static ReferenceExpression(node: ReferenceExpression) { return Transpile.from(node.referencing); } - static id(node: Identifier) { + static Identifier(node: Identifier) { return node.data; } - static param(node: ParameterExpression) { + static ParameterExpression(node: ParameterExpression) { return node.name.data; } } \ No newline at end of file diff --git a/test.ts b/test.ts new file mode 100644 index 0000000..f6e032c --- /dev/null +++ b/test.ts @@ -0,0 +1,9 @@ +enum Axis { + X = 0, + Y = 1, + Z = 2 +} +export function add(a: i32, b: i32) { + let c = a + b + return c +}