Compare commits

...

3 Commits

Author SHA1 Message Date
Luna c0c93183cb add return statements 2019-08-25 23:16:13 -03:00
Luna 8f44cbea23 parser: ensure calls only happen to Variables 2019-08-25 22:55:53 -03:00
Luna 5ba807d93f add function calls 2019-08-25 22:41:25 -03:00
3 changed files with 120 additions and 3 deletions

View File

@ -30,4 +30,8 @@ fn main(a int) int {
loop a > 2 {
println('skirts')
}
cock_and_ball_torture('cbt', 1, 2, 3)
return 23
}

View File

@ -76,6 +76,7 @@ pub const ExprType = enum {
Logical,
Literal,
Variable,
Call,
Grouping,
};
@ -84,6 +85,12 @@ pub const VarDecl = struct {
mutable: bool = false,
};
pub const CallExpr = struct {
callee: *Expr,
paren: Token,
arguments: ExprList,
};
pub const Expr = union(ExprType) {
Assign: AssignExpr,
VarDecl: VarDecl,
@ -95,6 +102,7 @@ pub const Expr = union(ExprType) {
Variable: Token,
Grouping: *Expr,
Call: CallExpr,
};
pub const Block = std.ArrayList(*Stmt);
@ -117,6 +125,13 @@ pub const Stmt = union(enum) {
If: IfStmt,
Loop: LoopStmt,
Return: ReturnStmt,
pub const ReturnStmt = struct {
keyword: Token,
value: *Expr,
};
pub fn mkPrintln(allocator: *std.mem.Allocator, expr: *Expr) !*Stmt {
var stmt = try allocator.create(Stmt);
stmt.* = Stmt{ .Println = expr };
@ -156,6 +171,18 @@ pub const Stmt = union(enum) {
return stmt;
}
pub fn mkReturn(allocator: *std.mem.Allocator, tok: Token, value: *Expr) !*Stmt {
var stmt = try allocator.create(Stmt);
stmt.* = Stmt{
.Return = ReturnStmt{
.keyword = tok,
.value = value,
},
};
return stmt;
}
};
pub const Node = union(NodeType) {
@ -305,6 +332,18 @@ pub fn printExpr(expr: *Expr) void {
std.debug.warn(")");
},
.Call => |call| {
std.debug.warn("(");
printExpr(call.callee);
for (call.arguments.toSlice()) |arg| {
std.debug.warn(" ");
printExpr(arg);
}
std.debug.warn(")");
},
else => std.debug.warn("UnknownExpr-{}", @tagName(expr.*)),
}
}
@ -341,6 +380,12 @@ pub fn printStmt(ident: usize, stmt: *Stmt) void {
std.debug.warn(")\n");
},
.Return => |ret| {
std.debug.warn("(return ");
printExpr(ret.value);
std.debug.warn(")\n");
},
else => std.debug.warn("UnknownStmt-{}", @tagName(stmt.*)),
}
}

View File

@ -241,6 +241,19 @@ pub const Parser = struct {
return vardecl;
}
fn mkCall(self: *@This(), callee: *Expr, paren: Token, args: ast.ExprList) !*Expr {
var expr = try self.allocator.create(Expr);
expr.* = Expr{
.Call = ast.CallExpr{
.callee = callee,
.paren = paren,
.arguments = args,
},
};
return expr;
}
fn mkBool(self: *Parser, val: bool) !*ast.Expr {
var expr = try self.allocator.create(Expr);
expr.* = Expr{
@ -451,6 +464,7 @@ pub const Parser = struct {
.If => try self.parseIfStmt(),
.Loop => try self.parseLoop(),
.Println => try self.parsePrintln(),
.Return => try self.parseReturn(),
// TODO make newlines tokens and consume newline?
else => try self.parseStmtExpr(),
@ -512,14 +526,20 @@ pub const Parser = struct {
return try Stmt.mkLoop(self.allocator, expr, body);
}
fn parseReturn(self: *@This()) !*Stmt {
const tok = try self.consumeSingle(.Return);
const expr = (try self.parseExpr()).Expr;
return try Stmt.mkReturn(self.allocator, tok, expr);
}
fn parsePrintln(self: *@This()) !*Stmt {
_ = try self.consumeSingle(.Println);
_ = try self.consumeSingle(.LeftParen);
var expr = try self.parseExpr();
var expr = (try self.parseExpr()).Expr;
_ = try self.consumeSingle(.RightParen);
return try Stmt.mkPrintln(self.allocator, expr.Expr);
return try Stmt.mkPrintln(self.allocator, expr);
}
fn parseStmtExpr(self: *@This()) !*Stmt {
@ -676,10 +696,58 @@ pub const Parser = struct {
return try self.mkUnary(op, right);
}
var expr = try self.parsePrimary();
var expr = try self.parseCall();
return expr;
}
fn parseCall(self: *@This()) !*Expr {
// we parse a primary expression instead of consuming a .Identifier
// since parseCall is connected to the rest of the parser. doing
// identifiers would break the rest of the rules that want primaries.
// nothing stops us from ensuring expr is a Variable though ;)
var expr = try self.parsePrimary();
while (true) {
if (self.check(.LeftParen)) {
if (ast.ExprType(expr.*) != .Variable) {
self.doError("cannot call non-variable{}", ast.ExprType(expr.*));
return Result.CompileError;
}
_ = try self.consumeSingle(.LeftParen);
expr = try self.finishCall(expr);
} else {
break;
}
}
return expr;
}
fn finishCall(self: *@This(), callee: *Expr) !*Expr {
var args = ast.ExprList.init(self.allocator);
errdefer args.deinit();
if (!self.check(.RightParen)) {
// emulating do-while really badly
var arg = (try self.parseExpr()).Expr;
try args.append(arg);
while (self.check(.Comma)) {
_ = try self.consumeSingle(.Comma);
arg = (try self.parseExpr()).Expr;
try args.append(arg);
}
}
var paren = try self.consume(.RightParen, "Expected ')' after arguments");
return self.mkCall(callee, paren, args);
}
fn parsePrimary(self: *@This()) !*Expr {
const curtype = self.peek().ttype;
const lexeme = self.peek().lexeme;