diff --git a/examples/hello.ry b/examples/hello.ry index debd7e6..43b93a2 100644 --- a/examples/hello.ry +++ b/examples/hello.ry @@ -1,7 +1,11 @@ // import std; -fn add() i32 { - return 1 + 1; +fn add(a: i32, b: i32) i32 { + return 69 + 69; +} + +fn and_fn() bool { + return true and false; } // type is void by default diff --git a/src/ast.zig b/src/ast.zig index 95d59c0..6663958 100644 --- a/src/ast.zig +++ b/src/ast.zig @@ -44,24 +44,42 @@ 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, }; -// looks like a BinaryExpr, but is not a BinaryExpr -pub const LogicalExpr = struct { - left: *Expr, - op: Token, - right: *Expr, -}; - pub const LiteralExpr = union(enum) { Bool: bool, Integer: []const u8, @@ -83,7 +101,6 @@ pub const ExprType = enum { Binary, Unary, - Logical, Literal, Variable, Call, @@ -134,7 +151,6 @@ pub const Expr = union(ExprType) { Binary: BinaryExpr, Unary: UnaryExpr, - Logical: LogicalExpr, Literal: LiteralExpr, Struct: StructExpr, diff --git a/src/ast_printer.zig b/src/ast_printer.zig index e818c00..4f9ced9 100644 --- a/src/ast_printer.zig +++ b/src/ast_printer.zig @@ -155,8 +155,37 @@ fn printTwoExprs(expr_a: *const Expr, expr_b: *const Expr) void { printExpr(expr_b); } +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) void { - std.debug.warn("({}", inner.op.lexeme); + std.debug.warn("({}", binOpToStr(inner.op)); printTwoExprs(inner.left, inner.right); std.debug.warn(")"); } @@ -170,7 +199,6 @@ 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), .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..384fd9a 100644 --- a/src/codegen.zig +++ b/src/codegen.zig @@ -10,8 +10,22 @@ fn sliceify(non_slice: ?[*]const u8) []const u8 { pub const CompileError = error{ LLVMError, EmitError, + TypeError, }; +/// Does not account for custom types e.g structs, better type resolution +/// should be found +fn basicTypeToLLVM(ret_type: []const u8) !llvm.LLVMTypeRef { + if (std.mem.eql(u8, ret_type, "i32")) { + return llvm.LLVMInt32Type(); + } else if (std.mem.eql(u8, ret_type, "bool")) { + return llvm.LLVMInt1Type(); + } else { + std.debug.warn("Invalid return type: {}\n", ret_type); + return CompileError.TypeError; + } +} + pub const Codegen = struct { allocator: *std.mem.Allocator, @@ -41,20 +55,32 @@ pub const Codegen = struct { var val_cstr = try std.cstr.addNullByte(self.allocator, val); break :blk2 llvm.LLVMConstRealOfString(llvm.LLVMDoubleType(), val_cstr.ptr); }, + .Bool => |val| blk2: { + if (val) { + break :blk2 llvm.LLVMConstInt(llvm.LLVMInt1Type(), 1, 1); + } else { + break :blk2 llvm.LLVMConstInt(llvm.LLVMInt1Type(), 0, 1); + } + }, else => unreachable, }; }, + .Binary => |binary| { 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"), + .And => llvm.LLVMBuildAnd(builder, left, right, c"andtmp"), + .Or => llvm.LLVMBuildOr(builder, left, right, c"ortmp"), - // 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; }, }; @@ -94,23 +120,24 @@ pub const Codegen = struct { const name_cstr = try std.cstr.addNullByte(self.allocator, name); errdefer self.allocator.free(name_cstr); - //const ret_type = decl.return_type.lexeme; + const fn_ret_type = decl.return_type.lexeme; var param_types = llvm.LLVMTypeList.init(self.allocator); errdefer param_types.deinit(); for (decl.params.toSlice()) |param| { - try param_types.append(llvm.LLVMInt32Type()); + // TODO better type resolution + try param_types.append(try basicTypeToLLVM(param.typ.lexeme)); } - var ret_type = llvm.LLVMFunctionType( - llvm.LLVMInt32Type(), + var llvm_ret_type = llvm.LLVMFunctionType( + try basicTypeToLLVM(fn_ret_type), param_types.toSlice().ptr, @intCast(c_uint, param_types.len), 0, ); - var func = llvm.LLVMAddFunction(mod, name_cstr.ptr, ret_type); + var func = llvm.LLVMAddFunction(mod, name_cstr.ptr, llvm_ret_type); var entry = llvm.LLVMAppendBasicBlock(func, c"entry"); var builder = llvm.LLVMCreateBuilder(); diff --git a/src/parsers.zig b/src/parsers.zig index d31c611..0a192e1 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{ @@ -231,19 +267,6 @@ pub const Parser = struct { return expr; } - fn mkLogical(self: *Parser, left: *Expr, op: Token, right: *Expr) !*Expr { - var expr = try self.allocator.create(Expr); - expr.* = Expr{ - .Logical = ast.LogicalExpr{ - .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{ @@ -822,55 +845,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 +888,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 +896,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), ); }, @@ -913,7 +921,7 @@ pub const Parser = struct { _ = try self.nextToken(); var right = try self.parseAnd(); - expr = try self.mkLogical(expr, op, right); + expr = try self.mkBinary(expr, try toBinaryOperator(op), right); } return expr; @@ -927,7 +935,7 @@ pub const Parser = struct { _ = try self.nextToken(); var right = try self.parseEquality(); - expr = try self.mkLogical(expr, op, right); + expr = try self.mkBinary(expr, try toBinaryOperator(op), right); } return expr; @@ -941,7 +949,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 +968,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 +984,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 +1000,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; diff --git a/src/scanners.zig b/src/scanners.zig index ced1da8..5271235 100644 --- a/src/scanners.zig +++ b/src/scanners.zig @@ -52,6 +52,8 @@ const keywords = [_][]const u8{ "println", "loop", "pub", + "and", + "or", }; const keyword_ttypes = [_]TokenType{ @@ -82,6 +84,8 @@ const keyword_ttypes = [_]TokenType{ .Println, .Loop, .Pub, + .And, + .Or, }; fn getKeyword(keyword: []const u8) ?TokenType {