2019-09-18 18:59:11 +00:00
|
|
|
const std = @import("std");
|
|
|
|
const scanners = @import("scanners.zig");
|
|
|
|
const main = @import("main.zig");
|
|
|
|
const ast = @import("ast.zig");
|
|
|
|
const tokens = @import("tokens.zig");
|
2019-09-20 15:22:46 +00:00
|
|
|
const ereport = @import("errors.zig");
|
2019-09-18 18:59:11 +00:00
|
|
|
const printer = @import("ast_printer.zig");
|
|
|
|
|
|
|
|
const Allocator = std.mem.Allocator;
|
|
|
|
const Scanner = scanners.Scanner;
|
|
|
|
const Token = tokens.Token;
|
|
|
|
const TokenType = tokens.TokenType;
|
|
|
|
|
2019-09-24 02:18:31 +00:00
|
|
|
pub const ParseError = error{
|
|
|
|
CompileError,
|
|
|
|
UnknownOperator,
|
2019-09-27 15:07:20 +00:00
|
|
|
InvalidInteger,
|
2019-09-24 02:18:31 +00:00
|
|
|
};
|
2019-09-18 18:59:11 +00:00
|
|
|
|
|
|
|
const Node = ast.Node;
|
|
|
|
const Expr = ast.Expr;
|
|
|
|
const Stmt = ast.Stmt;
|
|
|
|
|
|
|
|
const TokenList = std.ArrayList(Token);
|
|
|
|
|
|
|
|
const FieldState = struct {
|
|
|
|
public: bool = false,
|
|
|
|
mutable: bool = false,
|
|
|
|
mutable_outside: bool = false,
|
|
|
|
};
|
|
|
|
|
2019-09-24 02:18:31 +00:00
|
|
|
const operator_tokens = [_][]const u8{
|
|
|
|
"+", "-", "*", "/", "%", ">", ">=", "<", "<=", "==",
|
|
|
|
};
|
|
|
|
|
|
|
|
const operator_values = [_]ast.BinaryOperator{
|
|
|
|
.Add,
|
|
|
|
.Sub,
|
|
|
|
.Mul,
|
|
|
|
.Div,
|
|
|
|
.Mod,
|
|
|
|
|
|
|
|
.Greater,
|
|
|
|
.GreaterEqual,
|
|
|
|
.Less,
|
|
|
|
.LessEqual,
|
|
|
|
|
|
|
|
.Equal,
|
|
|
|
};
|
|
|
|
|
|
|
|
fn toBinaryOperator(op: Token) !ast.BinaryOperator {
|
|
|
|
const lex = op.lexeme;
|
|
|
|
|
|
|
|
inline for (operator_tokens) |op_str, idx| {
|
|
|
|
if (std.mem.eql(u8, lex, op_str)) return operator_values[idx];
|
|
|
|
}
|
|
|
|
|
|
|
|
// special cases: && + and => .And and || + or => .Or
|
|
|
|
if (std.mem.eql(u8, lex, "&&") or std.mem.eql(u8, lex, "and")) return .And;
|
|
|
|
if (std.mem.eql(u8, lex, "||") or std.mem.eql(u8, lex, "or")) return .Or;
|
|
|
|
|
|
|
|
return ParseError.UnknownOperator;
|
|
|
|
}
|
|
|
|
|
2019-09-25 17:34:32 +00:00
|
|
|
const unary_operator_tokens = [_][]const u8{
|
|
|
|
"!", "-",
|
|
|
|
};
|
|
|
|
|
|
|
|
const unary_operators = [_]ast.UnaryOperator{
|
|
|
|
.Not, .Negate,
|
|
|
|
};
|
|
|
|
|
|
|
|
fn toUnaryOperator(op: Token) !ast.UnaryOperator {
|
|
|
|
const lex = op.lexeme;
|
|
|
|
|
|
|
|
inline for (unary_operator_tokens) |op_str, idx| {
|
|
|
|
if (std.mem.eql(u8, lex, op_str)) return unary_operators[idx];
|
|
|
|
}
|
|
|
|
|
|
|
|
return ParseError.UnknownOperator;
|
|
|
|
}
|
|
|
|
|
2019-09-18 18:59:11 +00:00
|
|
|
pub const Parser = struct {
|
|
|
|
allocator: *Allocator,
|
|
|
|
scanner: *Scanner,
|
|
|
|
tokens: TokenList,
|
|
|
|
|
|
|
|
hadError: bool = false,
|
2019-09-20 16:16:55 +00:00
|
|
|
err_ctx: ?[]const u8 = null,
|
2019-09-18 18:59:11 +00:00
|
|
|
|
|
|
|
pub fn init(allocator: *Allocator, scanner: *Scanner) Parser {
|
|
|
|
return Parser{
|
|
|
|
.allocator = allocator,
|
|
|
|
.scanner = scanner,
|
|
|
|
.tokens = TokenList.init(allocator),
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn deinit(self: *@This()) void {
|
|
|
|
self.tokens.deinit();
|
|
|
|
}
|
|
|
|
|
2019-09-20 16:16:55 +00:00
|
|
|
fn setErrContext(self: *Parser, comptime fmt: ?[]const u8, args: ...) void {
|
|
|
|
if (fmt == null) {
|
|
|
|
self.err_ctx = null;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
var buf = self.allocator.alloc(u8, 256) catch unreachable;
|
|
|
|
self.err_ctx = std.fmt.bufPrint(buf, fmt.?, args) catch unreachable;
|
|
|
|
}
|
|
|
|
|
2019-09-20 15:22:46 +00:00
|
|
|
fn doError(self: *Parser, comptime fmt: []const u8, args: ...) ParseError {
|
2019-09-18 18:59:11 +00:00
|
|
|
self.hadError = true;
|
|
|
|
|
2019-09-20 16:16:55 +00:00
|
|
|
std.debug.warn("parser error at line {}", self.scanner.line);
|
|
|
|
if (self.err_ctx) |ctx| {
|
|
|
|
std.debug.warn(" on {}", ctx);
|
|
|
|
}
|
|
|
|
|
|
|
|
std.debug.warn("\n\t");
|
|
|
|
|
2019-09-18 18:59:11 +00:00
|
|
|
std.debug.warn(fmt, args);
|
|
|
|
std.debug.warn("\n");
|
2019-09-20 15:22:46 +00:00
|
|
|
|
|
|
|
return ParseError.CompileError;
|
|
|
|
}
|
|
|
|
|
|
|
|
fn synchronize(self: *Parser) !void {
|
|
|
|
// TODO is it nextToken()?
|
|
|
|
_ = try self.nextToken();
|
|
|
|
|
|
|
|
while (!self.isAtEnd()) {
|
|
|
|
if (self.previous().typ == .Semicolon) return;
|
|
|
|
|
|
|
|
// TODO add more "stmt"-"starting" tokens here
|
|
|
|
switch (self.peek().typ) {
|
|
|
|
.Fn => return,
|
|
|
|
else => {},
|
|
|
|
}
|
|
|
|
|
|
|
|
_ = try self.nextToken();
|
|
|
|
}
|
2019-09-18 18:59:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
fn peek(self: *Parser) Token {
|
|
|
|
return self.tokens.at(self.tokens.len - 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
fn previous(self: *Parser) Token {
|
|
|
|
return self.tokens.at(self.tokens.len - 2);
|
|
|
|
}
|
|
|
|
|
2019-09-20 15:22:46 +00:00
|
|
|
fn tokenError(self: *Parser, token: Token, msg: []const u8) ParseError {
|
2019-09-20 16:16:55 +00:00
|
|
|
std.debug.warn("ctx: '{}'\n", self.err_ctx);
|
2019-09-18 18:59:11 +00:00
|
|
|
if (token.typ == .EOF) {
|
2019-09-20 16:16:55 +00:00
|
|
|
ereport.report(token.line, " at end", self.err_ctx, msg);
|
2019-09-18 18:59:11 +00:00
|
|
|
} else {
|
2019-09-20 16:16:55 +00:00
|
|
|
ereport.reportFmt(token.line, self.err_ctx, " at '{}': {}", token.lexeme, msg);
|
2019-09-18 18:59:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return ParseError.CompileError;
|
|
|
|
}
|
|
|
|
|
|
|
|
fn isAtEnd(self: *Parser) bool {
|
|
|
|
return self.peek().typ == .EOF;
|
|
|
|
}
|
|
|
|
|
|
|
|
fn check(self: *Parser, typ: TokenType) bool {
|
|
|
|
if (self.isAtEnd()) return false;
|
|
|
|
return self.peek().typ == typ;
|
|
|
|
}
|
|
|
|
|
|
|
|
fn nextToken(self: *Parser) !Token {
|
|
|
|
var token: Token = undefined;
|
|
|
|
|
|
|
|
while (true) {
|
|
|
|
var next_token_opt = try self.scanner.nextToken();
|
|
|
|
if (next_token_opt) |token_nice| {
|
|
|
|
token = token_nice;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
try self.tokens.append(token);
|
|
|
|
std.debug.warn("skip to {}\n", token);
|
|
|
|
return token;
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Consume the current token type, then walk to the next token.
|
|
|
|
/// Returns the consumed token.
|
|
|
|
fn consume(self: *Parser, ttype: TokenType, comptime msg: []const u8) !Token {
|
|
|
|
if (self.check(ttype)) {
|
|
|
|
var tok = self.peek();
|
|
|
|
_ = try self.nextToken();
|
|
|
|
return tok;
|
|
|
|
}
|
|
|
|
|
2019-09-20 15:22:46 +00:00
|
|
|
return self.tokenError(self.peek(), msg);
|
2019-09-18 18:59:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Consume the current token. Gives default error messages
|
|
|
|
fn consumeSingle(self: *Parser, ttype: TokenType) !Token {
|
|
|
|
if (self.check(ttype)) {
|
|
|
|
var cur = self.peek();
|
|
|
|
_ = try self.nextToken();
|
|
|
|
return cur;
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO maybe this could be entirely comptime?
|
|
|
|
var buf_main: [1000]u8 = undefined;
|
|
|
|
var buf = try std.fmt.bufPrint(
|
|
|
|
buf_main[0..],
|
|
|
|
"expected {}, got {}",
|
|
|
|
ttype,
|
|
|
|
self.peek().typ,
|
|
|
|
);
|
|
|
|
|
2019-09-20 15:22:46 +00:00
|
|
|
return self.tokenError(self.peek(), buf);
|
2019-09-18 18:59:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/// check() against multiple tokens
|
|
|
|
fn compareAnyOf(self: *@This(), ttypes: []TokenType) bool {
|
|
|
|
for (ttypes) |typ| {
|
|
|
|
if (self.check(typ)) return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO maybe move helper functions to ast_helper.zig?
|
|
|
|
|
|
|
|
fn mkConstDecl(self: *Parser, consts: ast.ConstList) !*ast.Node {
|
|
|
|
var node = try self.allocator.create(Node);
|
|
|
|
node.* = Node{ .ConstDecl = consts };
|
|
|
|
return node;
|
|
|
|
}
|
|
|
|
|
|
|
|
fn mkBlock(self: *Parser, stmts: ast.StmtList) !*ast.Node {
|
|
|
|
var node = try self.allocator.create(Node);
|
|
|
|
node.* = Node{ .Block = stmts };
|
|
|
|
return node;
|
|
|
|
}
|
|
|
|
|
|
|
|
fn mkStmt(self: *Parser, stmt: *Stmt) !*ast.Node {
|
|
|
|
var node = try self.allocator.create(Node);
|
|
|
|
node.* = Node{ .Stmt = stmt };
|
|
|
|
return node;
|
|
|
|
}
|
|
|
|
|
|
|
|
fn mkStmtExpr(self: *Parser, expr: *Expr) !*Stmt {
|
|
|
|
var stmt = try self.allocator.create(Stmt);
|
|
|
|
stmt.* = Stmt{ .Expr = expr };
|
|
|
|
return stmt;
|
|
|
|
}
|
|
|
|
|
|
|
|
fn mkGrouping(self: *Parser, expr: *Expr) !*ast.Expr {
|
|
|
|
var grouping = try self.allocator.create(Expr);
|
|
|
|
grouping.* = Expr{ .Grouping = expr };
|
|
|
|
return grouping;
|
|
|
|
}
|
|
|
|
|
2019-09-25 17:34:32 +00:00
|
|
|
fn mkUnary(self: *Parser, op: ast.UnaryOperator, right: *Expr) !*Expr {
|
2019-09-18 18:59:11 +00:00
|
|
|
var expr = try self.allocator.create(Expr);
|
|
|
|
expr.* = Expr{
|
|
|
|
.Unary = ast.UnaryExpr{
|
|
|
|
.op = op,
|
|
|
|
.right = right,
|
|
|
|
},
|
|
|
|
};
|
|
|
|
return expr;
|
|
|
|
}
|
|
|
|
|
2019-09-24 02:18:31 +00:00
|
|
|
fn mkBinary(self: *Parser, left: *Expr, op: ast.BinaryOperator, right: *Expr) !*Expr {
|
2019-09-18 18:59:11 +00:00
|
|
|
var expr = try self.allocator.create(Expr);
|
|
|
|
expr.* = Expr{
|
|
|
|
.Binary = ast.BinaryExpr{
|
|
|
|
.left = left,
|
|
|
|
.op = op,
|
|
|
|
.right = right,
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
return expr;
|
|
|
|
}
|
|
|
|
|
|
|
|
fn mkAssign(self: *Parser, name: Token, value: *Expr) !*Expr {
|
|
|
|
var expr = try self.allocator.create(Expr);
|
|
|
|
expr.* = Expr{
|
|
|
|
.Assign = ast.AssignExpr{
|
|
|
|
.name = name,
|
|
|
|
.value = value,
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
return expr;
|
|
|
|
}
|
|
|
|
|
|
|
|
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 mkStructExpr(self: *@This(), name: Token, args: ast.StructInitList) !*Expr {
|
|
|
|
var expr = try self.allocator.create(Expr);
|
|
|
|
expr.* = Expr{
|
|
|
|
.Struct = ast.StructExpr{
|
|
|
|
.name = name,
|
|
|
|
.inits = args,
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
return expr;
|
|
|
|
}
|
|
|
|
|
2019-09-26 21:03:39 +00:00
|
|
|
fn mkGet(self: *@This(), target: *Expr, name: Token) !*Expr {
|
2019-09-18 18:59:11 +00:00
|
|
|
var expr = try self.allocator.create(Expr);
|
|
|
|
expr.* = Expr{
|
|
|
|
.Get = ast.GetExpr{
|
2019-09-26 21:03:39 +00:00
|
|
|
.target = target,
|
2019-09-18 18:59:11 +00:00
|
|
|
.name = name,
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
return expr;
|
|
|
|
}
|
|
|
|
|
|
|
|
fn mkSet(self: *@This(), struc: *Expr, field: Token, value: *Expr) !*Expr {
|
|
|
|
var expr = try self.allocator.create(Expr);
|
|
|
|
expr.* = Expr{
|
|
|
|
.Set = ast.SetExpr{
|
|
|
|
.struc = struc,
|
|
|
|
.field = field,
|
|
|
|
.value = value,
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
return expr;
|
|
|
|
}
|
|
|
|
|
|
|
|
fn mkBool(self: *Parser, val: bool) !*ast.Expr {
|
|
|
|
var expr = try self.allocator.create(Expr);
|
|
|
|
expr.* = Expr{
|
|
|
|
.Literal = ast.LiteralExpr{
|
|
|
|
.Bool = val,
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
return expr;
|
|
|
|
}
|
|
|
|
|
2019-09-27 15:07:20 +00:00
|
|
|
fn mkInt32(self: *Parser, val: i32) !*ast.Expr {
|
2019-09-18 18:59:11 +00:00
|
|
|
var expr = try self.allocator.create(Expr);
|
|
|
|
expr.* = Expr{
|
|
|
|
.Literal = ast.LiteralExpr{
|
2019-09-27 15:07:20 +00:00
|
|
|
.Integer32 = val,
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
return expr;
|
|
|
|
}
|
|
|
|
|
|
|
|
fn mkInt64(self: *Parser, val: i64) !*ast.Expr {
|
|
|
|
var expr = try self.allocator.create(Expr);
|
|
|
|
expr.* = Expr{
|
|
|
|
.Literal = ast.LiteralExpr{
|
|
|
|
.Integer64 = val,
|
2019-09-18 18:59:11 +00:00
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
return expr;
|
|
|
|
}
|
|
|
|
|
|
|
|
fn mkFloat(self: *Parser, val: []const u8) !*ast.Expr {
|
|
|
|
var expr = try self.allocator.create(Expr);
|
|
|
|
expr.* = Expr{
|
|
|
|
.Literal = ast.LiteralExpr{
|
|
|
|
.Float = val,
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
return expr;
|
|
|
|
}
|
|
|
|
|
|
|
|
fn mkString(self: *Parser, val: []const u8) !*ast.Expr {
|
|
|
|
var expr = try self.allocator.create(Expr);
|
|
|
|
expr.* = Expr{
|
|
|
|
.Literal = ast.LiteralExpr{
|
|
|
|
.String = val,
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
return expr;
|
|
|
|
}
|
|
|
|
|
|
|
|
fn mkArray(self: *Parser, exprs: ast.ExprList) !*ast.Expr {
|
|
|
|
var expr = try self.allocator.create(Expr);
|
|
|
|
expr.* = Expr{
|
|
|
|
.Literal = ast.LiteralExpr{
|
|
|
|
.Array = exprs,
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
return expr;
|
|
|
|
}
|
|
|
|
|
|
|
|
fn mkVariable(self: *Parser, variable: Token) !*ast.Expr {
|
|
|
|
var expr = try self.allocator.create(Expr);
|
|
|
|
expr.* = Expr{ .Variable = variable };
|
|
|
|
|
|
|
|
return expr;
|
|
|
|
}
|
|
|
|
|
2019-09-20 15:22:46 +00:00
|
|
|
pub fn parse(self: *Parser) !?*ast.Node {
|
2019-09-18 18:59:11 +00:00
|
|
|
var root = try Node.mkRoot(self.allocator);
|
|
|
|
|
|
|
|
var token_opt: ?Token = null;
|
|
|
|
|
|
|
|
while (true) {
|
|
|
|
if (token_opt == null) {
|
|
|
|
token_opt = try self.nextToken();
|
|
|
|
} else {
|
|
|
|
token_opt = self.peek();
|
|
|
|
}
|
|
|
|
|
|
|
|
var token = token_opt.?;
|
|
|
|
if (token.typ == .EOF) break;
|
|
|
|
|
2019-09-20 15:22:46 +00:00
|
|
|
var node = self.parseTopDecl() catch |err| {
|
|
|
|
if (err == ParseError.CompileError) return null;
|
|
|
|
return err;
|
|
|
|
};
|
2019-09-18 19:53:44 +00:00
|
|
|
try root.Root.append(node.*);
|
2019-09-18 18:59:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (self.hadError) {
|
|
|
|
return error.ParseError;
|
|
|
|
}
|
|
|
|
|
|
|
|
return root;
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Copy a token with a different lexeme.
|
|
|
|
fn mkToken(self: *@This(), ttype: TokenType, lexeme: []const u8, line: usize) !Token {
|
|
|
|
const owned_lexeme = try std.mem.dupe(self.allocator, u8, lexeme);
|
|
|
|
return Token{
|
|
|
|
.typ = ttype,
|
|
|
|
.lexeme = owned_lexeme,
|
|
|
|
.line = line,
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
fn parseFnDecl(self: *@This()) !*Node {
|
|
|
|
var param_list = ast.ParamList.init(self.allocator);
|
|
|
|
errdefer param_list.deinit();
|
|
|
|
|
|
|
|
var method: ?*ast.MethodData = null;
|
|
|
|
|
|
|
|
_ = try self.consumeSingle(.Fn);
|
|
|
|
|
|
|
|
if (self.check(.LeftParen)) {
|
|
|
|
method = try self.parsePreMethod();
|
|
|
|
}
|
|
|
|
|
|
|
|
const name = try self.consumeSingle(.Identifier);
|
|
|
|
|
2019-09-20 16:16:55 +00:00
|
|
|
self.setErrContext("function {}", name.lexeme);
|
|
|
|
|
2019-09-18 18:59:11 +00:00
|
|
|
_ = try self.consumeSingle(.LeftParen);
|
|
|
|
|
|
|
|
while (self.peek().typ != .RightParen) {
|
|
|
|
const param_name = try self.consumeSingle(.Identifier);
|
2019-09-20 16:32:35 +00:00
|
|
|
_ = try self.consumeSingle(.Colon);
|
2019-09-18 18:59:11 +00:00
|
|
|
const param_type = try self.consumeSingle(.Identifier);
|
|
|
|
|
|
|
|
try param_list.append(ast.ParamDecl{
|
|
|
|
.name = param_name,
|
|
|
|
.typ = param_type,
|
|
|
|
});
|
2019-09-20 16:32:35 +00:00
|
|
|
|
|
|
|
if (self.check(.Comma)) _ = try self.consumeSingle(.Comma);
|
2019-09-18 18:59:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
_ = try self.consumeSingle(.RightParen);
|
|
|
|
|
|
|
|
// the return type is default void if a type
|
|
|
|
// is not provided
|
|
|
|
var return_type: Token = undefined;
|
|
|
|
if (self.check(.Identifier)) {
|
|
|
|
return_type = try self.consumeSingle(.Identifier);
|
|
|
|
} else {
|
|
|
|
return_type = try self.mkToken(.Identifier, "void", name.line);
|
|
|
|
}
|
|
|
|
|
|
|
|
var block_node = try self.parseBlock();
|
2019-09-20 16:54:52 +00:00
|
|
|
return try Node.mkFnDecl(
|
|
|
|
self.allocator,
|
|
|
|
name,
|
|
|
|
param_list,
|
|
|
|
return_type,
|
|
|
|
block_node.Block,
|
|
|
|
method,
|
|
|
|
);
|
2019-09-18 18:59:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/// parse the (v [mut] T) part of the method (defined here
|
|
|
|
/// as a premethod)
|
|
|
|
fn parsePreMethod(self: *@This()) !?*ast.MethodData {
|
|
|
|
_ = try self.consumeSingle(.LeftParen);
|
|
|
|
|
|
|
|
var mutable_ref: bool = false;
|
|
|
|
const variable = try self.consumeSingle(.Identifier);
|
|
|
|
|
|
|
|
if (self.check(.Mut)) {
|
|
|
|
_ = try self.consumeSingle(.Mut);
|
|
|
|
mutable_ref = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
const typ = try self.consumeSingle(.Identifier);
|
|
|
|
|
|
|
|
_ = try self.consumeSingle(.RightParen);
|
|
|
|
|
|
|
|
// create method data and assign the values we got into it
|
|
|
|
var method = try self.allocator.create(ast.MethodData);
|
|
|
|
method.* = ast.MethodData{
|
|
|
|
.variable = variable,
|
|
|
|
.typ = typ,
|
|
|
|
.mutable = mutable_ref,
|
|
|
|
};
|
|
|
|
|
|
|
|
return method;
|
|
|
|
}
|
|
|
|
|
|
|
|
fn parseConstDecl(self: *@This()) !*Node {
|
|
|
|
var consts = ast.ConstList.init(self.allocator);
|
|
|
|
errdefer consts.deinit();
|
|
|
|
|
2019-09-20 16:24:52 +00:00
|
|
|
self.setErrContext("const");
|
|
|
|
|
2019-09-18 18:59:11 +00:00
|
|
|
_ = try self.consumeSingle(.Const);
|
|
|
|
_ = try self.consumeSingle(.LeftParen);
|
|
|
|
|
|
|
|
while (self.peek().typ != .RightParen) {
|
|
|
|
const const_name = try self.consumeSingle(.Identifier);
|
2019-09-20 16:24:52 +00:00
|
|
|
self.setErrContext("const {}", const_name);
|
|
|
|
|
2019-09-18 18:59:11 +00:00
|
|
|
_ = try self.consumeSingle(.Equal);
|
|
|
|
|
|
|
|
// const declarations dont have type, a future type system must
|
|
|
|
// check the output type of the expression and assign it to the
|
|
|
|
// const later on.
|
|
|
|
|
|
|
|
var expr = try self.parseExpr();
|
|
|
|
try consts.append(ast.SingleConst{
|
|
|
|
.name = const_name,
|
|
|
|
.expr = expr,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
_ = try self.consumeSingle(.RightParen);
|
|
|
|
return self.mkConstDecl(consts);
|
|
|
|
}
|
|
|
|
|
|
|
|
fn parseStructDecl(self: *@This()) !*Node {
|
|
|
|
var fields = ast.FieldList.init(self.allocator);
|
|
|
|
errdefer fields.deinit();
|
2019-09-20 16:32:35 +00:00
|
|
|
self.setErrContext("struct");
|
2019-09-18 18:59:11 +00:00
|
|
|
|
|
|
|
_ = try self.consumeSingle(.Struct);
|
|
|
|
var name = try self.consumeSingle(.Identifier);
|
|
|
|
|
2019-09-20 16:24:52 +00:00
|
|
|
self.setErrContext("struct {}", name);
|
|
|
|
|
2019-09-18 18:59:11 +00:00
|
|
|
_ = try self.consumeSingle(.LeftBrace);
|
|
|
|
|
|
|
|
var field_state = FieldState{};
|
|
|
|
|
|
|
|
while (!self.check(.RightBrace)) {
|
|
|
|
try self.parseFieldModifiers(&field_state);
|
|
|
|
|
|
|
|
const field_name = try self.consumeSingle(.Identifier);
|
2019-09-20 16:24:52 +00:00
|
|
|
self.setErrContext("struct {} field {}", name, field_name);
|
|
|
|
|
2019-09-18 18:59:11 +00:00
|
|
|
const field_type = try self.consumeSingle(.Identifier);
|
|
|
|
|
|
|
|
// we could create a FieldState on the heap and copy our current
|
|
|
|
// field state into a StructField.state, but copying via this makes
|
|
|
|
// things so much nicer.
|
|
|
|
|
|
|
|
try fields.append(ast.StructField{
|
|
|
|
.name = field_name,
|
|
|
|
.typ = field_type,
|
|
|
|
|
|
|
|
.mutable = field_state.mutable,
|
|
|
|
.public = field_state.public,
|
|
|
|
.mutable_outside = field_state.mutable_outside,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
_ = try self.consumeSingle(.RightBrace);
|
|
|
|
|
|
|
|
return Node.mkStructDecl(self.allocator, name, fields);
|
|
|
|
}
|
|
|
|
|
|
|
|
fn parseFieldModifiers(self: *@This(), field_state: *FieldState) !void {
|
|
|
|
|
|
|
|
// there are five access modifiers:
|
|
|
|
// - none (private immutable)
|
|
|
|
// - mut (private mutable)
|
|
|
|
// - pub (public immutable)
|
|
|
|
// - pub mut (public mutable only in module)
|
|
|
|
// - pub mut mut (public mutable everywhere)
|
|
|
|
|
|
|
|
// this function takes care of that by changing the current FieldState
|
|
|
|
// to what the modifiers dictate.
|
|
|
|
switch (self.peek().typ) {
|
|
|
|
.Mut => {
|
|
|
|
// There are no oher modifiers that start with mut, so we
|
|
|
|
// can just go the way of marking it as mutable
|
|
|
|
_ = try self.consumeSingle(.Mut);
|
|
|
|
_ = try self.consumeSingle(.Colon);
|
|
|
|
|
|
|
|
field_state.mutable = true;
|
|
|
|
},
|
|
|
|
|
|
|
|
// 'pub', 'pub mut', and 'pub mut mut' are all handled here
|
|
|
|
.Pub => {
|
|
|
|
_ = try self.consumeSingle(.Pub);
|
|
|
|
field_state.public = true;
|
|
|
|
|
|
|
|
if (self.check(.Mut)) {
|
|
|
|
_ = try self.consumeSingle(.Mut);
|
|
|
|
|
|
|
|
field_state.mutable = true;
|
|
|
|
if (self.check(.Mut)) {
|
|
|
|
_ = try self.consumeSingle(.Mut);
|
|
|
|
field_state.mutable_outside = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
_ = try self.consumeSingle(.Colon);
|
|
|
|
},
|
|
|
|
|
|
|
|
// if it isn't mut or pub we're likely in an identifier, just
|
|
|
|
// ignore it.
|
|
|
|
else => return,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn parseEnumDecl(self: *@This()) !*Node {
|
|
|
|
var fields = ast.TokenList.init(self.allocator);
|
|
|
|
errdefer fields.deinit();
|
|
|
|
|
2019-09-20 16:24:52 +00:00
|
|
|
self.setErrContext("enum");
|
|
|
|
|
|
|
|
_ = try self.consumeSingle(.Enum);
|
2019-09-18 18:59:11 +00:00
|
|
|
const name = try self.consumeSingle(.Identifier);
|
|
|
|
|
2019-09-20 16:24:52 +00:00
|
|
|
self.setErrContext("enum {}", name);
|
|
|
|
|
2019-09-18 18:59:11 +00:00
|
|
|
_ = try self.consumeSingle(.LeftBrace);
|
|
|
|
|
|
|
|
while (!self.check(.RightBrace)) {
|
|
|
|
try fields.append(try self.consumeSingle(.Identifier));
|
|
|
|
}
|
|
|
|
|
|
|
|
_ = try self.consumeSingle(.RightBrace);
|
|
|
|
|
|
|
|
return try Node.mkEnumDecl(self.allocator, name, fields);
|
|
|
|
}
|
|
|
|
|
|
|
|
fn parseTopDecl(self: *@This()) !*Node {
|
2019-09-20 16:16:55 +00:00
|
|
|
self.setErrContext(null);
|
|
|
|
|
2019-09-18 18:59:11 +00:00
|
|
|
return switch (self.peek().typ) {
|
|
|
|
.Fn => try self.parseFnDecl(),
|
|
|
|
.Const => try self.parseConstDecl(),
|
|
|
|
.Struct => try self.parseStructDecl(),
|
|
|
|
.Enum => try self.parseEnumDecl(),
|
|
|
|
|
|
|
|
else => |typ| blk: {
|
2019-09-20 15:22:46 +00:00
|
|
|
return self.doError("expected Fn, Const, Struct, got {}\n", typ);
|
2019-09-18 18:59:11 +00:00
|
|
|
},
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
fn parseBlockInternal(self: *@This(), comptime T: type) !T {
|
|
|
|
var stmts = T.init(self.allocator);
|
|
|
|
errdefer stmts.deinit();
|
|
|
|
|
|
|
|
_ = try self.consumeSingle(.LeftBrace);
|
|
|
|
|
|
|
|
while (self.peek().typ != .RightBrace) {
|
|
|
|
var stmt = try self.parseStmt();
|
|
|
|
printer.printNode(try self.mkStmt(stmt), 0);
|
2019-09-24 17:12:37 +00:00
|
|
|
if (self.check(.Semicolon))
|
|
|
|
_ = try self.consumeSingle(.Semicolon);
|
2019-09-18 19:53:44 +00:00
|
|
|
try stmts.append(stmt.*);
|
2019-09-18 18:59:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
_ = try self.consumeSingle(.RightBrace);
|
|
|
|
|
|
|
|
return stmts;
|
|
|
|
}
|
|
|
|
|
|
|
|
fn parseStmt(self: *@This()) anyerror!*Stmt {
|
|
|
|
return switch (self.peek().typ) {
|
2019-09-26 01:22:13 +00:00
|
|
|
.Var => try self.parseVarDecl(),
|
2019-09-18 18:59:11 +00:00
|
|
|
.If => try self.parseIfStmt(),
|
|
|
|
.Loop => try self.parseLoop(),
|
|
|
|
.For => try self.parseForStmt(),
|
|
|
|
.Println => try self.parsePrintln(),
|
|
|
|
.Return => try self.parseReturn(),
|
|
|
|
else => try self.parseStmtExpr(),
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2019-09-26 01:22:13 +00:00
|
|
|
fn parseVarDecl(self: *@This()) !*Stmt {
|
|
|
|
_ = try self.consumeSingle(.Var);
|
|
|
|
var name = try self.consumeSingle(.Identifier);
|
|
|
|
_ = try self.consumeSingle(.Equal);
|
|
|
|
var value = try self.parseExpr();
|
|
|
|
return try Stmt.mkVarDecl(self.allocator, name, value);
|
|
|
|
}
|
|
|
|
|
2019-09-18 18:59:11 +00:00
|
|
|
/// Parse a list of statements.
|
|
|
|
fn parseBlock(self: *@This()) !*Node {
|
|
|
|
var stmts = try self.parseBlockInternal(ast.StmtList);
|
|
|
|
return try self.mkBlock(stmts);
|
|
|
|
}
|
|
|
|
|
|
|
|
/// parse blocks inside statements
|
|
|
|
fn parseStmtBlock(self: *@This()) !ast.Block {
|
|
|
|
var block = try self.parseBlockInternal(ast.Block);
|
|
|
|
return block;
|
|
|
|
}
|
|
|
|
|
|
|
|
fn parseIfStmt(self: *@This()) !*Stmt {
|
|
|
|
_ = try self.consumeSingle(.If);
|
2019-09-24 17:12:37 +00:00
|
|
|
_ = try self.consumeSingle(.LeftParen);
|
2019-09-18 18:59:11 +00:00
|
|
|
var condition = try self.parseExpr();
|
2019-09-24 17:12:37 +00:00
|
|
|
_ = try self.consumeSingle(.RightParen);
|
2019-09-18 18:59:11 +00:00
|
|
|
|
|
|
|
const then_branch = try self.parseStmtBlock();
|
|
|
|
|
|
|
|
var else_branch: ?ast.Block = null;
|
|
|
|
|
|
|
|
if (self.check(.Else)) {
|
|
|
|
_ = try self.consumeSingle(.Else);
|
|
|
|
else_branch = try self.parseStmtBlock();
|
|
|
|
}
|
|
|
|
|
|
|
|
return try Stmt.mkIfStmt(
|
|
|
|
self.allocator,
|
|
|
|
condition,
|
|
|
|
then_branch,
|
|
|
|
else_branch,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
fn parseForStmt(self: *@This()) !*Stmt {
|
|
|
|
// There are two types of for in vig's V subset:
|
|
|
|
// - for x in y
|
|
|
|
// - for idx, x in y
|
|
|
|
_ = try self.consumeSingle(.For);
|
|
|
|
|
|
|
|
var index_var: ?Token = null;
|
|
|
|
var value_var: Token = undefined;
|
|
|
|
|
|
|
|
const subject_1 = try self.consumeSingle(.Identifier);
|
|
|
|
|
|
|
|
if (self.check(.Comma)) {
|
|
|
|
_ = try self.consumeSingle(.Comma);
|
|
|
|
|
|
|
|
const subject_2 = try self.consumeSingle(.Identifier);
|
|
|
|
index_var = subject_1;
|
|
|
|
value_var = subject_2;
|
|
|
|
} else {
|
|
|
|
value_var = subject_1;
|
|
|
|
}
|
|
|
|
|
|
|
|
_ = try self.consumeSingle(.In);
|
|
|
|
|
|
|
|
// MUST be identifier
|
|
|
|
var array = try self.consumeSingle(.Identifier);
|
|
|
|
var block = try self.parseStmtBlock();
|
|
|
|
|
|
|
|
return try Stmt.mkFor(
|
|
|
|
self.allocator,
|
|
|
|
index_var,
|
|
|
|
value_var,
|
|
|
|
array,
|
|
|
|
block,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
fn parseLoop(self: *@This()) !*Stmt {
|
|
|
|
_ = try self.consumeSingle(.Loop);
|
|
|
|
var expr: ?*Expr = null;
|
|
|
|
var body: ast.Block = undefined;
|
|
|
|
|
|
|
|
// 'loop {' = infinite loop
|
|
|
|
if (self.check(.LeftBrace)) {
|
|
|
|
body = try self.parseStmtBlock();
|
|
|
|
} else {
|
|
|
|
expr = try self.parseExpr();
|
|
|
|
body = try self.parseStmtBlock();
|
|
|
|
}
|
|
|
|
|
|
|
|
return try Stmt.mkLoop(self.allocator, expr, body);
|
|
|
|
}
|
|
|
|
|
|
|
|
fn parseReturn(self: *@This()) !*Stmt {
|
|
|
|
const tok = try self.consumeSingle(.Return);
|
2019-09-24 21:49:35 +00:00
|
|
|
// TODO if (self.check(.Semicolon)) insert a void literal?
|
2019-09-18 18:59:11 +00:00
|
|
|
const expr = try self.parseExpr();
|
|
|
|
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();
|
|
|
|
_ = try self.consumeSingle(.RightParen);
|
|
|
|
|
|
|
|
return try Stmt.mkPrintln(self.allocator, expr);
|
|
|
|
}
|
|
|
|
|
|
|
|
fn parseStmtExpr(self: *@This()) !*Stmt {
|
|
|
|
var expr = try self.parseExpr();
|
|
|
|
return try self.mkStmtExpr(expr);
|
|
|
|
}
|
|
|
|
|
|
|
|
fn parseExpr(self: *@This()) anyerror!*Expr {
|
|
|
|
return try self.parseAssignment();
|
|
|
|
}
|
|
|
|
|
|
|
|
fn parseAssignment(self: *@This()) anyerror!*Expr {
|
|
|
|
// there can be two assignments coming out of this function:
|
2019-09-26 01:22:13 +00:00
|
|
|
// - an assignment to a variable with =
|
|
|
|
// - an update to a variable with +=, -=
|
2019-09-18 18:59:11 +00:00
|
|
|
|
|
|
|
// one is a statement, other is an expression. since the normal result
|
|
|
|
// of this is an Expr, we wrap variable assignments in an Expr as well.
|
|
|
|
var mutable: bool = false;
|
|
|
|
|
|
|
|
if (self.check(.Mut)) {
|
|
|
|
_ = try self.consumeSingle(.Mut);
|
|
|
|
mutable = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
var expr = try self.parseOr();
|
|
|
|
|
|
|
|
if (self.compareAnyOf(&[_]TokenType{
|
2019-09-26 01:22:13 +00:00
|
|
|
.Equal, .PlusEqual, .MinusEqual, .StarEqual,
|
2019-09-18 18:59:11 +00:00
|
|
|
.SlashEqual,
|
|
|
|
})) {
|
|
|
|
return try self.finishAssignment(expr, mutable);
|
|
|
|
}
|
|
|
|
|
|
|
|
return expr;
|
|
|
|
}
|
|
|
|
|
|
|
|
fn finishAssignment(self: *@This(), expr: *Expr, mutable: bool) !*Expr {
|
2019-09-24 02:18:31 +00:00
|
|
|
var op_tok = self.peek();
|
2019-09-18 18:59:11 +00:00
|
|
|
_ = try self.nextToken();
|
|
|
|
|
2019-09-24 02:18:31 +00:00
|
|
|
var value = try self.parseAssignment();
|
2019-09-18 18:59:11 +00:00
|
|
|
|
2019-09-24 02:18:31 +00:00
|
|
|
// expr can be a (Variable|Set)
|
2019-09-26 01:22:13 +00:00
|
|
|
// op_tok can be one of two categories:
|
2019-09-24 02:18:31 +00:00
|
|
|
// - Equal, parses to (Assign|Set)
|
|
|
|
// - Inplace (+=, -=, *=, /=), parses to (Assign|Set)
|
|
|
|
|
|
|
|
// however, when dealing with inplace operators, we want those to parse
|
|
|
|
// to an assign/set with a binary expression with the undelying operator
|
|
|
|
// instead of the infix operator.
|
|
|
|
// e.g 'x += 1' parses down to 'x = x + 1'
|
|
|
|
|
|
|
|
// TODO do we add %=?
|
|
|
|
const binop: ?ast.BinaryOperator = switch (op_tok.typ) {
|
|
|
|
.PlusEqual => ast.BinaryOperator.Add,
|
|
|
|
.MinusEqual => .Sub,
|
|
|
|
.StarEqual => .Mul,
|
|
|
|
.SlashEqual => .Div,
|
|
|
|
else => null,
|
2019-09-18 18:59:11 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
switch (expr.*) {
|
|
|
|
.Variable => {
|
2019-09-24 02:18:31 +00:00
|
|
|
switch (op_tok.typ) {
|
2019-09-18 18:59:11 +00:00
|
|
|
.Equal => return try self.mkAssign(expr.Variable, value),
|
|
|
|
|
|
|
|
.PlusEqual, .MinusEqual, .StarEqual, .SlashEqual => {
|
|
|
|
return try self.mkAssign(
|
|
|
|
expr.Variable,
|
2019-09-24 02:18:31 +00:00
|
|
|
try self.mkBinary(expr, binop.?, value),
|
2019-09-18 18:59:11 +00:00
|
|
|
);
|
|
|
|
},
|
|
|
|
|
|
|
|
else => unreachable,
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
.Get => |get| {
|
2019-09-24 02:18:31 +00:00
|
|
|
switch (op_tok.typ) {
|
2019-09-26 01:22:13 +00:00
|
|
|
// TODO remove .ColonEqual from language
|
2019-09-18 18:59:11 +00:00
|
|
|
.ColonEqual => {
|
2019-09-20 15:22:46 +00:00
|
|
|
return self.doError("can not initialize struct field");
|
2019-09-18 18:59:11 +00:00
|
|
|
},
|
|
|
|
|
2019-09-26 21:03:39 +00:00
|
|
|
.Equal => return try self.mkSet(get.target, get.name, value),
|
2019-09-18 18:59:11 +00:00
|
|
|
|
|
|
|
.PlusEqual, .MinusEqual, .StarEqual, .SlashEqual => {
|
|
|
|
return try self.mkSet(
|
2019-09-26 21:03:39 +00:00
|
|
|
get.target,
|
2019-09-18 18:59:11 +00:00
|
|
|
get.name,
|
2019-09-24 02:18:31 +00:00
|
|
|
try self.mkBinary(expr, binop.?, value),
|
2019-09-18 18:59:11 +00:00
|
|
|
);
|
|
|
|
},
|
|
|
|
|
|
|
|
else => unreachable,
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
else => |expr_typ| {
|
2019-09-20 15:22:46 +00:00
|
|
|
return self.doError("Invalid assignment target {}", expr_typ);
|
2019-09-18 18:59:11 +00:00
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn parseOr(self: *@This()) !*Expr {
|
|
|
|
var expr = try self.parseAnd();
|
|
|
|
|
|
|
|
while (self.check(.Or)) {
|
|
|
|
var op = self.peek();
|
|
|
|
_ = try self.nextToken();
|
|
|
|
|
|
|
|
var right = try self.parseAnd();
|
2019-09-24 16:23:23 +00:00
|
|
|
expr = try self.mkBinary(expr, try toBinaryOperator(op), right);
|
2019-09-18 18:59:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return expr;
|
|
|
|
}
|
|
|
|
|
|
|
|
fn parseAnd(self: *@This()) !*Expr {
|
|
|
|
var expr = try self.parseEquality();
|
|
|
|
|
|
|
|
while (self.check(.And)) {
|
|
|
|
var op = self.peek();
|
|
|
|
_ = try self.nextToken();
|
|
|
|
|
|
|
|
var right = try self.parseEquality();
|
2019-09-24 16:23:23 +00:00
|
|
|
expr = try self.mkBinary(expr, try toBinaryOperator(op), right);
|
2019-09-18 18:59:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return expr;
|
|
|
|
}
|
|
|
|
|
|
|
|
fn parseEquality(self: *@This()) !*Expr {
|
|
|
|
var expr = try self.parseComparison();
|
|
|
|
|
|
|
|
while (self.check(.EqualEqual)) {
|
|
|
|
var op = self.peek();
|
|
|
|
_ = try self.nextToken();
|
|
|
|
|
|
|
|
var right = try self.parseComparison();
|
2019-09-24 02:18:31 +00:00
|
|
|
expr = try self.mkBinary(expr, .Equal, right);
|
2019-09-18 18:59:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return expr;
|
|
|
|
}
|
|
|
|
|
|
|
|
fn parseComparison(self: *@This()) !*Expr {
|
|
|
|
var expr = try self.parseAddition();
|
|
|
|
|
|
|
|
while (self.compareAnyOf(&[_]TokenType{
|
|
|
|
.Greater,
|
|
|
|
.GreaterEqual,
|
|
|
|
.Less,
|
|
|
|
.LessEqual,
|
|
|
|
})) {
|
|
|
|
var op = self.peek();
|
|
|
|
_ = try self.nextToken();
|
|
|
|
|
|
|
|
var right = try self.parseAddition();
|
2019-09-24 02:18:31 +00:00
|
|
|
expr = try self.mkBinary(expr, try toBinaryOperator(op), right);
|
2019-09-18 18:59:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return expr;
|
|
|
|
}
|
|
|
|
|
|
|
|
fn parseAddition(self: *@This()) !*Expr {
|
|
|
|
var expr = try self.parseMultiplication();
|
|
|
|
|
|
|
|
while (self.compareAnyOf(&[_]TokenType{
|
|
|
|
.Minus, .Plus,
|
|
|
|
})) {
|
|
|
|
var op = self.peek();
|
|
|
|
_ = try self.nextToken();
|
|
|
|
|
|
|
|
var right = try self.parseMultiplication();
|
2019-09-24 02:18:31 +00:00
|
|
|
expr = try self.mkBinary(expr, try toBinaryOperator(op), right);
|
2019-09-18 18:59:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return expr;
|
|
|
|
}
|
|
|
|
|
|
|
|
fn parseMultiplication(self: *@This()) !*Expr {
|
|
|
|
var expr = try self.parseUnary();
|
|
|
|
|
|
|
|
while (self.compareAnyOf(&[_]TokenType{
|
|
|
|
.Star, .Slash,
|
|
|
|
})) {
|
|
|
|
var op = self.peek();
|
|
|
|
_ = try self.nextToken();
|
|
|
|
var right = try self.parseUnary();
|
|
|
|
|
2019-09-24 02:18:31 +00:00
|
|
|
expr = try self.mkBinary(expr, try toBinaryOperator(op), right);
|
2019-09-18 18:59:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return expr;
|
|
|
|
}
|
|
|
|
|
|
|
|
fn parseUnary(self: *@This()) anyerror!*Expr {
|
|
|
|
if (self.compareAnyOf(&[_]TokenType{ .Bang, .Minus })) {
|
|
|
|
var op = self.previous();
|
|
|
|
var right = try self.parseUnary();
|
|
|
|
|
2019-09-25 17:34:32 +00:00
|
|
|
return try self.mkUnary(try toUnaryOperator(op), right);
|
2019-09-18 18:59:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
var expr = try self.parseCall();
|
|
|
|
return expr;
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Parse either:
|
|
|
|
/// - A function call
|
|
|
|
/// - A struct initialization (Point.{...})
|
|
|
|
/// - A struct Get expression (p.x)
|
|
|
|
fn parseCall(self: *@This()) !*Expr {
|
|
|
|
var expr = try self.parsePrimary();
|
|
|
|
|
|
|
|
while (true) {
|
|
|
|
if (self.check(.LeftParen)) {
|
|
|
|
_ = try self.consumeSingle(.LeftParen);
|
|
|
|
expr = try self.finishCall(expr);
|
|
|
|
} else if (self.check(.Dot)) {
|
|
|
|
_ = try self.consumeSingle(.Dot);
|
|
|
|
|
|
|
|
if (self.check(.LeftBrace)) {
|
|
|
|
_ = try self.consumeSingle(.LeftBrace);
|
|
|
|
expr = try self.finishStructVal(expr);
|
|
|
|
} else {
|
|
|
|
var name = try self.consume(
|
|
|
|
.Identifier,
|
|
|
|
"Expect property name after '.'",
|
|
|
|
);
|
|
|
|
|
|
|
|
expr = try self.mkGet(expr, name);
|
|
|
|
}
|
|
|
|
} else {
|
2019-09-20 16:32:35 +00:00
|
|
|
//return self.tokenError(self.peek(), "Expect expression.");
|
|
|
|
break;
|
2019-09-18 18:59:11 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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();
|
2019-09-18 19:53:44 +00:00
|
|
|
try args.append(arg.*);
|
2019-09-18 18:59:11 +00:00
|
|
|
|
|
|
|
while (self.check(.Comma)) {
|
|
|
|
_ = try self.consumeSingle(.Comma);
|
|
|
|
|
|
|
|
arg = try self.parseExpr();
|
2019-09-18 19:53:44 +00:00
|
|
|
try args.append(arg.*);
|
2019-09-18 18:59:11 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
var paren = try self.consume(.RightParen, "Expected ')' after arguments");
|
|
|
|
|
|
|
|
return self.mkCall(callee, paren, args);
|
|
|
|
}
|
|
|
|
|
|
|
|
fn finishStructVal(self: *@This(), expr: *Expr) !*Expr {
|
|
|
|
// <expr>{a: 10 b: 10}
|
|
|
|
// for this to work properly, <expr> must be Variable, since its a type.
|
|
|
|
if (ast.ExprType(expr.*) != .Variable) {
|
2019-09-20 15:22:46 +00:00
|
|
|
return self.doError("Expected variable for struct type, got {}", ast.ExprType(expr.*));
|
2019-09-18 18:59:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
var inits = ast.StructInitList.init(self.allocator);
|
|
|
|
errdefer inits.deinit();
|
|
|
|
|
|
|
|
while (!self.check(.RightBrace)) {
|
|
|
|
const field_name = try self.consumeSingle(.Identifier);
|
|
|
|
// TODO check .Comma for the quick initialization {val,val,val}
|
|
|
|
|
|
|
|
_ = try self.consumeSingle(.Colon);
|
|
|
|
const field_value = try self.parseExpr();
|
|
|
|
|
|
|
|
try inits.append(ast.StructInit{
|
|
|
|
.field = field_name,
|
|
|
|
.expr = field_value,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
_ = try self.consumeSingle(.RightBrace);
|
|
|
|
|
|
|
|
return try self.mkStructExpr(expr.Variable, inits);
|
|
|
|
}
|
|
|
|
|
|
|
|
fn parsePrimary(self: *@This()) !*Expr {
|
|
|
|
const curtype = self.peek().typ;
|
|
|
|
const lexeme = self.peek().lexeme;
|
|
|
|
|
|
|
|
var expr = switch (curtype) {
|
|
|
|
.False => try self.mkBool(false),
|
|
|
|
.True => try self.mkBool(true),
|
|
|
|
|
2019-09-27 15:07:20 +00:00
|
|
|
.Integer => blk: {
|
|
|
|
// hacky, but it works. the old approach was doing
|
|
|
|
// parseInt(i64) on the catch block of parseInt(i32)
|
|
|
|
var i32_num_opt: ?i32 = std.fmt.parseInt(i32, lexeme, 10) catch null;
|
|
|
|
var i64_num: i64 = std.fmt.parseInt(i64, lexeme, 10) catch |err| {
|
|
|
|
return self.doError(
|
|
|
|
"Invalid integer (not 32bit or 64bit) '{}': {}",
|
|
|
|
lexeme,
|
|
|
|
err,
|
|
|
|
);
|
|
|
|
};
|
|
|
|
|
|
|
|
if (i32_num_opt) |i32_num| {
|
|
|
|
break :blk try self.mkInt32(i32_num);
|
|
|
|
} else {
|
|
|
|
break :blk try self.mkInt64(i64_num);
|
|
|
|
}
|
|
|
|
|
|
|
|
// old one
|
|
|
|
|
|
|
|
//break :blk try self.mkInt32(std.fmt.parseInt(i32, lexeme, 10) catch |err32| {
|
|
|
|
// break :blk try self.mkInt64(std.fmt.parseInt(i64, lexeme, 10) catch |err64| {
|
|
|
|
// return self.doError(
|
|
|
|
// "Invalid integer (not 32bit or 64bit) '{}': {}",
|
|
|
|
// lexeme,
|
|
|
|
// err64,
|
|
|
|
// );
|
|
|
|
// });
|
|
|
|
//});
|
|
|
|
},
|
|
|
|
|
|
|
|
// TODO std.fmt.parseFloat
|
2019-09-18 18:59:11 +00:00
|
|
|
.Float => try self.mkFloat(lexeme),
|
2019-09-27 15:07:20 +00:00
|
|
|
|
2019-09-18 18:59:11 +00:00
|
|
|
.String => try self.mkString(lexeme),
|
|
|
|
.Identifier => try self.mkVariable(self.peek()),
|
|
|
|
|
|
|
|
// type checking for arrays happens at later stages
|
|
|
|
.LeftSquare => {
|
|
|
|
_ = try self.consumeSingle(.LeftSquare);
|
|
|
|
|
|
|
|
var exprs = ast.ExprList.init(self.allocator);
|
|
|
|
errdefer exprs.deinit();
|
|
|
|
|
|
|
|
while (!self.check(.RightSquare)) {
|
2019-09-18 19:53:44 +00:00
|
|
|
try exprs.append((try self.parseExpr()).*);
|
2019-09-18 18:59:11 +00:00
|
|
|
if (self.check(.Comma)) _ = try self.consumeSingle(.Comma);
|
|
|
|
}
|
|
|
|
|
|
|
|
_ = try self.consumeSingle(.RightSquare);
|
|
|
|
return try self.mkArray(exprs);
|
|
|
|
},
|
|
|
|
|
|
|
|
.LeftParen => {
|
|
|
|
_ = try self.nextToken();
|
|
|
|
var expr = try self.parseExpr();
|
|
|
|
_ = try self.consume(.RightParen, "Expected ')' after expression");
|
|
|
|
|
|
|
|
// for groupings, we don't want to skip tokens as we already
|
|
|
|
// consumed RightParen.
|
|
|
|
return try self.mkGrouping(expr);
|
|
|
|
},
|
|
|
|
|
|
|
|
else => blk: {
|
2019-09-20 15:22:46 +00:00
|
|
|
return self.doError("expected literal, got {}", curtype);
|
2019-09-18 18:59:11 +00:00
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
_ = try self.nextToken();
|
|
|
|
return expr;
|
|
|
|
}
|
|
|
|
};
|