Files
xsh/deps/luau/src/Ast/Ast.zig
T

2455 lines
69 KiB
Zig
Raw Normal View History

2026-08-23 11:11:36 +03:00
const std = @import("std");
const Location = @import("Location.zig").Location;
const Variant = @import("../Common/Variant.zig").Variant;
const cpp_std = @import("../cpp_std.zig");
const Ast = @This();
pub const Name = extern struct {
value: [*:0]const u8,
};
pub const Local = extern struct {
name: Name,
location: Location,
shadow: ?*Local,
functionDepth: usize,
loopDepth: usize,
isConst: bool,
/// exported is only a property set after construction
isExported: bool = false,
annotation: ?*Type,
};
pub fn Array(comptime T: type) type {
return extern struct {
data: ?[*]T = null,
size: usize = 0,
const This = @This();
pub fn slice(self: This) []T {
return if (self.data) |d| d[0..self.size] else &.{};
}
};
}
pub const TypeList = extern struct {
types: Array(*Type),
/// Null indicates no tail, not an untyped tail.
tailType: ?*TypePack = null,
};
pub const Node = extern struct {
vtable: *const anyopaque,
classIndex: Node.Kind,
location: Location,
pub const Kind = enum(u32) {
unknown,
attr,
generic_type,
generic_type_pack,
expr_group,
expr_constant_nil,
expr_constant_bool,
expr_constant_number,
expr_constant_integer,
expr_constant_string,
expr_local,
expr_global,
expr_varargs,
expr_call,
expr_index_name,
expr_index_expr,
expr_function,
expr_table,
expr_unary,
expr_binary,
expr_type_assertion,
expr_if_else,
expr_interp_string,
expr_instantiate,
stat_block,
stat_if,
stat_while,
stat_repeat,
stat_break,
stat_continue,
stat_return,
stat_expr,
stat_local,
stat_for,
stat_for_in,
stat_assign,
stat_compound_assign,
stat_function,
stat_local_function,
stat_type_alias,
stat_type_function,
stat_declare_global,
stat_declare_function,
stat_class,
stat_declare_extern_type,
type_reference,
type_table,
type_function,
type_typeof,
type_optional,
type_union,
type_intersection,
expr_error,
stat_error,
type_error,
type_singleton_bool,
type_singleton_string,
type_group,
type_pack_explicit,
type_pack_variadic,
type_pack_generic,
pub fn Type(self: Kind) type {
return switch (self) {
.unknown => Node,
.attr => Attr,
.generic_type => GenericType,
.generic_type_pack => GenericTypePack,
.expr_group => ExprGroup,
.expr_constant_nil => ExprConstantNil,
.expr_constant_bool => ExprConstantBool,
.expr_constant_number => ExprConstantNumber,
.expr_constant_integer => ExprConstantInteger,
.expr_constant_string => ExprConstantString,
.expr_local => ExprLocal,
.expr_global => ExprGlobal,
.expr_varargs => ExprVarargs,
.expr_call => ExprCall,
.expr_index_name => ExprIndexName,
.expr_index_expr => ExprIndexExpr,
.expr_function => ExprFunction,
.expr_table => ExprTable,
.expr_unary => ExprUnary,
.expr_binary => ExprBinary,
.expr_type_assertion => ExprTypeAssertion,
.expr_if_else => ExprIfElse,
.expr_interp_string => ExprInterpString,
.expr_instantiate => ExprInstantiate,
.stat_block => StatBlock,
.stat_if => StatIf,
.stat_while => StatWhile,
.stat_repeat => StatRepeat,
.stat_break => StatBreak,
.stat_continue => StatContinue,
.stat_return => StatReturn,
.stat_expr => StatExpr,
.stat_local => StatLocal,
.stat_for => StatFor,
.stat_for_in => StatForIn,
.stat_assign => StatAssign,
.stat_compound_assign => StatCompoundAssign,
.stat_function => StatFunction,
.stat_local_function => StatLocalFunction,
.stat_type_alias => StatTypeAlias,
.stat_type_function => StatTypeFunction,
.stat_declare_global => StatDeclareGlobal,
.stat_declare_function => StatDeclareFunction,
.stat_class => StatClass,
.stat_declare_extern_type => StatDeclareExternType,
.type_reference => TypeReference,
.type_table => TypeTable,
.type_function => TypeFunction,
.type_typeof => TypeTypeof,
.type_optional => TypeOptional,
.type_union => TypeUnion,
.type_intersection => TypeIntersection,
.expr_error => ExprError,
.stat_error => StatError,
.type_error => TypeError,
.type_singleton_bool => TypeSingletonBool,
.type_singleton_string => TypeSingletonString,
.type_group => TypeGroup,
.type_pack_explicit => TypePackExplicit,
.type_pack_variadic => TypePackVariadic,
.type_pack_generic => TypePackGeneric,
};
}
pub fn Parent(self: Kind) type {
return switch (self) {
.unknown => Node,
.attr,
.generic_type,
.generic_type_pack,
=> Node,
.expr_group,
.expr_constant_nil,
.expr_constant_bool,
.expr_constant_number,
.expr_constant_string,
.expr_local,
.expr_global,
.expr_varargs,
.expr_call,
.expr_index_name,
.expr_index_expr,
.expr_function,
.expr_table,
.expr_unary,
.expr_binary,
.expr_type_assertion,
.expr_if_else,
.expr_interp_string,
.expr_instantiate,
.expr_error,
=> Expr,
.stat_block,
.stat_if,
.stat_while,
.stat_repeat,
.stat_break,
.stat_continue,
.stat_return,
.stat_expr,
.stat_local,
.stat_for,
.stat_for_in,
.stat_assign,
.stat_compound_assign,
.stat_function,
.stat_local_function,
.stat_type_alias,
.stat_type_function,
.stat_declare_global,
.stat_declare_function,
.stat_class,
.stat_declare_extern_type,
.stat_error,
=> Stat,
.type_reference,
.type_table,
.type_function,
.type_typeof,
.type_optional,
.type_union,
.type_intersection,
.type_error,
.type_singleton_bool,
.type_singleton_string,
.type_group,
=> Ast.Type,
.type_pack_explicit,
.type_pack_variadic,
.type_pack_generic,
=> Ast.TypePack,
};
}
};
pub const is = IsFn;
pub const as = AsCastFn;
pub const asExpr = AsExprCastFn;
pub const asStat = AsStatCastFn;
pub const asType = AsTypeCastFn;
};
pub fn IsFn(base: anytype, comptime to: Node.Kind) bool {
return base.classIndex == to;
}
pub fn AsCastFn(base: anytype, comptime to: Node.Kind) ?*to.Type() {
return if (base.classIndex == to) @ptrCast(@alignCast(base)) else null;
}
pub fn AsStatCastFn(base: anytype) *Stat {
return @ptrCast(@alignCast(base));
}
pub fn AsExprCastFn(base: anytype) *Expr {
return @ptrCast(@alignCast(base));
}
pub fn AsTypeCastFn(base: anytype) *Type {
return @ptrCast(@alignCast(base));
}
pub const Attr = extern struct {
vtable: *const anyopaque,
classIndex: Node.Kind = .attr,
location: Location,
type: Attr.Type,
args: Array(*Expr),
name: Name,
pub const Type = enum(c_int) {
Checked = 0,
Native = 1,
Deprecated = 2,
DebugNoinline = 3,
Unknown = 4,
};
pub const is = IsFn;
pub const as = AsCastFn;
pub const asExpr = AsExprCastFn;
pub const asStat = AsStatCastFn;
pub const asType = AsTypeCastFn;
pub fn visit(self: *@This(), visitor: anytype) !void {
_ = try Visitor.visit(visitor, self);
}
};
pub const Expr = extern struct {
vtable: *const anyopaque,
classIndex: Node.Kind,
location: Location,
pub const is = IsFn;
pub const as = AsCastFn;
pub const asExpr = AsExprCastFn;
pub const asStat = AsStatCastFn;
pub const asType = AsTypeCastFn;
pub fn visit(self: *@This(), visitor: anytype) !void {
return Visitor.selfVisit(visitor, self);
}
pub fn isLValue(expr: *const Expr) bool {
return switch (expr.classIndex) {
.expr_local, .expr_global, .expr_index_name, .expr_index_expr => true,
else => false,
};
}
pub fn getIdentifier(node: *Expr) ?Name {
if (node.as(.expr_global)) |expr|
return expr.name;
if (node.as(.expr_local)) |expr|
return expr.local.name;
return null;
}
};
pub const Stat = extern struct {
vtable: *const anyopaque,
classIndex: Node.Kind,
location: Location,
hasSemicolon: bool,
pub const is = IsFn;
pub const as = AsCastFn;
pub const asExpr = AsExprCastFn;
pub const asStat = AsStatCastFn;
pub const asType = AsTypeCastFn;
pub fn visit(self: *@This(), visitor: anytype) !void {
return Visitor.selfVisit(visitor, self);
}
};
pub const GenericType = extern struct {
vtable: *const anyopaque,
classIndex: Node.Kind = .generic_type,
location: Location,
name: Name,
defaultValue: ?*Type = null,
pub const is = IsFn;
pub const as = AsCastFn;
pub const asExpr = AsExprCastFn;
pub const asStat = AsStatCastFn;
pub const asType = AsTypeCastFn;
pub fn visit(self: *@This(), visitor: anytype) !void {
if (try Visitor.visit(visitor, self)) {
if (self.defaultValue) |node|
try node.visit(visitor);
}
}
};
pub const GenericTypePack = extern struct {
vtable: *const anyopaque,
classIndex: Node.Kind = .generic_type_pack,
location: Location,
name: Name,
defaultValue: ?*TypePack = null,
pub const is = IsFn;
pub const as = AsCastFn;
pub const asExpr = AsExprCastFn;
pub const asStat = AsStatCastFn;
pub const asType = AsTypeCastFn;
pub fn visit(self: *@This(), visitor: anytype) !void {
if (try Visitor.visit(visitor, self)) {
if (self.defaultValue) |node|
try node.visit(visitor);
}
}
};
pub const ExprGroup = extern struct {
vtable: *const anyopaque,
classIndex: Node.Kind = .expr_group,
location: Location,
expr: *Expr,
pub const is = IsFn;
pub const as = AsCastFn;
pub const asExpr = AsExprCastFn;
pub const asStat = AsStatCastFn;
pub const asType = AsTypeCastFn;
pub fn visit(self: *@This(), visitor: anytype) !void {
if (try Visitor.visit(visitor, self)) {
try self.expr.visit(visitor);
}
}
};
pub const ExprConstantNil = extern struct {
vtable: *const anyopaque,
classIndex: Node.Kind = .expr_constant_nil,
location: Location,
pub const is = IsFn;
pub const as = AsCastFn;
pub const asExpr = AsExprCastFn;
pub const asStat = AsStatCastFn;
pub const asType = AsTypeCastFn;
pub fn visit(self: *@This(), visitor: anytype) !void {
_ = try Visitor.visit(visitor, self);
}
};
pub const ExprConstantBool = extern struct {
vtable: *const anyopaque,
classIndex: Node.Kind = .expr_constant_bool,
location: Location,
value: bool,
pub const is = IsFn;
pub const as = AsCastFn;
pub const asExpr = AsExprCastFn;
pub const asStat = AsStatCastFn;
pub const asType = AsTypeCastFn;
pub fn visit(self: *@This(), visitor: anytype) !void {
_ = try Visitor.visit(visitor, self);
}
};
pub const ConstantNumberParseResult = enum(c_int) {
Ok = 0,
Imprecise = 1,
Malformed = 2,
BinOverflow = 3,
HexOverflow = 4,
IntOverflow = 5,
};
pub const ExprConstantNumber = extern struct {
vtable: *const anyopaque,
classIndex: Node.Kind = .expr_constant_number,
location: Location,
value: f64,
parseResult: ConstantNumberParseResult,
pub const is = IsFn;
pub const as = AsCastFn;
pub const asExpr = AsExprCastFn;
pub const asStat = AsStatCastFn;
pub const asType = AsTypeCastFn;
pub fn visit(self: *@This(), visitor: anytype) !void {
_ = try Visitor.visit(visitor, self);
}
};
pub const ExprConstantInteger = extern struct {
vtable: *const anyopaque,
classIndex: Node.Kind = .expr_constant_integer,
location: Location,
value: i64,
parseResult: ConstantNumberParseResult,
pub const is = IsFn;
pub const as = AsCastFn;
pub const asExpr = AsExprCastFn;
pub const asStat = AsStatCastFn;
pub const asType = AsTypeCastFn;
pub fn visit(self: *@This(), visitor: anytype) !void {
_ = try Visitor.visit(visitor, self);
}
};
pub const ExprConstantString = extern struct {
vtable: *const anyopaque,
classIndex: Node.Kind = .expr_constant_string,
location: Location,
value: Array(u8),
quoteStyle: QuoteStyle,
pub const QuoteStyle = enum(c_int) {
/// A string created using double quotes or an interpolated string,
/// as in:
///
/// "foo", `My name is {protagonist}! / And I'm {antagonist}!`
///
QuotedSimple = 0,
/// A string created using single quotes, as in:
///
/// 'bar'
///
QuotedSingle = 1,
/// A string created using `[[ ... ]]` as in:
///
/// [[ Gee, this sure is a long string.
/// it even has a new line in it! ]]
///
QuotedRaw = 2,
/// A "string" in the context of a table literal, as in:
///
/// { foo = 42 } -- `foo` here is a "constant string"
///
Unquoted = 3,
};
pub const is = IsFn;
pub const as = AsCastFn;
pub const asExpr = AsExprCastFn;
pub const asStat = AsStatCastFn;
pub const asType = AsTypeCastFn;
pub fn visit(self: *@This(), visitor: anytype) !void {
_ = try Visitor.visit(visitor, self);
}
};
pub const ExprLocal = extern struct {
vtable: *const anyopaque,
classIndex: Node.Kind = .expr_local,
location: Location,
local: ?*Local,
upvalue: bool,
pub const is = IsFn;
pub const as = AsCastFn;
pub const asExpr = AsExprCastFn;
pub const asStat = AsStatCastFn;
pub const asType = AsTypeCastFn;
pub fn visit(self: *@This(), visitor: anytype) !void {
_ = try Visitor.visit(visitor, self);
}
};
pub const ExprGlobal = extern struct {
vtable: *const anyopaque,
classIndex: Node.Kind = .expr_global,
location: Location,
name: Name,
pub const is = IsFn;
pub const as = AsCastFn;
pub const asExpr = AsExprCastFn;
pub const asStat = AsStatCastFn;
pub const asType = AsTypeCastFn;
pub fn visit(self: *@This(), visitor: anytype) !void {
_ = try Visitor.visit(visitor, self);
}
};
pub const ExprVarargs = extern struct {
vtable: *const anyopaque,
classIndex: Node.Kind = .expr_varargs,
location: Location,
pub const is = IsFn;
pub const as = AsCastFn;
pub const asExpr = AsExprCastFn;
pub const asStat = AsStatCastFn;
pub const asType = AsTypeCastFn;
pub fn visit(self: *@This(), visitor: anytype) !void {
_ = try Visitor.visit(visitor, self);
}
};
pub const ExprCall = extern struct {
vtable: *const anyopaque,
classIndex: Node.Kind = .expr_call,
location: Location,
func: *Expr,
/// These will only be filled in specifically `t:f<<A, B>>()`.
/// In `f<<A, B>>()`, this is parsed as `f<<A, B>>` as an expression,
/// which is then called.
typeArguments: Array(TypeOrPack),
args: Array(*Expr),
self: bool,
argLocation: Location,
pub const is = IsFn;
pub const as = AsCastFn;
pub const asExpr = AsExprCastFn;
pub const asStat = AsStatCastFn;
pub const asType = AsTypeCastFn;
pub fn visit(self: *@This(), visitor: anytype) !void {
if (try Visitor.visit(visitor, self)) {
try self.func.visit(visitor);
for (self.args.slice()) |arg|
try arg.visit(visitor);
}
}
};
pub const ExprIndexName = extern struct {
vtable: *const anyopaque,
classIndex: Node.Kind = .expr_index_name,
location: Location,
expr: *Expr,
index: Name,
indexLocation: Location,
opPosition: Location.Position,
op: u8 = '.',
pub const is = IsFn;
pub const as = AsCastFn;
pub const asExpr = AsExprCastFn;
pub const asStat = AsStatCastFn;
pub const asType = AsTypeCastFn;
pub fn visit(self: *@This(), visitor: anytype) !void {
if (try Visitor.visit(visitor, self)) {
try self.expr.visit(visitor);
}
}
};
pub const ExprIndexExpr = extern struct {
vtable: *const anyopaque,
classIndex: Node.Kind = .expr_index_expr,
location: Location,
expr: *Expr,
index: *Expr,
pub const is = IsFn;
pub const as = AsCastFn;
pub const asExpr = AsExprCastFn;
pub const asStat = AsStatCastFn;
pub const asType = AsTypeCastFn;
pub fn visit(self: *@This(), visitor: anytype) !void {
if (try Visitor.visit(visitor, self)) {
try self.expr.visit(visitor);
try self.index.visit(visitor);
}
}
};
pub const ExprFunction = extern struct {
vtable: *const anyopaque,
classIndex: Node.Kind = .expr_function,
location: Location,
attributes: Array(*Attr),
generics: Array(*GenericType),
genericPacks: Array(*GenericTypePack),
self: ?*Local,
args: Array(*Local),
returnAnnotation: ?*TypePack,
vararg: bool = false,
varargLocation: Location,
varargAnnotation: ?*TypePack,
body: *StatBlock,
functionDepth: usize,
debugname: Name,
argLocation: cpp_std.Optional(Location),
pub const is = IsFn;
pub const as = AsCastFn;
pub const asExpr = AsExprCastFn;
pub const asStat = AsStatCastFn;
pub const asType = AsTypeCastFn;
pub fn visit(self: *@This(), visitor: anytype) !void {
if (try Visitor.visit(visitor, self)) {
for (self.args.slice()) |arg|
if (arg.annotation) |node|
try node.visit(visitor);
if (self.varargAnnotation) |node|
try node.visit(visitor);
if (self.returnAnnotation) |annotation|
try annotation.visit(visitor);
try self.body.visit(visitor);
}
}
pub fn hasNativeAttribute(self: *ExprFunction) bool {
for (self.attributes.slice()) |attr| {
if (attr.type == .Native)
return true;
}
return false;
}
pub fn hasAttribute(self: *ExprFunction, attrType: Attr.Type) bool {
for (self.attributes.slice()) |attr| {
if (attr.type == attrType)
return true;
}
return false;
}
};
pub const ExprTable = extern struct {
vtable: *const anyopaque,
classIndex: Node.Kind = .expr_table,
location: Location,
items: Array(Item),
pub const Item = extern struct {
pub const Kind = enum(c_int) {
List, // foo, in which case key is a nullptr
Record, // foo=bar, in which case key is a AstExprConstantString
General, // [foo]=bar
};
kind: Kind,
/// can be nullptr!
key: ?*Expr,
value: *Expr,
pub const as = AsCastFn;
pub const asExpr = AsExprCastFn;
pub const asStat = AsStatCastFn;
};
pub const is = IsFn;
pub const as = AsCastFn;
pub const asExpr = AsExprCastFn;
pub const asStat = AsStatCastFn;
pub const asType = AsTypeCastFn;
pub fn visit(self: *@This(), visitor: anytype) !void {
if (try Visitor.visit(visitor, self)) {
for (self.items.slice()) |item| {
if (item.key) |key|
try key.visit(visitor);
try item.value.visit(visitor);
}
}
}
};
pub const ExprUnary = extern struct {
vtable: *const anyopaque,
classIndex: Node.Kind = .expr_unary,
location: Location,
op: Op,
expr: *Expr,
pub const Op = enum(c_int) {
Not = 0,
Minus = 1,
Len = 2,
pub fn toString(self: Op) []const u8 {
return switch (self) {
.Not => "not",
.Minus => "-",
.Len => "#",
};
}
};
pub const is = IsFn;
pub const as = AsCastFn;
pub const asExpr = AsExprCastFn;
pub const asStat = AsStatCastFn;
pub const asType = AsTypeCastFn;
pub fn visit(self: *@This(), visitor: anytype) !void {
if (try Visitor.visit(visitor, self)) {
try self.expr.visit(visitor);
}
}
};
pub const ExprBinary = extern struct {
vtable: *const anyopaque,
classIndex: Node.Kind = .expr_binary,
location: Location,
op: Op,
left: *Expr,
right: *Expr,
pub const Op = enum(c_int) {
Add = 0,
Sub = 1,
Mul = 2,
Div = 3,
FloorDiv = 4,
Mod = 5,
Pow = 6,
Concat = 7,
CompareNe = 8,
CompareEq = 9,
CompareLt = 10,
CompareLe = 11,
CompareGt = 12,
CompareGe = 13,
And = 14,
Or = 15,
__Count = 16,
pub fn toString(self: Op) []const u8 {
return switch (self) {
.Add => "+",
.Sub => "-",
.Mul => "*",
.Div => "/",
.FloorDiv => "//",
.Mod => "%",
.Pow => "^",
.Concat => "..",
.CompareNe => "~=",
.CompareEq => "==",
.CompareLt => "<",
.CompareLe => "<=",
.CompareGt => ">",
.CompareGe => ">=",
.And => "and",
.Or => "or",
.__Count => unreachable,
};
}
};
pub const is = IsFn;
pub const as = AsCastFn;
pub const asExpr = AsExprCastFn;
pub const asStat = AsStatCastFn;
pub const asType = AsTypeCastFn;
pub fn visit(self: *@This(), visitor: anytype) !void {
if (try Visitor.visit(visitor, self)) {
try self.left.visit(visitor);
try self.right.visit(visitor);
}
}
};
pub const ExprTypeAssertion = extern struct {
vtable: *const anyopaque,
classIndex: Node.Kind = .expr_type_assertion,
location: Location,
expr: *Expr,
annotation: *Type,
pub const is = IsFn;
pub const as = AsCastFn;
pub const asExpr = AsExprCastFn;
pub const asStat = AsStatCastFn;
pub const asType = AsTypeCastFn;
pub fn visit(self: *@This(), visitor: anytype) !void {
if (try Visitor.visit(visitor, self)) {
try self.expr.visit(visitor);
try self.annotation.visit(visitor);
}
}
};
pub const ExprIfElse = extern struct {
vtable: *const anyopaque,
classIndex: Node.Kind = .expr_if_else,
location: Location,
condition: *Expr,
hasThen: bool,
trueExpr: *Expr,
hasElse: bool,
falseExpr: *Expr,
pub const is = IsFn;
pub const as = AsCastFn;
pub const asExpr = AsExprCastFn;
pub const asStat = AsStatCastFn;
pub const asType = AsTypeCastFn;
pub fn visit(self: *@This(), visitor: anytype) !void {
if (try Visitor.visit(visitor, self)) {
try self.condition.visit(visitor);
try self.trueExpr.visit(visitor);
try self.falseExpr.visit(visitor);
}
}
};
pub const ExprInterpString = extern struct {
vtable: *const anyopaque,
classIndex: Node.Kind = .expr_interp_string,
location: Location,
/// An interpolated string such as `foo{bar}baz` is represented as
/// an array of strings for "foo" and "bar", and an array of expressions for "baz".
/// `strings` will always have one more element than `expressions`.
strings: Array(Array(u8)),
expressions: Array(*Expr),
pub const is = IsFn;
pub const as = AsCastFn;
pub const asExpr = AsExprCastFn;
pub const asStat = AsStatCastFn;
pub const asType = AsTypeCastFn;
pub fn visit(self: *@This(), visitor: anytype) !void {
if (try Visitor.visit(visitor, self)) {
for (self.expressions.slice()) |expr|
try expr.visit(visitor);
}
}
};
/// f<<T>>
pub const ExprInstantiate = extern struct {
vtable: *const anyopaque,
classIndex: Node.Kind = .expr_instantiate,
location: Location,
expr: *Expr,
typeArguments: Array(TypeOrPack),
pub const is = IsFn;
pub const as = AsCastFn;
pub const asExpr = AsExprCastFn;
pub const asStat = AsStatCastFn;
pub const asType = AsTypeCastFn;
pub fn visit(self: *@This(), visitor: anytype) !void {
if (try Visitor.visit(visitor, self)) {
try self.expr.visit(visitor);
try TypeOrPack.visitArray(self.typeArguments, visitor);
}
}
};
pub const StatBlock = extern struct {
vtable: *const anyopaque,
classIndex: Node.Kind = .stat_block,
location: Location,
hasSemicolon: bool = false,
body: Array(*Stat),
/// Indicates whether or not this block has been terminated in a
/// syntactically valid way.
///
/// This is usually but not always done with the 'end' keyword. StatIf
/// and StatRepeat are the two main exceptions to this.
///
/// The 'then' clause of an if statement can properly be closed by the
/// keywords 'else' or 'elseif'. A 'repeat' loop's body is closed with the
/// 'until' keyword.
hasEnd: bool = false,
pub const is = IsFn;
pub const as = AsCastFn;
pub const asExpr = AsExprCastFn;
pub const asStat = AsStatCastFn;
pub const asType = AsTypeCastFn;
pub fn visit(self: *@This(), visitor: anytype) !void {
if (try Visitor.visit(visitor, self))
for (self.body.slice()) |stat|
try stat.visit(visitor);
}
};
pub const StatIf = extern struct {
vtable: *const anyopaque,
classIndex: Node.Kind = .stat_if,
location: Location,
hasSemicolon: bool = false,
condition: *Expr,
thenbody: *StatBlock,
elsebody: ?*Stat,
thenLocation: cpp_std.Optional(Location),
/// Active for 'elseif' as well
elseLocation: cpp_std.Optional(Location),
pub const is = IsFn;
pub const as = AsCastFn;
pub const asExpr = AsExprCastFn;
pub const asStat = AsStatCastFn;
pub const asType = AsTypeCastFn;
pub fn visit(self: *@This(), visitor: anytype) !void {
if (try Visitor.visit(visitor, self)) {
try self.condition.visit(visitor);
try self.thenbody.visit(visitor);
if (self.elsebody) |elsebody|
try elsebody.visit(visitor);
}
}
};
pub const StatWhile = extern struct {
vtable: *const anyopaque,
classIndex: Node.Kind = .stat_while,
location: Location,
hasSemicolon: bool = false,
condition: *Expr,
body: *StatBlock,
hasDo: bool = false,
doLocation: Location,
pub const is = IsFn;
pub const as = AsCastFn;
pub const asExpr = AsExprCastFn;
pub const asStat = AsStatCastFn;
pub const asType = AsTypeCastFn;
pub fn visit(self: *@This(), visitor: anytype) !void {
if (try Visitor.visit(visitor, self)) {
try self.condition.visit(visitor);
try self.body.visit(visitor);
}
}
};
pub const StatRepeat = extern struct {
vtable: *const anyopaque,
classIndex: Node.Kind = .stat_repeat,
location: Location,
hasSemicolon: bool = false,
condition: *Expr,
body: *StatBlock,
DEPRECATED_hasUntil: bool = false,
pub const is = IsFn;
pub const as = AsCastFn;
pub const asExpr = AsExprCastFn;
pub const asStat = AsStatCastFn;
pub const asType = AsTypeCastFn;
pub fn visit(self: *@This(), visitor: anytype) !void {
if (try Visitor.visit(visitor, self)) {
try self.body.visit(visitor);
try self.condition.visit(visitor);
}
}
};
pub const StatBreak = extern struct {
vtable: *const anyopaque,
classIndex: Node.Kind = .stat_break,
location: Location,
hasSemicolon: bool = false,
pub const is = IsFn;
pub const as = AsCastFn;
pub const asExpr = AsExprCastFn;
pub const asStat = AsStatCastFn;
pub const asType = AsTypeCastFn;
pub fn visit(self: *@This(), visitor: anytype) !void {
_ = try Visitor.visit(visitor, self);
}
};
pub const StatContinue = extern struct {
vtable: *const anyopaque,
classIndex: Node.Kind = .stat_continue,
location: Location,
hasSemicolon: bool = false,
pub const is = IsFn;
pub const as = AsCastFn;
pub const asExpr = AsExprCastFn;
pub const asStat = AsStatCastFn;
pub const asType = AsTypeCastFn;
pub fn visit(self: *@This(), visitor: anytype) !void {
_ = try Visitor.visit(visitor, self);
}
};
pub const StatReturn = extern struct {
vtable: *const anyopaque,
classIndex: Node.Kind = .stat_return,
location: Location,
hasSemicolon: bool = false,
list: Array(*Expr),
pub const is = IsFn;
pub const as = AsCastFn;
pub const asExpr = AsExprCastFn;
pub const asStat = AsStatCastFn;
pub const asType = AsTypeCastFn;
pub fn visit(self: *@This(), visitor: anytype) !void {
if (try Visitor.visit(visitor, self)) {
for (self.list.slice()) |expr|
try expr.visit(visitor);
}
}
};
pub const StatExpr = extern struct {
vtable: *const anyopaque,
classIndex: Node.Kind = .stat_expr,
location: Location,
hasSemicolon: bool = false,
expr: *Expr,
pub const is = IsFn;
pub const as = AsCastFn;
pub const asExpr = AsExprCastFn;
pub const asStat = AsStatCastFn;
pub const asType = AsTypeCastFn;
pub fn visit(self: *@This(), visitor: anytype) !void {
if (try Visitor.visit(visitor, self))
try self.expr.visit(visitor);
}
};
pub const StatLocal = extern struct {
vtable: *const anyopaque,
classIndex: Node.Kind = .stat_local,
location: Location,
hasSemicolon: bool = false,
vars: Array(*Local),
values: Array(*Expr),
isConst: bool = false,
isExported: bool = false,
keywordLocation: cpp_std.Optional(Location),
equalsSignLocation: cpp_std.Optional(Location),
pub const is = IsFn;
pub const as = AsCastFn;
pub const asExpr = AsExprCastFn;
pub const asStat = AsStatCastFn;
pub const asType = AsTypeCastFn;
pub fn visit(self: *@This(), visitor: anytype) !void {
if (try Visitor.visit(visitor, self)) {
for (self.vars.slice()) |@"var"|
if (@"var".annotation) |node|
try node.visit(visitor);
for (self.values.slice()) |expr|
try expr.visit(visitor);
}
}
};
pub const StatFor = extern struct {
vtable: *const anyopaque,
classIndex: Node.Kind = .stat_for,
location: Location,
hasSemicolon: bool = false,
variable: *Local,
from: *Expr,
to: *Expr,
step: ?*Expr,
body: *StatBlock,
hasDo: bool = false,
doLocation: Location,
pub const is = IsFn;
pub const as = AsCastFn;
pub const asExpr = AsExprCastFn;
pub const asStat = AsStatCastFn;
pub const asType = AsTypeCastFn;
pub fn visit(self: *@This(), visitor: anytype) !void {
if (try Visitor.visit(visitor, self)) {
if (self.variable.annotation) |node|
try node.visit(visitor);
try self.from.visit(visitor);
try self.to.visit(visitor);
if (self.step) |step|
try step.visit(visitor);
try self.body.visit(visitor);
}
}
};
pub const StatForIn = extern struct {
vtable: *const anyopaque,
classIndex: Node.Kind = .stat_for_in,
location: Location,
hasSemicolon: bool = false,
vars: Array(*Local),
values: Array(*Expr),
body: *StatBlock,
hasIn: bool = false,
inLocation: Location,
hasDo: bool = false,
doLocation: Location,
pub const is = IsFn;
pub const as = AsCastFn;
pub const asExpr = AsExprCastFn;
pub const asStat = AsStatCastFn;
pub const asType = AsTypeCastFn;
pub fn visit(self: *@This(), visitor: anytype) !void {
if (try Visitor.visit(visitor, self)) {
for (self.vars.slice()) |@"var"|
if (@"var".annotation) |node|
try node.visit(visitor);
for (self.values.slice()) |expr|
try expr.visit(visitor);
try self.body.visit(visitor);
}
}
};
pub const StatAssign = extern struct {
vtable: *const anyopaque,
classIndex: Node.Kind = .stat_assign,
location: Location,
hasSemicolon: bool = false,
vars: Array(*Expr),
values: Array(*Expr),
pub const is = IsFn;
pub const as = AsCastFn;
pub const asExpr = AsExprCastFn;
pub const asStat = AsStatCastFn;
pub const asType = AsTypeCastFn;
pub fn visit(self: *@This(), visitor: anytype) !void {
if (try Visitor.visit(visitor, self)) {
for (self.vars.slice()) |lvalue|
try lvalue.visit(visitor);
for (self.values.slice()) |expr|
try expr.visit(visitor);
}
}
};
pub const StatCompoundAssign = extern struct {
vtable: *const anyopaque,
classIndex: Node.Kind = .stat_compound_assign,
location: Location,
hasSemicolon: bool = false,
op: ExprBinary.Op,
variable: *Expr,
value: *Expr,
pub const is = IsFn;
pub const as = AsCastFn;
pub const asExpr = AsExprCastFn;
pub const asStat = AsStatCastFn;
pub const asType = AsTypeCastFn;
pub fn visit(self: *@This(), visitor: anytype) !void {
if (try Visitor.visit(visitor, self)) {
try self.variable.visit(visitor);
try self.value.visit(visitor);
}
}
};
pub const StatFunction = extern struct {
vtable: *const anyopaque,
classIndex: Node.Kind = .stat_function,
location: Location,
hasSemicolon: bool = false,
name: *Expr,
func: *ExprFunction,
pub const is = IsFn;
pub const as = AsCastFn;
pub const asExpr = AsExprCastFn;
pub const asStat = AsStatCastFn;
pub const asType = AsTypeCastFn;
pub fn visit(self: *@This(), visitor: anytype) !void {
if (try Visitor.visit(visitor, self)) {
try self.name.visit(visitor);
try self.func.visit(visitor);
}
}
};
pub const StatLocalFunction = extern struct {
vtable: *const anyopaque,
classIndex: Node.Kind = .stat_local_function,
location: Location,
hasSemicolon: bool = false,
name: *Local,
func: *ExprFunction,
isConst: bool = false,
/// Position of the `const` keyword; Position::missing() when isConst is false.
constKeywordBegin: Location.Position,
pub const is = IsFn;
pub const as = AsCastFn;
pub const asExpr = AsExprCastFn;
pub const asStat = AsStatCastFn;
pub const asType = AsTypeCastFn;
pub fn visit(self: *@This(), visitor: anytype) !void {
if (try Visitor.visit(visitor, self)) {
try self.func.visit(visitor);
}
}
};
pub const StatTypeAlias = extern struct {
vtable: *const anyopaque,
classIndex: Node.Kind = .stat_type_alias,
location: Location,
hasSemicolon: bool = false,
name: Name,
nameLocation: Location,
generics: Array(*GenericType),
genericPacks: Array(*GenericTypePack),
type: *Type,
exported: bool,
pub const is = IsFn;
pub const as = AsCastFn;
pub const asExpr = AsExprCastFn;
pub const asStat = AsStatCastFn;
pub const asType = AsTypeCastFn;
pub fn visit(self: *@This(), visitor: anytype) !void {
if (try Visitor.visit(visitor, self)) {
for (self.generics.slice()) |el|
try el.visit(visitor);
for (self.genericPacks.slice()) |el|
try el.visit(visitor);
try self.type.visit(visitor);
}
}
};
pub const StatTypeFunction = extern struct {
vtable: *const anyopaque,
classIndex: Node.Kind = .stat_type_function,
location: Location,
hasSemicolon: bool = false,
name: Name,
nameLocation: Location,
body: *ExprFunction = undefined,
exported: bool = false,
hasErrors: bool = false,
pub const is = IsFn;
pub const as = AsCastFn;
pub const asExpr = AsExprCastFn;
pub const asStat = AsStatCastFn;
pub const asType = AsTypeCastFn;
pub fn visit(self: *@This(), visitor: anytype) !void {
if (try Visitor.visit(visitor, self)) {
try self.body.visit(visitor);
}
}
};
pub const StatDeclareGlobal = extern struct {
vtable: *const anyopaque,
classIndex: Node.Kind = .stat_declare_global,
location: Location,
hasSemicolon: bool = false,
name: Name,
nameLocation: Location,
type: *Type,
pub const is = IsFn;
pub const as = AsCastFn;
pub const asExpr = AsExprCastFn;
pub const asStat = AsStatCastFn;
pub const asType = AsTypeCastFn;
pub fn visit(self: *@This(), visitor: anytype) !void {
if (try Visitor.visit(visitor, self)) {
try self.type.visit(visitor);
}
}
};
pub const ArgumentName = extern struct {
name: Name,
location: Location,
pub const is = IsFn;
pub const as = AsCastFn;
pub const asExpr = AsExprCastFn;
pub const asStat = AsStatCastFn;
pub const asType = AsTypeCastFn;
};
pub const StatDeclareFunction = extern struct {
vtable: *const anyopaque,
classIndex: Node.Kind = .stat_declare_function,
location: Location,
hasSemicolon: bool = false,
attributes: Array(*Attr),
name: Name,
nameLocation: Location,
generics: Array(*GenericType),
genericPacks: Array(*GenericTypePack),
params: TypeList,
paramNames: Array(ArgumentName),
vararg: bool = false,
varargLocation: Location,
retTypes: *TypePack,
pub const is = IsFn;
pub const as = AsCastFn;
pub const asExpr = AsExprCastFn;
pub const asStat = AsStatCastFn;
pub const asType = AsTypeCastFn;
pub fn visit(self: *@This(), visitor: anytype) !void {
if (try Visitor.visit(visitor, self)) {
try Visitor.visitTypeList(visitor, self.params);
try self.retTypes.visit(visitor);
}
}
pub fn isCheckedFunction(self: *StatDeclareFunction) bool {
for (self.attributes.slice()) |attr| {
if (attr.type == .Checked)
return true;
}
return false;
}
pub fn hasAttribute(self: *StatDeclareFunction, attrType: Attr.Type) bool {
for (self.attributes.slice()) |attr| {
if (attr.type == attrType)
return true;
}
return false;
}
};
pub const TableAccess = enum(c_int) {
Read = 1,
Write = 2,
ReadWrite = 3,
};
pub const DeclaredExternTypeProperty = extern struct {
name: Name,
nameLocation: Location,
ty: *Type = undefined,
isMethod: bool = false,
location: Location,
access: TableAccess = .ReadWrite,
pub const is = IsFn;
pub const as = AsCastFn;
pub const asExpr = AsExprCastFn;
pub const asStat = AsStatCastFn;
pub const asType = AsTypeCastFn;
};
pub const ClassProperty = extern struct {
qualifierLocation: Location,
name: Name,
nameLocation: Location,
typeColonLocation: cpp_std.Optional(Location) = .nullopt,
ty: ?*Type = null,
};
pub const ClassMethod = extern struct {
qualifierLocation: cpp_std.Optional(Location),
keywordLocation: Location,
functionName: Name,
nameLocation: Location,
function: *ExprFunction,
};
const ClassMember = Variant(&.{ ClassProperty, ClassMethod });
pub const StatClass = extern struct {
vtable: *const anyopaque,
classIndex: Node.Kind = .stat_class,
location: Location,
hasSemicolon: bool = false,
name: Name,
members: Array(ClassMember),
exported: bool,
pub const is = IsFn;
pub const as = AsCastFn;
pub const asExpr = AsExprCastFn;
pub const asStat = AsStatCastFn;
pub const asType = AsTypeCastFn;
pub fn visit(self: *@This(), visitor: anytype) !void {
if (try Visitor.visit(visitor, self)) {
for (self.members.slice()) |member| {
switch (member.typeId) {
0 => if (member.@"union"().@"0".ty) |ty|
try ty.visit(visitor),
1 => try member.@"union"().@"1".function.visit(visitor),
else => unreachable,
}
}
}
}
};
pub const TableIndexer = extern struct {
indexType: *Type,
resultType: *Type,
location: Location,
access: TableAccess = .ReadWrite,
accessLocation: cpp_std.Optional(Location),
};
pub const StatDeclareExternType = extern struct {
vtable: *const anyopaque,
classIndex: Node.Kind = .stat_declare_extern_type,
location: Location,
hasSemicolon: bool = false,
name: Name,
superName: cpp_std.Optional(Name),
props: Array(DeclaredExternTypeProperty),
indexer: *TableIndexer,
pub const is = IsFn;
pub const as = AsCastFn;
pub const asExpr = AsExprCastFn;
pub const asStat = AsStatCastFn;
pub const asType = AsTypeCastFn;
pub fn visit(self: *@This(), visitor: anytype) !void {
if (try Visitor.visit(visitor, self)) {
for (self.props.slice()) |prop|
try prop.ty.visit(visitor);
}
}
};
pub const Type = extern struct {
vtable: *const anyopaque,
classIndex: Node.Kind,
location: Location,
pub const is = IsFn;
pub const as = AsCastFn;
pub const asExpr = AsExprCastFn;
pub const asStat = AsStatCastFn;
pub const asType = AsTypeCastFn;
pub fn visit(self: *@This(), visitor: anytype) !void {
return Visitor.selfVisit(visitor, self);
}
};
/// Don't have Luau::Variant available, it's a bit of an overhead, but a plain struct is nice to use
pub const TypeOrPack = extern struct {
type: ?*Type = null,
typePack: ?*TypePack = null,
pub fn visitArray(self: Array(TypeOrPack), visitor: anytype) !void {
for (self.slice()) |param| {
if (param.type) |node|
try node.visit(visitor)
else
try param.typePack.?.visit(visitor);
}
}
};
pub const TypeReference = extern struct {
vtable: *const anyopaque,
classIndex: Node.Kind = .type_reference,
location: Location,
hasParameterList: bool,
prefix: cpp_std.Optional(Name),
prefixLocation: cpp_std.Optional(Location),
prefixLocal: ?*Local = null,
name: Name,
nameLocation: Location,
parameters: Array(TypeOrPack),
pub const is = IsFn;
pub const as = AsCastFn;
pub const asExpr = AsExprCastFn;
pub const asStat = AsStatCastFn;
pub const asType = AsTypeCastFn;
pub fn visit(self: *@This(), visitor: anytype) !void {
if (try Visitor.visit(visitor, self)) {
try TypeOrPack.visitArray(self.parameters, visitor);
}
}
};
pub const TableProp = extern struct {
name: Name,
location: Location,
type: *Type,
access: TableAccess = .ReadWrite,
accessLocation: cpp_std.Optional(Location),
};
pub const TypeTable = extern struct {
vtable: *const anyopaque,
classIndex: Node.Kind = .type_table,
location: Location,
props: Array(TableProp),
indexer: ?*TableIndexer,
pub const is = IsFn;
pub const as = AsCastFn;
pub const asExpr = AsExprCastFn;
pub const asStat = AsStatCastFn;
pub const asType = AsTypeCastFn;
pub fn visit(self: *@This(), visitor: anytype) !void {
if (try Visitor.visit(visitor, self)) {
for (self.props.slice()) |node|
try node.type.visit(visitor);
if (self.indexer) |indexer| {
try indexer.indexType.visit(visitor);
try indexer.resultType.visit(visitor);
}
}
}
};
pub const TypeFunction = extern struct {
vtable: *const anyopaque,
classIndex: Node.Kind = .type_function,
location: Location,
attributes: Array(*Attr),
generics: Array(*GenericType),
genericPacks: Array(*GenericTypePack),
argTypes: TypeList,
argNames: Array(cpp_std.Optional(ArgumentName)),
returnTypes: *TypePack,
pub const is = IsFn;
pub const as = AsCastFn;
pub const asExpr = AsExprCastFn;
pub const asStat = AsStatCastFn;
pub const asType = AsTypeCastFn;
pub fn visit(self: *@This(), visitor: anytype) !void {
if (try Visitor.visit(visitor, self)) {
try Visitor.visitTypeList(visitor, self.argTypes);
try self.returnTypes.visit(visitor);
}
}
pub fn isCheckedFunction(self: *TypeFunction) bool {
for (self.attributes.slice()) |attr| {
if (attr.type == .Checked)
return true;
}
return false;
}
pub fn hasAttribute(self: *TypeFunction, attrType: Attr.Type) bool {
for (self.attributes.slice()) |attr| {
if (attr.type == attrType)
return true;
}
return false;
}
};
pub const TypeTypeof = extern struct {
vtable: *const anyopaque,
classIndex: Node.Kind = .type_typeof,
location: Location,
expr: *Expr,
pub const is = IsFn;
pub const as = AsCastFn;
pub const asExpr = AsExprCastFn;
pub const asStat = AsStatCastFn;
pub const asType = AsTypeCastFn;
pub fn visit(self: *@This(), visitor: anytype) !void {
if (try Visitor.visit(visitor, self)) {
try self.expr.visit(visitor);
}
}
};
pub const TypeOptional = extern struct {
vtable: *const anyopaque,
classIndex: Node.Kind = .type_optional,
location: Location,
pub const is = IsFn;
pub const as = AsCastFn;
pub const asExpr = AsExprCastFn;
pub const asStat = AsStatCastFn;
pub const asType = AsTypeCastFn;
pub fn visit(self: *@This(), visitor: anytype) !void {
_ = try Visitor.visit(visitor, self);
}
};
pub const TypeUnion = extern struct {
vtable: *const anyopaque,
classIndex: Node.Kind = .type_union,
location: Location,
types: Array(*Type),
pub const is = IsFn;
pub const as = AsCastFn;
pub const asExpr = AsExprCastFn;
pub const asStat = AsStatCastFn;
pub const asType = AsTypeCastFn;
pub fn visit(self: *@This(), visitor: anytype) !void {
if (try Visitor.visit(visitor, self)) {
for (self.types.slice()) |node|
try node.visit(visitor);
}
}
};
pub const TypeIntersection = extern struct {
vtable: *const anyopaque,
classIndex: Node.Kind = .type_intersection,
location: Location,
types: Array(*Type),
pub const is = IsFn;
pub const as = AsCastFn;
pub const asExpr = AsExprCastFn;
pub const asStat = AsStatCastFn;
pub const asType = AsTypeCastFn;
pub fn visit(self: *@This(), visitor: anytype) !void {
if (try Visitor.visit(visitor, self)) {
for (self.types.slice()) |node|
try node.visit(visitor);
}
}
};
pub const ExprError = extern struct {
vtable: *const anyopaque,
classIndex: Node.Kind = .expr_error,
location: Location,
expressions: Array(*Expr),
messageIndex: c_uint,
pub const is = IsFn;
pub const as = AsCastFn;
pub const asExpr = AsExprCastFn;
pub const asStat = AsStatCastFn;
pub const asType = AsTypeCastFn;
pub fn visit(self: *@This(), visitor: anytype) !void {
if (try Visitor.visit(visitor, self)) {
for (self.expressions.slice()) |expression|
try expression.visit(visitor);
}
}
};
pub const StatError = extern struct {
vtable: *const anyopaque,
classIndex: Node.Kind = .stat_error,
location: Location,
MAYBE_hasSemicolon: bool = false,
expressions: Array(*Expr),
statements: Array(*Stat),
messageIndex: c_uint,
pub const is = IsFn;
pub const as = AsCastFn;
pub const asExpr = AsExprCastFn;
pub const asStat = AsStatCastFn;
pub const asType = AsTypeCastFn;
pub fn visit(self: *@This(), visitor: anytype) !void {
if (try Visitor.visit(visitor, self)) {
for (self.expressions.slice()) |expression|
try expression.visit(visitor);
for (self.statements.slice()) |statement|
try statement.visit(visitor);
}
}
};
pub const TypeError = extern struct {
vtable: *const anyopaque,
classIndex: Node.Kind = .type_error,
location: Location,
types: Array(*Type),
isMissing: bool,
messageIndex: c_uint,
pub const is = IsFn;
pub const as = AsCastFn;
pub const asExpr = AsExprCastFn;
pub const asStat = AsStatCastFn;
pub const asType = AsTypeCastFn;
pub fn visit(self: *@This(), visitor: anytype) !void {
if (try Visitor.visit(visitor, self)) {
for (self.types.slice()) |node|
try node.visit(visitor);
}
}
};
pub const TypeSingletonBool = extern struct {
vtable: *const anyopaque,
classIndex: Node.Kind = .type_singleton_bool,
location: Location,
value: bool,
pub const is = IsFn;
pub const as = AsCastFn;
pub const asExpr = AsExprCastFn;
pub const asStat = AsStatCastFn;
pub const asType = AsTypeCastFn;
pub fn visit(self: *@This(), visitor: anytype) !void {
_ = try Visitor.visit(visitor, self);
}
};
pub const TypeSingletonString = extern struct {
vtable: *const anyopaque,
classIndex: Node.Kind = .type_singleton_string,
location: Location,
value: Array(u8),
pub const is = IsFn;
pub const as = AsCastFn;
pub const asExpr = AsExprCastFn;
pub const asStat = AsStatCastFn;
pub const asType = AsTypeCastFn;
pub fn visit(self: *@This(), visitor: anytype) !void {
_ = try Visitor.visit(visitor, self);
}
};
pub const TypeGroup = extern struct {
vtable: *const anyopaque,
classIndex: Node.Kind = .type_group,
location: Location,
type: *Type,
pub const is = IsFn;
pub const as = AsCastFn;
pub const asExpr = AsExprCastFn;
pub const asStat = AsStatCastFn;
pub const asType = AsTypeCastFn;
pub fn visit(self: *@This(), visitor: anytype) !void {
if (try Visitor.visit(visitor, self)) {
try self.type.visit(visitor);
}
}
};
pub const TypePack = extern struct {
vtable: *const anyopaque,
classIndex: Node.Kind,
location: Location,
pub const is = IsFn;
pub const as = AsCastFn;
pub const asExpr = AsExprCastFn;
pub const asStat = AsStatCastFn;
pub const asType = AsTypeCastFn;
pub fn visit(self: *@This(), visitor: anytype) !void {
return Visitor.selfVisit(visitor, self);
}
};
pub const TypePackExplicit = extern struct {
vtable: *const anyopaque,
classIndex: Node.Kind = .type_pack_explicit,
location: Location,
typeList: TypeList,
pub const is = IsFn;
pub const as = AsCastFn;
pub const asExpr = AsExprCastFn;
pub const asStat = AsStatCastFn;
pub const asType = AsTypeCastFn;
pub fn visit(self: *@This(), visitor: anytype) !void {
if (try Visitor.visit(visitor, self)) {
for (self.typeList.types.slice()) |node|
try node.visit(visitor);
if (self.typeList.tailType) |node|
try node.visit(visitor);
}
}
};
pub const TypePackVariadic = extern struct {
vtable: *const anyopaque,
classIndex: Node.Kind = .type_pack_variadic,
location: Location,
variadicType: *Type,
pub const is = IsFn;
pub const as = AsCastFn;
pub const asExpr = AsExprCastFn;
pub const asStat = AsStatCastFn;
pub const asType = AsTypeCastFn;
pub fn visit(self: *@This(), visitor: anytype) !void {
if (try Visitor.visit(visitor, self))
try self.variadicType.visit(visitor);
}
};
pub const TypePackGeneric = extern struct {
vtable: *const anyopaque,
classIndex: Node.Kind = .type_pack_generic,
location: Location,
genericName: Name,
pub const is = IsFn;
pub const as = AsCastFn;
pub const asExpr = AsExprCastFn;
pub const asStat = AsStatCastFn;
pub const asType = AsTypeCastFn;
pub fn visit(self: *@This(), visitor: anytype) !void {
_ = try Visitor.visit(visitor, self);
}
};
pub const Visitor = struct {
pub fn visitTypeList(self: anytype, this: TypeList) !void {
for (this.types.slice()) |node|
try node.visit(self);
if (this.tailType) |node|
try node.visit(self);
}
fn getVisitorStruct(comptime self: type) type {
return switch (@typeInfo(self)) {
.pointer => |ptr| ptr.child,
.@"struct" => |s| s,
else => |t| @compileError("Visitor type unsupported: " ++ @typeName(t)),
};
}
fn hasVistDecl(comptime self: type, comptime name: [:0]const u8) bool {
return @hasDecl(getVisitorStruct(self), name);
}
fn callVisitorDecl(self: anytype, comptime name: [:0]const u8, this: anytype) !bool {
const visitor_type = @TypeOf(self);
const visitor_struct = getVisitorStruct(visitor_type);
const visitor_fn = @field(visitor_struct, name);
return switch (@typeInfo(visitor_type)) {
.type => visitor_fn(@ptrCast(@alignCast(this))),
.pointer, .@"struct" => visitor_fn(self, @ptrCast(@alignCast(this))),
else => unreachable,
};
}
fn visitByName(self: anytype, comptime name: [:0]const u8, this: anytype) ?bool {
const namespace = @typeName(Ast);
const ast_name = name[namespace.len + 1 ..];
const fn_name = "visit" ++ ast_name;
if (comptime hasVistDecl(@TypeOf(self), fn_name))
return callVisitorDecl(self, fn_name, this);
return null;
}
fn getParent(comptime ast: type) type {
const namespace = @typeName(Ast);
const ast_name = @typeName(ast)[namespace.len + 1 ..];
if (std.mem.eql(u8, ast_name, "Attr") or
std.mem.eql(u8, ast_name, "GenericType") or
std.mem.eql(u8, ast_name, "GenericTypePack") or
std.mem.eql(u8, ast_name, "Expr") or
std.mem.eql(u8, ast_name, "Stat"))
return Ast.Node
else if (std.mem.startsWith(u8, ast_name, "Expr"))
return Ast.Expr
else if (std.mem.startsWith(u8, ast_name, "Stat"))
return Ast.Stat
else if (std.mem.startsWith(u8, ast_name, "TypePack"))
return Ast.TypePack
else if (std.mem.startsWith(u8, ast_name, "Type"))
return Ast.Type;
@compileError("Invalid Ast type");
}
pub fn selfVisit(self: anytype, this: anytype) anyerror!void {
switch (this.classIndex) {
inline else => |kind| {
const kind_type = kind.Type();
if (@hasDecl(kind_type, "visit"))
try kind_type.visit(@ptrCast(@alignCast(this)), self);
},
}
}
pub fn visit(self: anytype, this: anytype) anyerror!bool {
const node_type = @typeInfo(@TypeOf(this));
const ast_type = node_type.pointer.child;
comptime if (node_type != .pointer)
@compileError("Invalid Ast type");
comptime if (!std.mem.startsWith(u8, @typeName(ast_type), @typeName(Ast)))
@compileError("Invalid Ast type");
const namespace = @typeName(Ast);
if (ast_type == Ast.Node) {
if (comptime hasVistDecl(@TypeOf(self), "visit"))
return callVisitorDecl(self, "visit", this);
return true;
} else if (ast_type == Ast.Type or ast_type == Ast.TypePack) {
const ast_name = @typeName(ast_type)[namespace.len + 1 ..];
if (comptime hasVistDecl(@TypeOf(self), "visit" ++ ast_name))
return callVisitorDecl(self, "visit" ++ ast_name, this);
return false;
} else {
const ast_name = @typeName(ast_type)[namespace.len + 1 ..];
const fn_name = "visit" ++ ast_name;
if (comptime hasVistDecl(@TypeOf(self), fn_name))
return callVisitorDecl(self, fn_name, this);
const parent = comptime getParent(ast_type);
return Visitor.visit(self, @as(*parent, @ptrCast(@alignCast(this))));
}
}
};
test Node {
const Lexer = @import("Lexer.zig");
const Parser = @import("Parser.zig");
const Allocator = @import("Allocator.zig");
{
const allocator = Allocator.init();
defer allocator.deinit();
const table = Lexer.AstNameTable.init(allocator);
defer table.deinit();
const source =
\\local x = 1;
\\local x = 2
\\local x = 3
\\
;
var parse_result = Parser.parse(source, table, allocator, .{});
defer parse_result.deinit();
const root = parse_result.root;
try std.testing.expectEqual(Node.Kind.stat_block, root.classIndex);
const stats = root.body.slice();
try std.testing.expectEqual(3, stats.len);
for (stats, 1..) |node, order| {
switch (node.classIndex) {
.stat_local => {
const local: *StatLocal = node.as(.stat_local).?;
try std.testing.expectEqualStrings("x", std.mem.span(local.vars.slice()[0].name.value));
try std.testing.expect(@as(f64, @floatFromInt(order)) == local.values.slice()[0].as(.expr_constant_number).?.value);
},
else => {},
}
}
}
{
const allocator = Allocator.init();
defer allocator.deinit();
const astNameTable = Lexer.AstNameTable.init(allocator);
defer astNameTable.deinit();
const source =
\\@native
\\function test()
\\end
\\
;
const parseResult = Parser.parse(source, astNameTable, allocator, .{});
defer parseResult.deinit();
{
const FunctionVisitor = struct {
hasNativeFunction: bool = false,
pub fn visitExprFunction(self: *@This(), node: *Ast.ExprFunction) !bool {
errdefer unreachable;
try node.body.visit(self);
if (!self.hasNativeFunction and node.hasNativeAttribute())
self.hasNativeFunction = true;
return false;
}
};
var visitor: FunctionVisitor = .{};
parseResult.root.visit(&visitor) catch unreachable;
// no errors expected, since none of the visitor method does error
try std.testing.expect(visitor.hasNativeFunction);
}
{
const FunctionVisitor = struct {
hasNativeFunction: bool = false,
pub fn visitExprFunction(self: *@This(), node: *Ast.ExprFunction) !bool {
try node.body.visit(self);
if (!self.hasNativeFunction and node.hasNativeAttribute()) {
self.hasNativeFunction = true;
return error.Done;
}
return false;
}
};
var visitor: FunctionVisitor = .{};
parseResult.root.visit(&visitor) catch |err| switch (err) {
error.Done => {}, // this error is defined in the visitor
else => unreachable,
};
try std.testing.expect(visitor.hasNativeFunction);
}
}
}
test "AstValuesCheck" {
if (@import("builtin").cpu.arch.isWasm() or @import("builtin").os.tag == .windows)
return error.SkipZigTest;
const AstValues = struct {
pub extern "c" const AstAttrIndex: u8;
pub extern "c" const AstGenericTypeIndex: u8;
pub extern "c" const AstGenericTypePackIndex: u8;
pub extern "c" const AstExprGroupIndex: u8;
pub extern "c" const AstExprConstantNilIndex: u8;
pub extern "c" const AstExprConstantBoolIndex: u8;
pub extern "c" const AstExprConstantNumberIndex: u8;
pub extern "c" const AstExprConstantIntegerIndex: u8;
pub extern "c" const AstExprConstantStringIndex: u8;
pub extern "c" const AstExprLocalIndex: u8;
pub extern "c" const AstExprGlobalIndex: u8;
pub extern "c" const AstExprVarargsIndex: u8;
pub extern "c" const AstExprCallIndex: u8;
pub extern "c" const AstExprIndexNameIndex: u8;
pub extern "c" const AstExprIndexExprIndex: u8;
pub extern "c" const AstExprFunctionIndex: u8;
pub extern "c" const AstExprTableIndex: u8;
pub extern "c" const AstExprUnaryIndex: u8;
pub extern "c" const AstExprBinaryIndex: u8;
pub extern "c" const AstExprTypeAssertionIndex: u8;
pub extern "c" const AstExprIfElseIndex: u8;
pub extern "c" const AstExprInterpStringIndex: u8;
pub extern "c" const AstExprInstantiateIndex: u8;
pub extern "c" const AstStatBlockIndex: u8;
pub extern "c" const AstStatIfIndex: u8;
pub extern "c" const AstStatWhileIndex: u8;
pub extern "c" const AstStatRepeatIndex: u8;
pub extern "c" const AstStatBreakIndex: u8;
pub extern "c" const AstStatContinueIndex: u8;
pub extern "c" const AstStatReturnIndex: u8;
pub extern "c" const AstStatExprIndex: u8;
pub extern "c" const AstStatLocalIndex: u8;
pub extern "c" const AstStatForIndex: u8;
pub extern "c" const AstStatForInIndex: u8;
pub extern "c" const AstStatAssignIndex: u8;
pub extern "c" const AstStatCompoundAssignIndex: u8;
pub extern "c" const AstStatFunctionIndex: u8;
pub extern "c" const AstStatLocalFunctionIndex: u8;
pub extern "c" const AstStatTypeAliasIndex: u8;
pub extern "c" const AstStatTypeFunctionIndex: u8;
pub extern "c" const AstStatDeclareFunctionIndex: u8;
pub extern "c" const AstStatDeclareGlobalIndex: u8;
pub extern "c" const AstStatClassIndex: u8;
pub extern "c" const AstStatDeclareExternTypeIndex: u8;
pub extern "c" const AstTypeReferenceIndex: u8;
pub extern "c" const AstTypeTableIndex: u8;
pub extern "c" const AstTypeFunctionIndex: u8;
pub extern "c" const AstTypeTypeofIndex: u8;
pub extern "c" const AstTypeOptionalIndex: u8;
pub extern "c" const AstTypeUnionIndex: u8;
pub extern "c" const AstTypeIntersectionIndex: u8;
pub extern "c" const AstExprErrorIndex: u8;
pub extern "c" const AstStatErrorIndex: u8;
pub extern "c" const AstTypeErrorIndex: u8;
pub extern "c" const AstTypeSingletonBoolIndex: u8;
pub extern "c" const AstTypeSingletonStringIndex: u8;
pub extern "c" const AstTypeGroupIndex: u8;
pub extern "c" const AstTypePackExplicitIndex: u8;
pub extern "c" const AstTypePackVariadicIndex: u8;
pub extern "c" const AstTypePackGenericIndex: u8;
pub extern "c" const AstAttrSize: usize;
pub extern "c" const AstGenericTypeSize: usize;
pub extern "c" const AstGenericTypePackSize: usize;
pub extern "c" const AstExprGroupSize: usize;
pub extern "c" const AstExprConstantNilSize: usize;
pub extern "c" const AstExprConstantBoolSize: usize;
pub extern "c" const AstExprConstantNumberSize: usize;
pub extern "c" const AstExprConstantIntegerSize: usize;
pub extern "c" const AstExprConstantStringSize: usize;
pub extern "c" const AstExprLocalSize: usize;
pub extern "c" const AstExprGlobalSize: usize;
pub extern "c" const AstExprVarargsSize: usize;
pub extern "c" const AstExprCallSize: usize;
pub extern "c" const AstExprIndexNameSize: usize;
pub extern "c" const AstExprIndexExprSize: usize;
pub extern "c" const AstExprFunctionSize: usize;
pub extern "c" const AstExprTableSize: usize;
pub extern "c" const AstExprUnarySize: usize;
pub extern "c" const AstExprBinarySize: usize;
pub extern "c" const AstExprTypeAssertionSize: usize;
pub extern "c" const AstExprIfElseSize: usize;
pub extern "c" const AstExprInterpStringSize: usize;
pub extern "c" const AstExprInstantiateSize: usize;
pub extern "c" const AstStatBlockSize: usize;
pub extern "c" const AstStatIfSize: usize;
pub extern "c" const AstStatWhileSize: usize;
pub extern "c" const AstStatRepeatSize: usize;
pub extern "c" const AstStatBreakSize: usize;
pub extern "c" const AstStatContinueSize: usize;
pub extern "c" const AstStatReturnSize: usize;
pub extern "c" const AstStatExprSize: usize;
pub extern "c" const AstStatLocalSize: usize;
pub extern "c" const AstStatForSize: usize;
pub extern "c" const AstStatForInSize: usize;
pub extern "c" const AstStatAssignSize: usize;
pub extern "c" const AstStatCompoundAssignSize: usize;
pub extern "c" const AstStatFunctionSize: usize;
pub extern "c" const AstStatLocalFunctionSize: usize;
pub extern "c" const AstStatTypeAliasSize: usize;
pub extern "c" const AstStatTypeFunctionSize: usize;
pub extern "c" const AstStatDeclareFunctionSize: usize;
pub extern "c" const AstStatDeclareGlobalSize: usize;
pub extern "c" const AstStatClassSize: usize;
pub extern "c" const AstStatDeclareExternTypeSize: usize;
pub extern "c" const AstTypeReferenceSize: usize;
pub extern "c" const AstTypeTableSize: usize;
pub extern "c" const AstTypeFunctionSize: usize;
pub extern "c" const AstTypeTypeofSize: usize;
pub extern "c" const AstTypeOptionalSize: usize;
pub extern "c" const AstTypeUnionSize: usize;
pub extern "c" const AstTypeIntersectionSize: usize;
pub extern "c" const AstExprErrorSize: usize;
pub extern "c" const AstStatErrorSize: usize;
pub extern "c" const AstTypeErrorSize: usize;
pub extern "c" const AstTypeSingletonBoolSize: usize;
pub extern "c" const AstTypeSingletonStringSize: usize;
pub extern "c" const AstTypeGroupSize: usize;
pub extern "c" const AstTypePackExplicitSize: usize;
pub extern "c" const AstTypePackVariadicSize: usize;
pub extern "c" const AstTypePackGenericSize: usize;
};
@setEvalBranchQuota(2000);
inline for (@typeInfo(AstValues).@"struct".decls) |decl| {
if (comptime std.mem.endsWith(u8, decl.name, "Index")) {
const name = decl.name[3 .. decl.name.len - 5];
const ast_node_type = @field(Ast, name);
const info = @typeInfo(ast_node_type).@"struct";
comptime var field: ?std.builtin.Type.StructField = null;
inline for (info.fields) |f| {
if (comptime std.mem.eql(u8, f.name, "classIndex")) {
field = f;
break;
}
}
if (field == null)
@compileError("classIndex field not found");
const default_value_ptr = field.?.default_value_ptr orelse @compileError("classIndex field does not have a default value");
const enum_value = @as(*const Ast.Node.Kind, @ptrCast(@alignCast(default_value_ptr))).*;
std.testing.expectEqual(@field(AstValues, decl.name), @intFromEnum(enum_value)) catch |err| {
std.debug.print("index error for {s}\n", .{name});
return err;
};
} else if (comptime std.mem.endsWith(u8, decl.name, "Size")) {
const name = decl.name[3 .. decl.name.len - 4];
const ast_node_type = @field(Ast, name);
std.testing.expectEqual(@field(AstValues, decl.name), @sizeOf(ast_node_type)) catch |err| {
std.debug.print("size error for {s}\n", .{name});
return err;
};
} else @compileError("unknown exported constant");
}
}
// sources:
// https://github.com/luau-lang/luau/blob/40d4815888f63362a6cb79b3e74c4aafa0b2cbf4/Ast/include/Luau/Ast.h
// https://github.com/luau-lang/luau/blob/40d4815888f63362a6cb79b3e74c4aafa0b2cbf4/Ast/src/Ast.cpp