Compare commits
No commits in common. "b0e123f83ae54d5c58511333de11646817962a7c" and "f70f2ea1aecf43eeac2f61cc50d3b526ab5dde89" have entirely different histories.
b0e123f83a
...
f70f2ea1ae
7 changed files with 307 additions and 61 deletions
|
@ -1,3 +0,0 @@
|
||||||
fn main() {
|
|
||||||
println("hello world!");
|
|
||||||
}
|
|
108
src/ast.zig
108
src/ast.zig
|
@ -1,25 +1,103 @@
|
||||||
const std = @import("std");
|
|
||||||
const tokens = @import("tokens.zig");
|
const tokens = @import("tokens.zig");
|
||||||
const Token = tokens.Token;
|
const Token = tokens.Token;
|
||||||
|
|
||||||
pub const NodeList = std.ArrayList(*Node);
|
pub const ExprType = enum {
|
||||||
|
Unary,
|
||||||
pub const NodeType = enum {
|
Binary,
|
||||||
Root,
|
Grouping,
|
||||||
FnDecl,
|
Number,
|
||||||
|
Bool,
|
||||||
|
Nil,
|
||||||
|
String,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const FnDecl = struct {
|
pub const Expr = union(ExprType) {
|
||||||
func_name: []const u8,
|
Unary: UnaryExpr,
|
||||||
|
Binary: BinaryExpr,
|
||||||
|
Grouping: Grouping,
|
||||||
|
Number: Number,
|
||||||
|
Bool: Bool,
|
||||||
|
Nil: Nil,
|
||||||
|
String: String,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const Node = union(NodeType) {
|
pub const UnaryExpr = struct {
|
||||||
Root: NodeList,
|
operator: Token,
|
||||||
FnDecl: FnDecl,
|
right: *Expr,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn mkRoot(allocator: *std.mem.Allocator) !*Node {
|
pub fn mkUnary(operator: Token, right: *Expr) Expr {
|
||||||
var node = try allocator.create(Node);
|
return Expr{
|
||||||
node.* = Node{ .Root = NodeList.init(allocator) };
|
.Unary = UnaryExpr{
|
||||||
return node;
|
.operator = operator,
|
||||||
|
.right = right,
|
||||||
|
},
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub const BinaryExpr = struct {
|
||||||
|
left: *Expr,
|
||||||
|
operator: Token,
|
||||||
|
right: *Expr,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn mkBinary(left: *Expr, operator: Token, right: *Expr) Expr {
|
||||||
|
return Expr{
|
||||||
|
.Binary = BinaryExpr{
|
||||||
|
.left = left,
|
||||||
|
.operator = operator,
|
||||||
|
.right = right,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const Grouping = struct {
|
||||||
|
expression: *Expr,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn mkGrouping(expression: *Expr) Expr {
|
||||||
|
return Expr{
|
||||||
|
.Grouping = Grouping{
|
||||||
|
.expression = expression,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Represents the default number literals in V.
|
||||||
|
pub const NumberType = enum {
|
||||||
|
Integer32,
|
||||||
|
Integer64,
|
||||||
|
Unsigned32,
|
||||||
|
Unsigned64,
|
||||||
|
Float32,
|
||||||
|
Float64,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// "translation" of V number types to Zig number types for nicer
|
||||||
|
/// representation.
|
||||||
|
pub const Number = union(NumberType) {
|
||||||
|
Integer32: i32,
|
||||||
|
Integer64: i64,
|
||||||
|
Unsigned32: u32,
|
||||||
|
Unsigned64: u64,
|
||||||
|
Float32: f32,
|
||||||
|
Float64: f64,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn mkNum(comptime T: type, num: T) Expr {
|
||||||
|
var expr = switch (T) {
|
||||||
|
i32 => Expr{ .Number = Number{ .Integer32 = num } },
|
||||||
|
i64 => Expr{ .Number = Number{ .Integer64 = num } },
|
||||||
|
u32 => Expr{ .Number = Number{ .Unsigned32 = num } },
|
||||||
|
u64 => Expr{ .Number = Number{ .Unsigned64 = num } },
|
||||||
|
f32 => Expr{ .Number = Number{ .Float32 = num } },
|
||||||
|
f64 => Expr{ .Number = Number{ .Float64 = num } },
|
||||||
|
else => unreachable,
|
||||||
|
};
|
||||||
|
|
||||||
|
return expr;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const Bool = bool;
|
||||||
|
pub const Nil = void;
|
||||||
|
pub const String = []u8;
|
||||||
|
|
32
src/ast_printer.zig
Normal file
32
src/ast_printer.zig
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
const std = @import("std");
|
||||||
|
const ast = @import("ast.zig");
|
||||||
|
|
||||||
|
fn parenthesize(name: []const u8, exprs: []*ast.Expr) void {
|
||||||
|
std.debug.warn("({}", name);
|
||||||
|
|
||||||
|
for (exprs) |expr| {
|
||||||
|
std.debug.warn(" ");
|
||||||
|
printAst(expr);
|
||||||
|
}
|
||||||
|
|
||||||
|
std.debug.warn(")");
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn printAst(ast_expr: *ast.Expr) void {
|
||||||
|
switch (ast_expr.*) {
|
||||||
|
.Binary => |expr| parenthesize(expr.operator.lexeme, &[]*ast.Expr{ expr.left, expr.right }),
|
||||||
|
.Grouping => |expr| parenthesize("group", &[]*ast.Expr{expr.expression}),
|
||||||
|
.Unary => |expr| parenthesize(expr.operator.lexeme, &[]*ast.Expr{expr.right}),
|
||||||
|
.Number => |ast_num| {
|
||||||
|
switch (ast_num) {
|
||||||
|
.Integer32 => |num| std.debug.warn("{}", num),
|
||||||
|
.Integer64 => |num| std.debug.warn("{}", num),
|
||||||
|
.Unsigned32 => |num| std.debug.warn("{}", num),
|
||||||
|
.Unsigned64 => |num| std.debug.warn("{}", num),
|
||||||
|
.Float32 => |num| std.debug.warn("{}", num),
|
||||||
|
.Float64 => |num| std.debug.warn("{}", num),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
else => unreachable,
|
||||||
|
}
|
||||||
|
}
|
|
@ -67,7 +67,8 @@ fn runPrompt(allocator: *Allocator) !void {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn main() anyerror!void {
|
pub fn main() anyerror!void {
|
||||||
var arena = std.heap.ArenaAllocator.init(std.heap.direct_allocator);
|
var da = std.heap.DirectAllocator.init();
|
||||||
|
var arena = std.heap.ArenaAllocator.init(&da.allocator);
|
||||||
defer arena.deinit();
|
defer arena.deinit();
|
||||||
var allocator = &arena.allocator;
|
var allocator = &arena.allocator;
|
||||||
|
|
||||||
|
|
171
src/parser.zig
171
src/parser.zig
|
@ -10,7 +10,8 @@ const Scanner = scanners.Scanner;
|
||||||
const Token = tokens.Token;
|
const Token = tokens.Token;
|
||||||
const TokenType = tokens.TokenType;
|
const TokenType = tokens.TokenType;
|
||||||
const Result = main.Result;
|
const Result = main.Result;
|
||||||
const Node = ast.Node;
|
|
||||||
|
const Expr = ast.Expr;
|
||||||
|
|
||||||
pub const Parser = struct {
|
pub const Parser = struct {
|
||||||
allocator: *Allocator,
|
allocator: *Allocator,
|
||||||
|
@ -23,7 +24,7 @@ pub const Parser = struct {
|
||||||
return Parser{ .allocator = allocator, .scanner = scanner };
|
return Parser{ .allocator = allocator, .scanner = scanner };
|
||||||
}
|
}
|
||||||
|
|
||||||
fn doError(self: *Parser, comptime fmt: []const u8, args: ...) Result!void {
|
fn doError(self: *Parser, comptime fmt: []const u8, args: ...) !void {
|
||||||
std.debug.warn("parser error at line {}\n\t", self.scanner.line);
|
std.debug.warn("parser error at line {}\n\t", self.scanner.line);
|
||||||
std.debug.warn(fmt, args);
|
std.debug.warn(fmt, args);
|
||||||
std.debug.warn("\n");
|
std.debug.warn("\n");
|
||||||
|
@ -49,6 +50,20 @@ pub const Parser = struct {
|
||||||
return Result.CompileError;
|
return Result.CompileError;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn synchronize(self: *Parser) void {
|
||||||
|
_ = self.advance();
|
||||||
|
while (!self.isAtEnd()) {
|
||||||
|
if (self.previous().ttype == .Semicolon) return;
|
||||||
|
|
||||||
|
switch (self.peek().ttype) {
|
||||||
|
.Struct, .Fn, .For, .If, .Return => return,
|
||||||
|
else => {},
|
||||||
|
}
|
||||||
|
|
||||||
|
_ = self.advance();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn isAtEnd(self: *Parser) bool {
|
fn isAtEnd(self: *Parser) bool {
|
||||||
return self.peek().ttype == .EOF;
|
return self.peek().ttype == .EOF;
|
||||||
}
|
}
|
||||||
|
@ -90,50 +105,150 @@ pub const Parser = struct {
|
||||||
return Result.CompileError;
|
return Result.CompileError;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn mkFnDecl(self: *Parser, name: []const u8) !*ast.Node {
|
fn equality(self: *Parser) !Expr {
|
||||||
var node = try self.allocator.create(Node.FnDecl);
|
var expr = try self.comparison();
|
||||||
node.* = Node.FnDecl{ .name = name };
|
|
||||||
return node;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn functionDecl(self: *Parser) !*ast.Node {
|
while (self.match(&[]TokenType{ TokenType.BangEqual, TokenType.EqualEqual })) {
|
||||||
// get the name
|
var operator = self.previous();
|
||||||
var name = try self.consume(.Identifier, "expected function name");
|
var right = try self.comparison();
|
||||||
return try self.mkFnDecl(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn processToken(self: *Parser, token: Token) Result!ast.Node {
|
expr = ast.mkBinary(&expr, operator, &right);
|
||||||
switch (token.ttype) {
|
|
||||||
//.Fn => try self.functionDecl(),
|
|
||||||
else => blk: {
|
|
||||||
try self.doError("TODO handle {}\n", token.ttype);
|
|
||||||
return Result.CompileError;
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return expr;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse(self: *Parser) !*ast.Node {
|
fn comparison(self: *Parser) !Expr {
|
||||||
|
var expr = try self.addition();
|
||||||
|
|
||||||
|
while (self.match(&[]TokenType{
|
||||||
|
TokenType.Greater,
|
||||||
|
TokenType.GreaterEqual,
|
||||||
|
TokenType.Less,
|
||||||
|
TokenType.LessEqual,
|
||||||
|
})) {
|
||||||
|
var operator = self.previous();
|
||||||
|
var right = try self.addition();
|
||||||
|
expr = ast.mkBinary(&expr, operator, &right);
|
||||||
|
}
|
||||||
|
|
||||||
|
return expr;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn addition(self: *Parser) !Expr {
|
||||||
|
var expr = try self.multiplication();
|
||||||
|
|
||||||
|
while (self.match(&[]TokenType{ TokenType.Minus, TokenType.Plus })) {
|
||||||
|
var operator = self.previous();
|
||||||
|
var right = try self.multiplication();
|
||||||
|
expr = ast.mkBinary(&expr, operator, &right);
|
||||||
|
}
|
||||||
|
|
||||||
|
return expr;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn multiplication(self: *Parser) anyerror!Expr {
|
||||||
|
var expr = try self.unary();
|
||||||
|
|
||||||
|
while (self.match(&[]TokenType{ TokenType.Slash, TokenType.Star })) {
|
||||||
|
var operator = self.previous();
|
||||||
|
var right = try self.unary();
|
||||||
|
expr = ast.mkBinary(&expr, operator, &right);
|
||||||
|
}
|
||||||
|
|
||||||
|
return expr;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn unary(self: *Parser) anyerror!Expr {
|
||||||
|
if (self.match(&[]TokenType{ TokenType.Bang, TokenType.Minus })) {
|
||||||
|
var operator = self.previous();
|
||||||
|
var right = try self.unary();
|
||||||
|
return ast.mkUnary(operator, &right);
|
||||||
|
}
|
||||||
|
|
||||||
|
return try self.primary();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn doInt(self: *Parser) !Expr {
|
||||||
|
var token = self.previous();
|
||||||
|
|
||||||
|
// try to parse it as an i32 first, if that fails, do i64.
|
||||||
|
|
||||||
|
var num_32 = std.fmt.parseInt(i32, token.lexeme, 10) catch |pi_err| {
|
||||||
|
if (pi_err == error.Overflow) {
|
||||||
|
var num_64 = std.fmt.parseInt(i64, token.lexeme, 10) catch |pi_err2| {
|
||||||
|
if (pi_err2 == error.Overflow) {
|
||||||
|
try self.tokenError(token, "Failed to parse number: overflow");
|
||||||
|
return Result.CompileError;
|
||||||
|
} else {
|
||||||
|
return pi_err2;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return ast.mkNum(i64, num_64);
|
||||||
|
} else {
|
||||||
|
return pi_err;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return ast.mkNum(i32, num_32);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn primary(self: *Parser) !Expr {
|
||||||
|
if (self.matchSingle(TokenType.False)) return ast.Expr{ .Bool = false };
|
||||||
|
if (self.matchSingle(TokenType.True)) return ast.Expr{ .Bool = true };
|
||||||
|
if (self.matchSingle(TokenType.None)) return ast.Expr{ .Nil = {} };
|
||||||
|
|
||||||
|
if (self.matchSingle(TokenType.Integer)) {
|
||||||
|
return try self.doInt();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (self.matchSingle(TokenType.String)) {
|
||||||
|
var lexeme = self.previous().lexeme;
|
||||||
|
var slice = try self.allocator.alloc(u8, lexeme.len);
|
||||||
|
std.mem.copy(u8, slice, lexeme);
|
||||||
|
|
||||||
|
return ast.Expr{ .String = slice };
|
||||||
|
}
|
||||||
|
|
||||||
|
if (self.matchSingle(TokenType.LeftParen)) {
|
||||||
|
var expr = try self.expression();
|
||||||
|
_ = try self.consume(.RightParen, "Expect ')' after expression");
|
||||||
|
|
||||||
|
return ast.mkGrouping(&expr);
|
||||||
|
}
|
||||||
|
|
||||||
|
try self.tokenError(self.peek(), "Expect expression");
|
||||||
|
return Result.CompileError;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn expression(self: *Parser) !Expr {
|
||||||
|
return try self.equality();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn parse(self: *Parser) !?*Expr {
|
||||||
self.tokens = try self.allocator.alloc(Token, 0);
|
self.tokens = try self.allocator.alloc(Token, 0);
|
||||||
var i: usize = 0;
|
var i: usize = 0;
|
||||||
var root = try ast.mkRoot(self.allocator);
|
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
var tok_opt = try self.scanner.nextToken();
|
var tok_opt = try self.scanner.nextToken();
|
||||||
|
|
||||||
if (tok_opt) |token| {
|
if (tok_opt) |token| {
|
||||||
self.tokens = try self.allocator.realloc(self.tokens, i + 1);
|
self.tokens = try self.allocator.realloc(self.tokens, i + 1);
|
||||||
self.tokens[i] = token;
|
self.tokens[i] = token;
|
||||||
i += 1;
|
i += 1;
|
||||||
|
|
||||||
if (token.ttype == .EOF) break;
|
if (token.ttype == .EOF) break;
|
||||||
|
|
||||||
var node = try self.processToken(token);
|
|
||||||
try root.Root.append(&node);
|
|
||||||
} else {
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return root;
|
std.debug.warn("{} tokens\n", i);
|
||||||
|
|
||||||
|
//return Expr{ .Number = ast.Number{ .Integer32 = 69 } };
|
||||||
|
//return self.root;
|
||||||
|
var expr = self.expression() catch |parse_err| {
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
return &expr;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -44,12 +44,22 @@ pub const Runner = struct {
|
||||||
|
|
||||||
scanner = scanners.Scanner.init(self.allocator, code);
|
scanner = scanners.Scanner.init(self.allocator, code);
|
||||||
var parser = Parser.init(self.allocator, &scanner);
|
var parser = Parser.init(self.allocator, &scanner);
|
||||||
var root = try parser.parse();
|
var expr_opt = try parser.parse();
|
||||||
|
|
||||||
var it = root.Root.iterator();
|
//var expr = ast.mkBinary(
|
||||||
|
// &ast.mkUnary(
|
||||||
|
// tokens.Token{ .ttype = .Minus, .lexeme = "-", .line = 1 },
|
||||||
|
// &ast.mkNum(i32, 123),
|
||||||
|
// ),
|
||||||
|
// tokens.Token{ .ttype = .Star, .lexeme = "*", .line = 1 },
|
||||||
|
// &ast.mkGrouping(&ast.mkNum(f32, 45.67)),
|
||||||
|
//);
|
||||||
|
|
||||||
while (it.next()) |node| {
|
if (expr_opt) |expr_ptr| {
|
||||||
std.debug.warn("{}\n", node.*);
|
printer.printAst(expr_ptr);
|
||||||
|
std.debug.warn("\n");
|
||||||
|
} else {
|
||||||
|
return Result.CompileError;
|
||||||
}
|
}
|
||||||
|
|
||||||
return Result.Ok;
|
return Result.Ok;
|
||||||
|
|
|
@ -24,7 +24,7 @@ fn isAlphaNumeric(char: u8) bool {
|
||||||
return isAlpha(char) or isDigit(char);
|
return isAlpha(char) or isDigit(char);
|
||||||
}
|
}
|
||||||
|
|
||||||
const keywords = [_][]const u8{
|
const keywords = [][]const u8{
|
||||||
"break",
|
"break",
|
||||||
"const",
|
"const",
|
||||||
"continue",
|
"continue",
|
||||||
|
@ -51,7 +51,7 @@ const keywords = [_][]const u8{
|
||||||
"None",
|
"None",
|
||||||
};
|
};
|
||||||
|
|
||||||
const keyword_ttypes = [_]TokenType{
|
const keyword_ttypes = []TokenType{
|
||||||
.Break,
|
.Break,
|
||||||
.Const,
|
.Const,
|
||||||
.Continue,
|
.Continue,
|
||||||
|
@ -186,17 +186,35 @@ pub const Scanner = struct {
|
||||||
|
|
||||||
/// Peek at the next character in the scanner
|
/// Peek at the next character in the scanner
|
||||||
fn peekNext(self: *Scanner) u8 {
|
fn peekNext(self: *Scanner) u8 {
|
||||||
if (self.current + 1 > self.source.len) return 0;
|
if (self.current > self.source.len) return 0;
|
||||||
return self.source[self.current];
|
return self.source[self.current];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Skip all whitespace, but increments Scanner.line when finding a newline.
|
||||||
|
fn skipWhitespace(self: *Scanner) void {
|
||||||
|
while (true) {
|
||||||
|
var c = self.peek();
|
||||||
|
|
||||||
|
switch (c) {
|
||||||
|
' ', '\r', '\t' => blk: {
|
||||||
|
_ = self.advance();
|
||||||
|
},
|
||||||
|
'\n' => blk: {
|
||||||
|
self.line += 1;
|
||||||
|
_ = self.advance();
|
||||||
|
},
|
||||||
|
else => return,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Consume a number.
|
/// Consume a number.
|
||||||
/// Returns either an Integer or a Float token. Proper typing
|
/// Returns either an Integer or a Float token. Proper typing
|
||||||
/// of the number (i32 i64 u32 u64 f32 f64) are for the parser.
|
/// of the number (i32 i64 u32 u64 f32 f64) are for the parser.
|
||||||
fn doNumber(self: *Scanner) Token {
|
fn doNumber(self: *Scanner) Token {
|
||||||
var ttype = TokenType.Integer;
|
var ttype = TokenType.Integer;
|
||||||
|
|
||||||
while (isDigit(self.peekNext())) {
|
while (isDigit(self.peek())) {
|
||||||
_ = self.advance();
|
_ = self.advance();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -261,6 +279,7 @@ pub const Scanner = struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn nextToken(self: *Scanner) !?Token {
|
pub fn nextToken(self: *Scanner) !?Token {
|
||||||
|
self.skipWhitespace();
|
||||||
self.start = self.current;
|
self.start = self.current;
|
||||||
|
|
||||||
if (self.isAtEnd()) return self.makeToken(TokenType.EOF);
|
if (self.isAtEnd()) return self.makeToken(TokenType.EOF);
|
||||||
|
@ -323,12 +342,6 @@ pub const Scanner = struct {
|
||||||
'\'' => try self.doString('\''),
|
'\'' => try self.doString('\''),
|
||||||
'"' => try self.doString('"'),
|
'"' => try self.doString('"'),
|
||||||
|
|
||||||
' ', '\r', '\t' => null,
|
|
||||||
'\n' => blk: {
|
|
||||||
self.line += 1;
|
|
||||||
break :blk null;
|
|
||||||
},
|
|
||||||
|
|
||||||
else => return ScannerError.Unexpected,
|
else => return ScannerError.Unexpected,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue