66import os
77import typing
88
9- from typing import Dict , Optional
9+ from typing import Dict , Optional , Tuple
1010
1111from scrapscript import (
1212 Access ,
@@ -63,16 +63,35 @@ def __init__(self, main_fn: CompiledFunction) -> None:
6363 self .functions : typing .List [CompiledFunction ] = [main_fn ]
6464 self .function : CompiledFunction = main_fn
6565 self .record_keys : Dict [str , int ] = {}
66+ self .record_builders : Dict [Tuple [str , ...], CompiledFunction ] = {}
6667 self .variant_tags : Dict [str , int ] = {}
6768 self .debug : bool = False
68- self .used_runtime_functions : set [str ] = set ()
6969
70- def record_key (self , key : str ) -> int :
71- result = self .record_keys .get (key )
72- if result is not None :
73- return result
74- result = self .record_keys [key ] = len (self .record_keys )
75- return result
70+ def record_key (self , key : str ) -> str :
71+ if key not in self .record_keys :
72+ self .record_keys [key ] = len (self .record_keys )
73+ return f"Record_{ key } "
74+
75+ def record_builder (self , keys : Tuple [str , ...]) -> CompiledFunction :
76+ builder = self .record_builders .get (keys )
77+ if builder is not None :
78+ return builder
79+
80+ builder = CompiledFunction (f"Record_builder_{ '_' .join (keys )} " , list (keys ))
81+ self .functions .append (builder )
82+ cur = self .function
83+ self .function = builder
84+
85+ result = self ._mktemp (f"mkrecord(heap, { len (keys )} )" )
86+ for i , key in enumerate (keys ):
87+ key_idx = self .record_key (key )
88+ self ._emit (f"record_set({ result } , /*index=*/{ i } , (struct record_field){{.key={ key_idx } , .value={ key } }});" )
89+ self ._debug ("collect(heap);" )
90+ self ._emit (f"return { result } ;" )
91+
92+ self .function = cur
93+ self .record_builders [keys ] = builder
94+ return builder
7695
7796 def variant_tag (self , key : str ) -> int :
7897 result = self .variant_tags .get (key )
@@ -295,11 +314,6 @@ def compile(self, env: Env, exp: Object) -> str:
295314 raise NameError (f"name '{ exp .name } ' is not defined" )
296315 return var_value
297316 if isinstance (exp , Apply ):
298- if isinstance (exp .func , Var ):
299- if exp .func .name == "runtime" :
300- assert isinstance (exp .arg , String )
301- self .used_runtime_functions .add (exp .arg .value )
302- return f"builtin_{ exp .arg .value } "
303317 callee = self .compile (env , exp .func )
304318 arg = self .compile (env , exp .arg )
305319 return self ._mktemp (f"closure_call({ callee } , { arg } )" )
@@ -314,14 +328,9 @@ def compile(self, env: Env, exp: Object) -> str:
314328 values : Dict [str , str ] = {}
315329 for key , value_exp in exp .data .items ():
316330 values [key ] = self .compile (env , value_exp )
317- result = self ._mktemp (f"mkrecord(heap, { len (values )} )" )
318- for i , (key , value ) in enumerate (values .items ()):
319- key_idx = self .record_key (key )
320- self ._emit (
321- f"record_set({ result } , /*index=*/{ i } , (struct record_field){{.key={ key_idx } , .value={ value } }});"
322- )
323- self ._debug ("collect(heap);" )
324- return result
331+ keys = tuple (sorted (exp .data .keys ()))
332+ builder = self .record_builder (keys )
333+ return self ._mktemp (f"{ builder .name } ({ ', ' .join (values [key ] for key in keys )} )" )
325334 if isinstance (exp , Access ):
326335 assert isinstance (exp .at , Var ), f"only Var access is supported, got { type (exp .at )} "
327336 record = self .compile (env , exp .obj )
@@ -345,11 +354,6 @@ def compile(self, env: Env, exp: Object) -> str:
345354# The const heap will never be scanned
346355# The const heap can be serialized to disk and mmap'd
347356
348- BUILTINS = [
349- "print" ,
350- "println" ,
351- ]
352-
353357
354358def env_get_split (key : str , default : Optional [typing .List [str ]] = None ) -> typing .List [str ]:
355359 import shlex
@@ -362,7 +366,7 @@ def env_get_split(key: str, default: Optional[typing.List[str]] = None) -> typin
362366 return []
363367
364368
365- def compile_to_string (source : str , memory : int , debug : bool ) -> str :
369+ def compile_to_string (source : str , debug : bool ) -> str :
366370 program = parse (tokenize (source ))
367371
368372 main_fn = CompiledFunction ("scrap_main" , params = [])
@@ -371,20 +375,20 @@ def compile_to_string(source: str, memory: int, debug: bool) -> str:
371375 result = compiler .compile ({}, program )
372376 main_fn .code .append (f"return { result } ;" )
373377
374- builtins = [builtin for builtin in BUILTINS if builtin in compiler .used_runtime_functions ]
375- for builtin in builtins :
376- fn = CompiledFunction (f"builtin_{ builtin } _wrapper" , params = ["this" , "arg" ])
377- fn .code .append (f"return { builtin } (arg);" )
378- compiler .functions .append (fn )
379-
380378 f = io .StringIO ()
381- print ('#include "runtime.c"' , file = f )
379+ with open ("runtime.c" , "r" ) as runtime :
380+ print (runtime .read (), file = f )
382381 print ("#define OBJECT_HANDLE(name, exp) GC_HANDLE(struct object*, name, exp)" , file = f )
383382 # Declare all functions
384383 print ("const char* record_keys[] = {" , file = f )
385384 for key in compiler .record_keys :
386385 print (f'"{ key } ",' , file = f )
387386 print ("};" , file = f )
387+ if compiler .record_keys :
388+ print ("enum {" , file = f )
389+ for key , idx in compiler .record_keys .items ():
390+ print (f"Record_{ key } = { idx } ," , file = f )
391+ print ("};" , file = f )
388392 if compiler .variant_tags :
389393 print ("const char* variant_names[] = {" , file = f )
390394 for key in compiler .variant_tags :
@@ -399,23 +403,11 @@ def compile_to_string(source: str, memory: int, debug: bool) -> str:
399403 print ("const char* variant_names[] = { NULL };" , file = f )
400404 for function in compiler .functions :
401405 print (function .decl () + ";" , file = f )
402- for builtin in builtins :
403- print (f"struct object* builtin_{ builtin } = NULL;" , file = f )
404406 for function in compiler .functions :
405407 print (f"{ function .decl ()} {{" , file = f )
406408 for line in function .code :
407409 print (line , file = f )
408410 print ("}" , file = f )
409- print ("int main() {" , file = f )
410- print (f"heap = make_heap({ memory } );" , file = f )
411- print ("HANDLES();" , file = f )
412- for builtin in builtins :
413- print (f"builtin_{ builtin } = mkclosure(heap, builtin_{ builtin } _wrapper, 0);" , file = f )
414- print (f"GC_PROTECT(builtin_{ builtin } );" , file = f )
415- print (f"struct object* result = { main_fn .name } ();" , file = f )
416- print ("println(result);" , file = f )
417- print ("destroy_heap(heap);" , file = f )
418- print ("}" , file = f )
419411 return f .getvalue ()
420412
421413
@@ -433,18 +425,18 @@ def discover_cflags(cc: typing.List[str], debug: bool = True) -> typing.List[str
433425
434426def compile_to_binary (source : str , memory : int , debug : bool ) -> str :
435427 import shlex
436- import shutil
437428 import subprocess
438429 import sysconfig
439430 import tempfile
440431
441432 cc = env_get_split ("CC" , shlex .split (sysconfig .get_config_var ("CC" )))
442433 cflags = discover_cflags (cc , debug )
443- c_code = compile_to_string (source , memory , debug )
434+ cflags += [f"-DMEMORY_SIZE={ memory } " ]
435+ c_code = compile_to_string (source , debug )
444436 with tempfile .NamedTemporaryFile (mode = "w" , suffix = ".c" , delete = False ) as c_file :
445- outdir = os .path .dirname (c_file .name )
446- shutil .copy ("runtime.c" , outdir )
447437 c_file .write (c_code )
438+ with open ("cli.c" , "r" ) as f :
439+ c_file .write (f .read ())
448440 with tempfile .NamedTemporaryFile (mode = "w" , suffix = ".out" , delete = False ) as out_file :
449441 subprocess .run ([* cc , * cflags , "-o" , out_file .name , c_file .name ], check = True )
450442 return out_file .name
@@ -461,15 +453,20 @@ def main() -> None:
461453 parser .add_argument ("--memory" , type = int , default = 1024 )
462454 parser .add_argument ("--run" , action = "store_true" )
463455 parser .add_argument ("--debug" , action = "store_true" , default = False )
456+ parser .add_argument ("--platform" , default = "cli.c" )
464457 args = parser .parse_args ()
465458
466459 with open (args .file , "r" ) as f :
467460 source = f .read ()
468461
469- c_program = compile_to_string (source , args .memory , args .debug )
462+ c_program = compile_to_string (source , args .debug )
463+
464+ with open (args .platform , "r" ) as f :
465+ platform = f .read ()
470466
471467 with open (args .output , "w" ) as f :
472468 f .write (c_program )
469+ f .write (platform )
473470
474471 if args .format :
475472 import subprocess
@@ -481,6 +478,7 @@ def main() -> None:
481478
482479 cc = env_get_split ("CC" , ["clang" ])
483480 cflags = discover_cflags (cc , args .debug )
481+ cflags += [f"-DMEMORY_SIZE={ args .memory } " ]
484482 ldflags = env_get_split ("LDFLAGS" )
485483 subprocess .run ([* cc , "-o" , "a.out" , * cflags , args .output , * ldflags ], check = True )
486484
0 commit comments