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: builtin work!!
// TODO: DODify
// TODO: Work with DocumentStore
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 ( ) ;
tree : Ast ,
root_scope : * InterpreterScope = undefined ,
allocator : std . mem . Allocator ,
type_info : std . ArrayListUnmanaged ( TypeInfo ) = . { } ,
type_info_map : std . HashMapUnmanaged ( TypeInfo , usize , TypeInfo . Context , std . hash_map . default_max_load_percentage ) = . { } ,
pub fn deinit ( interpreter : * ComptimeInterpreter ) void {
2022-10-28 06:22:03 +01:00
for ( interpreter . type_info . items ) | * ti | ti . deinit ( interpreter . allocator ) ;
2022-10-28 04:59:24 +01:00
interpreter . type_info . deinit ( interpreter . allocator ) ;
interpreter . type_info_map . deinit ( interpreter . allocator ) ;
}
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 {
return TypeInfo . eql ( self . interpreter , a , b ) ;
}
} ;
pub const Signedness = enum { signed , unsigned } ;
pub const Struct = struct {
/// Declarations contained within
scope : * InterpreterScope ,
fields : std . ArrayListUnmanaged ( FieldDefinition ) = . { } ,
} ;
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 ,
sentinel : ? ValueData ,
pub const Size = enum {
one ,
many ,
slice ,
c ,
} ;
} ;
pub const Fn = struct {
return_type : ? Type ,
/// Index into interpreter.declarations
params : std . ArrayListUnmanaged ( usize ) = . { } ,
} ;
/// Hack to get anytype working; only valid on fnparams
@ " anytype " ,
@ " type " ,
@ " bool " ,
@ " struct " : Struct ,
pointer : Pointer ,
int : Int ,
@ " comptime_int " ,
float : u16 ,
@ " comptime_float " ,
pub fn eql ( interpreter : ComptimeInterpreter , a : TypeInfo , b : TypeInfo ) bool {
if ( std . meta . activeTag ( a ) ! = std . meta . activeTag ( b ) ) return false ;
return switch ( a ) {
. @ " struct " = > false , // Struct declarations can never be equal
. 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 (
interpreter ,
interpreter . typeToTypeInfo ( ap . child ) ,
interpreter . typeToTypeInfo ( bp . child ) ,
) 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 | {
context . hasher . update ( std . mem . sliceAsBytes ( s . fields . items ) ) ;
// 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 ) } ) ;
TypeInfo . hash ( context , context . interpreter . typeToTypeInfo ( p . child ) ) ;
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-28 04:59:24 +01:00
} ;
pub const Type = struct {
node_idx : Ast . Node . Index ,
info_idx : usize ,
} ;
pub const Value = struct {
node_idx : Ast . Node . Index ,
@ " type " : Type ,
value_data : ValueData ,
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 ,
// @"struct": struct {
// },
// one_ptr: *anyopaque,
/// TODO: Optimize this with an ArrayList that uses anyopaque slice
slice_ptr : std . ArrayListUnmanaged ( ValueData ) ,
@ " comptime_int " : std . math . big . int . Managed ,
unsigned_int : u64 ,
signed_int : i64 ,
float : f64 ,
2022-10-28 19:24:38 +01:00
runtime ,
comptime_undetermined ,
2022-10-28 04:59:24 +01:00
pub fn eql ( data : ValueData , other_data : ValueData ) bool {
if ( std . meta . activeTag ( data ) ! = std . meta . activeTag ( other_data ) ) return false ;
// std.enums.
// std.meta.activeTag(u: anytype)
switch ( data ) {
. @ " bool " = > return data . @ " bool " = = other_data . @ " bool " ,
. @ " comptime_int " = > return data . @ " comptime_int " . eq ( other_data . @ " comptime_int " ) ,
. 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
}
}
} ;
pub const FieldDefinition = struct {
node_idx : Ast . Node . Index ,
/// Store name so tree doesn't need to be used to access field name
name : [ ] const u8 ,
@ " type " : Type ,
default_value : ? Value ,
} ;
pub const Declaration = struct {
node_idx : Ast . Node . Index ,
/// Store name so tree doesn't need to be used to access declaration name
name : [ ] const u8 ,
@ " type " : Type ,
value : Value ,
// 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
// }
// }
pub fn isConstant ( declaration : Declaration , tree : Ast ) bool {
return switch ( tree . nodes . items ( . tag ) [ declaration . node_idx ] ) {
. global_var_decl ,
. local_var_decl ,
. aligned_var_decl ,
. simple_var_decl ,
= > {
return tree . tokenSlice ( ast . varDecl ( tree , declaration . node_idx ) . ? . ast . mut_token ) . len = = 3 ;
} ,
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 ) {
// std.log.info("Deduplicating type {d}", .{interpreter.formatTypeInfo(unit.type_info.items[gpr.value_ptr.*])});
return Type { . node_idx = node_idx , . info_idx = gpr . value_ptr . * } ;
} else {
try interpreter . type_info . append ( interpreter . allocator , type_info ) ;
const info_idx = interpreter . type_info . items . len - 1 ;
gpr . value_ptr . * = info_idx ;
return Type { . node_idx = node_idx , . info_idx = info_idx } ;
}
}
pub fn typeToTypeInfo ( interpreter : ComptimeInterpreter , @ " type " : Type ) TypeInfo {
return interpreter . type_info . items [ @ " type " . info_idx ] ;
}
pub const TypeInfoFormatter = struct {
interpreter : * ComptimeInterpreter ,
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-28 06:22:03 +01:00
for ( s . fields . items ) | field | {
try writer . print ( " {s}: {s}, " , . { field . name , value . interpreter . formatTypeInfo ( value . interpreter . typeToTypeInfo ( field . @ " type " ) ) } ) ;
}
2022-10-28 04:59:24 +01:00
var iterator = s . scope . declarations . iterator ( ) ;
while ( iterator . next ( ) ) | di | {
const decl = di . value_ptr . * ;
if ( decl . isConstant ( value . interpreter . tree ) ) {
2022-10-28 06:22:03 +01:00
try writer . print ( " const {s}: {any} = TODO_PRINT_VALUES, " , . { decl . name , value . interpreter . formatTypeInfo ( value . interpreter . typeToTypeInfo ( decl . @ " type " ) ) } ) ;
2022-10-28 04:59:24 +01:00
} else {
2022-10-28 06:22:03 +01:00
try writer . print ( " var {s}: {any}, " , . { decl . name , value . interpreter . formatTypeInfo ( value . interpreter . typeToTypeInfo ( decl . @ " type " ) ) } ) ;
2022-10-28 04:59:24 +01:00
}
}
try writer . writeAll ( " } " ) ;
} ,
else = > try writer . print ( " UnimplementedTypeInfoPrint " , . { } ) ,
} ;
}
} ;
pub fn formatTypeInfo ( interpreter : * ComptimeInterpreter , ti : TypeInfo ) TypeInfoFormatter {
return TypeInfoFormatter { . interpreter = interpreter , . ti = ti } ;
}
pub const InterpreterScope = struct {
interpreter : * ComptimeInterpreter ,
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 } ;
pub fn scopeKind ( scope : InterpreterScope , tree : Ast ) ScopeKind {
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 ,
} ;
}
pub fn getLabel ( scope : InterpreterScope , tree : Ast ) ? Ast . TokenIndex {
const token_tags = tree . tokens . items ( . tag ) ;
return switch ( scope . scopeKind ( tree ) ) {
. 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 {
scope . declarations . deinit ( scope . interpreter . allocator ) ;
for ( scope . child_scopes . items ) | child | child . deinit ( ) ;
scope . child_scopes . deinit ( scope . interpreter . allocator ) ;
scope . interpreter . allocator . destroy ( scope ) ;
}
} ;
pub fn newScope ( interpreter : * ComptimeInterpreter , maybe_parent : ? * InterpreterScope , node_idx : Ast . Node . Index ) std . mem . Allocator . Error ! * InterpreterScope {
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
}
} ;
// 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-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 {
// _ = unit;
// _ = node;
// _ = observe_values;
const tree = interpreter . tree ;
const tags = tree . nodes . items ( . tag ) ;
const data = tree . nodes . items ( . data ) ;
const main_tokens = tree . nodes . items ( . main_token ) ;
std . log . info ( " {any} " , . { tags [ node_idx ] } ) ;
switch ( tags [ 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 ,
= > {
var container_scope = try interpreter . newScope ( scope , node_idx ) ;
var type_info = TypeInfo {
. @ " struct " = . {
. scope = container_scope ,
} ,
} ;
if ( node_idx = = 0 ) interpreter . root_scope = container_scope ;
var buffer : [ 2 ] Ast . Node . Index = undefined ;
const members = ast . declMembers ( tree , node_idx , & buffer ) ;
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 | {
var init_type = try interpreter . interpret ( field_info . ast . type_expr , container_scope , . { } ) ;
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
const name = tree . tokenSlice ( field_info . ast . name_token ) ;
const field = FieldDefinition {
. node_idx = member ,
. name = name ,
2022-10-28 06:22:03 +01:00
. @ " type " = ( try init_type . getValue ( ) ) . 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,
} ;
try type_info . @ " struct " . fields . append ( interpreter . allocator , field ) ;
} else {
_ = try interpreter . interpret ( member , container_scope , options ) ;
}
}
return InterpretResult { . value = Value {
. node_idx = node_idx ,
. @ " type " = try interpreter . createType ( node_idx , . { . @ " type " = . { } } ) ,
. value_data = . { . @ " type " = try interpreter . createType ( node_idx , type_info ) } ,
} } ;
} ,
. global_var_decl ,
. local_var_decl ,
. aligned_var_decl ,
. simple_var_decl ,
= > {
const decl = ast . varDecl ( tree , node_idx ) . ? ;
2022-10-28 06:22:03 +01:00
var value = try ( try interpreter . interpret ( decl . ast . init_node , scope , options ) ) . getValue ( ) ;
2022-10-28 04:59:24 +01:00
var @ " type " = if ( decl . ast . type_node = = 0 ) Value {
. node_idx = std . math . maxInt ( Ast . Node . Index ) ,
. @ " type " = try interpreter . createType ( node_idx , . { . @ " type " = . { } } ) ,
. value_data = . { . @ " type " = value . @ " type " } ,
2022-10-28 06:22:03 +01:00
} else try ( try interpreter . interpret ( decl . ast . type_node , scope , options ) ) . getValue ( ) ;
2022-10-28 04:59:24 +01:00
const name = analysis . getDeclName ( tree , node_idx ) . ? ;
try scope . ? . declarations . put ( interpreter . allocator , name , . {
. node_idx = node_idx ,
. name = name ,
. @ " type " = @ " type " . value_data . @ " type " ,
. @ " value " = value ,
} ) ;
return InterpretResult { . nothing = . { } } ;
} ,
. 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 | {
const maybe_block_label_string = if ( scope . ? . getLabel ( tree ) ) | i | tree . tokenSlice ( i ) else null ;
if ( lllll ) | l | {
if ( maybe_block_label_string ) | ls | {
if ( std . mem . eql ( u8 , l , ls ) ) {
return InterpretResult { . nothing = . { } } ;
} else return ret ;
} else return ret ;
} else {
return InterpretResult { . nothing = . { } } ;
}
} ,
. break_with_value = > | bwv | {
const maybe_block_label_string = if ( scope . ? . getLabel ( tree ) ) | i | tree . tokenSlice ( i ) else null ;
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 = > { } ,
}
}
return InterpretResult { . nothing = . { } } ;
} ,
. 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 {
. node_idx = node_idx ,
. @ " type " = try interpreter . createType ( node_idx , . { . @ " type " = . { } } ) ,
. value_data = . { . @ " type " = try interpreter . createType ( node_idx , . { . @ " bool " = . { } } ) } ,
} } ;
if ( std . mem . eql ( u8 , " true " , value ) ) return InterpretResult { . value = Value {
. node_idx = node_idx ,
. @ " type " = try interpreter . createType ( node_idx , . { . @ " bool " = . { } } ) ,
. value_data = . { . @ " bool " = true } ,
} } ;
if ( std . mem . eql ( u8 , " false " , value ) ) return InterpretResult { . value = Value {
. node_idx = node_idx ,
. @ " type " = try interpreter . createType ( node_idx , . { . @ " bool " = . { } } ) ,
. value_data = . { . @ " bool " = false } ,
} } ;
2022-10-28 04:59:24 +01:00
if ( std . mem . eql ( u8 , " type " , value ) ) {
return InterpretResult { . value = Value {
. node_idx = node_idx ,
. @ " type " = try interpreter . createType ( node_idx , . { . @ " type " = . { } } ) ,
. value_data = . { . @ " type " = try interpreter . createType ( node_idx , . { . @ " type " = . { } } ) } ,
} } ;
} else if ( value . len > = 2 and ( value [ 0 ] = = 'u' or value [ 0 ] = = 'i' ) ) int : {
return InterpretResult { . value = Value {
. node_idx = node_idx ,
. @ " type " = try interpreter . createType ( node_idx , . { . @ " type " = . { } } ) ,
. value_data = . { . @ " type " = try interpreter . createType ( node_idx , . {
. int = . {
. signedness = if ( value [ 0 ] = = 'u' ) . unsigned else . signed ,
. bits = std . fmt . parseInt ( u16 , value [ 1 . . ] , 10 ) catch break : int ,
} ,
} ) } ,
} } ;
}
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
var psi = scope . ? . parentScopeIterator ( ) ;
while ( psi . next ( ) ) | pscope | {
return InterpretResult { . value = ( pscope . declarations . get ( value ) orelse continue ) . value } ;
}
std . log . err ( " Identifier not found: {s} " , . { value } ) ;
2022-10-28 19:24:38 +01:00
return error . IdentifierNotFound ;
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 ) ;
} else return InterpretResult { . nothing = . { } } ;
}
} ,
. 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 {
. node_idx = node_idx ,
. @ " type " = try interpreter . createType ( node_idx , . { . @ " bool " = . { } } ) ,
2022-10-28 06:22:03 +01:00
. value_data = . { . @ " 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 ) ;
// if (nl == .failure) ;
return InterpretResult { . value = Value {
. node_idx = node_idx ,
. @ " type " = try interpreter . createType ( node_idx , . { . @ " comptime_int " = . { } } ) ,
. value_data = switch ( nl ) {
. 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 . { . @ " comptime_int " = bi } ;
} ,
2022-10-28 06:22:03 +01:00
. failure = > return error . CriticalAstFailure ,
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: Make this work with non identifiers
// TODO: Actually consider operators
const value = tree . getNodeSource ( data [ node_idx ] . lhs ) ;
var psi = scope . ? . parentScopeIterator ( ) ;
while ( psi . next ( ) ) | pscope | {
if ( pscope . declarations . getEntry ( value ) ) | decl |
2022-10-28 06:22:03 +01:00
decl . value_ptr . value = try ( try interpreter . interpret ( data [ node_idx ] . rhs , scope . ? , options ) ) . getValue ( ) ;
2022-10-28 04:59:24 +01:00
}
return InterpretResult { . nothing = . { } } ;
} ,
// .@"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 ) . ? ;
2022-10-28 19:24:38 +01:00
_ = params ;
2022-10-28 04:59:24 +01:00
const call_name = tree . tokenSlice ( main_tokens [ node_idx ] ) ;
if ( std . mem . eql ( u8 , call_name , " @compileLog " ) ) {
return InterpretResult { . nothing = . { } } ;
}
2022-10-28 19:24:38 +01:00
if ( std . mem . eql ( u8 , call_name , " @compileError " ) ) {
return InterpretResult { . @ " return " = . { } } ;
}
2022-10-28 04:59:24 +01:00
std . log . info ( " Builtin not implemented: {s} " , . { call_name } ) ;
@panic ( " Builtin not implemented " ) ;
2022-10-28 06:22:03 +01: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 {
. node_idx = node_idx ,
. @ " type " = try interpreter . createType ( node_idx , . {
. pointer = . {
. size = . slice ,
. is_const = true ,
. is_volatile = false ,
. child = try interpreter . createType ( 0 , . { . int = . {
. bits = 8 ,
. signedness = . unsigned ,
} } ) ,
. is_allowzero = false ,
. sentinel = . { . unsigned_int = 0 } ,
} ,
} ) ,
. value_data = . { . slice_ptr = . { } } ,
} ;
for ( value ) | z | {
try val . value_data . slice_ptr . append ( interpreter . allocator , . { . unsigned_int = z } ) ;
}
try val . value_data . slice_ptr . append ( interpreter . allocator , . { . unsigned_int = 0 } ) ;
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
// var type_info = TypeInfo{
// .@"fn" = .{
// .definition_scope = scope.?,
// .node_idx = node_idx,
// },
// };
// 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";
// var value = Value{
// .node_idx = node_idx,
// .@"type" = try interpreter.createType(node_idx, type_info),
// .value_data = .{ .@"fn" = .{} },
// };
2022-10-28 19:24:38 +01:00
// const name = analysis.getDeclName(tree, node_idx).?;
// // TODO: DANGER DANGER DANGER
2022-10-28 04:59:24 +01:00
// try scope.?.declarations.put(interpreter.allocator, name, .{
// .node_idx = node_idx,
// .name = name,
2022-10-28 19:24:38 +01:00
// .@"type" = undefined,
// .@"value" = undefined,
2022-10-28 04:59:24 +01:00
// });
return InterpretResult { . nothing = . { } } ;
} ,
. 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-28 19:24:38 +01:00
// TODO: Make this actually resolve function; requires interpreting whole file
// const res = try interpreter.interpret(call_full.ast.fn_expr, scope, .{});
// const value = try res.getValue();
2022-10-28 04:59:24 +01:00
2022-10-28 19:24:38 +01:00
const call_res = try interpreter . call ( tree . rootDecls ( ) [ 0 ] , args . items , options ) ;
// defer call_res.scope.deinit();
// TODO: Figure out call result memory model
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 ( ) ) ;
if ( value . value_data ! = . @ " bool " ) return error . InvalidOperation ;
return InterpretResult {
. value = . {
. node_idx = node_idx ,
. @ " type " = value . @ " type " ,
. value_data = . { . @ " bool " = ! value . value_data . @ " bool " } ,
} ,
} ;
} ,
2022-10-28 04:59:24 +01:00
else = > {
std . log . err ( " Unhandled {any} " , . { tags [ node_idx ] } ) ;
return InterpretResult { . nothing = . { } } ;
} ,
}
}
pub const CallResult = struct {
scope : * InterpreterScope ,
result : union ( enum ) {
value : Value ,
nothing ,
} ,
} ;
pub fn call (
interpreter : * ComptimeInterpreter ,
func_node_idx : Ast . Node . Index ,
arguments : [ ] const Value ,
options : InterpretOptions ,
) InterpretError ! CallResult {
// TODO: Eval, check parameter types
// TODO: Arguments
_ = options ;
2022-10-28 19:24:38 +01:00
// _ = arguments;
2022-10-28 04:59:24 +01:00
const tree = interpreter . tree ;
const tags = tree . nodes . items ( . tag ) ;
std . debug . assert ( tags [ func_node_idx ] = = . fn_decl ) ;
var fn_scope = try interpreter . newScope ( null , func_node_idx ) ;
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 | {
if ( param . name_token ) | nt | {
const decl = Declaration {
. node_idx = param . type_expr ,
. name = tree . tokenSlice ( nt ) ,
. @ " type " = arguments [ arg_index ] . @ " type " ,
. value = arguments [ arg_index ] ,
} ;
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 ) {
. @ " return " = > . { . nothing = { } } ,
. @ " return_with_value " = > | v | . { . value = v } ,
else = > @panic ( " bruh " ) ,
} ,
} ;
}