aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBruce Hill <bruce@bruce-hill.com>2025-04-06 21:43:19 -0400
committerBruce Hill <bruce@bruce-hill.com>2025-04-06 21:43:19 -0400
commitd8afa73368cdff38125fa1f7d17ad5ce54c84def (patch)
treeac4e54673da6ac32df1e351b913b7c1ddd1118b9
parentf4020db2f0d772481ba71edf78fbb65575badc00 (diff)
Improved inline C code: now uses `C_code` keyword and supports
interpolation with @
-rw-r--r--docs/c-interoperability.md8
-rw-r--r--examples/commands/commands.tm16
-rw-r--r--examples/game/player.tm8
-rw-r--r--examples/http-server/http-server.tm24
-rw-r--r--examples/http/http.tm44
-rw-r--r--examples/log/log.tm4
-rw-r--r--examples/patterns/patterns.tm26
-rw-r--r--examples/pthreads/pthreads.tm40
-rw-r--r--examples/random/random.tm191
-rw-r--r--examples/time/time.tm104
-rw-r--r--src/ast.c2
-rw-r--r--src/ast.h3
-rw-r--r--src/compile.c82
-rw-r--r--src/parse.c148
-rw-r--r--test/inline_c.tm4
15 files changed, 352 insertions, 352 deletions
diff --git a/docs/c-interoperability.md b/docs/c-interoperability.md
index 3c5bbff1..3c0906ee 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 81c43692..ace7deb5 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 9f166d9e..2e5e54f6 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 92651ca1..3d72c1e8 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 29727e6e..3fe41ae2 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 94984b81..9c3396e7 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 3e671b80..bab0c3dc 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 61f94d0a..7f720d5a 100644
--- a/examples/pthreads/pthreads.tm
+++ b/examples/pthreads/pthreads.tm
@@ -3,66 +3,66 @@ use <pthread.h>
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 94528dd7..13600b47 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.<random-bits>
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 4a5bfcf7..1796654d 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 d4fd9569..b3506058 100644
--- a/src/ast.c
+++ b/src/ast.c
@@ -190,7 +190,7 @@ CORD ast_to_xml(ast_t *ast)
T(NonOptional, "<NonOptional>%r</NonOptional>", ast_to_xml(data.value))
T(DocTest, "<DocTest>%r%r</DocTest>", optional_tagged("expression", data.expr), optional_tagged("expected", data.expected))
T(Use, "<Use>%r%r</Use>", optional_tagged("var", data.var), xml_escape(data.path))
- T(InlineCCode, "<InlineCode>%r</InlineCode>", xml_escape(data.code))
+ T(InlineCCode, "<InlineCode>%r</InlineCode>", ast_list_to_xml(data.chunks))
T(Deserialize, "<Deserialize><type>%r</type>%r</Deserialize>", type_ast_to_xml(data.type), ast_to_xml(data.value))
T(Extend, "<Extend name=\"%s\">%r</Extend>", data.name, ast_to_xml(data.body))
T(ExplicitlyTyped, "<ExplicitlyTyped type=\"%r\">%r</ExplicitlyTyped>", type_to_cord(data.type), ast_to_xml(data.ast))
diff --git a/src/ast.h b/src/ast.h
index b48582f2..aeb418d9 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 b49f4c49..cdfaf5c6 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 3b8f55bf..c31bea64 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 a79359d1..77b56249 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);
}