2022-10-28 04:59:24 +01:00
//! Hacky comptime interpreter, courtesy of midnight code run fuelled by spite;
//! hope that one day this can use async... <33
// TODO: DODify
const std = @import ( " std " ) ;
const ast = @import ( " ast.zig " ) ;
const zig = std . zig ;
const Ast = zig . Ast ;
const analysis = @import ( " analysis.zig " ) ;
const DocumentStore = @import ( " DocumentStore.zig " ) ;
const ComptimeInterpreter = @This ( ) ;
2022-11-10 04:46:23 +00:00
const log = std . log . scoped ( . comptime_interpreter ) ;
2022-10-31 20:00:02 +00:00
// TODO: Investigate arena
2022-10-28 04:59:24 +01:00
allocator : std . mem . Allocator ,
2022-10-29 06:46:22 +01:00
document_store : * DocumentStore ,
2022-11-11 01:51:02 +00:00
uri : DocumentStore . Uri ,
2022-10-29 22:28:44 +01:00
root_type : ? Type = null ,
2022-10-28 04:59:24 +01:00
2022-10-30 08:07:49 +00:00
/// Interpreter diagnostic errors
errors : std . AutoArrayHashMapUnmanaged ( Ast . Node . Index , InterpreterError ) = . { } ,
2022-10-29 22:28:44 +01:00
// TODO: Deduplicate typeinfo across different interpreters
2022-10-28 04:59:24 +01:00
type_info : std . ArrayListUnmanaged ( TypeInfo ) = . { } ,
type_info_map : std . HashMapUnmanaged ( TypeInfo , usize , TypeInfo . Context , std . hash_map . default_max_load_percentage ) = . { } ,
2022-10-31 20:00:02 +00:00
// TODO: Use DOD
value_data_list : std . ArrayListUnmanaged ( * ValueData ) = . { } ,
2022-11-11 01:51:02 +00:00
pub fn getHandle ( interpreter : * ComptimeInterpreter ) * const DocumentStore . Handle {
// This interpreter is loaded from a known-valid handle so a valid handle must exist
return interpreter . document_store . getOrLoadHandle ( interpreter . uri ) . ? ;
}
2022-10-30 08:07:49 +00:00
pub const InterpreterError = struct {
code : [ ] const u8 ,
message : [ ] const u8 ,
} ;
2022-10-31 20:00:02 +00:00
/// `message` must be allocated with interpreter allocator
2022-10-31 05:51:51 +00:00
pub fn recordError ( interpreter : * ComptimeInterpreter , node_idx : Ast . Node . Index , code : [ ] const u8 , message : [ ] const u8 ) error { OutOfMemory } ! void {
2022-10-30 08:07:49 +00:00
try interpreter . errors . put ( interpreter . allocator , node_idx , . {
. code = code ,
. message = message ,
} ) ;
}
2022-10-28 04:59:24 +01:00
pub fn deinit ( interpreter : * ComptimeInterpreter ) void {
2022-10-31 20:00:02 +00:00
var err_it = interpreter . errors . iterator ( ) ;
while ( err_it . next ( ) ) | entry | interpreter . allocator . free ( entry . value_ptr . message ) ;
2022-10-29 22:28:44 +01:00
if ( interpreter . root_type ) | rt | rt . getTypeInfo ( ) . getScopeOfType ( ) . ? . deinit ( ) ;
2022-10-28 06:22:03 +01:00
for ( interpreter . type_info . items ) | * ti | ti . deinit ( interpreter . allocator ) ;
2022-10-31 20:00:02 +00:00
for ( interpreter . value_data_list . items ) | ti | interpreter . allocator . destroy ( ti ) ;
interpreter . errors . deinit ( interpreter . allocator ) ;
2022-10-28 04:59:24 +01:00
interpreter . type_info . deinit ( interpreter . allocator ) ;
interpreter . type_info_map . deinit ( interpreter . allocator ) ;
2022-10-31 20:00:02 +00:00
interpreter . value_data_list . deinit ( interpreter . allocator ) ;
2022-10-28 04:59:24 +01:00
}
pub const TypeInfo = union ( enum ) {
pub const Context = struct {
interpreter : ComptimeInterpreter ,
hasher : * std . hash . Wyhash ,
pub fn hash ( self : @This ( ) , s : TypeInfo ) u64 {
TypeInfo . hash ( self , s ) ;
return self . hasher . final ( ) ;
}
pub fn eql ( self : @This ( ) , a : TypeInfo , b : TypeInfo ) bool {
2022-11-08 20:54:30 +00:00
_ = self ;
return TypeInfo . eql ( a , b ) ;
2022-10-28 04:59:24 +01:00
}
} ;
pub const Signedness = enum { signed , unsigned } ;
pub const Struct = struct {
/// Declarations contained within
scope : * InterpreterScope ,
2022-10-30 08:07:49 +00:00
fields : std . StringHashMapUnmanaged ( FieldDefinition ) = . { } ,
2022-10-28 04:59:24 +01:00
} ;
pub const Int = struct {
bits : u16 ,
signedness : Signedness ,
} ;
pub const Pointer = struct {
size : Size ,
is_const : bool ,
is_volatile : bool ,
child : Type ,
is_allowzero : bool ,
2022-10-31 20:00:02 +00:00
sentinel : ? * ValueData ,
2022-10-28 04:59:24 +01:00
pub const Size = enum {
one ,
many ,
slice ,
c ,
} ;
} ;
pub const Fn = struct {
return_type : ? Type ,
/// Index into interpreter.declarations
params : std . ArrayListUnmanaged ( usize ) = . { } ,
} ;
2022-10-31 20:00:02 +00:00
pub const Array = struct {
len : usize ,
child : Type ,
sentinel : ? * ValueData ,
} ;
2022-10-28 04:59:24 +01:00
/// Hack to get anytype working; only valid on fnparams
@ " anytype " ,
@ " type " ,
@ " bool " ,
@ " struct " : Struct ,
pointer : Pointer ,
2022-10-29 06:46:22 +01:00
@ " fn " : Fn ,
2022-10-28 04:59:24 +01:00
int : Int ,
@ " comptime_int " ,
float : u16 ,
@ " comptime_float " ,
2022-10-31 20:00:02 +00:00
array : Array ,
2022-11-08 20:54:30 +00:00
pub fn eql ( a : TypeInfo , b : TypeInfo ) bool {
2022-10-28 04:59:24 +01:00
if ( std . meta . activeTag ( a ) ! = std . meta . activeTag ( b ) ) return false ;
return switch ( a ) {
2022-11-08 20:54:30 +00:00
. @ " struct " = > false , // Struct declarations can never be equal (this is a lie, gotta fix this)
2022-10-28 04:59:24 +01:00
. pointer = > p : {
const ap = a . pointer ;
const bp = b . pointer ;
break : p ap . size = = bp . size and ap . is_const = = bp . is_const and ap . is_volatile = = bp . is_volatile and eql (
2022-10-29 22:28:44 +01:00
ap . child . getTypeInfo ( ) ,
bp . child . getTypeInfo ( ) ,
2022-10-28 04:59:24 +01:00
) and ap . is_allowzero = = bp . is_allowzero and ( ( ap . sentinel = = null and bp . sentinel = = null ) or ( ( ap . sentinel ! = null and bp . sentinel ! = null ) and ap . sentinel . ? . eql ( bp . sentinel . ? ) ) ) ;
} ,
. int = > a . int . signedness = = b . int . signedness and a . int . bits = = b . int . bits ,
. float = > a . float = = b . float ,
else = > return true ,
} ;
}
pub fn hash ( context : TypeInfo . Context , ti : TypeInfo ) void {
context . hasher . update ( & [ _ ] u8 { @enumToInt ( ti ) } ) ;
return switch ( ti ) {
. @ " struct " = > | s | {
2022-10-30 08:07:49 +00:00
_ = s ;
// TODO: Fix
// context.hasher.update(std.mem.sliceAsBytes(s.fields.items));
2022-10-28 04:59:24 +01:00
// TODO: Fix
// context.hasher.update(std.mem.sliceAsBytes(s.declarations.items));
} ,
. pointer = > | p | {
// const ap = a.pointer;
// const bp = b.pointer;
context . hasher . update ( & [ _ ] u8 { @enumToInt ( p . size ) , @boolToInt ( p . is_const ) , @boolToInt ( p . is_volatile ) } ) ;
2022-10-29 22:28:44 +01:00
TypeInfo . hash ( context , p . child . getTypeInfo ( ) ) ;
2022-10-28 04:59:24 +01:00
context . hasher . update ( & [ _ ] u8 { @boolToInt ( p . is_allowzero ) } ) ;
// TODO: Hash Sentinel
// break :p ap.size == bp.size and ap.is_const == bp.is_const and ap.is_volatile == bp.is_volatile and eql(
// source_unit,
// source_interpreter.type_info.items[ap.child.info_idx],
// source_interpreter.type_info.items[bp.child.info_idx],
// ) and ap.is_allowzero == bp.is_allowzero and ((ap.sentinel == null and bp.sentinel == null) or ((ap.sentinel != null and bp.sentinel != null) and ap.sentinel.?.eql(bp.sentinel.?)));
} ,
. int = > | i | {
// a.int.signedness == b.int.signedness and a.int.bits == b.int.bits;
context . hasher . update ( & [ _ ] u8 { @enumToInt ( i . signedness ) } ) ;
context . hasher . update ( & std . mem . toBytes ( i . bits ) ) ;
} ,
. float = > | f | context . hasher . update ( & std . mem . toBytes ( f ) ) ,
else = > { } ,
} ;
}
2022-10-28 06:22:03 +01:00
pub fn deinit ( ti : * TypeInfo , allocator : std . mem . Allocator ) void {
switch ( ti . * ) {
. @ " struct " = > | * s | s . fields . deinit ( allocator ) ,
else = > { } ,
}
}
2022-10-29 06:46:22 +01:00
pub fn getScopeOfType ( ti : TypeInfo ) ? * InterpreterScope {
return switch ( ti ) {
. @ " struct " = > | s | s . scope ,
else = > null ,
} ;
}
2022-10-28 04:59:24 +01:00
} ;
pub const Type = struct {
2022-11-01 03:36:13 +00:00
interpreter : * ComptimeInterpreter ,
2022-10-29 22:28:44 +01:00
2022-10-28 04:59:24 +01:00
node_idx : Ast . Node . Index ,
info_idx : usize ,
2022-10-29 22:28:44 +01:00
pub fn getTypeInfo ( @ " type " : Type ) TypeInfo {
2022-11-01 03:36:13 +00:00
return @ " type " . interpreter . type_info . items [ @ " type " . info_idx ] ;
2022-10-29 22:28:44 +01:00
}
2022-10-30 08:07:49 +00:00
/// Be careful with this; typeinfo resizes reassign pointers!
pub fn getTypeInfoMutable ( @ " type " : Type ) * TypeInfo {
2022-11-01 03:36:13 +00:00
return & @ " type " . interpreter . type_info . items [ @ " type " . info_idx ] ;
2022-10-30 08:07:49 +00:00
}
2022-10-28 04:59:24 +01:00
} ;
pub const Value = struct {
2022-11-01 03:36:13 +00:00
interpreter : * ComptimeInterpreter ,
2022-10-29 22:28:44 +01:00
2022-10-28 04:59:24 +01:00
node_idx : Ast . Node . Index ,
@ " type " : Type ,
2022-10-31 20:00:02 +00:00
value_data : * ValueData ,
2022-10-28 04:59:24 +01:00
pub fn eql ( value : Value , other_value : Value ) bool {
return value . value_data . eql ( other_value . value_data ) ;
}
} ;
pub const ValueData = union ( enum ) {
// TODO: Support larger ints, floats; bigints?
@ " type " : Type ,
@ " bool " : bool ,
2022-11-11 01:51:02 +00:00
@ " struct " : struct { } ,
2022-10-31 05:51:51 +00:00
/// This is what a pointer is; we don't need to map
/// this to anything because @ptrToInt is comptime-illegal
/// Pointer equality scares me though :( (but that's for later)
one_ptr : * ValueData ,
2022-10-30 08:07:49 +00:00
/// Special case slice; this is extremely common at comptime so it makes sense
2022-10-31 05:51:51 +00:00
slice_of_const_u8 : [ ] const u8 ,
2022-10-28 04:59:24 +01:00
unsigned_int : u64 ,
signed_int : i64 ,
2022-10-31 05:51:51 +00:00
/// If the int does not fit into the previous respective slots,
/// use a bit int to store it
big_int : std . math . big . int . Managed ,
2022-10-28 04:59:24 +01:00
float : f64 ,
2022-10-29 06:46:22 +01:00
@ " fn " ,
2022-10-28 19:24:38 +01:00
runtime ,
comptime_undetermined ,
2022-10-31 20:00:02 +00:00
pub fn eql ( data : * ValueData , other_data : * ValueData ) bool {
if ( std . meta . activeTag ( data . * ) ! = std . meta . activeTag ( other_data . * ) ) return false ;
2022-10-28 04:59:24 +01:00
// std.enums.
// std.meta.activeTag(u: anytype)
2022-10-31 20:00:02 +00:00
switch ( data . * ) {
2022-10-28 04:59:24 +01:00
. @ " bool " = > return data . @ " bool " = = other_data . @ " bool " ,
2022-10-31 05:51:51 +00:00
. big_int = > return data . big_int . eq ( other_data . big_int ) ,
2022-10-28 04:59:24 +01:00
. unsigned_int = > return data . unsigned_int = = other_data . unsigned_int ,
. signed_int = > return data . signed_int = = other_data . signed_int ,
. float = > return data . float = = other_data . float ,
2022-10-28 19:24:38 +01:00
else = > return false ,
2022-10-28 04:59:24 +01:00
}
}
2022-10-31 05:51:51 +00:00
/// Get the bit count required to store a certain integer
pub fn bitCount ( data : ValueData ) ? u16 {
return switch ( data ) {
// TODO: Implement for signed ints
2022-11-01 03:36:13 +00:00
. unsigned_int = > | i | if ( i = = 0 ) 0 else std . math . log2_int_ceil ( @TypeOf ( i ) , i + 1 ) ,
2022-10-31 05:51:51 +00:00
. big_int = > | bi | @intCast ( u16 , bi . bitCountAbs ( ) ) ,
else = > null ,
} ;
}
2022-10-28 04:59:24 +01:00
} ;
pub const FieldDefinition = struct {
node_idx : Ast . Node . Index ,
/// Store name so tree doesn't need to be used to access field name
2022-11-26 17:22:16 +00:00
/// When the field is a tuple field, `name` will be an empty slice
2022-10-28 04:59:24 +01:00
name : [ ] const u8 ,
@ " type " : Type ,
default_value : ? Value ,
} ;
pub const Declaration = struct {
2022-11-01 03:36:13 +00:00
scope : * InterpreterScope ,
2022-10-28 04:59:24 +01:00
node_idx : Ast . Node . Index ,
/// Store name so tree doesn't need to be used to access declaration name
name : [ ] const u8 ,
2022-11-01 03:36:13 +00:00
/// If value is null, declaration has not been interpreted yet
value : ? Value = null ,
2022-10-28 04:59:24 +01:00
// TODO: figure this out
// pub const DeclarationKind = enum{variable, function};
// pub fn declarationKind(declaration: Declaration, tree: Ast) DeclarationKind {
// return switch(tree.nodes.items(.tag)[declaration.node_idx]) {
// .fn_proto,
// .fn_proto_one,
// .fn_proto_simple,
// .fn_proto_multi,
// .fn_decl
// }
// }
2022-11-01 03:36:13 +00:00
pub fn getValue ( decl : * Declaration ) InterpretError ! Value {
var interpreter = decl . scope . interpreter ;
2022-11-11 01:51:02 +00:00
const tree = decl . scope . interpreter . getHandle ( ) . tree ;
2022-11-01 03:36:13 +00:00
const tags = tree . nodes . items ( . tag ) ;
if ( decl . value = = null ) {
switch ( tags [ decl . node_idx ] ) {
. global_var_decl ,
. local_var_decl ,
. aligned_var_decl ,
. simple_var_decl ,
= > {
const var_decl = ast . varDecl ( tree , decl . node_idx ) . ? ;
if ( var_decl . ast . init_node = = 0 )
return error . CriticalAstFailure ;
var value = try ( try interpreter . interpret ( var_decl . ast . init_node , decl . scope , . { } ) ) . getValue ( ) ;
if ( var_decl . ast . type_node ! = 0 ) {
var type_val = try ( try interpreter . interpret ( var_decl . ast . type_node , decl . scope , . { } ) ) . getValue ( ) ;
if ( type_val . @ " type " . getTypeInfo ( ) ! = . @ " type " ) {
try interpreter . recordError (
decl . node_idx ,
" expected_type " ,
std . fmt . allocPrint ( interpreter . allocator , " expected type 'type', found '{s}' " , . { interpreter . formatTypeInfo ( type_val . @ " type " . getTypeInfo ( ) ) } ) catch return error . CriticalAstFailure ,
) ;
return error . InvalidCast ;
}
value = try interpreter . cast ( var_decl . ast . type_node , type_val . value_data . @ " type " , value ) ;
}
decl . value = value ;
} ,
else = > @panic ( " No other case supported for lazy declaration evaluation " ) ,
}
}
return decl . value . ? ;
}
pub fn isConstant ( declaration : Declaration ) bool {
2022-11-11 01:51:02 +00:00
const tree = declaration . scope . interpreter . getHandle ( ) . tree ;
2022-10-28 04:59:24 +01:00
return switch ( tree . nodes . items ( . tag ) [ declaration . node_idx ] ) {
. global_var_decl ,
. local_var_decl ,
. aligned_var_decl ,
. simple_var_decl ,
= > {
2022-10-29 06:46:22 +01:00
return tree . tokenSlice ( ast . varDecl ( tree , declaration . node_idx ) . ? . ast . mut_token ) . len ! = 3 ;
2022-10-28 04:59:24 +01:00
} ,
else = > false ,
} ;
}
} ;
pub fn createType ( interpreter : * ComptimeInterpreter , node_idx : Ast . Node . Index , type_info : TypeInfo ) std . mem . Allocator . Error ! Type {
// TODO: Figure out dedup
var hasher = std . hash . Wyhash . init ( 0 ) ;
var gpr = try interpreter . type_info_map . getOrPutContext ( interpreter . allocator , type_info , . { . interpreter = interpreter . * , . hasher = & hasher } ) ;
if ( gpr . found_existing ) {
2022-11-01 03:36:13 +00:00
return Type { . interpreter = interpreter , . node_idx = node_idx , . info_idx = gpr . value_ptr . * } ;
2022-10-28 04:59:24 +01:00
} else {
try interpreter . type_info . append ( interpreter . allocator , type_info ) ;
const info_idx = interpreter . type_info . items . len - 1 ;
gpr . value_ptr . * = info_idx ;
2022-11-01 03:36:13 +00:00
return Type { . interpreter = interpreter , . node_idx = node_idx , . info_idx = info_idx } ;
2022-10-28 04:59:24 +01:00
}
}
2022-10-31 20:00:02 +00:00
pub fn createValueData ( interpreter : * ComptimeInterpreter , data : ValueData ) error { OutOfMemory } ! * ValueData {
var vd = try interpreter . allocator . create ( ValueData ) ;
try interpreter . value_data_list . append ( interpreter . allocator , vd ) ;
vd . * = data ;
return vd ;
}
2022-10-28 04:59:24 +01:00
pub const TypeInfoFormatter = struct {
2022-10-29 06:46:22 +01:00
interpreter : * const ComptimeInterpreter ,
2022-10-28 04:59:24 +01:00
ti : TypeInfo ,
pub fn format ( value : TypeInfoFormatter , comptime fmt : [ ] const u8 , options : std . fmt . FormatOptions , writer : anytype ) ! void {
_ = fmt ;
_ = options ;
return switch ( value . ti ) {
. int = > | ii | switch ( ii . signedness ) {
. signed = > try writer . print ( " i{d} " , . { ii . bits } ) ,
. unsigned = > try writer . print ( " u{d} " , . { ii . bits } ) ,
} , // TODO
. float = > | f | try writer . print ( " f{d} " , . { f } ) ,
. @ " comptime_int " = > try writer . writeAll ( " comptime_int " ) ,
. @ " comptime_float " = > try writer . writeAll ( " comptime_float " ) ,
. @ " type " = > try writer . writeAll ( " type " ) ,
. @ " bool " = > try writer . writeAll ( " bool " ) ,
. @ " struct " = > | s | {
try writer . writeAll ( " struct { " ) ;
2022-10-30 08:07:49 +00:00
var field_iterator = s . fields . iterator ( ) ;
while ( field_iterator . next ( ) ) | di | {
try writer . print ( " {s}: {s}, " , . { di . key_ptr . * , value . interpreter . formatTypeInfo ( di . value_ptr . * . @ " type " . getTypeInfo ( ) ) } ) ;
2022-10-28 06:22:03 +01:00
}
2022-10-30 08:07:49 +00:00
2022-10-28 04:59:24 +01:00
var iterator = s . scope . declarations . iterator ( ) ;
while ( iterator . next ( ) ) | di | {
2022-11-01 03:36:13 +00:00
const decl = di . value_ptr ;
if ( decl . isConstant ( ) ) {
if ( decl . value ) | sv | {
try writer . print ( " const {s}: {any} = { }, " , . {
decl . name ,
value . interpreter . formatTypeInfo ( sv . @ " type " . getTypeInfo ( ) ) ,
value . interpreter . formatValue ( sv ) ,
} ) ;
} else {
try writer . print ( " const {s} (not analyzed), " , . { decl . name } ) ;
}
2022-10-28 04:59:24 +01:00
} else {
2022-11-01 03:36:13 +00:00
if ( decl . value ) | sv | {
try writer . print ( " var {s}: {any} = { }, " , . {
decl . name ,
value . interpreter . formatTypeInfo ( sv . @ " type " . getTypeInfo ( ) ) ,
value . interpreter . formatValue ( sv ) ,
} ) ;
} else {
try writer . print ( " var {s} (not analyzed), " , . { decl . name } ) ;
}
2022-10-28 04:59:24 +01:00
}
}
try writer . writeAll ( " } " ) ;
} ,
else = > try writer . print ( " UnimplementedTypeInfoPrint " , . { } ) ,
} ;
}
} ;
2022-10-29 06:46:22 +01:00
pub fn formatTypeInfo ( interpreter : * const ComptimeInterpreter , ti : TypeInfo ) TypeInfoFormatter {
2022-10-28 04:59:24 +01:00
return TypeInfoFormatter { . interpreter = interpreter , . ti = ti } ;
}
2022-10-31 20:00:02 +00:00
pub const ValueFormatter = struct {
interpreter : * const ComptimeInterpreter ,
val : Value ,
pub fn format ( form : ValueFormatter , comptime fmt : [ ] const u8 , options : std . fmt . FormatOptions , writer : anytype ) ! void {
_ = fmt ;
_ = options ;
var value = form . val ;
var ti = value . @ " type " . getTypeInfo ( ) ;
return switch ( ti ) {
. int , . @ " comptime_int " = > switch ( value . value_data . * ) {
2022-11-08 20:54:30 +00:00
. unsigned_int = > | a | try writer . print ( " {d} " , . { a } ) ,
. signed_int = > | a | try writer . print ( " {d} " , . { a } ) ,
. big_int = > | a | try writer . print ( " {d} " , . { a } ) ,
2022-10-31 20:00:02 +00:00
else = > unreachable ,
} ,
2022-11-01 03:36:13 +00:00
. @ " type " = > try writer . print ( " { } " , . { form . interpreter . formatTypeInfo ( value . value_data . @ " type " . getTypeInfo ( ) ) } ) ,
2022-10-31 20:00:02 +00:00
else = > try writer . print ( " UnimplementedValuePrint " , . { } ) ,
} ;
}
} ;
pub fn formatValue ( interpreter : * const ComptimeInterpreter , value : Value ) ValueFormatter {
return ValueFormatter { . interpreter = interpreter , . val = value } ;
}
// pub const Comptimeness = enum { @"comptime", runtime };
2022-10-28 04:59:24 +01:00
pub const InterpreterScope = struct {
interpreter : * ComptimeInterpreter ,
2022-10-31 20:00:02 +00:00
// TODO: Actually use this value
// comptimeness: Comptimeness,
2022-10-28 04:59:24 +01:00
parent : ? * InterpreterScope = null ,
node_idx : Ast . Node . Index ,
declarations : std . StringHashMapUnmanaged ( Declaration ) = . { } ,
/// Resizes can modify element pointer locations, so we use a list of pointers
child_scopes : std . ArrayListUnmanaged ( * InterpreterScope ) = . { } ,
pub const ScopeKind = enum { container , block , function } ;
2022-11-01 03:36:13 +00:00
pub fn scopeKind ( scope : InterpreterScope ) ScopeKind {
2022-11-11 01:51:02 +00:00
const tree = scope . interpreter . getHandle ( ) . tree ;
2022-10-28 04:59:24 +01:00
return switch ( tree . nodes . items ( . tag ) [ scope . node_idx ] ) {
. container_decl ,
. container_decl_trailing ,
. container_decl_arg ,
. container_decl_arg_trailing ,
. container_decl_two ,
. container_decl_two_trailing ,
. tagged_union ,
. tagged_union_trailing ,
. tagged_union_two ,
. tagged_union_two_trailing ,
. tagged_union_enum_tag ,
. tagged_union_enum_tag_trailing ,
. root ,
. error_set_decl ,
= > . container ,
else = > . block ,
} ;
}
2022-11-01 03:36:13 +00:00
pub fn getLabel ( scope : InterpreterScope ) ? Ast . TokenIndex {
2022-11-11 01:51:02 +00:00
const tree = scope . interpreter . getHandle ( ) . tree ;
2022-10-28 04:59:24 +01:00
const token_tags = tree . tokens . items ( . tag ) ;
2022-11-01 03:36:13 +00:00
return switch ( scope . scopeKind ( ) ) {
2022-10-28 04:59:24 +01:00
. block = > z : {
const lbrace = tree . nodes . items ( . main_token ) [ scope . node_idx ] ;
break : z if ( token_tags [ lbrace - 1 ] = = . colon and token_tags [ lbrace - 2 ] = = . identifier )
lbrace - 2
else
null ;
} ,
else = > null ,
} ;
}
pub const ParentScopeIterator = struct {
maybe_scope : ? * InterpreterScope ,
pub fn next ( psi : * ParentScopeIterator ) ? * InterpreterScope {
if ( psi . maybe_scope ) | scope | {
const curr = scope ;
psi . maybe_scope = scope . parent ;
return curr ;
} else return null ;
}
} ;
pub fn parentScopeIterator ( scope : * InterpreterScope ) ParentScopeIterator {
return ParentScopeIterator { . maybe_scope = scope } ;
}
pub fn deinit ( scope : * InterpreterScope ) void {
2022-10-29 06:46:22 +01:00
const allocator = scope . interpreter . allocator ;
scope . declarations . deinit ( allocator ) ;
2022-10-28 04:59:24 +01:00
for ( scope . child_scopes . items ) | child | child . deinit ( ) ;
2022-10-29 06:46:22 +01:00
scope . child_scopes . deinit ( allocator ) ;
2022-10-28 04:59:24 +01:00
2022-10-29 06:46:22 +01:00
allocator . destroy ( scope ) ;
2022-10-28 04:59:24 +01:00
}
} ;
2022-10-31 20:00:02 +00:00
pub fn newScope (
interpreter : * ComptimeInterpreter ,
maybe_parent : ? * InterpreterScope ,
node_idx : Ast . Node . Index ,
) std . mem . Allocator . Error ! * InterpreterScope {
2022-10-28 04:59:24 +01:00
var ls = try interpreter . allocator . create ( InterpreterScope ) ;
if ( maybe_parent ) | parent | try parent . child_scopes . append ( interpreter . allocator , ls ) ;
ls . * = . {
. interpreter = interpreter ,
. parent = maybe_parent ,
. node_idx = node_idx ,
} ;
return ls ;
}
pub const InterpretResult = union ( enum ) {
@ " break " : ? [ ] const u8 ,
break_with_value : struct {
label : ? [ ] const u8 ,
value : Value ,
} ,
value : Value ,
@ " return " ,
return_with_value : Value ,
nothing ,
pub fn maybeGetValue ( result : InterpretResult ) ? Value {
return switch ( result ) {
. break_with_value = > | v | v . value ,
. value = > | v | v ,
2022-10-28 19:24:38 +01:00
. return_with_value = > | v | v ,
2022-10-28 04:59:24 +01:00
else = > null ,
} ;
}
2022-10-28 06:22:03 +01:00
pub fn getValue ( result : InterpretResult ) error { ExpectedValue } ! Value {
return result . maybeGetValue ( ) orelse error . ExpectedValue ;
2022-10-28 04:59:24 +01:00
}
} ;
2022-10-29 22:28:44 +01:00
fn getDeclCount ( tree : Ast , node_idx : Ast . Node . Index ) usize {
var buffer : [ 2 ] Ast . Node . Index = undefined ;
const members = ast . declMembers ( tree , node_idx , & buffer ) ;
var count : usize = 0 ;
for ( members ) | member | {
switch ( tree . nodes . items ( . tag ) [ member ] ) {
. global_var_decl ,
. local_var_decl ,
. aligned_var_decl ,
. simple_var_decl ,
= > count + = 1 ,
else = > { } ,
}
}
return count ;
}
pub fn huntItDown (
interpreter : * ComptimeInterpreter ,
scope : * InterpreterScope ,
decl_name : [ ] const u8 ,
options : InterpretOptions ,
2022-11-01 03:36:13 +00:00
) InterpretError ! * Declaration {
2022-11-11 01:51:02 +00:00
const tree = interpreter . getHandle ( ) . tree ;
2022-10-29 22:28:44 +01:00
const tags = tree . nodes . items ( . tag ) ;
var psi = scope . parentScopeIterator ( ) ;
while ( psi . next ( ) ) | pscope | {
2022-11-01 03:36:13 +00:00
const known_decl = pscope . declarations . getEntry ( decl_name ) ;
if ( pscope . scopeKind ( ) = = . container and
2022-10-29 22:28:44 +01:00
known_decl = = null and
pscope . declarations . count ( ) ! = getDeclCount ( tree , pscope . node_idx ) )
{
2022-11-10 04:46:23 +00:00
log . info ( " Order-independent evaluating {s}... " , . { decl_name } ) ;
2022-10-29 22:28:44 +01:00
var buffer : [ 2 ] Ast . Node . Index = undefined ;
const members = ast . declMembers ( tree , pscope . node_idx , & buffer ) ;
for ( members ) | member | {
switch ( tags [ member ] ) {
. global_var_decl ,
. local_var_decl ,
. aligned_var_decl ,
. simple_var_decl ,
= > {
if ( std . mem . eql ( u8 , analysis . getDeclName ( tree , member ) . ? , decl_name ) ) {
_ = try interpreter . interpret ( member , pscope , options ) ;
2022-11-01 03:36:13 +00:00
return pscope . declarations . getEntry ( decl_name ) . ? . value_ptr ;
2022-10-29 22:28:44 +01:00
}
} ,
else = > { } ,
}
}
}
2022-11-01 03:36:13 +00:00
return ( known_decl orelse continue ) . value_ptr ;
2022-10-29 22:28:44 +01:00
}
2022-11-10 04:46:23 +00:00
log . err ( " Identifier not found: {s} " , . { decl_name } ) ;
2022-10-29 22:28:44 +01:00
return error . IdentifierNotFound ;
}
2022-10-31 05:51:51 +00:00
pub fn cast (
interpreter : * ComptimeInterpreter ,
node_idx : Ast . Node . Index ,
dest_type : Type ,
value : Value ,
) error { OutOfMemory , InvalidCast } ! Value {
const value_data = value . value_data ;
const to_type_info = dest_type . getTypeInfo ( ) ;
const from_type_info = value . @ " type " . getTypeInfo ( ) ;
2022-11-08 20:54:30 +00:00
// TODO: Implement more implicit casts
if ( from_type_info . eql ( to_type_info ) ) return value ;
2022-10-31 05:51:51 +00:00
const err = switch ( from_type_info ) {
. @ " comptime_int " = > switch ( to_type_info ) {
. int = > {
if ( value_data . bitCount ( ) . ? > to_type_info . int . bits ) {
2022-10-31 20:00:02 +00:00
switch ( value_data . * ) {
2022-11-08 20:54:30 +00:00
. unsigned_int = > | bi | try interpreter . recordError ( node_idx , " invalid_cast " , try std . fmt . allocPrint ( interpreter . allocator , " integer value {d} cannot be coerced to type '{s}' " , . { bi , interpreter . formatTypeInfo ( to_type_info ) } ) ) ,
. signed_int = > | bi | try interpreter . recordError ( node_idx , " invalid_cast " , try std . fmt . allocPrint ( interpreter . allocator , " integer value {d} cannot be coerced to type '{s}' " , . { bi , interpreter . formatTypeInfo ( to_type_info ) } ) ) ,
. big_int = > | bi | try interpreter . recordError ( node_idx , " invalid_cast " , try std . fmt . allocPrint ( interpreter . allocator , " integer value {d} cannot be coerced to type '{s}' " , . { bi , interpreter . formatTypeInfo ( to_type_info ) } ) ) ,
2022-10-31 05:51:51 +00:00
else = > unreachable ,
}
return error . InvalidCast ;
}
} ,
else = > error . InvalidCast ,
} ,
else = > error . InvalidCast ,
} ;
err catch | e | {
2022-10-31 20:00:02 +00:00
try interpreter . recordError ( node_idx , " invalid_cast " , try std . fmt . allocPrint ( interpreter . allocator , " invalid cast from '{s}' to '{s}' " , . { interpreter . formatTypeInfo ( from_type_info ) , interpreter . formatTypeInfo ( to_type_info ) } ) ) ;
2022-10-31 05:51:51 +00:00
return e ;
} ;
return Value {
2022-11-01 03:36:13 +00:00
. interpreter = interpreter ,
2022-10-31 05:51:51 +00:00
. node_idx = node_idx ,
. @ " type " = dest_type ,
. value_data = value . value_data ,
} ;
}
2022-10-28 04:59:24 +01:00
// Might be useful in the future
pub const InterpretOptions = struct { } ;
2022-10-28 06:22:03 +01:00
pub const InterpretError = std . mem . Allocator . Error | | std . fmt . ParseIntError | | std . fmt . ParseFloatError | | error {
InvalidCharacter ,
InvalidBase ,
ExpectedValue ,
InvalidOperation ,
CriticalAstFailure ,
InvalidBuiltin ,
2022-10-28 19:24:38 +01:00
IdentifierNotFound ,
2022-10-29 22:28:44 +01:00
MissingArguments ,
ImportFailure ,
2022-10-31 05:51:51 +00:00
InvalidCast ,
2022-10-28 06:22:03 +01:00
} ;
2022-10-28 04:59:24 +01:00
pub fn interpret (
interpreter : * ComptimeInterpreter ,
node_idx : Ast . Node . Index ,
scope : ? * InterpreterScope ,
options : InterpretOptions ,
) InterpretError ! InterpretResult {
2022-11-11 01:51:02 +00:00
const tree = interpreter . getHandle ( ) . tree ;
2022-10-28 04:59:24 +01:00
const tags = tree . nodes . items ( . tag ) ;
const data = tree . nodes . items ( . data ) ;
const main_tokens = tree . nodes . items ( . main_token ) ;
switch ( tags [ node_idx ] ) {
. container_decl ,
. container_decl_trailing ,
. container_decl_arg ,
. container_decl_arg_trailing ,
. container_decl_two ,
. container_decl_two_trailing ,
2022-10-29 06:46:22 +01:00
// .tagged_union, // TODO: Fix these
// .tagged_union_trailing,
// .tagged_union_two,
// .tagged_union_two_trailing,
// .tagged_union_enum_tag,
// .tagged_union_enum_tag_trailing,
2022-10-28 04:59:24 +01:00
. root ,
. error_set_decl ,
= > {
var container_scope = try interpreter . newScope ( scope , node_idx ) ;
var type_info = TypeInfo {
. @ " struct " = . {
. scope = container_scope ,
} ,
} ;
2022-10-29 22:28:44 +01:00
var cont_type = try interpreter . createType ( node_idx , type_info ) ;
2022-10-28 04:59:24 +01:00
2022-10-29 22:28:44 +01:00
if ( node_idx = = 0 ) interpreter . root_type = cont_type ;
2022-10-28 04:59:24 +01:00
var buffer : [ 2 ] Ast . Node . Index = undefined ;
const members = ast . declMembers ( tree , node_idx , & buffer ) ;
2022-11-26 17:22:16 +00:00
var field_idx : usize = 0 ;
2022-10-28 04:59:24 +01:00
for ( members ) | member | {
const maybe_container_field : ? zig . Ast . full . ContainerField = switch ( tags [ member ] ) {
. container_field = > tree . containerField ( member ) ,
. container_field_align = > tree . containerFieldAlign ( member ) ,
. container_field_init = > tree . containerFieldInit ( member ) ,
else = > null ,
} ;
if ( maybe_container_field ) | field_info | {
2022-10-30 08:07:49 +00:00
var init_type_value = try ( try interpreter . interpret ( field_info . ast . type_expr , container_scope , . { } ) ) . getValue ( ) ;
2022-10-28 04:59:24 +01:00
var default_value = if ( field_info . ast . value_expr = = 0 )
null
else
2022-10-28 06:22:03 +01:00
try ( try interpreter . interpret ( field_info . ast . value_expr , container_scope , . { } ) ) . getValue ( ) ;
2022-10-28 04:59:24 +01:00
2022-10-30 08:07:49 +00:00
if ( init_type_value . @ " type " . getTypeInfo ( ) ! = . @ " type " ) {
try interpreter . recordError (
field_info . ast . type_expr ,
2022-11-01 03:36:13 +00:00
" expected_type " ,
2022-10-31 20:00:02 +00:00
try std . fmt . allocPrint ( interpreter . allocator , " expected type 'type', found '{s}' " , . { interpreter . formatTypeInfo ( init_type_value . @ " type " . getTypeInfo ( ) ) } ) ,
2022-10-30 08:07:49 +00:00
) ;
continue ;
}
2022-11-26 17:22:16 +00:00
const name = if ( field_info . ast . tuple_like )
& [ 0 ] u8 { }
else tree . tokenSlice ( field_info . ast . main_token ) ;
2022-10-28 04:59:24 +01:00
const field = FieldDefinition {
. node_idx = member ,
. name = name ,
2022-10-30 08:07:49 +00:00
. @ " type " = init_type_value . value_data . @ " type " ,
2022-10-28 04:59:24 +01:00
. default_value = default_value ,
// TODO: Default values
// .@"type" = T: {
// var value = (try interpreter.interpret(field_info.ast.type_expr, scope_idx, true)).?.value;
// break :T @ptrCast(*Type, @alignCast(@alignOf(*Type), value)).*;
// },
// .value = null,
} ;
2022-10-30 08:07:49 +00:00
try cont_type . getTypeInfoMutable ( ) . @ " struct " . fields . put ( interpreter . allocator , name , field ) ;
2022-11-26 17:22:16 +00:00
field_idx + = 1 ;
2022-10-28 04:59:24 +01:00
} else {
_ = try interpreter . interpret ( member , container_scope , options ) ;
}
}
return InterpretResult { . value = Value {
2022-11-01 03:36:13 +00:00
. interpreter = interpreter ,
2022-10-28 04:59:24 +01:00
. node_idx = node_idx ,
2022-10-31 05:51:51 +00:00
. @ " type " = try interpreter . createType ( node_idx , . { . @ " type " = { } } ) ,
2022-10-31 20:00:02 +00:00
. value_data = try interpreter . createValueData ( . { . @ " type " = cont_type } ) ,
2022-10-28 04:59:24 +01:00
} } ;
} ,
. global_var_decl ,
. local_var_decl ,
. aligned_var_decl ,
. simple_var_decl ,
= > {
2022-10-29 22:28:44 +01:00
// TODO: Add 0 check
const name = analysis . getDeclName ( tree , node_idx ) . ? ;
if ( scope . ? . declarations . contains ( name ) )
2022-10-31 05:51:51 +00:00
return InterpretResult { . nothing = { } } ;
2022-10-29 22:28:44 +01:00
2022-10-28 04:59:24 +01:00
const decl = ast . varDecl ( tree , node_idx ) . ? ;
2022-10-29 22:28:44 +01:00
if ( decl . ast . init_node = = 0 )
2022-10-31 05:51:51 +00:00
return InterpretResult { . nothing = { } } ;
2022-10-29 22:28:44 +01:00
2022-10-28 04:59:24 +01:00
try scope . ? . declarations . put ( interpreter . allocator , name , . {
2022-11-01 03:36:13 +00:00
. scope = scope . ? ,
2022-10-28 04:59:24 +01:00
. node_idx = node_idx ,
. name = name ,
} ) ;
2022-11-11 01:51:02 +00:00
// TODO: Am I a dumbo shrimp? (e.g. is this tree shaking correct? works on my machine so like...)
// if (scope.?.scopeKind() != .container) {
if ( scope . ? . node_idx ! = 0 )
2022-11-01 03:36:13 +00:00
_ = try scope . ? . declarations . getPtr ( name ) . ? . getValue ( ) ;
2022-10-31 05:51:51 +00:00
return InterpretResult { . nothing = { } } ;
2022-10-28 04:59:24 +01:00
} ,
. block ,
. block_semicolon ,
. block_two ,
. block_two_semicolon ,
= > {
// try interpreter.scopes.append(interpreter.allocator, .{
// .node_idx = node_idx,
// .parent_scope = parent_scope_idx orelse std.math.maxInt(usize),
// });
// const scope_idx = interpreter.scopes.items.len - 1;
var block_scope = try interpreter . newScope ( scope , node_idx ) ;
var buffer : [ 2 ] Ast . Node . Index = undefined ;
const statements = ast . blockStatements ( tree , node_idx , & buffer ) . ? ;
for ( statements ) | idx | {
const ret = try interpreter . interpret ( idx , block_scope , options ) ;
switch ( ret ) {
. @ " break " = > | lllll | {
2022-11-01 03:36:13 +00:00
const maybe_block_label_string = if ( scope . ? . getLabel ( ) ) | i | tree . tokenSlice ( i ) else null ;
2022-10-28 04:59:24 +01:00
if ( lllll ) | l | {
if ( maybe_block_label_string ) | ls | {
if ( std . mem . eql ( u8 , l , ls ) ) {
2022-10-31 05:51:51 +00:00
return InterpretResult { . nothing = { } } ;
2022-10-28 04:59:24 +01:00
} else return ret ;
} else return ret ;
} else {
2022-10-31 05:51:51 +00:00
return InterpretResult { . nothing = { } } ;
2022-10-28 04:59:24 +01:00
}
} ,
. break_with_value = > | bwv | {
2022-11-01 03:36:13 +00:00
const maybe_block_label_string = if ( scope . ? . getLabel ( ) ) | i | tree . tokenSlice ( i ) else null ;
2022-10-28 04:59:24 +01:00
if ( bwv . label ) | l | {
if ( maybe_block_label_string ) | ls | {
if ( std . mem . eql ( u8 , l , ls ) ) {
return InterpretResult { . value = bwv . value } ;
} else return ret ;
} else return ret ;
} else {
return InterpretResult { . value = bwv . value } ;
}
} ,
. @ " return " , . return_with_value = > return ret ,
else = > { } ,
}
}
2022-10-31 05:51:51 +00:00
return InterpretResult { . nothing = { } } ;
2022-10-28 04:59:24 +01:00
} ,
. identifier = > {
var value = tree . getNodeSource ( node_idx ) ;
2022-10-28 06:22:03 +01:00
if ( std . mem . eql ( u8 , " bool " , value ) ) return InterpretResult { . value = Value {
2022-11-01 03:36:13 +00:00
. interpreter = interpreter ,
2022-10-28 06:22:03 +01:00
. node_idx = node_idx ,
2022-10-31 05:51:51 +00:00
. @ " type " = try interpreter . createType ( node_idx , . { . @ " type " = { } } ) ,
2022-10-31 20:00:02 +00:00
. value_data = try interpreter . createValueData ( . { . @ " type " = try interpreter . createType ( node_idx , . { . @ " bool " = { } } ) } ) ,
2022-10-28 06:22:03 +01:00
} } ;
if ( std . mem . eql ( u8 , " true " , value ) ) return InterpretResult { . value = Value {
2022-11-01 03:36:13 +00:00
. interpreter = interpreter ,
2022-10-28 06:22:03 +01:00
. node_idx = node_idx ,
2022-10-31 05:51:51 +00:00
. @ " type " = try interpreter . createType ( node_idx , . { . @ " bool " = { } } ) ,
2022-10-31 20:00:02 +00:00
. value_data = try interpreter . createValueData ( . { . @ " bool " = true } ) ,
2022-10-28 06:22:03 +01:00
} } ;
if ( std . mem . eql ( u8 , " false " , value ) ) return InterpretResult { . value = Value {
2022-11-01 03:36:13 +00:00
. interpreter = interpreter ,
2022-10-28 06:22:03 +01:00
. node_idx = node_idx ,
2022-10-31 05:51:51 +00:00
. @ " type " = try interpreter . createType ( node_idx , . { . @ " bool " = { } } ) ,
2022-10-31 20:00:02 +00:00
. value_data = try interpreter . createValueData ( . { . @ " bool " = false } ) ,
2022-10-28 06:22:03 +01:00
} } ;
2022-10-30 08:07:49 +00:00
if ( value . len = = 5 and ( value [ 0 ] = = 'u' or value [ 0 ] = = 'i' ) and std . mem . eql ( u8 , " size " , value [ 1 . . ] ) ) return InterpretResult {
. value = Value {
2022-11-01 03:36:13 +00:00
. interpreter = interpreter ,
2022-10-30 08:07:49 +00:00
. node_idx = node_idx ,
2022-10-31 05:51:51 +00:00
. @ " type " = try interpreter . createType ( node_idx , . { . @ " type " = { } } ) ,
2022-10-31 20:00:02 +00:00
. value_data = try interpreter . createValueData ( . {
2022-10-30 08:07:49 +00:00
. @ " type " = try interpreter . createType ( node_idx , . {
. int = . {
. signedness = if ( value [ 0 ] = = 'u' ) . unsigned else . signed ,
. bits = 64 , // TODO: Platform specific
} ,
} ) ,
2022-10-31 20:00:02 +00:00
} ) ,
2022-10-30 08:07:49 +00:00
} ,
} ;
2022-10-28 04:59:24 +01:00
if ( std . mem . eql ( u8 , " type " , value ) ) {
return InterpretResult { . value = Value {
2022-11-01 03:36:13 +00:00
. interpreter = interpreter ,
2022-10-28 04:59:24 +01:00
. node_idx = node_idx ,
2022-10-31 05:51:51 +00:00
. @ " type " = try interpreter . createType ( node_idx , . { . @ " type " = { } } ) ,
2022-10-31 20:00:02 +00:00
. value_data = try interpreter . createValueData ( . { . @ " type " = try interpreter . createType ( node_idx , . { . @ " type " = { } } ) } ) ,
2022-10-28 04:59:24 +01:00
} } ;
} else if ( value . len > = 2 and ( value [ 0 ] = = 'u' or value [ 0 ] = = 'i' ) ) int : {
return InterpretResult { . value = Value {
2022-11-01 03:36:13 +00:00
. interpreter = interpreter ,
2022-10-28 04:59:24 +01:00
. node_idx = node_idx ,
2022-10-31 05:51:51 +00:00
. @ " type " = try interpreter . createType ( node_idx , . { . @ " type " = { } } ) ,
2022-10-31 20:00:02 +00:00
. value_data = try interpreter . createValueData ( . { . @ " type " = try interpreter . createType ( node_idx , . {
2022-10-28 04:59:24 +01:00
. int = . {
. signedness = if ( value [ 0 ] = = 'u' ) . unsigned else . signed ,
. bits = std . fmt . parseInt ( u16 , value [ 1 . . ] , 10 ) catch break : int ,
} ,
2022-10-31 20:00:02 +00:00
} ) } ) ,
2022-10-28 04:59:24 +01:00
} } ;
}
2022-10-28 06:22:03 +01:00
// TODO: Floats
2022-10-28 04:59:24 +01:00
// Logic to find identifiers in accessible scopes
2022-11-01 03:36:13 +00:00
return InterpretResult { . value = try ( interpreter . huntItDown ( scope . ? , value , options ) catch | err | {
2022-10-30 08:07:49 +00:00
if ( err = = error . IdentifierNotFound ) try interpreter . recordError (
node_idx ,
2022-11-08 20:54:30 +00:00
" undeclared_identifier " ,
try std . fmt . allocPrint ( interpreter . allocator , " use of undeclared identifier '{s}' " , . { value } ) ,
2022-10-30 08:07:49 +00:00
) ;
return err ;
2022-11-01 03:36:13 +00:00
} ) . getValue ( ) } ;
2022-10-28 04:59:24 +01:00
} ,
2022-10-29 06:46:22 +01:00
. field_access = > {
if ( data [ node_idx ] . rhs = = 0 ) return error . CriticalAstFailure ;
const rhs_str = ast . tokenSlice ( tree , data [ node_idx ] . rhs ) catch return error . CriticalAstFailure ;
var ir = try interpreter . interpret ( data [ node_idx ] . lhs , scope , options ) ;
var irv = try ir . getValue ( ) ;
2022-10-29 22:28:44 +01:00
var sub_scope = irv . value_data . @ " type " . getTypeInfo ( ) . getScopeOfType ( ) orelse return error . IdentifierNotFound ;
2022-11-11 01:51:02 +00:00
var scope_sub_decl = sub_scope . interpreter . huntItDown ( sub_scope , rhs_str , options ) catch | err | {
2022-10-30 08:07:49 +00:00
if ( err = = error . IdentifierNotFound ) try interpreter . recordError (
node_idx ,
2022-11-08 20:54:30 +00:00
" undeclared_identifier " ,
try std . fmt . allocPrint ( interpreter . allocator , " use of undeclared identifier '{s}' " , . { rhs_str } ) ,
2022-10-30 08:07:49 +00:00
) ;
return err ;
} ;
2022-10-29 06:46:22 +01:00
return InterpretResult {
2022-11-01 03:36:13 +00:00
. value = try scope_sub_decl . getValue ( ) ,
2022-10-29 06:46:22 +01:00
} ;
} ,
2022-10-28 04:59:24 +01:00
. grouped_expression = > {
return try interpreter . interpret ( data [ node_idx ] . lhs , scope , options ) ;
} ,
. @ " break " = > {
const label = if ( data [ node_idx ] . lhs = = 0 ) null else tree . tokenSlice ( data [ node_idx ] . lhs ) ;
return if ( data [ node_idx ] . rhs = = 0 )
InterpretResult { . @ " break " = label }
else
2022-10-28 06:22:03 +01:00
InterpretResult { . break_with_value = . { . label = label , . value = try ( try interpreter . interpret ( data [ node_idx ] . rhs , scope , options ) ) . getValue ( ) } } ;
2022-10-28 04:59:24 +01:00
} ,
. @ " return " = > {
return if ( data [ node_idx ] . lhs = = 0 )
InterpretResult { . @ " return " = { } }
else
2022-10-28 06:22:03 +01:00
InterpretResult { . return_with_value = try ( try interpreter . interpret ( data [ node_idx ] . lhs , scope , options ) ) . getValue ( ) } ;
2022-10-28 04:59:24 +01:00
} ,
. @ " if " , . if_simple = > {
const iff = ast . ifFull ( tree , node_idx ) ;
// TODO: Don't evaluate runtime ifs
// if (options.observe_values) {
const ir = try interpreter . interpret ( iff . ast . cond_expr , scope , options ) ;
2022-10-28 06:22:03 +01:00
if ( ( try ir . getValue ( ) ) . value_data . @ " bool " ) {
2022-10-28 04:59:24 +01:00
return try interpreter . interpret ( iff . ast . then_expr , scope , options ) ;
} else {
if ( iff . ast . else_expr ! = 0 ) {
return try interpreter . interpret ( iff . ast . else_expr , scope , options ) ;
2022-10-31 05:51:51 +00:00
} else return InterpretResult { . nothing = { } } ;
2022-10-28 04:59:24 +01:00
}
} ,
. equal_equal = > {
var a = try interpreter . interpret ( data [ node_idx ] . lhs , scope , options ) ;
var b = try interpreter . interpret ( data [ node_idx ] . rhs , scope , options ) ;
return InterpretResult { . value = Value {
2022-11-01 03:36:13 +00:00
. interpreter = interpreter ,
2022-10-28 04:59:24 +01:00
. node_idx = node_idx ,
2022-10-31 05:51:51 +00:00
. @ " type " = try interpreter . createType ( node_idx , . { . @ " bool " = { } } ) ,
2022-10-31 20:00:02 +00:00
. value_data = try interpreter . createValueData ( . { . @ " bool " = ( try a . getValue ( ) ) . eql ( try b . getValue ( ) ) } ) ,
2022-10-28 04:59:24 +01:00
} } ;
// a.getValue().eql(b.getValue())
} ,
. number_literal = > {
const s = tree . getNodeSource ( node_idx ) ;
const nl = std . zig . parseNumberLiteral ( s ) ;
2022-10-31 05:51:51 +00:00
return InterpretResult {
. value = Value {
2022-11-01 03:36:13 +00:00
. interpreter = interpreter ,
2022-10-31 05:51:51 +00:00
. node_idx = node_idx ,
. @ " type " = try interpreter . createType ( node_idx , . { . @ " comptime_int " = { } } ) ,
2022-10-31 20:00:02 +00:00
. value_data = try interpreter . createValueData ( switch ( nl ) {
2022-10-31 05:51:51 +00:00
. float = > . { . float = try std . fmt . parseFloat ( f64 , s ) } ,
. int = > if ( s [ 0 ] = = '-' ) ValueData { . signed_int = try std . fmt . parseInt ( i64 , s , 0 ) } else ValueData { . unsigned_int = try std . fmt . parseInt ( u64 , s , 0 ) } ,
. big_int = > | bii | ppp : {
var bi = try std . math . big . int . Managed . init ( interpreter . allocator ) ;
try bi . setString ( @enumToInt ( bii ) , s [ if ( bii ! = . decimal ) @as ( usize , 2 ) else @as ( usize , 0 ) . . ] ) ;
break : ppp . { . big_int = bi } ;
} ,
. failure = > return error . CriticalAstFailure ,
2022-10-31 20:00:02 +00:00
} ) ,
2022-10-28 04:59:24 +01:00
} ,
2022-10-31 05:51:51 +00:00
} ;
2022-10-28 04:59:24 +01:00
} ,
. assign ,
. assign_bit_and ,
. assign_bit_or ,
. assign_shl ,
. assign_shr ,
. assign_bit_xor ,
. assign_div ,
. assign_sub ,
. assign_sub_wrap ,
. assign_mod ,
. assign_add ,
. assign_add_wrap ,
. assign_mul ,
. assign_mul_wrap ,
= > {
// TODO: Actually consider operators
2022-11-08 20:54:30 +00:00
if ( std . mem . eql ( u8 , tree . getNodeSource ( data [ node_idx ] . lhs ) , " _ " ) ) {
_ = try interpreter . interpret ( data [ node_idx ] . rhs , scope . ? , options ) ;
return InterpretResult { . nothing = { } } ;
}
2022-10-28 04:59:24 +01:00
2022-10-31 20:00:02 +00:00
var ir = try interpreter . interpret ( data [ node_idx ] . lhs , scope , options ) ;
var to_value = try ir . getValue ( ) ;
var from_value = ( try ( try interpreter . interpret ( data [ node_idx ] . rhs , scope . ? , options ) ) . getValue ( ) ) ;
to_value . value_data . * = ( try interpreter . cast ( node_idx , to_value . @ " type " , from_value ) ) . value_data . * ;
2022-10-28 04:59:24 +01:00
2022-10-31 05:51:51 +00:00
return InterpretResult { . nothing = { } } ;
2022-10-28 04:59:24 +01:00
} ,
// .@"switch",
// .switch_comma,
// => {
// const cond = data[node_idx].lhs;
// const extra = tree.extraData(data[node_idx].rhs, Ast.Node.SubRange);
// const cases = tree.extra_data[extra.start..extra.end];
// for (cases) |case| {
// const switch_case: Ast.full.SwitchCase = switch (tags[case]) {
// .switch_case => tree.switchCase(case),
// .switch_case_one => tree.switchCaseOne(case),
// else => continue,
// };
// }
// },
. builtin_call ,
. builtin_call_comma ,
. builtin_call_two ,
. builtin_call_two_comma ,
= > {
var buffer : [ 2 ] Ast . Node . Index = undefined ;
const params = ast . builtinCallParams ( tree , node_idx , & buffer ) . ? ;
const call_name = tree . tokenSlice ( main_tokens [ node_idx ] ) ;
if ( std . mem . eql ( u8 , call_name , " @compileLog " ) ) {
2022-10-31 20:00:02 +00:00
var final = std . ArrayList ( u8 ) . init ( interpreter . allocator ) ;
var writer = final . writer ( ) ;
try writer . writeAll ( " log: " ) ;
for ( params ) | param , index | {
var value = ( try interpreter . interpret ( param , scope , options ) ) . maybeGetValue ( ) orelse {
try writer . writeAll ( " indeterminate " ) ;
continue ;
} ;
try writer . print ( " @as({s}, {s}) " , . { interpreter . formatTypeInfo ( value . @ " type " . getTypeInfo ( ) ) , interpreter . formatValue ( value ) } ) ;
if ( index ! = params . len - 1 )
try writer . writeAll ( " , " ) ;
}
2022-12-02 20:14:58 +00:00
try interpreter . recordError ( node_idx , " compile_log " , try final . toOwnedSlice ( ) ) ;
2022-10-31 20:00:02 +00:00
2022-10-31 05:51:51 +00:00
return InterpretResult { . nothing = { } } ;
2022-10-28 04:59:24 +01:00
}
2022-10-28 19:24:38 +01:00
if ( std . mem . eql ( u8 , call_name , " @compileError " ) ) {
2022-10-31 20:00:02 +00:00
// TODO: Add message
try interpreter . recordError ( node_idx , " compile_error " , try std . fmt . allocPrint ( interpreter . allocator , " compile error " , . { } ) ) ;
2022-10-31 05:51:51 +00:00
return InterpretResult { . @ " return " = { } } ;
2022-10-28 19:24:38 +01:00
}
2022-10-29 22:28:44 +01:00
if ( std . mem . eql ( u8 , call_name , " @import " ) ) {
if ( params . len = = 0 ) return error . InvalidBuiltin ;
const import_param = params [ 0 ] ;
if ( tags [ import_param ] ! = . string_literal ) return error . InvalidBuiltin ;
const import_str = tree . tokenSlice ( main_tokens [ import_param ] ) ;
2022-11-11 01:51:02 +00:00
log . info ( " Resolving {s} from {s} " , . { import_str [ 1 . . import_str . len - 1 ] , interpreter . uri } ) ;
// TODO: Implement root support
if ( std . mem . eql ( u8 , import_str [ 1 . . import_str . len - 1 ] , " root " ) ) {
return InterpretResult { . value = Value {
. interpreter = interpreter ,
. node_idx = node_idx ,
. @ " type " = try interpreter . createType ( node_idx , . { . @ " struct " = . { . scope = try interpreter . newScope ( null , 0 ) } } ) ,
. value_data = try interpreter . createValueData ( . { . @ " struct " = . { } } ) ,
} } ;
}
var import_uri = ( try interpreter . document_store . uriFromImportStr ( interpreter . allocator , interpreter . getHandle ( ) . * , import_str [ 1 . . import_str . len - 1 ] ) ) orelse return error . ImportFailure ;
2022-10-29 22:28:44 +01:00
defer interpreter . allocator . free ( import_uri ) ;
var handle = interpreter . document_store . getOrLoadHandle ( import_uri ) orelse return error . ImportFailure ;
try interpreter . document_store . ensureInterpreterExists ( handle . uri ) ;
return InterpretResult { . value = Value {
2022-11-01 03:36:13 +00:00
. interpreter = interpreter ,
2022-10-29 22:28:44 +01:00
. node_idx = node_idx ,
2022-10-31 05:51:51 +00:00
. @ " type " = try interpreter . createType ( node_idx , . { . @ " type " = { } } ) ,
2022-10-31 20:00:02 +00:00
. value_data = try interpreter . createValueData ( . { . @ " type " = handle . interpreter . ? . root_type . ? } ) ,
2022-10-29 22:28:44 +01:00
} } ;
}
2022-10-30 08:07:49 +00:00
if ( std . mem . eql ( u8 , call_name , " @TypeOf " ) ) {
if ( params . len ! = 1 ) return error . InvalidBuiltin ;
const value = try ( try interpreter . interpret ( params [ 0 ] , scope , options ) ) . getValue ( ) ;
return InterpretResult { . value = Value {
2022-11-01 03:36:13 +00:00
. interpreter = interpreter ,
2022-10-30 08:07:49 +00:00
. node_idx = node_idx ,
2022-10-31 05:51:51 +00:00
. @ " type " = try interpreter . createType ( node_idx , . { . @ " type " = { } } ) ,
2022-10-31 20:00:02 +00:00
. value_data = try interpreter . createValueData ( . { . @ " type " = value . @ " type " } ) ,
2022-10-30 08:07:49 +00:00
} } ;
}
if ( std . mem . eql ( u8 , call_name , " @hasDecl " ) ) {
if ( params . len ! = 2 ) return error . InvalidBuiltin ;
const value = try ( try interpreter . interpret ( params [ 0 ] , scope , options ) ) . getValue ( ) ;
const field_name = try ( try interpreter . interpret ( params [ 1 ] , scope , options ) ) . getValue ( ) ;
if ( value . @ " type " . getTypeInfo ( ) ! = . @ " type " ) return error . InvalidBuiltin ;
if ( field_name . @ " type " . getTypeInfo ( ) ! = . @ " pointer " ) return error . InvalidBuiltin ; // Check if it's a []const u8
const ti = value . value_data . @ " type " . getTypeInfo ( ) ;
if ( ti . getScopeOfType ( ) = = null ) return error . InvalidBuiltin ;
return InterpretResult { . value = Value {
2022-11-01 03:36:13 +00:00
. interpreter = interpreter ,
2022-10-30 08:07:49 +00:00
. node_idx = node_idx ,
2022-10-31 05:51:51 +00:00
. @ " type " = try interpreter . createType ( node_idx , . { . @ " bool " = { } } ) ,
2022-10-31 20:00:02 +00:00
. value_data = try interpreter . createValueData ( . { . @ " bool " = ti . getScopeOfType ( ) . ? . declarations . contains ( field_name . value_data . slice_of_const_u8 ) } ) ,
2022-10-30 08:07:49 +00:00
} } ;
}
2022-10-31 05:51:51 +00:00
if ( std . mem . eql ( u8 , call_name , " @as " ) ) {
if ( params . len ! = 2 ) return error . InvalidBuiltin ;
const as_type = try ( try interpreter . interpret ( params [ 0 ] , scope , options ) ) . getValue ( ) ;
const value = try ( try interpreter . interpret ( params [ 1 ] , scope , options ) ) . getValue ( ) ;
if ( as_type . @ " type " . getTypeInfo ( ) ! = . @ " type " ) return error . InvalidBuiltin ;
return InterpretResult { . value = try interpreter . cast ( node_idx , as_type . value_data . @ " type " , value ) } ;
}
2022-11-10 04:46:23 +00:00
log . err ( " Builtin not implemented: {s} " , . { call_name } ) ;
2022-10-30 08:07:49 +00:00
return error . InvalidBuiltin ;
2022-10-28 04:59:24 +01:00
} ,
. string_literal = > {
const value = tree . getNodeSource ( node_idx ) [ 1 . . tree . getNodeSource ( node_idx ) . len - 1 ] ;
var val = Value {
2022-11-01 03:36:13 +00:00
. interpreter = interpreter ,
2022-10-28 04:59:24 +01:00
. node_idx = node_idx ,
2022-10-31 05:51:51 +00:00
// TODO: This is literally the wrong type lmao
// the actual type is *[len:0]u8 because we're pointing
// to a fixed size value in the data(?) section (when we're compilign zig code)
2022-10-28 04:59:24 +01:00
. @ " type " = try interpreter . createType ( node_idx , . {
. pointer = . {
2022-10-31 20:00:02 +00:00
. size = . one ,
2022-10-28 04:59:24 +01:00
. is_const = true ,
. is_volatile = false ,
. child = try interpreter . createType ( 0 , . { . int = . {
. bits = 8 ,
. signedness = . unsigned ,
} } ) ,
. is_allowzero = false ,
2022-10-31 20:00:02 +00:00
. sentinel = null ,
2022-10-28 04:59:24 +01:00
} ,
} ) ,
2022-10-31 20:00:02 +00:00
. value_data = try interpreter . createValueData ( . { . slice_of_const_u8 = value } ) ,
2022-10-28 04:59:24 +01:00
} ;
2022-10-30 08:07:49 +00:00
// TODO: Add type casting, sentinel
// TODO: Should this be a `*const [len:0]u8`?
// try val.value_data.slice_ptr.append(interpreter.allocator, .{ .unsigned_int = 0 });
2022-10-28 04:59:24 +01:00
return InterpretResult { . value = val } ;
} ,
// TODO: Add comptime autodetection; e.g. const MyArrayList = std.ArrayList(u8)
. @ " comptime " = > {
return try interpreter . interpret ( data [ node_idx ] . lhs , scope , . { } ) ;
} ,
// .fn_proto,
// .fn_proto_multi,
// .fn_proto_one,
// .fn_proto_simple,
. fn_decl = > {
// var buf: [1]Ast.Node.Index = undefined;
// const func = ast.fnProto(tree, node_idx, &buf).?;
// TODO: Add params
2022-10-29 06:46:22 +01:00
var type_info = TypeInfo {
. @ " fn " = . {
. return_type = null ,
} ,
} ;
2022-10-28 04:59:24 +01:00
// var it = func.iterate(&tree);
// while (ast.nextFnParam(&it)) |param| {
// // Add parameter decls
// if (param.name_token) |name_token| {
// // TODO: Think of new method for functions
// if ((try interpreter.interpret(param.type_expr, func_scope_idx, .{ .observe_values = true, .is_comptime = true })).maybeGetValue()) |value| {
// try interpreter.addDeclaration(func_scope_idx, value.value_data.@"type");
// try fnd.params.append(interpreter.allocator, interpreter.declarations.items.len - 1);
// } else {
// try interpreter.addDeclaration(parent_scope_idx.?, .{
// .node_idx = node_idx,
// .name = tree.tokenSlice(name_token),
// .scope_idx = func_scope_idx, // orelse std.math.maxInt(usize),
// .@"value" = undefined,
// .@"type" = interpreter.createType(0, .{ .@"anytype" = .{} }),
// });
// try fnd.params.append(interpreter.allocator, interpreter.declarations.items.len - 1);
// }
// }
// }
// if ((try interpreter.interpret(func.ast.return_type, func_scope_idx, .{ .observe_values = true, .is_comptime = true })).maybeGetValue()) |value|
// fnd.return_type = value.value_data.@"type";
2022-10-29 06:46:22 +01:00
var value = Value {
2022-11-01 03:36:13 +00:00
. interpreter = interpreter ,
2022-10-29 06:46:22 +01:00
. node_idx = node_idx ,
. @ " type " = try interpreter . createType ( node_idx , type_info ) ,
2022-10-31 20:00:02 +00:00
. value_data = try interpreter . createValueData ( . { . @ " fn " = { } } ) ,
2022-10-29 06:46:22 +01:00
} ;
2022-10-28 04:59:24 +01:00
2022-10-29 06:46:22 +01:00
const name = analysis . getDeclName ( tree , node_idx ) . ? ;
try scope . ? . declarations . put ( interpreter . allocator , name , . {
2022-11-01 03:36:13 +00:00
. scope = scope . ? ,
2022-10-29 06:46:22 +01:00
. node_idx = node_idx ,
. name = name ,
. @ " value " = value ,
} ) ;
2022-10-28 04:59:24 +01:00
2022-10-31 05:51:51 +00:00
return InterpretResult { . nothing = { } } ;
2022-10-28 04:59:24 +01:00
} ,
. call ,
. call_comma ,
. async_call ,
. async_call_comma ,
. call_one ,
. call_one_comma ,
. async_call_one ,
. async_call_one_comma ,
= > {
2022-10-28 19:24:38 +01:00
var params : [ 1 ] Ast . Node . Index = undefined ;
const call_full = ast . callFull ( tree , node_idx , & params ) orelse unreachable ;
2022-10-28 04:59:24 +01:00
2022-10-28 19:24:38 +01:00
var args = try std . ArrayListUnmanaged ( Value ) . initCapacity ( interpreter . allocator , call_full . ast . params . len ) ;
defer args . deinit ( interpreter . allocator ) ;
2022-10-28 04:59:24 +01:00
2022-10-28 19:24:38 +01:00
for ( call_full . ast . params ) | param | {
try args . append ( interpreter . allocator , try ( try interpreter . interpret ( param , scope , . { } ) ) . getValue ( ) ) ;
}
2022-10-28 04:59:24 +01:00
2022-10-29 22:28:44 +01:00
const func_id_result = try interpreter . interpret ( call_full . ast . fn_expr , interpreter . root_type . ? . getTypeInfo ( ) . getScopeOfType ( ) . ? , . { } ) ;
2022-10-29 06:46:22 +01:00
const func_id_val = try func_id_result . getValue ( ) ;
2022-10-28 04:59:24 +01:00
2022-10-29 22:28:44 +01:00
const call_res = try interpreter . call ( interpreter . root_type . ? . getTypeInfo ( ) . getScopeOfType ( ) . ? , func_id_val . node_idx , args . items , options ) ;
2022-10-28 19:24:38 +01:00
// defer call_res.scope.deinit();
2022-10-29 06:46:22 +01:00
// TODO: Figure out call result memory model; this is actually fine because newScope
// makes this a child of the decl scope which is freed on refresh... in theory
2022-10-28 19:24:38 +01:00
return switch ( call_res . result ) {
. value = > | v | . { . value = v } ,
. nothing = > . { . nothing = { } } ,
} ;
2022-10-28 04:59:24 +01:00
} ,
2022-10-28 06:22:03 +01:00
. bool_not = > {
const result = try interpreter . interpret ( data [ node_idx ] . lhs , scope , . { } ) ;
const value = ( try result . getValue ( ) ) ;
2022-10-31 20:00:02 +00:00
if ( value . value_data . * ! = . @ " bool " ) return error . InvalidOperation ;
2022-10-28 06:22:03 +01:00
return InterpretResult {
. value = . {
2022-11-01 03:36:13 +00:00
. interpreter = interpreter ,
2022-10-28 06:22:03 +01:00
. node_idx = node_idx ,
. @ " type " = value . @ " type " ,
2022-10-31 20:00:02 +00:00
. value_data = try interpreter . createValueData ( . { . @ " bool " = ! value . value_data . @ " bool " } ) ,
} ,
} ;
} ,
. address_of = > {
// TODO: Make const pointers if we're drawing from a const;
// variables are the only non-const(?)
const result = try interpreter . interpret ( data [ node_idx ] . lhs , scope , . { } ) ;
const value = ( try result . getValue ( ) ) ;
return InterpretResult {
. value = . {
2022-11-01 03:36:13 +00:00
. interpreter = interpreter ,
2022-10-31 20:00:02 +00:00
. node_idx = node_idx ,
. @ " type " = try interpreter . createType ( node_idx , . {
. pointer = . {
. size = . one ,
. is_const = false ,
. is_volatile = false ,
. child = value . @ " type " ,
. is_allowzero = false ,
. sentinel = null ,
} ,
} ) ,
. value_data = try interpreter . createValueData ( . { . @ " one_ptr " = value . value_data } ) ,
} ,
} ;
} ,
. deref = > {
const result = try interpreter . interpret ( data [ node_idx ] . lhs , scope , . { } ) ;
const value = ( try result . getValue ( ) ) ;
const ti = value . @ " type " . getTypeInfo ( ) ;
if ( ti ! = . pointer ) {
try interpreter . recordError ( node_idx , " invalid_deref " , try std . fmt . allocPrint ( interpreter . allocator , " cannot deference non-pointer " , . { } ) ) ;
return error . InvalidOperation ;
}
// TODO: Check if this is a one_ptr or not
return InterpretResult {
. value = . {
2022-11-01 03:36:13 +00:00
. interpreter = interpreter ,
2022-10-31 20:00:02 +00:00
. node_idx = node_idx ,
. @ " type " = ti . pointer . child ,
. value_data = value . value_data . one_ptr ,
2022-10-28 06:22:03 +01:00
} ,
} ;
} ,
2022-10-28 04:59:24 +01:00
else = > {
2022-11-10 04:46:23 +00:00
log . err ( " Unhandled {any} " , . { tags [ node_idx ] } ) ;
2022-10-31 05:51:51 +00:00
return InterpretResult { . nothing = { } } ;
2022-10-28 04:59:24 +01:00
} ,
}
}
pub const CallResult = struct {
scope : * InterpreterScope ,
result : union ( enum ) {
value : Value ,
nothing ,
} ,
} ;
pub fn call (
interpreter : * ComptimeInterpreter ,
2022-10-29 06:46:22 +01:00
scope : ? * InterpreterScope ,
2022-10-28 04:59:24 +01:00
func_node_idx : Ast . Node . Index ,
arguments : [ ] const Value ,
options : InterpretOptions ,
) InterpretError ! CallResult {
2022-11-08 20:54:30 +00:00
// _ = options;
2022-10-29 22:28:44 +01:00
// TODO: type check args
2022-10-28 04:59:24 +01:00
2022-11-11 01:51:02 +00:00
const tree = interpreter . getHandle ( ) . tree ;
2022-10-28 04:59:24 +01:00
const tags = tree . nodes . items ( . tag ) ;
2022-11-17 00:28:01 +00:00
if ( tags [ func_node_idx ] ! = . fn_decl ) return error . CriticalAstFailure ;
2022-10-28 04:59:24 +01:00
2022-11-01 03:36:13 +00:00
// TODO: Make argument scope to evaluate arguments in
2022-10-29 06:46:22 +01:00
var fn_scope = try interpreter . newScope ( scope , func_node_idx ) ;
2022-10-28 04:59:24 +01:00
2022-10-28 19:24:38 +01:00
var buf : [ 1 ] Ast . Node . Index = undefined ;
var proto = ast . fnProto ( tree , func_node_idx , & buf ) . ? ;
var arg_it = proto . iterate ( & tree ) ;
var arg_index : usize = 0 ;
while ( ast . nextFnParam ( & arg_it ) ) | param | {
2022-10-29 22:28:44 +01:00
if ( arg_index > = arguments . len ) return error . MissingArguments ;
2022-11-08 20:54:30 +00:00
var tex = try ( try interpreter . interpret ( param . type_expr , fn_scope , options ) ) . getValue ( ) ;
if ( tex . @ " type " . getTypeInfo ( ) ! = . @ " type " ) {
try interpreter . recordError (
param . type_expr ,
" expected_type " ,
std . fmt . allocPrint ( interpreter . allocator , " expected type 'type', found '{s}' " , . { interpreter . formatTypeInfo ( tex . @ " type " . getTypeInfo ( ) ) } ) catch return error . CriticalAstFailure ,
) ;
return error . InvalidCast ;
}
2022-10-28 19:24:38 +01:00
if ( param . name_token ) | nt | {
const decl = Declaration {
2022-11-01 03:36:13 +00:00
. scope = fn_scope ,
2022-10-28 19:24:38 +01:00
. node_idx = param . type_expr ,
. name = tree . tokenSlice ( nt ) ,
2022-11-08 20:54:30 +00:00
. value = try interpreter . cast ( arguments [ arg_index ] . node_idx , tex . value_data . @ " type " , arguments [ arg_index ] ) ,
2022-10-28 19:24:38 +01:00
} ;
try fn_scope . declarations . put ( interpreter . allocator , tree . tokenSlice ( nt ) , decl ) ;
arg_index + = 1 ;
}
}
2022-10-28 04:59:24 +01:00
const body = tree . nodes . items ( . data ) [ func_node_idx ] . rhs ;
const result = try interpreter . interpret ( body , fn_scope , . { } ) ;
// TODO: Defers
return CallResult {
. scope = fn_scope ,
. result = switch ( result ) {
2022-11-08 20:54:30 +00:00
. @ " return " , . nothing = > . { . nothing = { } } , // nothing could be due to an error
2022-10-28 04:59:24 +01:00
. @ " return_with_value " = > | v | . { . value = v } ,
else = > @panic ( " bruh " ) ,
} ,
} ;
}