Skip to content

Commit

Permalink
Add returns
Browse files Browse the repository at this point in the history
  • Loading branch information
mtso committed Aug 7, 2021
1 parent 7a8c5d7 commit cf6b3ad
Show file tree
Hide file tree
Showing 5 changed files with 129 additions and 40 deletions.
14 changes: 14 additions & 0 deletions ast.ts
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,7 @@ export module stmt {
export type Visitor<T> = {
visitBlockStmt: (exp: Block) => T;
visitExpressionStmt: (exp: Expression) => T;
visitReturnStmt: (exp: Return) => T;
visitVarStmt: (exp: Var) => T;
};
export class Block extends Stmt {
Expand All @@ -186,6 +187,19 @@ export module stmt {
return visitor.visitExpressionStmt(this);
}
}
export class Return extends Stmt {
keyword: Token;
value: Expr | null;

constructor(keyword: Token, value: Expr | null) {
super();
this.keyword = keyword;
this.value = value;
}
accept<T>(visitor: Visitor<T>): T {
return visitor.visitReturnStmt(this);
}
}
export class Var extends Stmt {
name: Token;
initializer: Expr;
Expand Down
98 changes: 59 additions & 39 deletions obi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -205,11 +205,11 @@ class ObiFunction extends Callable {
try {
return interpreter.executeBlock(this.declaration.body, environment);
} catch (err) {
// if (err instanceof Return) {
// return (err as Return).value;
// } else {
throw err;
// }
if (err instanceof Return) {
return (err as Return).value;
} else {
throw err;
}
}
return null;
}
Expand All @@ -232,6 +232,13 @@ class RuntimeError extends Error {
this.token = token;
}
}
class Return extends Error {
value: any;
constructor(value: any) {
super();
this.value = value;
}
}

class Resolver implements expr.Visitor<void>, stmt.Visitor<void> {
private interpreter: Interpreter;
Expand Down Expand Up @@ -267,16 +274,16 @@ class Resolver implements expr.Visitor<void>, stmt.Visitor<void> {
// // this.currentFunction = enclosingFunction;
// }
resolveFunction(func: expr.Function, type: FunctionType) {
// const enclosingFunction = this.currentFunction;
// this.currentFunction = type;
const enclosingFunction = this.currentFunction;
this.currentFunction = type;
this.beginScope();
for (const param of func.parameters) {
this.declare(param);
this.define(param);
}
this.resolveStmts(func.body);
this.endScope();
// this.currentFunction = enclosingFunction;
this.currentFunction = enclosingFunction;
}

beginScope() {
Expand Down Expand Up @@ -319,6 +326,20 @@ class Resolver implements expr.Visitor<void>, stmt.Visitor<void> {
visitExpressionStmt(stm: stmt.Expression) {
this.resolveExpr(stm.expression);
}
visitReturnStmt(stm: stmt.Return) {
if (this.currentFunction === FunctionType.NONE) {
Obi.errorToken(stm.keyword, "Can't return from top-level code.");
}
if (stm.value !== null) {
// if (this.currentFunction === FunctionType.INITIALIZER) {
// Obi.errorToken(
// stm.keyword,
// "Can't return a value from an initializer.",
// );
// }
this.resolveExpr(stm.value);
}
}
visitVarStmt(stm: stmt.Var) {
this.declare(stm.name);
if (stm.initializer !== null) {
Expand Down Expand Up @@ -368,7 +389,10 @@ class Resolver implements expr.Visitor<void>, stmt.Visitor<void> {
for (let i = 0; i < exp.cases.length; i++) {
const case_ = exp.cases[i];
if (case_.isDefault && i !== exp.cases.length - 1) {
Obi.errorToken(exp.where, "Match branches after default case will never be reached.");
Obi.errorToken(
exp.where,
"Match branches after default case will never be reached.",
);
}
if (null !== case_.pattern) this.resolveExpr(case_.pattern);
this.resolveStmt(case_.branch);
Expand Down Expand Up @@ -460,6 +484,11 @@ class Interpreter implements expr.Visitor<any>, stmt.Visitor<any> {
visitExpressionStmt(stm: stmt.Expression): any {
return this.evaluate(stm.expression);
}
visitReturnStmt(stm: stmt.Return) {
let value = null;
if (stm.value !== null) value = this.evaluate(stm.value);
throw new Return(value);
}
visitVarStmt(stm: stmt.Var): any {
let value = null;
if (stm.initializer != null) {
Expand Down Expand Up @@ -786,11 +815,7 @@ class Parser {
// }

private statement(): Stmt {
// if (this.match(TT.FOR)) return this.forStatement();
// if (this.match(TT.IF)) return this.ifStatement();
// if (this.match(TT.PRINT)) return this.printStatement();
// if (this.match(TT.RETURN)) return this.returnStatement();
// if (this.match(TT.WHILE)) return this.whileStatement();
if (this.match(TT.RETURN)) return this.returnStatement();
if (this.match(TT.LEFT_BRACE)) return new stmt.Block(this.block());
return this.expressionStatement();
}
Expand Down Expand Up @@ -839,21 +864,15 @@ class Parser {
// return new stmt.If(condition, thenBranch, elseBranch);
// }

// private printStatement(): Stmt {
// const value = this.expression();
// this.consume(TT.SEMICOLON, "Expect ';' after value.");
// return new stmt.Print(value);
// }

// private returnStatement(): Stmt {
// const keyword = this.previous();
// let value = null;
// if (!this.check(TT.SEMICOLON)) {
// value = this.expression();
// }
// this.consume(TT.SEMICOLON, "Expect ';' after return value.");
// return new stmt.Return(keyword, value);
// }
private returnStatement(): Stmt {
const keyword = this.previous();
let value = null;
if (!this.check(TT.SEMICOLON)) {
value = this.expression();
}
this.consume(TT.SEMICOLON, "Expect ';' after return value.");
return new stmt.Return(keyword, value);
}

private varDeclaration() {
const name = this.consume(TT.IDENTIFIER, "Expect variable name.");
Expand Down Expand Up @@ -1115,7 +1134,7 @@ class Parser {
}

table() {
const table : { [key: string] : any } = {};
const table: { [key: string]: any } = {};
let index = 0;

while (true) {
Expand Down Expand Up @@ -1144,7 +1163,8 @@ class Parser {
if (this.match(TT.NUMBER)) return new expr.Literal(this.previous().literal);
if (this.match(TT.STRING)) return new expr.Literal(this.previous().literal);

if (this.match(TT.LEFT_BRACKET)) return new expr.Literal(this.table());
// Turn off list literals for now.
// if (this.match(TT.LEFT_BRACKET)) return new expr.Literal(this.table());

// if (this.match(TT.SUPER)) {
// const keyword = this.previous();
Expand Down Expand Up @@ -1373,14 +1393,14 @@ class Scanner {
this.addToken(TT.RIGHT_BRACE);
this.column += 1;
break;
case "[":
this.addToken(TT.LEFT_BRACKET);
this.column += 1;
break;
case "]":
this.addToken(TT.RIGHT_BRACKET);
this.column += 1;
break;
// case "[":
// this.addToken(TT.LEFT_BRACKET);
// this.column += 1;
// break;
// case "]":
// this.addToken(TT.RIGHT_BRACKET);
// this.column += 1;
// break;
case ",":
this.addToken(TT.COMMA);
this.column += 1;
Expand Down
46 changes: 46 additions & 0 deletions sketch.md
Original file line number Diff line number Diff line change
Expand Up @@ -108,3 +108,49 @@ a := 5 == 4;
a == 5 match ( 5 -> print("5"); )

match 5 ( )

# edge cases with trailer and classes

foo.(bar)() {

}

foo.(Iter)() {

}

foo.iter() fun(item) { getStuff }

type Foo { init() {

} }

foo = Foo(a, b); foo.(b)() { what }

html() { what }

// html {

}

// trailer on method foo.html {

}

// chained GETs foo.route("/").handler("GET") {

}

html() { head() { link(1, 2); }; };

// trailing on init Foo() {

}

class Table { fields: Map<string, any>; methods: Map<string, any>; } fun Foo(a,
b) { _a := a; [ get a = fun() { _a; }, set a = fun(val) { _a = val; }, printA =
fun () { print(a); } ]; }

foo.printA(); foo.a = 5;

fun makeFoo() { kin Foo { print() { print("a"); } } Foo(); }
9 changes: 9 additions & 0 deletions tests/functions/return.obi
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
fun foo(a) {
match(a) {
5 -> return "5";
_ -> return "not 5";
};
}

print(foo(5)); // expect: "5"
print(foo(4)); // expect: "not 5"
2 changes: 1 addition & 1 deletion tools/generate_ast.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ const defineAst = (baseName: string, types: string[]) => {
// "Function = name: Token, parameters: Token[], body: Stmt[]",
// "If = condition: Expr, thenBranch: Stmt, elseBranch: Stmt | null",
// "Print = expression: Expr",
// "Return = keyword: Token, value: Expr | null",
"Return = keyword: Token, value: Expr | null",
"Var = name: Token, initializer: Expr",
// "While = condition: Expr, body: Stmt",
]);
Expand Down

0 comments on commit cf6b3ad

Please sign in to comment.