diff --git a/examples/hello.ry b/examples/hello.ry index fc0e890..f3909eb 100644 --- a/examples/hello.ry +++ b/examples/hello.ry @@ -36,14 +36,11 @@ fn func_refer_param(b: i32) i32 { } fn multwo(num: i32, double_flag: bool) i32 { - if (!double_flag) { - var value = 32; - return value; - } - if (double_flag) { + var truthy = true; return num * 2; } else { + var falsey = false; return num; } } diff --git a/src/analysis.zig b/src/analysis.zig index acb9b6d..8a6a90d 100644 --- a/src/analysis.zig +++ b/src/analysis.zig @@ -214,9 +214,9 @@ pub const TypeSolver = struct { }; }, + // for now, unary operators only have .Not .Unary => |unary| { var right_type = try self.resolveExprType(ctx, unary.right); - return switch (unary.op) { .Negate => right_type, .Not => right_type, diff --git a/src/ast.zig b/src/ast.zig index 85eb602..efb0015 100644 --- a/src/ast.zig +++ b/src/ast.zig @@ -29,6 +29,7 @@ pub const ParamDecl = struct { pub const MethodData = struct { variable: Token, typ: Token, + mutable: bool, }; pub const FnDecl = struct { @@ -287,6 +288,10 @@ pub const FieldList = std.ArrayList(StructField); pub const StructField = struct { name: Token, typ: Token, + + mutable: bool = false, + public: bool = false, + mutable_outside: bool = false, }; pub const Struct = struct { diff --git a/src/ast_printer.zig b/src/ast_printer.zig index ad6ec20..13b6fe4 100644 --- a/src/ast_printer.zig +++ b/src/ast_printer.zig @@ -48,7 +48,11 @@ pub fn printNode(node: *const Node, ident: usize) void { const vari = method.variable.lexeme; const typ = method.typ.lexeme; - warn("(method {} {} {} {} (", vari, typ, name, ret_type); + if (method.mutable) { + warn("(method mut {} {} {} {} (", vari, typ, name, ret_type); + } else { + warn("(method {} {} {} {} (", vari, typ, name, ret_type); + } } else { warn("(fn {} {} (", name, ret_type); } @@ -108,7 +112,22 @@ pub fn printNode(node: *const Node, ident: usize) void { .Struct => |struc| { print(ident, "(struct {} (\n", struc.name.lexeme); for (struc.fields.toSlice()) |field| { - print(ident + 1, "({} {})\n", field.name.lexeme, field.typ.lexeme); + printIdent(ident + 1); + if (field.mutable) { + std.debug.warn("(mut "); + } else { + std.debug.warn("("); + } + + if (field.public) { + std.debug.warn("pub "); + } + + if (field.mutable_outside) { + std.debug.warn("MUT_OUT "); + } + + std.debug.warn("{} {})\n", field.name.lexeme, field.typ.lexeme); } print(ident, "))\n"); }, diff --git a/src/codegen.zig b/src/codegen.zig index 592a79b..e71517e 100644 --- a/src/codegen.zig +++ b/src/codegen.zig @@ -132,15 +132,6 @@ pub const Codegen = struct { }; }, - .Unary => |unary| { - var right = try self.emitExpr(builder, unary.right); - - return switch (unary.op) { - .Negate => llvm.LLVMBuildNeg(builder, right, c"neg_tmp"), - .Not => llvm.LLVMBuildNot(builder, right, c"neg_tmp"), - }; - }, - .Binary => |binary| { var left = try self.emitExpr(builder, binary.left); var right = try self.emitExpr(builder, binary.right); diff --git a/src/parsers.zig b/src/parsers.zig index 81c83cb..026d11e 100644 --- a/src/parsers.zig +++ b/src/parsers.zig @@ -23,6 +23,12 @@ const Stmt = ast.Stmt; const TokenList = std.ArrayList(Token); +const FieldState = struct { + public: bool = false, + mutable: bool = false, + mutable_outside: bool = false, +}; + const operator_tokens = [_][]const u8{ "+", "-", "*", "/", "%", ">", ">=", "<", "<=", "==", }; @@ -505,12 +511,19 @@ pub const Parser = struct { ); } - /// parse the (v T) part of the method (defined here + /// 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); @@ -520,6 +533,7 @@ pub const Parser = struct { method.* = ast.MethodData{ .variable = variable, .typ = typ, + .mutable = mutable_ref, }; return method; @@ -567,14 +581,27 @@ pub const Parser = struct { _ = try self.consumeSingle(.LeftBrace); + var field_state = FieldState{}; + while (!self.check(.RightBrace)) { + try self.parseFieldModifiers(&field_state); + const field_name = try self.consumeSingle(.Identifier); self.setErrContext("struct {} field {}", name, field_name); 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, }); } @@ -583,6 +610,51 @@ pub const Parser = struct { 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(); @@ -776,22 +848,29 @@ pub const Parser = struct { // there can be two assignments coming out of this function: // - an assignment to a variable with = // - an update to a variable with +=, -= + + // 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{ .Equal, .PlusEqual, .MinusEqual, .StarEqual, .SlashEqual, })) { - return try self.finishAssignment(expr); + return try self.finishAssignment(expr, mutable); } return expr; } - fn finishAssignment( - self: *@This(), - expr: *Expr, - ) !*Expr { + fn finishAssignment(self: *@This(), expr: *Expr, mutable: bool) !*Expr { var op_tok = self.peek(); _ = try self.nextToken(); @@ -954,9 +1033,8 @@ pub const Parser = struct { fn parseUnary(self: *@This()) anyerror!*Expr { if (self.compareAnyOf(&[_]TokenType{ .Bang, .Minus })) { - var op = self.peek(); - _ = try self.nextToken(); - var right = try self.parseCall(); + var op = self.previous(); + var right = try self.parseUnary(); return try self.mkUnary(try toUnaryOperator(op), right); }