Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions src/lexer.zig
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,10 @@ pub const Lexer = struct {
};
}

pub fn reset(l: *Lexer) void {
l.* = Lexer.init(l.content, l.file_path);
}

pub fn eat(l: *Lexer, token: TokenKind) void {
l.expect(token);
l.nexti();
Expand Down
7 changes: 4 additions & 3 deletions src/main.zig
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ const lexer_pkg = @import("lexer.zig");
const parser_pkg = @import("parser.zig");

const Lexer = lexer_pkg.Lexer;
const Parser = parser_pkg.Parser;

const parse = parser_pkg.parse;

const LIMIT = 1024 * 10;

Expand All @@ -30,8 +31,8 @@ pub fn main(init: std.process.Init) !void {
content,
file_path,
);
var parser = Parser.init(&l, alloc);
const ast = try parser.parse();

const ast = try parse(&l, alloc);

ast.print();

Expand Down
317 changes: 152 additions & 165 deletions src/parser.zig
Original file line number Diff line number Diff line change
Expand Up @@ -14,198 +14,185 @@ const Arg = ast_pkg.Arg;

const panic = std.debug.panic;

pub const Parser = struct {
const Self = @This();

l: *Lexer,
alloc: Allocator,

pub fn init(l: *Lexer, alloc: Allocator) Self {
return .{
.l = l,
.alloc = alloc,
};
pub fn parse(l: *Lexer, alloc: Allocator) !Ast {
l.reset();
l.nexti();
var decls: std.ArrayList(ProgramDecl) = .empty;
while (l.token != .end) {
const expr = try parse_decl(l, alloc);
try decls.append(alloc, expr);
}

pub fn parse(self: *Self) !Ast {
self.l.nexti();
var decls: std.ArrayList(ProgramDecl) = .empty;
while (self.l.token != .end) {
const expr = try parse_decl(self.l, self.alloc);
try decls.append(self.alloc, expr);
}
return Ast.create(decls);
}

return Ast.create(decls);
fn parse_decl(l: *Lexer, alloc: Allocator) !ProgramDecl {
if (l.token == .fn_) {
return parse_fn_decl(l, alloc);
}
panic("panic in parse_decl with {}", .{l.token});
}

fn parse_decl(l: *Lexer, alloc: Allocator) !ProgramDecl {
if (l.token == .fn_) {
return parse_fn_decl(l, alloc);
}
panic("panic in parse_decl with {}", .{l.token});
}
fn parse_fn_decl(l: *Lexer, alloc: Allocator) !ProgramDecl {
l.eat(.fn_);

fn parse_fn_decl(l: *Lexer, alloc: Allocator) !ProgramDecl {
l.eat(.fn_);
// parse fn signature
l.expect(.id);
const id = l.name.as_str(l.content);
var args: std.ArrayList(Arg) = .empty;
l.eat(.id);

// parse fn signature
l.eat(.oparen);
while (l.token != .cparen) {
l.expect(.id);
const id = l.name.as_str(l.content);
var args: std.ArrayList(Arg) = .empty;
const arg_name = l.name.as_str(l.content);
l.eat(.id);

l.eat(.oparen);
while (l.token != .cparen) {
l.expect(.id);
const arg_name = l.name.as_str(l.content);
l.eat(.id);

l.eat(.colon);
l.expect(.id);
const type_ = l.name.as_str(l.content);
l.eat(.id);
const arg: Arg = .{ .name = arg_name, .type_ = type_ };

try args.append(alloc, arg);

if (l.token != .comma) {
l.expect(.cparen);
} else {
l.nexti();
}
}

l.eat(.cparen);
l.eat(.colon);
l.expect(.id);
const return_type = l.name.as_str(l.content);
const type_ = l.name.as_str(l.content);
l.eat(.id);
// end parse fn signature
const arg: Arg = .{ .name = arg_name, .type_ = type_ };

// parse fn body
const body = try parse_block_stmt(l, alloc);
try args.append(alloc, arg);

return ProgramDecl.create_fn(id, args, body, return_type);
}

fn parse_stmt(l: *Lexer, alloc: Allocator) error{OutOfMemory}!Stmt {
if (l.token == .var_) {
// var declaration
const stmt = parse_var_decl_stmt(l, alloc);
l.eat(.semicolon);
return stmt;
} else if (l.token == .if_) {
return parse_if_stmt(l, alloc);
if (l.token != .comma) {
l.expect(.cparen);
} else {
const stmt = parse_no_var_decl_stmt(l, alloc);
l.eat(.semicolon);
return stmt;
l.nexti();
}
}

fn parse_no_var_decl_stmt(l: *Lexer, alloc: Allocator) !Stmt {
const expr = try parse_expr(l, alloc);
return .{ .no_assign = .{ .value = expr } };
l.eat(.cparen);
l.expect(.id);
const return_type = l.name.as_str(l.content);
l.eat(.id);
// end parse fn signature

// parse fn body
const body = try parse_block_stmt(l, alloc);

return ProgramDecl.create_fn(id, args, body, return_type);
}

fn parse_stmt(l: *Lexer, alloc: Allocator) error{OutOfMemory}!Stmt {
if (l.token == .var_) {
// var declaration
const stmt = parse_var_decl_stmt(l, alloc);
l.eat(.semicolon);
return stmt;
} else if (l.token == .if_) {
return parse_if_stmt(l, alloc);
} else {
const stmt = parse_no_var_decl_stmt(l, alloc);
l.eat(.semicolon);
return stmt;
}

fn parse_var_decl_stmt(l: *Lexer, alloc: Allocator) !Stmt {
l.eat(.var_);
l.expect(.id);
const name = l.name.as_str(l.content);
l.eat(.id);
l.eat(.colon);
l.expect(.id);
const type_ = l.name.as_str(l.content);
l.eat(.id);
l.eat(.assign);
const expr = try parse_expr(l, alloc);
return .{ .assign = .{
.var_ = name,
.type_ = type_,
.value = expr,
} };
}

fn parse_no_var_decl_stmt(l: *Lexer, alloc: Allocator) !Stmt {
const expr = try parse_expr(l, alloc);
return .{ .no_assign = .{ .value = expr } };
}

fn parse_var_decl_stmt(l: *Lexer, alloc: Allocator) !Stmt {
l.eat(.var_);
l.expect(.id);
const name = l.name.as_str(l.content);
l.eat(.id);
l.eat(.colon);
l.expect(.id);
const type_ = l.name.as_str(l.content);
l.eat(.id);
l.eat(.assign);
const expr = try parse_expr(l, alloc);
return .{ .assign = .{
.var_ = name,
.type_ = type_,
.value = expr,
} };
}

fn parse_if_stmt(l: *Lexer, alloc: Allocator) !Stmt {
l.eat(.if_);
const if_eval = try parse_expr(l, alloc);
const if_body = try parse_block_stmt(l, alloc);
var elseif_evals: std.ArrayList(Expr) = .empty;
var elseif_thens: std.ArrayList(BlockStmt) = .empty;

while (l.token == .elseif) {
l.eat(.elseif);
const eval = try parse_expr(l, alloc);

const then = try parse_block_stmt(l, alloc);
try elseif_evals.append(alloc, eval);
try elseif_thens.append(alloc, then);
}

fn parse_if_stmt(l: *Lexer, alloc: Allocator) !Stmt {
l.eat(.if_);
const if_eval = try parse_expr(l, alloc);
const if_body = try parse_block_stmt(l, alloc);
var elseif_evals: std.ArrayList(Expr) = .empty;
var elseif_thens: std.ArrayList(BlockStmt) = .empty;

while (l.token == .elseif) {
l.eat(.elseif);
const eval = try parse_expr(l, alloc);

const then = try parse_block_stmt(l, alloc);
try elseif_evals.append(alloc, eval);
try elseif_thens.append(alloc, then);
}

var else_then: ?BlockStmt = null;
var else_then: ?BlockStmt = null;

if (l.token == .else_) {
l.eat(.else_);
else_then = try parse_block_stmt(l, alloc);
}

return Stmt.create_if(
if_eval,
if_body,
elseif_evals,
elseif_thens,
else_then,
);
if (l.token == .else_) {
l.eat(.else_);
else_then = try parse_block_stmt(l, alloc);
}

fn parse_block_stmt(l: *Lexer, alloc: Allocator) !BlockStmt {
var block: std.ArrayList(Stmt) = .empty;
l.eat(.obrace);
while (l.token != .cbrace) {
const stmt = try parse_stmt(l, alloc);
try block.append(alloc, stmt);
}
l.eat(.cbrace);

return .{ .stmts = block };
return Stmt.create_if(
if_eval,
if_body,
elseif_evals,
elseif_thens,
else_then,
);
}

fn parse_block_stmt(l: *Lexer, alloc: Allocator) !BlockStmt {
var block: std.ArrayList(Stmt) = .empty;
l.eat(.obrace);
while (l.token != .cbrace) {
const stmt = try parse_stmt(l, alloc);
try block.append(alloc, stmt);
}
l.eat(.cbrace);

fn parse_expr(l: *Lexer, alloc: Allocator) error{OutOfMemory}!Expr {
if (l.token == .id) {
const next_l = l.nextl();
if (next_l.token == .oparen) {
// fn call
return parse_fn_call_expr(l, alloc);
}
} else if (l.token == .str) {
return parse_str(l);
}
panic("parse_expr panics with {}, name: {s}", .{ l.token, l.name.as_str(l.content) });
}
return .{ .stmts = block };
}

fn parse_str(l: *Lexer) !Expr {
const raw_str = l.name.as_str(l.content);
l.eat(.str);
return Expr.create_str(raw_str);
fn parse_expr(l: *Lexer, alloc: Allocator) error{OutOfMemory}!Expr {
if (l.token == .id) {
const next_l = l.nextl();
if (next_l.token == .oparen) {
// fn call
return parse_fn_call_expr(l, alloc);
}
} else if (l.token == .str) {
return parse_str(l);
}

fn parse_fn_call_expr(l: *Lexer, alloc: Allocator) !Expr {
const fn_name = l.name.as_str(l.content);
l.eat(.id);
l.eat(.oparen);
var args: std.ArrayList(Expr) = .empty;

while (l.token != .cparen) {
const arg = try parse_expr(l, alloc);

try args.append(alloc, arg);
if (l.token == .comma) {
l.eat(.comma);
} else {
l.expect(.cparen);
}
panic("parse_expr panics with {}, name: {s}", .{ l.token, l.name.as_str(l.content) });
}

fn parse_str(l: *Lexer) !Expr {
const raw_str = l.name.as_str(l.content);
l.eat(.str);
return Expr.create_str(raw_str);
}

fn parse_fn_call_expr(l: *Lexer, alloc: Allocator) !Expr {
const fn_name = l.name.as_str(l.content);
l.eat(.id);
l.eat(.oparen);
var args: std.ArrayList(Expr) = .empty;

while (l.token != .cparen) {
const arg = try parse_expr(l, alloc);

try args.append(alloc, arg);
if (l.token == .comma) {
l.eat(.comma);
} else {
l.expect(.cparen);
}
l.eat(.cparen);

return Expr.create_fn_call(fn_name, args);
}
};
l.eat(.cparen);

return Expr.create_fn_call(fn_name, args);
}
Loading