Files
sx/src/ast.zig
2026-03-05 16:20:36 +02:00

511 lines
12 KiB
Zig

const std = @import("std");
pub const Span = struct {
start: u32,
end: u32,
};
pub const Node = struct {
span: Span,
data: Data,
source_file: ?[]const u8 = null,
pub const Data = union(enum) {
root: Root,
fn_decl: FnDecl,
block: Block,
int_literal: IntLiteral,
float_literal: FloatLiteral,
bool_literal: BoolLiteral,
string_literal: StringLiteral,
identifier: Identifier,
enum_literal: EnumLiteral,
binary_op: BinaryOp,
chained_comparison: ChainedComparison,
unary_op: UnaryOp,
call: Call,
field_access: FieldAccess,
if_expr: IfExpr,
match_expr: MatchExpr,
match_arm: MatchArm,
const_decl: ConstDecl,
var_decl: VarDecl,
assignment: Assignment,
multi_assign: MultiAssign,
destructure_decl: DestructureDecl,
enum_decl: EnumDecl,
struct_decl: StructDecl,
struct_literal: StructLiteral,
union_decl: UnionDecl,
lambda: Lambda,
type_expr: TypeExpr,
param: Param,
defer_stmt: DeferStmt,
push_stmt: PushStmt,
comptime_expr: ComptimeExpr,
insert_expr: InsertExpr,
return_stmt: ReturnStmt,
import_decl: ImportDecl,
namespace_decl: NamespaceDecl,
array_type_expr: ArrayTypeExpr,
slice_type_expr: SliceTypeExpr,
array_literal: ArrayLiteral,
parameterized_type_expr: ParameterizedTypeExpr,
index_expr: IndexExpr,
slice_expr: SliceExpr,
pointer_type_expr: PointerTypeExpr,
many_pointer_type_expr: ManyPointerTypeExpr,
optional_type_expr: OptionalTypeExpr,
force_unwrap: ForceUnwrap,
null_coalesce: NullCoalesce,
deref_expr: DerefExpr,
null_literal: void,
while_expr: WhileExpr,
for_expr: ForExpr,
spread_expr: SpreadExpr,
break_expr: void,
continue_expr: void,
undef_literal: void,
inferred_type: void,
builtin_expr: void,
compiler_expr: void,
foreign_expr: ForeignExpr,
library_decl: LibraryDecl,
function_type_expr: FunctionTypeExpr,
closure_type_expr: ClosureTypeExpr,
tuple_type_expr: TupleTypeExpr,
tuple_literal: TupleLiteral,
ufcs_alias: UfcsAlias,
c_import_decl: CImportDecl,
protocol_decl: ProtocolDecl,
impl_block: ImplBlock,
pub fn declName(self: Data) ?[]const u8 {
return switch (self) {
.fn_decl => |d| d.name,
.const_decl => |d| d.name,
.var_decl => |d| d.name,
.enum_decl => |d| d.name,
.struct_decl => |d| d.name,
.union_decl => |d| d.name,
.namespace_decl => |d| d.name,
.ufcs_alias => |d| d.name,
.c_import_decl => |d| d.name,
.protocol_decl => |d| d.name,
else => null,
};
}
};
};
pub const Root = struct {
decls: []const *Node,
};
pub const CallingConvention = enum { default, c };
pub const FnDecl = struct {
name: []const u8,
params: []const Param,
return_type: ?*Node,
body: *Node,
type_params: []const StructTypeParam = &.{},
is_arrow: bool = false,
call_conv: CallingConvention = .default,
};
pub const Param = struct {
name: []const u8,
name_span: Span,
type_expr: *Node,
is_variadic: bool = false,
is_comptime: bool = false,
};
pub const Block = struct {
stmts: []const *Node,
};
pub const IntLiteral = struct {
value: i64,
};
pub const FloatLiteral = struct {
value: f64,
};
pub const BoolLiteral = struct {
value: bool,
};
pub const StringLiteral = struct {
raw: []const u8,
is_raw: bool = false,
};
pub const Identifier = struct {
name: []const u8,
};
pub const EnumLiteral = struct {
name: []const u8, // without the leading dot
};
pub const BinaryOp = struct {
op: Op,
lhs: *Node,
rhs: *Node,
pub const Op = enum {
add,
sub,
mul,
div,
mod,
eq,
neq,
lt,
lte,
gt,
gte,
and_op,
or_op,
bit_and,
bit_or,
bit_xor,
shl,
shr,
in_op,
};
};
pub const ChainedComparison = struct {
operands: []const *Node,
ops: []const BinaryOp.Op,
};
pub const UnaryOp = struct {
op: Op,
operand: *Node,
pub const Op = enum {
negate,
not,
bit_not,
xx,
address_of,
};
};
pub const Call = struct {
callee: *Node,
args: []const *Node,
};
pub const FieldAccess = struct {
object: *Node,
field: []const u8,
is_optional: bool = false,
};
pub const IfExpr = struct {
condition: *Node,
then_branch: *Node,
else_branch: ?*Node,
is_inline: bool, // true for `if cond then a else b`
is_comptime: bool = false, // true for `inline if` — compile-time branch elimination
binding_name: ?[]const u8 = null, // for `if val := expr { ... }` optional binding
};
pub const MatchExpr = struct {
subject: *Node,
arms: []const MatchArm,
is_comptime: bool = false,
};
pub const MatchArm = struct {
pattern: ?*Node, // null = else (default) arm
body: *Node,
is_break: bool,
capture: ?[]const u8 = null, // payload binding name: case .variant: (name) { ... }
};
pub const ConstDecl = struct {
name: []const u8,
type_annotation: ?*Node,
value: *Node,
};
pub const VarDecl = struct {
name: []const u8,
type_annotation: ?*Node,
value: ?*Node,
};
pub const Assignment = struct {
target: *Node,
op: Op,
value: *Node,
pub const Op = enum {
assign,
add_assign,
sub_assign,
mul_assign,
div_assign,
mod_assign,
and_assign,
or_assign,
xor_assign,
shl_assign,
shr_assign,
};
};
pub const MultiAssign = struct {
targets: []const *Node,
values: []const *Node,
};
pub const DestructureDecl = struct {
names: []const []const u8,
value: *Node,
};
pub const EnumDecl = struct {
name: []const u8,
variant_names: []const []const u8,
variant_types: []const ?*Node = &.{}, // null entries = no payload; empty = payload-less enum
is_flags: bool = false,
variant_values: []const ?*Node = &.{}, // explicit value per variant (null = auto), empty = all auto
backing_type: ?*Node = null, // optional backing type: enum u8 { ... }
};
pub const UnionDecl = struct {
name: []const u8,
field_names: []const []const u8,
field_types: []const *Node,
};
pub const StructTypeParam = struct {
name: []const u8, // e.g. "N" or "T" (without $)
constraint: *Node, // type_expr: "u32" for value param, "Type" for type param
protocol_constraints: []const []const u8 = &.{}, // e.g. ["Eq", "Hashable"] for $T/Eq/Hashable
};
pub const UsingEntry = struct {
insert_index: u32, // position in field_names where used fields are spliced
type_name: []const u8, // struct type to inline
};
pub const StructDecl = struct {
name: []const u8,
field_names: []const []const u8,
field_types: []const *Node, // type_expr nodes
field_defaults: []const ?*Node, // default value per field, null if none
type_params: []const StructTypeParam = &.{},
using_entries: []const UsingEntry = &.{},
methods: []const *Node = &.{}, // fn_decl nodes for struct methods
constants: []const *Node = &.{}, // const_decl nodes for struct-level constants
};
pub const StructFieldInit = struct {
name: ?[]const u8, // null for positional, non-null for named/shorthand
value: *Node,
};
pub const StructLiteral = struct {
struct_name: ?[]const u8, // null for anonymous `.{ ... }`
type_expr: ?*Node = null, // for GenericType(args).{ ... }
field_inits: []const StructFieldInit,
init_block: ?*Node = null, // optional `{ stmts }` block after struct literal
};
pub const Lambda = struct {
params: []const Param,
return_type: ?*Node,
body: *Node,
type_params: []const StructTypeParam = &.{},
call_conv: CallingConvention = .default,
};
pub const TypeExpr = struct {
name: []const u8,
is_generic: bool = false,
protocol_constraints: []const []const u8 = &.{}, // e.g. ["Eq", "Hashable"] for $T/Eq/Hashable
};
pub const DeferStmt = struct {
expr: *Node,
};
pub const PushStmt = struct {
context_expr: *Node,
body: *Node,
};
pub const ComptimeExpr = struct {
expr: *Node,
};
pub const InsertExpr = struct {
expr: *Node,
};
pub const ReturnStmt = struct {
value: ?*Node,
};
pub const ImportDecl = struct {
path: []const u8,
name: ?[]const u8,
};
pub const ArrayTypeExpr = struct {
length: *Node, // int_literal for the size
element_type: *Node, // type_expr for the element type
};
pub const SliceTypeExpr = struct {
element_type: *Node, // type_expr for the element type
};
pub const ArrayLiteral = struct {
elements: []const *Node,
type_expr: ?*Node = null,
};
pub const ParameterizedTypeExpr = struct {
name: []const u8, // e.g. "Vector", or later generic struct names
args: []const *Node, // e.g. [int_literal(3), type_expr("f32")]
};
pub const IndexExpr = struct {
object: *Node,
index: *Node,
};
pub const SliceExpr = struct {
object: *Node,
start: ?*Node = null,
end: ?*Node = null,
};
pub const PointerTypeExpr = struct {
pointee_type: *Node,
};
pub const ManyPointerTypeExpr = struct {
element_type: *Node,
};
pub const OptionalTypeExpr = struct {
inner_type: *Node,
};
pub const ForceUnwrap = struct {
operand: *Node,
};
pub const NullCoalesce = struct {
lhs: *Node,
rhs: *Node,
};
pub const DerefExpr = struct {
operand: *Node,
};
pub const WhileExpr = struct {
condition: *Node,
body: *Node,
binding_name: ?[]const u8 = null, // for `while val := expr { ... }` optional binding
};
pub const ForExpr = struct {
iterable: *Node,
body: *Node,
capture_name: []const u8,
index_name: ?[]const u8 = null,
};
pub const SpreadExpr = struct {
operand: *Node,
};
pub const NamespaceDecl = struct {
name: []const u8,
decls: []const *Node,
};
pub const ForeignExpr = struct {
library_ref: ?[]const u8 = null, // identifier name of library constant
c_name: ?[]const u8 = null, // C symbol name override
};
pub const LibraryDecl = struct {
lib_name: []const u8,
name: []const u8, // sx-side constant name
};
pub const FunctionTypeExpr = struct {
param_types: []const *Node,
param_names: ?[]const ?[]const u8 = null, // optional documentation names
return_type: ?*Node, // null = void return
};
pub const ClosureTypeExpr = struct {
param_types: []const *Node,
param_names: ?[]const ?[]const u8 = null, // optional documentation names
return_type: ?*Node, // null = void return
};
pub const TupleTypeExpr = struct {
field_types: []const *Node,
field_names: ?[]const []const u8, // null for positional
};
pub const TupleLiteral = struct {
elements: []const TupleElement,
};
pub const TupleElement = struct {
name: ?[]const u8, // null for positional
value: *Node,
};
pub const UfcsAlias = struct {
name: []const u8,
target: []const u8,
};
pub const CImportDecl = struct {
includes: []const []const u8,
sources: []const []const u8,
defines: []const []const u8,
flags: []const []const u8,
name: ?[]const u8 = null,
bitcode_paths: []const []const u8 = &.{}, // populated during import resolution
};
pub const ProtocolMethodDecl = struct {
name: []const u8,
params: []const *Node, // type_expr nodes for parameter types (excluding implicit self)
param_names: []const []const u8, // parameter names (excluding implicit self)
return_type: ?*Node, // null = void return
default_body: ?*Node, // null = required method, non-null = default implementation
};
pub const ProtocolDecl = struct {
name: []const u8,
methods: []const ProtocolMethodDecl,
is_inline: bool = false, // #inline — embedded fn ptrs instead of vtable pointer
};
pub const ImplBlock = struct {
protocol_name: []const u8,
target_type: []const u8,
target_type_params: []const StructTypeParam = &.{}, // for `impl P for List($T)`
methods: []const *Node, // fn_decl nodes
};