2024-03-18 09:47:07 -07:00
// Compilation logic
2024-02-04 12:23:59 -08:00
# include <ctype.h>
# include <gc.h>
2024-08-12 22:30:25 -07:00
# include <gc/cord.h>
# include <gmp.h>
2024-02-04 12:23:59 -08:00
# include <stdio.h>
2024-03-03 16:12:53 -08:00
# include <uninorm.h>
2024-02-04 12:23:59 -08:00
# include "ast.h"
2024-08-12 22:30:25 -07:00
# include "builtins/integers.h"
2024-03-03 15:15:45 -08:00
# include "builtins/text.h"
2024-02-04 15:04:41 -08:00
# include "compile.h"
2024-02-24 12:24:44 -08:00
# include "enums.h"
2024-02-24 10:27:49 -08:00
# include "structs.h"
2024-02-17 13:56:19 -08:00
# include "environment.h"
2024-02-17 14:00:21 -08:00
# include "typecheck.h"
2024-03-09 21:03:21 -08:00
# include "builtins/util.h"
2024-02-04 12:23:59 -08:00
2024-03-22 10:53:23 -07:00
static CORD compile_to_pointer_depth ( env_t * env , ast_t * ast , int64_t target_depth , bool allow_optional ) ;
2024-05-15 10:40:27 -07:00
static env_t * with_enum_scope ( env_t * env , type_t * t ) ;
2024-08-10 17:50:15 -07:00
static CORD compile_math_method ( env_t * env , binop_e op , ast_t * lhs , ast_t * rhs , type_t * required_type ) ;
2024-07-04 13:23:05 -07:00
static CORD compile_string ( env_t * env , ast_t * ast , CORD color ) ;
2024-07-13 14:17:58 -07:00
static CORD compile_arguments ( env_t * env , ast_t * call_ast , arg_t * spec_args , arg_ast_t * call_args ) ;
2024-08-12 14:44:05 -07:00
static CORD compile_maybe_incref ( env_t * env , ast_t * ast ) ;
2024-03-22 10:53:23 -07:00
2024-03-09 13:03:38 -08:00
static bool promote ( env_t * env , CORD * code , type_t * actual , type_t * needed )
{
if ( type_eq ( actual , needed ) )
return true ;
if ( ! can_promote ( actual , needed ) )
return false ;
2024-08-12 23:45:57 -07:00
if ( actual - > tag = = IntType & & needed - > tag = = IntType & & Match ( needed , IntType ) - > bits = = 0 ) {
* code = CORD_all ( " I( " , * code , " ) " ) ;
return true ;
}
2024-08-14 11:57:01 -07:00
if ( actual - > tag = = IntType & & needed - > tag = = NumType ) {
* code = CORD_all ( type_to_cord ( actual ) , " _to_ " , type_to_cord ( needed ) , " ( " , * code , " ) " ) ;
2024-08-12 23:51:10 -07:00
return true ;
}
2024-08-17 12:14:45 -07:00
if ( actual - > tag = = NumType & & needed - > tag = = IntType )
return false ;
2024-03-09 13:03:38 -08:00
if ( actual - > tag = = IntType | | actual - > tag = = NumType )
return true ;
// Automatic dereferencing:
if ( actual - > tag = = PointerType & & ! Match ( actual , PointerType ) - > is_optional
& & can_promote ( Match ( actual , PointerType ) - > pointed , needed ) ) {
* code = CORD_all ( " *( " , * code , " ) " ) ;
return promote ( env , code , Match ( actual , PointerType ) - > pointed , needed ) ;
}
2024-05-12 16:21:44 -07:00
// Optional and stack ref promotion:
2024-03-09 13:03:38 -08:00
if ( actual - > tag = = PointerType & & needed - > tag = = PointerType )
return true ;
2024-05-12 16:21:44 -07:00
if ( needed - > tag = = ClosureType & & actual - > tag = = FunctionType ) {
2024-08-15 10:59:01 -07:00
* code = CORD_all ( " ((closure_t){ " , * code , " , NULL}) " ) ;
2024-03-09 13:03:38 -08:00
return true ;
}
2024-05-12 16:21:44 -07:00
if ( needed - > tag = = ClosureType & & actual - > tag = = ClosureType )
return true ;
2024-05-12 13:09:24 -07:00
if ( needed - > tag = = FunctionType & & actual - > tag = = FunctionType ) {
2024-06-16 15:09:54 -07:00
* code = CORD_all ( " ( " , compile_type ( needed ) , " ) " , * code ) ;
2024-05-12 13:09:24 -07:00
return true ;
}
2024-08-10 12:15:38 -07:00
// Set -> Array promotion:
if ( needed - > tag = = ArrayType & & actual - > tag = = SetType
& & type_eq ( Match ( needed , ArrayType ) - > item_type , Match ( actual , SetType ) - > item_type ) ) {
* code = CORD_all ( " ( " , * code , " ).entries " ) ;
return true ;
}
2024-03-09 13:03:38 -08:00
return false ;
}
2024-08-12 14:44:05 -07:00
CORD compile_maybe_incref ( env_t * env , ast_t * ast )
{
type_t * t = get_type ( env , ast ) ;
if ( is_idempotent ( ast ) & & can_be_mutated ( env , ast ) ) {
if ( t - > tag = = ArrayType )
return CORD_all ( " ARRAY_COPY( " , compile ( env , ast ) , " ) " ) ;
else if ( t - > tag = = TableType | | t - > tag = = SetType )
return CORD_all ( " TABLE_COPY( " , compile ( env , ast ) , " ) " ) ;
}
return compile ( env , ast ) ;
}
2024-06-06 13:28:53 -07:00
static table_t * get_closed_vars ( env_t * env , ast_t * lambda_ast )
{
auto lambda = Match ( lambda_ast , Lambda ) ;
env_t * body_scope = fresh_scope ( env ) ;
2024-07-14 11:13:23 -07:00
body_scope - > code = new ( compilation_unit_t ) ; // Don't put any code in the headers or anything
2024-06-06 13:28:53 -07:00
for ( arg_ast_t * arg = lambda - > args ; arg ; arg = arg - > next ) {
type_t * arg_type = get_arg_ast_type ( env , arg ) ;
set_binding ( body_scope , arg - > name , new ( binding_t , . type = arg_type , . code = CORD_cat ( " $ " , arg - > name ) ) ) ;
}
fn_ctx_t fn_ctx = ( fn_ctx_t ) {
2024-07-14 11:13:23 -07:00
. parent = env - > fn_ctx ,
. closure_scope = env - > locals ,
2024-06-06 13:28:53 -07:00
. closed_vars = new ( table_t ) ,
} ;
body_scope - > fn_ctx = & fn_ctx ;
body_scope - > locals - > fallback = env - > globals ;
2024-07-13 15:43:50 -07:00
type_t * ret_t = get_type ( body_scope , lambda - > body ) ;
if ( ret_t - > tag = = ReturnType )
ret_t = Match ( ret_t , ReturnType ) - > ret ;
2024-06-06 13:28:53 -07:00
fn_ctx . return_type = ret_t ;
// Find which variables are captured in the closure:
env_t * tmp_scope = fresh_scope ( body_scope ) ;
for ( ast_list_t * stmt = Match ( lambda - > body , Block ) - > statements ; stmt ; stmt = stmt - > next ) {
2024-07-14 10:22:42 -07:00
bind_statement ( tmp_scope , stmt - > ast ) ;
2024-06-06 13:28:53 -07:00
type_t * stmt_type = get_type ( tmp_scope , stmt - > ast ) ;
2024-07-14 10:22:42 -07:00
if ( stmt - > next | | ( stmt_type - > tag = = VoidType | | stmt_type - > tag = = AbortType | | get_type ( tmp_scope , stmt - > ast ) - > tag = = ReturnType ) )
2024-06-06 13:28:53 -07:00
( void ) compile_statement ( tmp_scope , stmt - > ast ) ;
else
( void ) compile ( tmp_scope , stmt - > ast ) ;
}
return fn_ctx . closed_vars ;
}
2024-06-16 15:09:54 -07:00
CORD compile_declaration ( type_t * t , CORD name )
2024-03-09 11:02:19 -08:00
{
if ( t - > tag = = FunctionType ) {
auto fn = Match ( t , FunctionType ) ;
2024-06-16 15:09:54 -07:00
CORD code = CORD_all ( compile_type ( fn - > ret ) , " (* " , name , " )( " ) ;
2024-03-09 11:02:19 -08:00
for ( arg_t * arg = fn - > args ; arg ; arg = arg - > next ) {
2024-06-16 15:09:54 -07:00
code = CORD_all ( code , compile_type ( arg - > type ) ) ;
2024-03-09 11:02:19 -08:00
if ( arg - > next ) code = CORD_cat ( code , " , " ) ;
}
return CORD_all ( code , " ) " ) ;
2024-06-06 13:28:53 -07:00
} else if ( t - > tag ! = ModuleType ) {
2024-06-16 15:09:54 -07:00
return CORD_all ( compile_type ( t ) , " " , name ) ;
2024-06-06 13:28:53 -07:00
} else {
return CORD_EMPTY ;
2024-03-09 11:02:19 -08:00
}
}
2024-06-16 15:09:54 -07:00
CORD compile_type ( type_t * t )
2024-02-17 23:34:39 -08:00
{
switch ( t - > tag ) {
2024-07-13 15:43:50 -07:00
case ReturnType : errx ( 1 , " Shouldn't be compiling ReturnType to a type " ) ;
2024-02-17 23:34:39 -08:00
case AbortType : return " void " ;
case VoidType : return " void " ;
case MemoryType : return " void " ;
case BoolType : return " Bool_t " ;
2024-05-18 11:38:41 -07:00
case CStringType : return " char* " ;
2024-08-12 22:30:25 -07:00
case IntType : return Match ( t , IntType ) - > bits = = 0 ? " Int_t " : CORD_asprintf ( " Int%ld_t " , Match ( t , IntType ) - > bits ) ;
2024-03-03 10:37:05 -08:00
case NumType : return Match ( t , NumType ) - > bits = = 64 ? " Num_t " : CORD_asprintf ( " Num%ld_t " , Match ( t , NumType ) - > bits ) ;
2024-03-03 15:15:45 -08:00
case TextType : {
2024-03-21 22:52:00 -07:00
auto text = Match ( t , TextType ) ;
2024-06-16 15:09:54 -07:00
return text - > lang ? CORD_all ( namespace_prefix ( text - > env - > libname , text - > env - > namespace - > parent ) , text - > lang , " _t " ) : " Text_t " ;
2024-02-17 23:34:39 -08:00
}
case ArrayType : return " array_t " ;
2024-08-10 12:15:38 -07:00
case SetType : return " table_t " ;
2024-08-11 11:47:34 -07:00
case ChannelType : return " channel_t* " ;
2024-02-17 23:34:39 -08:00
case TableType : return " table_t " ;
2024-03-09 11:02:19 -08:00
case FunctionType : {
auto fn = Match ( t , FunctionType ) ;
2024-06-16 15:09:54 -07:00
CORD code = CORD_all ( compile_type ( fn - > ret ) , " (*)( " ) ;
2024-03-09 11:02:19 -08:00
for ( arg_t * arg = fn - > args ; arg ; arg = arg - > next ) {
2024-06-16 15:09:54 -07:00
code = CORD_all ( code , compile_type ( arg - > type ) ) ;
2024-03-09 11:02:19 -08:00
if ( arg - > next ) code = CORD_cat ( code , " , " ) ;
}
return CORD_all ( code , " ) " ) ;
}
case ClosureType : return " closure_t " ;
2024-06-16 15:09:54 -07:00
case PointerType : return CORD_cat ( compile_type ( Match ( t , PointerType ) - > pointed ) , " * " ) ;
2024-03-21 22:52:00 -07:00
case StructType : {
auto s = Match ( t , StructType ) ;
2024-08-11 11:47:34 -07:00
if ( t = = THREAD_TYPE )
return " pthread_t* " ;
2024-06-16 15:09:54 -07:00
return CORD_all ( " struct " , namespace_prefix ( s - > env - > libname , s - > env - > namespace - > parent ) , s - > name , " _s " ) ;
2024-03-21 22:52:00 -07:00
}
case EnumType : {
auto e = Match ( t , EnumType ) ;
2024-06-16 15:09:54 -07:00
return CORD_all ( namespace_prefix ( e - > env - > libname , e - > env - > namespace - > parent ) , e - > name , " _t " ) ;
2024-03-21 22:52:00 -07:00
}
2024-02-17 23:34:39 -08:00
case TypeInfoType : return " TypeInfo " ;
2024-06-06 13:28:53 -07:00
default : compiler_err ( NULL , NULL , NULL , " Compiling type is not implemented for type with tag %d " , t - > tag ) ;
2024-02-17 23:34:39 -08:00
}
}
2024-03-24 11:28:20 -07:00
static CORD compile_lvalue ( env_t * env , ast_t * ast )
2024-03-14 10:28:30 -07:00
{
if ( ! can_be_mutated ( env , ast ) ) {
if ( ast - > tag = = Index | | ast - > tag = = FieldAccess ) {
ast_t * subject = ast - > tag = = Index ? Match ( ast , Index ) - > indexed : Match ( ast , FieldAccess ) - > fielded ;
2024-03-22 10:53:23 -07:00
code_err ( subject , " This is an immutable value, you can't assign to it " ) ;
2024-03-14 10:28:30 -07:00
} else {
code_err ( ast , " This is a value of type %T and can't be assigned to " , get_type ( env , ast ) ) ;
}
}
2024-03-24 11:28:20 -07:00
if ( ast - > tag = = Index ) {
auto index = Match ( ast , Index ) ;
2024-03-22 10:53:23 -07:00
type_t * container_t = get_type ( env , index - > indexed ) ;
2024-03-24 11:28:20 -07:00
if ( ! index - > index & & container_t - > tag = = PointerType ) {
if ( Match ( container_t , PointerType ) - > is_optional )
code_err ( index - > indexed , " This pointer might be null, so it can't be safely assigned to " ) ;
return compile ( env , ast ) ;
}
2024-03-22 10:53:23 -07:00
container_t = value_type ( container_t ) ;
2024-03-24 11:28:20 -07:00
if ( container_t - > tag = = ArrayType ) {
2024-03-22 10:53:23 -07:00
CORD target_code = compile_to_pointer_depth ( env , index - > indexed , 1 , false ) ;
type_t * item_type = Match ( container_t , ArrayType ) - > item_type ;
2024-06-16 15:09:54 -07:00
return CORD_all ( " Array_lvalue( " , compile_type ( item_type ) , " , " , target_code , " , " ,
2024-08-03 12:06:59 -07:00
compile ( env , index - > index ) , " , " , CORD_asprintf ( " %ld " , padded_type_size ( item_type ) ) ,
2024-03-29 09:54:31 -07:00
" , " , Text $ quoted ( ast - > file - > filename , false ) , " , " , heap_strf ( " %ld " , ast - > start - ast - > file - > text ) ,
2024-03-24 11:28:20 -07:00
" , " , heap_strf ( " %ld " , ast - > end - ast - > file - > text ) , " ) " ) ;
} else {
code_err ( ast , " I don't know how to assign to this target " ) ;
2024-03-22 10:53:23 -07:00
}
2024-03-24 11:28:20 -07:00
} else if ( ast - > tag = = Var | | ast - > tag = = FieldAccess ) {
return compile ( env , ast ) ;
} else {
code_err ( ast , " I don't know how to assign to this " ) ;
2024-03-22 10:53:23 -07:00
}
2024-03-24 11:28:20 -07:00
}
static CORD compile_assignment ( env_t * env , ast_t * target , CORD value )
{
return CORD_all ( compile_lvalue ( env , target ) , " = " , value , " ; \ n " );
2024-03-22 10:53:23 -07:00
}
2024-02-13 11:42:33 -08:00
CORD compile_statement ( env_t * env , ast_t * ast )
2024-02-04 15:04:41 -08:00
{
switch ( ast - > tag ) {
2024-03-14 10:28:30 -07:00
case When : {
2024-05-23 21:03:46 -07:00
// Typecheck to verify exhaustiveness:
type_t * result_t = get_type ( env , ast ) ;
( void ) result_t ;
2024-03-14 10:28:30 -07:00
auto when = Match ( ast , When ) ;
type_t * subject_t = get_type ( env , when - > subject ) ;
2024-05-23 21:03:46 -07:00
if ( subject_t - > tag = = PointerType ) {
ast_t * var = when - > clauses - > args - > ast ;
CORD var_code = compile ( env , var ) ;
env_t * non_null_scope = fresh_scope ( env ) ;
auto ptr = Match ( subject_t , PointerType ) ;
type_t * non_optional_t = Type ( PointerType , . pointed = ptr - > pointed , . is_stack = ptr - > is_stack ,
. is_readonly = ptr - > is_readonly , . is_optional = false ) ;
set_binding ( non_null_scope , Match ( var , Var ) - > name , new ( binding_t , . type = non_optional_t , . code = var_code ) ) ;
return CORD_all (
" { \n " ,
2024-06-16 15:09:54 -07:00
compile_declaration ( subject_t , var_code ) , " = " , compile ( env , when - > subject ) , " ; \n "
2024-05-23 21:03:46 -07:00
" if ( " , var_code , " ) \n " , compile_statement ( non_null_scope , when - > clauses - > body ) ,
" \n else \n " , compile_statement ( env , when - > else_body ) , " \n } " ) ;
}
2024-03-14 10:28:30 -07:00
auto enum_t = Match ( subject_t , EnumType ) ;
2024-06-16 15:09:54 -07:00
CORD code = CORD_all ( " { " , compile_type ( subject_t ) , " subject = " , compile ( env , when - > subject ) , " ; \n "
2024-04-16 10:50:07 -07:00
" switch (subject.$tag) { " ) ;
2024-03-14 10:28:30 -07:00
for ( when_clause_t * clause = when - > clauses ; clause ; clause = clause - > next ) {
const char * clause_tag_name = Match ( clause - > tag_name , Var ) - > name ;
2024-06-17 14:23:39 -07:00
code = CORD_all ( code , " case " , namespace_prefix ( enum_t - > env - > libname , enum_t - > env - > namespace ) , " tag$ " , clause_tag_name , " : { \n " ) ;
2024-03-14 10:28:30 -07:00
type_t * tag_type = NULL ;
for ( tag_t * tag = enum_t - > tags ; tag ; tag = tag - > next ) {
if ( streq ( tag - > name , clause_tag_name ) ) {
tag_type = tag - > type ;
break ;
}
}
assert ( tag_type ) ;
env_t * scope = env ;
2024-05-01 10:53:51 -07:00
auto tag_struct = Match ( tag_type , StructType ) ;
if ( clause - > args & & ! clause - > args - > next & & tag_struct - > fields & & tag_struct - > fields - > next ) {
2024-08-03 13:39:04 -07:00
code = CORD_all ( code , compile_type ( tag_type ) , " " , compile ( env , clause - > args - > ast ) , " = subject.$ " , clause_tag_name , " ; \n " ) ;
2024-05-01 10:53:51 -07:00
scope = fresh_scope ( scope ) ;
set_binding ( scope , Match ( clause - > args - > ast , Var ) - > name , new ( binding_t , . type = tag_type ) ) ;
} else if ( clause - > args ) {
scope = fresh_scope ( scope ) ;
ast_list_t * var = clause - > args ;
arg_t * field = tag_struct - > fields ;
while ( var | | field ) {
if ( ! var )
code_err ( clause - > tag_name , " The field %T.%s.%s wasn't accounted for " , subject_t , clause_tag_name , field - > name ) ;
if ( ! field )
code_err ( var - > ast , " This is one more field than %T has " , subject_t ) ;
2024-08-03 13:39:04 -07:00
code = CORD_all ( code , compile_type ( field - > type ) , " " , compile ( env , var - > ast ) , " = subject.$ " , clause_tag_name , " .$ " , field - > name , " ; \n " ) ;
2024-05-01 10:53:51 -07:00
set_binding ( scope , Match ( var - > ast , Var ) - > name , new ( binding_t , . type = field - > type ) ) ;
var = var - > next ;
field = field - > next ;
}
2024-03-14 10:28:30 -07:00
}
code = CORD_all ( code , compile_statement ( scope , clause - > body ) , " \n break; \n } \n " ) ;
}
if ( when - > else_body ) {
code = CORD_all ( code , " default: { \n " , compile_statement ( env , when - > else_body ) , " \n break; \n } " ) ;
2024-07-23 16:46:42 -07:00
} else {
code = CORD_all ( code , " default: errx(1, \" Invalid tag! \" ); \n " ) ;
2024-03-14 10:28:30 -07:00
}
code = CORD_all ( code , " \n } \n } " ) ;
return code ;
}
case DocTest : {
auto test = Match ( ast , DocTest ) ;
type_t * expr_t = get_type ( env , test - > expr ) ;
if ( ! expr_t )
code_err ( test - > expr , " I couldn't figure out the type of this expression " ) ;
CORD output = NULL ;
if ( test - > output ) {
const uint8_t * raw = ( const uint8_t * ) CORD_to_const_char_star ( test - > output ) ;
uint8_t buf [ 128 ] = { 0 } ;
size_t norm_len = sizeof ( buf ) ;
uint8_t * norm = u8_normalize ( UNINORM_NFD , ( uint8_t * ) raw , strlen ( ( char * ) raw ) + 1 , buf , & norm_len ) ;
assert ( norm [ norm_len - 1 ] = = 0 ) ;
output = CORD_from_char_star ( ( char * ) norm ) ;
if ( norm & & norm ! = buf ) free ( norm ) ;
}
if ( test - > expr - > tag = = Declare ) {
auto decl = Match ( test - > expr , Declare ) ;
2024-06-13 10:17:51 -07:00
if ( decl - > value - > tag = = Use | | decl - > value - > tag = = Import ) {
2024-04-21 08:22:11 -07:00
assert ( compile_statement ( env , test - > expr ) = = CORD_EMPTY ) ;
return CORD_asprintf (
" test(NULL, NULL, %r, %r, %ld, %ld); " ,
compile ( env , WrapAST ( test - > expr , TextLiteral , . cord = output ) ) ,
compile ( env , WrapAST ( test - > expr , TextLiteral , . cord = test - > expr - > file - > filename ) ) ,
( int64_t ) ( test - > expr - > start - test - > expr - > file - > text ) ,
( int64_t ) ( test - > expr - > end - test - > expr - > file - > text ) ) ;
} else {
2024-05-13 21:30:57 -07:00
CORD var = CORD_all ( " $ " , Match ( decl - > var , Var ) - > name ) ;
2024-04-21 08:22:11 -07:00
return CORD_asprintf (
2024-05-13 21:30:57 -07:00
" %r; \n "
2024-05-23 21:03:46 -07:00
" test(({ %r = %r; &%r;}), %r, %r, %r, %ld, %ld); \n " ,
2024-06-16 15:09:54 -07:00
compile_declaration ( get_type ( env , decl - > value ) , var ) ,
2024-05-13 21:30:57 -07:00
var ,
2024-08-12 14:44:05 -07:00
compile_maybe_incref ( env , decl - > value ) ,
2024-05-13 21:30:57 -07:00
var ,
2024-04-21 08:22:11 -07:00
compile_type_info ( env , get_type ( env , decl - > value ) ) ,
compile ( env , WrapAST ( test - > expr , TextLiteral , . cord = output ) ) ,
compile ( env , WrapAST ( test - > expr , TextLiteral , . cord = test - > expr - > file - > filename ) ) ,
( int64_t ) ( test - > expr - > start - test - > expr - > file - > text ) ,
( int64_t ) ( test - > expr - > end - test - > expr - > file - > text ) ) ;
}
2024-03-14 10:28:30 -07:00
} else if ( test - > expr - > tag = = Assign ) {
auto assign = Match ( test - > expr , Assign ) ;
if ( ! assign - > targets - > next & & assign - > targets - > ast - > tag = = Var ) {
// Common case: assigning to one variable:
2024-05-23 21:09:39 -07:00
type_t * lhs_t = get_type ( env , assign - > targets - > ast ) ;
if ( lhs_t - > tag = = PointerType & & Match ( lhs_t , PointerType ) - > is_stack )
2024-05-21 18:55:52 -07:00
code_err ( test - > expr , " Stack references cannot be assigned to local variables because the variable may outlive the stack memory. " ) ;
2024-05-23 21:09:39 -07:00
env_t * val_scope = with_enum_scope ( env , lhs_t ) ;
type_t * rhs_t = get_type ( val_scope , assign - > values - > ast ) ;
2024-08-12 14:44:05 -07:00
CORD value = compile_maybe_incref ( val_scope , assign - > values - > ast ) ;
2024-05-23 21:09:39 -07:00
if ( ! promote ( env , & value , rhs_t , lhs_t ) )
code_err ( assign - > values - > ast , " You cannot assign a %T value to a %T operand " , rhs_t , lhs_t ) ;
2024-05-13 21:30:57 -07:00
return CORD_asprintf (
" test(({ %r; &%r; }), %r, %r, %r, %ld, %ld); " ,
2024-05-23 21:09:39 -07:00
compile_assignment ( env , assign - > targets - > ast , value ) ,
2024-05-13 21:30:57 -07:00
compile ( env , assign - > targets - > ast ) ,
2024-05-23 21:09:39 -07:00
compile_type_info ( env , lhs_t ) ,
2024-03-14 10:28:30 -07:00
compile ( env , WrapAST ( test - > expr , TextLiteral , . cord = test - > output ) ) ,
compile ( env , WrapAST ( test - > expr , TextLiteral , . cord = test - > expr - > file - > filename ) ) ,
( int64_t ) ( test - > expr - > start - test - > expr - > file - > text ) ,
( int64_t ) ( test - > expr - > end - test - > expr - > file - > text ) ) ;
} else {
// Multi-assign or assignment to potentially non-idempotent targets
if ( test - > output & & assign - > targets - > next )
code_err ( ast , " Sorry, but doctesting with '=' is not supported for multi-assignments " ) ;
2024-05-13 21:30:57 -07:00
CORD code = " test(({ // Assignment \n " ;
2024-03-14 10:28:30 -07:00
int64_t i = 1 ;
2024-04-23 09:54:56 -07:00
for ( ast_list_t * target = assign - > targets , * value = assign - > values ; target & & value ; target = target - > next , value = value - > next ) {
type_t * target_type = get_type ( env , target - > ast ) ;
2024-05-21 18:55:52 -07:00
if ( target_type - > tag = = PointerType & & Match ( target_type , PointerType ) - > is_stack )
code_err ( ast , " Stack references cannot be assigned to local variables because the variable may outlive the stack memory. " ) ;
2024-05-15 10:42:45 -07:00
env_t * val_scope = with_enum_scope ( env , target_type ) ;
type_t * value_type = get_type ( val_scope , value - > ast ) ;
2024-08-12 14:44:05 -07:00
CORD val_code = compile_maybe_incref ( val_scope , value - > ast ) ;
2024-04-23 09:54:56 -07:00
if ( ! promote ( env , & val_code , value_type , target_type ) )
code_err ( value - > ast , " This %T value cannot be converted to a %T type " , value_type , target_type ) ;
2024-06-16 15:09:54 -07:00
CORD_appendf ( & code , " %r $%ld = %r; \n " , compile_type ( target_type ) , i + + , val_code ) ;
2024-04-23 09:54:56 -07:00
}
2024-03-14 10:28:30 -07:00
i = 1 ;
2024-03-22 10:53:23 -07:00
for ( ast_list_t * target = assign - > targets ; target ; target = target - > next )
code = CORD_all ( code , compile_assignment ( env , target - > ast , CORD_asprintf ( " $%ld " , i + + ) ) ) ;
2024-03-14 10:28:30 -07:00
2024-08-12 14:44:05 -07:00
CORD_appendf ( & code , " &$1; }), %r, %r, %r, %ld, %ld); " ,
2024-03-14 10:28:30 -07:00
compile_type_info ( env , get_type ( env , assign - > targets - > ast ) ) ,
compile ( env , WrapAST ( test - > expr , TextLiteral , . cord = test - > output ) ) ,
compile ( env , WrapAST ( test - > expr , TextLiteral , . cord = test - > expr - > file - > filename ) ) ,
( int64_t ) ( test - > expr - > start - test - > expr - > file - > text ) ,
( int64_t ) ( test - > expr - > end - test - > expr - > file - > text ) ) ;
2024-05-13 21:30:57 -07:00
return code ;
2024-03-14 10:28:30 -07:00
}
2024-07-01 08:31:50 -07:00
} else if ( test - > expr - > tag = = UpdateAssign ) {
return CORD_asprintf (
" test(({ %r; &%r; }), %r, %r, %r, %ld, %ld); " ,
compile_statement ( env , test - > expr ) ,
compile_lvalue ( env , Match ( test - > expr , UpdateAssign ) - > lhs ) ,
compile_type_info ( env , get_type ( env , Match ( test - > expr , UpdateAssign ) - > lhs ) ) ,
compile ( env , WrapAST ( test - > expr , TextLiteral , . cord = test - > output ) ) ,
compile ( env , WrapAST ( test - > expr , TextLiteral , . cord = test - > expr - > file - > filename ) ) ,
( int64_t ) ( test - > expr - > start - test - > expr - > file - > text ) ,
( int64_t ) ( test - > expr - > end - test - > expr - > file - > text ) ) ;
2024-07-13 15:43:50 -07:00
} else if ( expr_t - > tag = = VoidType | | expr_t - > tag = = AbortType | | expr_t - > tag = = ReturnType ) {
2024-03-14 10:28:30 -07:00
return CORD_asprintf (
2024-05-13 21:30:57 -07:00
" test(({ %r; NULL; }), NULL, NULL, %r, %ld, %ld); " ,
2024-03-14 10:28:30 -07:00
compile_statement ( env , test - > expr ) ,
compile ( env , WrapAST ( test - > expr , TextLiteral , . cord = test - > expr - > file - > filename ) ) ,
( int64_t ) ( test - > expr - > start - test - > expr - > file - > text ) ,
( int64_t ) ( test - > expr - > end - test - > expr - > file - > text ) ) ;
} else {
return CORD_asprintf (
2024-08-12 14:44:05 -07:00
" test(%r, %r, %r, %r, %ld, %ld); " ,
test - > expr - > tag = = Var ? CORD_all ( " & " , compile ( env , test - > expr ) )
: CORD_all ( " ( " , compile_type ( expr_t ) , " [1]){ " , compile ( env , test - > expr ) , " } " ) ,
2024-03-14 10:28:30 -07:00
compile_type_info ( env , expr_t ) ,
compile ( env , WrapAST ( test - > expr , TextLiteral , . cord = output ) ) ,
compile ( env , WrapAST ( test - > expr , TextLiteral , . cord = test - > expr - > file - > filename ) ) ,
( int64_t ) ( test - > expr - > start - test - > expr - > file - > text ) ,
( int64_t ) ( test - > expr - > end - test - > expr - > file - > text ) ) ;
}
}
case Declare : {
auto decl = Match ( ast , Declare ) ;
2024-06-13 10:17:51 -07:00
if ( decl - > value - > tag = = Use | | decl - > value - > tag = = Import ) {
2024-04-24 10:53:37 -07:00
return compile_statement ( env , decl - > value ) ;
2024-03-19 11:22:03 -07:00
} else {
type_t * t = get_type ( env , decl - > value ) ;
2024-07-13 15:43:50 -07:00
if ( t - > tag = = AbortType | | t - > tag = = VoidType | | t - > tag = = ReturnType )
2024-03-19 11:22:03 -07:00
code_err ( ast , " You can't declare a variable with a %T value " , t ) ;
2024-08-12 14:44:05 -07:00
return CORD_all ( compile_declaration ( t , CORD_cat ( " $ " , Match ( decl - > var , Var ) - > name ) ) , " = " , compile_maybe_incref ( env , decl - > value ) , " ; " );
2024-03-19 11:22:03 -07:00
}
2024-03-14 10:28:30 -07:00
}
case Assign : {
auto assign = Match ( ast , Assign ) ;
2024-05-15 10:39:35 -07:00
// Single assignment, no temp vars needed:
if ( assign - > targets & & ! assign - > targets - > next ) {
type_t * lhs_t = get_type ( env , assign - > targets - > ast ) ;
2024-05-21 18:55:52 -07:00
if ( lhs_t - > tag = = PointerType & & Match ( lhs_t , PointerType ) - > is_stack )
code_err ( ast , " Stack references cannot be assigned to local variables because the variable may outlive the stack memory. " ) ;
2024-05-15 10:40:27 -07:00
env_t * val_env = with_enum_scope ( env , lhs_t ) ;
2024-05-15 10:39:35 -07:00
type_t * rhs_t = get_type ( val_env , assign - > values - > ast ) ;
2024-08-12 14:44:05 -07:00
CORD val = compile_maybe_incref ( val_env , assign - > values - > ast ) ;
2024-05-15 10:39:35 -07:00
if ( ! promote ( env , & val , rhs_t , lhs_t ) )
code_err ( assign - > values - > ast , " You cannot assign a %T value to a %T operand " , rhs_t , lhs_t ) ;
return compile_assignment ( env , assign - > targets - > ast , val ) ;
}
2024-03-14 10:28:30 -07:00
CORD code = " { // Assignment \n " ;
int64_t i = 1 ;
2024-05-15 10:39:35 -07:00
for ( ast_list_t * value = assign - > values , * target = assign - > targets ; value & & target ; value = value - > next , target = target - > next ) {
type_t * lhs_t = get_type ( env , target - > ast ) ;
2024-05-21 18:55:52 -07:00
if ( lhs_t - > tag = = PointerType & & Match ( lhs_t , PointerType ) - > is_stack )
code_err ( ast , " Stack references cannot be assigned to local variables because the variable may outlive the stack memory. " ) ;
2024-05-15 10:40:27 -07:00
env_t * val_env = with_enum_scope ( env , lhs_t ) ;
2024-05-15 10:39:35 -07:00
type_t * rhs_t = get_type ( val_env , value - > ast ) ;
2024-08-12 14:44:05 -07:00
CORD val = compile_maybe_incref ( val_env , value - > ast ) ;
2024-05-15 10:39:35 -07:00
if ( ! promote ( env , & val , rhs_t , lhs_t ) )
code_err ( value - > ast , " You cannot assign a %T value to a %T operand " , rhs_t , lhs_t ) ;
2024-06-16 15:09:54 -07:00
CORD_appendf ( & code , " %r $%ld = %r; \n " , compile_type ( lhs_t ) , i + + , val ) ;
2024-05-15 10:39:35 -07:00
}
2024-03-14 10:28:30 -07:00
i = 1 ;
for ( ast_list_t * target = assign - > targets ; target ; target = target - > next ) {
2024-03-22 10:53:23 -07:00
code = CORD_cat ( code , compile_assignment ( env , target - > ast , CORD_asprintf ( " $%ld " , i + + ) ) ) ;
2024-03-14 10:28:30 -07:00
}
return CORD_cat ( code , " \n } " ) ;
}
case UpdateAssign : {
auto update = Match ( ast , UpdateAssign ) ;
2024-03-24 11:28:20 -07:00
CORD lhs = compile_lvalue ( env , update - > lhs ) ;
2024-07-01 08:20:41 -07:00
2024-08-10 17:50:15 -07:00
CORD method_call = compile_math_method ( env , update - > op , update - > lhs , update - > rhs , get_type ( env , update - > lhs ) ) ;
2024-07-01 08:20:41 -07:00
if ( method_call )
return CORD_all ( lhs , " = " , method_call , " ; " ) ;
2024-03-14 10:28:30 -07:00
CORD rhs = compile ( env , update - > rhs ) ;
type_t * lhs_t = get_type ( env , update - > lhs ) ;
type_t * rhs_t = get_type ( env , update - > rhs ) ;
type_t * operand_t ;
if ( promote ( env , & rhs , rhs_t , lhs_t ) )
operand_t = lhs_t ;
else if ( promote ( env , & lhs , lhs_t , rhs_t ) )
operand_t = rhs_t ;
else if ( lhs_t - > tag = = ArrayType & & promote ( env , & rhs , rhs_t , Match ( lhs_t , ArrayType ) - > item_type ) )
operand_t = lhs_t ;
else
code_err ( ast , " I can't do operations between %T and %T " , lhs_t , rhs_t ) ;
2024-07-01 08:29:28 -07:00
2024-03-14 10:28:30 -07:00
switch ( update - > op ) {
2024-07-01 08:43:18 -07:00
case BINOP_MULT :
if ( operand_t - > tag ! = IntType & & operand_t - > tag ! = NumType )
code_err ( ast , " I can't do a multiply assignment with this operator between %T and %T " , lhs_t , rhs_t ) ;
return CORD_all ( lhs , " *= " , rhs , " ; " ) ;
case BINOP_DIVIDE :
if ( operand_t - > tag ! = IntType & & operand_t - > tag ! = NumType )
code_err ( ast , " I can't do a divide assignment with this operator between %T and %T " , lhs_t , rhs_t ) ;
return CORD_all ( lhs , " /= " , rhs , " ; " ) ;
case BINOP_MOD :
if ( operand_t - > tag ! = IntType & & operand_t - > tag ! = NumType )
code_err ( ast , " I can't do a mod assignment with this operator between %T and %T " , lhs_t , rhs_t ) ;
return CORD_all ( lhs , " = " , lhs , " % " , rhs ) ;
case BINOP_MOD1 :
if ( operand_t - > tag ! = IntType & & operand_t - > tag ! = NumType )
code_err ( ast , " I can't do a mod assignment with this operator between %T and %T " , lhs_t , rhs_t ) ;
2024-08-10 17:40:41 -07:00
return CORD_all ( lhs , " = ((( " , lhs , " ) - 1 ) % " , rhs, " ) + 1 ; " );
2024-07-01 08:43:18 -07:00
case BINOP_PLUS :
if ( operand_t - > tag ! = IntType & & operand_t - > tag ! = NumType )
code_err ( ast , " I can't do an addition assignment with this operator between %T and %T " , lhs_t , rhs_t ) ;
return CORD_all ( lhs , " += " , rhs , " ; " ) ;
case BINOP_MINUS :
if ( operand_t - > tag ! = IntType & & operand_t - > tag ! = NumType )
code_err ( ast , " I can't do a subtraction assignment with this operator between %T and %T " , lhs_t , rhs_t ) ;
return CORD_all ( lhs , " -= " , rhs , " ; " ) ;
2024-03-14 10:28:30 -07:00
case BINOP_POWER : {
if ( lhs_t - > tag ! = NumType )
code_err ( ast , " '^=' is only supported for Num types " ) ;
if ( lhs_t - > tag = = NumType & & Match ( lhs_t , NumType ) - > bits = = 32 )
2024-03-18 15:06:20 -07:00
return CORD_all ( lhs , " = powf( " , lhs , " , " , rhs , " ) ; " );
2024-03-14 10:28:30 -07:00
else
2024-03-18 15:06:20 -07:00
return CORD_all ( lhs , " = pow( " , lhs , " , " , rhs , " ) ; " );
2024-03-14 10:28:30 -07:00
}
2024-07-01 08:43:18 -07:00
case BINOP_LSHIFT :
if ( operand_t - > tag ! = IntType )
code_err ( ast , " I can't do a shift assignment with this operator between %T and %T " , lhs_t , rhs_t ) ;
return CORD_all ( lhs , " <<= " , rhs , " ; " ) ;
case BINOP_RSHIFT :
if ( operand_t - > tag ! = IntType )
code_err ( ast , " I can't do a shift assignment with this operator between %T and %T " , lhs_t , rhs_t ) ;
return CORD_all ( lhs , " >>= " , rhs , " ; " ) ;
2024-03-14 10:28:30 -07:00
case BINOP_AND : {
if ( operand_t - > tag = = BoolType )
2024-07-01 08:20:41 -07:00
return CORD_all ( " if ( " , lhs , " ) " , lhs, " = " , rhs, " ; " );
2024-03-14 10:28:30 -07:00
else if ( operand_t - > tag = = IntType )
2024-07-01 08:20:41 -07:00
return CORD_all ( lhs , " &= " , rhs , " ; " ) ;
2024-03-14 10:28:30 -07:00
else
code_err ( ast , " 'or=' is not implemented for %T types " , operand_t ) ;
}
case BINOP_OR : {
if ( operand_t - > tag = = BoolType )
2024-07-01 08:20:41 -07:00
return CORD_all ( " if (!( " , lhs , " ) ) " , lhs, " = " , rhs, " ; " );
2024-03-14 10:28:30 -07:00
else if ( operand_t - > tag = = IntType )
2024-07-01 08:20:41 -07:00
return CORD_all ( lhs , " |= " , rhs , " ; " ) ;
2024-03-14 10:28:30 -07:00
else
code_err ( ast , " 'or=' is not implemented for %T types " , operand_t ) ;
}
2024-07-01 08:43:18 -07:00
case BINOP_XOR :
if ( operand_t - > tag ! = IntType & & operand_t - > tag ! = BoolType )
code_err ( ast , " I can't do an xor assignment with this operator between %T and %T " , lhs_t , rhs_t ) ;
return CORD_all ( lhs , " ^= " , rhs , " ; " ) ;
2024-03-14 10:28:30 -07:00
case BINOP_CONCAT : {
if ( operand_t - > tag = = TextType ) {
2024-07-01 08:20:41 -07:00
return CORD_all ( lhs , " = CORD_cat( " , lhs , " , " , rhs , " ) ; " );
2024-03-14 10:28:30 -07:00
} else if ( operand_t - > tag = = ArrayType ) {
2024-08-03 12:33:50 -07:00
CORD padded_item_size = CORD_asprintf ( " %ld " , padded_type_size ( Match ( operand_t , ArrayType ) - > item_type ) ) ;
2024-03-14 10:28:30 -07:00
if ( promote ( env , & rhs , rhs_t , Match ( lhs_t , ArrayType ) - > item_type ) ) {
// arr ++= item
if ( update - > lhs - > tag = = Var )
2024-08-12 22:30:25 -07:00
return CORD_all ( " Array$insert(& " , lhs , " , stack( " , rhs , " ) , I ( 0 ) , " , padded_item_size, " ) ; " );
2024-03-14 10:28:30 -07:00
else
2024-08-03 12:33:50 -07:00
return CORD_all ( lhs , " Array$concat( " , lhs , " , Array( " , rhs , " ) , " , padded_item_size, " ) ; " );
2024-03-14 10:28:30 -07:00
} else {
// arr ++= [...]
if ( update - > lhs - > tag = = Var )
2024-08-12 22:30:25 -07:00
return CORD_all ( " Array$insert_all(& " , lhs , " , " , rhs , " , I(0) , " , padded_item_size, " ) ; " );
2024-03-14 10:28:30 -07:00
else
2024-08-03 12:33:50 -07:00
return CORD_all ( lhs , " Array$concat( " , lhs , " , " , rhs , " , " , padded_item_size , " ) ; " );
2024-03-14 10:28:30 -07:00
}
} else {
code_err ( ast , " '++=' is not implemented for %T types " , operand_t ) ;
}
}
default : code_err ( ast , " Update assignments are not implemented for this operation " ) ;
}
}
case StructDef : {
compile_struct_def ( env , ast ) ;
return CORD_EMPTY ;
}
case EnumDef : {
compile_enum_def ( env , ast ) ;
return CORD_EMPTY ;
}
case LangDef : {
auto def = Match ( ast , LangDef ) ;
2024-03-21 10:32:52 -07:00
CORD_appendf ( & env - > code - > typeinfos , " public const TypeInfo %r%s = {%zu, %zu, {.tag=TextInfo, .TextInfo={%r}}}; \n " ,
2024-06-16 13:08:35 -07:00
namespace_prefix ( env - > libname , env - > namespace ) , def - > name , sizeof ( CORD ) , __alignof__ ( CORD ) ,
2024-03-29 09:54:31 -07:00
Text $ quoted ( def - > name , false ) ) ;
2024-03-14 10:28:30 -07:00
compile_namespace ( env , def - > name , def - > namespace ) ;
return CORD_EMPTY ;
}
case FunctionDef : {
auto fndef = Match ( ast , FunctionDef ) ;
2024-04-17 10:44:01 -07:00
bool is_private = Match ( fndef - > name , Var ) - > name [ 0 ] = = ' _ ' ;
2024-06-16 13:08:35 -07:00
CORD name = compile ( env , fndef - > name ) ;
2024-03-14 10:28:30 -07:00
type_t * ret_t = fndef - > ret_type ? parse_type_ast ( env , fndef - > ret_type ) : Type ( VoidType ) ;
CORD arg_signature = " ( " ;
for ( arg_ast_t * arg = fndef - > args ; arg ; arg = arg - > next ) {
type_t * arg_type = get_arg_ast_type ( env , arg ) ;
2024-06-16 15:09:54 -07:00
arg_signature = CORD_cat ( arg_signature , compile_declaration ( arg_type , CORD_cat ( " $ " , arg - > name ) ) ) ;
2024-03-14 10:28:30 -07:00
if ( arg - > next ) arg_signature = CORD_cat ( arg_signature , " , " ) ;
}
arg_signature = CORD_cat ( arg_signature , " ) " ) ;
2024-06-16 15:09:54 -07:00
CORD ret_type_code = compile_type ( ret_t ) ;
2024-03-14 10:28:30 -07:00
2024-04-17 10:44:01 -07:00
if ( is_private )
2024-03-14 10:28:30 -07:00
env - > code - > staticdefs = CORD_all ( env - > code - > staticdefs , " static " , ret_type_code , " " , name , arg_signature , " ; \n " ) ;
CORD code ;
if ( fndef - > cache ) {
code = CORD_all ( " static " , ret_type_code , " " , name , " $uncached " , arg_signature ) ;
} else {
code = CORD_all ( ret_type_code , " " , name , arg_signature ) ;
if ( fndef - > is_inline )
code = CORD_cat ( " inline " , code ) ;
2024-04-17 10:44:01 -07:00
if ( ! is_private )
2024-03-14 10:28:30 -07:00
code = CORD_cat ( " public " , code ) ;
}
2024-04-10 08:49:43 -07:00
env_t * body_scope = fresh_scope ( env ) ;
2024-07-04 15:09:33 -07:00
body_scope - > deferred = NULL ;
2024-06-16 12:12:00 -07:00
body_scope - > namespace = NULL ;
2024-03-14 10:28:30 -07:00
for ( arg_ast_t * arg = fndef - > args ; arg ; arg = arg - > next ) {
type_t * arg_type = get_arg_ast_type ( env , arg ) ;
2024-04-16 10:50:07 -07:00
set_binding ( body_scope , arg - > name , new ( binding_t , . type = arg_type , . code = CORD_cat ( " $ " , arg - > name ) ) ) ;
2024-03-14 10:28:30 -07:00
}
2024-03-15 10:35:30 -07:00
fn_ctx_t fn_ctx = ( fn_ctx_t ) {
2024-07-14 11:13:23 -07:00
. parent = NULL ,
2024-03-14 10:28:30 -07:00
. return_type = ret_t ,
. closure_scope = NULL ,
. closed_vars = NULL ,
} ;
body_scope - > fn_ctx = & fn_ctx ;
2024-07-13 15:43:50 -07:00
type_t * body_type = get_type ( body_scope , fndef - > body ) ;
if ( ret_t - > tag ! = VoidType & & ret_t - > tag ! = AbortType & & body_type - > tag ! = AbortType & & body_type - > tag ! = ReturnType )
2024-07-04 15:27:08 -07:00
code_err ( ast , " This function can reach the end without returning a %T value! " , ret_t ) ;
2024-03-14 10:28:30 -07:00
CORD body = compile_statement ( body_scope , fndef - > body ) ;
2024-08-17 11:41:31 -07:00
if ( streq ( Match ( fndef - > name , Var ) - > name , " main " ) )
body = CORD_all ( " _initialize(); \n " , body ) ;
2024-03-14 10:28:30 -07:00
if ( CORD_fetch ( body , 0 ) ! = ' { ' )
body = CORD_asprintf ( " { \n %r \n } " , body ) ;
env - > code - > funcs = CORD_all ( env - > code - > funcs , code , " " , body , " \n " ) ;
2024-08-15 22:46:05 -07:00
if ( fndef - > cache & & fndef - > args = = NULL ) { // no-args cache just uses a static var
CORD wrapper = CORD_all (
is_private ? CORD_EMPTY : " public " , ret_type_code , " " , name , " (void) { \n "
" static " , compile_declaration ( ret_t , " cached_result " ) , " ; \n " ,
" static bool initialized = false; \n " ,
" if (!initialized) { \n "
" \t cached_result = " , name , " $uncached(); \n " ,
" \t initialized = true; \n " ,
" } \n " ,
" return cached_result; \n "
" } \n " ) ;
env - > code - > funcs = CORD_cat ( env - > code - > funcs , wrapper ) ;
} else if ( fndef - > cache & & fndef - > cache - > tag = = Int ) {
2024-08-12 22:30:25 -07:00
int64_t cache_size = Int64 $ from_text ( Match ( fndef - > cache , Int ) - > str , NULL ) ;
2024-07-23 16:46:42 -07:00
const char * arg_type_name = heap_strf ( " %s$args " , Match ( fndef - > name , Var ) - > name ) ;
2024-03-14 10:28:30 -07:00
ast_t * args_def = FakeAST ( StructDef , . name = arg_type_name , . fields = fndef - > args ) ;
2024-05-18 22:46:30 -07:00
prebind_statement ( env , args_def ) ;
2024-03-14 10:28:30 -07:00
bind_statement ( env , args_def ) ;
( void ) compile_statement ( env , args_def ) ;
2024-03-29 09:54:31 -07:00
type_t * args_t = Table $ str_get ( * env - > types , arg_type_name ) ;
2024-03-14 10:28:30 -07:00
assert ( args_t ) ;
CORD all_args = CORD_EMPTY ;
for ( arg_ast_t * arg = fndef - > args ; arg ; arg = arg - > next )
2024-04-16 10:50:07 -07:00
all_args = CORD_all ( all_args , " $ " , arg - > name , arg - > next ? " , " : CORD_EMPTY ) ;
2024-03-14 10:28:30 -07:00
CORD pop_code = CORD_EMPTY ;
2024-08-12 22:30:25 -07:00
if ( fndef - > cache - > tag = = Int & & cache_size > 0 ) {
pop_code = CORD_all ( " if (cache.entries.length > " , CORD_asprintf ( " %ld " , cache_size ) ,
" ) Table$remove(&cache, cache.entries.data + cache.entries.stride*Int$random(I(0), I(cache.entries.length-1)), table_type); \n " ) ;
2024-03-14 10:28:30 -07:00
}
2024-07-23 16:46:42 -07:00
CORD arg_typedef = compile_struct_typedef ( env , args_def ) ;
2024-06-06 13:28:53 -07:00
env - > code - > local_typedefs = CORD_all ( env - > code - > local_typedefs , arg_typedef ) ;
2024-07-23 16:46:42 -07:00
env - > code - > staticdefs = CORD_all ( env - > code - > staticdefs ,
" extern const TypeInfo " , namespace_prefix ( env - > libname , env - > namespace ) , arg_type_name , " ; \n " ) ;
2024-03-14 10:28:30 -07:00
CORD wrapper = CORD_all (
2024-04-17 10:44:01 -07:00
is_private ? CORD_EMPTY : " public " , ret_type_code , " " , name , arg_signature , " { \n "
2024-04-16 10:50:07 -07:00
" static table_t cache = {}; \n " ,
2024-06-16 15:09:54 -07:00
compile_type ( args_t ) , " args = { " , all_args , " }; \n "
2024-06-06 13:28:53 -07:00
" const TypeInfo *table_type = $TableInfo( " , compile_type_info ( env , args_t ) , " , " , compile_type_info ( env , ret_t ) , " ); \n " ,
2024-08-15 22:46:05 -07:00
compile_declaration ( Type ( PointerType , . pointed = ret_t , . is_optional = true ) , " cached " ) , " = Table$get_raw(cache, &args, table_type); \n "
2024-04-16 10:50:07 -07:00
" if (cached) return *cached; \n " ,
2024-08-15 22:46:05 -07:00
compile_declaration ( ret_t , " ret " ) , " = " , name , " $uncached( " , all_args , " ); \n " ,
2024-03-14 10:28:30 -07:00
pop_code ,
2024-04-16 10:50:07 -07:00
" Table$set(&cache, &args, &ret, table_type); \n "
" return ret; \n "
2024-03-14 10:28:30 -07:00
" } \n " ) ;
env - > code - > funcs = CORD_cat ( env - > code - > funcs , wrapper ) ;
}
return CORD_EMPTY ;
}
case Skip : {
2024-03-15 10:35:30 -07:00
const char * target = Match ( ast , Skip ) - > target ;
for ( loop_ctx_t * ctx = env - > loop_ctx ; ctx ; ctx = ctx - > next ) {
2024-07-13 14:17:58 -07:00
bool matched = ! target | | CORD_cmp ( target , ctx - > loop_name ) = = 0 ;
for ( ast_list_t * var = ctx - > loop_vars ; var & & ! matched ; var = var - > next )
matched = ( CORD_cmp ( target , Match ( var - > ast , Var ) - > name ) = = 0 ) ;
if ( matched ) {
2024-03-15 10:35:30 -07:00
if ( ! ctx - > skip_label ) {
static int64_t skip_label_count = 1 ;
CORD_sprintf ( & ctx - > skip_label , " skip_%ld " , skip_label_count ) ;
+ + skip_label_count ;
}
2024-07-04 15:00:01 -07:00
CORD code = CORD_EMPTY ;
for ( deferral_t * deferred = env - > deferred ; deferred & & deferred ! = ctx - > deferred ; deferred = deferred - > next )
code = CORD_all ( code , compile_statement ( deferred - > defer_env , deferred - > block ) ) ;
return CORD_all ( code , " goto " , ctx - > skip_label , " ; " ) ;
2024-03-15 10:35:30 -07:00
}
}
if ( env - > loop_ctx )
code_err ( ast , " This 'skip' is not inside any loop " ) ;
else if ( target )
code_err ( ast , " No loop target named '%s' was found " , target ) ;
else
code_err ( ast , " I couldn't figure out how to make this skip work! " ) ;
2024-03-14 10:28:30 -07:00
}
case Stop : {
2024-03-15 10:35:30 -07:00
const char * target = Match ( ast , Stop ) - > target ;
for ( loop_ctx_t * ctx = env - > loop_ctx ; ctx ; ctx = ctx - > next ) {
2024-07-13 14:17:58 -07:00
bool matched = ! target | | CORD_cmp ( target , ctx - > loop_name ) = = 0 ;
for ( ast_list_t * var = ctx - > loop_vars ; var & & ! matched ; var = var - > next )
matched = ( CORD_cmp ( target , Match ( var - > ast , Var ) - > name ) = = 0 ) ;
if ( matched ) {
2024-03-15 10:35:30 -07:00
if ( ! ctx - > stop_label ) {
static int64_t stop_label_count = 1 ;
CORD_sprintf ( & ctx - > stop_label , " stop_%ld " , stop_label_count ) ;
+ + stop_label_count ;
}
2024-07-04 15:00:01 -07:00
CORD code = CORD_EMPTY ;
for ( deferral_t * deferred = env - > deferred ; deferred & & deferred ! = ctx - > deferred ; deferred = deferred - > next )
code = CORD_all ( code , compile_statement ( deferred - > defer_env , deferred - > block ) ) ;
return CORD_all ( code , " goto " , ctx - > stop_label , " ; " ) ;
2024-03-15 10:35:30 -07:00
}
}
if ( env - > loop_ctx )
code_err ( ast , " This 'stop' is not inside any loop " ) ;
else if ( target )
code_err ( ast , " No loop target named '%s' was found " , target ) ;
else
code_err ( ast , " I couldn't figure out how to make this stop work! " ) ;
2024-03-14 10:28:30 -07:00
}
case Pass : return " ; " ;
2024-07-04 15:00:01 -07:00
case Defer : {
ast_t * body = Match ( ast , Defer ) - > body ;
table_t * closed_vars = get_closed_vars ( env , FakeAST ( Lambda , . args = NULL , . body = body ) ) ;
static int defer_id = 0 ;
env_t * defer_env = fresh_scope ( env ) ;
CORD code = CORD_EMPTY ;
for ( int64_t i = 1 ; i < = Table $ length ( * closed_vars ) ; i + + ) {
struct { const char * name ; binding_t * b ; } * entry = Table $ entry ( * closed_vars , i ) ;
if ( entry - > b - > type - > tag = = ModuleType )
continue ;
CORD defer_name = CORD_asprintf ( " defer$%d$%s " , + + defer_id , entry - > name ) ;
code = CORD_all (
code , compile_declaration ( entry - > b - > type , defer_name ) , " = " , entry - > b - > code , " ; \n " ) ;
set_binding ( defer_env , entry - > name , new ( binding_t , . type = entry - > b - > type , . code = defer_name ) ) ;
2024-07-14 11:13:23 -07:00
if ( env - > fn_ctx - > closed_vars )
Table $ str_set ( env - > fn_ctx - > closed_vars , entry - > name , entry - > b ) ;
2024-07-04 15:00:01 -07:00
}
env - > deferred = new ( deferral_t , . defer_env = defer_env , . block = body , . next = env - > deferred ) ;
return code ;
}
2024-07-04 13:23:05 -07:00
case PrintStatement : {
ast_list_t * to_print = Match ( ast , PrintStatement ) - > to_print ;
if ( ! to_print )
return CORD_EMPTY ;
CORD code = " say(CORD_all( " ;
for ( ast_list_t * chunk = to_print ; chunk ; chunk = chunk - > next ) {
2024-07-04 13:25:06 -07:00
if ( chunk - > ast - > tag = = TextLiteral ) {
2024-07-04 13:23:05 -07:00
code = CORD_cat ( code , compile ( env , chunk - > ast ) ) ;
} else {
code = CORD_cat ( code , compile_string ( env , chunk - > ast , " USE_COLOR " ) ) ;
}
if ( chunk - > next ) code = CORD_cat ( code , " , " ) ;
}
return CORD_cat ( code , " ) ) ; " );
}
2024-03-14 10:28:30 -07:00
case Return : {
if ( ! env - > fn_ctx ) code_err ( ast , " This return statement is not inside any function " ) ;
auto ret = Match ( ast , Return ) - > value ;
assert ( env - > fn_ctx - > return_type ) ;
2024-07-04 15:00:01 -07:00
CORD code = CORD_EMPTY ;
for ( deferral_t * deferred = env - > deferred ; deferred ; deferred = deferred - > next ) {
code = CORD_all ( code , compile_statement ( deferred - > defer_env , deferred - > block ) ) ;
}
2024-03-14 10:28:30 -07:00
if ( ret ) {
2024-07-04 15:27:08 -07:00
if ( env - > fn_ctx - > return_type - > tag = = VoidType | | env - > fn_ctx - > return_type - > tag = = AbortType )
code_err ( ast , " This function is not supposed to return any values, according to its type signature " ) ;
2024-05-15 10:40:27 -07:00
env = with_enum_scope ( env , env - > fn_ctx - > return_type ) ;
2024-03-14 10:28:30 -07:00
type_t * ret_t = get_type ( env , ret ) ;
CORD value = compile ( env , ret ) ;
if ( ! promote ( env , & value , ret_t , env - > fn_ctx - > return_type ) )
code_err ( ast , " This function expects a return value of type %T, but this return has type %T " ,
env - > fn_ctx - > return_type , ret_t ) ;
2024-07-04 15:00:01 -07:00
return CORD_all ( code , " return " , value , " ; " ) ;
2024-03-14 10:28:30 -07:00
} else {
if ( env - > fn_ctx - > return_type - > tag ! = VoidType )
2024-07-04 15:27:08 -07:00
code_err ( ast , " This function expects you to return a %T value " , env - > fn_ctx - > return_type ) ;
2024-07-04 15:00:01 -07:00
return CORD_all ( code , " return; " ) ;
2024-03-14 10:28:30 -07:00
}
}
case While : {
auto while_ = Match ( ast , While ) ;
2024-03-15 10:45:25 -07:00
env_t * scope = fresh_scope ( env ) ;
loop_ctx_t loop_ctx = ( loop_ctx_t ) {
. loop_name = " while " ,
2024-07-04 15:00:01 -07:00
. deferred = scope - > deferred ,
2024-03-15 10:45:25 -07:00
. next = env - > loop_ctx ,
} ;
scope - > loop_ctx = & loop_ctx ;
CORD body = compile_statement ( scope , while_ - > body ) ;
if ( loop_ctx . skip_label )
body = CORD_all ( body , " \n " , loop_ctx . skip_label , " : continue; " ) ;
2024-05-23 09:40:21 -07:00
CORD loop = CORD_all ( " while ( " , while_ - > condition ? compile ( scope , while_ - > condition ) : " yes " , " ) { \n \t " , body , " \n } " ) ;
2024-03-15 10:45:25 -07:00
if ( loop_ctx . stop_label )
loop = CORD_all ( loop , " \n " , loop_ctx . stop_label , " :; " ) ;
return loop ;
2024-03-14 10:28:30 -07:00
}
case For : {
auto for_ = Match ( ast , For ) ;
2024-03-17 12:59:06 -07:00
// TODO: optimize case for iterating over comprehensions so we don't need to create
// an intermediary array/table
2024-03-14 10:28:30 -07:00
type_t * iter_t = get_type ( env , for_ - > iter ) ;
2024-03-15 10:35:30 -07:00
env_t * body_scope = for_scope ( env , ast ) ;
loop_ctx_t loop_ctx = ( loop_ctx_t ) {
2024-03-15 10:45:25 -07:00
. loop_name = " for " ,
2024-07-13 14:17:58 -07:00
. loop_vars = for_ - > vars ,
2024-07-04 15:00:01 -07:00
. deferred = body_scope - > deferred ,
2024-03-15 10:35:30 -07:00
. next = body_scope - > loop_ctx ,
} ;
body_scope - > loop_ctx = & loop_ctx ;
CORD body = compile_statement ( body_scope , for_ - > body ) ;
2024-03-15 10:38:25 -07:00
if ( loop_ctx . skip_label )
body = CORD_all ( body , " \n " , loop_ctx . skip_label , " : continue; " ) ;
CORD stop = loop_ctx . stop_label ? CORD_all ( " \n " , loop_ctx . stop_label , " :; " ) : CORD_EMPTY ;
2024-03-15 10:35:30 -07:00
2024-08-05 11:40:28 -07:00
if ( iter_t = = RANGE_TYPE ) {
CORD range = compile ( env , for_ - > iter ) ;
2024-08-17 11:41:31 -07:00
CORD value = for_ - > vars ? compile ( body_scope , for_ - > vars - > ast ) : " i " ;
2024-08-05 11:40:28 -07:00
if ( for_ - > empty )
code_err ( ast , " Ranges are never empty, they always contain at least their starting element " ) ;
return CORD_all (
" { \n "
" const Range_t range = " , range , " ; \n "
2024-08-13 00:20:01 -07:00
" if (range.step.small == 0) fail( \" This range has a 'step' of zero and will loop infinitely! \" ); \n "
" bool negative = (Int$compare_value(range.step, I(0)) < 0); \n "
2024-08-13 08:35:22 -07:00
" for (Int_t " , value , " = range.first; ({ int32_t cmp = Int$compare_value( " , value , " , range.last); negative ? cmp >= 0 : cmp <= 0;}) ; " , value , " = Int$plus( " , value , " , range.step)) { \n "
2024-08-05 11:40:28 -07:00
" \t " , body ,
" \n } " ,
stop ,
" \n } " ) ;
}
2024-03-14 10:28:30 -07:00
switch ( iter_t - > tag ) {
case ArrayType : {
type_t * item_t = Match ( iter_t , ArrayType ) - > item_type ;
2024-08-12 22:30:25 -07:00
CORD index = CORD_EMPTY ;
2024-08-12 14:44:05 -07:00
CORD value = CORD_EMPTY ;
2024-07-13 14:17:58 -07:00
if ( for_ - > vars ) {
if ( for_ - > vars - > next ) {
if ( for_ - > vars - > next - > next )
code_err ( for_ - > vars - > next - > next - > ast , " This is too many variables for this loop " ) ;
2024-08-17 11:41:31 -07:00
index = compile ( body_scope , for_ - > vars - > ast ) ;
value = compile ( body_scope , for_ - > vars - > next - > ast ) ;
2024-07-13 14:17:58 -07:00
} else {
2024-08-17 11:41:31 -07:00
value = compile ( body_scope , for_ - > vars - > ast ) ;
2024-07-13 14:17:58 -07:00
}
}
2024-08-12 14:44:05 -07:00
CORD loop = CORD_EMPTY ;
2024-07-20 14:13:15 -07:00
ast_t * array = for_ - > iter ;
// Micro-optimization: inline the logic for iterating over
// `array:from(i)` and `array:to(i)` because these happen inside
// hot path inner loops and can actually meaningfully affect
// performance:
2024-08-12 22:30:25 -07:00
// if (for_->iter->tag == MethodCall && streq(Match(for_->iter, MethodCall)->name, "to")
// && value_type(get_type(env, Match(for_->iter, MethodCall)->self))->tag == ArrayType) {
// array = Match(for_->iter, MethodCall)->self;
// CORD limit = compile_arguments(env, for_->iter, new(arg_t, .type=INT_TYPE, .name="last"), Match(for_->iter, MethodCall)->args);
// loop = CORD_all(loop, "for (int64_t ", index, " = 1, raw_limit = ", limit,
// ", limit = raw_limit < 0 ? iterating.length + raw_limit + 1 : raw_limit; ",
// index, " <= limit; ++", index, ")");
// } else if (for_->iter->tag == MethodCall && streq(Match(for_->iter, MethodCall)->name, "from")
// && value_type(get_type(env, Match(for_->iter, MethodCall)->self))->tag == ArrayType) {
// array = Match(for_->iter, MethodCall)->self;
// CORD first = compile_arguments(env, for_->iter, new(arg_t, .type=INT_TYPE, .name="last"), Match(for_->iter, MethodCall)->args);
// loop = CORD_all(loop, "for (int64_t first = ", first, ", ", index, " = MAX(1, first < 1 ? iterating.length + first + 1 : first", "); ",
// index, " <= iterating.length; ++", index, ")");
// } else {
loop = CORD_all ( loop , " for (int64_t i = 1; i <= iterating.length; ++i) " ) ;
// }
if ( index ! = CORD_EMPTY )
body = CORD_all ( " Int_t " , index , " = I(i); \n " , body ) ;
2024-08-12 14:44:05 -07:00
if ( value ! = CORD_EMPTY ) {
loop = CORD_all ( loop , " { \n " ,
compile_declaration ( item_t , value ) ,
2024-08-12 22:30:25 -07:00
" = *( " , compile_type ( item_t ) , " *)(iterating.data + (i-1)*iterating.stride); \n " ,
2024-08-12 14:44:05 -07:00
body , " \n } " ) ;
} else {
loop = CORD_all ( loop , " { \n " , body , " \n } " ) ;
2024-07-20 14:13:15 -07:00
}
2024-08-10 12:15:38 -07:00
2024-08-12 14:44:05 -07:00
if ( can_be_mutated ( env , array ) & & is_idempotent ( array ) ) {
CORD array_code = compile ( env , array ) ;
loop = CORD_all ( " { \n "
" array_t iterating = ARRAY_COPY( " , array_code , " ); \n " ,
loop ,
stop ,
" \n ARRAY_DECREF( " , array_code , " ); \n "
" } \n " ) ;
if ( for_ - > empty )
loop = CORD_all ( " if ( " , array_code , " .length > 0) { \n " , loop , " \n } else " , compile_statement ( env , for_ - > empty ) ) ;
} else {
loop = CORD_all ( " { \n "
" array_t iterating = " , compile ( env , array ) , " ; \n " ,
for_ - > empty ? " if (iterating.length > 0) { \n " : CORD_EMPTY ,
loop ,
for_ - > empty ? CORD_all ( " \n } else " , compile_statement ( env , for_ - > empty ) ) : CORD_EMPTY ,
stop ,
" } \n " ) ;
2024-08-10 12:15:38 -07:00
}
return loop ;
}
2024-08-12 14:44:05 -07:00
case SetType : case TableType : {
CORD loop = " for (int64_t i = 0; i < iterating.length; ++i) { \n " ;
2024-07-13 14:17:58 -07:00
if ( for_ - > vars ) {
2024-08-12 14:44:05 -07:00
if ( iter_t - > tag = = SetType ) {
if ( for_ - > vars - > next )
code_err ( for_ - > vars - > next - > ast , " This is too many variables for this loop " ) ;
2024-08-17 11:41:31 -07:00
CORD item = compile ( body_scope , for_ - > vars - > ast ) ;
2024-08-12 14:44:05 -07:00
type_t * item_type = Match ( iter_t , SetType ) - > item_type ;
loop = CORD_all ( loop , compile_declaration ( item_type , item ) , " = *( " , compile_type ( item_type ) , " *)( " ,
" iterating.data + i*iterating.stride); \n " ) ;
2024-07-13 14:17:58 -07:00
} else {
2024-08-17 11:41:31 -07:00
CORD key = compile ( body_scope , for_ - > vars - > ast ) ;
2024-08-12 14:44:05 -07:00
type_t * key_t = Match ( iter_t , TableType ) - > key_type ;
loop = CORD_all ( loop , compile_declaration ( key_t , key ) , " = *( " , compile_type ( key_t ) , " *)( " ,
" iterating.data + i*iterating.stride); \n " ) ;
if ( for_ - > vars - > next ) {
if ( for_ - > vars - > next - > next )
code_err ( for_ - > vars - > next - > next - > ast , " This is too many variables for this loop " ) ;
type_t * value_t = Match ( iter_t , TableType ) - > value_type ;
2024-08-17 11:41:31 -07:00
CORD value = compile ( body_scope , for_ - > vars - > next - > ast ) ;
2024-08-12 14:44:05 -07:00
size_t value_offset = type_size ( key_t ) ;
if ( type_align ( value_t ) > 1 & & value_offset % type_align ( value_t ) )
value_offset + = type_align ( value_t ) - ( value_offset % type_align ( value_t ) ) ; // padding
loop = CORD_all ( loop , compile_declaration ( value_t , value ) , " = *( " , compile_type ( value_t ) , " *)( " ,
" iterating.data + i*iterating.stride + " , heap_strf ( " %zu " , value_offset ) , " ); \n " ) ;
}
2024-07-13 14:17:58 -07:00
}
}
2024-08-12 14:44:05 -07:00
loop = CORD_all ( loop , body , " \n } " ) ;
if ( for_ - > empty ) {
loop = CORD_all ( " if (iterating.length > 0) { \n " , loop , " \n } else " , compile_statement ( env , for_ - > empty ) ) ;
2024-07-13 14:17:58 -07:00
}
2024-08-12 14:44:05 -07:00
if ( can_be_mutated ( env , for_ - > iter ) & & is_idempotent ( for_ - > iter ) ) {
loop = CORD_all (
" { \n " ,
" array_t iterating = ARRAY_COPY(( " , compile ( env , for_ - > iter ) , " ).entries); \n " ,
loop ,
" ARRAY_DECREF(( " , compile ( env , for_ - > iter ) , " ).entries); \n "
" } \n " ) ;
} else {
loop = CORD_all (
" { \n " ,
" array_t iterating = ( " , compile ( env , for_ - > iter ) , " ).entries; \n " ,
loop ,
" } \n " ) ;
2024-03-14 10:28:30 -07:00
}
2024-03-26 11:02:48 -07:00
return loop ;
2024-03-14 10:28:30 -07:00
}
case IntType : {
CORD n = compile ( env , for_ - > iter ) ;
2024-07-13 14:17:58 -07:00
if ( for_ - > empty ) {
2024-03-14 10:28:30 -07:00
return CORD_all (
" { \n "
2024-08-14 11:57:01 -07:00
" int64_t n = Int_to_Int64( " , n , " , false); \n "
2024-04-16 10:50:07 -07:00
" if (n > 0) { \n "
2024-08-12 22:30:25 -07:00
" for (int64_t i = 1; i <= n; ++i) { \n " ,
2024-08-17 11:41:31 -07:00
for_ - > vars ? CORD_all ( " \t Int_t " , compile ( body_scope , for_ - > vars - > ast ) , " = I(i); \n " ) : CORD_EMPTY ,
2024-03-15 10:35:30 -07:00
" \t " , body ,
" \n } "
" \n } else " , compile_statement ( env , for_ - > empty ) ,
2024-03-15 10:38:25 -07:00
stop ,
2024-03-14 10:28:30 -07:00
" \n } " ) ;
} else {
return CORD_all (
2024-08-14 11:57:01 -07:00
" for (int64_t i = 1, n = Int_to_Int64( " , compile ( env , for_ - > iter ) , " , false); i <= n; ++i) { \n " ,
2024-08-17 11:41:31 -07:00
for_ - > vars ? CORD_all ( " \t Int_t " , compile ( body_scope , for_ - > vars - > ast ) , " = I(i); \n " ) : CORD_EMPTY ,
2024-03-15 10:35:30 -07:00
" \t " , body ,
" \n } " ,
2024-03-15 10:38:25 -07:00
stop ,
2024-03-15 10:35:30 -07:00
" \n " ) ;
2024-03-14 10:28:30 -07:00
}
}
2024-07-13 14:17:58 -07:00
case FunctionType : case ClosureType : {
2024-07-20 13:45:13 -07:00
// Iterator function:
2024-07-13 14:17:58 -07:00
CORD code = " { \n " ;
code = CORD_all ( code , compile_declaration ( iter_t , " next " ) , " = " , compile ( env , for_ - > iter ) , " ; \n " ) ;
2024-07-20 13:45:13 -07:00
auto fn = iter_t - > tag = = ClosureType ? Match ( Match ( iter_t , ClosureType ) - > fn , FunctionType ) : Match ( iter_t , FunctionType ) ;
code = CORD_all ( code , compile_declaration ( fn - > ret , " cur " ) , " ; \n " ) ; // Iteration enum
2024-07-13 14:17:58 -07:00
CORD next_fn ;
if ( iter_t - > tag = = ClosureType ) {
type_t * fn_t = Match ( iter_t , ClosureType ) - > fn ;
arg_t * closure_fn_args = NULL ;
for ( arg_t * arg = Match ( fn_t , FunctionType ) - > args ; arg ; arg = arg - > next )
closure_fn_args = new ( arg_t , . name = arg - > name , . type = arg - > type , . default_val = arg - > default_val , . next = closure_fn_args ) ;
closure_fn_args = new ( arg_t , . name = " userdata " , . type = Type ( PointerType , . pointed = Type ( MemoryType ) ) , . next = closure_fn_args ) ;
REVERSE_LIST ( closure_fn_args ) ;
CORD fn_type_code = compile_type ( Type ( FunctionType , . args = closure_fn_args , . ret = Match ( fn_t , FunctionType ) - > ret ) ) ;
2024-07-20 13:45:13 -07:00
next_fn = CORD_all ( " (( " , fn_type_code , " )next.fn) " ) ;
2024-07-13 14:17:58 -07:00
} else {
2024-07-20 13:45:13 -07:00
next_fn = " next " ;
2024-07-13 14:17:58 -07:00
}
2024-07-20 13:45:13 -07:00
env_t * enum_env = Match ( fn - > ret , EnumType ) - > env ;
next_fn = CORD_all ( " (cur= " , next_fn , iter_t - > tag = = ClosureType ? " (next.userdata) " : " () " , " ).$tag == " ,
namespace_prefix ( enum_env - > libname , enum_env - > namespace ) , " tag$Next " ) ;
2024-07-13 15:05:14 -07:00
if ( for_ - > empty ) {
code = CORD_all ( code , " if ( " , next_fn , " ) { \n "
" \t do{ \n \t \t " , body , " \t } while( " , next_fn , " ); \n "
" } else { \n \t " , compile_statement ( env , for_ - > empty ) , " } " , stop , " \n } \n " ) ;
} else {
code = CORD_all ( code , " for(; " , next_fn , " ; ) { \n \t " , body , " } \n " , stop , " \n } \n " ) ;
}
2024-07-13 14:17:58 -07:00
return code ;
}
2024-03-14 10:28:30 -07:00
default : code_err ( for_ - > iter , " Iteration is not implemented for type: %T " , iter_t ) ;
}
}
case If : {
auto if_ = Match ( ast , If ) ;
2024-05-23 21:03:46 -07:00
type_t * cond_t = get_type ( env , if_ - > condition ) ;
if ( cond_t - > tag = = PointerType ) {
if ( ! Match ( cond_t , PointerType ) - > is_optional )
code_err ( if_ - > condition , " This pointer will always be non-null, so it should not be used in a conditional. " ) ;
} else if ( cond_t - > tag ! = BoolType ) {
code_err ( if_ - > condition , " Only boolean values and optional pointers can be used in conditionals (this is a %T) " , cond_t ) ;
2024-03-14 10:28:30 -07:00
}
2024-05-23 21:03:46 -07:00
CORD code ;
CORD_sprintf ( & code , " if (%r) %r " , compile ( env , if_ - > condition ) , compile_statement ( env , if_ - > body ) ) ;
if ( if_ - > else_body )
code = CORD_all ( code , " \n else " , compile_statement ( env , if_ - > else_body ) ) ;
return code ;
2024-03-14 10:28:30 -07:00
}
case Block : {
ast_list_t * stmts = Match ( ast , Block ) - > statements ;
CORD code = " { \n " ;
2024-07-04 15:00:01 -07:00
deferral_t * prev_deferred = env - > deferred ;
2024-03-14 10:28:30 -07:00
env = fresh_scope ( env ) ;
2024-05-18 22:46:30 -07:00
for ( ast_list_t * stmt = stmts ; stmt ; stmt = stmt - > next )
prebind_statement ( env , stmt - > ast ) ;
2024-03-14 10:28:30 -07:00
for ( ast_list_t * stmt = stmts ; stmt ; stmt = stmt - > next ) {
bind_statement ( env , stmt - > ast ) ;
code = CORD_all ( code , compile_statement ( env , stmt - > ast ) , " \n " ) ;
}
2024-07-04 15:00:01 -07:00
for ( deferral_t * deferred = env - > deferred ; deferred & & deferred ! = prev_deferred ; deferred = deferred - > next ) {
code = CORD_all ( code , compile_statement ( deferred - > defer_env , deferred - > block ) ) ;
}
2024-07-04 15:09:33 -07:00
return CORD_cat ( code , " } \n " ) ;
2024-03-14 10:28:30 -07:00
}
2024-03-17 12:26:25 -07:00
case Comprehension : {
auto comp = Match ( ast , Comprehension ) ;
assert ( env - > comprehension_var ) ;
if ( comp - > expr - > tag = = Comprehension ) { // Nested comprehension
ast_t * body = comp - > filter ? WrapAST ( ast , If , . condition = comp - > filter , . body = comp - > expr ) : comp - > expr ;
2024-07-13 14:17:58 -07:00
ast_t * loop = WrapAST ( ast , For , . vars = comp - > vars , . iter = comp - > iter , . body = body ) ;
2024-03-17 12:26:25 -07:00
return compile_statement ( env , loop ) ;
} else if ( comp - > expr - > tag = = TableEntry ) { // Table comprehension
auto e = Match ( comp - > expr , TableEntry ) ;
ast_t * body = WrapAST ( ast , MethodCall , . name = " set " , . self = FakeAST ( StackReference , FakeAST ( Var , env - > comprehension_var ) ) ,
. args = new ( arg_ast_t , . value = e - > key , . next = new ( arg_ast_t , . value = e - > value ) ) ) ;
if ( comp - > filter )
body = WrapAST ( body , If , . condition = comp - > filter , . body = body ) ;
2024-07-13 14:17:58 -07:00
ast_t * loop = WrapAST ( ast , For , . vars = comp - > vars , . iter = comp - > iter , . body = body ) ;
2024-03-17 12:26:25 -07:00
return compile_statement ( env , loop ) ;
} else { // Array comprehension
ast_t * body = WrapAST ( comp - > expr , MethodCall , . name = " insert " , . self = FakeAST ( StackReference , FakeAST ( Var , env - > comprehension_var ) ) ,
. args = new ( arg_ast_t , . value = comp - > expr ) ) ;
if ( comp - > filter )
body = WrapAST ( body , If , . condition = comp - > filter , . body = body ) ;
2024-07-13 14:17:58 -07:00
ast_t * loop = WrapAST ( ast , For , . vars = comp - > vars , . iter = comp - > iter , . body = body ) ;
2024-03-17 12:26:25 -07:00
return compile_statement ( env , loop ) ;
}
}
2024-06-06 13:28:53 -07:00
case Extern : return CORD_EMPTY ;
2024-03-14 10:28:30 -07:00
case InlineCCode : return Match ( ast , InlineCCode ) - > code ;
2024-06-13 10:17:51 -07:00
case Use : case Import : return CORD_EMPTY ;
2024-02-04 15:04:41 -08:00
default :
2024-05-12 10:50:06 -07:00
if ( ! is_discardable ( env , ast ) )
code_err ( ast , " The result of this statement cannot be discarded " ) ;
2024-03-14 10:28:30 -07:00
return CORD_asprintf ( " (void) % r ; " , compile(env, ast));
2024-02-04 15:04:41 -08:00
}
}
2024-04-13 12:20:42 -07:00
// CORD compile_statement(env_t *env, ast_t *ast) {
// CORD stmt = _compile_statement(env, ast);
// if (!stmt)
// return stmt;
// int64_t line = get_line_number(ast->file, ast->start);
// return CORD_asprintf("#line %ld\n%r", line, stmt);
// }
2024-03-09 15:32:36 -08:00
CORD expr_as_text ( env_t * env , CORD expr , type_t * t , CORD color )
2024-02-17 17:47:43 -08:00
{
switch ( t - > tag ) {
2024-05-12 17:12:00 -07:00
case MemoryType : return CORD_asprintf ( " Memory$as_text(stack(%r), %r, &$Memory) " , expr , color ) ;
2024-06-17 14:31:50 -07:00
case BoolType : return CORD_asprintf ( " Bool$as_text((Bool_t[1]){%r}, %r, &$Bool) " , expr , color ) ;
2024-05-18 11:38:41 -07:00
case CStringType : return CORD_asprintf ( " CString$as_text(stack(%r), %r, &$CString) " , expr , color ) ;
2024-03-03 10:37:05 -08:00
case IntType : {
CORD name = type_to_cord ( t ) ;
2024-05-12 17:12:00 -07:00
return CORD_asprintf ( " %r$as_text(stack(%r) , % r , & $ % r ) " , name, expr, color, name) ;
2024-03-03 10:37:05 -08:00
}
case NumType : {
CORD name = type_to_cord ( t ) ;
2024-05-12 17:12:00 -07:00
return CORD_asprintf ( " %r$as_text(stack(%r) , % r , & $ % r ) " , name, expr, color, name) ;
2024-03-03 15:16:33 -08:00
}
2024-03-09 15:32:36 -08:00
case TextType : {
2024-07-04 13:22:52 -07:00
return CORD_asprintf ( " Text$as_text(stack(%r) , % r , % r ) " , expr, color, compile_type_info(env, t)) ;
2024-03-09 15:32:36 -08:00
}
2024-05-12 17:12:00 -07:00
case ArrayType : return CORD_asprintf ( " Array$as_text(stack(%r), %r, %r) " , expr , color , compile_type_info ( env , t ) ) ;
2024-08-10 12:15:38 -07:00
case SetType : return CORD_asprintf ( " Table$as_text(stack(%r), %r, %r) " , expr , color , compile_type_info ( env , t ) ) ;
2024-08-11 11:47:34 -07:00
case ChannelType : return CORD_asprintf ( " Channel$as_text(stack(%r), %r, %r) " , expr , color , compile_type_info ( env , t ) ) ;
2024-05-12 17:12:00 -07:00
case TableType : return CORD_asprintf ( " Table$as_text(stack(%r), %r, %r) " , expr , color , compile_type_info ( env , t ) ) ;
case FunctionType : case ClosureType : return CORD_asprintf ( " Func$as_text(stack(%r), %r, %r) " , expr , color , compile_type_info ( env , t ) ) ;
case PointerType : return CORD_asprintf ( " Pointer$as_text(stack(%r), %r, %r) " , expr , color , compile_type_info ( env , t ) ) ;
2024-05-12 17:13:19 -07:00
case StructType : case EnumType :
2024-05-12 17:12:00 -07:00
return CORD_asprintf ( " (%r)->CustomInfo.as_text(stack(%r), %r, %r) " ,
2024-05-12 10:50:06 -07:00
compile_type_info ( env , t ) , expr , color , compile_type_info ( env , t ) ) ;
2024-02-17 17:47:43 -08:00
default : compiler_err ( NULL , NULL , NULL , " Stringifying is not supported for %T " , t ) ;
}
}
CORD compile_string ( env_t * env , ast_t * ast , CORD color )
{
type_t * t = get_type ( env , ast ) ;
CORD expr = compile ( env , ast ) ;
2024-03-09 15:32:36 -08:00
return expr_as_text ( env , expr , t , color ) ;
2024-02-17 17:47:43 -08:00
}
2024-03-22 10:53:23 -07:00
CORD compile_to_pointer_depth ( env_t * env , ast_t * ast , int64_t target_depth , bool allow_optional )
2024-02-18 11:28:35 -08:00
{
CORD val = compile ( env , ast ) ;
type_t * t = get_type ( env , ast ) ;
int64_t depth = 0 ;
for ( type_t * tt = t ; tt - > tag = = PointerType ; tt = Match ( tt , PointerType ) - > pointed )
+ + depth ;
while ( depth ! = target_depth ) {
if ( depth < target_depth ) {
if ( ast - > tag = = Var & & target_depth = = 1 )
2024-03-03 11:34:13 -08:00
val = CORD_all ( " (& " , val , " ) " ) ;
2024-02-18 11:28:35 -08:00
else
2024-07-22 10:54:03 -07:00
code_err ( ast , " This should be a pointer, not %T " , get_type ( env , ast ) ) ;
2024-02-18 11:28:35 -08:00
t = Type ( PointerType , . pointed = t , . is_stack = true ) ;
+ + depth ;
} else {
auto ptr = Match ( t , PointerType ) ;
if ( ptr - > is_optional )
code_err ( ast , " You can't dereference this value, since it's not guaranteed to be non-null " ) ;
val = CORD_all ( " *( " , val , " ) " ) ;
t = ptr - > pointed ;
- - depth ;
}
}
2024-02-20 10:06:03 -08:00
if ( ! allow_optional ) {
while ( t - > tag = = PointerType ) {
auto ptr = Match ( t , PointerType ) ;
if ( ptr - > is_optional )
code_err ( ast , " You can't dereference this value, since it's not guaranteed to be non-null " ) ;
t = ptr - > pointed ;
}
}
2024-02-18 11:28:35 -08:00
return val ;
}
2024-05-15 10:40:27 -07:00
env_t * with_enum_scope ( env_t * env , type_t * t )
2024-05-15 10:24:43 -07:00
{
if ( t - > tag ! = EnumType ) return env ;
env = fresh_scope ( env ) ;
env_t * ns_env = Match ( t , EnumType ) - > env ;
for ( tag_t * tag = Match ( t , EnumType ) - > tags ; tag ; tag = tag - > next ) {
if ( get_binding ( env , tag - > name ) )
continue ;
2024-05-18 22:46:30 -07:00
binding_t * b = get_binding ( ns_env , tag - > name ) ;
assert ( b ) ;
set_binding ( env , tag - > name , b ) ;
2024-05-15 10:24:43 -07:00
}
return env ;
}
2024-07-13 14:17:58 -07:00
CORD compile_arguments ( env_t * env , ast_t * call_ast , arg_t * spec_args , arg_ast_t * call_args )
2024-03-08 11:23:16 -08:00
{
table_t used_args = { } ;
CORD code = CORD_EMPTY ;
2024-03-09 13:03:38 -08:00
env_t * default_scope = global_scope ( env ) ;
2024-03-08 11:23:16 -08:00
for ( arg_t * spec_arg = spec_args ; spec_arg ; spec_arg = spec_arg - > next ) {
// Find keyword:
if ( spec_arg - > name ) {
for ( arg_ast_t * call_arg = call_args ; call_arg ; call_arg = call_arg - > next ) {
if ( call_arg - > name & & streq ( call_arg - > name , spec_arg - > name ) ) {
2024-08-13 01:24:36 -07:00
CORD value ;
if ( spec_arg - > type - > tag = = NumType & & call_arg - > value - > tag = = Int ) {
Int_t int_val = Int $ from_text ( Match ( call_arg - > value , Int ) - > str ) ;
2024-08-14 11:57:01 -07:00
double n = Int_to_Num ( int_val ) ;
2024-08-13 01:24:36 -07:00
value = CORD_asprintf ( Match ( spec_arg - > type , NumType ) - > bits = = 64
? " N64(%.9g) " : " N32(%.9g) " , n ) ;
} else {
env_t * arg_env = with_enum_scope ( env , spec_arg - > type ) ;
type_t * actual_t = get_type ( arg_env , call_arg - > value ) ;
value = compile_maybe_incref ( arg_env , call_arg - > value ) ;
if ( ! promote ( arg_env , & value , actual_t , spec_arg - > type ) )
code_err ( call_arg - > value , " This argument is supposed to be a %T, but this value is a %T " , spec_arg - > type , actual_t ) ;
}
2024-03-29 09:54:31 -07:00
Table $ str_set ( & used_args , call_arg - > name , call_arg ) ;
2024-03-08 11:23:16 -08:00
if ( code ) code = CORD_cat ( code , " , " ) ;
2024-03-09 13:03:38 -08:00
code = CORD_cat ( code , value ) ;
2024-03-08 11:23:16 -08:00
goto found_it ;
}
}
}
// Find positional:
int64_t i = 1 ;
for ( arg_ast_t * call_arg = call_args ; call_arg ; call_arg = call_arg - > next ) {
if ( call_arg - > name ) continue ;
const char * pseudoname = heap_strf ( " %ld " , i + + ) ;
2024-03-29 09:54:31 -07:00
if ( ! Table $ str_get ( used_args , pseudoname ) ) {
2024-08-13 01:24:36 -07:00
CORD value ;
if ( spec_arg - > type - > tag = = NumType & & call_arg - > value - > tag = = Int ) {
Int_t int_val = Int $ from_text ( Match ( call_arg - > value , Int ) - > str ) ;
2024-08-14 11:57:01 -07:00
double n = Int_to_Num ( int_val ) ;
2024-08-13 01:24:36 -07:00
value = CORD_asprintf ( Match ( spec_arg - > type , NumType ) - > bits = = 64
? " N64(%.9g) " : " N32(%.9g) " , n ) ;
} else {
env_t * arg_env = with_enum_scope ( env , spec_arg - > type ) ;
type_t * actual_t = get_type ( arg_env , call_arg - > value ) ;
value = compile_maybe_incref ( arg_env , call_arg - > value ) ;
if ( ! promote ( arg_env , & value , actual_t , spec_arg - > type ) )
code_err ( call_arg - > value , " This argument is supposed to be a %T, but this value is a %T " , spec_arg - > type , actual_t ) ;
}
2024-03-29 09:54:31 -07:00
Table $ str_set ( & used_args , pseudoname , call_arg ) ;
2024-03-08 11:23:16 -08:00
if ( code ) code = CORD_cat ( code , " , " ) ;
2024-03-09 13:03:38 -08:00
code = CORD_cat ( code , value ) ;
2024-03-08 11:23:16 -08:00
goto found_it ;
}
}
if ( spec_arg - > default_val ) {
if ( code ) code = CORD_cat ( code , " , " ) ;
2024-08-12 14:44:05 -07:00
code = CORD_cat ( code , compile_maybe_incref ( default_scope , spec_arg - > default_val ) ) ;
2024-03-08 11:23:16 -08:00
goto found_it ;
}
assert ( spec_arg - > name ) ;
code_err ( call_ast , " The required argument '%s' was not provided " , spec_arg - > name ) ;
found_it : continue ;
}
int64_t i = 1 ;
for ( arg_ast_t * call_arg = call_args ; call_arg ; call_arg = call_arg - > next ) {
if ( call_arg - > name ) {
2024-03-29 09:54:31 -07:00
if ( ! Table $ str_get ( used_args , call_arg - > name ) )
2024-03-08 11:23:16 -08:00
code_err ( call_arg - > value , " There is no argument with the name '%s' " , call_arg - > name ) ;
} else {
const char * pseudoname = heap_strf ( " %ld " , i + + ) ;
2024-03-29 09:54:31 -07:00
if ( ! Table $ str_get ( used_args , pseudoname ) )
2024-03-08 11:23:16 -08:00
code_err ( call_arg - > value , " This is one argument too many! " ) ;
}
}
return code ;
}
2024-08-10 17:50:15 -07:00
CORD compile_math_method ( env_t * env , binop_e op , ast_t * lhs , ast_t * rhs , type_t * required_type )
2024-07-01 08:12:00 -07:00
{
2024-08-10 17:50:15 -07:00
// Math methods are things like plus(), minus(), etc. If we don't find a
2024-07-01 08:12:00 -07:00
// matching method, return CORD_EMPTY.
const char * method_name = binop_method_names [ op ] ;
if ( ! method_name )
return CORD_EMPTY ;
type_t * lhs_t = get_type ( env , lhs ) ;
type_t * rhs_t = get_type ( env , rhs ) ;
2024-08-10 17:50:15 -07:00
# define binding_works(b, lhs_t, rhs_t, ret_t) \
( b & & b - > type - > tag = = FunctionType & & ( { auto fn = Match ( b - > type , FunctionType ) ; \
( type_eq ( fn - > ret , ret_t ) \
& & ( fn - > args & & type_eq ( fn - > args - > type , lhs_t ) ) \
& & ( fn - > args - > next & & can_promote ( fn - > args - > next - > type , rhs_t ) ) \
& & ( ! required_type | | type_eq ( required_type , fn - > ret ) ) ) ; } ) )
switch ( op ) {
case BINOP_MULT : {
2024-08-12 22:30:25 -07:00
if ( type_eq ( lhs_t , rhs_t ) ) {
binding_t * b = get_namespace_binding ( env , lhs , binop_method_names [ op ] ) ;
if ( binding_works ( b , lhs_t , rhs_t , lhs_t ) )
return CORD_all ( b - > code , " ( " , compile ( env , lhs ) , " , " , compile ( env , rhs ) , " ) " ) ;
} else if ( lhs_t - > tag = = NumType | | lhs_t - > tag = = IntType ) {
2024-08-10 17:50:15 -07:00
binding_t * b = get_namespace_binding ( env , rhs , " scaled_by " ) ;
if ( binding_works ( b , rhs_t , lhs_t , rhs_t ) )
return CORD_all ( b - > code , " ( " , compile ( env , rhs ) , " , " , compile ( env , lhs ) , " ) " ) ;
} else if ( rhs_t - > tag = = NumType | | rhs_t - > tag = = IntType ) {
binding_t * b = get_namespace_binding ( env , lhs , " scaled_by " ) ;
if ( binding_works ( b , lhs_t , rhs_t , lhs_t ) )
return CORD_all ( b - > code , " ( " , compile ( env , lhs ) , " , " , compile ( env , rhs ) , " ) " ) ;
2024-07-01 08:12:00 -07:00
}
2024-08-10 17:50:15 -07:00
break ;
}
case BINOP_PLUS : case BINOP_MINUS : case BINOP_AND : case BINOP_OR : case BINOP_XOR : {
if ( type_eq ( lhs_t , rhs_t ) ) {
binding_t * b = get_namespace_binding ( env , lhs , binop_method_names [ op ] ) ;
if ( binding_works ( b , lhs_t , rhs_t , lhs_t ) )
return CORD_all ( b - > code , " ( " , compile ( env , lhs ) , " , " , compile ( env , rhs ) , " ) " ) ;
2024-07-01 08:12:00 -07:00
}
2024-08-10 17:50:15 -07:00
break ;
}
case BINOP_DIVIDE : case BINOP_MOD : case BINOP_MOD1 : {
if ( rhs_t - > tag = = NumType | | rhs_t - > tag = = IntType ) {
binding_t * b = get_namespace_binding ( env , lhs , binop_method_names [ op ] ) ;
if ( binding_works ( b , lhs_t , rhs_t , lhs_t ) )
return CORD_all ( b - > code , " ( " , compile ( env , lhs ) , " , " , compile ( env , rhs ) , " ) " ) ;
}
break ;
}
case BINOP_LSHIFT : case BINOP_RSHIFT : {
if ( rhs_t - > tag = = IntType ) {
binding_t * b = get_namespace_binding ( env , lhs , binop_method_names [ op ] ) ;
if ( binding_works ( b , lhs_t , rhs_t , lhs_t ) )
return CORD_all ( b - > code , " ( " , compile ( env , lhs ) , " , " , compile ( env , rhs ) , " ) " ) ;
}
break ;
}
case BINOP_POWER : {
2024-08-13 13:32:00 -07:00
if ( rhs_t - > tag = = NumType | | rhs_t - > tag = = IntType ) {
2024-08-10 17:50:15 -07:00
binding_t * b = get_namespace_binding ( env , lhs , binop_method_names [ op ] ) ;
if ( binding_works ( b , lhs_t , rhs_t , lhs_t ) )
return CORD_all ( b - > code , " ( " , compile ( env , lhs ) , " , " , compile ( env , rhs ) , " ) " ) ;
}
break ;
}
default : break ;
2024-07-01 08:12:00 -07:00
}
return CORD_EMPTY ;
}
2024-02-13 11:42:33 -08:00
CORD compile ( env_t * env , ast_t * ast )
2024-02-04 12:23:59 -08:00
{
switch ( ast - > tag ) {
2024-04-12 10:09:31 -07:00
case Nil : {
type_t * t = parse_type_ast ( env , Match ( ast , Nil ) - > type ) ;
2024-06-16 15:09:54 -07:00
return CORD_all ( " (( " , compile_type ( t ) , " )NULL) " ) ;
2024-04-12 10:09:31 -07:00
}
2024-02-12 00:00:31 -08:00
case Bool : return Match ( ast , Bool ) - > b ? " yes " : " no " ;
2024-02-29 10:28:39 -08:00
case Var : {
binding_t * b = get_binding ( env , Match ( ast , Var ) - > name ) ;
if ( b )
2024-04-16 10:50:07 -07:00
return b - > code ? b - > code : CORD_cat ( " $ " , Match ( ast , Var ) - > name ) ;
return CORD_cat ( " $ " , Match ( ast , Var ) - > name ) ;
2024-02-29 10:49:24 -08:00
// code_err(ast, "I don't know of any variable by this name");
2024-02-29 10:28:39 -08:00
}
2024-08-12 22:30:25 -07:00
case Int : {
const char * str = Match ( ast , Int ) - > str ;
Int_t int_val = Int $ from_text ( str ) ;
mpz_t i ;
mpz_init_set_int ( i , int_val ) ;
switch ( Match ( ast , Int ) - > bits ) {
case 0 :
2024-08-13 11:13:02 -07:00
if ( mpz_cmpabs_ui ( i , BIGGEST_SMALL_INT ) < = 0 ) {
2024-08-17 12:17:41 -07:00
return CORD_asprintf ( " I_small(%s) " , str) ;
2024-08-13 11:13:02 -07:00
} else if ( mpz_cmp_si ( i , INT64_MAX ) < = 0 & & mpz_cmp_si ( i , INT64_MIN ) > = 0 ) {
2024-08-14 11:57:01 -07:00
return CORD_asprintf ( " Int64_to_Int(%s) " , str) ;
2024-08-12 22:30:25 -07:00
} else {
return CORD_asprintf ( " Int$from_text( \" %s \" ) " , str) ;
}
case 64 :
if ( ( mpz_cmp_si ( i , INT64_MAX ) < 0 ) & & ( mpz_cmp_si ( i , INT64_MIN ) > 0 ) )
return CORD_asprintf ( " I64(%s) " , str) ;
code_err ( ast , " This value cannot fit in a 64-bit integer " ) ;
case 32 :
if ( ( mpz_cmp_si ( i , INT32_MAX ) < 0 ) & & ( mpz_cmp_si ( i , INT32_MIN ) > 0 ) )
return CORD_asprintf ( " I32(%s) " , str) ;
code_err ( ast , " This value cannot fit in a 32-bit integer " ) ;
case 16 :
if ( ( mpz_cmp_si ( i , INT16_MAX ) < 0 ) & & ( mpz_cmp_si ( i , INT16_MIN ) > 0 ) )
return CORD_asprintf ( " I16(%s) " , str) ;
code_err ( ast , " This value cannot fit in a 16-bit integer " ) ;
case 8 :
if ( ( mpz_cmp_si ( i , INT8_MAX ) < 0 ) & & ( mpz_cmp_si ( i , INT8_MIN ) > 0 ) )
return CORD_asprintf ( " I8(%s) " , str) ;
code_err ( ast , " This value cannot fit in a 8-bit integer " ) ;
default : code_err ( ast , " Not a valid integer bit width " ) ;
}
}
2024-02-13 20:37:24 -08:00
case Num : {
2024-08-10 13:03:00 -07:00
return CORD_asprintf ( Match ( ast , Num ) - > bits = = 64 ? " N64(%.9g) " : " N32(%.9g) " , Match ( ast , Num ) - > n ) ;
2024-02-13 20:37:24 -08:00
}
2024-02-17 22:27:25 -08:00
case Length : {
ast_t * expr = Match ( ast , Length ) - > value ;
type_t * t = get_type ( env , expr ) ;
2024-02-18 11:28:35 -08:00
switch ( value_type ( t ) - > tag ) {
2024-03-03 15:15:45 -08:00
case TextType : {
2024-02-20 10:06:03 -08:00
CORD str = compile_to_pointer_depth ( env , expr , 0 , false ) ;
2024-03-29 09:54:31 -07:00
return CORD_all ( " Text$num_clusters( " , str , " ) " ) ;
2024-02-18 11:28:35 -08:00
}
case ArrayType : {
if ( t - > tag = = PointerType ) {
2024-02-20 10:06:03 -08:00
CORD arr = compile_to_pointer_depth ( env , expr , 1 , false ) ;
2024-08-12 22:30:25 -07:00
return CORD_all ( " I(( " , arr , " ) - > length ) " ) ;
2024-02-18 11:28:35 -08:00
} else {
2024-02-20 10:06:03 -08:00
CORD arr = compile_to_pointer_depth ( env , expr , 0 , false ) ;
2024-08-12 22:30:25 -07:00
return CORD_all ( " I(( " , arr , " ) . length ) " ) ;
2024-02-18 11:28:35 -08:00
}
}
case TableType : {
if ( t - > tag = = PointerType ) {
2024-02-20 10:06:03 -08:00
CORD table = compile_to_pointer_depth ( env , expr , 1 , false ) ;
2024-08-12 22:30:25 -07:00
return CORD_all ( " I(( " , table , " ) - > entries . length ) " ) ;
2024-02-18 11:28:35 -08:00
} else {
2024-02-20 10:06:03 -08:00
CORD table = compile_to_pointer_depth ( env , expr , 0 , false ) ;
2024-08-12 22:30:25 -07:00
return CORD_all ( " I(( " , table , " ) . entries . length ) " ) ;
2024-02-18 11:28:35 -08:00
}
2024-02-17 22:27:25 -08:00
}
2024-07-04 10:37:23 -07:00
default : {
code_err ( ast , " Length is not implemented for %T values " , t ) ;
}
2024-02-17 22:27:25 -08:00
}
break ;
}
2024-03-14 10:47:40 -07:00
case Not : {
ast_t * value = Match ( ast , Not ) - > value ;
2024-08-12 22:30:25 -07:00
type_t * t = get_type ( env , ast ) ;
binding_t * b = get_namespace_binding ( env , value , " negated " ) ;
if ( b & & b - > type - > tag = = FunctionType ) {
auto fn = Match ( b - > type , FunctionType ) ;
if ( fn - > args & & can_promote ( t , get_arg_type ( env , fn - > args ) ) )
return CORD_all ( b - > code , " ( " , compile_arguments ( env , ast , fn - > args , new ( arg_ast_t , . value = value ) ) , " ) " ) ;
}
2024-03-14 10:47:40 -07:00
if ( t - > tag = = BoolType )
return CORD_all ( " !( " , compile ( env , value ) , " ) " ) ;
else if ( t - > tag = = IntType )
return CORD_all ( " ~( " , compile ( env , value ) , " ) " ) ;
else if ( t - > tag = = ArrayType | | t - > tag = = TableType )
return CORD_all ( " !( " , compile ( env , WrapAST ( ast , Length , value ) ) , " ) " ) ;
else if ( t - > tag = = TextType )
return CORD_all ( " !( " , compile ( env , value ) , " ) " ) ;
2024-08-10 17:50:15 -07:00
code_err ( ast , " I don't know how to negate values of type %T " , t ) ;
2024-03-14 10:47:40 -07:00
}
2024-07-04 10:37:23 -07:00
case Negative : {
ast_t * value = Match ( ast , Negative ) - > value ;
type_t * t = get_type ( env , value ) ;
2024-08-10 17:50:15 -07:00
binding_t * b = get_namespace_binding ( env , value , " negative " ) ;
2024-07-04 10:37:23 -07:00
if ( b & & b - > type - > tag = = FunctionType ) {
auto fn = Match ( b - > type , FunctionType ) ;
if ( fn - > args & & can_promote ( t , get_arg_type ( env , fn - > args ) ) )
return CORD_all ( b - > code , " ( " , compile_arguments ( env , ast , fn - > args , new ( arg_ast_t , . value = value ) ) , " ) " ) ;
}
2024-08-12 22:30:25 -07:00
if ( t - > tag = = IntType | | t - > tag = = NumType )
return CORD_all ( " -( " , compile ( env , value ) , " ) " ) ;
2024-07-04 10:37:23 -07:00
code_err ( ast , " I don't know how to get the negative value of type %T " , t ) ;
}
2024-04-16 10:50:07 -07:00
case HeapAllocate : return CORD_asprintf ( " heap(%r) " , compile ( env , Match ( ast , HeapAllocate ) - > value ) ) ;
2024-02-25 09:24:38 -08:00
case StackReference : {
2024-02-25 10:04:35 -08:00
ast_t * subject = Match ( ast , StackReference ) - > value ;
if ( can_be_mutated ( env , subject ) )
2024-07-22 10:54:03 -07:00
return CORD_all ( " (& " , compile_lvalue ( env , subject ) , " ) " ) ;
else
code_err ( subject , " This subject can't be mutated! " ) ;
2024-02-25 09:24:38 -08:00
}
2024-04-30 10:18:47 -07:00
case Optional : {
return compile ( env , Match ( ast , Optional ) - > value ) ;
}
2024-02-04 12:23:59 -08:00
case BinaryOp : {
auto binop = Match ( ast , BinaryOp ) ;
2024-08-10 17:50:15 -07:00
CORD method_call = compile_math_method ( env , binop - > op , binop - > lhs , binop - > rhs , NULL ) ;
2024-07-01 08:12:00 -07:00
if ( method_call ! = CORD_EMPTY )
return method_call ;
2024-02-13 11:42:33 -08:00
CORD lhs = compile ( env , binop - > lhs ) ;
CORD rhs = compile ( env , binop - > rhs ) ;
2024-02-17 17:21:01 -08:00
2024-02-22 10:18:47 -08:00
type_t * lhs_t = get_type ( env , binop - > lhs ) ;
type_t * rhs_t = get_type ( env , binop - > rhs ) ;
type_t * operand_t ;
2024-03-09 13:03:38 -08:00
if ( promote ( env , & rhs , rhs_t , lhs_t ) )
2024-02-22 10:18:47 -08:00
operand_t = lhs_t ;
2024-03-09 13:03:38 -08:00
else if ( promote ( env , & lhs , lhs_t , rhs_t ) )
2024-02-22 10:18:47 -08:00
operand_t = rhs_t ;
else
2024-02-22 10:26:43 -08:00
code_err ( ast , " I can't do operations between %T and %T " , lhs_t , rhs_t ) ;
2024-02-17 17:21:01 -08:00
2024-02-04 12:23:59 -08:00
switch ( binop - > op ) {
2024-02-22 10:09:46 -08:00
case BINOP_POWER : {
2024-08-12 23:09:18 -07:00
if ( operand_t - > tag ! = NumType )
code_err ( ast , " Exponentiation is only supported for Num types " ) ;
2024-02-22 10:18:47 -08:00
if ( operand_t - > tag = = NumType & & Match ( operand_t , NumType ) - > bits = = 32 )
2024-02-22 10:09:46 -08:00
return CORD_all ( " powf( " , lhs , " , " , rhs , " ) " ) ;
else
return CORD_all ( " pow( " , lhs , " , " , rhs , " ) " ) ;
}
2024-02-17 17:25:31 -08:00
case BINOP_MULT : {
2024-02-22 10:18:47 -08:00
if ( operand_t - > tag ! = IntType & & operand_t - > tag ! = NumType )
2024-02-17 17:25:31 -08:00
code_err ( ast , " Math operations are only supported for numeric types " ) ;
2024-08-10 17:40:41 -07:00
return CORD_all ( " ( " , lhs , " * " , rhs , " ) " ) ;
2024-02-17 17:25:31 -08:00
}
case BINOP_DIVIDE : {
2024-02-22 10:18:47 -08:00
if ( operand_t - > tag ! = IntType & & operand_t - > tag ! = NumType )
2024-02-17 17:25:31 -08:00
code_err ( ast , " Math operations are only supported for numeric types " ) ;
2024-08-10 17:40:41 -07:00
return CORD_all ( " ( " , lhs , " / " , rhs , " ) " ) ;
2024-02-17 17:25:31 -08:00
}
case BINOP_MOD : {
2024-02-22 10:18:47 -08:00
if ( operand_t - > tag ! = IntType & & operand_t - > tag ! = NumType )
2024-02-17 17:25:31 -08:00
code_err ( ast , " Math operations are only supported for numeric types " ) ;
2024-08-10 17:40:41 -07:00
return CORD_all ( " ( " , lhs , " % " , rhs , " ) " ) ;
2024-02-17 17:25:31 -08:00
}
case BINOP_MOD1 : {
2024-02-22 10:18:47 -08:00
if ( operand_t - > tag ! = IntType & & operand_t - > tag ! = NumType )
2024-02-17 17:25:31 -08:00
code_err ( ast , " Math operations are only supported for numeric types " ) ;
2024-08-10 17:40:41 -07:00
return CORD_all ( " (((( " , lhs , " ) - 1 ) % ( " , rhs, " ) ) + 1 ) " ) ;
2024-02-17 17:25:31 -08:00
}
case BINOP_PLUS : {
2024-02-22 10:18:47 -08:00
if ( operand_t - > tag ! = IntType & & operand_t - > tag ! = NumType )
2024-02-17 17:25:31 -08:00
code_err ( ast , " Math operations are only supported for numeric types " ) ;
2024-08-10 17:40:41 -07:00
return CORD_all ( " ( " , lhs , " + " , rhs , " ) " ) ;
2024-02-17 17:25:31 -08:00
}
case BINOP_MINUS : {
2024-02-22 10:18:47 -08:00
if ( operand_t - > tag ! = IntType & & operand_t - > tag ! = NumType )
2024-02-17 17:25:31 -08:00
code_err ( ast , " Math operations are only supported for numeric types " ) ;
2024-08-10 17:40:41 -07:00
return CORD_all ( " ( " , lhs , " - " , rhs , " ) " ) ;
2024-02-17 17:25:31 -08:00
}
case BINOP_LSHIFT : {
2024-02-22 10:18:47 -08:00
if ( operand_t - > tag ! = IntType & & operand_t - > tag ! = NumType )
2024-02-17 17:25:31 -08:00
code_err ( ast , " Math operations are only supported for numeric types " ) ;
2024-08-10 17:40:41 -07:00
return CORD_all ( " ( " , lhs , " << " , rhs , " ) " ) ;
2024-02-17 17:25:31 -08:00
}
case BINOP_RSHIFT : {
2024-02-22 10:18:47 -08:00
if ( operand_t - > tag ! = IntType & & operand_t - > tag ! = NumType )
2024-02-17 17:25:31 -08:00
code_err ( ast , " Math operations are only supported for numeric types " ) ;
2024-08-10 17:40:41 -07:00
return CORD_all ( " ( " , lhs , " >> " , rhs , " ) " ) ;
2024-02-17 17:25:31 -08:00
}
2024-02-17 17:21:01 -08:00
case BINOP_EQ : {
2024-02-22 10:18:47 -08:00
switch ( operand_t - > tag ) {
2024-08-12 22:30:25 -07:00
case IntType :
if ( Match ( operand_t , IntType ) - > bits = = 0 )
return CORD_all ( " Int$equal_value( " , lhs , " , " , rhs , " ) " ) ;
return CORD_all ( " ( " , lhs , " == " , rhs , " ) " ) ;
case BoolType : case NumType : case PointerType : case FunctionType :
2024-08-10 17:40:41 -07:00
return CORD_all ( " ( " , lhs , " == " , rhs , " ) " ) ;
2024-02-17 17:21:01 -08:00
default :
2024-05-12 17:12:00 -07:00
return CORD_asprintf ( " generic_equal(stack(%r) , stack ( % r ) , % r ) " , lhs, rhs, compile_type_info(env, operand_t)) ;
2024-02-17 17:21:01 -08:00
}
}
case BINOP_NE : {
2024-02-22 10:18:47 -08:00
switch ( operand_t - > tag ) {
2024-08-12 22:30:25 -07:00
case IntType :
if ( Match ( operand_t , IntType ) - > bits = = 0 )
return CORD_all ( " !Int$equal_value( " , lhs , " , " , rhs , " ) " ) ;
return CORD_all ( " ( " , lhs , " != " , rhs , " ) " ) ;
case BoolType : case NumType : case PointerType : case FunctionType :
2024-08-10 17:40:41 -07:00
return CORD_all ( " ( " , lhs , " != " , rhs , " ) " ) ;
2024-02-17 17:21:01 -08:00
default :
2024-05-12 17:12:00 -07:00
return CORD_asprintf ( " !generic_equal(stack(%r) , stack ( % r ) , % r ) " , lhs, rhs, compile_type_info(env, operand_t)) ;
2024-02-17 17:21:01 -08:00
}
}
case BINOP_LT : {
2024-02-22 10:18:47 -08:00
switch ( operand_t - > tag ) {
2024-08-12 22:30:25 -07:00
case IntType :
if ( Match ( operand_t , IntType ) - > bits = = 0 )
return CORD_all ( " (Int$compare_value( " , lhs , " , " , rhs , " ) < 0 ) " ) ;
return CORD_all ( " ( " , lhs , " != " , rhs , " ) " ) ;
case BoolType : case NumType : case PointerType : case FunctionType :
2024-08-10 17:40:41 -07:00
return CORD_all ( " ( " , lhs , " < " , rhs , " ) " ) ;
2024-02-17 17:21:01 -08:00
default :
2024-05-12 17:12:00 -07:00
return CORD_asprintf ( " (generic_compare(stack(%r) , stack ( % r ) , % r ) < 0 ) " , lhs, rhs, compile_type_info(env, operand_t)) ;
2024-02-17 17:21:01 -08:00
}
}
case BINOP_LE : {
2024-02-22 10:18:47 -08:00
switch ( operand_t - > tag ) {
2024-08-12 22:30:25 -07:00
case IntType :
if ( Match ( operand_t , IntType ) - > bits = = 0 )
return CORD_all ( " (Int$compare_value( " , lhs , " , " , rhs , " ) < = 0 ) " ) ;
return CORD_all ( " ( " , lhs , " != " , rhs , " ) " ) ;
case BoolType : case NumType : case PointerType : case FunctionType :
2024-08-10 17:40:41 -07:00
return CORD_all ( " ( " , lhs , " <= " , rhs , " ) " ) ;
2024-02-17 17:21:01 -08:00
default :
2024-05-12 17:12:00 -07:00
return CORD_asprintf ( " (generic_compare(stack(%r) , stack ( % r ) , % r ) < = 0 ) " , lhs, rhs, compile_type_info(env, operand_t)) ;
2024-02-17 17:21:01 -08:00
}
}
case BINOP_GT : {
2024-02-22 10:18:47 -08:00
switch ( operand_t - > tag ) {
2024-08-12 22:30:25 -07:00
case IntType :
if ( Match ( operand_t , IntType ) - > bits = = 0 )
return CORD_all ( " (Int$compare_value( " , lhs , " , " , rhs , " ) > 0 ) " ) ;
return CORD_all ( " ( " , lhs , " != " , rhs , " ) " ) ;
case BoolType : case NumType : case PointerType : case FunctionType :
2024-08-10 17:40:41 -07:00
return CORD_all ( " ( " , lhs , " > " , rhs , " ) " ) ;
2024-02-17 17:21:01 -08:00
default :
2024-05-12 17:12:00 -07:00
return CORD_asprintf ( " (generic_compare(stack(%r) , stack ( % r ) , % r ) > 0 ) " , lhs, rhs, compile_type_info(env, operand_t)) ;
2024-02-17 17:21:01 -08:00
}
}
case BINOP_GE : {
2024-02-22 10:18:47 -08:00
switch ( operand_t - > tag ) {
2024-08-12 22:30:25 -07:00
case IntType :
if ( Match ( operand_t , IntType ) - > bits = = 0 )
return CORD_all ( " (Int$compare_value( " , lhs , " , " , rhs , " ) > = 0 ) " ) ;
return CORD_all ( " ( " , lhs , " != " , rhs , " ) " ) ;
case BoolType : case NumType : case PointerType : case FunctionType :
2024-08-10 17:40:41 -07:00
return CORD_all ( " ( " , lhs , " >= " , rhs , " ) " ) ;
2024-02-17 17:21:01 -08:00
default :
2024-05-12 17:12:00 -07:00
return CORD_asprintf ( " (generic_compare(stack(%r) , stack ( % r ) , % r ) > = 0 ) " , lhs, rhs, compile_type_info(env, operand_t)) ;
2024-02-17 17:21:01 -08:00
}
}
2024-02-17 17:25:31 -08:00
case BINOP_AND : {
2024-02-22 10:18:47 -08:00
if ( operand_t - > tag = = BoolType )
2024-08-10 17:40:41 -07:00
return CORD_all ( " ( " , lhs , " && " , rhs , " ) " ) ;
2024-02-22 10:18:47 -08:00
else if ( operand_t - > tag = = IntType )
2024-08-10 17:40:41 -07:00
return CORD_all ( " ( " , lhs , " & " , rhs , " ) " ) ;
2024-02-17 17:25:31 -08:00
else
code_err ( ast , " Boolean operators are only supported for Bool and integer types " ) ;
}
2024-04-02 10:08:06 -07:00
case BINOP_CMP : {
2024-05-12 17:12:00 -07:00
return CORD_all ( " generic_compare(stack( " , lhs , " ) , stack ( " , rhs, " ) , " , compile_type_info(env, operand_t), " ) " ) ;
2024-04-02 10:08:06 -07:00
}
2024-02-17 17:25:31 -08:00
case BINOP_OR : {
2024-02-22 10:18:47 -08:00
if ( operand_t - > tag = = BoolType )
2024-08-10 17:40:41 -07:00
return CORD_all ( " ( " , lhs , " || " , rhs , " ) " ) ;
2024-02-22 10:18:47 -08:00
else if ( operand_t - > tag = = IntType )
2024-08-10 17:40:41 -07:00
return CORD_all ( " ( " , lhs , " | " , rhs , " ) " ) ;
2024-02-17 17:25:31 -08:00
else
code_err ( ast , " Boolean operators are only supported for Bool and integer types " ) ;
}
2024-03-14 10:28:30 -07:00
case BINOP_XOR : {
if ( operand_t - > tag = = BoolType | | operand_t - > tag = = IntType )
2024-08-10 17:40:41 -07:00
return CORD_all ( " ( " , lhs , " ^ " , rhs , " ) " ) ;
2024-03-14 10:28:30 -07:00
else
code_err ( ast , " Boolean operators are only supported for Bool and integer types " ) ;
}
case BINOP_CONCAT : {
switch ( operand_t - > tag ) {
case TextType : {
return CORD_all ( " CORD_cat( " , lhs , " , " , rhs , " ) " ) ;
}
case ArrayType : {
2024-08-03 12:33:50 -07:00
CORD padded_item_size = CORD_asprintf ( " %ld " , padded_type_size ( Match ( operand_t , ArrayType ) - > item_type ) ) ;
return CORD_all ( " Array$concat( " , lhs , " , " , rhs , " , " , padded_item_size , " ) " ) ;
2024-03-14 10:28:30 -07:00
}
default :
code_err ( ast , " Concatenation isn't supported for %T types " , operand_t ) ;
2024-02-23 10:29:20 -08:00
}
2024-02-23 10:24:06 -08:00
}
2024-03-14 10:28:30 -07:00
default : break ;
2024-02-04 12:23:59 -08:00
}
2024-03-14 10:28:30 -07:00
code_err ( ast , " unimplemented binop " ) ;
2024-02-04 12:23:59 -08:00
}
2024-03-03 15:15:45 -08:00
case TextLiteral : {
CORD literal = Match ( ast , TextLiteral ) - > cord ;
2024-02-11 12:31:30 -08:00
if ( literal = = CORD_EMPTY )
2024-02-11 12:22:32 -08:00
return " (CORD)CORD_EMPTY " ;
2024-02-24 11:36:08 -08:00
CORD code = " (CORD) \" " ;
2024-02-11 12:31:30 -08:00
CORD_pos i ;
CORD_FOR ( i , literal ) {
2024-02-16 10:29:02 -08:00
char c = CORD_pos_fetch ( i ) ;
2024-02-11 12:31:30 -08:00
switch ( c ) {
2024-02-04 15:04:41 -08:00
case ' \\ ' : code = CORD_cat ( code , " \\ \\ " ) ; break ;
case ' " ' : code = CORD_cat ( code , " \\ \" " ) ; break ;
case ' \a ' : code = CORD_cat ( code , " \\ a " ) ; break ;
case ' \b ' : code = CORD_cat ( code , " \\ b " ) ; break ;
case ' \n ' : code = CORD_cat ( code , " \\ n " ) ; break ;
case ' \r ' : code = CORD_cat ( code , " \\ r " ) ; break ;
case ' \t ' : code = CORD_cat ( code , " \\ t " ) ; break ;
case ' \v ' : code = CORD_cat ( code , " \\ v " ) ; break ;
2024-02-04 12:23:59 -08:00
default : {
2024-02-11 12:31:30 -08:00
if ( isprint ( c ) )
code = CORD_cat_char ( code , c ) ;
2024-02-04 12:23:59 -08:00
else
2024-03-03 14:49:40 -08:00
CORD_sprintf ( & code , " %r \\ x%02X " , code , ( uint8_t ) c ) ;
2024-02-04 12:23:59 -08:00
break ;
}
}
}
2024-02-04 15:04:41 -08:00
return CORD_cat_char ( code , ' " ' ) ;
2024-02-04 12:23:59 -08:00
}
2024-03-03 15:15:45 -08:00
case TextJoin : {
2024-03-09 15:22:12 -08:00
const char * lang = Match ( ast , TextJoin ) - > lang ;
2024-03-29 09:54:31 -07:00
type_t * text_t = Table $ str_get ( * env - > types , lang ? lang : " Text " ) ;
2024-03-09 15:47:56 -08:00
if ( ! text_t | | text_t - > tag ! = TextType )
code_err ( ast , " %s is not a valid text language name " , lang ) ;
2024-03-21 10:33:10 -07:00
env_t * lang_env = lang ? Match ( get_binding ( env , lang ) - > type , TypeInfoType ) - > env : NULL ;
2024-03-03 15:15:45 -08:00
ast_list_t * chunks = Match ( ast , TextJoin ) - > children ;
2024-02-10 12:33:35 -08:00
if ( ! chunks ) {
2024-02-11 12:22:32 -08:00
return " (CORD)CORD_EMPTY " ;
2024-03-09 15:22:12 -08:00
} else if ( ! chunks - > next & & chunks - > ast - > tag = = TextLiteral ) {
return compile ( env , chunks - > ast ) ;
2024-02-10 12:33:35 -08:00
} else {
2024-02-18 01:26:26 -08:00
CORD code = " CORD_all( " ;
2024-02-10 12:33:35 -08:00
for ( ast_list_t * chunk = chunks ; chunk ; chunk = chunk - > next ) {
2024-03-09 15:22:12 -08:00
CORD chunk_code ;
2024-02-18 01:26:26 -08:00
type_t * chunk_t = get_type ( env , chunk - > ast ) ;
2024-03-09 15:22:12 -08:00
if ( chunk - > ast - > tag = = TextLiteral ) {
chunk_code = compile ( env , chunk - > ast ) ;
} else if ( chunk_t - > tag = = TextType & & streq ( Match ( chunk_t , TextType ) - > lang , lang ) ) {
chunk_code = compile ( env , chunk - > ast ) ;
2024-03-21 10:33:10 -07:00
} else if ( lang & & lang_env ) {
2024-03-09 15:22:12 -08:00
// Get conversion function:
chunk_code = compile ( env , chunk - > ast ) ;
2024-03-29 09:54:31 -07:00
for ( int64_t i = 1 ; i < = Table $ length ( * lang_env - > locals ) ; i + + ) {
struct { const char * name ; binding_t * b ; } * entry = Table $ entry ( * lang_env - > locals , i ) ;
2024-03-09 15:22:12 -08:00
if ( entry - > b - > type - > tag ! = FunctionType ) continue ;
2024-03-09 15:47:56 -08:00
if ( ! ( streq ( entry - > name , " escape " ) | | strncmp ( entry - > name , " escape_ " , strlen ( " escape_ " ) ) = = 0 ) )
continue ;
2024-03-09 15:22:12 -08:00
auto fn = Match ( entry - > b - > type , FunctionType ) ;
if ( ! fn - > args | | fn - > args - > next ) continue ;
if ( fn - > ret - > tag ! = TextType | | ! streq ( Match ( fn - > ret , TextType ) - > lang , lang ) )
continue ;
if ( ! promote ( env , & chunk_code , chunk_t , fn - > args - > type ) )
continue ;
chunk_code = CORD_all ( entry - > b - > code , " ( " , chunk_code , " ) " ) ;
goto found_conversion ;
}
2024-03-09 15:32:36 -08:00
code_err ( chunk - > ast , " I don't know how to convert %T to %T " , chunk_t , text_t ) ;
2024-03-09 15:22:12 -08:00
found_conversion : ;
} else {
chunk_code = compile_string ( env , chunk - > ast , " no " ) ;
}
code = CORD_cat ( code , chunk_code ) ;
2024-02-18 01:26:26 -08:00
if ( chunk - > next ) code = CORD_cat ( code , " , " ) ;
2024-02-10 12:33:35 -08:00
}
2024-02-18 01:26:26 -08:00
return CORD_cat ( code , " ) " ) ;
2024-02-04 12:23:59 -08:00
}
}
case Block : {
2024-02-04 15:04:41 -08:00
ast_list_t * stmts = Match ( ast , Block ) - > statements ;
if ( stmts & & ! stmts - > next )
2024-03-14 10:28:30 -07:00
return compile ( env , stmts - > ast ) ;
2024-02-04 15:04:41 -08:00
2024-03-14 10:28:30 -07:00
CORD code = " ({ \n " ;
2024-07-04 15:00:01 -07:00
deferral_t * prev_deferred = env - > deferred ;
2024-02-17 14:00:21 -08:00
env = fresh_scope ( env ) ;
2024-05-18 22:46:30 -07:00
for ( ast_list_t * stmt = stmts ; stmt ; stmt = stmt - > next )
prebind_statement ( env , stmt - > ast ) ;
2024-02-04 15:04:41 -08:00
for ( ast_list_t * stmt = stmts ; stmt ; stmt = stmt - > next ) {
2024-02-24 11:29:40 -08:00
bind_statement ( env , stmt - > ast ) ;
2024-03-14 10:28:30 -07:00
if ( stmt - > next ) {
code = CORD_all ( code , compile_statement ( env , stmt - > ast ) , " \n " ) ;
} else {
2024-07-04 15:00:01 -07:00
// TODO: put defer after evaluating block expression
for ( deferral_t * deferred = env - > deferred ; deferred & & deferred ! = prev_deferred ; deferred = deferred - > next ) {
code = CORD_all ( code , compile_statement ( deferred - > defer_env , deferred - > block ) ) ;
}
2024-03-14 10:28:30 -07:00
code = CORD_all ( code , compile ( env , stmt - > ast ) , " ; \n " ) ;
}
2024-02-25 10:13:36 -08:00
}
2024-07-04 15:00:01 -07:00
2024-03-14 10:28:30 -07:00
return CORD_cat ( code , " }) " ) ;
2024-02-04 12:23:59 -08:00
}
2024-03-05 11:46:01 -08:00
case Min : case Max : {
type_t * t = get_type ( env , ast ) ;
ast_t * key = ast - > tag = = Min ? Match ( ast , Min ) - > key : Match ( ast , Max ) - > key ;
ast_t * lhs = ast - > tag = = Min ? Match ( ast , Min ) - > lhs : Match ( ast , Max ) - > lhs ;
ast_t * rhs = ast - > tag = = Min ? Match ( ast , Min ) - > rhs : Match ( ast , Max ) - > rhs ;
2024-03-07 09:15:38 -08:00
const char * key_name = " $ " ;
2024-03-05 11:46:01 -08:00
if ( key = = NULL ) key = FakeAST ( Var , key_name ) ;
env_t * expr_env = fresh_scope ( env ) ;
2024-04-16 10:50:07 -07:00
set_binding ( expr_env , key_name , new ( binding_t , . type = t , . code = " ternary$lhs " ) ) ;
2024-03-05 11:46:01 -08:00
CORD lhs_key = compile ( expr_env , key ) ;
2024-04-16 10:50:07 -07:00
set_binding ( expr_env , key_name , new ( binding_t , . type = t , . code = " ternary$rhs " ) ) ;
2024-03-05 11:46:01 -08:00
CORD rhs_key = compile ( expr_env , key ) ;
type_t * key_t = get_type ( expr_env , key ) ;
CORD comparison ;
2024-08-13 00:05:56 -07:00
if ( key_t - > tag = = IntType & & Match ( key_t , IntType ) - > bits = = 0 )
comparison = CORD_all ( " (Int$compare_value( " , lhs_key , " , " , rhs_key , " ) " , ( ast - > tag = = Min ? " <= " : " >= " ) , " 0) " ) ;
else if ( key_t - > tag = = IntType | | key_t - > tag = = NumType | | key_t - > tag = = BoolType | | key_t - > tag = = PointerType )
2024-03-05 11:46:01 -08:00
comparison = CORD_all ( " (( " , lhs_key , " ) " , ( ast - > tag = = Min ? " <= " : " >= " ) , " ( " , rhs_key , " )) " ) ;
else if ( key_t - > tag = = TextType )
comparison = CORD_all ( " CORD_cmp( " , lhs_key , " , " , rhs_key , " ) " , ( ast - > tag = = Min ? " <= " : " >= " ) , " 0 " ) ;
else
2024-05-12 17:12:00 -07:00
comparison = CORD_all ( " generic_compare(stack( " , lhs_key , " ), stack( " , rhs_key , " ), " , compile_type_info ( env , key_t ) , " ) " ,
2024-03-05 11:46:01 -08:00
( ast - > tag = = Min ? " <= " : " >= " ) , " 0 " ) ;
return CORD_all (
" ({ \n " ,
2024-06-16 15:09:54 -07:00
compile_type ( t ) , " ternary$lhs = " , compile ( env , lhs ) , " , ternary$rhs = " , compile ( env , rhs ) , " ; \n " ,
2024-04-16 10:50:07 -07:00
comparison , " ? ternary$lhs : ternary$rhs; \n "
2024-03-05 11:46:01 -08:00
" }) " ) ;
2024-02-10 14:36:04 -08:00
}
2024-02-04 18:13:50 -08:00
case Array : {
2024-08-04 11:22:58 -07:00
type_t * array_type = get_type ( env , ast ) ;
if ( padded_type_size ( Match ( array_type , ArrayType ) - > item_type ) > ARRAY_MAX_STRIDE )
code_err ( ast , " This array holds items that take up %ld bytes, but the maximum supported size is %ld bytes. Consider using an array of pointers instead. " ,
padded_type_size ( Match ( array_type , ArrayType ) - > item_type ) , ARRAY_MAX_STRIDE ) ;
2024-02-04 18:13:50 -08:00
auto array = Match ( ast , Array ) ;
if ( ! array - > items )
2024-02-25 12:28:46 -08:00
return " (array_t){.length=0} " ;
2024-03-05 23:27:01 -08:00
int64_t n = 0 ;
2024-03-13 23:37:56 -07:00
for ( ast_list_t * item = array - > items ; item ; item = item - > next ) {
2024-03-05 23:27:01 -08:00
+ + n ;
2024-03-17 11:46:36 -07:00
if ( item - > ast - > tag = = Comprehension )
2024-03-13 23:37:56 -07:00
goto array_comprehension ;
}
2024-03-05 23:27:01 -08:00
2024-03-13 23:48:07 -07:00
{
type_t * item_type = Match ( array_type , ArrayType ) - > item_type ;
2024-06-16 15:09:54 -07:00
CORD code = CORD_all ( " TypedArrayN( " , compile_type ( item_type ) , CORD_asprintf ( " , %ld " , n ) ) ;
2024-03-13 23:48:07 -07:00
for ( ast_list_t * item = array - > items ; item ; item = item - > next )
code = CORD_all ( code , " , " , compile ( env , item - > ast ) ) ;
return CORD_cat ( code , " ) " ) ;
}
2024-03-13 23:37:56 -07:00
array_comprehension :
{
env_t * scope = fresh_scope ( env ) ;
2024-03-17 12:26:25 -07:00
static int64_t comp_num = 1 ;
2024-04-16 10:50:07 -07:00
scope - > comprehension_var = heap_strf ( " arr$%ld " , comp_num + + ) ;
2024-03-17 12:26:25 -07:00
CORD code = CORD_all ( " ({ array_t " , scope - > comprehension_var , " = {}; " ) ;
set_binding ( scope , scope - > comprehension_var , new ( binding_t , . type = array_type , . code = scope - > comprehension_var ) ) ;
2024-03-13 23:37:56 -07:00
for ( ast_list_t * item = array - > items ; item ; item = item - > next ) {
2024-03-17 11:46:36 -07:00
if ( item - > ast - > tag = = Comprehension ) {
2024-03-17 12:26:25 -07:00
code = CORD_all ( code , " \n " , compile_statement ( scope , item - > ast ) ) ;
2024-03-13 23:37:56 -07:00
} else {
CORD insert = compile_statement (
2024-03-17 12:26:25 -07:00
scope , WrapAST ( item - > ast , MethodCall , . name = " insert " , . self = FakeAST ( StackReference , FakeAST ( Var , scope - > comprehension_var ) ) ,
2024-03-13 23:37:56 -07:00
. args = new ( arg_ast_t , . value = item - > ast ) ) ) ;
code = CORD_all ( code , " \n " , insert ) ;
}
}
2024-03-17 12:26:25 -07:00
code = CORD_all ( code , " " , scope - > comprehension_var , " ; }) " ) ;
2024-03-13 23:37:56 -07:00
return code ;
}
2024-02-04 18:13:50 -08:00
}
2024-08-11 11:47:34 -07:00
case Channel : {
2024-08-11 12:04:22 -07:00
auto chan = Match ( ast , Channel ) ;
type_t * item_t = parse_type_ast ( env , chan - > item_type ) ;
2024-08-11 11:47:34 -07:00
if ( ! can_send_over_channel ( item_t ) )
code_err ( ast , " This item type can't be sent over a channel because it contains reference to memory that may not be thread-safe. " ) ;
2024-08-11 12:04:22 -07:00
if ( chan - > max_size ) {
CORD max_size = compile ( env , chan - > max_size ) ;
if ( ! promote ( env , & max_size , get_type ( env , chan - > max_size ) , INT_TYPE ) )
code_err ( chan - > max_size , " This value must be an integer, not %T " , get_type ( env , chan - > max_size ) ) ;
return CORD_all ( " Channel$new( " , max_size , " ) " ) ;
} else {
2024-08-13 00:27:32 -07:00
return " Channel$new(I(INT32_MAX)) " ;
2024-08-11 12:04:22 -07:00
}
2024-08-11 11:47:34 -07:00
}
2024-02-17 21:12:23 -08:00
case Table : {
auto table = Match ( ast , Table ) ;
if ( ! table - > entries ) {
2024-08-03 13:23:28 -07:00
CORD code = " ((table_t){ " ;
2024-02-17 21:12:23 -08:00
if ( table - > fallback )
code = CORD_all ( code , " .fallback= " , compile ( env , table - > fallback ) , " , " ) ;
2024-08-03 13:23:28 -07:00
return CORD_cat ( code , " }) " ) ;
2024-02-17 21:12:23 -08:00
}
2024-03-17 11:46:36 -07:00
2024-03-13 23:48:07 -07:00
type_t * table_type = get_type ( env , ast ) ;
type_t * key_t = Match ( table_type , TableType ) - > key_type ;
type_t * value_t = Match ( table_type , TableType ) - > value_type ;
2024-02-18 01:26:26 -08:00
2024-03-17 11:46:36 -07:00
for ( ast_list_t * entry = table - > entries ; entry ; entry = entry - > next ) {
if ( entry - > ast - > tag = = Comprehension )
goto table_comprehension ;
}
{ // No comprehension:
2024-04-16 10:50:07 -07:00
CORD code = CORD_all ( " Table( " ,
2024-06-16 15:09:54 -07:00
compile_type ( key_t ) , " , " ,
compile_type ( value_t ) , " , " ,
2024-03-17 11:46:36 -07:00
compile_type_info ( env , key_t ) , " , " ,
compile_type_info ( env , value_t ) ) ;
if ( table - > fallback )
2024-04-16 10:50:07 -07:00
code = CORD_all ( code , " , /*fallback:*/ heap( " , compile ( env , table - > fallback ) , " ) " ) ;
2024-03-17 11:46:36 -07:00
else
code = CORD_all ( code , " , /*fallback:*/ NULL " ) ;
2024-02-18 01:26:26 -08:00
2024-03-17 11:46:36 -07:00
size_t n = 0 ;
for ( ast_list_t * entry = table - > entries ; entry ; entry = entry - > next )
+ + n ;
CORD_appendf ( & code , " , %zu " , n ) ;
for ( ast_list_t * entry = table - > entries ; entry ; entry = entry - > next ) {
auto e = Match ( entry - > ast , TableEntry ) ;
code = CORD_all ( code , " , \n \t { " , compile ( env , e - > key ) , " , " , compile ( env , e - > value ) , " } " ) ;
}
return CORD_cat ( code , " ) " ) ;
}
table_comprehension :
{
2024-03-17 12:26:25 -07:00
static int64_t comp_num = 1 ;
env_t * scope = fresh_scope ( env ) ;
2024-04-16 10:50:07 -07:00
scope - > comprehension_var = heap_strf ( " table$%ld " , comp_num + + ) ;
2024-03-17 12:26:25 -07:00
CORD code = CORD_all ( " ({ table_t " , scope - > comprehension_var , " = { " ) ;
2024-03-17 11:46:36 -07:00
if ( table - > fallback )
2024-04-16 10:50:07 -07:00
code = CORD_all ( code , " .fallback=heap( " , compile ( env , table - > fallback ) , " ), " ) ;
2024-03-17 11:46:36 -07:00
code = CORD_cat ( code , " }; " ) ;
2024-03-17 12:26:25 -07:00
set_binding ( scope , scope - > comprehension_var , new ( binding_t , . type = table_type , . code = scope - > comprehension_var ) ) ;
2024-03-17 11:46:36 -07:00
for ( ast_list_t * entry = table - > entries ; entry ; entry = entry - > next ) {
if ( entry - > ast - > tag = = Comprehension ) {
2024-03-17 12:26:25 -07:00
code = CORD_all ( code , " \n " , compile_statement ( scope , entry - > ast ) ) ;
2024-03-17 11:46:36 -07:00
} else {
auto e = Match ( entry - > ast , TableEntry ) ;
CORD set = compile_statement (
2024-03-17 12:26:25 -07:00
scope , WrapAST ( entry - > ast , MethodCall , . name = " set " , . self = FakeAST ( StackReference , FakeAST ( Var , scope - > comprehension_var ) ) ,
2024-03-17 11:46:36 -07:00
. args = new ( arg_ast_t , . value = e - > key , . next = new ( arg_ast_t , . value = e - > value ) ) ) ) ;
code = CORD_all ( code , " \n " , set ) ;
}
}
2024-03-17 12:26:25 -07:00
code = CORD_all ( code , " " , scope - > comprehension_var , " ; }) " ) ;
2024-03-17 11:46:36 -07:00
return code ;
2024-02-17 21:12:23 -08:00
}
2024-08-10 12:15:38 -07:00
}
case Set : {
auto set = Match ( ast , Set ) ;
if ( ! set - > items )
return " ((table_t){}) " ;
type_t * set_type = get_type ( env , ast ) ;
type_t * item_type = Match ( set_type , SetType ) - > item_type ;
for ( ast_list_t * item = set - > items ; item ; item = item - > next ) {
if ( item - > ast - > tag = = Comprehension )
goto set_comprehension ;
}
{ // No comprehension:
CORD code = CORD_all ( " Set( " ,
compile_type ( item_type ) , " , " ,
compile_type_info ( env , item_type ) ) ;
size_t n = 0 ;
for ( ast_list_t * item = set - > items ; item ; item = item - > next )
+ + n ;
CORD_appendf ( & code , " , %zu " , n ) ;
for ( ast_list_t * item = set - > items ; item ; item = item - > next ) {
code = CORD_all ( code , " , \n \t " , compile ( env , item - > ast ) ) ;
}
return CORD_cat ( code , " ) " ) ;
}
set_comprehension :
{
static int64_t comp_num = 1 ;
env_t * scope = fresh_scope ( env ) ;
scope - > comprehension_var = heap_strf ( " set$%ld " , comp_num + + ) ;
CORD code = CORD_all ( " ({ table_t " , scope - > comprehension_var , " = {}; " ) ;
set_binding ( scope , scope - > comprehension_var , new ( binding_t , . type = set_type , . code = scope - > comprehension_var ) ) ;
for ( ast_list_t * item = set - > items ; item ; item = item - > next ) {
if ( item - > ast - > tag = = Comprehension ) {
code = CORD_all ( code , " \n " , compile_statement ( scope , item - > ast ) ) ;
} else {
CORD add_item = compile_statement (
scope , WrapAST ( item - > ast , MethodCall , . name = " add " , . self = FakeAST ( StackReference , FakeAST ( Var , scope - > comprehension_var ) ) ,
. args = new ( arg_ast_t , . value = item - > ast ) ) ) ;
code = CORD_all ( code , " \n " , add_item ) ;
}
}
code = CORD_all ( code , " " , scope - > comprehension_var , " ; }) " ) ;
return code ;
}
2024-02-17 21:12:23 -08:00
}
2024-03-17 11:46:36 -07:00
case Comprehension : {
2024-03-17 12:59:06 -07:00
ast_t * base = Match ( ast , Comprehension ) - > expr ;
while ( base - > tag = = Comprehension )
base = Match ( ast , Comprehension ) - > expr ;
if ( base - > tag = = TableEntry )
return compile ( env , WrapAST ( ast , Table , . entries = new ( ast_list_t , . ast = ast ) ) ) ;
else
return compile ( env , WrapAST ( ast , Array , . items = new ( ast_list_t , . ast = ast ) ) ) ;
2024-03-17 11:46:36 -07:00
}
2024-03-09 11:02:19 -08:00
case Lambda : {
auto lambda = Match ( ast , Lambda ) ;
2024-06-16 13:08:35 -07:00
CORD name = CORD_asprintf ( " %rlambda$%ld " , namespace_prefix ( env - > libname , env - > namespace ) , lambda - > id ) ;
2024-03-09 11:02:19 -08:00
env_t * body_scope = fresh_scope ( env ) ;
for ( arg_ast_t * arg = lambda - > args ; arg ; arg = arg - > next ) {
type_t * arg_type = get_arg_ast_type ( env , arg ) ;
2024-04-16 10:50:07 -07:00
set_binding ( body_scope , arg - > name , new ( binding_t , . type = arg_type , . code = CORD_cat ( " $ " , arg - > name ) ) ) ;
2024-03-09 11:02:19 -08:00
}
2024-03-09 13:03:38 -08:00
2024-03-15 10:35:30 -07:00
fn_ctx_t fn_ctx = ( fn_ctx_t ) {
2024-07-14 11:13:23 -07:00
. parent = env - > fn_ctx ,
. closure_scope = env - > locals ,
2024-03-09 13:03:38 -08:00
. closed_vars = new ( table_t ) ,
} ;
body_scope - > fn_ctx = & fn_ctx ;
body_scope - > locals - > fallback = env - > globals ;
2024-07-04 15:09:33 -07:00
body_scope - > deferred = NULL ;
2024-07-13 15:43:50 -07:00
type_t * ret_t = get_type ( body_scope , lambda - > body ) ;
if ( ret_t - > tag = = ReturnType )
ret_t = Match ( ret_t , ReturnType ) - > ret ;
2024-06-06 13:28:53 -07:00
fn_ctx . return_type = ret_t ;
2024-03-09 13:03:38 -08:00
2024-07-14 11:13:23 -07:00
if ( env - > fn_ctx - > closed_vars ) {
for ( int64_t i = 1 ; i < = Table $ length ( * env - > fn_ctx - > closed_vars ) ; i + + ) {
struct { const char * name ; binding_t * b ; } * entry = Table $ entry ( * env - > fn_ctx - > closed_vars , i ) ;
set_binding ( body_scope , entry - > name , new ( binding_t , . type = entry - > b - > type , . code = CORD_cat ( " userdata-> " , entry - > name ) ) ) ;
Table $ str_set ( fn_ctx . closed_vars , entry - > name , entry - > b ) ;
}
}
2024-06-16 15:09:54 -07:00
CORD code = CORD_all ( " static " , compile_type ( ret_t ) , " " , name , " ( " ) ;
2024-03-09 11:02:19 -08:00
for ( arg_ast_t * arg = lambda - > args ; arg ; arg = arg - > next ) {
type_t * arg_type = get_arg_ast_type ( env , arg ) ;
2024-06-16 15:09:54 -07:00
code = CORD_all ( code , compile_type ( arg_type ) , " $ " , arg - > name , " , " ) ;
2024-03-09 11:02:19 -08:00
}
2024-03-09 13:03:38 -08:00
2024-07-23 16:46:42 -07:00
CORD args_typedef = compile_statement_typedefs ( env , ast ) ;
2024-06-06 13:28:53 -07:00
env - > code - > local_typedefs = CORD_all ( env - > code - > local_typedefs , args_typedef ) ;
2024-03-09 13:03:38 -08:00
2024-06-06 13:28:53 -07:00
table_t * closed_vars = get_closed_vars ( env , ast ) ;
2024-03-09 13:03:38 -08:00
CORD userdata ;
2024-06-06 13:28:53 -07:00
if ( Table $ length ( * closed_vars ) = = 0 ) {
2024-04-16 10:50:07 -07:00
code = CORD_cat ( code , " void *userdata) " ) ;
2024-03-09 13:03:38 -08:00
userdata = " NULL " ;
} else {
userdata = CORD_all ( " new( " , name , " $userdata_t " ) ;
2024-06-06 13:28:53 -07:00
for ( int64_t i = 1 ; i < = Table $ length ( * closed_vars ) ; i + + ) {
struct { const char * name ; binding_t * b ; } * entry = Table $ entry ( * closed_vars , i ) ;
2024-05-27 13:33:51 -07:00
if ( entry - > b - > type - > tag = = ModuleType )
continue ;
2024-08-12 14:44:05 -07:00
CORD binding_code = get_binding ( env , entry - > name ) - > code ;
if ( entry - > b - > type - > tag = = ArrayType )
userdata = CORD_all ( userdata , " , ARRAY_COPY( " , binding_code , " ) " ) ;
else if ( entry - > b - > type - > tag = = TableType | | entry - > b - > type - > tag = = SetType )
userdata = CORD_all ( userdata , " , TABLE_COPY( " , binding_code , " ) " ) ;
else
userdata = CORD_all ( userdata , " , " , binding_code ) ;
2024-03-09 13:03:38 -08:00
}
userdata = CORD_all ( userdata , " ) " ) ;
2024-04-16 10:50:07 -07:00
code = CORD_all ( code , name , " $userdata_t *userdata) " ) ;
2024-03-09 13:03:38 -08:00
}
2024-03-09 11:02:19 -08:00
2024-03-09 11:09:18 -08:00
CORD body = CORD_EMPTY ;
for ( ast_list_t * stmt = Match ( lambda - > body , Block ) - > statements ; stmt ; stmt = stmt - > next ) {
2024-07-14 10:22:42 -07:00
bind_statement ( body_scope , stmt - > ast ) ;
2024-07-13 15:43:50 -07:00
if ( stmt - > next | | ret_t - > tag = = VoidType | | ret_t - > tag = = AbortType | | get_type ( body_scope , stmt - > ast ) - > tag = = ReturnType )
2024-03-09 11:09:18 -08:00
body = CORD_all ( body , compile_statement ( body_scope , stmt - > ast ) , " \n " ) ;
else
body = CORD_all ( body , compile_statement ( body_scope , FakeAST ( Return , stmt - > ast ) ) , " \n " ) ;
}
2024-07-04 15:09:33 -07:00
if ( ( ret_t - > tag = = VoidType | | ret_t - > tag = = AbortType ) & & body_scope - > deferred )
body = CORD_all ( body , compile_statement ( body_scope , FakeAST ( Return ) ) , " \n " ) ;
env - > code - > funcs = CORD_all ( env - > code - > funcs , code , " { \n " , body , " \n } \n " ) ;
2024-08-15 10:59:01 -07:00
return CORD_all ( " ((closure_t) { " , name, " , " , userdata, " } ) " );
2024-03-09 11:02:19 -08:00
}
2024-03-08 11:23:16 -08:00
case MethodCall : {
auto call = Match ( ast , MethodCall ) ;
type_t * self_t = get_type ( env , call - > self ) ;
type_t * self_value_t = value_type ( self_t ) ;
switch ( self_value_t - > tag ) {
case ArrayType : {
// TODO: check for readonly
2024-04-02 10:08:06 -07:00
type_t * item_t = Match ( self_value_t , ArrayType ) - > item_type ;
2024-08-11 11:47:34 -07:00
CORD padded_item_size = CORD_asprintf ( " %ld " , padded_type_size ( item_t ) ) ;
2024-03-08 11:23:16 -08:00
if ( streq ( call - > name , " insert " ) ) {
CORD self = compile_to_pointer_depth ( env , call - > self , 1 , false ) ;
2024-03-13 23:37:56 -07:00
arg_t * arg_spec = new ( arg_t , . name = " item " , . type = item_t ,
2024-08-12 22:30:25 -07:00
. next = new ( arg_t , . name = " at " , . type = INT_TYPE , . default_val = FakeAST ( Int , . str = " 0 " , . bits = 0 ) ) ) ;
2024-03-29 09:54:31 -07:00
return CORD_all ( " Array$insert_value( " , self , " , " , compile_arguments ( env , ast , arg_spec , call - > args ) , " , " ,
2024-08-03 12:33:50 -07:00
padded_item_size , " ) " ) ;
2024-03-08 11:25:17 -08:00
} else if ( streq ( call - > name , " insert_all " ) ) {
CORD self = compile_to_pointer_depth ( env , call - > self , 1 , false ) ;
arg_t * arg_spec = new ( arg_t , . name = " items " , . type = self_value_t ,
2024-08-12 22:30:25 -07:00
. next = new ( arg_t , . name = " at " , . type = INT_TYPE , . default_val = FakeAST ( Int , . str = " 0 " , . bits = 0 ) ) ) ;
2024-03-29 09:54:31 -07:00
return CORD_all ( " Array$insert_all( " , self , " , " , compile_arguments ( env , ast , arg_spec , call - > args ) , " , " ,
2024-08-03 12:33:50 -07:00
padded_item_size , " ) " ) ;
2024-08-14 23:39:35 -07:00
} else if ( streq ( call - > name , " remove_at " ) ) {
2024-03-08 11:23:16 -08:00
CORD self = compile_to_pointer_depth ( env , call - > self , 1 , false ) ;
2024-08-12 22:30:25 -07:00
arg_t * arg_spec = new ( arg_t , . name = " index " , . type = INT_TYPE , . default_val = FakeAST ( Int , . str = " -1 " , . bits = 0 ) ,
. next = new ( arg_t , . name = " count " , . type = INT_TYPE , . default_val = FakeAST ( Int , . str = " 1 " , . bits = 0 ) ) ) ;
2024-08-14 23:39:35 -07:00
return CORD_all ( " Array$remove_at( " , self , " , " , compile_arguments ( env , ast , arg_spec , call - > args ) , " , " ,
2024-08-03 12:33:50 -07:00
padded_item_size , " ) " ) ;
2024-08-14 23:39:35 -07:00
} else if ( streq ( call - > name , " remove_item " ) ) {
CORD self = compile_to_pointer_depth ( env , call - > self , 1 , false ) ;
arg_t * arg_spec = new ( arg_t , . name = " item " , . type = item_t ,
. next = new ( arg_t , . name = " max_count " , . type = INT_TYPE , . default_val = FakeAST ( Int , . str = " -1 " , . bits = 0 ) ) ) ;
return CORD_all ( " Array$remove_item_value( " , self , " , " , compile_arguments ( env , ast , arg_spec , call - > args ) , " , " ,
compile_type_info ( env , self_value_t ) , " ) " ) ;
2024-03-08 11:23:16 -08:00
} else if ( streq ( call - > name , " random " ) ) {
CORD self = compile_to_pointer_depth ( env , call - > self , 0 , false ) ;
2024-04-02 10:08:06 -07:00
( void ) compile_arguments ( env , ast , NULL , call - > args ) ;
2024-08-04 10:03:55 -07:00
return CORD_all ( " Array$random_value( " , self , " , " , compile_type ( item_t ) , " ) " ) ;
2024-08-14 23:39:35 -07:00
} else if ( streq ( call - > name , " has " ) ) {
CORD self = compile_to_pointer_depth ( env , call - > self , 0 , false ) ;
arg_t * arg_spec = new ( arg_t , . name = " item " , . type = item_t ) ;
return CORD_all ( " Array$has_value( " , self , " , " , compile_arguments ( env , ast , arg_spec , call - > args ) , " , " ,
compile_type_info ( env , self_value_t ) , " ) " ) ;
2024-04-02 20:28:59 -07:00
} else if ( streq ( call - > name , " sample " ) ) {
CORD self = compile_to_pointer_depth ( env , call - > self , 0 , false ) ;
2024-08-12 22:30:25 -07:00
arg_t * arg_spec = new ( arg_t , . name = " count " , . type = INT_TYPE ,
2024-04-02 20:40:18 -07:00
. next = new ( arg_t , . name = " weights " , . type = Type ( ArrayType , . item_type = Type ( NumType , . bits = 64 ) ) ,
2024-08-11 11:47:34 -07:00
. default_val = FakeAST ( Array , . item_type = new ( type_ast_t , . tag = VarTypeAST , . __data . VarTypeAST . name = " Num " ) ) ) ) ;
2024-04-02 20:28:59 -07:00
return CORD_all ( " Array$sample( " , self , " , " , compile_arguments ( env , ast , arg_spec , call - > args ) , " , " ,
2024-08-03 12:33:50 -07:00
padded_item_size , " ) " ) ;
2024-03-08 11:23:16 -08:00
} else if ( streq ( call - > name , " shuffle " ) ) {
CORD self = compile_to_pointer_depth ( env , call - > self , 1 , false ) ;
2024-04-02 10:08:06 -07:00
( void ) compile_arguments ( env , ast , NULL , call - > args ) ;
2024-08-03 12:33:50 -07:00
return CORD_all ( " Array$shuffle( " , self , " , " , padded_item_size , " ) " ) ;
2024-08-14 23:17:53 -07:00
} else if ( streq ( call - > name , " shuffled " ) ) {
CORD self = compile_to_pointer_depth ( env , call - > self , 0 , false ) ;
( void ) compile_arguments ( env , ast , NULL , call - > args ) ;
return CORD_all ( " Array$shuffled( " , self , " , " , padded_item_size , " ) " ) ;
2024-04-02 10:13:33 -07:00
} else if ( streq ( call - > name , " sort " ) | | streq ( call - > name , " sorted " ) ) {
CORD self = compile_to_pointer_depth ( env , call - > self , streq ( call - > name , " sort " ) ? 1 : 0 , false ) ;
2024-04-02 10:08:06 -07:00
CORD comparison ;
if ( call - > args ) {
type_t * item_ptr = Type ( PointerType , . pointed = item_t , . is_stack = true , . is_readonly = true ) ;
type_t * fn_t = Type ( FunctionType , . args = new ( arg_t , . name = " x " , . type = item_ptr , . next = new ( arg_t , . name = " y " , . type = item_ptr ) ) ,
. ret = Type ( IntType , . bits = 32 ) ) ;
arg_t * arg_spec = new ( arg_t , . name = " by " , . type = Type ( ClosureType , . fn = fn_t ) ) ;
comparison = compile_arguments ( env , ast , arg_spec , call - > args ) ;
} else {
2024-08-15 10:59:01 -07:00
comparison = CORD_all ( " ((closure_t){.fn=generic_compare, .userdata=(void*) " , compile_type_info ( env , item_t ) , " }) " ) ;
2024-04-02 10:08:06 -07:00
}
2024-08-03 12:33:50 -07:00
return CORD_all ( " Array$ " , call - > name , " ( " , self , " , " , comparison , " , " , padded_item_size , " ) " ) ;
2024-04-19 10:29:04 -07:00
} else if ( streq ( call - > name , " heapify " ) ) {
CORD self = compile_to_pointer_depth ( env , call - > self , 1 , false ) ;
CORD comparison ;
if ( call - > args ) {
type_t * item_ptr = Type ( PointerType , . pointed = item_t , . is_stack = true ) ;
type_t * fn_t = Type ( FunctionType , . args = new ( arg_t , . name = " x " , . type = item_ptr , . next = new ( arg_t , . name = " y " , . type = item_ptr ) ) ,
. ret = Type ( IntType , . bits = 32 ) ) ;
arg_t * arg_spec = new ( arg_t , . name = " by " , . type = Type ( ClosureType , . fn = fn_t ) ) ;
comparison = compile_arguments ( env , ast , arg_spec , call - > args ) ;
} else {
comparison = CORD_all ( " ((closure_t){.fn=generic_compare, .userdata=(void*) " , compile_type_info ( env , item_t ) , " }) " ) ;
}
2024-08-03 12:33:50 -07:00
return CORD_all ( " Array$heapify( " , self , " , " , comparison , " , " , padded_item_size , " ) " ) ;
2024-04-19 10:29:04 -07:00
} else if ( streq ( call - > name , " heap_push " ) ) {
CORD self = compile_to_pointer_depth ( env , call - > self , 1 , false ) ;
type_t * item_ptr = Type ( PointerType , . pointed = item_t , . is_stack = true ) ;
type_t * fn_t = Type ( FunctionType , . args = new ( arg_t , . name = " x " , . type = item_ptr , . next = new ( arg_t , . name = " y " , . type = item_ptr ) ) ,
. ret = Type ( IntType , . bits = 32 ) ) ;
2024-08-15 10:59:01 -07:00
ast_t * default_cmp = FakeAST ( InlineCCode ,
. code = CORD_all ( " ((closure_t){.fn=generic_compare, .userdata=(void*) " ,
compile_type_info ( env , item_t ) , " }) " ) ,
. type = NewTypeAST ( NULL , NULL , NULL , FunctionTypeAST ) ) ;
arg_t * arg_spec = new ( arg_t , . name = " item " , . type = item_t ,
. next = new ( arg_t , . name = " by " , . type = Type ( ClosureType , . fn = fn_t ) , . default_val = default_cmp ) ) ;
2024-04-19 10:29:04 -07:00
CORD arg_code = compile_arguments ( env , ast , arg_spec , call - > args ) ;
2024-08-03 12:33:50 -07:00
return CORD_all ( " Array$heap_push_value( " , self , " , " , arg_code , " , " , padded_item_size , " ) " ) ;
2024-04-19 10:29:04 -07:00
} else if ( streq ( call - > name , " heap_pop " ) ) {
CORD self = compile_to_pointer_depth ( env , call - > self , 1 , false ) ;
type_t * item_ptr = Type ( PointerType , . pointed = item_t , . is_stack = true ) ;
type_t * fn_t = Type ( FunctionType , . args = new ( arg_t , . name = " x " , . type = item_ptr , . next = new ( arg_t , . name = " y " , . type = item_ptr ) ) ,
. ret = Type ( IntType , . bits = 32 ) ) ;
2024-08-15 10:59:01 -07:00
ast_t * default_cmp = FakeAST ( InlineCCode ,
. code = CORD_all ( " ((closure_t){.fn=generic_compare, .userdata=(void*) " ,
compile_type_info ( env , item_t ) , " }) " ) ,
. type = NewTypeAST ( NULL , NULL , NULL , FunctionTypeAST ) ) ;
2024-04-19 10:29:04 -07:00
arg_t * arg_spec = new ( arg_t , . name = " by " , . type = Type ( ClosureType , . fn = fn_t ) , . default_val = default_cmp ) ;
CORD arg_code = compile_arguments ( env , ast , arg_spec , call - > args ) ;
2024-08-03 12:33:50 -07:00
return CORD_all ( " Array$heap_pop_value( " , self , " , " , arg_code , " , " , padded_item_size , " , " , compile_type ( item_t ) , " ) " ) ;
2024-08-14 22:59:42 -07:00
} else if ( streq ( call - > name , " binary_search " ) ) {
CORD self = compile_to_pointer_depth ( env , call - > self , 0 , false ) ;
type_t * item_ptr = Type ( PointerType , . pointed = item_t , . is_stack = true ) ;
type_t * fn_t = Type ( FunctionType , . args = new ( arg_t , . name = " x " , . type = item_ptr , . next = new ( arg_t , . name = " y " , . type = item_ptr ) ) ,
. ret = Type ( IntType , . bits = 32 ) ) ;
2024-08-15 10:59:01 -07:00
ast_t * default_cmp = FakeAST ( InlineCCode ,
. code = CORD_all ( " ((closure_t){.fn=generic_compare, .userdata=(void*) " ,
compile_type_info ( env , item_t ) , " }) " ) ,
. type = NewTypeAST ( NULL , NULL , NULL , FunctionTypeAST ) ) ;
arg_t * arg_spec = new ( arg_t , . name = " target " , . type = item_t ,
. next = new ( arg_t , . name = " by " , . type = Type ( ClosureType , . fn = fn_t ) , . default_val = default_cmp ) ) ;
2024-08-14 22:59:42 -07:00
CORD arg_code = compile_arguments ( env , ast , arg_spec , call - > args ) ;
return CORD_all ( " Array$binary_search_value( " , self , " , " , arg_code , " ) " ) ;
2024-03-08 11:23:16 -08:00
} else if ( streq ( call - > name , " clear " ) ) {
CORD self = compile_to_pointer_depth ( env , call - > self , 1 , false ) ;
2024-04-02 10:08:06 -07:00
( void ) compile_arguments ( env , ast , NULL , call - > args ) ;
2024-03-29 09:54:31 -07:00
return CORD_all ( " Array$clear( " , self , " ) " ) ;
2024-08-15 00:01:04 -07:00
} else if ( streq ( call - > name , " find " ) ) {
CORD self = compile_to_pointer_depth ( env , call - > self , 0 , false ) ;
arg_t * arg_spec = new ( arg_t , . name = " item " , . type = item_t ) ;
return CORD_all ( " Array$find_value( " , self , " , " , compile_arguments ( env , ast , arg_spec , call - > args ) ,
" , " , compile_type_info ( env , self_value_t ) , " ) " ) ;
2024-07-10 10:34:45 -07:00
} else if ( streq ( call - > name , " from " ) ) {
2024-07-22 10:54:03 -07:00
CORD self = compile_to_pointer_depth ( env , call - > self , 0 , false ) ;
2024-08-12 22:30:25 -07:00
arg_t * arg_spec = new ( arg_t , . name = " first " , . type = INT_TYPE ) ;
2024-07-10 10:34:45 -07:00
return CORD_all ( " Array$from( " , self , " , " , compile_arguments ( env , ast , arg_spec , call - > args ) , " ) " ) ;
2024-07-10 10:42:58 -07:00
} else if ( streq ( call - > name , " to " ) ) {
2024-07-22 10:54:03 -07:00
CORD self = compile_to_pointer_depth ( env , call - > self , 0 , false ) ;
2024-08-12 22:30:25 -07:00
arg_t * arg_spec = new ( arg_t , . name = " last " , . type = INT_TYPE ) ;
2024-07-10 10:42:58 -07:00
return CORD_all ( " Array$to( " , self , " , " , compile_arguments ( env , ast , arg_spec , call - > args ) , " ) " ) ;
2024-07-10 10:34:45 -07:00
} else if ( streq ( call - > name , " by " ) ) {
2024-07-22 10:54:03 -07:00
CORD self = compile_to_pointer_depth ( env , call - > self , 0 , false ) ;
2024-08-12 22:30:25 -07:00
arg_t * arg_spec = new ( arg_t , . name = " stride " , . type = INT_TYPE ) ;
2024-08-03 12:33:50 -07:00
return CORD_all ( " Array$by( " , self , " , " , compile_arguments ( env , ast , arg_spec , call - > args ) , " , " , padded_item_size , " ) " ) ;
2024-03-26 11:59:52 -07:00
} else if ( streq ( call - > name , " reversed " ) ) {
CORD self = compile_to_pointer_depth ( env , call - > self , 0 , false ) ;
2024-04-02 10:08:06 -07:00
( void ) compile_arguments ( env , ast , NULL , call - > args ) ;
2024-08-03 12:33:50 -07:00
return CORD_all ( " Array$reversed( " , self , " , " , padded_item_size , " ) " ) ;
2024-08-10 13:19:36 -07:00
} else if ( streq ( call - > name , " unique " ) ) {
CORD self = compile_to_pointer_depth ( env , call - > self , 0 , false ) ;
( void ) compile_arguments ( env , ast , NULL , call - > args ) ;
return CORD_all ( " Table$from_entries( " , self , " , $SetInfo( " , compile_type_info ( env , item_t ) , " )) " ) ;
2024-08-10 13:36:50 -07:00
} else if ( streq ( call - > name , " counts " ) ) {
CORD self = compile_to_pointer_depth ( env , call - > self , 0 , false ) ;
( void ) compile_arguments ( env , ast , NULL , call - > args ) ;
return CORD_all ( " Array$counts( " , self , " , " , compile_type_info ( env , self_value_t ) , " ) " ) ;
2024-03-08 11:23:16 -08:00
} else code_err ( ast , " There is no '%s' method for arrays " , call - > name ) ;
}
2024-08-10 12:15:38 -07:00
case SetType : {
auto set = Match ( self_value_t , SetType ) ;
if ( streq ( call - > name , " has " ) ) {
CORD self = compile_to_pointer_depth ( env , call - > self , 0 , false ) ;
arg_t * arg_spec = new ( arg_t , . name = " key " , . type = set - > item_type ) ;
return CORD_all ( " Table$has_value( " , self , " , " , compile_arguments ( env , ast , arg_spec , call - > args ) , " , " ,
compile_type_info ( env , self_value_t ) , " ) " ) ;
} else if ( streq ( call - > name , " add " ) ) {
CORD self = compile_to_pointer_depth ( env , call - > self , 1 , false ) ;
arg_t * arg_spec = new ( arg_t , . name = " item " , . type = set - > item_type ) ;
return CORD_all ( " Table$set_value( " , self , " , " , compile_arguments ( env , ast , arg_spec , call - > args ) , " , NULL, " ,
compile_type_info ( env , self_value_t ) , " ) " ) ;
} else if ( streq ( call - > name , " add_all " ) ) {
arg_t * arg_spec = new ( arg_t , . name = " items " , . type = Type ( ArrayType , . item_type = Match ( self_value_t , SetType ) - > item_type ) ) ;
return CORD_all ( " ({ table_t *set = " , compile_to_pointer_depth ( env , call - > self , 1 , false ) , " ; " ,
" array_t to_add = " , compile_arguments ( env , ast , arg_spec , call - > args ) , " ; " ,
" for (int64_t i = 0; i < to_add.length; i++) \n "
" Table$set(set, to_add.data + i*to_add.stride, NULL, " , compile_type_info ( env , self_value_t ) , " ); \n " ,
" (void)0; }) " ) ;
} else if ( streq ( call - > name , " remove " ) ) {
CORD self = compile_to_pointer_depth ( env , call - > self , 1 , false ) ;
arg_t * arg_spec = new ( arg_t , . name = " item " , . type = set - > item_type ) ;
return CORD_all ( " Table$remove_value( " , self , " , " , compile_arguments ( env , ast , arg_spec , call - > args ) , " , " ,
compile_type_info ( env , self_value_t ) , " ) " ) ;
} else if ( streq ( call - > name , " remove_all " ) ) {
arg_t * arg_spec = new ( arg_t , . name = " items " , . type = Type ( ArrayType , . item_type = Match ( self_value_t , SetType ) - > item_type ) ) ;
return CORD_all ( " ({ table_t *set = " , compile_to_pointer_depth ( env , call - > self , 1 , false ) , " ; " ,
" array_t to_add = " , compile_arguments ( env , ast , arg_spec , call - > args ) , " ; " ,
" for (int64_t i = 0; i < to_add.length; i++) \n "
" Table$remove(set, to_add.data + i*to_add.stride, " , compile_type_info ( env , self_value_t ) , " ); \n " ,
" (void)0; }) " ) ;
} else if ( streq ( call - > name , " clear " ) ) {
CORD self = compile_to_pointer_depth ( env , call - > self , 1 , false ) ;
( void ) compile_arguments ( env , ast , NULL , call - > args ) ;
return CORD_all ( " Table$clear( " , self , " ) " ) ;
} else if ( streq ( call - > name , " with " ) ) {
CORD self = compile_to_pointer_depth ( env , call - > self , 0 , false ) ;
arg_t * arg_spec = new ( arg_t , . name = " other " , . type = self_value_t ) ;
return CORD_all ( " Table$with( " , self , " , " , compile_arguments ( env , ast , arg_spec , call - > args ) ,
" , " , compile_type_info ( env , self_value_t ) , " ) " ) ;
} else if ( streq ( call - > name , " overlap " ) ) {
CORD self = compile_to_pointer_depth ( env , call - > self , 0 , false ) ;
arg_t * arg_spec = new ( arg_t , . name = " other " , . type = self_value_t ) ;
return CORD_all ( " Table$overlap( " , self , " , " , compile_arguments ( env , ast , arg_spec , call - > args ) ,
" , " , compile_type_info ( env , self_value_t ) , " ) " ) ;
} else if ( streq ( call - > name , " without " ) ) {
CORD self = compile_to_pointer_depth ( env , call - > self , 0 , false ) ;
arg_t * arg_spec = new ( arg_t , . name = " other " , . type = self_value_t ) ;
return CORD_all ( " Table$without( " , self , " , " , compile_arguments ( env , ast , arg_spec , call - > args ) ,
" , " , compile_type_info ( env , self_value_t ) , " ) " ) ;
} else if ( streq ( call - > name , " is_subset_of " ) ) {
CORD self = compile_to_pointer_depth ( env , call - > self , 0 , false ) ;
arg_t * arg_spec = new ( arg_t , . name = " other " , . type = self_value_t ,
. next = new ( arg_t , . name = " strict " , . type = Type ( BoolType ) , . default_val = FakeAST ( Bool , false ) ) ) ;
return CORD_all ( " Table$is_subset_of( " , self , " , " , compile_arguments ( env , ast , arg_spec , call - > args ) ,
" , " , compile_type_info ( env , self_value_t ) , " ) " ) ;
} else if ( streq ( call - > name , " is_superset_of " ) ) {
CORD self = compile_to_pointer_depth ( env , call - > self , 0 , false ) ;
arg_t * arg_spec = new ( arg_t , . name = " other " , . type = self_value_t ,
. next = new ( arg_t , . name = " strict " , . type = Type ( BoolType ) , . default_val = FakeAST ( Bool , false ) ) ) ;
return CORD_all ( " Table$is_superset_of( " , self , " , " , compile_arguments ( env , ast , arg_spec , call - > args ) ,
" , " , compile_type_info ( env , self_value_t ) , " ) " ) ;
} else code_err ( ast , " There is no '%s' method for tables " , call - > name ) ;
}
2024-08-11 11:47:34 -07:00
case ChannelType : {
type_t * item_t = Match ( self_value_t , ChannelType ) - > item_type ;
CORD padded_item_size = CORD_asprintf ( " %ld " , padded_type_size ( item_t ) ) ;
if ( streq ( call - > name , " push " ) ) {
CORD self = compile_to_pointer_depth ( env , call - > self , 0 , false ) ;
arg_t * arg_spec = new ( arg_t , . name = " item " , . type = item_t ) ;
return CORD_all ( " Channel$push_value( " , self , " , " , compile_arguments ( env , ast , arg_spec , call - > args ) , " , " ,
padded_item_size , " ) " ) ;
} else if ( streq ( call - > name , " push_all " ) ) {
CORD self = compile_to_pointer_depth ( env , call - > self , 0 , false ) ;
arg_t * arg_spec = new ( arg_t , . name = " to_push " , . type = Type ( ArrayType , . item_type = item_t ) ) ;
return CORD_all ( " Channel$push_all( " , self , " , " , compile_arguments ( env , ast , arg_spec , call - > args ) , " , " ,
padded_item_size , " ) " ) ;
} else if ( streq ( call - > name , " pop " ) ) {
CORD self = compile_to_pointer_depth ( env , call - > self , 0 , false ) ;
( void ) compile_arguments ( env , ast , NULL , call - > args ) ;
return CORD_all ( " Channel$pop_value( " , self , " , " , compile_type ( item_t ) , " , " , padded_item_size , " ) " ) ;
} else if ( streq ( call - > name , " clear " ) ) {
CORD self = compile_to_pointer_depth ( env , call - > self , 0 , false ) ;
( void ) compile_arguments ( env , ast , NULL , call - > args ) ;
return CORD_all ( " Channel$clear( " , self , " ) " ) ;
} else if ( streq ( call - > name , " view " ) ) {
CORD self = compile_to_pointer_depth ( env , call - > self , 0 , false ) ;
( void ) compile_arguments ( env , ast , NULL , call - > args ) ;
return CORD_all ( " Channel$view( " , self , " ) " ) ;
} else code_err ( ast , " There is no '%s' method for channels " , call - > name ) ;
}
2024-03-08 23:48:09 -08:00
case TableType : {
auto table = Match ( self_value_t , TableType ) ;
if ( streq ( call - > name , " get " ) ) {
CORD self = compile_to_pointer_depth ( env , call - > self , 0 , false ) ;
2024-08-10 13:03:41 -07:00
if ( call - > args - > next ) {
arg_t * arg_spec = new ( arg_t , . name = " key " , . type = table - > key_type , . next = new ( arg_t , . name = " default " , . type = table - > value_type ) ) ;
return CORD_all ( " Table$get_value_or_default( " , self , " , " , compile_type ( table - > key_type ) , " , " , compile_type ( table - > value_type ) , " , " ,
compile_arguments ( env , ast , arg_spec , call - > args ) , " , " , compile_type_info ( env , self_value_t ) , " ) " ) ;
} else {
arg_t * arg_spec = new ( arg_t , . name = " key " , . type = table - > key_type ) ;
file_t * f = ast - > file ;
return CORD_all ( " Table$get_value_or_fail( " , self , " , " , compile_type ( table - > key_type ) , " , " , compile_type ( table - > value_type ) , " , " ,
compile_arguments ( env , ast , arg_spec , call - > args ) , " , " , compile_type_info ( env , self_value_t ) , " , " ,
Text $ quoted ( f - > filename , false ) , " , " , CORD_asprintf ( " %ld " , ( int64_t ) ( ast - > start - f - > text ) ) , " , " ,
CORD_asprintf ( " %ld " , ( int64_t ) ( ast - > end - f - > text ) ) ,
" ) " ) ;
}
2024-08-17 12:01:01 -07:00
} else if ( streq ( call - > name , " get_or_null " ) ) {
if ( table - > value_type - > tag ! = PointerType )
code_err ( ast , " The table method :get_or_null() is only supported for tables whose value type is a pointer, not %T " , table - > value_type ) ;
CORD self = compile_to_pointer_depth ( env , call - > self , 0 , false ) ;
arg_t * arg_spec = new ( arg_t , . name = " key " , . type = table - > key_type ) ;
return CORD_all ( " Table$get_value_or_default( " , self , " , " , compile_type ( table - > key_type ) , " , " , compile_type ( table - > value_type ) , " , " ,
compile_arguments ( env , ast , arg_spec , call - > args ) , " , NULL, " , compile_type_info ( env , self_value_t ) , " ) " ) ;
2024-08-10 12:15:38 -07:00
} else if ( streq ( call - > name , " has " ) ) {
CORD self = compile_to_pointer_depth ( env , call - > self , 0 , false ) ;
arg_t * arg_spec = new ( arg_t , . name = " key " , . type = table - > key_type ) ;
return CORD_all ( " Table$has_value( " , self , " , " , compile_arguments ( env , ast , arg_spec , call - > args ) , " , " ,
2024-03-08 23:48:09 -08:00
compile_type_info ( env , self_value_t ) , " ) " ) ;
2024-03-14 10:28:30 -07:00
} else if ( streq ( call - > name , " set " ) ) {
CORD self = compile_to_pointer_depth ( env , call - > self , 1 , false ) ;
2024-03-17 11:46:36 -07:00
arg_t * arg_spec = new ( arg_t , . name = " key " , . type = table - > key_type ,
. next = new ( arg_t , . name = " value " , . type = table - > value_type ) ) ;
2024-03-29 09:54:31 -07:00
return CORD_all ( " Table$set_value( " , self , " , " , compile_arguments ( env , ast , arg_spec , call - > args ) , " , " ,
2024-03-14 10:28:30 -07:00
compile_type_info ( env , self_value_t ) , " ) " ) ;
2024-08-10 13:03:41 -07:00
} else if ( streq ( call - > name , " bump " ) ) {
CORD self = compile_to_pointer_depth ( env , call - > self , 1 , false ) ;
if ( ! ( table - > value_type - > tag = = IntType | | table - > value_type - > tag = = NumType ) )
code_err ( ast , " bump() is only supported for tables with numeric value types, not %T " , self_value_t ) ;
ast_t * one = table - > value_type - > tag = = IntType
2024-08-12 22:30:25 -07:00
? FakeAST ( Int , . str = " 1 " , . bits = Match ( table - > value_type , IntType ) - > bits )
2024-08-10 13:03:41 -07:00
: FakeAST ( Num , . n = 1 , . bits = Match ( table - > value_type , NumType ) - > bits ) ;
arg_t * arg_spec = new ( arg_t , . name = " key " , . type = table - > key_type ,
. next = new ( arg_t , . name = " amount " , . type = table - > value_type , . default_val = one ) ) ;
return CORD_all ( " Table$bump( " , self , " , " , compile_arguments ( env , ast , arg_spec , call - > args ) , " , " ,
compile_type_info ( env , self_value_t ) , " ) " ) ;
2024-03-14 10:28:30 -07:00
} else if ( streq ( call - > name , " remove " ) ) {
CORD self = compile_to_pointer_depth ( env , call - > self , 1 , false ) ;
2024-08-08 10:20:38 -07:00
arg_t * arg_spec = new ( arg_t , . name = " key " , . type = table - > key_type ) ;
return CORD_all ( " Table$remove_value( " , self , " , " , compile_arguments ( env , ast , arg_spec , call - > args ) , " , " ,
2024-03-14 10:28:30 -07:00
compile_type_info ( env , self_value_t ) , " ) " ) ;
} else if ( streq ( call - > name , " clear " ) ) {
CORD self = compile_to_pointer_depth ( env , call - > self , 1 , false ) ;
2024-04-02 10:08:06 -07:00
( void ) compile_arguments ( env , ast , NULL , call - > args ) ;
2024-03-29 09:54:31 -07:00
return CORD_all ( " Table$clear( " , self , " ) " ) ;
2024-08-03 14:42:45 -07:00
} else if ( streq ( call - > name , " sorted " ) ) {
CORD self = compile_to_pointer_depth ( env , call - > self , 0 , false ) ;
( void ) compile_arguments ( env , ast , NULL , call - > args ) ;
return CORD_all ( " Table$sorted( " , self , " , " , compile_type_info ( env , self_value_t ) , " ) " ) ;
2024-03-14 10:28:30 -07:00
} else code_err ( ast , " There is no '%s' method for tables " , call - > name ) ;
2024-02-22 09:45:12 -08:00
}
2024-03-14 10:28:30 -07:00
default : {
auto methodcall = Match ( ast , MethodCall ) ;
type_t * fn_t = get_method_type ( env , methodcall - > self , methodcall - > name ) ;
arg_ast_t * args = new ( arg_ast_t , . value = methodcall - > self , . next = methodcall - > args ) ;
binding_t * b = get_namespace_binding ( env , methodcall - > self , methodcall - > name ) ;
if ( ! b ) code_err ( ast , " No such method " ) ;
return CORD_all ( b - > code , " ( " , compile_arguments ( env , ast , Match ( fn_t , FunctionType ) - > args , args ) , " ) " ) ;
2024-02-22 09:45:12 -08:00
}
2024-02-18 01:26:26 -08:00
}
2024-03-14 10:28:30 -07:00
}
case FunctionCall : {
auto call = Match ( ast , FunctionCall ) ;
type_t * fn_t = get_type ( env , call - > fn ) ;
if ( fn_t - > tag = = FunctionType ) {
CORD fn = compile ( env , call - > fn ) ;
return CORD_all ( fn , " ( " , compile_arguments ( env , ast , Match ( fn_t , FunctionType ) - > args , call - > args ) , " ) " ) ;
} else if ( fn_t - > tag = = TypeInfoType ) {
type_t * t = Match ( fn_t , TypeInfoType ) - > type ;
2024-04-23 10:12:49 -07:00
if ( t - > tag = = StructType ) {
2024-05-12 10:50:06 -07:00
// Struct constructor:
2024-04-23 10:12:49 -07:00
fn_t = Type ( FunctionType , . args = Match ( t , StructType ) - > fields , . ret = t ) ;
2024-06-16 15:09:54 -07:00
return CORD_all ( " (( " , compile_type ( t ) , " ) { " , compile_arguments(env, ast, Match(fn_t, FunctionType)->args, call->args), " } ) " );
2024-08-14 11:57:01 -07:00
} else if ( t - > tag = = NumType | | ( t - > tag = = IntType & & Match ( t , IntType ) - > bits = = 0 ) ) {
2024-08-12 23:20:48 -07:00
type_t * actual = get_type ( env , call - > args - > value ) ;
2024-08-14 11:57:01 -07:00
arg_t * args = new ( arg_t , . name = " i " , . type = actual ) ; // No truncation argument
CORD arg_code = compile_arguments ( env , ast , args , call - > args ) ;
return CORD_all ( type_to_cord ( actual ) , " _to_ " , type_to_cord ( t ) , " ( " , arg_code , " ) " ) ;
} else if ( t - > tag = = IntType ) {
2024-04-23 10:12:49 -07:00
type_t * actual = get_type ( env , call - > args - > value ) ;
2024-08-14 11:57:01 -07:00
arg_t * args = new ( arg_t , . name = " i " , . type = actual , . next = new ( arg_t , . name = " truncate " , . type = Type ( BoolType ) ,
. default_val = FakeAST ( Bool , false ) ) ) ;
CORD arg_code = compile_arguments ( env , ast , args , call - > args ) ;
return CORD_all ( type_to_cord ( actual ) , " _to_ " , type_to_cord ( t ) , " ( " , arg_code , " ) " ) ;
2024-05-18 11:38:41 -07:00
} else if ( t - > tag = = TextType ) {
// Text constructor:
if ( ! call - > args | | call - > args - > next )
code_err ( call - > fn , " This constructor takes exactly 1 argument " ) ;
type_t * actual = get_type ( env , call - > args - > value ) ;
return expr_as_text ( env , compile ( env , call - > args - > value ) , actual , " no " ) ;
} else if ( t - > tag = = CStringType ) {
// C String constructor:
if ( ! call - > args | | call - > args - > next )
code_err ( call - > fn , " This constructor takes exactly 1 argument " ) ;
type_t * actual = get_type ( env , call - > args - > value ) ;
return CORD_all ( " CORD_to_char_star( " , expr_as_text ( env , compile ( env , call - > args - > value ) , actual , " no " ) , " ) " ) ;
2024-04-23 10:12:49 -07:00
} else {
2024-03-14 10:28:30 -07:00
code_err ( call - > fn , " This is not a type that has a constructor " ) ;
2024-04-23 10:12:49 -07:00
}
2024-03-14 10:28:30 -07:00
} else if ( fn_t - > tag = = ClosureType ) {
fn_t = Match ( fn_t , ClosureType ) - > fn ;
arg_t * type_args = Match ( fn_t , FunctionType ) - > args ;
2024-02-18 01:26:26 -08:00
2024-03-14 10:28:30 -07:00
arg_t * closure_fn_args = NULL ;
for ( arg_t * arg = Match ( fn_t , FunctionType ) - > args ; arg ; arg = arg - > next )
closure_fn_args = new ( arg_t , . name = arg - > name , . type = arg - > type , . default_val = arg - > default_val , . next = closure_fn_args ) ;
2024-04-16 10:50:07 -07:00
closure_fn_args = new ( arg_t , . name = " userdata " , . type = Type ( PointerType , . pointed = Type ( MemoryType ) ) , . next = closure_fn_args ) ;
2024-03-14 10:28:30 -07:00
REVERSE_LIST ( closure_fn_args ) ;
2024-06-16 15:09:54 -07:00
CORD fn_type_code = compile_type ( Type ( FunctionType , . args = closure_fn_args , . ret = Match ( fn_t , FunctionType ) - > ret ) ) ;
2024-03-14 10:28:30 -07:00
CORD closure = compile ( env , call - > fn ) ;
CORD arg_code = compile_arguments ( env , ast , type_args , call - > args ) ;
if ( arg_code ) arg_code = CORD_cat ( arg_code , " , " ) ;
if ( call - > fn - > tag = = Var ) {
return CORD_all ( " (( " , fn_type_code , " ) " , closure, " . fn ) ( " , arg_code, closure, " . userdata ) " ) ;
2024-03-06 09:56:30 -08:00
} else {
2024-04-16 10:50:07 -07:00
return CORD_all ( " ({ closure_t closure = " , closure , " ; (( " , fn_type_code , " )closure.fn)( " ,
arg_code , " closure.userdata); }) " ) ;
2024-03-06 09:56:30 -08:00
}
2024-03-14 10:28:30 -07:00
} else {
code_err ( call - > fn , " This is not a function, it's a %T " , fn_t ) ;
2024-02-18 01:26:26 -08:00
}
2024-03-14 10:28:30 -07:00
}
case When :
code_err ( ast , " 'when' expressions are not yet implemented " ) ;
case If : {
auto if_ = Match ( ast , If ) ;
if ( ! if_ - > else_body )
code_err ( ast , " 'if' expressions can only be used if you also have an 'else' block " ) ;
type_t * t = get_type ( env , ast ) ;
if ( t - > tag = = VoidType | | t - > tag = = AbortType )
code_err ( ast , " This expression has a %T type, but it needs to have a real value " , t ) ;
2024-05-23 21:03:46 -07:00
type_t * true_type = get_type ( env , if_ - > body ) ;
type_t * false_type = get_type ( env , if_ - > else_body ) ;
2024-07-13 15:43:50 -07:00
if ( true_type - > tag = = AbortType | | true_type - > tag = = ReturnType )
2024-05-23 21:03:46 -07:00
return CORD_all ( " ({ if ( " , compile ( env , if_ - > condition ) , " ) " , compile_statement ( env , if_ - > body ) ,
" \n " , compile ( env , if_ - > else_body ) , " ; }) " ) ;
2024-07-13 15:43:50 -07:00
else if ( false_type - > tag = = AbortType | | false_type - > tag = = ReturnType )
2024-05-23 21:03:46 -07:00
return CORD_all ( " ({ if (!( " , compile ( env , if_ - > condition ) , " )) " , compile_statement ( env , if_ - > else_body ) ,
" \n " , compile ( env , if_ - > body ) , " ; }) " ) ;
else
return CORD_all ( " (( " , compile ( env , if_ - > condition ) , " ) ? " ,
compile ( env , if_ - > body ) , " : " , compile ( env , if_ - > else_body ) , " ) " ) ;
2024-02-04 18:13:50 -08:00
}
2024-02-25 12:28:46 -08:00
case Reduction : {
auto reduction = Match ( ast , Reduction ) ;
type_t * t = get_type ( env , ast ) ;
CORD code = CORD_all (
" ({ // Reduction: \n " ,
2024-06-16 15:09:54 -07:00
compile_declaration ( t , " reduction " ) , " ; \n "
2024-07-13 16:58:21 -07:00
" Bool_t is_first = yes; \n "
2024-02-25 12:28:46 -08:00
) ;
env_t * scope = fresh_scope ( env ) ;
2024-03-05 11:46:01 -08:00
ast_t * result = FakeAST ( Var , " $reduction " ) ;
2024-04-16 10:50:07 -07:00
set_binding ( scope , " $reduction " , new ( binding_t , . type = t , . code = " reduction " ) ) ;
2024-02-25 13:02:36 -08:00
ast_t * empty = NULL ;
2024-02-25 12:28:46 -08:00
if ( reduction - > fallback ) {
type_t * fallback_type = get_type ( scope , reduction - > fallback ) ;
2024-07-13 15:43:50 -07:00
if ( fallback_type - > tag = = AbortType | | fallback_type - > tag = = ReturnType ) {
2024-02-25 13:02:36 -08:00
empty = reduction - > fallback ;
2024-02-25 12:28:46 -08:00
} else {
2024-02-25 13:02:36 -08:00
empty = FakeAST ( Assign , . targets = new ( ast_list_t , . ast = result ) , . values = new ( ast_list_t , . ast = reduction - > fallback ) ) ;
2024-02-25 12:28:46 -08:00
}
} else {
2024-02-25 13:02:36 -08:00
empty = FakeAST (
InlineCCode ,
2024-03-21 10:32:52 -07:00
CORD_asprintf ( " fail_source(%r, %ld, %ld, \" This collection was empty! \" ); \n " ,
2024-03-29 09:54:31 -07:00
Text $ quoted ( ast - > file - > filename , false ) , ( long ) ( reduction - > iter - > start - reduction - > iter - > file - > text ) ,
2024-02-25 13:02:36 -08:00
( long ) ( reduction - > iter - > end - reduction - > iter - > file - > text ) ) ) ;
2024-02-25 12:28:46 -08:00
}
2024-03-05 11:46:01 -08:00
ast_t * item = FakeAST ( Var , " $iter_value " ) ;
2024-07-20 13:45:13 -07:00
ast_t * body = FakeAST ( InlineCCode , . code = " {} " ) ; // placeholder
2024-07-13 16:58:21 -07:00
ast_t * loop = FakeAST ( For , . vars = new ( ast_list_t , . ast = item ) , . iter = reduction - > iter , . body = body , . empty = empty ) ;
2024-07-20 13:45:13 -07:00
env_t * body_scope = for_scope ( scope , loop ) ;
body - > __data . InlineCCode . code = CORD_all (
" if (is_first) { \n "
" reduction = " , compile ( body_scope , item ) , " ; \n "
" is_first = no; \n "
" } else { \n "
" reduction = " , compile ( body_scope , reduction - > combination ) , " ; \n "
" } \n " ) ;
2024-04-16 10:50:07 -07:00
code = CORD_all ( code , compile_statement ( scope , loop ) , " \n reduction;}) " ) ;
2024-02-25 12:28:46 -08:00
return code ;
}
2024-02-11 22:46:32 -08:00
case FieldAccess : {
auto f = Match ( ast , FieldAccess ) ;
2024-02-17 17:47:43 -08:00
type_t * fielded_t = get_type ( env , f - > fielded ) ;
2024-02-20 10:06:03 -08:00
type_t * value_t = value_type ( fielded_t ) ;
switch ( value_t - > tag ) {
2024-03-03 10:04:50 -08:00
case TypeInfoType : {
auto info = Match ( value_t , TypeInfoType ) ;
2024-08-17 18:03:04 -07:00
if ( f - > field [ 0 ] = = ' _ ' ) {
for ( table_t * locals = env - > locals ; locals ; locals = locals - > fallback ) {
if ( locals = = info - > env - > locals )
goto is_inside_type ;
}
code_err ( ast , " Fields that start with underscores are not accessible on types outside of the type definition. " , f - > field ) ;
is_inside_type : ;
}
2024-03-21 10:33:10 -07:00
binding_t * b = get_binding ( info - > env , f - > field ) ;
2024-03-03 10:04:50 -08:00
if ( ! b ) code_err ( ast , " I couldn't find the field '%s' on this type " , f - > field ) ;
if ( ! b - > code ) code_err ( ast , " I couldn't figure out how to compile this field " ) ;
return b - > code ;
}
2024-02-20 10:06:03 -08:00
case StructType : {
for ( arg_t * field = Match ( value_t , StructType ) - > fields ; field ; field = field - > next ) {
if ( streq ( field - > name , f - > field ) ) {
if ( fielded_t - > tag = = PointerType ) {
CORD fielded = compile_to_pointer_depth ( env , f - > fielded , 1 , false ) ;
2024-08-03 13:39:04 -07:00
return CORD_asprintf ( " (%r) - > $ % s " , fielded, f->field) ;
2024-02-20 10:06:03 -08:00
} else {
CORD fielded = compile ( env , f - > fielded ) ;
2024-08-03 13:39:04 -07:00
return CORD_asprintf ( " (%r) . $ % s " , fielded, f->field) ;
2024-02-20 10:06:03 -08:00
}
}
}
code_err ( ast , " The field '%s' is not a valid field name of %T " , f - > field , value_t ) ;
}
2024-08-10 12:15:38 -07:00
case SetType : {
2024-08-12 15:00:54 -07:00
if ( streq ( f - > field , " items " ) )
return CORD_all ( " ( " , compile_to_pointer_depth ( env , f - > fielded , 0 , false ) , " ).entries " ) ;
2024-08-10 12:15:38 -07:00
code_err ( ast , " There is no '%s' field on sets " , f - > field ) ;
}
2024-02-25 11:35:25 -08:00
case TableType : {
if ( streq ( f - > field , " keys " ) ) {
2024-08-12 14:57:14 -07:00
return CORD_all ( " ( " , compile_to_pointer_depth ( env , f - > fielded , 0 , false ) , " ).entries " ) ;
2024-02-25 11:35:25 -08:00
} else if ( streq ( f - > field , " values " ) ) {
auto table = Match ( value_t , TableType ) ;
size_t offset = type_size ( table - > key_type ) ;
size_t align = type_align ( table - > value_type ) ;
if ( align > 1 & & offset % align > 0 )
offset + = align - ( offset % align ) ;
2024-08-12 14:57:14 -07:00
return CORD_all ( " ({ array_t *entries = &( " , compile_to_pointer_depth ( env , f - > fielded , 0 , false ) , " ).entries ; \ n "
" ARRAY_INCREF(*entries); \n "
" array_t values = *entries; \n "
" values.data += " , CORD_asprintf ( " %zu " , offset ) , " ; \n "
" values; }) " ) ;
2024-02-25 11:35:25 -08:00
} else if ( streq ( f - > field , " fallback " ) ) {
return CORD_all ( " ( " , compile_to_pointer_depth ( env , f - > fielded , 0 , false ) , " ).fallback " ) ;
}
code_err ( ast , " There is no '%s' field on tables " , f - > field ) ;
}
2024-03-19 11:22:03 -07:00
case ModuleType : {
const char * name = Match ( value_t , ModuleType ) - > name ;
2024-03-29 09:54:31 -07:00
env_t * module_env = Table $ str_get ( * env - > imports , name ) ;
2024-03-19 11:22:03 -07:00
return compile ( module_env , WrapAST ( ast , Var , f - > field ) ) ;
}
2024-02-20 10:06:03 -08:00
default :
2024-03-19 11:22:03 -07:00
code_err ( ast , " Field accesses are not supported on %T values " , fielded_t ) ;
2024-02-17 17:47:43 -08:00
}
2024-02-11 22:46:32 -08:00
}
2024-02-18 11:28:35 -08:00
case Index : {
auto indexing = Match ( ast , Index ) ;
2024-02-25 09:20:25 -08:00
type_t * indexed_type = get_type ( env , indexing - > indexed ) ;
2024-08-12 14:44:05 -07:00
if ( ! indexing - > index ) {
if ( indexed_type - > tag ! = PointerType )
code_err ( ast , " Only pointers can use the '[]' operator to dereference the entire value. " ) ;
2024-02-29 10:00:28 -08:00
auto ptr = Match ( indexed_type , PointerType ) ;
if ( ptr - > is_optional )
code_err ( ast , " This pointer is potentially null, so it can't be safely dereferenced " ) ;
if ( ptr - > pointed - > tag = = ArrayType ) {
2024-08-04 14:23:42 -07:00
return CORD_all ( " ({ array_t *arr = " , compile ( env , indexing - > indexed ) , " ; ARRAY_INCREF ( * arr ) ; * arr ; } ) " );
2024-08-12 14:44:05 -07:00
} else if ( ptr - > pointed - > tag = = TableType | | ptr - > pointed - > tag = = SetType ) {
return CORD_all ( " ({ table_t *t = " , compile ( env , indexing - > indexed ) , " ; TABLE_INCREF ( * t ) ; * t ; } ) " );
2024-02-29 10:00:28 -08:00
} else {
return CORD_all ( " *( " , compile ( env , indexing - > indexed ) , " ) " ) ;
}
2024-02-25 09:20:25 -08:00
}
2024-08-12 14:44:05 -07:00
2024-02-25 09:20:25 -08:00
type_t * container_t = value_type ( indexed_type ) ;
2024-02-18 11:28:35 -08:00
type_t * index_t = get_type ( env , indexing - > index ) ;
2024-08-10 13:03:41 -07:00
if ( container_t - > tag = = ArrayType ) {
2024-02-18 11:28:35 -08:00
if ( index_t - > tag ! = IntType )
code_err ( indexing - > index , " Arrays can only be indexed by integers, not %T " , index_t ) ;
type_t * item_type = Match ( container_t , ArrayType ) - > item_type ;
2024-02-25 10:04:35 -08:00
CORD arr = compile_to_pointer_depth ( env , indexing - > indexed , 0 , false ) ;
2024-02-18 11:28:35 -08:00
CORD index = compile ( env , indexing - > index ) ;
2024-02-18 11:53:52 -08:00
file_t * f = indexing - > index - > file ;
if ( indexing - > unchecked )
2024-07-10 10:47:15 -07:00
return CORD_all ( " Array_get_unchecked( " , compile_type ( item_type ) , " , " , arr , " , " , index , " ) " ) ;
2024-02-18 11:53:52 -08:00
else
2024-06-16 15:09:54 -07:00
return CORD_all ( " Array_get( " , compile_type ( item_type ) , " , " , arr , " , " , index , " , " ,
2024-03-29 09:54:31 -07:00
Text $ quoted ( f - > filename , false ) , " , " , CORD_asprintf ( " %ld " , ( int64_t ) ( indexing - > index - > start - f - > text ) ) , " , " ,
2024-02-18 11:53:52 -08:00
CORD_asprintf ( " %ld " , ( int64_t ) ( indexing - > index - > end - f - > text ) ) ,
" ) " ) ;
2024-08-10 13:03:41 -07:00
} else {
code_err ( ast , " Indexing is not supported for type: %T " , container_t ) ;
2024-02-18 11:28:35 -08:00
}
}
2024-05-18 13:31:34 -07:00
case InlineCCode : {
type_t * t = get_type ( env , ast ) ;
if ( t - > tag = = VoidType )
return CORD_all ( " { \n " , Match ( ast , InlineCCode ) - > code , " \n } " ) ;
else
return Match ( ast , InlineCCode ) - > code ;
}
2024-05-27 14:45:22 -07:00
case Use : code_err ( ast , " Compiling 'use' as expression! " ) ;
2024-06-13 10:17:51 -07:00
case Import : code_err ( ast , " Compiling 'import' as expression! " ) ;
2024-07-04 15:00:01 -07:00
case Defer : code_err ( ast , " Compiling 'defer' as expression! " ) ;
2024-02-29 10:49:24 -08:00
case LinkerDirective : code_err ( ast , " Linker directives are not supported yet " ) ;
2024-05-18 11:38:41 -07:00
case Extern : code_err ( ast , " Externs are not supported as expressions " ) ;
2024-02-29 10:49:24 -08:00
case TableEntry : code_err ( ast , " Table entries should not be compiled directly " ) ;
2024-03-14 10:28:30 -07:00
case Declare : case Assign : case UpdateAssign : case For : case While : case StructDef : case LangDef :
2024-07-04 13:23:05 -07:00
case EnumDef : case FunctionDef : case Skip : case Stop : case Pass : case Return : case DocTest : case PrintStatement :
2024-03-14 10:28:30 -07:00
code_err ( ast , " This is not a valid expression " ) ;
2024-03-09 11:02:19 -08:00
case Unknown : code_err ( ast , " Unknown AST " ) ;
2024-02-04 12:23:59 -08:00
}
2024-02-29 10:49:24 -08:00
code_err ( ast , " Unknown AST: %W " , ast ) ;
2024-04-24 10:53:37 -07:00
return CORD_EMPTY ;
2024-02-04 12:23:59 -08:00
}
2024-03-06 09:41:18 -08:00
void compile_namespace ( env_t * env , const char * ns_name , ast_t * block )
{
env_t * ns_env = namespace_env ( env , ns_name ) ;
2024-08-17 11:41:31 -07:00
CORD prefix = namespace_prefix ( ns_env - > libname , ns_env - > namespace ) ;
// First prepare variable initializers to prevent unitialized access:
for ( ast_list_t * stmt = block ? Match ( block , Block ) - > statements : NULL ; stmt ; stmt = stmt - > next ) {
if ( stmt - > ast - > tag = = Declare ) {
auto decl = Match ( stmt - > ast , Declare ) ;
type_t * t = get_type ( ns_env , decl - > value ) ;
if ( t - > tag = = AbortType | | t - > tag = = VoidType | | t - > tag = = ReturnType )
code_err ( stmt - > ast , " You can't declare a variable with a %T value " , t ) ;
CORD name_code = CORD_all ( prefix , Match ( decl - > var , Var ) - > name ) ;
if ( ! is_constant ( env , decl - > value ) ) {
env - > code - > variable_initializers = CORD_all (
env - > code - > variable_initializers ,
name_code , " = " , compile_maybe_incref ( ns_env , decl - > value ) , " , \n " ,
name_code , " $initialized = true; \n " ) ;
CORD checked_access = CORD_all ( " check_initialized( " , name_code , " , \" " , Match ( decl - > var , Var ) - > name , " \" ) " ) ;
set_binding ( ns_env , Match ( decl - > var , Var ) - > name , new ( binding_t , . type = t , . code = checked_access ) ) ;
}
}
}
2024-03-06 09:41:18 -08:00
for ( ast_list_t * stmt = block ? Match ( block , Block ) - > statements : NULL ; stmt ; stmt = stmt - > next ) {
ast_t * ast = stmt - > ast ;
switch ( ast - > tag ) {
2024-03-09 20:21:44 -08:00
case FunctionDef : {
2024-03-06 09:41:18 -08:00
CORD code = compile_statement ( ns_env , ast ) ;
env - > code - > funcs = CORD_cat ( env - > code - > funcs , code ) ;
break ;
2024-03-09 20:21:44 -08:00
}
2024-03-06 09:41:18 -08:00
case Declare : {
auto decl = Match ( ast , Declare ) ;
type_t * t = get_type ( ns_env , decl - > value ) ;
2024-08-17 18:03:04 -07:00
bool is_private = ( Match ( decl - > var , Var ) - > name [ 0 ] = = ' _ ' ) ;
2024-08-17 11:41:31 -07:00
CORD name_code = CORD_all ( prefix , Match ( decl - > var , Var ) - > name ) ;
if ( ! is_constant ( env , decl - > value ) ) {
env - > code - > staticdefs = CORD_all (
env - > code - > staticdefs ,
" static bool " , name_code , " $initialized = false; \n " ,
2024-08-17 18:03:04 -07:00
is_private ? " static " : CORD_EMPTY ,
2024-08-17 11:41:31 -07:00
compile_declaration ( t , name_code ) , " ; \n " ) ;
} else {
env - > code - > staticdefs = CORD_all (
env - > code - > staticdefs ,
2024-08-17 18:03:04 -07:00
is_private ? " static " : CORD_EMPTY ,
2024-08-17 11:41:31 -07:00
compile_declaration ( t , name_code ) , " = " ,
compile ( ns_env , decl - > value ) , " ; \n " ) ;
}
2024-03-06 09:41:18 -08:00
break ;
}
default : {
CORD code = compile_statement ( ns_env , ast ) ;
2024-04-12 10:09:31 -07:00
assert ( ! code ) ;
2024-03-06 09:41:18 -08:00
break ;
}
2024-06-06 13:28:53 -07:00
}
2024-03-06 09:41:18 -08:00
}
2024-06-06 13:28:53 -07:00
}
2024-07-23 16:46:42 -07:00
CORD compile_namespace_definitions ( env_t * env , const char * ns_name , ast_t * block )
2024-06-06 13:28:53 -07:00
{
env_t * ns_env = namespace_env ( env , ns_name ) ;
CORD header = CORD_EMPTY ;
for ( ast_list_t * stmt = block ? Match ( block , Block ) - > statements : NULL ; stmt ; stmt = stmt - > next ) {
2024-07-23 16:46:42 -07:00
header = CORD_all ( header , compile_statement_definitions ( ns_env , stmt - > ast ) ) ;
2024-03-06 09:41:18 -08:00
}
2024-06-06 13:28:53 -07:00
return header ;
2024-03-06 09:41:18 -08:00
}
2024-02-17 15:38:29 -08:00
CORD compile_type_info ( env_t * env , type_t * t )
{
switch ( t - > tag ) {
2024-05-18 11:38:41 -07:00
case BoolType : case IntType : case NumType : case CStringType :
return CORD_asprintf ( " &$%r " , type_to_cord ( t ) ) ;
2024-03-21 22:52:00 -07:00
case TextType : {
auto text = Match ( t , TextType ) ;
2024-06-16 15:09:54 -07:00
return text - > lang ? CORD_all ( " (& " , namespace_prefix ( text - > env - > libname , text - > env - > namespace - > parent ) , text - > lang , " ) " ) : " &$Text " ;
2024-03-21 22:52:00 -07:00
}
case StructType : {
auto s = Match ( t , StructType ) ;
2024-06-16 15:09:54 -07:00
return CORD_all ( " (& " , namespace_prefix ( s - > env - > libname , s - > env - > namespace - > parent ) , s - > name , " ) " ) ;
2024-03-21 22:52:00 -07:00
}
case EnumType : {
auto e = Match ( t , EnumType ) ;
2024-06-16 15:09:54 -07:00
return CORD_all ( " (& " , namespace_prefix ( e - > env - > libname , e - > env - > namespace - > parent ) , e - > name , " ) " ) ;
2024-03-21 22:52:00 -07:00
}
2024-02-17 15:38:29 -08:00
case ArrayType : {
type_t * item_t = Match ( t , ArrayType ) - > item_type ;
2024-08-10 12:15:38 -07:00
return CORD_all ( " $ArrayInfo( " , compile_type_info ( env , item_t ) , " ) " ) ;
}
case SetType : {
type_t * item_type = Match ( t , SetType ) - > item_type ;
return CORD_all ( " $SetInfo( " , compile_type_info ( env , item_type ) , " ) " ) ;
2024-02-17 15:38:29 -08:00
}
2024-08-11 11:47:34 -07:00
case ChannelType : {
type_t * item_t = Match ( t , ChannelType ) - > item_type ;
return CORD_asprintf ( " $ChannelInfo(%r) " , compile_type_info(env, item_t)) ;
}
2024-02-17 15:38:29 -08:00
case TableType : {
type_t * key_type = Match ( t , TableType ) - > key_type ;
type_t * value_type = Match ( t , TableType ) - > value_type ;
2024-08-10 12:15:38 -07:00
return CORD_all ( " $TableInfo( " , compile_type_info ( env , key_type ) , " , " , compile_type_info ( env , value_type ) , " ) " ) ;
2024-02-17 15:38:29 -08:00
}
case PointerType : {
auto ptr = Match ( t , PointerType ) ;
2024-04-30 10:18:47 -07:00
CORD sigil = ptr - > is_stack ? " & " : " @ " ;
2024-04-02 10:08:06 -07:00
if ( ptr - > is_readonly ) sigil = CORD_cat ( sigil , " % " ) ;
2024-04-30 10:18:47 -07:00
return CORD_asprintf ( " $PointerInfo(%r, %r, %s) " ,
Text $ quoted ( sigil , false ) ,
compile_type_info ( env , ptr - > pointed ) ,
ptr - > is_optional ? " yes " : " no " ) ;
2024-02-17 15:38:29 -08:00
}
case FunctionType : {
2024-03-29 09:54:31 -07:00
return CORD_asprintf ( " $FunctionInfo(%r) " , Text$quoted(type_to_cord(t), false)) ;
2024-02-17 15:38:29 -08:00
}
case ClosureType : {
2024-03-29 09:54:31 -07:00
return CORD_asprintf ( " $ClosureInfo(%r) " , Text$quoted(type_to_cord(t), false)) ;
2024-02-17 15:38:29 -08:00
}
2024-03-29 09:54:31 -07:00
case TypeInfoType : return " &$TypeInfo " ;
2024-04-21 11:58:33 -07:00
case MemoryType : return " &$Memory " ;
case VoidType : return " &$Void " ;
2024-02-26 20:10:19 -08:00
default :
compiler_err ( NULL , 0 , 0 , " I couldn't convert to a type info: %T " , t ) ;
2024-02-17 15:38:29 -08:00
}
}
2024-02-17 14:29:56 -08:00
2024-04-20 11:55:27 -07:00
CORD compile_cli_arg_call ( env_t * env , CORD fn_name , type_t * fn_type )
2024-04-12 10:09:31 -07:00
{
2024-04-20 11:55:27 -07:00
auto fn_info = Match ( fn_type , FunctionType ) ;
2024-04-20 12:00:31 -07:00
if ( ! fn_info - > args ) {
return CORD_all (
" if (argc > 1) \n "
" errx(1, \" This program doesn't take any arguments. \" ); \n " ,
fn_name , " (); \n " ) ;
}
2024-04-12 10:09:31 -07:00
env_t * main_env = fresh_scope ( env ) ;
CORD usage = CORD_EMPTY ;
for ( arg_t * arg = fn_info - > args ; arg ; arg = arg - > next ) {
usage = CORD_cat ( usage , " " ) ;
type_t * t = get_arg_type ( main_env , arg ) ;
2024-08-12 22:30:25 -07:00
CORD flag = Text $ replace ( arg - > name , " _ " , " - " , I ( - 1 ) ) ;
2024-04-12 10:09:31 -07:00
if ( arg - > default_val ) {
if ( t - > tag = = BoolType )
usage = CORD_all ( usage , " [-- " , flag , " ] " ) ;
else
usage = CORD_all ( usage , " [-- " , flag , " =...] " ) ;
} else {
if ( t - > tag = = BoolType )
usage = CORD_all ( usage , " [-- " , flag , " |--no- " , flag , " ] " ) ;
2024-04-12 10:43:23 -07:00
else if ( t - > tag = = ArrayType )
usage = CORD_all ( usage , " < " , flag , " ...> " ) ;
2024-04-12 10:09:31 -07:00
else
usage = CORD_all ( usage , " < " , flag , " > " ) ;
}
}
2024-04-20 11:55:27 -07:00
CORD code = CORD_all ( " CORD usage = CORD_all( \" Usage: \" , argv[0], " , usage ? Text $ quoted ( usage , false ) : " CORD_EMPTY " , " ); \n " ,
" #define USAGE_ERR(...) errx(1, CORD_to_const_char_star(CORD_all(__VA_ARGS__))) \n "
" #define IS_FLAG(str, flag) (strncmp(str, flag, strlen(flag) == 0 && (str[strlen(flag)] == 0 || str[strlen(flag)] == '=')) == 0) \n " ) ;
2024-04-12 10:09:31 -07:00
// Declare args:
for ( arg_t * arg = fn_info - > args ; arg ; arg = arg - > next ) {
type_t * t = get_arg_type ( main_env , arg ) ;
assert ( arg - > name ) ;
code = CORD_all (
2024-06-16 15:09:54 -07:00
code , compile_declaration ( t , CORD_cat ( " $ " , arg - > name ) ) , " ; \n " ,
2024-04-12 10:09:31 -07:00
" bool " , arg - > name , " $is_set = no; \n " ) ;
2024-04-16 10:50:07 -07:00
set_binding ( env , arg - > name , new ( binding_t , . type = t , . code = CORD_cat ( " $ " , arg - > name ) ) ) ;
2024-04-12 10:09:31 -07:00
}
// Provide --flags:
2024-04-16 10:50:07 -07:00
code = CORD_all ( code , " CORD flag; \n "
2024-04-12 10:09:31 -07:00
" for (int i = 1; i < argc; ) { \n "
" if (streq(argv[i], \" -- \" )) { \n "
" argv[i] = NULL; \n "
" break; \n "
" } \n "
" if (strncmp(argv[i], \" -- \" , 2) != 0) { \n ++i; \n continue; \n } \n " ) ;
for ( arg_t * arg = fn_info - > args ; arg ; arg = arg - > next ) {
type_t * t = get_arg_type ( main_env , arg ) ;
2024-08-12 22:30:25 -07:00
CORD flag = Text $ replace ( arg - > name , " _ " , " - " , I ( - 1 ) ) ;
2024-04-12 10:09:31 -07:00
switch ( t - > tag ) {
case BoolType : {
2024-04-16 10:50:07 -07:00
code = CORD_all ( code , " else if (pop_flag(argv, &i, \" " , flag , " \" , &flag)) { \n "
" if (flag) { \n " ,
2024-04-17 10:52:07 -07:00
" $ " , arg - > name , " = Bool$from_text(flag, & " , arg - > name , " $is_set " , " ); \n "
2024-04-12 10:09:31 -07:00
" if (! " , arg - > name , " $is_set) \n "
2024-04-16 10:50:07 -07:00
" USAGE_ERR( \" Invalid argument for '-- " , flag , " ' \\ n \" , usage); \n " ,
2024-04-12 10:09:31 -07:00
" } else { \n " ,
2024-04-17 10:52:07 -07:00
" $ " , arg - > name , " = yes; \n " ,
2024-04-12 10:09:31 -07:00
arg - > name , " $is_set = yes; \n "
" } \n "
" } \n " ) ;
break ;
}
case TextType : {
2024-04-16 10:50:07 -07:00
code = CORD_all ( code , " else if (pop_flag(argv, &i, \" " , flag , " \" , &flag)) { \n " ,
2024-04-17 10:52:07 -07:00
" $ " , arg - > name , " = CORD_to_const_char_star(flag); \n " ,
2024-04-12 10:09:31 -07:00
arg - > name , " $is_set = yes; \n "
" } \n " ) ;
break ;
}
2024-04-12 10:43:23 -07:00
case ArrayType : {
if ( Match ( t , ArrayType ) - > item_type - > tag ! = TextType )
compiler_err ( NULL , NULL , NULL , " Main function has unsupported argument type: %T (only arrays of Text are supported) " , t ) ;
2024-04-16 10:50:07 -07:00
code = CORD_all ( code , " else if (pop_flag(argv, &i, \" " , flag , " \" , &flag)) { \n " ,
2024-04-17 10:52:07 -07:00
" $ " , arg - > name , " = Text$split(CORD_to_const_char_star(flag), \" , \" ); \n " ,
2024-04-12 10:43:23 -07:00
arg - > name , " $is_set = yes; \n "
" } \n " ) ;
break ;
}
2024-04-12 10:09:31 -07:00
case IntType : case NumType : {
CORD type_name = type_to_cord ( t ) ;
2024-04-16 10:50:07 -07:00
code = CORD_all ( code , " else if (pop_flag(argv, &i, \" " , flag , " \" , &flag)) { \n " ,
" if (flag == CORD_EMPTY) \n "
" USAGE_ERR( \" No value provided for '-- " , flag , " ' \\ n \" , usage); \n "
" CORD invalid = CORD_EMPTY; \n " ,
2024-04-17 10:52:07 -07:00
" $ " , arg - > name , " = " , type_name , " $from_text(flag, &invalid); \n "
2024-04-16 10:50:07 -07:00
" if (invalid != CORD_EMPTY) \n "
" USAGE_ERR( \" Invalid value provided for '-- " , flag , " ' \\ n \" , usage); \n " ,
2024-04-12 10:09:31 -07:00
arg - > name , " $is_set = yes; \n "
" } \n " ) ;
break ;
}
default :
compiler_err ( NULL , NULL , NULL , " Main function has unsupported argument type: %T " , t ) ;
}
}
code = CORD_all (
code , " else { \n "
2024-04-16 10:50:07 -07:00
" USAGE_ERR( \" Unrecognized argument: \" , argv[i], \" \\ n \" , usage); \n "
2024-04-12 10:09:31 -07:00
" } \n "
" } \n "
" int i = 1; \n "
" while (i < argc && argv[i] == NULL) \n "
" ++i; \n " ) ;
for ( arg_t * arg = fn_info - > args ; arg ; arg = arg - > next ) {
type_t * t = get_arg_type ( env , arg ) ;
2024-04-12 10:43:23 -07:00
code = CORD_all ( code , " if (! " , arg - > name , " $is_set) { \n " ) ;
if ( t - > tag = = ArrayType ) {
code = CORD_all (
2024-04-17 10:52:07 -07:00
code , " $ " , arg - > name , " = (array_t){}; \n "
2024-04-12 10:43:23 -07:00
" for (; i < argc; i++) { \n "
" if (argv[i]) { \n "
2024-04-16 10:50:07 -07:00
" CORD arg = CORD_from_char_star(argv[i]); \n "
2024-04-17 10:52:07 -07:00
" Array$insert(&$ " , arg - > name , " , &arg, 0, $ArrayInfo(&$Text)); \n "
2024-04-12 10:43:23 -07:00
" argv[i] = NULL; \n "
" } \n "
" } \n " ,
arg - > name , " $is_set = yes; \n " ) ;
} else if ( arg - > default_val ) {
2024-04-17 10:52:07 -07:00
code = CORD_all ( code , " $ " , arg - > name , " = " , compile ( env , arg - > default_val ) , " ; \n " ) ;
2024-04-12 10:09:31 -07:00
} else {
code = CORD_all (
code ,
" if (i < argc) { " ) ;
if ( t - > tag = = TextType ) {
2024-04-17 10:52:07 -07:00
code = CORD_all ( code , " $ " , arg - > name , " = CORD_from_char_star(argv[i]); \n " ) ;
2024-04-12 10:09:31 -07:00
} else {
code = CORD_all (
code ,
2024-04-16 10:50:07 -07:00
" CORD invalid; \n " ,
2024-04-17 10:52:07 -07:00
" $ " , arg - > name , " = " , type_to_cord ( t ) , " $from_text(argv[i], &invalid) " , " ; \n "
2024-04-16 10:50:07 -07:00
" if (invalid != CORD_EMPTY) \n "
" USAGE_ERR( \" Unable to parse this argument as a " , type_to_cord ( t ) , " : \" , CORD_from_char_star(argv[i])); \n " ) ;
2024-04-12 10:09:31 -07:00
}
code = CORD_all (
code ,
" argv[i++] = NULL; \n "
" while (i < argc && argv[i] == NULL) \n "
" ++i; \n } else { \n "
2024-04-16 10:50:07 -07:00
" USAGE_ERR( \" Required argument ' " , arg - > name , " ' was not provided! \\ n \" , usage); \n " ,
2024-04-12 10:09:31 -07:00
" } \n " ) ;
}
code = CORD_all ( code , " } \n " ) ;
}
code = CORD_all ( code , " for (; i < argc; i++) { \n "
2024-04-16 10:50:07 -07:00
" if (argv[i]) \n USAGE_ERR( \" Unexpected argument: \" , Text$quoted(argv[i], false), \" \\ n \" , usage); \n } \n " ) ;
2024-04-12 10:09:31 -07:00
2024-04-20 11:55:27 -07:00
code = CORD_all ( code , fn_name , " ( " ) ;
2024-04-12 10:09:31 -07:00
for ( arg_t * arg = fn_info - > args ; arg ; arg = arg - > next ) {
2024-04-17 10:52:07 -07:00
code = CORD_all ( code , " $ " , arg - > name ) ;
2024-04-12 10:09:31 -07:00
if ( arg - > next ) code = CORD_all ( code , " , " ) ;
}
2024-04-20 11:55:27 -07:00
code = CORD_all ( code , " ); \n " ) ;
2024-04-12 10:09:31 -07:00
return code ;
}
2024-06-06 13:28:53 -07:00
CORD compile_file ( env_t * env , ast_t * ast )
2024-02-17 23:17:44 -08:00
{
2024-08-17 11:41:31 -07:00
// First prepare variable initializers to prevent unitialized access:
2024-04-10 08:49:43 -07:00
for ( ast_list_t * stmt = Match ( ast , Block ) - > statements ; stmt ; stmt = stmt - > next ) {
if ( stmt - > ast - > tag = = Declare ) {
auto decl = Match ( stmt - > ast , Declare ) ;
2024-04-17 10:44:01 -07:00
const char * decl_name = Match ( decl - > var , Var ) - > name ;
2024-08-17 11:41:31 -07:00
CORD full_name = CORD_all ( namespace_prefix ( env - > libname , env - > namespace ) , decl_name ) ;
2024-04-10 08:49:43 -07:00
type_t * t = get_type ( env , decl - > value ) ;
2024-07-13 15:43:50 -07:00
if ( t - > tag = = AbortType | | t - > tag = = VoidType | | t - > tag = = ReturnType )
2024-04-10 08:49:43 -07:00
code_err ( stmt - > ast , " You can't declare a variable with a %T value " , t ) ;
2024-08-17 11:41:31 -07:00
if ( ! ( decl - > value - > tag = = Use | | decl - > value - > tag = = Import | | is_constant ( env , decl - > value ) ) ) {
env - > code - > variable_initializers = CORD_all (
env - > code - > variable_initializers ,
full_name , " = " , compile_maybe_incref ( env , decl - > value ) , " , \n " ,
full_name , " $initialized = true; \n " ) ;
CORD checked_access = CORD_all ( " check_initialized( " , full_name , " , \" " , decl_name , " \" ) " ) ;
set_binding ( env , decl_name , new ( binding_t , . type = t , . code = checked_access ) ) ;
}
}
}
2024-04-17 10:44:01 -07:00
2024-08-17 11:41:31 -07:00
for ( ast_list_t * stmt = Match ( ast , Block ) - > statements ; stmt ; stmt = stmt - > next ) {
if ( stmt - > ast - > tag = = Declare ) {
auto decl = Match ( stmt - > ast , Declare ) ;
const char * decl_name = Match ( decl - > var , Var ) - > name ;
CORD full_name = CORD_all ( namespace_prefix ( env - > libname , env - > namespace ) , decl_name ) ;
bool is_private = ( decl_name [ 0 ] = = ' _ ' ) ;
type_t * t = get_type ( env , decl - > value ) ;
2024-06-13 10:17:51 -07:00
if ( decl - > value - > tag = = Use | | decl - > value - > tag = = Import ) {
2024-05-27 14:45:22 -07:00
assert ( compile_statement ( env , stmt - > ast ) = = CORD_EMPTY ) ;
2024-08-17 11:41:31 -07:00
} else if ( ! is_constant ( env , decl - > value ) ) {
2024-04-17 10:44:01 -07:00
env - > code - > staticdefs = CORD_all (
env - > code - > staticdefs ,
2024-08-17 11:41:31 -07:00
" static bool " , full_name , " $initialized = false; \n " ,
is_private ? " static " : CORD_EMPTY ,
compile_declaration ( t , full_name ) , " ; \n " ) ;
2024-04-17 10:44:01 -07:00
} else {
env - > code - > staticdefs = CORD_all (
env - > code - > staticdefs ,
2024-08-17 11:41:31 -07:00
is_private ? " static " : CORD_EMPTY ,
compile_declaration ( t , full_name ) , " = " , compile ( env , decl - > value ) , " ; \n " ) ;
2024-04-17 10:44:01 -07:00
}
2024-05-18 17:53:03 -07:00
} else if ( stmt - > ast - > tag = = InlineCCode ) {
CORD code = compile_statement ( env , stmt - > ast ) ;
2024-05-28 16:02:00 -07:00
env - > code - > staticdefs = CORD_all ( env - > code - > staticdefs , code , " \n " ) ;
2024-04-10 08:49:43 -07:00
} else {
CORD code = compile_statement ( env , stmt - > ast ) ;
2024-04-12 10:09:31 -07:00
assert ( ! code ) ;
2024-04-10 08:49:43 -07:00
}
2024-02-17 23:17:44 -08:00
}
2024-06-06 13:28:53 -07:00
const char * name = file_base_name ( ast - > file - > filename ) ;
return CORD_all (
// "#line 1 ", Text$quoted(ast->file->filename, false), "\n",
" #include <tomo/tomo.h> \n "
" #include \" " , name , " .tm.h \" \n \n " ,
env - > code - > local_typedefs , " \n " ,
env - > code - > staticdefs , " \n " ,
2024-08-17 11:41:31 -07:00
" static void _initialize(void) { \n " ,
env - > code - > variable_initializers ,
" } \n " ,
2024-06-06 13:28:53 -07:00
env - > code - > funcs , " \n " ,
2024-08-17 11:41:31 -07:00
env - > code - > typeinfos , " \n " ) ;
2024-06-06 13:28:53 -07:00
}
2024-07-23 16:46:42 -07:00
CORD compile_statement_imports ( env_t * env , ast_t * ast )
2024-06-06 13:28:53 -07:00
{
switch ( ast - > tag ) {
case DocTest : {
auto test = Match ( ast , DocTest ) ;
2024-07-23 16:46:42 -07:00
return compile_statement_imports ( env , test - > expr ) ;
}
case Declare : {
auto decl = Match ( ast , Declare ) ;
if ( decl - > value - > tag = = Use | | decl - > value - > tag = = Import )
return compile_statement_imports ( env , decl - > value ) ;
return CORD_EMPTY ;
}
case Import : {
const char * path = Match ( ast , Import ) - > path ;
return CORD_all ( " #include \" " , path , " .tm.h \" \n " ) ;
}
case Use : {
const char * name = Match ( ast , Use ) - > name ;
if ( strncmp ( name , " -l " , 2 ) = = 0 )
return CORD_EMPTY ;
else
return CORD_all ( " #include <tomo/lib " , name , " .h> \n " ) ;
}
default : return CORD_EMPTY ;
}
}
CORD compile_statement_typedefs ( env_t * env , ast_t * ast )
{
switch ( ast - > tag ) {
case DocTest : {
auto test = Match ( ast , DocTest ) ;
return compile_statement_typedefs ( env , test - > expr ) ;
}
case StructDef : {
return compile_struct_typedef ( env , ast ) ;
}
case EnumDef : {
return compile_enum_typedef ( env , ast ) ;
}
case LangDef : {
auto def = Match ( ast , LangDef ) ;
return CORD_all ( " typedef CORD " , namespace_prefix ( env - > libname , env - > namespace ) , def - > name , " _t ; \ n " );
}
case Lambda : {
auto lambda = Match ( ast , Lambda ) ;
table_t * closed_vars = get_closed_vars ( env , ast ) ;
if ( Table $ length ( * closed_vars ) = = 0 )
return CORD_EMPTY ;
CORD def = " typedef struct { " ;
for ( int64_t i = 1 ; i < = Table $ length ( * closed_vars ) ; i + + ) {
struct { const char * name ; binding_t * b ; } * entry = Table $ entry ( * closed_vars , i ) ;
if ( entry - > b - > type - > tag = = ModuleType )
continue ;
def = CORD_all ( def , compile_declaration ( entry - > b - > type , entry - > name ) , " ; " ) ;
}
CORD name = CORD_asprintf ( " %rlambda$%ld " , namespace_prefix ( env - > libname , env - > namespace ) , lambda - > id ) ;
return CORD_all ( def , " } " , name , " $userdata_t; " ) ;
}
default :
return CORD_EMPTY ;
}
}
CORD compile_statement_definitions ( env_t * env , ast_t * ast )
{
switch ( ast - > tag ) {
case DocTest : {
auto test = Match ( ast , DocTest ) ;
return compile_statement_definitions ( env , test - > expr ) ;
2024-06-06 13:28:53 -07:00
}
case Declare : {
auto decl = Match ( ast , Declare ) ;
2024-06-13 10:17:51 -07:00
if ( decl - > value - > tag = = Use | | decl - > value - > tag = = Import ) {
2024-07-23 16:46:42 -07:00
return compile_statement_definitions ( env , decl - > value ) ;
2024-06-06 13:28:53 -07:00
}
type_t * t = get_type ( env , decl - > value ) ;
assert ( t - > tag ! = ModuleType ) ;
2024-07-13 15:43:50 -07:00
if ( t - > tag = = AbortType | | t - > tag = = VoidType | | t - > tag = = ReturnType )
2024-06-06 13:28:53 -07:00
code_err ( ast , " You can't declare a variable with a %T value " , t ) ;
const char * decl_name = Match ( decl - > var , Var ) - > name ;
bool is_private = ( decl_name [ 0 ] = = ' _ ' ) ;
2024-07-23 16:46:42 -07:00
CORD code = ( decl - > value - > tag = = Use | | decl - > value - > tag = = Import ) ? compile_statement_definitions ( env , decl - > value ) : CORD_EMPTY ;
2024-06-06 13:28:53 -07:00
if ( is_private ) {
return code ;
} else {
return CORD_all (
2024-06-16 15:09:54 -07:00
code , " \n " " extern " , compile_declaration ( t , CORD_cat ( namespace_prefix ( env - > libname , env - > namespace ) , decl_name ) ) , " ; \n " ) ;
2024-06-06 13:28:53 -07:00
}
}
case StructDef : {
2024-07-23 16:46:42 -07:00
auto def = Match ( ast , StructDef ) ;
CORD full_name = CORD_cat ( namespace_prefix ( env - > libname , env - > namespace ) , def - > name ) ;
return CORD_all (
" extern const TypeInfo " , full_name , " ; \n " ,
compile_namespace_definitions ( env , def - > name , def - > namespace ) ) ;
2024-06-06 13:28:53 -07:00
}
case EnumDef : {
2024-07-23 16:46:42 -07:00
return compile_enum_declarations ( env , ast ) ;
2024-06-06 13:28:53 -07:00
}
case LangDef : {
auto def = Match ( ast , LangDef ) ;
2024-07-23 16:46:42 -07:00
CORD full_name = CORD_cat ( namespace_prefix ( env - > libname , env - > namespace ) , def - > name ) ;
2024-06-06 13:28:53 -07:00
return CORD_all (
2024-07-23 16:46:42 -07:00
" extern const TypeInfo " , full_name , " ; \n " ,
compile_namespace_definitions ( env , def - > name , def - > namespace ) ) ;
2024-06-06 13:28:53 -07:00
}
case FunctionDef : {
auto fndef = Match ( ast , FunctionDef ) ;
2024-06-13 18:20:50 -07:00
const char * decl_name = Match ( fndef - > name , Var ) - > name ;
bool is_private = decl_name [ 0 ] = = ' _ ' ;
2024-06-06 13:28:53 -07:00
if ( is_private ) return CORD_EMPTY ;
CORD arg_signature = " ( " ;
for ( arg_ast_t * arg = fndef - > args ; arg ; arg = arg - > next ) {
type_t * arg_type = get_arg_ast_type ( env , arg ) ;
2024-06-16 15:09:54 -07:00
arg_signature = CORD_cat ( arg_signature , compile_declaration ( arg_type , CORD_cat ( " $ " , arg - > name ) ) ) ;
2024-06-06 13:28:53 -07:00
if ( arg - > next ) arg_signature = CORD_cat ( arg_signature , " , " ) ;
}
arg_signature = CORD_cat ( arg_signature , " ) " ) ;
type_t * ret_t = fndef - > ret_type ? parse_type_ast ( env , fndef - > ret_type ) : Type ( VoidType ) ;
2024-06-16 15:09:54 -07:00
CORD ret_type_code = compile_type ( ret_t ) ;
2024-07-23 16:46:42 -07:00
return CORD_all ( ret_type_code , " " , namespace_prefix ( env - > libname , env - > namespace ) , decl_name , arg_signature , " ; \ n " );
2024-06-06 13:28:53 -07:00
}
case Extern : {
auto ext = Match ( ast , Extern ) ;
type_t * t = parse_type_ast ( env , ext - > type ) ;
CORD decl ;
if ( t - > tag = = ClosureType ) {
t = Match ( t , ClosureType ) - > fn ;
auto fn = Match ( t , FunctionType ) ;
2024-06-16 15:09:54 -07:00
decl = CORD_all ( compile_type ( fn - > ret ) , " " , ext - > name , " ( " ) ;
2024-06-06 13:28:53 -07:00
for ( arg_t * arg = fn - > args ; arg ; arg = arg - > next ) {
2024-06-16 15:09:54 -07:00
decl = CORD_all ( decl , compile_type ( arg - > type ) ) ;
2024-06-06 13:28:53 -07:00
if ( arg - > next ) decl = CORD_cat ( decl , " , " ) ;
}
decl = CORD_cat ( decl , " ) " ) ;
} else {
2024-06-16 15:09:54 -07:00
decl = compile_declaration ( t , ext - > name ) ;
2024-06-06 13:28:53 -07:00
}
return CORD_all ( " extern " , decl , " ; \n " ) ;
}
default :
return CORD_EMPTY ;
}
}
CORD compile_header ( env_t * env , ast_t * ast )
{
// "#line 1 ", Text$quoted(ast->file->filename, false), "\n",
CORD header = " #pragma once \n "
" #include <tomo/tomo.h> \n " ;
for ( ast_list_t * stmt = Match ( ast , Block ) - > statements ; stmt ; stmt = stmt - > next )
2024-07-23 16:46:42 -07:00
header = CORD_all ( header , compile_statement_imports ( env , stmt - > ast ) ) ;
for ( ast_list_t * stmt = Match ( ast , Block ) - > statements ; stmt ; stmt = stmt - > next )
header = CORD_all ( header , compile_statement_typedefs ( env , stmt - > ast ) ) ;
for ( ast_list_t * stmt = Match ( ast , Block ) - > statements ; stmt ; stmt = stmt - > next )
header = CORD_all ( header , compile_statement_definitions ( env , stmt - > ast ) ) ;
2024-06-06 13:28:53 -07:00
return header ;
2024-02-17 23:17:44 -08:00
}
2024-02-04 12:23:59 -08:00
// vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1,\:0