feat: folding logical operations
This commit is contained in:
parent
92e8bcd866
commit
2260ccda25
24 changed files with 310 additions and 151 deletions
|
|
@ -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,
|
||||
|
|
|
|||
10
src/Ir.zig
10
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,
|
||||
|
|
|
|||
221
src/Sema.zig
221
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),
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
6
src/Story/testdata/I118/story.ink
vendored
Normal file
6
src/Story/testdata/I118/story.ink
vendored
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
VAR negativeLiteral = -1
|
||||
VAR negativeLiteral2 = not not false
|
||||
VAR negativeLiteral3 = !(0)
|
||||
{negativeLiteral + 0}
|
||||
{negativeLiteral2 + 0}
|
||||
{negativeLiteral3 + 0}
|
||||
3
src/Story/testdata/I118/transcript.txt
vendored
Normal file
3
src/Story/testdata/I118/transcript.txt
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
-1
|
||||
0
|
||||
1
|
||||
0
src/Story/testdata/I121/input.txt
vendored
Normal file
0
src/Story/testdata/I121/input.txt
vendored
Normal file
0
src/Story/testdata/I133/input.txt
vendored
Normal file
0
src/Story/testdata/I133/input.txt
vendored
Normal file
1
src/Story/testdata/I133/story.ink
vendored
Normal file
1
src/Story/testdata/I133/story.ink
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
{ 7 / 3.0 }
|
||||
1
src/Story/testdata/I133/transcript.txt
vendored
Normal file
1
src/Story/testdata/I133/transcript.txt
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
2.3333333
|
||||
0
src/Story/testdata/I134/input.txt
vendored
Normal file
0
src/Story/testdata/I134/input.txt
vendored
Normal file
2
src/Story/testdata/I134/story.ink
vendored
Normal file
2
src/Story/testdata/I134/story.ink
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
{ 1 == 1 }
|
||||
{ 1 != 1 }
|
||||
2
src/Story/testdata/I134/transcript.txt
vendored
Normal file
2
src/Story/testdata/I134/transcript.txt
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
true
|
||||
false
|
||||
0
src/Story/testdata/I135/input.txt
vendored
Normal file
0
src/Story/testdata/I135/input.txt
vendored
Normal file
2
src/Story/testdata/I135/story.ink
vendored
Normal file
2
src/Story/testdata/I135/story.ink
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
{ (1 == 1) + 1 }
|
||||
{ (1 != 1) - 1 }
|
||||
2
src/Story/testdata/I135/transcript.txt
vendored
Normal file
2
src/Story/testdata/I135/transcript.txt
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
2
|
||||
-1
|
||||
|
|
@ -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| {
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue