diff --git a/docs/c-interoperability.md b/docs/c-interoperability.md index 3c5bbff..3c0906e 100644 --- a/docs/c-interoperability.md +++ b/docs/c-interoperability.md @@ -16,21 +16,19 @@ from inside a Tomo source file. ## Inline C Code -As a final escape hatch, you can use `inline C` to add code that will be put, +As a final escape hatch, you can use `C_code` to add code that will be put, verbatim in the transpiled C code generated by Tomo. There are two forms: one that creates an expression value and one that creates a block that is executed without evaluating to anything: ```tomo # Inline C block: -inline C { +C_code { printf("This is just a block that is executed without a return value\n"); } # Inline C expression (you must specify a type) -val := inline C : Int32 { - int x = 1; x + 1 -} +val := C_code : Int32 (int x = 1; x + 1) ``` Inline C expressions must specify a type and they can be [compound statement diff --git a/examples/commands/commands.tm b/examples/commands/commands.tm index 81c4369..ace7deb 100644 --- a/examples/commands/commands.tm +++ b/examples/commands/commands.tm @@ -58,22 +58,22 @@ struct Command(command:Text, args:[Text]=[], env:{Text=Text}={}) stderr : [Byte] status := run_command(command.command, command.args, command.env, input_bytes, &stdout, &stderr) - if inline C : Bool { WIFEXITED(_$status) } - return ProgramResult(stdout, stderr, ExitType.Exited(inline C : Int32 { WEXITSTATUS(_$status) })) + if C_code:Bool(WIFEXITED(_$status)) + return ProgramResult(stdout, stderr, ExitType.Exited(C_code:Int32(WEXITSTATUS(_$status)))) - if inline C : Bool { WIFSIGNALED(_$status) } - return ProgramResult(stdout, stderr, ExitType.Signaled(inline C : Int32 { WTERMSIG(_$status) })) + if C_code:Bool(WIFSIGNALED(_$status)) + return ProgramResult(stdout, stderr, ExitType.Signaled(C_code:Int32(WTERMSIG(_$status)))) return ProgramResult(stdout, stderr, ExitType.Failed) func run(command:Command, -> ExitType) status := run_command(command.command, command.args, command.env, none, none, none) - if inline C : Bool { WIFEXITED(_$status) } - return ExitType.Exited(inline C : Int32 { WEXITSTATUS(_$status) }) + if C_code:Bool(WIFEXITED(_$status)) + return ExitType.Exited(C_code:Int32(WEXITSTATUS(_$status))) - if inline C : Bool { WIFSIGNALED(_$status) } - return ExitType.Signaled(inline C : Int32 { WTERMSIG(_$status) }) + if C_code:Bool(WIFSIGNALED(_$status)) + return ExitType.Signaled(C_code:Int32(WTERMSIG(_$status))) return ExitType.Failed diff --git a/examples/game/player.tm b/examples/game/player.tm index 9f166d9..2e5e54f 100644 --- a/examples/game/player.tm +++ b/examples/game/player.tm @@ -10,12 +10,12 @@ struct Player(pos,prev_pos:Vector2) COLOR := Color(0x60, 0x60, 0xbF) func update(p:@Player) - target_x := inline C:Num32 { + target_x := C_code:Num32( (Num32_t)((IsKeyDown(KEY_A) ? -1 : 0) + (IsKeyDown(KEY_D) ? 1 : 0)) - } - target_y := inline C:Num32 { + ) + target_y := C_code:Num32( (Num32_t)((IsKeyDown(KEY_W) ? -1 : 0) + (IsKeyDown(KEY_S) ? 1 : 0)) - } + ) target_vel := Vector2(target_x, target_y).norm() * Player.WALK_SPEED vel := (p.pos - p.prev_pos)/World.DT diff --git a/examples/http-server/http-server.tm b/examples/http-server/http-server.tm index 92651ca..3d72c1e 100644 --- a/examples/http-server/http-server.tm +++ b/examples/http-server/http-server.tm @@ -22,30 +22,30 @@ func serve(port:Int32, handler:func(request:HTTPRequest -> HTTPResponse), num_th workers.insert(pthread_t.new(func() repeat connection := connections.dequeue() - request_text := inline C : Text { + request_text := C_code:Text( Text_t request = EMPTY_TEXT; char buf[1024] = {}; - for (ssize_t n; (n = read(_$connection, buf, sizeof(buf) - 1)) > 0; ) { + for (ssize_t n; (n = read(@connection, buf, sizeof(buf) - 1)) > 0; ) { buf[n] = 0; request = Text$concat(request, Text$from_strn(buf, n)); if (request.length > 1000000 || strstr(buf, "\r\n\r\n")) break; } request - } + ) request := HTTPRequest.from_text(request_text) or skip response := handler(request).bytes() - inline C { - if (_$response.stride != 1) - Array$compact(&_$response, 1); - write(_$connection, _$response.data, _$response.length); - close(_$connection); + C_code { + if (@response.stride != 1) + Array$compact(&@response, 1); + write(@connection, @response.data, @response.length); + close(@connection); } )) - sock := inline C : Int32 { + sock := C_code:Int32( int s = socket(AF_INET, SOCK_STREAM, 0); if (s < 0) err(1, "Couldn't connect to socket!"); @@ -53,17 +53,17 @@ func serve(port:Int32, handler:func(request:HTTPRequest -> HTTPResponse), num_th if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) < 0) err(1, "Couldn't set socket option"); - struct sockaddr_in addr = {AF_INET, htons(_$port), INADDR_ANY}; + struct sockaddr_in addr = {AF_INET, htons(@port), INADDR_ANY}; if (bind(s, (struct sockaddr*)&addr, sizeof(addr)) < 0) err(1, "Couldn't bind to socket"); if (listen(s, 8) < 0) err(1, "Couldn't listen on socket"); s - } + ) repeat - conn := inline C : Int32 { accept(_$sock, NULL, NULL) } + conn := C_code:Int32(accept(@sock, NULL, NULL)) stop if conn < 0 connections.enqueue(conn) diff --git a/examples/http/http.tm b/examples/http/http.tm index 29727e6..3fe41ae 100644 --- a/examples/http/http.tm +++ b/examples/http/http.tm @@ -10,74 +10,72 @@ enum _Method(GET, POST, PUT, PATCH, DELETE) func _send(method:_Method, url:Text, data:Text?, headers:[Text]=[] -> HTTPResponse) chunks : @[Text] save_chunk := func(chunk:CString, size:Int64, n:Int64) - chunks.insert(inline C:Text { - Text$format("%.*s", _$size*_$n, _$chunk) - }) + chunks.insert(C_code:Text(Text$from_strn(@chunk, @size*@n))) return n*size - inline C { + C_code { CURL *curl = curl_easy_init(); struct curl_slist *chunk = NULL; - curl_easy_setopt(curl, CURLOPT_URL, Text$as_c_string(_$url)); - curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, _$save_chunk.fn); - curl_easy_setopt(curl, CURLOPT_WRITEDATA, _$save_chunk.userdata); + curl_easy_setopt(curl, CURLOPT_URL, @(CString(url))); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, @save_chunk.fn); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, @save_chunk.userdata); } defer - inline C { + C_code { if (chunk) curl_slist_free_all(chunk); curl_easy_cleanup(curl); } when method is POST - inline C { + C_code { curl_easy_setopt(curl, CURLOPT_POST, 1L); } if posting := data - inline C { - curl_easy_setopt(curl, CURLOPT_POSTFIELDS, Text$as_c_string(_$posting)); + C_code { + curl_easy_setopt(curl, CURLOPT_POSTFIELDS, @(CString(posting))); } is PUT - inline C { + C_code { curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "PUT"); } if putting := data - inline C { - curl_easy_setopt(curl, CURLOPT_POSTFIELDS, Text$as_c_string(_$putting)); + C_code { + curl_easy_setopt(curl, CURLOPT_POSTFIELDS, @(CString(putting))); } is PATCH - inline C { + C_code { curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "PATCH"); } if patching := data - inline C { - curl_easy_setopt(curl, CURLOPT_POSTFIELDS, Text$as_c_string(_$patching)); + C_code { + curl_easy_setopt(curl, CURLOPT_POSTFIELDS, @(CString(patching))); } is DELETE - inline C { + C_code { curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "DELETE"); } else pass for header in headers - inline C { - chunk = curl_slist_append(chunk, Text$as_c_string(_$header)); + C_code { + chunk = curl_slist_append(chunk, @(CString(header))); } - inline C { + C_code { if (chunk) curl_easy_setopt(curl, CURLOPT_HTTPHEADER, chunk); } code := Int64(0) - inline C { + C_code { CURLcode res = curl_easy_perform(curl); if (res != CURLE_OK) fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(res)); - curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &_$code); + curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &@code); } return HTTPResponse(Int(code), "".join(chunks)) diff --git a/examples/log/log.tm b/examples/log/log.tm index 94984b8..9c3396e 100644 --- a/examples/log/log.tm +++ b/examples/log/log.tm @@ -6,13 +6,13 @@ timestamp_format := CString("%F %T") logfiles : @|Path| func _timestamp(->Text) - c_str := inline C:CString { + c_str := C_code:CString( char *str = GC_MALLOC_ATOMIC(20); time_t t; time(&t); struct tm *tm_info = localtime(&t); strftime(str, 20, "%F %T", tm_info); str - } + ) return c_str.as_text() func info(text:Text, newline=yes) diff --git a/examples/patterns/patterns.tm b/examples/patterns/patterns.tm index 3e671b8..bab0c3d 100644 --- a/examples/patterns/patterns.tm +++ b/examples/patterns/patterns.tm @@ -4,44 +4,44 @@ struct PatternMatch(text:Text, index:Int, captures:[Text]) lang Pat convert(text:Text -> Pat) - return inline C : Pat { Pattern$escape_text(_$text); } + return C_code:Pat(Pattern$escape_text(@text)) convert(n:Int -> Pat) return Pat.from_text("$n") extend Text func matches_pattern(text:Text, pattern:Pat -> Bool) - return inline C : Bool { Pattern$matches(_$text, _$pattern); } + return C_code:Bool(Pattern$matches(@text, @pattern)) func pattern_captures(text:Text, pattern:Pat -> [Text]?) - return inline C : [Text]? { Pattern$captures(_$text, _$pattern); } + return C_code:[Text]?(Pattern$captures(@text, @pattern)) func replace_pattern(text:Text, pattern:Pat, replacement:Text, backref="@", recursive=yes -> Text) - return inline C : Text { Pattern$replace(_$text, _$pattern, _$replacement, _$backref, _$recursive); } + return C_code:Text(Pattern$replace(@text, @pattern, @replacement, @backref, @recursive)) func translate_patterns(text:Text, replacements:{Pat=Text}, backref="@", recursive=yes -> Text) - return inline C : Text { Pattern$replace_all(_$text, _$replacements, _$backref, _$recursive); } + return C_code:Text(Pattern$replace_all(@text, @replacements, @backref, @recursive)) func has_pattern(text:Text, pattern:Pat -> Bool) - return inline C : Bool { Pattern$has(_$text, _$pattern); } + return C_code:Bool(Pattern$has(@text, @pattern)) func find_patterns(text:Text, pattern:Pat -> [PatternMatch]) - return inline C : [PatternMatch] { Pattern$find_all(_$text, _$pattern); } + return C_code:[PatternMatch](Pattern$find_all(@text, @pattern)) func by_pattern(text:Text, pattern:Pat -> func(->PatternMatch?)) - return inline C : func(->PatternMatch?) { Pattern$by_match(_$text, _$pattern); } + return C_code:func(->PatternMatch?)(Pattern$by_match(@text, @pattern)) func each_pattern(text:Text, pattern:Pat, fn:func(m:PatternMatch), recursive=yes) - inline C { Pattern$each(_$text, _$pattern, _$fn, _$recursive); } + C_code { Pattern$each(@text, @pattern, @fn, @recursive); } func map_pattern(text:Text, pattern:Pat, fn:func(m:PatternMatch -> Text), recursive=yes -> Text) - return inline C : Text { Pattern$map(_$text, _$pattern, _$fn, _$recursive); } + return C_code:Text(Pattern$map(@text, @pattern, @fn, @recursive)) func split_pattern(text:Text, pattern:Pat -> [Text]) - return inline C : [Text] { Pattern$split(_$text, _$pattern); } + return C_code:[Text](Pattern$split(@text, @pattern)) func by_pattern_split(text:Text, pattern:Pat -> func(->Text?)) - return inline C : func(->Text?) { Pattern$by_split(_$text, _$pattern); } + return C_code:func(->Text?)(Pattern$by_split(@text, @pattern)) func trim_pattern(text:Text, pattern=$Pat"{space}", left=yes, right=yes -> Text) - return inline C : Text { Pattern$trim(_$text, _$pattern, _$left, _$right); } + return C_code:Text(Pattern$trim(@text, @pattern, @left, @right)) diff --git a/examples/pthreads/pthreads.tm b/examples/pthreads/pthreads.tm index 61f94d0..7f720d5 100644 --- a/examples/pthreads/pthreads.tm +++ b/examples/pthreads/pthreads.tm @@ -3,66 +3,66 @@ use struct pthread_mutex_t(; extern, opaque) func new(->@pthread_mutex_t) - return inline C : @pthread_mutex_t { + return C_code : @pthread_mutex_t( pthread_mutex_t *mutex = GC_MALLOC(sizeof(pthread_mutex_t)); pthread_mutex_init(mutex, NULL); GC_register_finalizer(mutex, (void*)pthread_mutex_destroy, NULL, NULL, NULL); mutex - } + ) func lock(m:&pthread_mutex_t) - fail("Failed to lock mutex") unless inline C : Int32 { pthread_mutex_lock(_$m); } == 0 + fail("Failed to lock mutex") unless C_code:Int32(pthread_mutex_lock(@m)) == 0 func unlock(m:&pthread_mutex_t) - fail("Failed to unlock mutex") unless inline C : Int32 { pthread_mutex_unlock(_$m); } == 0 + fail("Failed to unlock mutex") unless C_code:Int32(pthread_mutex_unlock(@m)) == 0 struct pthread_cond_t(; extern, opaque) func new(->@pthread_cond_t) - return inline C : @pthread_cond_t { + return C_code : @pthread_cond_t( pthread_cond_t *cond = GC_MALLOC(sizeof(pthread_cond_t)); pthread_cond_init(cond, NULL); GC_register_finalizer(cond, (void*)pthread_cond_destroy, NULL, NULL, NULL); cond - } + ) func wait(cond:&pthread_cond_t, mutex:&pthread_mutex_t) - fail("Failed to wait on condition") unless inline C : Int32 { pthread_cond_wait(_$cond, _$mutex); } == 0 + fail("Failed to wait on condition") unless C_code:Int32(pthread_cond_wait(@cond, @mutex)) == 0 func signal(cond:&pthread_cond_t) - fail("Failed to signal pthread_cond_t") unless inline C : Int32 { pthread_cond_signal(_$cond); } == 0 + fail("Failed to signal pthread_cond_t") unless C_code:Int32(pthread_cond_signal(@cond)) == 0 func broadcast(cond:&pthread_cond_t) - fail("Failed to broadcast pthread_cond_t") unless inline C : Int32 { pthread_cond_broadcast(_$cond); } == 0 + fail("Failed to broadcast pthread_cond_t") unless C_code:Int32(pthread_cond_broadcast(@cond)) == 0 struct pthread_rwlock_t(; extern, opaque) func new(->@pthread_rwlock_t) - return inline C : @pthread_rwlock_t { + return C_code : @pthread_rwlock_t ( pthread_rwlock_t *lock = GC_MALLOC(sizeof(pthread_rwlock_t)); pthread_rwlock_init(lock, NULL); GC_register_finalizer(lock, (void*)pthread_rwlock_destroy, NULL, NULL, NULL); lock - } + ) func read_lock(lock:&pthread_rwlock_t) - inline C { pthread_rwlock_rdlock(_$lock); } + C_code { pthread_rwlock_rdlock(@lock); } func write_lock(lock:&pthread_rwlock_t) - inline C { pthread_rwlock_wrlock(_$lock); } + C_code { pthread_rwlock_wrlock(@lock); } func unlock(lock:&pthread_rwlock_t) - inline C { pthread_rwlock_unlock(_$lock); } + C_code { pthread_rwlock_unlock(@lock); } struct pthread_t(; extern, opaque) func new(fn:func() -> @pthread_t) - return inline C : @pthread_t { + return C_code:@pthread_t( pthread_t *thread = GC_MALLOC(sizeof(pthread_t)); - pthread_create(thread, NULL, _$fn.fn, _$fn.userdata); + pthread_create(thread, NULL, @fn.fn, @fn.userdata); thread - } + ) - func join(p:pthread_t) inline C { pthread_join(_$p, NULL); } - func cancel(p:pthread_t) inline C { pthread_cancel(_$p); } - func detatch(p:pthread_t) inline C { pthread_detach(_$p); } + func join(p:pthread_t) C_code { pthread_join(@p, NULL); } + func cancel(p:pthread_t) C_code { pthread_cancel(@p); } + func detatch(p:pthread_t) C_code { pthread_detach(@p); } struct IntQueue(_queue:@[Int], _mutex:@pthread_mutex_t, _cond:@pthread_cond_t) func new(initial:[Int]=[] -> IntQueue) diff --git a/examples/random/random.tm b/examples/random/random.tm index 94528dd..13600b4 100644 --- a/examples/random/random.tm +++ b/examples/random/random.tm @@ -5,77 +5,72 @@ use ./chacha.h struct chacha_ctx(j0,j1,j2,j3,j4,j5,j6,j7,j8,j9,j10,j11,j12,j13,j14,j15:Int32; extern, secret) func from_seed(seed:[Byte]=[] -> chacha_ctx) - return inline C : chacha_ctx { + return C_code:chacha_ctx( chacha_ctx ctx; uint8_t seed_bytes[KEYSZ + IVSZ] = {}; for (int64_t i = 0; i < (int64_t)sizeof(seed_bytes); i++) - seed_bytes[i] = i < _$seed.length ? *(uint8_t*)(_$seed.data + i*_$seed.stride) : 0; + seed_bytes[i] = i < @seed.length ? *(uint8_t*)(@seed.data + i*@seed.stride) : 0; chacha_keysetup(&ctx, seed_bytes); chacha_ivsetup(&ctx, seed_bytes + KEYSZ); - ctx; - } + ctx + ) random := RandomNumberGenerator.new() func _os_random_bytes(count:Int64 -> [Byte]) - return inline C : [Byte] { - uint8_t *random_bytes = GC_MALLOC_ATOMIC(_$count); - getrandom(random_bytes, _$count, 0); - (Array_t){.length=_$count, .data=random_bytes, .stride=1, .atomic=1}; - } - + return C_code:[Byte]( + uint8_t *random_bytes = GC_MALLOC_ATOMIC(@count); + getrandom(random_bytes, @count, 0); + (Array_t){.length=@count, .data=random_bytes, .stride=1, .atomic=1} + ) struct RandomNumberGenerator(_chacha:chacha_ctx, _random_bytes:[Byte]=[]; secret) func new(seed:[Byte]?=none, -> @RandomNumberGenerator) ctx := chacha_ctx.from_seed(seed or _os_random_bytes(40)) return @RandomNumberGenerator(ctx, []) func _rekey(rng:&RandomNumberGenerator) - rng._random_bytes = inline C : [Byte] { + rng._random_bytes = C_code:[Byte]( Byte_t new_keystream[KEYSZ + IVSZ] = {}; // Fill the buffer with the keystream - chacha_encrypt_bytes(&_$rng->_chacha, new_keystream, new_keystream, sizeof(new_keystream)); + chacha_encrypt_bytes(&@rng->_chacha, new_keystream, new_keystream, sizeof(new_keystream)); // Immediately reinitialize for backtracking resistance - chacha_keysetup(&_$rng->_chacha, new_keystream); - chacha_ivsetup(&_$rng->_chacha, new_keystream + KEYSZ); + chacha_keysetup(&@rng->_chacha, new_keystream); + chacha_ivsetup(&@rng->_chacha, new_keystream + KEYSZ); Array_t new_bytes = (Array_t){.data=GC_MALLOC_ATOMIC(1024), .length=1024, .stride=1, .atomic=1}; memset(new_bytes.data, 0, new_bytes.length); - chacha_encrypt_bytes(&_$rng->_chacha, new_bytes.data, new_bytes.data, new_bytes.length); - new_bytes; - } + chacha_encrypt_bytes(&@rng->_chacha, new_bytes.data, new_bytes.data, new_bytes.length); + new_bytes + ) func _fill_bytes(rng:&RandomNumberGenerator, dest:&Memory, needed:Int64) - inline C { - while (_$needed > 0) { - if (_$rng->_random_bytes.length == 0) - _$random$RandomNumberGenerator$_rekey(_$rng); + C_code { + while (@needed > 0) { + if (@rng->_random_bytes.length == 0) + @(rng._rekey()); - assert(_$rng->_random_bytes.stride == 1); + assert(@rng->_random_bytes.stride == 1); - int64_t batch_size = MIN(_$needed, _$rng->_random_bytes.length); - uint8_t *batch_src = _$rng->_random_bytes.data; - memcpy(_$dest, batch_src, batch_size); + int64_t batch_size = MIN(@needed, @rng->_random_bytes.length); + uint8_t *batch_src = @rng->_random_bytes.data; + memcpy(@dest, batch_src, batch_size); memset(batch_src, 0, batch_size); - _$rng->_random_bytes.data += batch_size; - _$rng->_random_bytes.length -= batch_size; - _$dest += batch_size; - _$needed -= batch_size; + @rng->_random_bytes.data += batch_size; + @rng->_random_bytes.length -= batch_size; + @dest += batch_size; + @needed -= batch_size; } } func bytes(rng:&RandomNumberGenerator, count:Int -> [Byte]) - return inline C : [Byte] { - int64_t count64 = Int64$from_int(_$count, false); - Array_t ret = {.data=GC_MALLOC_ATOMIC(count64), .stride=1, .atomic=1, .length=count64}; - _$random$RandomNumberGenerator$_fill_bytes(_$rng, ret.data, count64); - ret; - } + count64 := Int64(count) + buf := C_code:@Memory(GC_MALLOC_ATOMIC(@count64)) + rng._fill_bytes(buf, count64) + return C_code:[Byte]((Array_t){.data=@buf, .stride=1, .atomic=1, .length=@count64}) func byte(rng:&RandomNumberGenerator -> Byte) - return inline C : Byte { - Byte_t b; - _$random$RandomNumberGenerator$_fill_bytes(_$rng, &b, sizeof(b)); - b; - } + byte : &Byte + rng._fill_bytes(byte, 1) + return byte[] func bool(rng:&RandomNumberGenerator, probability=0.5 -> Bool) if probability == 0.5 @@ -86,97 +81,95 @@ struct RandomNumberGenerator(_chacha:chacha_ctx, _random_bytes:[Byte]=[]; secret func int64(rng:&RandomNumberGenerator, min=Int64.min, max=Int64.max -> Int64) fail("Random minimum value $min is larger than the maximum value $max") if min > max return min if min == max + random_int64 : &Int64 + rng._fill_bytes(random_int64, 8) if min == Int64.min and max == Int64.max - return inline C : Int64 { - int64_t i; - _$random$RandomNumberGenerator$_fill_bytes(_$rng, &i, sizeof(i)); - i; - } + return random_int64 - return inline C : Int64 { - uint64_t range = (uint64_t)_$max - (uint64_t)_$min + 1; + return C_code:Int64( + uint64_t range = (uint64_t)@max - (uint64_t)@min + 1; uint64_t min_r = -range % range; uint64_t r; + @random_int64 = &r; for (;;) { - _$random$RandomNumberGenerator$_fill_bytes(_$rng, (uint8_t*)&r, sizeof(r)); + @(rng._fill_bytes(random_int64, 8)); if (r >= min_r) break; } - (int64_t)((uint64_t)_$min + (r % range)); - } + (int64_t)((uint64_t)@min + (r % range)) + ) func int32(rng:&RandomNumberGenerator, min=Int32.min, max=Int32.max -> Int32) fail("Random minimum value $min is larger than the maximum value $max") if min > max return min if min == max + random_int32 : &Int32 + rng._fill_bytes(random_int32, 8) if min == Int32.min and max == Int32.max - return inline C : Int32 { - int32_t i; - _$random$RandomNumberGenerator$_fill_bytes(_$rng, &i, sizeof(i)); - i; - } + return random_int32 - return inline C : Int32 { - uint32_t range = (uint32_t)_$max - (uint32_t)_$min + 1; + return C_code:Int32( + uint32_t range = (uint32_t)@max - (uint32_t)@min + 1; uint32_t min_r = -range % range; uint32_t r; + @random_int32 = &r; for (;;) { - _$random$RandomNumberGenerator$_fill_bytes(_$rng, (uint8_t*)&r, sizeof(r)); + @(rng._fill_bytes(random_int32, 8)); if (r >= min_r) break; } - (int32_t)((uint32_t)_$min + (r % range)); - } + (int32_t)((uint32_t)@min + (r % range)) + ) func int16(rng:&RandomNumberGenerator, min=Int16.min, max=Int16.max -> Int16) fail("Random minimum value $min is larger than the maximum value $max") if min > max return min if min == max + random_int16 : &Int16 + rng._fill_bytes(random_int16, 8) if min == Int16.min and max == Int16.max - return inline C : Int16 { - int16_t i; - _$random$RandomNumberGenerator$_fill_bytes(_$rng, &i, sizeof(i)); - i; - } + return random_int16 - return inline C : Int16 { - uint16_t range = (uint16_t)_$max - (uint16_t)_$min + 1; + return C_code:Int16( + uint16_t range = (uint16_t)@max - (uint16_t)@min + 1; uint16_t min_r = -range % range; uint16_t r; + @random_int16 = &r; for (;;) { - _$random$RandomNumberGenerator$_fill_bytes(_$rng, (uint8_t*)&r, sizeof(r)); + @(rng._fill_bytes(random_int16, 8)); if (r >= min_r) break; } - (int16_t)((uint16_t)_$min + (r % range)); - } + (int16_t)((uint16_t)@min + (r % range)) + ) func int8(rng:&RandomNumberGenerator, min=Int8.min, max=Int8.max -> Int8) fail("Random minimum value $min is larger than the maximum value $max") if min > max return min if min == max + random_int8 : &Int8 + rng._fill_bytes(random_int8, 8) if min == Int8.min and max == Int8.max - return inline C : Int8 { - int8_t i; - _$random$RandomNumberGenerator$_fill_bytes(_$rng, &i, sizeof(i)); - i; - } + return random_int8 - return inline C : Int8 { - uint8_t range = (uint8_t)_$max - (uint8_t)_$min + 1; + return C_code:Int8( + uint8_t range = (uint8_t)@max - (uint8_t)@min + 1; uint8_t min_r = -range % range; uint8_t r; + @random_int8 = &r; for (;;) { - _$random$RandomNumberGenerator$_fill_bytes(_$rng, (uint8_t*)&r, sizeof(r)); + @(rng._fill_bytes(random_int8, 8)); if (r >= min_r) break; } - (int8_t)((uint8_t)_$min + (r % range)); - } + (int8_t)((uint8_t)@min + (r % range)) + ) func num(rng:&RandomNumberGenerator, min=0., max=1. -> Num) - return inline C : Num { - if (_$min > _$max) fail("Random minimum value (", _$min, ") is larger than the maximum value (", _$max, ")"); - if (_$min == _$max) return _$min; + num_buf : &Num + return C_code:Num( + if (@min > @max) fail("Random minimum value (", @min, ") is larger than the maximum value (", @max, ")"); + if (@min == @max) return @min; union { Num_t num; uint64_t bits; } r = {.bits=0}, one = {.num=1.0}; - _$random$RandomNumberGenerator$_fill_bytes(_$rng, (uint8_t*)&r, sizeof(r)); + @num_buf = &r.num; + @(rng._fill_bytes(num_buf, 8)); // Set r.num to 1. r.bits &= ~(0xFFFULL << 52); @@ -184,37 +177,37 @@ struct RandomNumberGenerator(_chacha:chacha_ctx, _random_bytes:[Byte]=[]; secret r.num -= 1.0; - (_$min == 0.0 && _$max == 1.0) ? r.num : ((1.0-r.num)*_$min + r.num*_$max); - } + (@min == 0.0 && @max == 1.0) ? r.num : ((1.0-r.num)*@min + r.num*@max) + ) func num32(rng:&RandomNumberGenerator, min=Num32(0.), max=Num32(1.) -> Num32) return Num32(rng.num(Num(min), Num(max))) func int(rng:&RandomNumberGenerator, min:Int, max:Int -> Int) - return inline C : Int { - if (likely(((_$min.small & _$max.small) & 1) != 0)) { - int32_t r = _$random$RandomNumberGenerator$int32(_$rng, (int32_t)(_$min.small >> 2), (int32_t)(_$max.small >> 2)); + return C_code:Int( + if (likely(((@min.small & @max.small) & 1) != 0)) { + int32_t r = @(rng.int32(Int32(min), Int32(max))); return I_small(r); } - int32_t cmp = Int$compare_value(_$min, _$max); + int32_t cmp = @(min <> max); if (cmp > 0) - fail("Random minimum value (", _$min, ") is larger than the maximum value (", _$max, ")"); - if (cmp == 0) return _$min; + fail("Random minimum value (", @min, ") is larger than the maximum value (", @max, ")"); + if (cmp == 0) return @min; mpz_t range_size; - mpz_init_set_int(range_size, _$max); - if (_$min.small & 1) { + mpz_init_set_int(range_size, @max); + if (@min.small & 1) { mpz_t min_mpz; - mpz_init_set_si(min_mpz, _$min.small >> 2); + mpz_init_set_si(min_mpz, @min.small >> 2); mpz_sub(range_size, range_size, min_mpz); } else { - mpz_sub(range_size, range_size, *_$min.big); + mpz_sub(range_size, range_size, *@min.big); } gmp_randstate_t gmp_rng; gmp_randinit_default(gmp_rng); - int64_t seed = _$random$RandomNumberGenerator$int64(_$rng, INT64_MIN, INT64_MAX); + int64_t seed = @(rng.int64()); gmp_randseed_ui(gmp_rng, (unsigned long)seed); mpz_t r; @@ -222,8 +215,8 @@ struct RandomNumberGenerator(_chacha:chacha_ctx, _random_bytes:[Byte]=[]; secret mpz_urandomm(r, gmp_rng, range_size); gmp_randclear(gmp_rng); - Int$plus(_$min, Int$from_mpz(r)); - } + Int$plus(@min, Int$from_mpz(r)) + ) func main() diff --git a/examples/time/time.tm b/examples/time/time.tm index 4a5bfcf..1796654 100644 --- a/examples/time/time.tm +++ b/examples/time/time.tm @@ -7,15 +7,15 @@ struct TimeInfo(year,month,day,hour,minute,second,nanosecond:Int, weekday:Weekda struct Time(tv_sec:Int64, tv_usec:Int64; extern) func now(->Time) - return inline C : Time { + return C_code : Time ( struct timespec ts; if (clock_gettime(CLOCK_REALTIME, &ts) != 0) fail("Couldn't get the time!"); - (Time){.tv_sec=ts.tv_sec, .tv_usec=ts.tv_nsec/1000}; - } + (Time){.tv_sec=ts.tv_sec, .tv_usec=ts.tv_nsec/1000} + ) func local_timezone(->Text) - inline C { + C_code { if (_local_timezone.length < 0) { static char buf[PATH_MAX]; ssize_t len = readlink("/etc/localtime", buf, sizeof(buf)); @@ -29,47 +29,47 @@ struct Time(tv_sec:Int64, tv_usec:Int64; extern) fail("Could not resolve local tz!"); } } - return inline C : Text { _local_timezone; } + return C_code:Text(_local_timezone) func set_local_timezone(timezone:Text) - inline C { - setenv("TZ", Text$as_c_string(_$timezone), 1); - _local_timezone = _$timezone; + C_code { + setenv("TZ", @(CString(timezone)), 1); + _local_timezone = @timezone; tzset(); } func format(t:Time, format="%c", timezone=Time.local_timezone() -> Text) - return inline C : Text { + return C_code : Text ( struct tm result; - time_t time = _$t.tv_sec; + time_t time = @t.tv_sec; struct tm *final_info; - WITH_TIMEZONE(_$timezone, final_info = localtime_r(&time, &result)); + WITH_TIMEZONE(@timezone, final_info = localtime_r(&time, &result)); static char buf[256]; - size_t len = strftime(buf, sizeof(buf), String(_$format), final_info); - Text$from_strn(buf, len); - } + size_t len = strftime(buf, sizeof(buf), String(@format), final_info); + Text$from_strn(buf, len) + ) func new(year,month,day:Int, hour=0, minute=0, second=0.0, timezone=Time.local_timezone() -> Time) - return inline C : Time{ + return C_code : Time( struct tm info = { - .tm_min=Int32$from_int(_$minute, false), - .tm_hour=Int32$from_int(_$hour, false), - .tm_mday=Int32$from_int(_$day, false), - .tm_mon=Int32$from_int(_$month, false) - 1, - .tm_year=Int32$from_int(_$year, false) - 1900, + .tm_min=Int32$from_int(@minute, false), + .tm_hour=Int32$from_int(@hour, false), + .tm_mday=Int32$from_int(@day, false), + .tm_mon=Int32$from_int(@month, false) - 1, + .tm_year=Int32$from_int(@year, false) - 1900, .tm_isdst=-1, }; time_t t; - WITH_TIMEZONE(_$timezone, t = mktime(&info)); - (Time){.tv_sec=t + (time_t)_$second, .tv_usec=(suseconds_t)(fmod(_$second, 1.0) * 1e9)}; - } + WITH_TIMEZONE(@timezone, t = mktime(&info)); + (Time){.tv_sec=t + (time_t)@second, .tv_usec=(suseconds_t)(fmod(@second, 1.0) * 1e9)} + ) func unix_timestamp(t:Time -> Int64) - return inline C : Int64 { (int64_t)_$t.tv_sec } + return C_code:Int64((int64_t)@t.tv_sec) func from_unix_timestamp(timestamp:Int64 -> Time) - return inline C : Time { (Time){.tv_sec=_$timestamp}; } + return C_code:Time((Time){.tv_sec=@timestamp};) func seconds_till(t:Time, target:Time -> Num) seconds := Num(target.tv_sec - t.tv_sec) @@ -83,14 +83,14 @@ struct Time(tv_sec:Int64, tv_usec:Int64; extern) return t.seconds_till(target)/3600. func relative(t:Time, relative_to=Time.now(), timezone=Time.local_timezone() -> Text) - inline C { + C_code { struct tm info = {}; struct tm relative_info = {}; - WITH_TIMEZONE(_$timezone, { - localtime_r(&_$t.tv_sec, &info); - localtime_r(&_$relative_to.tv_sec, &relative_info); + WITH_TIMEZONE(@timezone, { + localtime_r(&@t.tv_sec, &info); + localtime_r(&@relative_to.tv_sec, &relative_info); }); - double second_diff = _$time$Time$seconds_till(_$relative_to, _$t); + double second_diff = @(relative_to.seconds_till(t)); if (info.tm_year != relative_info.tm_year && fabs(second_diff) > 365.*24.*60.*60.) return num_format((long)info.tm_year - (long)relative_info.tm_year, "year"); else if (info.tm_mon != relative_info.tm_mon && fabs(second_diff) > 31.*24.*60.*60.) @@ -129,9 +129,9 @@ struct Time(tv_sec:Int64, tv_usec:Int64; extern) return t.format("%F") func info(t:Time, timezone=Time.local_timezone() -> TimeInfo) - return inline C : TimeInfo { + return C_code : TimeInfo ( struct tm info = {}; - WITH_TIMEZONE(_$timezone, localtime_r(&_$t.tv_sec, &info)); + WITH_TIMEZONE(@timezone, localtime_r(&@t.tv_sec, &info)); (_$time$TimeInfo$$type){ .year = I(info.tm_year + 1900), .month = I(info.tm_mon + 1), @@ -139,50 +139,50 @@ struct Time(tv_sec:Int64, tv_usec:Int64; extern) .hour = I(info.tm_hour), .minute = I(info.tm_min), .second = I(info.tm_sec), - .nanosecond = I(_$t.tv_usec), + .nanosecond = I(@t.tv_usec), .weekday = (_$time$Weekday$$type){.$tag=info.tm_wday + 1}, .day_of_year = I(info.tm_yday), - .timezone = _$timezone, - }; - } + .timezone = @timezone, + } + ) func after(t:Time, seconds=0.0, minutes=0.0, hours=0.0, days=0, weeks=0, months=0, years=0, timezone=Time.local_timezone() -> Time) - return inline C : Time { - double offset = _$seconds + 60.*_$minutes + 3600.*_$hours ; - _$t.tv_sec += (time_t)offset; + return C_code : Time ( + double offset = @seconds + 60.*@minutes + 3600.*@hours ; + @t.tv_sec += (time_t)offset; struct tm info = {}; - WITH_TIMEZONE(_$timezone, localtime_r(&_$t.tv_sec, &info)); + WITH_TIMEZONE(@timezone, localtime_r(&@t.tv_sec, &info)); - info.tm_mday += Int32$from_int(_$days, false) + 7*Int32$from_int(_$weeks, false); - info.tm_mon += Int32$from_int(_$months, false); - info.tm_year += Int32$from_int(_$years, false); + info.tm_mday += Int32$from_int(@days, false) + 7*Int32$from_int(@weeks, false); + info.tm_mon += Int32$from_int(@months, false); + info.tm_year += Int32$from_int(@years, false); time_t t = mktime(&info); (Time){ .tv_sec=t, - .tv_usec=_$t.tv_usec + (suseconds_t)(fmod(offset, 1.0) * 1e9), - }; - } + .tv_usec=@t.tv_usec + (suseconds_t)(fmod(offset, 1.0) * 1e9), + } + ) func parse(text:Text, format="%Y-%m-%dT%H:%M:%S%z", timezone=Time.local_timezone() -> Time?) - return inline C : Time? { + return C_code : Time? ( struct tm info = {.tm_isdst=-1}; - const char *str = Text$as_c_string(_$text); - const char *fmt = Text$as_c_string(_$format); + const char *str = Text$as_c_string(@text); + const char *fmt = Text$as_c_string(@format); if (strstr(fmt, "%Z")) fail("The %Z specifier is not supported for time parsing!"); char *invalid; - WITH_TIMEZONE(_$timezone, invalid = strptime(str, fmt, &info)); + WITH_TIMEZONE(@timezone, invalid = strptime(str, fmt, &info)); if (!invalid || invalid[0] != '\0') return (_$time$$OptionalTime$$type){.is_none=true}; long offset = info.tm_gmtoff; // Need to cache this because mktime() mutates it to local tz >:( time_t t; - WITH_TIMEZONE(_$timezone, t = mktime(&info)); + WITH_TIMEZONE(@timezone, t = mktime(&info)); (_$time$$OptionalTime$$type){.value={.tv_sec=t + offset - info.tm_gmtoff}}; - } + ) func _run_tests() >> Time.now().format() diff --git a/src/ast.c b/src/ast.c index d4fd956..b350605 100644 --- a/src/ast.c +++ b/src/ast.c @@ -190,7 +190,7 @@ CORD ast_to_xml(ast_t *ast) T(NonOptional, "%r", ast_to_xml(data.value)) T(DocTest, "%r%r", optional_tagged("expression", data.expr), optional_tagged("expected", data.expected)) T(Use, "%r%r", optional_tagged("var", data.var), xml_escape(data.path)) - T(InlineCCode, "%r", xml_escape(data.code)) + T(InlineCCode, "%r", ast_list_to_xml(data.chunks)) T(Deserialize, "%r%r", type_ast_to_xml(data.type), ast_to_xml(data.value)) T(Extend, "%r", data.name, ast_to_xml(data.body)) T(ExplicitlyTyped, "%r", type_to_cord(data.type), ast_to_xml(data.ast)) diff --git a/src/ast.h b/src/ast.h index b48582f..aeb418d 100644 --- a/src/ast.h +++ b/src/ast.h @@ -19,6 +19,7 @@ #define FakeAST(ast_tag, ...) (new(ast_t, .tag=ast_tag, .__data.ast_tag={__VA_ARGS__})) #define WrapAST(ast, ast_tag, ...) (new(ast_t, .file=(ast)->file, .start=(ast)->start, .end=(ast)->end, .tag=ast_tag, .__data.ast_tag={__VA_ARGS__})) #define TextAST(ast, _str) WrapAST(ast, TextLiteral, .str=GC_strdup(_str)) +#define LiteralCode(code, ...) new(ast_t, .tag=InlineCCode, .__data.InlineCCode={.chunks=new(ast_list_t, .ast=FakeAST(TextLiteral, code)), __VA_ARGS__}) #define Match(x, _tag) ((x)->tag == _tag ? &(x)->__data._tag : (errx(1, __FILE__ ":%d This was supposed to be a " # _tag "\n", __LINE__), &(x)->__data._tag)) #define BINARY_OPERANDS(ast) ({ if (!is_binary_operation(ast)) errx(1, __FILE__ ":%d This is not a binary operation!", __LINE__); (ast)->__data.Plus; }) @@ -324,7 +325,7 @@ struct ast_s { enum { USE_LOCAL, USE_MODULE, USE_SHARED_OBJECT, USE_HEADER, USE_C_CODE, USE_ASM } what; } Use; struct { - CORD code; + ast_list_t *chunks; struct type_s *type; type_ast_t *type_ast; } InlineCCode; diff --git a/src/compile.c b/src/compile.c index b49f4c4..cdfaf5c 100644 --- a/src/compile.c +++ b/src/compile.c @@ -123,7 +123,7 @@ static bool promote(env_t *env, ast_t *ast, CORD *code, type_t *actual, type_t * // Numeric promotions/demotions if ((is_numeric_type(actual) || actual->tag == BoolType) && (is_numeric_type(needed) || needed->tag == BoolType)) { - arg_ast_t *args = new(arg_ast_t, .value=FakeAST(InlineCCode, .code=*code, .type=actual)); + arg_ast_t *args = new(arg_ast_t, .value=LiteralCode(*code, .type=actual)); binding_t *constructor = get_constructor(env, needed, args); if (constructor) { auto fn = Match(constructor->type, FunctionType); @@ -555,7 +555,7 @@ static CORD compile_update_assignment(env_t *env, ast_t *ast) *binop = *ast; binop->tag = binop_tag(binop->tag); if (needs_idemotency_fix) - binop->__data.Plus.lhs = WrapAST(update.lhs, InlineCCode, .code="*lhs", .type=lhs_t); + binop->__data.Plus.lhs = LiteralCode("*lhs", .type=lhs_t); update_assignment = CORD_all(lhs, " = ", compile_to_type(env, binop, lhs_t), ";"); } @@ -1027,7 +1027,7 @@ static CORD _compile_statement(env_t *env, ast_t *ast) if (!is_idempotent(when->subject)) { prefix = CORD_all("{\n", compile_declaration(subject_t, "_when_subject"), " = ", compile(env, subject), ";\n"); suffix = "}\n"; - subject = WrapAST(subject, InlineCCode, .type=subject_t, .code="_when_subject"); + subject = LiteralCode("_when_subject", .type=subject_t); } CORD code = CORD_EMPTY; @@ -1195,7 +1195,7 @@ static CORD _compile_statement(env_t *env, ast_t *ast) ast_t *update_var = new(ast_t); *update_var = *test->expr; - update_var->__data.PlusUpdate.lhs = WrapAST(update.lhs, InlineCCode, .code="(*expr)", .type=lhs_t); // UNSAFE + update_var->__data.PlusUpdate.lhs = LiteralCode("(*expr)", .type=lhs_t); // UNSAFE test_code = CORD_all("({", compile_declaration(Type(PointerType, lhs_t), "expr"), " = &(", compile_lvalue(env, update.lhs), "); ", compile_statement(env, update_var), "; *expr; })"); @@ -1845,7 +1845,15 @@ static CORD _compile_statement(env_t *env, ast_t *ast) case Extern: return CORD_EMPTY; case InlineCCode: { auto inline_code = Match(ast, InlineCCode); - return inline_code->code; + CORD code = CORD_EMPTY; + for (ast_list_t *chunk = inline_code->chunks; chunk; chunk = chunk->next) { + if (chunk->ast->tag == TextLiteral) { + code = CORD_all(code, Match(chunk->ast, TextLiteral)->cord); + } else { + code = CORD_all(code, compile(env, chunk->ast)); + } + } + return code; } case Use: { auto use = Match(ast, Use); @@ -2060,8 +2068,8 @@ CORD compile_typed_array(env_t *env, ast_t *ast, type_t *array_type) env_t *scope = item_type->tag == EnumType ? with_enum_scope(env, item_type) : fresh_scope(env); static int64_t comp_num = 1; const char *comprehension_name = String("arr$", comp_num++); - ast_t *comprehension_var = FakeAST(InlineCCode, .code=CORD_all("&", comprehension_name), - .type=Type(PointerType, .pointed=array_type, .is_stack=true)); + ast_t *comprehension_var = LiteralCode(CORD_all("&", comprehension_name), + .type=Type(PointerType, .pointed=array_type, .is_stack=true)); Closure_t comp_action = {.fn=add_to_array_comprehension, .userdata=comprehension_var}; scope->comprehension_action = &comp_action; CORD code = CORD_all("({ Array_t ", comprehension_name, " = {};"); @@ -2109,8 +2117,8 @@ CORD compile_typed_set(env_t *env, ast_t *ast, type_t *set_type) static int64_t comp_num = 1; env_t *scope = item_type->tag == EnumType ? with_enum_scope(env, item_type) : fresh_scope(env); const char *comprehension_name = String("set$", comp_num++); - ast_t *comprehension_var = FakeAST(InlineCCode, .code=CORD_all("&", comprehension_name), - .type=Type(PointerType, .pointed=set_type, .is_stack=true)); + ast_t *comprehension_var = LiteralCode(CORD_all("&", comprehension_name), + .type=Type(PointerType, .pointed=set_type, .is_stack=true)); CORD code = CORD_all("({ Table_t ", comprehension_name, " = {};"); Closure_t comp_action = {.fn=add_to_set_comprehension, .userdata=comprehension_var}; scope->comprehension_action = &comp_action; @@ -2177,7 +2185,7 @@ CORD compile_typed_table(env_t *env, ast_t *ast, type_t *table_type) static int64_t comp_num = 1; env_t *scope = fresh_scope(env); const char *comprehension_name = String("table$", comp_num++); - ast_t *comprehension_var = FakeAST(InlineCCode, .code=CORD_all("&", comprehension_name), + ast_t *comprehension_var = LiteralCode(CORD_all("&", comprehension_name), .type=Type(PointerType, .pointed=table_type, .is_stack=true)); CORD code = CORD_all("({ Table_t ", comprehension_name, " = {"); @@ -3129,10 +3137,9 @@ CORD compile(env_t *env, ast_t *ast) EXPECT_POINTER("an", "array"); type_t *item_ptr = Type(PointerType, .pointed=item_t, .is_stack=true); type_t *fn_t = NewFunctionType(Type(IntType, .bits=TYPE_IBITS32), {.name="x", .type=item_ptr}, {.name="y", .type=item_ptr}); - ast_t *default_cmp = FakeAST(InlineCCode, - .code=CORD_all("((Closure_t){.fn=generic_compare, .userdata=(void*)", - compile_type_info(item_t), "})"), - .type=Type(ClosureType, .fn=fn_t)); + ast_t *default_cmp = LiteralCode(CORD_all("((Closure_t){.fn=generic_compare, .userdata=(void*)", + compile_type_info(item_t), "})"), + .type=Type(ClosureType, .fn=fn_t)); 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)); CORD arg_code = compile_arguments(env, ast, arg_spec, call->args); @@ -3141,10 +3148,9 @@ CORD compile(env_t *env, ast_t *ast) EXPECT_POINTER("an", "array"); type_t *item_ptr = Type(PointerType, .pointed=item_t, .is_stack=true); type_t *fn_t = NewFunctionType(Type(IntType, .bits=TYPE_IBITS32), {.name="x", .type=item_ptr}, {.name="y", .type=item_ptr}); - ast_t *default_cmp = FakeAST(InlineCCode, - .code=CORD_all("((Closure_t){.fn=generic_compare, .userdata=(void*)", - compile_type_info(item_t), "})"), - .type=Type(ClosureType, .fn=fn_t)); + ast_t *default_cmp = LiteralCode(CORD_all("((Closure_t){.fn=generic_compare, .userdata=(void*)", + compile_type_info(item_t), "})"), + .type=Type(ClosureType, .fn=fn_t)); 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); return CORD_all("Array$heap_pop_value(", self, ", ", arg_code, ", ", compile_type(item_t), ", _, ", @@ -3153,10 +3159,10 @@ CORD compile(env_t *env, ast_t *ast) self = compile_to_pointer_depth(env, call->self, 0, call->args != NULL); type_t *item_ptr = Type(PointerType, .pointed=item_t, .is_stack=true); type_t *fn_t = NewFunctionType(Type(IntType, .bits=TYPE_IBITS32), {.name="x", .type=item_ptr}, {.name="y", .type=item_ptr}); - ast_t *default_cmp = FakeAST(InlineCCode, - .code=CORD_all("((Closure_t){.fn=generic_compare, .userdata=(void*)", - compile_type_info(item_t), "})"), - .type=Type(ClosureType, .fn=fn_t)); + ast_t *default_cmp = LiteralCode( + CORD_all("((Closure_t){.fn=generic_compare, .userdata=(void*)", + compile_type_info(item_t), "})"), + .type=Type(ClosureType, .fn=fn_t)); 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)); CORD arg_code = compile_arguments(env, ast, arg_spec, call->args); @@ -3522,7 +3528,7 @@ CORD compile(env_t *env, ast_t *ast) static int64_t next_id = 1; ast_t *item = FakeAST(Var, String("$it", next_id++)); - ast_t *body = FakeAST(InlineCCode, .code="{}"); // placeholder + ast_t *body = LiteralCode("{}"); // placeholder ast_t *loop = FakeAST(For, .vars=new(ast_list_t, .ast=item), .iter=reduction->iter, .body=body); env_t *body_scope = for_scope(env, loop); if (op == Equals || op == NotEquals || op == LessThan || op == LessThanOrEquals || op == GreaterThan || op == GreaterThanOrEquals) { @@ -3542,8 +3548,8 @@ CORD compile(env_t *env, ast_t *ast) ); ast_t *comparison = new(ast_t, .file=ast->file, .start=ast->start, .end=ast->end, - .tag=op, .__data.Plus.lhs=FakeAST(InlineCCode, .code="prev", .type=item_value_type), .__data.Plus.rhs=item_value); - body->__data.InlineCCode.code = CORD_all( + .tag=op, .__data.Plus.lhs=LiteralCode("prev", .type=item_value_type), .__data.Plus.rhs=item_value); + body->__data.InlineCCode.chunks = new(ast_list_t, .ast=FakeAST(TextLiteral, CORD_all( "if (result == NONE_BOOL) {\n" " prev = ", compile(body_scope, item_value), ";\n" " result = yes;\n" @@ -3554,7 +3560,7 @@ CORD compile(env_t *env, ast_t *ast) " result = no;\n", " break;\n", " }\n", - "}\n"); + "}\n"))); code = CORD_all(code, compile_statement(env, loop), "\nresult;})"); return code; } else if (op == Min || op == Max) { @@ -3576,25 +3582,25 @@ CORD compile(env_t *env, ast_t *ast) code = CORD_all(code, compile_declaration(key_type, superlative_key), ";\n"); ast_t *comparison = new(ast_t, .file=ast->file, .start=ast->start, .end=ast->end, - .tag=cmp_op, .__data.Plus.lhs=FakeAST(InlineCCode, .code="key", .type=key_type), - .__data.Plus.rhs=FakeAST(InlineCCode, .code=superlative_key, .type=key_type)); + .tag=cmp_op, .__data.Plus.lhs=LiteralCode("key", .type=key_type), + .__data.Plus.rhs=LiteralCode(superlative_key, .type=key_type)); - body->__data.InlineCCode.code = CORD_all( + body->__data.InlineCCode.chunks = new(ast_list_t, .ast=FakeAST(TextLiteral, CORD_all( compile_declaration(key_type, "key"), " = ", compile(key_scope, reduction->key), ";\n", "if (!has_value || ", compile(body_scope, comparison), ") {\n" " ", superlative, " = ", compile(body_scope, item), ";\n" " ", superlative_key, " = key;\n" " has_value = yes;\n" - "}\n"); + "}\n"))); } else { ast_t *comparison = new(ast_t, .file=ast->file, .start=ast->start, .end=ast->end, .tag=cmp_op, .__data.Plus.lhs=item, - .__data.Plus.rhs=FakeAST(InlineCCode, .code=superlative, .type=item_t)); - body->__data.InlineCCode.code = CORD_all( + .__data.Plus.rhs=LiteralCode(superlative, .type=item_t)); + body->__data.InlineCCode.chunks = new(ast_list_t, .ast=FakeAST(TextLiteral, CORD_all( "if (!has_value || ", compile(body_scope, comparison), ") {\n" " ", superlative, " = ", compile(body_scope, item), ";\n" " has_value = yes;\n" - "}\n"); + "}\n"))); } @@ -3634,16 +3640,16 @@ CORD compile(env_t *env, ast_t *ast) } ast_t *combination = new(ast_t, .file=ast->file, .start=ast->start, .end=ast->end, - .tag=op, .__data.Plus.lhs=FakeAST(InlineCCode, .code="reduction", .type=reduction_type), + .tag=op, .__data.Plus.lhs=LiteralCode("reduction", .type=reduction_type), .__data.Plus.rhs=item_value); - body->__data.InlineCCode.code = CORD_all( + body->__data.InlineCCode.chunks = new(ast_list_t, .ast=FakeAST(TextLiteral, CORD_all( "if (!has_value) {\n" " reduction = ", compile(body_scope, item_value), ";\n" " has_value = yes;\n" "} else {\n" " reduction = ", compile(body_scope, combination), ";\n", early_out, - "}\n"); + "}\n"))); code = CORD_all(code, compile_statement(env, loop), "\nhas_value ? ", promote_to_optional(reduction_type, "reduction"), " : ", compile_none(reduction_type), ";})"); @@ -3815,9 +3821,9 @@ CORD compile(env_t *env, ast_t *ast) case InlineCCode: { type_t *t = get_type(env, ast); if (t->tag == VoidType) - return CORD_all("{\n", Match(ast, InlineCCode)->code, "\n}"); + return CORD_all("{\n", compile_statement(env, ast), "\n}"); else - return Match(ast, InlineCCode)->code; + return compile_statement(env, ast); } case Use: code_err(ast, "Compiling 'use' as expression!"); case Defer: code_err(ast, "Compiling 'defer' as expression!"); diff --git a/src/parse.c b/src/parse.c index 3b8f55b..c31bea6 100644 --- a/src/parse.c +++ b/src/parse.c @@ -61,8 +61,8 @@ int op_tightness[] = { }; static const char *keywords[] = { - "_max_", "_min_", "and", "break", "continue", "defer", "deserialize", "do", "else", "enum", - "extend", "extern", "for", "func", "if", "in", "inline", "lang", "mod", "mod1", "no", "none", + "C_code", "_max_", "_min_", "and", "break", "continue", "defer", "deserialize", "do", "else", "enum", + "extend", "extern", "for", "func", "if", "in", "lang", "mod", "mod1", "no", "none", "not", "or", "pass", "return", "skip", "skip", "stop", "struct", "then", "unless", "use", "when", "while", "xor", "yes", }; @@ -147,6 +147,7 @@ static PARSER(parse_var); static PARSER(parse_when); static PARSER(parse_while); static PARSER(parse_deserialize); +static ast_list_t *_parse_text_helper(parse_ctx_t *ctx, const char **out_pos, char open_quote, char close_quote, char open_interp); // // Print a parse error and exit (or use the on_err longjmp) @@ -1186,55 +1187,11 @@ PARSER(parse_bool) { return NULL; } -PARSER(parse_text) { - // ('"' ... '"' / "'" ... "'" / "`" ... "`") - // "$" [name] [interp-char] quote-char ... close-quote - const char *start = pos; - const char *lang = NULL; - - // Escape sequence, e.g. \r\n - if (*pos == '\\') { - CORD cord = CORD_EMPTY; - do { - const char *c = unescape(ctx, &pos); - cord = CORD_cat(cord, c); - // cord = CORD_cat_char(cord, c); - } while (*pos == '\\'); - return NewAST(ctx->file, start, pos, TextLiteral, .cord=cord); - } - - char open_quote, close_quote, open_interp = '$'; - if (match(&pos, "\"")) { // Double quote - open_quote = '"', close_quote = '"', open_interp = '$'; - } else if (match(&pos, "`")) { // Backtick - open_quote = '`', close_quote = '`', open_interp = '$'; - } else if (match(&pos, "'")) { // Single quote - open_quote = '\'', close_quote = '\'', open_interp = '\x03'; - } else if (match(&pos, "$")) { // Customized strings - lang = get_id(&pos); - // $"..." or $@"...." - static const char *interp_chars = "~!@#$%^&*+=\\?"; - if (match(&pos, "$")) { // Disable interpolation with $ - open_interp = '\x03'; - } else if (strchr(interp_chars, *pos)) { - open_interp = *pos; - ++pos; - } else if (*pos == '(') { - open_interp = '@'; // For shell commands - } - static const char *quote_chars = "\"'`|/;([{<"; - if (!strchr(quote_chars, *pos)) - parser_err(ctx, pos, pos+1, "This is not a valid string quotation character. Valid characters are: \"'`|/;([{<"); - open_quote = *pos; - ++pos; - close_quote = closing[(int)open_quote] ? closing[(int)open_quote] : open_quote; - } else { - return NULL; - } - +ast_list_t *_parse_text_helper(parse_ctx_t *ctx, const char **out_pos, char open_quote, char close_quote, char open_interp) +{ + const char *pos = *out_pos; int64_t starting_indent = get_indent(ctx, pos); int64_t string_indent = starting_indent + SPACES_PER_INDENT; - ast_list_t *chunks = NULL; CORD chunk = CORD_EMPTY; const char *chunk_start = pos; @@ -1299,6 +1256,57 @@ PARSER(parse_text) { REVERSE_LIST(chunks); char close_str[2] = {close_quote, 0}; expect_closing(ctx, &pos, close_str, "I was expecting a ", close_quote, " to finish this string"); + *out_pos = pos; + return chunks; +} + +PARSER(parse_text) { + // ('"' ... '"' / "'" ... "'" / "`" ... "`") + // "$" [name] [interp-char] quote-char ... close-quote + const char *start = pos; + const char *lang = NULL; + + // Escape sequence, e.g. \r\n + if (*pos == '\\') { + CORD cord = CORD_EMPTY; + do { + const char *c = unescape(ctx, &pos); + cord = CORD_cat(cord, c); + // cord = CORD_cat_char(cord, c); + } while (*pos == '\\'); + return NewAST(ctx->file, start, pos, TextLiteral, .cord=cord); + } + + char open_quote, close_quote, open_interp = '$'; + if (match(&pos, "\"")) { // Double quote + open_quote = '"', close_quote = '"', open_interp = '$'; + } else if (match(&pos, "`")) { // Backtick + open_quote = '`', close_quote = '`', open_interp = '$'; + } else if (match(&pos, "'")) { // Single quote + open_quote = '\'', close_quote = '\'', open_interp = '\x03'; + } else if (match(&pos, "$")) { // Customized strings + lang = get_id(&pos); + // $"..." or $@"...." + static const char *interp_chars = "~!@#$%^&*+=\\?"; + if (match(&pos, "$")) { // Disable interpolation with $ + open_interp = '\x03'; + } else if (strchr(interp_chars, *pos)) { + open_interp = *pos; + ++pos; + } else if (*pos == '(') { + open_interp = '@'; // For shell commands + } + static const char *quote_chars = "\"'`|/;([{<"; + if (!strchr(quote_chars, *pos)) + parser_err(ctx, pos, pos+1, "This is not a valid string quotation character. Valid characters are: \"'`|/;([{<"); + open_quote = *pos; + ++pos; + close_quote = closing[(int)open_quote] ? closing[(int)open_quote] : open_quote; + } else { + return NULL; + } + + ast_list_t *chunks = _parse_text_helper(ctx, &pos, open_quote, close_quote, open_interp); return NewAST(ctx->file, start, pos, TextJoin, .lang=lang, .children=chunks); } @@ -2258,34 +2266,30 @@ PARSER(parse_extern) { PARSER(parse_inline_c) { const char *start = pos; - if (!match_word(&pos, "inline")) return NULL; - - spaces(&pos); - if (!match_word(&pos, "C")) return NULL; + if (!match_word(&pos, "C_code")) return NULL; spaces(&pos); type_ast_t *type = NULL; - if (match(&pos, ":")) - type = expect(ctx, start, &pos, parse_type, "I couldn't parse the type for this inline C code"); - - spaces(&pos); - if (!match(&pos, "{")) - parser_err(ctx, start, pos, "I expected a '{' here"); - - int depth = 1; - const char *c_code_start = pos; - for (; *pos && depth > 0; ++pos) { - if (*pos == '}') --depth; - else if (*pos == '{') ++depth; + ast_list_t *chunks; + if (match(&pos, ":")) { + type = expect(ctx, start, &pos, parse_type, "I couldn't parse the type for this C_code code"); + spaces(&pos); + if (!match(&pos, "(")) + parser_err(ctx, start, pos, "I expected a '(' here"); + chunks = new(ast_list_t, .ast=NewAST(ctx->file, pos, pos, TextLiteral, "({"), + .next=_parse_text_helper(ctx, &pos, '(', ')', '@')); + if (type) { + REVERSE_LIST(chunks); + chunks = new(ast_list_t, .ast=NewAST(ctx->file, pos, pos, TextLiteral, "; })"), .next=chunks); + REVERSE_LIST(chunks); + } + } else { + if (!match(&pos, "{")) + parser_err(ctx, start, pos, "I expected a '{' here"); + chunks = _parse_text_helper(ctx, &pos, '{', '}', '@'); } - if (depth != 0) - parser_err(ctx, start, start+1, "I couldn't find the closing '}' for this inline C code"); - - CORD c_code = GC_strndup(c_code_start, (size_t)((pos-1) - c_code_start)); - if (type) - c_code = CORD_all("({ ", c_code, "; })"); - return NewAST(ctx->file, start, pos, InlineCCode, .code=c_code, .type_ast=type); + return NewAST(ctx->file, start, pos, InlineCCode, .chunks=chunks, .type_ast=type); } PARSER(parse_doctest) { diff --git a/test/inline_c.tm b/test/inline_c.tm index a79359d..77b5624 100644 --- a/test/inline_c.tm +++ b/test/inline_c.tm @@ -1,8 +1,8 @@ func main() - >> inline C:Int32 { int x = 1 + 2; x } + >> C_code:Int32(int x = 1 + 2; x) = Int32(3) - >> inline C { + >> C_code { say(Text("Inline C code works!"), true); }