6
6
import os
7
7
import typing
8
8
9
- from typing import Dict , Optional
9
+ from typing import Dict , Optional , Tuple
10
10
11
11
from scrapscript import (
12
12
Access ,
@@ -63,16 +63,35 @@ def __init__(self, main_fn: CompiledFunction) -> None:
63
63
self .functions : typing .List [CompiledFunction ] = [main_fn ]
64
64
self .function : CompiledFunction = main_fn
65
65
self .record_keys : Dict [str , int ] = {}
66
+ self .record_builders : Dict [Tuple [str , ...], CompiledFunction ] = {}
66
67
self .variant_tags : Dict [str , int ] = {}
67
68
self .debug : bool = False
68
- self .used_runtime_functions : set [str ] = set ()
69
69
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
76
95
77
96
def variant_tag (self , key : str ) -> int :
78
97
result = self .variant_tags .get (key )
@@ -295,11 +314,6 @@ def compile(self, env: Env, exp: Object) -> str:
295
314
raise NameError (f"name '{ exp .name } ' is not defined" )
296
315
return var_value
297
316
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 } "
303
317
callee = self .compile (env , exp .func )
304
318
arg = self .compile (env , exp .arg )
305
319
return self ._mktemp (f"closure_call({ callee } , { arg } )" )
@@ -314,14 +328,9 @@ def compile(self, env: Env, exp: Object) -> str:
314
328
values : Dict [str , str ] = {}
315
329
for key , value_exp in exp .data .items ():
316
330
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 )} )" )
325
334
if isinstance (exp , Access ):
326
335
assert isinstance (exp .at , Var ), f"only Var access is supported, got { type (exp .at )} "
327
336
record = self .compile (env , exp .obj )
@@ -345,11 +354,6 @@ def compile(self, env: Env, exp: Object) -> str:
345
354
# The const heap will never be scanned
346
355
# The const heap can be serialized to disk and mmap'd
347
356
348
- BUILTINS = [
349
- "print" ,
350
- "println" ,
351
- ]
352
-
353
357
354
358
def env_get_split (key : str , default : Optional [typing .List [str ]] = None ) -> typing .List [str ]:
355
359
import shlex
@@ -362,7 +366,7 @@ def env_get_split(key: str, default: Optional[typing.List[str]] = None) -> typin
362
366
return []
363
367
364
368
365
- def compile_to_string (source : str , memory : int , debug : bool ) -> str :
369
+ def compile_to_string (source : str , debug : bool ) -> str :
366
370
program = parse (tokenize (source ))
367
371
368
372
main_fn = CompiledFunction ("scrap_main" , params = [])
@@ -371,20 +375,20 @@ def compile_to_string(source: str, memory: int, debug: bool) -> str:
371
375
result = compiler .compile ({}, program )
372
376
main_fn .code .append (f"return { result } ;" )
373
377
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
-
380
378
f = io .StringIO ()
381
- print ('#include "runtime.c"' , file = f )
379
+ with open ("runtime.c" , "r" ) as runtime :
380
+ print (runtime .read (), file = f )
382
381
print ("#define OBJECT_HANDLE(name, exp) GC_HANDLE(struct object*, name, exp)" , file = f )
383
382
# Declare all functions
384
383
print ("const char* record_keys[] = {" , file = f )
385
384
for key in compiler .record_keys :
386
385
print (f'"{ key } ",' , file = f )
387
386
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 )
388
392
if compiler .variant_tags :
389
393
print ("const char* variant_names[] = {" , file = f )
390
394
for key in compiler .variant_tags :
@@ -399,23 +403,11 @@ def compile_to_string(source: str, memory: int, debug: bool) -> str:
399
403
print ("const char* variant_names[] = { NULL };" , file = f )
400
404
for function in compiler .functions :
401
405
print (function .decl () + ";" , file = f )
402
- for builtin in builtins :
403
- print (f"struct object* builtin_{ builtin } = NULL;" , file = f )
404
406
for function in compiler .functions :
405
407
print (f"{ function .decl ()} {{" , file = f )
406
408
for line in function .code :
407
409
print (line , file = f )
408
410
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 )
419
411
return f .getvalue ()
420
412
421
413
@@ -433,18 +425,18 @@ def discover_cflags(cc: typing.List[str], debug: bool = True) -> typing.List[str
433
425
434
426
def compile_to_binary (source : str , memory : int , debug : bool ) -> str :
435
427
import shlex
436
- import shutil
437
428
import subprocess
438
429
import sysconfig
439
430
import tempfile
440
431
441
432
cc = env_get_split ("CC" , shlex .split (sysconfig .get_config_var ("CC" )))
442
433
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 )
444
436
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 )
447
437
c_file .write (c_code )
438
+ with open ("cli.c" , "r" ) as f :
439
+ c_file .write (f .read ())
448
440
with tempfile .NamedTemporaryFile (mode = "w" , suffix = ".out" , delete = False ) as out_file :
449
441
subprocess .run ([* cc , * cflags , "-o" , out_file .name , c_file .name ], check = True )
450
442
return out_file .name
@@ -461,15 +453,20 @@ def main() -> None:
461
453
parser .add_argument ("--memory" , type = int , default = 1024 )
462
454
parser .add_argument ("--run" , action = "store_true" )
463
455
parser .add_argument ("--debug" , action = "store_true" , default = False )
456
+ parser .add_argument ("--platform" , default = "cli.c" )
464
457
args = parser .parse_args ()
465
458
466
459
with open (args .file , "r" ) as f :
467
460
source = f .read ()
468
461
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 ()
470
466
471
467
with open (args .output , "w" ) as f :
472
468
f .write (c_program )
469
+ f .write (platform )
473
470
474
471
if args .format :
475
472
import subprocess
@@ -481,6 +478,7 @@ def main() -> None:
481
478
482
479
cc = env_get_split ("CC" , ["clang" ])
483
480
cflags = discover_cflags (cc , args .debug )
481
+ cflags += [f"-DMEMORY_SIZE={ args .memory } " ]
484
482
ldflags = env_get_split ("LDFLAGS" )
485
483
subprocess .run ([* cc , "-o" , "a.out" , * cflags , args .output , * ldflags ], check = True )
486
484
0 commit comments