Skip to content

Commit

Permalink
feat: Type as values (#21)
Browse files Browse the repository at this point in the history
  • Loading branch information
giann committed Aug 21, 2023
1 parent 6cdfc3b commit c5a24f1
Show file tree
Hide file tree
Showing 10 changed files with 345 additions and 16 deletions.
1 change: 1 addition & 0 deletions src/chunk.zig
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,7 @@ pub const OpCode = enum(u8) {
OP_IMPORT,

OP_TO_STRING,
OP_TYPEOF,
};

/// A chunk of code to execute
Expand Down
1 change: 1 addition & 0 deletions src/disassembler.zig
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,7 @@ pub fn disassembleInstruction(chunk: *Chunk, offset: usize) !usize {
.OP_RESOLVE,
.OP_TRY_END,
.OP_GET_ENUM_CASE_FROM_VALUE,
.OP_TYPEOF,
=> simpleInstruction(instruction, offset),

.OP_SWAP => bytesInstruction(instruction, chunk, offset),
Expand Down
183 changes: 183 additions & 0 deletions src/node.zig
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,8 @@ pub const ParseNodeType = enum(u8) {
Try,
Range,
Zdef,
TypeExpression,
TypeOfExpression,
};

pub const RenderError = Allocator.Error || std.fmt.BufPrintError;
Expand Down Expand Up @@ -7823,6 +7825,187 @@ pub const ExportNode = struct {
}
};

pub const TypeExpressionNode = struct {
const Self = @This();

node: ParseNode = .{
.node_type = .TypeExpression,
.toJson = stringify,
.toByteCode = generate,
.toValue = val,
.isConstant = constant,
.render = render,
},

value: Value,

fn constant(_: *anyopaque) bool {
return true;
}

fn val(nodePtr: *anyopaque, _: *GarbageCollector) anyerror!Value {
const node: *ParseNode = @ptrCast(@alignCast(nodePtr));
return Self.cast(node).?.value;
}

fn generate(nodePtr: *anyopaque, codegenPtr: *anyopaque, _: ?*std.ArrayList(usize)) anyerror!?*ObjFunction {
const codegen: *CodeGen = @ptrCast(@alignCast(codegenPtr));
const node: *ParseNode = @ptrCast(@alignCast(nodePtr));

if (node.synchronize(codegen)) {
return null;
}

const self = Self.cast(node).?;

try codegen.emitConstant(self.node.location, self.value);

try node.patchOptJumps(codegen);
try node.endScope(codegen);

return null;
}

fn stringify(nodePtr: *anyopaque, out: *const std.ArrayList(u8).Writer) RenderError!void {
var node: *ParseNode = @ptrCast(@alignCast(nodePtr));
var self = Self.cast(node).?;

try out.print("{{\"node\": \"TypeExpression\", \"value\": \"", .{});

try ObjTypeDef.cast(self.value.obj()).?.toString(out);

try out.print("\", ", .{});

try ParseNode.stringify(node, out);

try out.writeAll("}");
}

fn render(nodePtr: *anyopaque, out: *const std.ArrayList(u8).Writer, depth: usize) RenderError!void {
const node: *ParseNode = @ptrCast(@alignCast(nodePtr));
const self = Self.cast(node).?;

try out.writeByteNTimes(' ', depth * 4);

try out.print("<", .{});

try ObjTypeDef.cast(self.value.obj()).?.toString(out);

try out.writeAll(">");
}

pub fn toNode(self: *Self) *ParseNode {
return &self.node;
}

pub fn cast(nodePtr: *anyopaque) ?*Self {
var node: *ParseNode = @ptrCast(@alignCast(nodePtr));

if (node.node_type != .TypeExpression) {
return null;
}

return @fieldParentPtr(Self, "node", node);
}
};

pub const TypeOfExpressionNode = struct {
const Self = @This();

node: ParseNode = .{
.node_type = .TypeOfExpression,
.toJson = stringify,
.toByteCode = generate,
.toValue = val,
.isConstant = constant,
.render = render,
},

expression: *ParseNode,

fn constant(nodePtr: *anyopaque) bool {
const node: *ParseNode = @ptrCast(@alignCast(nodePtr));
const self = Self.cast(node).?;

return self.expression.isConstant(self.expression);
}

fn val(nodePtr: *anyopaque, gc: *GarbageCollector) anyerror!Value {
const node: *ParseNode = @ptrCast(@alignCast(nodePtr));
if (node.isConstant(node)) {
const self = Self.cast(node).?;

const expr = try self.expression.toValue(self.expression, gc);

return (try Value.typeOf(expr, gc)).toValue();
}

return GenError.NotConstant;
}

fn generate(nodePtr: *anyopaque, codegenPtr: *anyopaque, breaks: ?*std.ArrayList(usize)) anyerror!?*ObjFunction {
const codegen: *CodeGen = @ptrCast(@alignCast(codegenPtr));
const node: *ParseNode = @ptrCast(@alignCast(nodePtr));

if (node.synchronize(codegen)) {
return null;
}

const self = Self.cast(node).?;

_ = try self.expression.toByteCode(self.expression, codegen, breaks);

try codegen.emitOpCode(node.location, .OP_TYPEOF);

try node.patchOptJumps(codegen);
try node.endScope(codegen);

return null;
}

fn stringify(nodePtr: *anyopaque, out: *const std.ArrayList(u8).Writer) RenderError!void {
var node: *ParseNode = @ptrCast(@alignCast(nodePtr));
var self = Self.cast(node).?;

try out.print("{{\"node\": \"TypeOfExpression\", \"expression\": \"", .{});

try self.expression.toJson(self.expression, out);

try out.print(", ", .{});

try ParseNode.stringify(node, out);

try out.writeAll("}");
}

fn render(nodePtr: *anyopaque, out: *const std.ArrayList(u8).Writer, depth: usize) RenderError!void {
const node: *ParseNode = @ptrCast(@alignCast(nodePtr));
const self = Self.cast(node).?;

try out.writeByteNTimes(' ', depth * 4);

try out.print("<", .{});

try self.expression.render(self.expression, out, depth);

try out.writeAll(">");
}

pub fn toNode(self: *Self) *ParseNode {
return &self.node;
}

pub fn cast(nodePtr: *anyopaque) ?*Self {
var node: *ParseNode = @ptrCast(@alignCast(nodePtr));

if (node.node_type != .TypeOfExpression) {
return null;
}

return @fieldParentPtr(Self, "node", node);
}
};

pub const ZdefNode = struct {
const Self = @This();

Expand Down
40 changes: 38 additions & 2 deletions src/obj.zig
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,42 @@ pub const Obj = struct {
return obj.cast(T, obj_type);
}

pub fn typeOf(self: *Self, gc: *GarbageCollector) anyerror!*ObjTypeDef {
return switch (self.obj_type) {
.String => try gc.type_registry.getTypeDef(.{ .def_type = .String }),
.Pattern => try gc.type_registry.getTypeDef(.{ .def_type = .Pattern }),
.Fiber => try gc.type_registry.getTypeDef(.{ .def_type = .Fiber }),
.Type, .Object, .Enum => try gc.type_registry.getTypeDef(.{ .def_type = .Type }),
.ObjectInstance => instance: {
const obj_instance = ObjObjectInstance.cast(self).?;

if (obj_instance.object) |object| {
break :instance object.type_def;
} else {
break :instance obj_instance.type_def.?;
}
},
.EnumInstance => ObjEnumInstance.cast(self).?.enum_ref.type_def,
.Function => ObjFunction.cast(self).?.type_def,
.UpValue => upvalue: {
const upvalue: *ObjUpValue = ObjUpValue.cast(self).?;

break :upvalue (upvalue.closed orelse upvalue.location.*).typeOf(gc);
},
.Closure => ObjClosure.cast(self).?.function.type_def,
.List => ObjList.cast(self).?.type_def,
.Map => ObjMap.cast(self).?.type_def,
.Bound => bound: {
const bound: *ObjBoundMethod = ObjBoundMethod.cast(self).?;
break :bound try (if (bound.closure) |cls| cls.function.toValue() else bound.native.?.toValue()).typeOf(gc);
},
.ForeignStruct => ObjForeignStruct.cast(self).?.type_def,
.UserData => try gc.type_registry.getTypeDef(.{ .def_type = .UserData }),
// FIXME: apart from list/map types we actually can embark typedef of objnatives at runtime
.Native => unreachable,
};
}

pub fn is(self: *Self, type_def: *ObjTypeDef) bool {
return switch (self.obj_type) {
.String => type_def.def_type == .String,
Expand Down Expand Up @@ -110,8 +146,8 @@ pub const Obj = struct {
);
},
.ForeignStruct => type_def.def_type == .ForeignStruct and ObjForeignStruct.cast(self).?.is(type_def),

.UserData, .Native => unreachable, // TODO: we don't know how to embark NativeFn type at runtime yet
.UserData => type_def.def_type == .UserData,
.Native => unreachable, // TODO: we don't know how to embark NativeFn type at runtime yet
};
}

Expand Down
61 changes: 49 additions & 12 deletions src/parser.zig
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,8 @@ const ImportNode = _node.ImportNode;
const RangeNode = _node.RangeNode;
const TryNode = _node.TryNode;
const ZdefNode = _node.ZdefNode;
const TypeExpressionNode = _node.TypeExpressionNode;
const TypeOfExpressionNode = _node.TypeOfExpressionNode;
const ParsedArg = _node.ParsedArg;
const OpCode = _chunk.OpCode;
const TypeRegistry = _obj.TypeRegistry;
Expand Down Expand Up @@ -310,7 +312,7 @@ pub const Parser = struct {
.{ .prefix = null, .infix = null, .precedence = .None }, // Comma
.{ .prefix = null, .infix = null, .precedence = .None }, // Semicolon
.{ .prefix = null, .infix = binary, .precedence = .Comparison }, // Greater
.{ .prefix = null, .infix = binary, .precedence = .Comparison }, // Less
.{ .prefix = typeExpression, .infix = binary, .precedence = .Comparison }, // Less
.{ .prefix = null, .infix = binary, .precedence = .Term }, // Plus
.{ .prefix = unary, .infix = binary, .precedence = .Term }, // Minus
.{ .prefix = null, .infix = binary, .precedence = .Factor }, // Star
Expand Down Expand Up @@ -397,8 +399,9 @@ pub const Parser = struct {
.{ .prefix = resolveFiber, .infix = null, .precedence = .Primary }, // resolve
.{ .prefix = yield, .infix = null, .precedence = .Primary }, // yield
.{ .prefix = null, .infix = range, .precedence = .Primary }, // ..
.{ .prefix = null, .infix = null, .precedence = .None }, // Any
.{ .prefix = null, .infix = null, .precedence = .None }, // Zdf
.{ .prefix = null, .infix = null, .precedence = .None }, // any
.{ .prefix = null, .infix = null, .precedence = .None }, // zdef
.{ .prefix = typeOfExpression, .infix = null, .precedence = .IsAs }, // typeof
};

pub const ScriptImport = struct {
Expand Down Expand Up @@ -1179,13 +1182,13 @@ pub const Parser = struct {
constant,
true,
)
// else if (try self.match(.Type))
// try self.varDeclaration(
// try self.gc.type_registry.getTypeDef(.{ .optional = try self.match(.Question), .def_type = .Type }),
// .Semicolon,
// constant,
// true,
// )
else if (try self.match(.Type))
try self.varDeclaration(
try self.gc.type_registry.getTypeDef(.{ .optional = try self.match(.Question), .def_type = .Type }),
.Semicolon,
constant,
true,
)
else if (try self.match(.Any))
try self.varDeclaration(
try self.gc.type_registry.getTypeDef(.{ .def_type = .Any }),
Expand Down Expand Up @@ -3541,6 +3544,40 @@ pub const Parser = struct {
}
}

fn typeOfExpression(self: *Self, _: bool) anyerror!*ParseNode {
const start_location = self.parser.previous_token.?;

const expr = try self.expression(false);

var node = try self.gc.allocator.create(TypeOfExpressionNode);
node.* = .{
.expression = expr,
};
node.node.type_def = try self.gc.type_registry.getTypeDef(.{ .def_type = .Type });
node.node.location = start_location;
node.node.end_location = self.parser.previous_token.?;

return &node.node;
}

fn typeExpression(self: *Self, _: bool) anyerror!*ParseNode {
const start_location = self.parser.previous_token.?;

const type_def = try self.parseTypeDef(null);

try self.consume(.Greater, "Expected `>` after type expression.");

var node = try self.gc.allocator.create(TypeExpressionNode);
node.* = .{
.value = type_def.toValue(),
};
node.node.type_def = try self.gc.type_registry.getTypeDef(.{ .def_type = .Type });
node.node.location = start_location;
node.node.end_location = self.parser.previous_token.?;

return &node.node;
}

fn unary(self: *Self, _: bool) anyerror!*ParseNode {
const start_location = self.parser.previous_token.?;

Expand Down Expand Up @@ -5378,8 +5415,8 @@ pub const Parser = struct {
return try self.gc.type_registry.getTypeDef(.{ .optional = try self.match(.Question), .def_type = .Bool });
} else if (try self.match(.Any)) {
return try self.gc.type_registry.getTypeDef(.{ .optional = try self.match(.Question), .def_type = .Any });
// } else if (try self.match(.Type)) {
// return try self.gc.type_registry.getTypeDef(.{ .optional = try self.match(.Question), .def_type = .Type });
} else if (try self.match(.Type)) {
return try self.gc.type_registry.getTypeDef(.{ .optional = try self.match(.Question), .def_type = .Type });
} else if (try self.match(.LeftBracket)) {
return self.parseListType(generic_types);
} else if (try self.match(.LeftBrace)) {
Expand Down
3 changes: 3 additions & 0 deletions src/token.zig
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,7 @@ pub const TokenType = enum {
Spread, // ..
Any, // any
Zdef, // zdef
TypeOf, // typeof
};

pub const keywords = std.ComptimeStringMap(
Expand Down Expand Up @@ -234,5 +235,7 @@ pub const keywords = std.ComptimeStringMap(
.{ "yield", .Yield },
.{ "any", .Any },
.{ "zdef", .Zdef },
.{ "type", .Type },
.{ "typeof", .TypeOf },
},
);
Loading

0 comments on commit c5a24f1

Please sign in to comment.