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,
|
op: Ir.Inst.Tag,
|
||||||
) InnerError!Ir.Inst.Ref {
|
) InnerError!Ir.Inst.Ref {
|
||||||
const data = expr_node.data.bin;
|
const data = expr_node.data.bin;
|
||||||
assert(data.lhs != null and data.rhs != null);
|
|
||||||
const lhs = try expr(gi, scope, data.lhs.?);
|
const lhs = try expr(gi, scope, data.lhs.?);
|
||||||
const rhs = try expr(gi, scope, data.rhs.?);
|
const rhs = try expr(gi, scope, data.rhs.?);
|
||||||
return gi.addBinaryNode(op, lhs, 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) {
|
fn parseNumberLiteral(bytes: []const u8) union(enum) {
|
||||||
int: i64,
|
int: i64,
|
||||||
float: f64,
|
float: f64,
|
||||||
|
|
@ -731,39 +704,39 @@ fn identifier(
|
||||||
return block.addStrTok(.decl_ref, str.index, node.loc.start);
|
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 {
|
fn expr(gi: *GenIr, scope: *Scope, optional_node: ?*const Ast.Node) InnerError!Ir.Inst.Ref {
|
||||||
const expr_node = optional_expr.?;
|
const node = optional_node.?;
|
||||||
switch (expr_node.tag) {
|
switch (node.tag) {
|
||||||
.file => unreachable,
|
.file => unreachable,
|
||||||
.true_literal => return .bool_true,
|
.true_literal => return .bool_true,
|
||||||
.false_literal => return .bool_false,
|
.false_literal => return .bool_false,
|
||||||
.number_literal => return numberLiteral(gi, expr_node),
|
.number_literal => return numberLiteral(gi, node),
|
||||||
.string_literal => return stringLiteral(gi, expr_node),
|
.string_literal => return stringLiteral(gi, node),
|
||||||
.string_expr => return stringExpr(gi, expr_node),
|
.string_expr => return stringExpr(gi, node),
|
||||||
.empty_string => return stringLiteral(gi, expr_node),
|
.empty_string => return stringLiteral(gi, node),
|
||||||
.identifier => return identifier(gi, scope, expr_node),
|
.identifier => return identifier(gi, scope, node),
|
||||||
.add_expr => return binaryOp(gi, scope, expr_node, .add),
|
.add_expr => return binaryOp(gi, scope, node, .add),
|
||||||
.subtract_expr => return binaryOp(gi, scope, expr_node, .sub),
|
.subtract_expr => return binaryOp(gi, scope, node, .sub),
|
||||||
.multiply_expr => return binaryOp(gi, scope, expr_node, .mul),
|
.multiply_expr => return binaryOp(gi, scope, node, .mul),
|
||||||
.divide_expr => return binaryOp(gi, scope, expr_node, .div),
|
.divide_expr => return binaryOp(gi, scope, node, .div),
|
||||||
.mod_expr => return binaryOp(gi, scope, expr_node, .mod),
|
.mod_expr => return binaryOp(gi, scope, node, .mod),
|
||||||
.negate_expr => return unaryOp(gi, scope, expr_node, .neg),
|
.negate_expr => return unaryOp(gi, scope, node, .neg),
|
||||||
.logical_and_expr => unreachable,
|
.logical_not_expr => return unaryOp(gi, scope, node, .not),
|
||||||
.logical_or_expr => unreachable,
|
.logical_and_expr => return binaryOp(gi, scope, node, .bool_and),
|
||||||
.logical_not_expr => return unaryOp(gi, scope, expr_node, .not),
|
.logical_or_expr => return binaryOp(gi, scope, node, .bool_or),
|
||||||
.logical_equality_expr => return binaryOp(gi, scope, expr_node, .cmp_eq),
|
.logical_equality_expr => return binaryOp(gi, scope, node, .cmp_eq),
|
||||||
.logical_inequality_expr => return binaryOp(gi, scope, expr_node, .cmp_neq),
|
.logical_inequality_expr => return binaryOp(gi, scope, node, .cmp_neq),
|
||||||
.logical_greater_expr => return binaryOp(gi, scope, expr_node, .cmp_gt),
|
.logical_greater_expr => return binaryOp(gi, scope, node, .cmp_gt),
|
||||||
.logical_greater_or_equal_expr => return binaryOp(gi, scope, expr_node, .cmp_gte),
|
.logical_greater_or_equal_expr => return binaryOp(gi, scope, node, .cmp_gte),
|
||||||
.logical_lesser_expr => return binaryOp(gi, scope, expr_node, .cmp_lt),
|
.logical_lesser_expr => return binaryOp(gi, scope, node, .cmp_lt),
|
||||||
.logical_lesser_or_equal_expr => return binaryOp(gi, scope, expr_node, .cmp_lte),
|
.logical_lesser_or_equal_expr => return binaryOp(gi, scope, node, .cmp_lte),
|
||||||
.call_expr => unreachable,
|
.call_expr => unreachable,
|
||||||
.choice_expr => unreachable,
|
.choice_expr => unreachable,
|
||||||
.choice_start_expr => unreachable,
|
.choice_start_expr => unreachable,
|
||||||
.choice_option_expr => unreachable,
|
.choice_option_expr => unreachable,
|
||||||
.choice_inner_expr => unreachable,
|
.choice_inner_expr => unreachable,
|
||||||
.divert_expr => unreachable,
|
.divert_expr => unreachable,
|
||||||
.selector_expr => return fieldAccess(gi, scope, expr_node),
|
.selector_expr => return fieldAccess(gi, scope, node),
|
||||||
.assign_stmt => unreachable,
|
.assign_stmt => unreachable,
|
||||||
.block_stmt => unreachable,
|
.block_stmt => unreachable,
|
||||||
.content_stmt => unreachable,
|
.content_stmt => unreachable,
|
||||||
|
|
|
||||||
10
src/Ir.zig
10
src/Ir.zig
|
|
@ -170,11 +170,21 @@ pub const Inst = struct {
|
||||||
neg,
|
neg,
|
||||||
/// Uses the `un` union field.
|
/// Uses the `un` union field.
|
||||||
not,
|
not,
|
||||||
|
/// Uses the `bin` union field.
|
||||||
|
bool_and,
|
||||||
|
/// Uses the `bin` union field.
|
||||||
|
bool_or,
|
||||||
|
/// Uses the `bin` union field.
|
||||||
cmp_eq,
|
cmp_eq,
|
||||||
|
/// Uses the `bin` union field.
|
||||||
cmp_neq,
|
cmp_neq,
|
||||||
|
/// Uses the `bin` union field.
|
||||||
cmp_gt,
|
cmp_gt,
|
||||||
|
/// Uses the `bin` union field.
|
||||||
cmp_gte,
|
cmp_gte,
|
||||||
|
/// Uses the `bin` union field.
|
||||||
cmp_lt,
|
cmp_lt,
|
||||||
|
/// Uses the `bin` union field.
|
||||||
cmp_lte,
|
cmp_lte,
|
||||||
/// Uses the `int` union field.
|
/// Uses the `int` union field.
|
||||||
int,
|
int,
|
||||||
|
|
|
||||||
221
src/Sema.zig
221
src/Sema.zig
|
|
@ -35,19 +35,41 @@ pub const Value = struct {
|
||||||
ip_index: InternPool.Index,
|
ip_index: InternPool.Index,
|
||||||
|
|
||||||
pub const Unwrapped = union(enum) {
|
pub const Unwrapped = union(enum) {
|
||||||
|
bool: bool,
|
||||||
int: i64,
|
int: i64,
|
||||||
float: f64,
|
float: f64,
|
||||||
|
|
||||||
fn toFloat(v: Value.Unwrapped) f64 {
|
pub fn toFloat(v: Unwrapped) f64 {
|
||||||
return switch (v) {
|
return switch (v) {
|
||||||
.int => |i| @floatFromInt(i),
|
.bool => |boolean| @floatFromInt(@intFromBool(boolean)),
|
||||||
.float => |f| f,
|
.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 {
|
pub fn unwrap(value: Value, ip: *InternPool) Unwrapped {
|
||||||
switch (ip.values.items[@intFromEnum(value.toInterned())]) {
|
switch (ip.values.items[@intFromEnum(value.toInterned())]) {
|
||||||
|
.bool => |boolean| return .{ .bool = boolean },
|
||||||
.int => |int| return .{ .int = int },
|
.int => |int| return .{ .int = int },
|
||||||
.float => |float| return .{ .float = @bitCast(float) },
|
.float => |float| return .{ .float = @bitCast(float) },
|
||||||
.str => @panic("String unwrapping not implemented!"),
|
.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(
|
fn foldConstant(
|
||||||
lhs: Value.Unwrapped,
|
lhs: Value.Unwrapped,
|
||||||
rhs: Value.Unwrapped,
|
rhs: Value.Unwrapped,
|
||||||
op: Story.Opcode,
|
op: Story.Opcode,
|
||||||
) !Value.Unwrapped {
|
) !Value.Unwrapped {
|
||||||
if (lhs == .int and rhs == .int) {
|
|
||||||
switch (op) {
|
switch (op) {
|
||||||
.add => return .{ .int = lhs.int +% rhs.int },
|
.add,
|
||||||
.sub => return .{ .int = lhs.int -% rhs.int },
|
.sub,
|
||||||
.mul => return .{ .int = lhs.int *% rhs.int },
|
.mul,
|
||||||
.div => {
|
.div,
|
||||||
if (rhs.int == 0)
|
.mod,
|
||||||
return error.DivisionByZero;
|
=> return foldArith(lhs, rhs, op),
|
||||||
return .{ .int = @divTrunc(lhs.int, rhs.int) };
|
.cmp_eq,
|
||||||
},
|
.cmp_neq,
|
||||||
.mod => if (rhs.int == 0)
|
.cmp_lt,
|
||||||
return error.DivisionByZero
|
.cmp_gt,
|
||||||
else
|
.cmp_lte,
|
||||||
return .{ .int = @mod(lhs.int, rhs.int) },
|
.cmp_gte,
|
||||||
else => unreachable,
|
=> return foldCmp(lhs, rhs, op),
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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) },
|
|
||||||
else => unreachable,
|
else => unreachable,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -351,20 +420,27 @@ fn irUnaryOp(
|
||||||
//const lhs_src: SrcLoc = .{ .src_offset = 0 };
|
//const lhs_src: SrcLoc = .{ .src_offset = 0 };
|
||||||
//try sema.analyzeArithmeticArg(builder, lhs, lhs_src);
|
//try sema.analyzeArithmeticArg(builder, lhs, lhs_src);
|
||||||
|
|
||||||
if (sema.resolveValue(lhs)) |lhs_value| {
|
if (sema.resolveValue(lhs)) |lhs_info| {
|
||||||
const lhs_unwrapped = lhs_value.unwrap(ip);
|
switch (lhs_info.unwrap(ip)) {
|
||||||
switch (lhs_unwrapped) {
|
.bool => |boolean| {
|
||||||
.int => |int| {
|
|
||||||
const new_value = switch (op) {
|
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,
|
.neg => -int,
|
||||||
else => unreachable,
|
else => unreachable,
|
||||||
};
|
};
|
||||||
return .{ .value = try ip.getOrPutInt(gpa, new_value) };
|
return .{ .value = try ip.getOrPutInt(gpa, new_value) };
|
||||||
},
|
},
|
||||||
.float => |float| {
|
.float => |float| {
|
||||||
const new_value = switch (op) {
|
const new_value: f64 = switch (op) {
|
||||||
.not => return error.TypeError,
|
.not => if (float > 0.0) 0.0 else 1.0,
|
||||||
.neg => -float,
|
.neg => -float,
|
||||||
else => unreachable,
|
else => unreachable,
|
||||||
};
|
};
|
||||||
|
|
@ -384,8 +460,9 @@ fn irBinaryOp(
|
||||||
inst: Ir.Inst.Index,
|
inst: Ir.Inst.Index,
|
||||||
op: Story.Opcode,
|
op: Story.Opcode,
|
||||||
) InnerError!ValueInfo {
|
) InnerError!ValueInfo {
|
||||||
const data = sema.ir.instructions[@intFromEnum(inst)].data.bin;
|
const gpa = sema.gpa;
|
||||||
const ip = &sema.module.intern_pool;
|
const ip = &sema.module.intern_pool;
|
||||||
|
const data = sema.ir.instructions[@intFromEnum(inst)].data.bin;
|
||||||
const lhs = sema.resolveInst(data.lhs);
|
const lhs = sema.resolveInst(data.lhs);
|
||||||
const rhs = sema.resolveInst(data.rhs);
|
const rhs = sema.resolveInst(data.rhs);
|
||||||
//const lhs_src: SrcLoc = .{ .src_offset = 0 };
|
//const lhs_src: SrcLoc = .{ .src_offset = 0 };
|
||||||
|
|
@ -395,16 +472,13 @@ fn irBinaryOp(
|
||||||
|
|
||||||
if (sema.resolveValue(lhs)) |lhs_value| {
|
if (sema.resolveValue(lhs)) |lhs_value| {
|
||||||
if (sema.resolveValue(rhs)) |rhs_value| {
|
if (sema.resolveValue(rhs)) |rhs_value| {
|
||||||
const lhs_unwrap = lhs_value.unwrap(ip);
|
const lhs_coerced = lhs_value.unwrap(ip).coerce();
|
||||||
const rhs_unwrap = rhs_value.unwrap(ip);
|
const rhs_coerced = rhs_value.unwrap(ip).coerce();
|
||||||
switch (try foldConstant(lhs_unwrap, rhs_unwrap, op)) {
|
return switch (try foldConstant(lhs_coerced, rhs_coerced, op)) {
|
||||||
.int => |int| return .{
|
.bool => |boolean| .{ .value = ip.getOrPutBool(boolean) },
|
||||||
.value = try ip.getOrPutInt(sema.gpa, int),
|
.int => |int| .{ .value = try ip.getOrPutInt(gpa, int) },
|
||||||
},
|
.float => |float| .{ .value = try ip.getOrPutFloat(gpa, float) },
|
||||||
.float => |float| return .{
|
};
|
||||||
.value = try ip.getOrPutFloat(sema.gpa, float),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -414,6 +488,40 @@ fn irBinaryOp(
|
||||||
return .none;
|
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(
|
fn irDeclRef(
|
||||||
sema: *Sema,
|
sema: *Sema,
|
||||||
builder: *Builder,
|
builder: *Builder,
|
||||||
|
|
@ -672,7 +780,6 @@ fn irDivert(
|
||||||
sema.gpa,
|
sema.gpa,
|
||||||
extra.data.field_name_start,
|
extra.data.field_name_start,
|
||||||
);
|
);
|
||||||
std.debug.print("Target: {any}\n", .{target});
|
|
||||||
const e = try sema.lookupInNamespace(target.namespace.?, ip_index, callee_src);
|
const e = try sema.lookupInNamespace(target.namespace.?, ip_index, callee_src);
|
||||||
switch (e.tag) {
|
switch (e.tag) {
|
||||||
.knot => {
|
.knot => {
|
||||||
|
|
@ -782,12 +889,10 @@ fn analyzeBodyInner(
|
||||||
.mod => try irBinaryOp(sema, builder, inst, .mod),
|
.mod => try irBinaryOp(sema, builder, inst, .mod),
|
||||||
.neg => try irUnaryOp(sema, builder, inst, .neg),
|
.neg => try irUnaryOp(sema, builder, inst, .neg),
|
||||||
.not => try irUnaryOp(sema, builder, inst, .not),
|
.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_eq => try irBinaryOp(sema, builder, inst, .cmp_eq),
|
||||||
.cmp_neq => blk: {
|
.cmp_neq => try irBinaryOp(sema, builder, inst, .cmp_neq),
|
||||||
const val = try irBinaryOp(sema, builder, inst, .cmp_eq);
|
|
||||||
try builder.addByteOp(.not);
|
|
||||||
break :blk val;
|
|
||||||
},
|
|
||||||
.cmp_lt => try irBinaryOp(sema, builder, inst, .cmp_lt),
|
.cmp_lt => try irBinaryOp(sema, builder, inst, .cmp_lt),
|
||||||
.cmp_lte => try irBinaryOp(sema, builder, inst, .cmp_lte),
|
.cmp_lte => try irBinaryOp(sema, builder, inst, .cmp_lte),
|
||||||
.cmp_gt => try irBinaryOp(sema, builder, inst, .cmp_gt),
|
.cmp_gt => try irBinaryOp(sema, builder, inst, .cmp_gt),
|
||||||
|
|
|
||||||
|
|
@ -46,6 +46,15 @@ pub const Value = union(enum) {
|
||||||
return v == .int or v == .float;
|
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 {
|
pub fn isTruthy(v: Value) bool {
|
||||||
return switch (v) {
|
return switch (v) {
|
||||||
//.nil => false,
|
//.nil => false,
|
||||||
|
|
@ -69,8 +78,11 @@ pub const Value = union(enum) {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn arith(lhs: Value, rhs: Value, op: Opcode, story: *Story) !Value {
|
pub fn arith(lhs: Value, rhs: Value, op: Opcode, story: *Story) !Value {
|
||||||
if (lhs.isNumeric() and rhs.isNumeric())
|
if (lhs.coerce()) |l| {
|
||||||
return numericArith(lhs, rhs, op);
|
if (rhs.coerce()) |r| {
|
||||||
|
return numericArith(l, r, op);
|
||||||
|
}
|
||||||
|
}
|
||||||
if (op == .add) {
|
if (op == .add) {
|
||||||
if (lhs == .object and lhs.object.tag == .string)
|
if (lhs == .object and lhs.object.tag == .string)
|
||||||
return concat(lhs, rhs, story);
|
return concat(lhs, rhs, story);
|
||||||
|
|
@ -177,6 +189,36 @@ pub const Value = union(enum) {
|
||||||
.object => return error.TypeError,
|
.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 {
|
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 {
|
pub fn dumpValue(_: Dumper, w: *std.Io.Writer, value: *const Value) !void {
|
||||||
switch (value.*) {
|
switch (value.*) {
|
||||||
.bool => |boolean| try w.print("<{s} value={s}>", .{
|
.bool => |_| try w.print(
|
||||||
value.tagBytes(),
|
"<{s} value={f}>",
|
||||||
if (boolean) "true" else "false",
|
.{ value.tagBytes(), value },
|
||||||
}),
|
),
|
||||||
.int => |int| try w.print("<{s} value={d}, address={*}>", .{
|
.int => |_| try w.print(
|
||||||
value.tagBytes(),
|
"<{s} value={f}, address={*}>",
|
||||||
int,
|
.{ value.tagBytes(), value, value },
|
||||||
value,
|
),
|
||||||
}),
|
.float => |_| try w.print(
|
||||||
.float => |float| try w.print("<{s} value={d}, address={*}>", .{
|
"<{s} value={f}, address={*}>",
|
||||||
value.tagBytes(),
|
.{ value.tagBytes(), value, value },
|
||||||
float,
|
),
|
||||||
value,
|
|
||||||
}),
|
|
||||||
.object => |object| switch (object.tag) {
|
.object => |object| switch (object.tag) {
|
||||||
.string => {
|
.string => {
|
||||||
const typed_object: *const Object.String = @ptrCast(object);
|
const typed_object: *const Object.String = @ptrCast(object);
|
||||||
|
|
|
||||||
|
|
@ -87,27 +87,16 @@ pub const String = struct {
|
||||||
gpa.free(base[0..alloc_len]);
|
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];
|
return obj.bytes[0..obj.length];
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn fromValue(story: *Story, value: Story.Value) !*Object.String {
|
pub fn fromValue(story: *Story, value: Story.Value) !*Object.String {
|
||||||
// NOTE: 20 bytes should be enough.
|
const print_buffer_len = 64;
|
||||||
const print_buffer_len = 20;
|
|
||||||
var print_buffer: [print_buffer_len]u8 = undefined;
|
var print_buffer: [print_buffer_len]u8 = undefined;
|
||||||
switch (value) {
|
switch (value) {
|
||||||
.bool => |boolean| {
|
.bool, .int, .float => {
|
||||||
const bytes = try std.fmt.bufPrint(&print_buffer, "{s}", .{
|
const bytes = try std.fmt.bufPrint(&print_buffer, "{f}", .{value});
|
||||||
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});
|
|
||||||
return .create(story, .{ .bytes = bytes });
|
return .create(story, .{ .bytes = bytes });
|
||||||
},
|
},
|
||||||
.object => |object| switch (object.tag) {
|
.object => |object| switch (object.tag) {
|
||||||
|
|
|
||||||
|
|
@ -18,8 +18,24 @@ test "fixture - constant folding" {
|
||||||
try testRuntimeFixture("constant-folding");
|
try testRuntimeFixture("constant-folding");
|
||||||
}
|
}
|
||||||
|
|
||||||
test "fixture - arithmetic" {
|
test "fixture - I118 (Literal unary)" {
|
||||||
try testRuntimeFixture("arithmetic");
|
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 {
|
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,
|
code_chunks: std.ArrayListUnmanaged(*Module.CodeChunk) = .empty,
|
||||||
|
|
||||||
pub const Index = enum(u32) {
|
pub const Index = enum(u32) {
|
||||||
|
bool_true,
|
||||||
|
bool_false,
|
||||||
none,
|
none,
|
||||||
_,
|
_,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const Key = union(enum) {
|
pub const Key = union(enum) {
|
||||||
|
bool: bool,
|
||||||
int: i64,
|
int: i64,
|
||||||
float: u64, // We can't hash floating point numbers.
|
float: u64, // We can't hash floating point numbers.
|
||||||
str: Ir.NullTerminatedString,
|
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(
|
pub fn getOrPutInt(
|
||||||
ip: *InternPool,
|
ip: *InternPool,
|
||||||
gpa: std.mem.Allocator,
|
gpa: std.mem.Allocator,
|
||||||
value: i64,
|
value: i64,
|
||||||
) error{OutOfMemory}!Index {
|
) error{OutOfMemory}!Index {
|
||||||
return ip.getOrPutValue(gpa, .{
|
return ip.getOrPutValue(gpa, .{ .int = value });
|
||||||
.int = value,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn getOrPutFloat(
|
pub fn getOrPutFloat(
|
||||||
|
|
@ -125,9 +130,7 @@ pub const InternPool = struct {
|
||||||
gpa: std.mem.Allocator,
|
gpa: std.mem.Allocator,
|
||||||
value: f64,
|
value: f64,
|
||||||
) error{OutOfMemory}!Index {
|
) error{OutOfMemory}!Index {
|
||||||
return ip.getOrPutValue(gpa, .{
|
return ip.getOrPutValue(gpa, .{ .float = @bitCast(value) });
|
||||||
.float = @bitCast(value),
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn getOrPutStr(
|
pub fn getOrPutStr(
|
||||||
|
|
@ -135,9 +138,7 @@ pub const InternPool = struct {
|
||||||
gpa: std.mem.Allocator,
|
gpa: std.mem.Allocator,
|
||||||
value: Ir.NullTerminatedString,
|
value: Ir.NullTerminatedString,
|
||||||
) error{OutOfMemory}!Index {
|
) error{OutOfMemory}!Index {
|
||||||
return ip.getOrPutValue(gpa, .{
|
return ip.getOrPutValue(gpa, .{ .str = value });
|
||||||
.str = value,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn getStrBytes(ip: *InternPool, ir: Ir, index: Index) []const u8 {
|
pub fn getStrBytes(ip: *InternPool, ir: Ir, index: Index) []const u8 {
|
||||||
|
|
@ -370,6 +371,9 @@ pub const Module = struct {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} else {
|
} 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.
|
// TODO: Revisit this.
|
||||||
module.generateFile() catch |err| switch (err) {
|
module.generateFile() catch |err| switch (err) {
|
||||||
error.OutOfMemory => return error.OutOfMemory,
|
error.OutOfMemory => return error.OutOfMemory,
|
||||||
|
|
@ -386,6 +390,7 @@ pub const Module = struct {
|
||||||
value: InternPool.Key,
|
value: InternPool.Key,
|
||||||
) !Value {
|
) !Value {
|
||||||
switch (value) {
|
switch (value) {
|
||||||
|
.bool => |boolean| return .{ .bool = boolean },
|
||||||
.int => |int| return .{ .int = @intCast(int) },
|
.int => |int| return .{ .int = @intCast(int) },
|
||||||
.float => |float| return .{ .float = @bitCast(float) },
|
.float => |float| return .{ .float = @bitCast(float) },
|
||||||
.str => |str| {
|
.str => |str| {
|
||||||
|
|
|
||||||
|
|
@ -309,6 +309,8 @@ pub const Writer = struct {
|
||||||
.mod => try self.writeBinaryInst(w, inst),
|
.mod => try self.writeBinaryInst(w, inst),
|
||||||
.neg => try self.writeUnaryInst(w, inst),
|
.neg => try self.writeUnaryInst(w, inst),
|
||||||
.not => 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_eq => try self.writeBinaryInst(w, inst),
|
||||||
.cmp_neq => try self.writeBinaryInst(w, inst),
|
.cmp_neq => try self.writeBinaryInst(w, inst),
|
||||||
.cmp_gt => try self.writeBinaryInst(w, inst),
|
.cmp_gt => try self.writeBinaryInst(w, inst),
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue