Skip to content

Commit

Permalink
basic enums
Browse files Browse the repository at this point in the history
  • Loading branch information
JairusSW committed May 30, 2024
1 parent 7993239 commit 97f3511
Show file tree
Hide file tree
Showing 8 changed files with 214 additions and 109 deletions.
19 changes: 10 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
<p align="center">
<img width="800" src="https://raw.githubusercontent.com/JairusSW/Zep/master/assets/logo.svg" alt="logo">
<br>
</p>

<div align="center">
<h1>Zep Language</h1>
<h3>Compiles to WebAssembly.<br />Tuned for WebAssembly.</h3>
</div>
<h3 align="center">
<pre>
███████╗███████╗██████╗
╚══███╔╝██╔════╝██╔══██╗
███╔╝ █████╗ ██████╔╝
███╔╝ ██╔══╝ ██╔═══╝
███████╗███████╗██║
╚══════╝╚══════╝╚═╝

v0.0.0-wip</pre></h3>

## Capabilities
- ✅ Tokenizer
Expand Down
14 changes: 14 additions & 0 deletions src/ast/nodes/EnumDeclaration.ts
Original file line number Diff line number Diff line change
@@ -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;
}
}
14 changes: 14 additions & 0 deletions src/ast/nodes/EnumElement.ts
Original file line number Diff line number Diff line change
@@ -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;
}
}
2 changes: 1 addition & 1 deletion src/ast/nodes/ReferenceExpression.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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();
}
}
184 changes: 115 additions & 69 deletions src/parser/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand All @@ -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 {
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -490,15 +538,14 @@ export class Parser {
args,
);

this.program.statements.push(node);
return node;
}
parseReferenceExpression(
scope: Scope = this.program.globalScope,
): 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,
Expand Down Expand Up @@ -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`,
Expand All @@ -562,7 +609,6 @@ export class Parser {
}
}
const node = new BinaryExpression(left, op, right);
this.program.statements.push(node);
// Check scope
return node;
}
Expand Down
22 changes: 12 additions & 10 deletions src/test.ts
Original file line number Diff line number Diff line change
@@ -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
}
Expand All @@ -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);
Loading

0 comments on commit 97f3511

Please sign in to comment.