diff --git a/src/AstGen.zig b/src/AstGen.zig index b827ffa..a3bbdea 100644 --- a/src/AstGen.zig +++ b/src/AstGen.zig @@ -630,38 +630,11 @@ fn binaryOp( op: Ir.Inst.Tag, ) InnerError!Ir.Inst.Ref { const data = expr_node.data.bin; - assert(data.lhs != null and data.rhs != null); const lhs = try expr(gi, scope, data.lhs.?); const rhs = try expr(gi, scope, data.rhs.?); return gi.addBinaryNode(op, lhs, rhs); } -fn logicalOp( - gen: *GenIr, - scope: *Scope, - node: *const Ast.Node, - op: Story.Opcode, -) InnerError!void { - const data = node.data.bin; - assert(data.lhs != null and data.rhs != null); - try expr(gen, scope, data.lhs); - - const else_label = try gen.makeLabel(); - const fixup_offset = try gen.emitJumpInst(op); - _ = try gen.makeFixup(.{ - .mode = .relative, - .label_index = else_label, - .code_offset = fixup_offset, - }); - - try gen.emitSimpleInst(.pop); - const rhs_label = try gen.makeLabel(); - gen.setLabel(rhs_label); - - try expr(gen, scope, data.rhs); - gen.setLabel(else_label); -} - fn parseNumberLiteral(bytes: []const u8) union(enum) { int: i64, float: f64, @@ -731,39 +704,39 @@ fn identifier( return block.addStrTok(.decl_ref, str.index, node.loc.start); } -fn expr(gi: *GenIr, scope: *Scope, optional_expr: ?*const Ast.Node) InnerError!Ir.Inst.Ref { - const expr_node = optional_expr.?; - switch (expr_node.tag) { +fn expr(gi: *GenIr, scope: *Scope, optional_node: ?*const Ast.Node) InnerError!Ir.Inst.Ref { + const node = optional_node.?; + switch (node.tag) { .file => unreachable, .true_literal => return .bool_true, .false_literal => return .bool_false, - .number_literal => return numberLiteral(gi, expr_node), - .string_literal => return stringLiteral(gi, expr_node), - .string_expr => return stringExpr(gi, expr_node), - .empty_string => return stringLiteral(gi, expr_node), - .identifier => return identifier(gi, scope, expr_node), - .add_expr => return binaryOp(gi, scope, expr_node, .add), - .subtract_expr => return binaryOp(gi, scope, expr_node, .sub), - .multiply_expr => return binaryOp(gi, scope, expr_node, .mul), - .divide_expr => return binaryOp(gi, scope, expr_node, .div), - .mod_expr => return binaryOp(gi, scope, expr_node, .mod), - .negate_expr => return unaryOp(gi, scope, expr_node, .neg), - .logical_and_expr => unreachable, - .logical_or_expr => unreachable, - .logical_not_expr => return unaryOp(gi, scope, expr_node, .not), - .logical_equality_expr => return binaryOp(gi, scope, expr_node, .cmp_eq), - .logical_inequality_expr => return binaryOp(gi, scope, expr_node, .cmp_neq), - .logical_greater_expr => return binaryOp(gi, scope, expr_node, .cmp_gt), - .logical_greater_or_equal_expr => return binaryOp(gi, scope, expr_node, .cmp_gte), - .logical_lesser_expr => return binaryOp(gi, scope, expr_node, .cmp_lt), - .logical_lesser_or_equal_expr => return binaryOp(gi, scope, expr_node, .cmp_lte), + .number_literal => return numberLiteral(gi, node), + .string_literal => return stringLiteral(gi, node), + .string_expr => return stringExpr(gi, node), + .empty_string => return stringLiteral(gi, node), + .identifier => return identifier(gi, scope, node), + .add_expr => return binaryOp(gi, scope, node, .add), + .subtract_expr => return binaryOp(gi, scope, node, .sub), + .multiply_expr => return binaryOp(gi, scope, node, .mul), + .divide_expr => return binaryOp(gi, scope, node, .div), + .mod_expr => return binaryOp(gi, scope, node, .mod), + .negate_expr => return unaryOp(gi, scope, node, .neg), + .logical_not_expr => return unaryOp(gi, scope, node, .not), + .logical_and_expr => return binaryOp(gi, scope, node, .bool_and), + .logical_or_expr => return binaryOp(gi, scope, node, .bool_or), + .logical_equality_expr => return binaryOp(gi, scope, node, .cmp_eq), + .logical_inequality_expr => return binaryOp(gi, scope, node, .cmp_neq), + .logical_greater_expr => return binaryOp(gi, scope, node, .cmp_gt), + .logical_greater_or_equal_expr => return binaryOp(gi, scope, node, .cmp_gte), + .logical_lesser_expr => return binaryOp(gi, scope, node, .cmp_lt), + .logical_lesser_or_equal_expr => return binaryOp(gi, scope, node, .cmp_lte), .call_expr => unreachable, .choice_expr => unreachable, .choice_start_expr => unreachable, .choice_option_expr => unreachable, .choice_inner_expr => unreachable, .divert_expr => unreachable, - .selector_expr => return fieldAccess(gi, scope, expr_node), + .selector_expr => return fieldAccess(gi, scope, node), .assign_stmt => unreachable, .block_stmt => unreachable, .content_stmt => unreachable, diff --git a/src/Ir.zig b/src/Ir.zig index 0091e93..c01364a 100644 --- a/src/Ir.zig +++ b/src/Ir.zig @@ -170,11 +170,21 @@ pub const Inst = struct { neg, /// Uses the `un` union field. not, + /// Uses the `bin` union field. + bool_and, + /// Uses the `bin` union field. + bool_or, + /// Uses the `bin` union field. cmp_eq, + /// Uses the `bin` union field. cmp_neq, + /// Uses the `bin` union field. cmp_gt, + /// Uses the `bin` union field. cmp_gte, + /// Uses the `bin` union field. cmp_lt, + /// Uses the `bin` union field. cmp_lte, /// Uses the `int` union field. int, diff --git a/src/Sema.zig b/src/Sema.zig index a6b1c8b..5402480 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -35,19 +35,41 @@ pub const Value = struct { ip_index: InternPool.Index, pub const Unwrapped = union(enum) { + bool: bool, int: i64, float: f64, - fn toFloat(v: Value.Unwrapped) f64 { + pub fn toFloat(v: Unwrapped) f64 { return switch (v) { - .int => |i| @floatFromInt(i), - .float => |f| f, + .bool => |boolean| @floatFromInt(@intFromBool(boolean)), + .int => |int| @floatFromInt(int), + .float => |float| float, + }; + } + + pub fn isTruthy(v: Unwrapped) bool { + return switch (v) { + //.null => false, + .bool => |boolean| boolean, + .int => |int| int != 0, + .float => |float| float != 0.0, + //.str => true, + }; + } + + pub fn coerce(value: Unwrapped) Unwrapped { + return switch (value) { + .int => value, + .float => value, + .bool => |boolean| .{ .int = if (boolean) 1 else 0 }, + //else => null, }; } }; pub fn unwrap(value: Value, ip: *InternPool) Unwrapped { switch (ip.values.items[@intFromEnum(value.toInterned())]) { + .bool => |boolean| return .{ .bool = boolean }, .int => |int| return .{ .int = int }, .float => |float| return .{ .float = @bitCast(float) }, .str => @panic("String unwrapping not implemented!"), @@ -279,43 +301,90 @@ pub const Builder = struct { } }; +fn foldArith( + lhs: Value.Unwrapped, + rhs: Value.Unwrapped, + op: Story.Opcode, +) !Value.Unwrapped { + const l = lhs.coerce(); + const r = rhs.coerce(); + if (l == .int and r == .int) { + return switch (op) { + .add => .{ .int = l.int +% r.int }, + .sub => .{ .int = l.int -% r.int }, + .mul => .{ .int = l.int *% r.int }, + .div => if (r.int == 0) + error.DivisionByZero + else + .{ .int = @divTrunc(l.int, r.int) }, + .mod => if (r.int == 0) + error.DivisionByZero + else + .{ .int = @mod(l.int, r.int) }, + else => unreachable, + }; + } + + const lf = l.toFloat(); + const rf = r.toFloat(); + return switch (op) { + .add => .{ .float = lf + rf }, + .sub => .{ .float = lf - rf }, + .mul => .{ .float = lf * rf }, + .div => if (rf == 0.0) + error.DivisionByZero + else + .{ .float = lf / rf }, + .mod => if (rf == 0.0) + error.DivisionByZero + else + .{ .float = @mod(lf, rf) }, + else => unreachable, + }; +} + +fn foldCmp( + lhs: Value.Unwrapped, + rhs: Value.Unwrapped, + op: Story.Opcode, +) !Value.Unwrapped { + switch (op) { + .cmp_eq => return .{ .bool = std.meta.eql(lhs, rhs) }, + .cmp_neq => return .{ .bool = !std.meta.eql(lhs, rhs) }, + else => {}, + } + + const lf = lhs.coerce().toFloat(); + const rf = rhs.coerce().toFloat(); + const result = switch (op) { + .cmp_lt => lf < rf, + .cmp_gt => lf > rf, + .cmp_lte => lf <= rf, + .cmp_gte => lf >= rf, + else => unreachable, + }; + return .{ .bool = result }; +} + fn foldConstant( lhs: Value.Unwrapped, rhs: Value.Unwrapped, op: Story.Opcode, ) !Value.Unwrapped { - if (lhs == .int and rhs == .int) { - switch (op) { - .add => return .{ .int = lhs.int +% rhs.int }, - .sub => return .{ .int = lhs.int -% rhs.int }, - .mul => return .{ .int = lhs.int *% rhs.int }, - .div => { - if (rhs.int == 0) - return error.DivisionByZero; - return .{ .int = @divTrunc(lhs.int, rhs.int) }; - }, - .mod => if (rhs.int == 0) - return error.DivisionByZero - else - return .{ .int = @mod(lhs.int, rhs.int) }, - else => unreachable, - } - } - - const l = lhs.toFloat(); - const r = rhs.toFloat(); switch (op) { - .add => return .{ .float = l + r }, - .sub => return .{ .float = l - r }, - .mul => return .{ .float = l * r }, - .div => if (r == 0.0) - return error.DivisionByZero - else - return .{ .float = l / r }, - .mod => if (r == 0.0) - return error.DivisionByZero - else - return .{ .float = @mod(l, r) }, + .add, + .sub, + .mul, + .div, + .mod, + => return foldArith(lhs, rhs, op), + .cmp_eq, + .cmp_neq, + .cmp_lt, + .cmp_gt, + .cmp_lte, + .cmp_gte, + => return foldCmp(lhs, rhs, op), else => unreachable, } } @@ -351,20 +420,27 @@ fn irUnaryOp( //const lhs_src: SrcLoc = .{ .src_offset = 0 }; //try sema.analyzeArithmeticArg(builder, lhs, lhs_src); - if (sema.resolveValue(lhs)) |lhs_value| { - const lhs_unwrapped = lhs_value.unwrap(ip); - switch (lhs_unwrapped) { - .int => |int| { + if (sema.resolveValue(lhs)) |lhs_info| { + switch (lhs_info.unwrap(ip)) { + .bool => |boolean| { const new_value = switch (op) { - .not => return error.TypeError, + .not => !boolean, + .neg => return error.TypeError, + else => unreachable, + }; + return .{ .value = ip.getOrPutBool(new_value) }; + }, + .int => |int| { + const new_value: i64 = switch (op) { + .not => if (int > 0) 0 else 1, .neg => -int, else => unreachable, }; return .{ .value = try ip.getOrPutInt(gpa, new_value) }; }, .float => |float| { - const new_value = switch (op) { - .not => return error.TypeError, + const new_value: f64 = switch (op) { + .not => if (float > 0.0) 0.0 else 1.0, .neg => -float, else => unreachable, }; @@ -384,8 +460,9 @@ fn irBinaryOp( inst: Ir.Inst.Index, op: Story.Opcode, ) InnerError!ValueInfo { - const data = sema.ir.instructions[@intFromEnum(inst)].data.bin; + const gpa = sema.gpa; const ip = &sema.module.intern_pool; + const data = sema.ir.instructions[@intFromEnum(inst)].data.bin; const lhs = sema.resolveInst(data.lhs); const rhs = sema.resolveInst(data.rhs); //const lhs_src: SrcLoc = .{ .src_offset = 0 }; @@ -395,16 +472,13 @@ fn irBinaryOp( if (sema.resolveValue(lhs)) |lhs_value| { if (sema.resolveValue(rhs)) |rhs_value| { - const lhs_unwrap = lhs_value.unwrap(ip); - const rhs_unwrap = rhs_value.unwrap(ip); - switch (try foldConstant(lhs_unwrap, rhs_unwrap, op)) { - .int => |int| return .{ - .value = try ip.getOrPutInt(sema.gpa, int), - }, - .float => |float| return .{ - .value = try ip.getOrPutFloat(sema.gpa, float), - }, - } + const lhs_coerced = lhs_value.unwrap(ip).coerce(); + const rhs_coerced = rhs_value.unwrap(ip).coerce(); + return switch (try foldConstant(lhs_coerced, rhs_coerced, op)) { + .bool => |boolean| .{ .value = ip.getOrPutBool(boolean) }, + .int => |int| .{ .value = try ip.getOrPutInt(gpa, int) }, + .float => |float| .{ .value = try ip.getOrPutFloat(gpa, float) }, + }; } } @@ -414,6 +488,40 @@ fn irBinaryOp( return .none; } +fn irLogicalOp( + sema: *Sema, + builder: *Builder, + inst: Ir.Inst.Index, + logical_or: bool, +) InnerError!ValueInfo { + const ip = &sema.module.intern_pool; + const data = sema.ir.instructions[@intFromEnum(inst)].data.bin; + const lhs = sema.resolveInst(data.lhs); + const rhs = sema.resolveInst(data.rhs); + + if (sema.resolveValue(lhs)) |lhs_info| { + const lhs_value = lhs_info.unwrap(ip); + if (sema.resolveValue(rhs)) |_| { + return if (logical_or) + if (lhs_value.isTruthy()) lhs else rhs + else if (!lhs_value.isTruthy()) lhs else rhs; + } + + if (logical_or and lhs_value.isTruthy()) return lhs; + if (!logical_or and !lhs_value.isTruthy()) return lhs; + try builder.ensureLoad(rhs); + return .none; + } + + const else_label = try builder.addLabel(); + try builder.ensureLoad(lhs); + try builder.addFixup(if (logical_or) .jmp_t else .jmp_f, else_label); + try builder.addByteOp(.pop); + try builder.ensureLoad(rhs); + builder.setLabel(else_label); + return .none; +} + fn irDeclRef( sema: *Sema, builder: *Builder, @@ -672,7 +780,6 @@ fn irDivert( sema.gpa, extra.data.field_name_start, ); - std.debug.print("Target: {any}\n", .{target}); const e = try sema.lookupInNamespace(target.namespace.?, ip_index, callee_src); switch (e.tag) { .knot => { @@ -782,12 +889,10 @@ fn analyzeBodyInner( .mod => try irBinaryOp(sema, builder, inst, .mod), .neg => try irUnaryOp(sema, builder, inst, .neg), .not => try irUnaryOp(sema, builder, inst, .not), + .bool_and => try irLogicalOp(sema, builder, inst, false), + .bool_or => try irLogicalOp(sema, builder, inst, true), .cmp_eq => try irBinaryOp(sema, builder, inst, .cmp_eq), - .cmp_neq => blk: { - const val = try irBinaryOp(sema, builder, inst, .cmp_eq); - try builder.addByteOp(.not); - break :blk val; - }, + .cmp_neq => try irBinaryOp(sema, builder, inst, .cmp_neq), .cmp_lt => try irBinaryOp(sema, builder, inst, .cmp_lt), .cmp_lte => try irBinaryOp(sema, builder, inst, .cmp_lte), .cmp_gt => try irBinaryOp(sema, builder, inst, .cmp_gt), diff --git a/src/Story.zig b/src/Story.zig index bb64e5d..a943717 100644 --- a/src/Story.zig +++ b/src/Story.zig @@ -46,6 +46,15 @@ pub const Value = union(enum) { return v == .int or v == .float; } + pub fn coerce(v: Value) ?Value { + return switch (v) { + .int => v, + .float => v, + .bool => |boolean| .{ .int = if (boolean) 1 else 0 }, + else => null, + }; + } + pub fn isTruthy(v: Value) bool { return switch (v) { //.nil => false, @@ -69,8 +78,11 @@ pub const Value = union(enum) { } pub fn arith(lhs: Value, rhs: Value, op: Opcode, story: *Story) !Value { - if (lhs.isNumeric() and rhs.isNumeric()) - return numericArith(lhs, rhs, op); + if (lhs.coerce()) |l| { + if (rhs.coerce()) |r| { + return numericArith(l, r, op); + } + } if (op == .add) { if (lhs == .object and lhs.object.tag == .string) return concat(lhs, rhs, story); @@ -177,6 +189,36 @@ pub const Value = union(enum) { .object => return error.TypeError, } } + + pub fn format(value: Value, writer: *std.Io.Writer) error{WriteFailed}!void { + switch (value) { + .bool => |boolean| try writer.writeAll(if (boolean) "true" else "false"), + .int => |int| try writer.print("{d}", .{int}), + .float => |float| { + var buf: [64]u8 = undefined; + if (std.math.isNan(float)) return writer.writeAll("NaN"); + if (std.math.isInf(float)) return writer.writeAll(if (float > 0) "Inf" else "-Inf"); + + var str = std.fmt.bufPrint(&buf, "{d:.7}", .{float}) catch |err| switch (err) { + error.NoSpaceLeft => unreachable, + else => |e| return e, + }; + if (std.mem.indexOfScalar(u8, str, '.')) |dot| { + var end = str.len; + while (end > dot + 2 and str[end - 1] == '0') end -= 1; + str = str[0..end]; + } + try writer.writeAll(str); + }, + .object => |object| switch (object.tag) { + .string => { + const typed: *const Object.String = @ptrCast(object); + try writer.writeAll(typed.toSlice()); + }, + else => try writer.print("<{s} {*}>", .{ object.tag.tagBytes(), object }), + }, + } + } }; pub const CallFrame = struct { diff --git a/src/Story/Dumper.zig b/src/Story/Dumper.zig index b6af3b7..1989dfb 100644 --- a/src/Story/Dumper.zig +++ b/src/Story/Dumper.zig @@ -279,20 +279,18 @@ pub fn dumpKnot(self: *Dumper, w: *std.Io.Writer, knot: *const Object.Knot) !voi pub fn dumpValue(_: Dumper, w: *std.Io.Writer, value: *const Value) !void { switch (value.*) { - .bool => |boolean| try w.print("<{s} value={s}>", .{ - value.tagBytes(), - if (boolean) "true" else "false", - }), - .int => |int| try w.print("<{s} value={d}, address={*}>", .{ - value.tagBytes(), - int, - value, - }), - .float => |float| try w.print("<{s} value={d}, address={*}>", .{ - value.tagBytes(), - float, - value, - }), + .bool => |_| try w.print( + "<{s} value={f}>", + .{ value.tagBytes(), value }, + ), + .int => |_| try w.print( + "<{s} value={f}, address={*}>", + .{ value.tagBytes(), value, value }, + ), + .float => |_| try w.print( + "<{s} value={f}, address={*}>", + .{ value.tagBytes(), value, value }, + ), .object => |object| switch (object.tag) { .string => { const typed_object: *const Object.String = @ptrCast(object); diff --git a/src/Story/Object.zig b/src/Story/Object.zig index 6ff1553..e331e31 100644 --- a/src/Story/Object.zig +++ b/src/Story/Object.zig @@ -87,27 +87,16 @@ pub const String = struct { gpa.free(base[0..alloc_len]); } - pub fn toSlice(obj: *Object.String) []const u8 { + pub fn toSlice(obj: *const Object.String) []const u8 { return obj.bytes[0..obj.length]; } pub fn fromValue(story: *Story, value: Story.Value) !*Object.String { - // NOTE: 20 bytes should be enough. - const print_buffer_len = 20; + const print_buffer_len = 64; var print_buffer: [print_buffer_len]u8 = undefined; switch (value) { - .bool => |boolean| { - const bytes = try std.fmt.bufPrint(&print_buffer, "{s}", .{ - if (boolean) "true" else "false", - }); - return .create(story, .{ .bytes = bytes }); - }, - .int => |int| { - const bytes = try std.fmt.bufPrint(&print_buffer, "{d}", .{int}); - return .create(story, .{ .bytes = bytes }); - }, - .float => |float| { - const bytes = try std.fmt.bufPrint(&print_buffer, "{d}", .{float}); + .bool, .int, .float => { + const bytes = try std.fmt.bufPrint(&print_buffer, "{f}", .{value}); return .create(story, .{ .bytes = bytes }); }, .object => |object| switch (object.tag) { diff --git a/src/Story/runtime_tests.zig b/src/Story/runtime_tests.zig index c92bdea..4db4af9 100644 --- a/src/Story/runtime_tests.zig +++ b/src/Story/runtime_tests.zig @@ -18,8 +18,24 @@ test "fixture - constant folding" { try testRuntimeFixture("constant-folding"); } -test "fixture - arithmetic" { - try testRuntimeFixture("arithmetic"); +test "fixture - I118 (Literal unary)" { + try testRuntimeFixture("I118"); +} + +test "fixture - I121 (Arithmetic)" { + try testRuntimeFixture("I121"); +} + +test "fixture - I133 (Float printing precision)" { + try testRuntimeFixture("I133"); +} + +test "fixture - I134 (Native bools)" { + try testRuntimeFixture("I134"); +} + +test "fixture - I135 (Bools can be coerced)" { + try testRuntimeFixture("I135"); } const Options = struct { diff --git a/src/Story/testdata/arithmetic/input.txt b/src/Story/testdata/I118/input.txt similarity index 100% rename from src/Story/testdata/arithmetic/input.txt rename to src/Story/testdata/I118/input.txt diff --git a/src/Story/testdata/I118/story.ink b/src/Story/testdata/I118/story.ink new file mode 100644 index 0000000..a4441d3 --- /dev/null +++ b/src/Story/testdata/I118/story.ink @@ -0,0 +1,6 @@ +VAR negativeLiteral = -1 +VAR negativeLiteral2 = not not false +VAR negativeLiteral3 = !(0) +{negativeLiteral + 0} +{negativeLiteral2 + 0} +{negativeLiteral3 + 0} diff --git a/src/Story/testdata/I118/transcript.txt b/src/Story/testdata/I118/transcript.txt new file mode 100644 index 0000000..6f858bd --- /dev/null +++ b/src/Story/testdata/I118/transcript.txt @@ -0,0 +1,3 @@ +-1 +0 +1 diff --git a/src/Story/testdata/I121/input.txt b/src/Story/testdata/I121/input.txt new file mode 100644 index 0000000..e69de29 diff --git a/src/Story/testdata/arithmetic/story.ink b/src/Story/testdata/I121/story.ink similarity index 100% rename from src/Story/testdata/arithmetic/story.ink rename to src/Story/testdata/I121/story.ink diff --git a/src/Story/testdata/arithmetic/transcript.txt b/src/Story/testdata/I121/transcript.txt similarity index 100% rename from src/Story/testdata/arithmetic/transcript.txt rename to src/Story/testdata/I121/transcript.txt diff --git a/src/Story/testdata/I133/input.txt b/src/Story/testdata/I133/input.txt new file mode 100644 index 0000000..e69de29 diff --git a/src/Story/testdata/I133/story.ink b/src/Story/testdata/I133/story.ink new file mode 100644 index 0000000..716b904 --- /dev/null +++ b/src/Story/testdata/I133/story.ink @@ -0,0 +1 @@ +{ 7 / 3.0 } diff --git a/src/Story/testdata/I133/transcript.txt b/src/Story/testdata/I133/transcript.txt new file mode 100644 index 0000000..18710ca --- /dev/null +++ b/src/Story/testdata/I133/transcript.txt @@ -0,0 +1 @@ +2.3333333 diff --git a/src/Story/testdata/I134/input.txt b/src/Story/testdata/I134/input.txt new file mode 100644 index 0000000..e69de29 diff --git a/src/Story/testdata/I134/story.ink b/src/Story/testdata/I134/story.ink new file mode 100644 index 0000000..9192542 --- /dev/null +++ b/src/Story/testdata/I134/story.ink @@ -0,0 +1,2 @@ +{ 1 == 1 } +{ 1 != 1 } diff --git a/src/Story/testdata/I134/transcript.txt b/src/Story/testdata/I134/transcript.txt new file mode 100644 index 0000000..da29283 --- /dev/null +++ b/src/Story/testdata/I134/transcript.txt @@ -0,0 +1,2 @@ +true +false diff --git a/src/Story/testdata/I135/input.txt b/src/Story/testdata/I135/input.txt new file mode 100644 index 0000000..e69de29 diff --git a/src/Story/testdata/I135/story.ink b/src/Story/testdata/I135/story.ink new file mode 100644 index 0000000..363f220 --- /dev/null +++ b/src/Story/testdata/I135/story.ink @@ -0,0 +1,2 @@ +{ (1 == 1) + 1 } +{ (1 != 1) - 1 } diff --git a/src/Story/testdata/I135/transcript.txt b/src/Story/testdata/I135/transcript.txt new file mode 100644 index 0000000..4bbcfcf --- /dev/null +++ b/src/Story/testdata/I135/transcript.txt @@ -0,0 +1,2 @@ +2 +-1 diff --git a/src/compile.zig b/src/compile.zig index 566bc24..7460ef9 100644 --- a/src/compile.zig +++ b/src/compile.zig @@ -85,11 +85,14 @@ pub const InternPool = struct { code_chunks: std.ArrayListUnmanaged(*Module.CodeChunk) = .empty, pub const Index = enum(u32) { + bool_true, + bool_false, none, _, }; pub const Key = union(enum) { + bool: bool, int: i64, float: u64, // We can't hash floating point numbers. str: Ir.NullTerminatedString, @@ -110,14 +113,16 @@ pub const InternPool = struct { } } + pub fn getOrPutBool(_: *InternPool, value: bool) Index { + return if (value) return .bool_true else .bool_false; + } + pub fn getOrPutInt( ip: *InternPool, gpa: std.mem.Allocator, value: i64, ) error{OutOfMemory}!Index { - return ip.getOrPutValue(gpa, .{ - .int = value, - }); + return ip.getOrPutValue(gpa, .{ .int = value }); } pub fn getOrPutFloat( @@ -125,9 +130,7 @@ pub const InternPool = struct { gpa: std.mem.Allocator, value: f64, ) error{OutOfMemory}!Index { - return ip.getOrPutValue(gpa, .{ - .float = @bitCast(value), - }); + return ip.getOrPutValue(gpa, .{ .float = @bitCast(value) }); } pub fn getOrPutStr( @@ -135,9 +138,7 @@ pub const InternPool = struct { gpa: std.mem.Allocator, value: Ir.NullTerminatedString, ) error{OutOfMemory}!Index { - return ip.getOrPutValue(gpa, .{ - .str = value, - }); + return ip.getOrPutValue(gpa, .{ .str = value }); } pub fn getStrBytes(ip: *InternPool, ir: Ir, index: Index) []const u8 { @@ -370,6 +371,9 @@ pub const Module = struct { }); } } else { + try module.intern_pool.values.append(gpa, .{ .bool = true }); + try module.intern_pool.values.append(gpa, .{ .bool = false }); + try module.intern_pool.values.append(gpa, .{ .str = @enumFromInt(0) }); // TODO: Revisit this. module.generateFile() catch |err| switch (err) { error.OutOfMemory => return error.OutOfMemory, @@ -386,6 +390,7 @@ pub const Module = struct { value: InternPool.Key, ) !Value { switch (value) { + .bool => |boolean| return .{ .bool = boolean }, .int => |int| return .{ .int = @intCast(int) }, .float => |float| return .{ .float = @bitCast(float) }, .str => |str| { diff --git a/src/print_ir.zig b/src/print_ir.zig index 269c98a..a17f00f 100644 --- a/src/print_ir.zig +++ b/src/print_ir.zig @@ -309,6 +309,8 @@ pub const Writer = struct { .mod => try self.writeBinaryInst(w, inst), .neg => try self.writeUnaryInst(w, inst), .not => try self.writeUnaryInst(w, inst), + .bool_and => try self.writeBinaryInst(w, inst), + .bool_or => try self.writeBinaryInst(w, inst), .cmp_eq => try self.writeBinaryInst(w, inst), .cmp_neq => try self.writeBinaryInst(w, inst), .cmp_gt => try self.writeBinaryInst(w, inst),