diff --git a/src/ast.zig b/src/ast.zig index 95d59c0..9b432d7 100644 --- a/src/ast.zig +++ b/src/ast.zig @@ -44,12 +44,37 @@ pub const SingleConst = struct { expr: *Expr, }; +pub const BinaryOperator = enum { + + // numeric + Add, + Sub, + Mul, + Div, + Mod, + + // numeric -> bool + Greater, + GreaterEqual, + Less, + LessEqual, + + // booleans + Equal, + And, + Or, +}; + pub const BinaryExpr = struct { left: *Expr, - op: Token, + op: BinaryOperator, right: *Expr, }; +pub const UnaryOperator = enum { + Not, +}; + pub const UnaryExpr = struct { op: Token, right: *Expr, diff --git a/src/ast_printer.zig b/src/ast_printer.zig index e818c00..ee9fb61 100644 --- a/src/ast_printer.zig +++ b/src/ast_printer.zig @@ -155,8 +155,41 @@ fn printTwoExprs(expr_a: *const Expr, expr_b: *const Expr) void { printExpr(expr_b); } -fn printBinOp(inner: var) void { - std.debug.warn("({}", inner.op.lexeme); +const operator_tokens = [_][]const u8{ + "+", "-", "*", "/", "%", ">", ">=", "<", "<=", "==", "&&", "||", +}; + +const operator_values = [_]BinaryOperator{ + .Add, + .Sub, + .Mul, + .Div, + .Mod, + + .Greater, + .GreaterEqual, + .Less, + .LessEqual, + + .Equal, + .And, + .Or, +}; + +fn binOpToStr(op: BinaryOperator) ?[]const u8 { + inline for (operator_values) |val, idx| { + if (val == op) return operator_tokens[idx]; + } + + return null; +} + +fn printBinOp(inner: var, comptime is_binop: bool) void { + if (is_binop) { + std.debug.warn("({}", binOpToStr(inner.op)); + } else { + std.debug.warn("({}", inner.op.lexeme); + } printTwoExprs(inner.left, inner.right); std.debug.warn(")"); } @@ -169,8 +202,8 @@ fn printSingleOp(tok: []const u8, applied: *const Expr) void { pub fn printExpr(expr: *const Expr) void { switch (expr.*) { - .Binary => |binary| printBinOp(binary), - .Logical => |logical| printBinOp(logical), + .Binary => |binary| printBinOp(binary, true), + .Logical => |logical| printBinOp(logical, false), .Unary => |unary| printSingleOp(unary.op.lexeme, unary.right), .Grouping => |expr_ptr| printSingleOp("group", expr_ptr), diff --git a/src/codegen.zig b/src/codegen.zig index 583a699..b5a2e45 100644 --- a/src/codegen.zig +++ b/src/codegen.zig @@ -48,13 +48,15 @@ pub const Codegen = struct { var left = try self.emitExpr(builder, binary.left); var right = try self.emitExpr(builder, binary.right); - return switch (binary.op.lexeme[0]) { - // TODO other operators - '+' => llvm.LLVMBuildAdd(builder, left, right, c"addtmp"), + return switch (binary.op) { + .Add => llvm.LLVMBuildAdd(builder, left, right, c"addtmp"), + .Sub => llvm.LLVMBuildSub(builder, left, right, c"subtmp"), + .Mul => llvm.LLVMBuildMul(builder, left, right, c"multmp"), + + //.Div => llvm.LLVMBuildDiv(builder, left, right, c"divtmp"), - // TODO codegen errors else => { - std.debug.warn("Unexpected binary operator: '{}'\n", binary.op.lexeme); + std.debug.warn("Unexpected binary operator: '{}'\n", binary.op); return CompileError.EmitError; }, }; diff --git a/src/parsers.zig b/src/parsers.zig index d31c611..b2156e6 100644 --- a/src/parsers.zig +++ b/src/parsers.zig @@ -11,7 +11,10 @@ const Scanner = scanners.Scanner; const Token = tokens.Token; const TokenType = tokens.TokenType; -pub const ParseError = error{CompileError}; +pub const ParseError = error{ + CompileError, + UnknownOperator, +}; const Node = ast.Node; const Expr = ast.Expr; @@ -25,6 +28,39 @@ const FieldState = struct { mutable_outside: bool = false, }; +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; +} + pub const Parser = struct { allocator: *Allocator, scanner: *Scanner, @@ -218,7 +254,7 @@ pub const Parser = struct { return expr; } - fn mkBinary(self: *Parser, left: *Expr, op: Token, right: *Expr) !*Expr { + fn mkBinary(self: *Parser, left: *Expr, op: ast.BinaryOperator, right: *Expr) !*Expr { var expr = try self.allocator.create(Expr); expr.* = Expr{ .Binary = ast.BinaryExpr{ @@ -822,55 +858,41 @@ pub const Parser = struct { } fn finishAssignment(self: *@This(), expr: *Expr, mutable: bool) !*Expr { - var op = self.peek(); + var op_tok = self.peek(); _ = try self.nextToken(); + var value = try self.parseAssignment(); - // TODO convert binary's op field from Token to - // something else, maybe enum'd + // expr can be a (Variable|Set) + // op_tok can be one of three categories: + // - ColonEqual, parses to VarDecl (only when expr = Variable) + // - Equal, parses to (Assign|Set) + // - Inplace (+=, -=, *=, /=), parses to (Assign|Set) - const new_op_ttype: TokenType = switch (op.typ) { - .ColonEqual => TokenType.ColonEqual, - .Equal => .Equal, + // 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' - .PlusEqual => .Plus, - .MinusEqual => .Minus, - .StarEqual => .Star, - .SlashEqual => .Slash, - - else => unreachable, - }; - - // we create new_lexeme so that - // the AST printer properly prints - // x += 1 - // as - // (set x (+ x 1)) - // and not - // (set x (+= x 1)) - const new_lexeme: []const u8 = switch (op.typ) { - .ColonEqual => ":=", - .Equal => "=", - - .PlusEqual => "+", - .MinusEqual => "-", - .StarEqual => "*", - .SlashEqual => "/", - - else => unreachable, + // TODO do we add %=? + const binop: ?ast.BinaryOperator = switch (op_tok.typ) { + .PlusEqual => ast.BinaryOperator.Add, + .MinusEqual => .Sub, + .StarEqual => .Mul, + .SlashEqual => .Div, + else => null, }; switch (expr.*) { .Variable => { - switch (op.typ) { + switch (op_tok.typ) { .ColonEqual => return try self.mkVarDecl(expr.Variable, value, mutable), .Equal => return try self.mkAssign(expr.Variable, value), .PlusEqual, .MinusEqual, .StarEqual, .SlashEqual => { - var new_op = try self.mkToken(new_op_ttype, new_lexeme, op.line); return try self.mkAssign( expr.Variable, - try self.mkBinary(expr, new_op, value), + try self.mkBinary(expr, binop.?, value), ); }, @@ -879,7 +901,7 @@ pub const Parser = struct { }, .Get => |get| { - switch (op.typ) { + switch (op_tok.typ) { .ColonEqual => { return self.doError("can not initialize struct field"); }, @@ -887,11 +909,10 @@ pub const Parser = struct { .Equal => return try self.mkSet(get.struc, get.name, value), .PlusEqual, .MinusEqual, .StarEqual, .SlashEqual => { - var new_op = try self.mkToken(new_op_ttype, new_lexeme, op.line); return try self.mkSet( get.struc, get.name, - try self.mkBinary(expr, new_op, value), + try self.mkBinary(expr, binop.?, value), ); }, @@ -941,7 +962,7 @@ pub const Parser = struct { _ = try self.nextToken(); var right = try self.parseComparison(); - expr = try self.mkBinary(expr, op, right); + expr = try self.mkBinary(expr, .Equal, right); } return expr; @@ -960,7 +981,7 @@ pub const Parser = struct { _ = try self.nextToken(); var right = try self.parseAddition(); - expr = try self.mkBinary(expr, op, right); + expr = try self.mkBinary(expr, try toBinaryOperator(op), right); } return expr; @@ -976,7 +997,7 @@ pub const Parser = struct { _ = try self.nextToken(); var right = try self.parseMultiplication(); - expr = try self.mkBinary(expr, op, right); + expr = try self.mkBinary(expr, try toBinaryOperator(op), right); } return expr; @@ -992,7 +1013,7 @@ pub const Parser = struct { _ = try self.nextToken(); var right = try self.parseUnary(); - expr = try self.mkBinary(expr, op, right); + expr = try self.mkBinary(expr, try toBinaryOperator(op), right); } return expr;