@@ -10046,6 +10046,33 @@ static Value builtin_extend(Interpreter *interp, Value *args, int argc, Expr **a
1004610046 return value_bool (false);
1004710047}
1004810048
10049+ static int module_include_bindings (Interpreter * interp , Env * caller_env , Env * mod_env ) {
10050+ if (!interp || !caller_env || !mod_env ) {
10051+ return -1 ;
10052+ }
10053+
10054+ for (size_t i = 0 ; i < mod_env -> count ; i ++ ) {
10055+ EnvEntry * e = & mod_env -> entries [i ];
10056+ if (!e -> initialized ) {
10057+ continue ;
10058+ }
10059+ if (e -> name && e -> name [0 ] == '_' && e -> name [1 ] == '_' ) {
10060+ continue ;
10061+ }
10062+ if (!env_assign (caller_env , e -> name , e -> value , e -> decl_type , e -> decl_base , true)) {
10063+ if (interp -> error ) {
10064+ free (interp -> error );
10065+ }
10066+ interp -> error = strdup ("INCLUDE failed to assign module binding" );
10067+ interp -> error_line = 0 ;
10068+ interp -> error_col = 0 ;
10069+ return -1 ;
10070+ }
10071+ }
10072+
10073+ return 0 ;
10074+ }
10075+
1004910076static int module_export_bindings (Interpreter * interp , Env * caller_env , Env * mod_env , const char * alias , int line ,
1005010077 int col , const char * fail_msg ) {
1005110078 if (!interp || !caller_env || !mod_env || !alias || alias [0 ] == '\0' ) {
@@ -10486,6 +10513,283 @@ static Value builtin_import(Interpreter *interp, Value *args, int argc, Expr **a
1048610513 return value_bool (false);
1048710514}
1048810515
10516+ static Value builtin_include (Interpreter * interp , Value * args , int argc , Expr * * arg_nodes , Env * env , int line ,
10517+ int col ) {
10518+ (void )arg_nodes ;
10519+ (void )env ;
10520+ if (argc < 1 ) {
10521+ RUNTIME_ERROR (interp , "INCLUDE expects a module name STR" , line , col );
10522+ }
10523+ if (args [0 ].type != VAL_STR ) {
10524+ RUNTIME_ERROR (interp , "INCLUDE first argument must be STR" , line , col );
10525+ }
10526+ const char * modname = args [0 ].as .s ? args [0 ].as .s : "" ;
10527+
10528+ const char * referer_source = NULL ;
10529+ EnvEntry * src_entry = env_get_entry (env , "__MODULE_SOURCE__" );
10530+ if (src_entry && src_entry -> initialized && src_entry -> value .type == VAL_STR ) {
10531+ referer_source = src_entry -> value .as .s ;
10532+ }
10533+
10534+ char referer_dir [1024 ] = {0 };
10535+ if (referer_source && referer_source [0 ] != '\0' ) {
10536+ strncpy (referer_dir , referer_source , sizeof (referer_dir ) - 1 );
10537+ char * last_sep = NULL ;
10538+ for (char * p = referer_dir ; * p ; p ++ ) {
10539+ if (* p == '/' || * p == '\\' ) {
10540+ last_sep = p ;
10541+ }
10542+ }
10543+ if (last_sep ) {
10544+ * last_sep = '\0' ;
10545+ }
10546+ } else {
10547+ strncpy (referer_dir , "." , sizeof (referer_dir ) - 1 );
10548+ }
10549+
10550+ #ifdef _WIN32
10551+ const char PATH_SEP = '\\' ;
10552+ #else
10553+ const char PATH_SEP = '/' ;
10554+ #endif
10555+ char base [1024 ];
10556+ base [0 ] = '\0' ;
10557+ const char * p = modname ;
10558+ char * b = base ;
10559+ while (* p && (size_t )(b - base ) + 1 < sizeof (base )) {
10560+ if (p [0 ] == '.' && p [1 ] == '.' ) {
10561+ * b ++ = PATH_SEP ;
10562+ p += 2 ;
10563+ continue ;
10564+ }
10565+ * b ++ = * p ++ ;
10566+ }
10567+ * b = '\0' ;
10568+
10569+ struct stat st ;
10570+ char candidate [2048 ];
10571+ char * found_path = NULL ;
10572+ char * srcbuf = NULL ;
10573+
10574+ const char * search_dirs [5 ];
10575+ search_dirs [0 ] = referer_dir ;
10576+
10577+ EnvEntry * primary_src_entry =
10578+ interp && interp -> global_env ? env_get_entry (interp -> global_env , "__MODULE_SOURCE__" ) : NULL ;
10579+ char primary_program_dir [1024 ];
10580+ char primary_std_dir [1024 ];
10581+ char primary_usr_dir [1024 ];
10582+ primary_program_dir [0 ] = '\0' ;
10583+ primary_std_dir [0 ] = '\0' ;
10584+ primary_usr_dir [0 ] = '\0' ;
10585+ if (primary_src_entry && primary_src_entry -> initialized && primary_src_entry -> value .type == VAL_STR &&
10586+ primary_src_entry -> value .as .s && primary_src_entry -> value .as .s [0 ] != '\0' ) {
10587+ strncpy (primary_program_dir , primary_src_entry -> value .as .s , sizeof (primary_program_dir ) - 1 );
10588+ primary_program_dir [sizeof (primary_program_dir ) - 1 ] = '\0' ;
10589+ char * last_sep = NULL ;
10590+ for (char * q = primary_program_dir ; * q ; q ++ ) {
10591+ if (* q == '/' || * q == '\\' ) {
10592+ last_sep = q ;
10593+ }
10594+ }
10595+ if (last_sep ) {
10596+ * last_sep = '\0' ;
10597+ }
10598+ if (snprintf (primary_std_dir , sizeof (primary_std_dir ), "%s/lib/std" , primary_program_dir ) >= 0 ) {
10599+ search_dirs [1 ] = primary_std_dir ;
10600+ } else {
10601+ search_dirs [1 ] = "lib/std" ;
10602+ }
10603+ if (snprintf (primary_usr_dir , sizeof (primary_usr_dir ), "%s/lib/usr" , primary_program_dir ) >= 0 ) {
10604+ search_dirs [2 ] = primary_usr_dir ;
10605+ } else {
10606+ search_dirs [2 ] = "lib/usr" ;
10607+ }
10608+ } else {
10609+ search_dirs [1 ] = "lib/std" ;
10610+ search_dirs [2 ] = "lib/usr" ;
10611+ }
10612+
10613+ char exe_program_dir [1024 ];
10614+ char exe_std_dir [1024 ];
10615+ char exe_usr_dir [1024 ];
10616+ exe_program_dir [0 ] = '\0' ;
10617+ exe_std_dir [0 ] = '\0' ;
10618+ exe_usr_dir [0 ] = '\0' ;
10619+ if (g_argv && g_argv [0 ] && g_argv [0 ][0 ] != '\0' ) {
10620+ strncpy (exe_program_dir , g_argv [0 ], sizeof (exe_program_dir ) - 1 );
10621+ exe_program_dir [sizeof (exe_program_dir ) - 1 ] = '\0' ;
10622+ char * last_sep = NULL ;
10623+ for (char * q = exe_program_dir ; * q ; q ++ ) {
10624+ if (* q == '/' || * q == '\\' ) {
10625+ last_sep = q ;
10626+ }
10627+ }
10628+ if (last_sep ) {
10629+ * last_sep = '\0' ;
10630+ }
10631+ if (snprintf (exe_std_dir , sizeof (exe_std_dir ), "%s/lib/std" , exe_program_dir ) >= 0 ) {
10632+ search_dirs [3 ] = exe_std_dir ;
10633+ } else {
10634+ search_dirs [3 ] = "lib/std" ;
10635+ }
10636+ if (snprintf (exe_usr_dir , sizeof (exe_usr_dir ), "%s/lib/usr" , exe_program_dir ) >= 0 ) {
10637+ search_dirs [4 ] = exe_usr_dir ;
10638+ } else {
10639+ search_dirs [4 ] = "lib/usr" ;
10640+ }
10641+ } else {
10642+ search_dirs [3 ] = "lib/std" ;
10643+ search_dirs [4 ] = "lib/usr" ;
10644+ }
10645+
10646+ for (int sd = 0 ; sd < 5 && !found_path ; sd ++ ) {
10647+ const char * sdir = search_dirs [sd ];
10648+ if (!sdir ) {
10649+ continue ;
10650+ }
10651+
10652+ if (snprintf (candidate , sizeof (candidate ), "%s/%s" , sdir , base ) < 0 ) {
10653+ continue ;
10654+ }
10655+ if (stat (candidate , & st ) == 0 && (st .st_mode & S_IFMT ) == S_IFDIR ) {
10656+ char initpath [2048 ];
10657+ if (snprintf (initpath , sizeof (initpath ), "%s/%s/init.pre" , sdir , base ) < 0 ) {
10658+ continue ;
10659+ }
10660+ if (stat (initpath , & st ) == 0 && (st .st_mode & S_IFMT ) == S_IFREG ) {
10661+ found_path = strdup (initpath );
10662+ break ;
10663+ }
10664+ char buf [256 ];
10665+ snprintf (buf , sizeof (buf ), "INCLUDE: package '%s' missing init.pre" , modname );
10666+ RUNTIME_ERROR (interp , buf , line , col );
10667+ }
10668+
10669+ char filepath [2048 ];
10670+ if (snprintf (filepath , sizeof (filepath ), "%s/%s.pre" , sdir , base ) < 0 ) {
10671+ continue ;
10672+ }
10673+ if (stat (filepath , & st ) == 0 && (st .st_mode & S_IFMT ) == S_IFREG ) {
10674+ found_path = strdup (filepath );
10675+ break ;
10676+ }
10677+ }
10678+
10679+ char * canonical_path = found_path ? prefix_fullpath_dup (found_path ) : NULL ;
10680+ const char * cache_key = canonical_path ? canonical_path : modname ;
10681+
10682+ if (!found_path ) {
10683+ Env * existing = module_env_lookup (interp , cache_key );
10684+ if (!existing ) {
10685+ free (found_path );
10686+ free (canonical_path );
10687+ char buf [256 ];
10688+ snprintf (buf , sizeof (buf ), "INCLUDE: module '%s' not found" , modname );
10689+ RUNTIME_ERROR (interp , buf , line , col );
10690+ }
10691+ }
10692+
10693+ Env * mod_env = module_env_lookup (interp , cache_key );
10694+ if (!mod_env ) {
10695+ mod_env = module_env_lookup (interp , modname );
10696+ }
10697+ if (!mod_env ) {
10698+ if (module_register (interp , cache_key ) != 0 ) {
10699+ free (found_path );
10700+ free (canonical_path );
10701+ RUNTIME_ERROR (interp , "INCLUDE failed to register module" , line , col );
10702+ }
10703+ mod_env = module_env_lookup (interp , cache_key );
10704+ }
10705+ if (!mod_env ) {
10706+ free (found_path );
10707+ free (canonical_path );
10708+ RUNTIME_ERROR (interp , "INCLUDE failed to lookup module env" , line , col );
10709+ }
10710+
10711+ if (strcmp (modname , cache_key ) != 0 ) {
10712+ (void )module_register_alias (interp , modname , mod_env );
10713+ }
10714+ if (found_path && strcmp (found_path , cache_key ) != 0 ) {
10715+ (void )module_register_alias (interp , found_path , mod_env );
10716+ }
10717+
10718+ EnvEntry * scope_entry = env_get_entry (mod_env , "__MODULE_SCOPE__" );
10719+ if (!scope_entry || !scope_entry -> initialized || scope_entry -> value .type != VAL_STR ) {
10720+ env_assign (mod_env , "__MODULE_SCOPE__" , value_str (modname ), TYPE_STR , 0 , true);
10721+ }
10722+
10723+ EnvEntry * marker = env_get_entry (mod_env , "__MODULE_LOADED__" );
10724+ if ((!marker || !marker -> initialized ) && found_path ) {
10725+ FILE * f = fopen (found_path , "rb" );
10726+ if (f ) {
10727+ fseek (f , 0 , SEEK_END );
10728+ long len = ftell (f );
10729+ fseek (f , 0 , SEEK_SET );
10730+ srcbuf = malloc ((size_t )len + 1 );
10731+ if (!srcbuf ) {
10732+ fclose (f );
10733+ free (found_path );
10734+ free (canonical_path );
10735+ RUNTIME_ERROR (interp , "Out of memory" , line , col );
10736+ }
10737+ if (fread (srcbuf , 1 , (size_t )len , f ) != (size_t )len ) {
10738+ free (srcbuf );
10739+ srcbuf = NULL ;
10740+ }
10741+ if (srcbuf ) {
10742+ srcbuf [len ] = '\0' ;
10743+ fclose (f );
10744+
10745+ env_assign (mod_env , "__MODULE_SOURCE__" , value_str (cache_key ), TYPE_STR , 0 , true);
10746+
10747+ Lexer lex ;
10748+ lexer_init (& lex , srcbuf , found_path );
10749+ Parser parser ;
10750+ parser_init (& parser , & lex );
10751+
10752+ Stmt * program = parser_parse (& parser );
10753+ if (parser .had_error ) {
10754+ free (srcbuf );
10755+ free (found_path );
10756+ free (canonical_path );
10757+ interp -> error = strdup ("INCLUDE: parse error" );
10758+ interp -> error_line = parser .current_token .line ;
10759+ interp -> error_col = parser .current_token .column ;
10760+ return value_null ();
10761+ }
10762+
10763+ ExecResult res = exec_program_in_env (interp , program , mod_env );
10764+ if (res .status == EXEC_ERROR ) {
10765+ free (srcbuf );
10766+ free (found_path );
10767+ free (canonical_path );
10768+ interp -> error = res .error ? strdup (res .error ) : strdup ("Runtime error in INCLUDE" );
10769+ interp -> error_line = res .error_line ;
10770+ interp -> error_col = res .error_column ;
10771+ free (res .error );
10772+ return value_null ();
10773+ }
10774+
10775+ env_assign (mod_env , "__MODULE_LOADED__" , value_int (1 ), TYPE_INT , 0 , true);
10776+ free (srcbuf );
10777+ } else {
10778+ fclose (f );
10779+ }
10780+ }
10781+ }
10782+
10783+ free (found_path );
10784+ free (canonical_path );
10785+
10786+ if (module_include_bindings (interp , env , mod_env ) != 0 ) {
10787+ return value_null ();
10788+ }
10789+
10790+ return value_bool (false);
10791+ }
10792+
1048910793// TNS operator: two forms
1049010794// 1) TNS(STR: string) -> 1-D TNS of STR single-character elements
1049110795// 2) TNS(TNS: shape, ANY: value) -> creates tensor with given shape filled with value
@@ -11552,6 +11856,7 @@ static BuiltinFunction builtins_table[] = {
1155211856 {"EXTEND" , 1 , 1 , builtin_extend },
1155311857 {"IMPORT" , 1 , 2 , builtin_import },
1155411858 {"IMPORT_PATH" , 1 , 2 , builtin_import_path },
11859+ {"INCLUDE" , 1 , 1 , builtin_include },
1155511860 {"EXPORT" , 2 , 2 , builtin_export },
1155611861
1155711862 // Sentinel
0 commit comments