1
0
Fork 0
forked from fun/fun

All incomplete examples have been updated and some corrections have been made to the parser to make them work. (0.37.17)

This commit is contained in:
Johannes Findeisen 2025-12-23 20:49:07 +01:00
commit 92aacff71a
41 changed files with 654 additions and 130 deletions

View file

@ -1,5 +1,5 @@
cmake_minimum_required(VERSION 3.10)
project(fun VERSION 0.37.15 LANGUAGES C)
project(fun VERSION 0.37.17 LANGUAGES C)
set(CMAKE_C_STANDARD 99)
set(CMAKE_C_STANDARD_REQUIRED ON)

View file

@ -15,3 +15,9 @@ boolean b = false
print("b => " + to_string(b))
/* Try typeof via built-in function if available */
print("typeof(b) => " + typeof(b))
/* Expected output:
b => false
typeof(b) => Boolean
*/

View file

@ -52,3 +52,25 @@ if f
print("if f: branch taken (unexpected)")
else
print("if f: else branch taken")
/* Expected output:
typeof(t) => Boolean
typeof(f) => Boolean
t literal => true
f literal => false
true && true => true
true && false => false
false || true => 1
false || false => 0
!true => false
!false => true
true == true => true
true != false => true
true == 1 => true
false == 0 => true
1 == true => true
0 != true => true
if t: branch taken
if f: else branch taken
*/

View file

@ -52,3 +52,26 @@ print(typeof(cast(42, "Function"))) // -> "Nil"
print(cast(cast("42", "Number"), "String")) // -> "42"
print("=== Done ===")
/* Expected output:
=== CAST demo ===
123
0
42
100
String
0
1
Array
1
42
String
0
Nil
Function
Nil
42
=== Done ===
*/

View file

@ -22,3 +22,8 @@ if ok == 1
print("Downloaded to " + path)
else
print("Download failed")
/* Expected output:
Downloaded to ./downloaded.png
*/

View file

@ -23,3 +23,9 @@ print("Raw length: " + to_string(len(resp)))
obj = json_parse(resp)
if obj != nil
print("Title: " + obj["slideshow"]["title"])
/* Expected output:
Raw length: 429
Title: Sample Slide Show
*/

View file

@ -30,3 +30,30 @@ if obj != nil
if obj != nil
print("Origin: " + to_string(obj["origin"]))
/* Possible output:
Response: {
"args": {},
"data": "",
"files": {},
"form": {
"lang": "fun",
"name": "Fun"
},
"headers": {
"Accept": "*/*",
"Content-Length": "17",
"Content-Type": "application/x-www-form-urlencoded",
"Host": "httpbin.org",
"X-Amzn-Trace-Id": "Root=1-6944834b-74f499e251c322713e7dd9a8"
},
"json": null,
"origin": "5.252.226.107",
"url": "https://httpbin.org/post"
}
Content-Type: application/x-www-form-urlencoded
Host: httpbin.org
Origin: 5.252.226.107
*/

View file

@ -19,5 +19,7 @@ echo("Hello, ")
echo("world")
print("!")
// Expected output:
// Hello, world!
/* Expected output:
Hello, world!
*/

View file

@ -59,3 +59,20 @@ print(a < b && n >= 10) // expect 1
print((a + b) == 5 && flag) // expect 1
print("=== Expressions test end ===")
/* Expected output:
=== Expressions test start ===
16
10
-10
1
true
false
true
1
true
true
true
=== Expressions test end ===
*/

View file

@ -78,7 +78,7 @@ for x in range(1, 3)
print("=== for/range test end ===")
/*
/* Expected output:
=== for/range test start ===
0
1
@ -112,3 +112,4 @@ print("=== for/range test end ===")
4
=== for/range test end ===
*/

View file

@ -57,3 +57,17 @@ number n = 5
print(add(n, 10)) // expect 15
print("=== Functions test end ===")
/* Expected output:
=== Functions test start ===
5
7
7
4
6
6
42
15
=== Functions test end ===
*/

View file

@ -14,3 +14,8 @@
*/
print("Have fun!")
/* Expected output:
Have fun!
*/

View file

@ -12,7 +12,7 @@
// Have fun in Fun
string s = "Have fun!"
fun have_fun()
print("Have fun!")
print(s)
@ -20,3 +20,13 @@ fun have_fun()
print(n)
have_fun()
print("Have fun!")
/* Expected output:
Have fun!
Have fun!
10
Have fun!
*/

View file

@ -41,3 +41,12 @@ if (n >= 10)
print("answer") // expect: answer
print("=== if/else-if/else done ===")
/* Expected output:
=== if/else-if/else test ===
big
42
answer
=== if/else-if/else done ===
*/

View file

@ -11,7 +11,7 @@
// Demonstrates system library includes after installation to /usr/lib/fun
// includes can also be done with a leading # like in C, but some programmers maybe like more clean code without a
// includes can also be done with a leading # like in C, but some programmers maybe like more clean code without a
// leading #.
include <hello.fun>
include <utils/math.fun>
@ -24,3 +24,10 @@ number y = 32
print("add(" + to_string(x) + ", " + to_string(y) + ") = " + to_string(add(x, y)))
print("times(" + to_string(x) + ", " + to_string(y) + ") = " + to_string(times(x, y)))
/* Expected output:
== include lib demo ==
Hello from system lib!
add(10, 32) = 42
times(10, 32) = 320
*/

View file

@ -27,3 +27,9 @@ print("sum(" + to_string(a) + ", " + to_string(b) + ") = " + to_string(sum(a, b)
// To include from the system library directory (/usr/lib/fun), use angle brackets:
// #include <some_lib.fun>
/* Expected output:
== include local demo ==
Hello, Fun
sum(2, 3) = 5
*/

View file

@ -14,3 +14,8 @@ fun greet(name)
fun sum(a, b)
return a + b
/* Expected output:
None, it's a lib...
*/

View file

@ -44,3 +44,15 @@ ok = ini.save(nil)
print("saved=" + to_string(ok))
ini.close()
/* Expected output:
[app]
name=FunApp
version=1.2.3
debug=1
[database]
host=localhost
port=5432
saved=1
*/

View file

@ -73,3 +73,28 @@ else
// Clean up
ini_free(h)
/* Expected output:
[app]
name=FunApp
version=1.2.3
debug=1
[database]
host=localhost
port=5432
user=fun
pass=secret
pool_size=8
timeout=2.5
[network]
ssl=1
retries=3
base_url=https://api.example.com
[features]
feature_x=0
feature_y=0
[paths]
data_dir=./data
log_file=./logs/app.log
*/

View file

@ -28,3 +28,9 @@ else
if ok
ini_save(h, path)
ini_free(h)
/* Expected output:
user=<EFBFBD><EFBFBD><EFBFBD><EFBFBD>U
retries=3
ssl=1
*/

View file

@ -68,3 +68,26 @@ else
print(" max_files=" + to_string(logs_max_files))
ini_free(h)
/* Expected output:
[server]
host=example.org
port=8080
[server.tls]
enabled=1
version=1.3
ciphers=TLS_AES_256_GCM_SHA384,TLS_CHACHA20_POLY1305_SHA256
[users.admin]
name=alice
active=1
quota_gb=100
[users.guest]
name=bob
active=0
quota_gb=5
[paths.logs]
dir=./var/log/fun
rotate=1
max_files=7
*/

View file

@ -53,7 +53,7 @@ print(len(cfg["users"])) // number of users
// Derive a small summary map
summary = {}
summary["user_count"] = len(cfg["users"])
summary["user_count"] = len(cfg["users"])
summary["first_user_name"] = cfg["users"][1]["name"]
summary["features_enabled"] = cfg["features"]["enabled"]
@ -63,3 +63,45 @@ print(json.stringify(summary, 1))
out_path = "/tmp/fun_complex_out.json"
ok = json.to_file(out_path, cfg, 1)
print(ok) // 1 on success
/* Expected output:
-- JSON: parse from string and pretty print --
Dump:
{"name": Ada, "active": true, "score": 99.5, "count": 42, "tags": [C, Ada, Math], "extra": nil}
Ada
true
42
3
{
"name":"Ada",
"active":true,
"score":99.5,
"count":42,
"tags":[
"C",
"Ada",
"Math"
],
"extra":null
}
-- JSON: load from file, inspect, and save pretty to /tmp --
Dump:
nil
Dump:
{"project": {"name": Fun, "version": 0.27.2, "website": https://fun-lang.xyz, "license": {"name": Apache-2.0, "url": https://opensource.org/license/apache-2-0}}, "features": {"enabled": [arrays, maps, json, pcsc], "experimental": {"repl": true, "sockets": true, "odbc": false, "notes": nil}}, "users": [{"id": 1, "name": Ada, "roles": [admin, math], "active": true, "score": 99.5, "prefs": {"theme": dark, "editor": {"tabWidth": 2, "font": Fira Code}}}, {"id": 2, "name": Linus, "roles": [user, kernel], "active": false, "score": 88, "prefs": {"theme": light, "editor": {"tabWidth": 8, "font": Monospace}}}], "metrics": {"counters": [0, 1, 1, 2, 3, 5, 8], "latency_ms": {"p50": 1.23, "p90": 3.21, "p99": 12.34}, "builds": 1234567890123456789, "last_release_ts": 1732406400000}, "matrix": [[1, 2, 3], [4, 5, 6], [7, 8, 9]], "notes": UTF-8 emojis: 🚀🔥, "null_field": nil}
Fun
0.27.2
2
{
"user_count":2,
"first_user_name":"Linus",
"features_enabled":[
"arrays",
"maps",
"json",
"pcsc"
]
}
1
*/

View file

@ -1,6 +1,6 @@
#!/usr/bin/env fun
// The shebang line makes no sense here because this is a library which will
// never be executed, but it shows that it is not wrong.
// The shebang line makes no sense here because this is a library which will
// never be executed, but it shows that it is not wrong.
/*
* This file is part of the Fun programming language.
@ -23,3 +23,8 @@ class Greeter(string prefix)
// Methods must declare 'this' as the first parameter
fun say(this, name)
print(this.prefix + " " + to_string(name))
/* Expected output:
None, it's a lib.
*/

View file

@ -25,3 +25,11 @@ fun greet()
else
print("Hello, " + user + "!")
greet()
/* Possible output:
HOME=/home/hanez
SHELL=/bin/zsh
FUN_NOT_SET=
Hello, hanez!
*/

View file

@ -28,22 +28,22 @@ print(pcre2_test(pattern, text, flags))
m = pcre2_match(pattern, text, flags)
if (m != nil)
print("first full:")
print(m["full"])
print(m["full"])
print("span:")
print(m["start"])
print(m["start"])
print("..")
print(m["end"])
print(m["end"])
print("groups count:")
print(len(m["groups"]))
all = pcre2_findall("\\w+", text, flags)
for x in all
print("all:")
print(x["full"])
print(x["full"])
print("@")
print(x["start"])
print(x["start"])
print("..")
print(x["end"])
print(x["end"])
print("")
print("-- More regex demos --")
@ -59,28 +59,28 @@ email_flags = OR(flags, 1) // UTF | I
print("Emails (findall):")
emails = pcre2_findall(email_pat, email_text, email_flags)
for e in emails
print(e["full"])
print(e["full"])
// Demo 2: URLs (very simple, for demo purposes)
url_text = "See http://example.com and https://fun-lang.xyz/docs?x=1#top"
url_pat = "https?://[A-Za-z0-9._~:/?#[@]!$&'()*+,;=%-]+"
print("URLs:")
for u in pcre2_findall(url_pat, url_text, flags)
print(u["full"])
print(u["full"])
// Demo 3: IPv4 addresses
ip_text = "ping 8.8.8.8 and 192.168.0.1; not 999.999.999.999"
ip_pat = "(?:(?:25[0-5]|2[0-4]\\d|1?\\d?\\d)\\.){3}(?:25[0-5]|2[0-4]\\d|1?\\d?\\d)"
print("IPv4:")
for ip in pcre2_findall(ip_pat, ip_text, flags)
print(ip["full"])
print(ip["full"])
// Demo 4: Dates (YYYY-MM-DD)
date_text = "Born 1999-12-31, updated 2025-11-25, bad 2025-13-40"
date_pat = "(\\d{4})-(0[1-9]|1[0-2])-(0[1-9]|[12]\\d|3[01])"
print("Dates (with groups Y/M/D):")
for d in pcre2_findall(date_pat, date_text, flags)
print(d["full"])
print(d["full"])
print(join(d["groups"], "/"))
// Demo 5: Hex colors (#RRGGBB)
@ -88,14 +88,14 @@ color_text = "Palette: #FF00FF, #1a2b3c, not #abcd or #12345g"
color_pat = "#[0-9A-Fa-f]{6}"
print("Hex colors:")
for c in pcre2_findall(color_pat, color_text, flags)
print(c["full"])
print(c["full"])
// Demo 6: Quoted strings with escapes
q_text = 'say "hi there" and "indented" plus "quote\\"inside"'
q_pat = '"([^"\\\\]|\\\\.)*"'
print("Quoted strings (with escapes):")
for q in pcre2_findall(q_pat, q_text, flags)
print(q["full"])
print(q["full"])
// Demo 7: Multiline anchors with /m (M flag)
ml_text = "first line\nSecond line\nthird"
@ -103,7 +103,7 @@ ml_pat = "^(\\w+)"
ml_flags = OR(flags, 2) // UTF | M
print("Multiline ^ anchors (first token of each line):")
for ml in pcre2_findall(ml_pat, ml_text, ml_flags)
print(ml["full"])
print(ml["full"])
// Demo 8: Dotall vs non-dotall
ds_text = "BEGIN\nline1\nline2\nEND"
@ -119,14 +119,14 @@ wb_text = "The theater and the THE can differ."
wb_pat = "\\bthe\\b"
print("Word boundary, case-insensitive:")
for w in pcre2_findall(wb_pat, wb_text, OR(flags, 1))
print(w["full"])
print(w["full"])
// Demo 10: Lookahead word followed by number
la_text = "foo 123, bar, baz 9"
la_pat = "\\w+(?=\\s+\\d+)"
print("Lookahead (word before number):")
for a in pcre2_findall(la_pat, la_text, flags)
print(a["full"])
print(a["full"])
// Demo 11: Non-greedy vs greedy
ng_text = "<a>one</a><a>two</a>"
@ -134,7 +134,81 @@ greedy = "<a>.*</a>"
lazy = "<a>.*?</a>"
print("Greedy:")
for g in pcre2_findall(greedy, ng_text, OR(flags, 4)) // DOTALL ensures '.' covers any
print(g["full"])
print(g["full"])
print("Non-greedy:")
for l in pcre2_findall(lazy, ng_text, OR(flags, 4))
print(l["full"])
print(l["full"])
/* Expected output:
-- PCRE2 builtins (no class) --
test:
1
first full:
Hello
span:
0
..
5
groups count:
1
all:
Hello
@
0
..
5
all:
123
@
6
..
9
all:
world
@
10
..
15
-- More regex demos --
Emails (findall):
one@example.com
Two@Example.COM
URLs:
IPv4:
8.8.8.8
192.168.0.1
Dates (with groups Y/M/D):
1999-12-31
1999/12/31
2025-11-25
2025/11/25
Hex colors:
#FF00FF
#1a2b3c
Quoted strings (with escapes):
"hi there"
"indented"
"quote\"inside"
Multiline ^ anchors (first token of each line):
first
Second
third
Dotall OFF (should fail):
0
Dotall ON (should match):
1
Word boundary, case-insensitive:
The
the
THE
Lookahead (word before number):
foo
baz
Greedy:
<a>one</a><a>two</a>
Non-greedy:
<a>one</a>
<a>two</a>
*/

View file

@ -46,3 +46,5 @@ SHA-1('abc') = e6cd2bcee460c45d41565ac877d866a159a16e19
SHA-1(616263 hex) = e6cd2bcee460c45d41565ac877d866a159a16e19
SHA-1('') = da39a3ee5e6b4b0d3255bfef95601890afd80709
=== done ===
*/

View file

@ -27,3 +27,9 @@ print(s.sha384_hex("616263"))
// Raw string input
print(s.sha384_str("abc"))
/* Expected output:
cb00753f45a35e8bb5a03d699ac65007272c32ab0eded1631a8b605a43ff5bed8086072ba1e7cc2358baeca134c825a7
cb00753f45a35e8bb5a03d699ac65007272c32ab0eded1631a8b605a43ff5bed8086072ba1e7cc2358baeca134c825a7
*/

View file

@ -8,7 +8,7 @@
* Licensed under the terms of the Apache-2.0 license.
* https://opensource.org/license/apache-2-0
*/
// Short-circuit demo for || and &&
print("=== short-circuit demo ===")
@ -43,3 +43,15 @@ else
print(0)
print("=== end ===")
/* Expected output:
=== short-circuit demo ===
1
0
OR-NEEDED
1
AND-NEEDED
1
=== end ===
*/

View file

@ -8,7 +8,7 @@
* Licensed under the terms of the Apache-2.0 license.
* https://opensource.org/license/apache-2-0
*/
// Strings test: concatenation with variables and literals, functions returning strings
print("=== strings test start ===")
@ -42,3 +42,16 @@ if (a != "")
print("a is non-empty")
print("=== strings test end ===")
/* Expected output:
=== strings test start ===
Hello, World!
Hi there
x
x
Hello, Fun
Hi, Hello, You
a is non-empty
=== strings test end ===
*/

View file

@ -32,3 +32,21 @@ else
data = sock_recv(fd, 8192)
print(data)
sock_close(fd)
/* Possible output:
HTTP/1.1 200 OK
Date: Thu, 18 Dec 2025 23:05:52 GMT
Content-Type: text/html
Connection: close
Vary: accept-encoding
Server: cloudflare
Last-Modified: Wed, 17 Dec 2025 03:51:06 GMT
Accept-Ranges: bytes
Cache-Control: max-age=14400
cf-cache-status: REVALIDATED
CF-RAY: 9b024eb71d91e51d-TXL
<!doctype html><html lang="en"><head><title>Example Domain</title><meta name="viewport" content="width=device-width, initial-scale=1"><style>body{background:#eee;width:60vw;margin:15vh auto;font-family:system-ui,sans-serif}h1{font-size:1.5em}div{opacity:0.8}a:link,a:visited{color:#348}</style><body><div><h1>Example Domain</h1><p>This domain is for use in documentation examples without needing permission. Avoid use in operations.<p><a href="https://iana.org/domains/example">Learn more</a></div></body></html>
*/

View file

@ -30,3 +30,22 @@ else
resp = c.recv_all(8192)
print(resp)
c.close()
/* Possible output:
HTTP/1.1 200 OK
Date: Thu, 18 Dec 2025 23:06:52 GMT
Content-Type: text/html
Connection: close
Vary: accept-encoding
Server: cloudflare
Last-Modified: Wed, 17 Dec 2025 03:51:06 GMT
Accept-Ranges: bytes
Age: 59
Cache-Control: max-age=14400
cf-cache-status: HIT
CF-RAY: 9b02502e3f993237-TXL
<!doctype html><html lang="en"><head><title>Example Domain</title><meta name="viewport" content="width=device-width, initial-scale=1"><style>body{background:#eee;width:60vw;margin:15vh auto;font-family:system-ui,sans-serif}h1{font-size:1.5em}div{opacity:0.8}a:link,a:visited{color:#348}</style><body><div><h1>Example Domain</h1><p>This domain is for use in documentation examples without needing permission. Avoid use in operations.<p><a href="https://iana.org/domains/example">Learn more</a></div></body></html>
*/

View file

@ -27,3 +27,8 @@ tk.pack("ok")
// Enter GUI loop (no-op if built without FUN_WITH_TCLTK)
tk.loop()
/* Expected output:
A GUI... ;)
*/

View file

@ -24,3 +24,10 @@ catch err
finally
print("finally always runs (syntactically)")
print("after try")
/* Expected output:
before try
inside try body
after try
*/

View file

@ -70,3 +70,32 @@ print(typeof(nn)) // -> "Nil"
print("")
print("Done. Uncomment lines above to see type errors in action.")
/* Expected output:
== Dynamic variable (untyped) ==
Number
100
String
hello
== Typed variables remain type-stable ==
String
hello
Number
0
0
Sint64
456
== Integer width clamping ==
-126
56
255
44
== Nil-typed variables must stay Nil ==
Nil
Done. Uncomment lines above to see type errors in action.
*/

View file

@ -19,3 +19,12 @@ print("Now trying to assign a number to a string variable...")
s = 42 // Runtime TypeError: expected String (assignment rejected)
print("This line will not execute due to the type error")
/* Expected output:
Reassigning typed variables to another type should fail
String
hello
Now trying to assign a number to a string variable...
TypeError: expected String
*/

View file

@ -70,3 +70,24 @@ print("ui8 value = " + to_string(ui8) + ", typeof(ui8) = " + typeof(ui8)) // Uin
si8 = si8 - 5
print("si8 value = " + to_string(si8) + ", typeof(si8) = " + typeof(si8)) // Sint8
/* Expected output:
typeof(si8) = Sint8
typeof(si16) = Sint16
typeof(si32) = Sint32
typeof(si64) = Sint64
typeof(ui8) = Uint8
typeof(ui16) = Uint16
typeof(ui32) = Uint32
typeof(ui64) = Uint64
typeof(n) = Sint64
typeof(s) = String
typeof(arr) = Array
typeof(m) = Map
typeof(f) = Function
typeof(x) = Nil
typeof(ui8 + 1) = Number
typeof(si16 - 2) = Number
ui8 value = 9, typeof(ui8) = Uint8
si8 value = -6, typeof(si8) = Sint8
*/

View file

@ -73,3 +73,32 @@ print("pt['y'] = " + to_string(pt["y"]) + " :: " + typeof(pt["y"]))
// Conversions
print("to_string(123) => " + to_string(123))
print("to_number(\"456\") => " + to_string(to_number("456")))
/* Expected output:
a = 42 :: Sint64
b = -7 :: Sint64
zero = 0 :: Sint64
sum (a + b) = 35
diff (a - b) = 49
prod (a * 3) = 126
quot (a / 2) = 21
rem (a % 5) = 2
a > b = 1
a >= b = 1
a < b = 0
a <= b = 0
a == b = false
a != b = true
t = true :: Boolean
f = false :: Boolean
t && (a > 0) = true
f || (a < 0) = 0
sum 1..5 = 15
nums length = 5
nums[2] = 3
pt['x'] = 10 :: Number
pt['y'] = -3 :: Number
to_string(123) => 123
to_number("456") => 456
*/

View file

@ -77,3 +77,45 @@ print(typeof(p)) // -> "Class"
print("")
print("=== Done ===")
/* Expected output:
=== Dynamic (untyped) ===
Number
String
=== Numbers and integer subtypes ===
Sint64
42
-126
4464
255
0
=== Strings ===
String
hello
=== Nil ===
Nil
nil
=== Arrays ===
Array
3
2
[1, 2]
=== Maps ===
Map
v
=== Functions ===
Function
12
=== Classes and Class-typed variables ===
Class
=== Done ===
*/

View file

@ -8,7 +8,7 @@
* Licensed under the terms of the Apache-2.0 license.
* https://opensource.org/license/apache-2-0
*/
// While loops feature test: simple loops, nested with if/else, and functions
print("=== while test start ===")
@ -42,3 +42,21 @@ fun countdown(n)
print(countdown(3)) // expect: 3,2,1 then 0
print("=== while test end ===")
/* Expected output:
=== while test start ===
0
1
2
3
4
3
99
1
3
2
1
0
=== while test end ===
*/

View file

@ -568,19 +568,35 @@ static int emit_primary(Bytecode *bc, const char *src, size_t len, size_t *pos)
}
if (strcmp(name, "typeof") == 0) {
(*pos)++; /* '(' */
/* Disable compile-time shortcut for typeof(<identifier>); always evaluate at runtime */
/* Special handling for typeof(<identifier>) to return declared subtype for integers */
size_t peek = *pos;
char *vname = NULL;
int handled = 0;
if (read_identifier_into(src, len, &peek, &vname)) {
/* allow spaces before ')' */
skip_spaces(src, len, &peek);
if (peek < len && src[peek] == ')') {
/* Fall back to runtime evaluation path */
int meta = 0;
int lidx = local_find(vname);
if (lidx >= 0) {
meta = g_locals->types[lidx];
} else {
int gi = sym_index(vname);
if (gi >= 0) meta = G.types[gi];
}
if (meta != 0 && meta != TYPE_META_STRING && meta != TYPE_META_BOOLEAN && meta != TYPE_META_NIL && meta != TYPE_META_CLASS && meta != TYPE_META_FLOAT) {
/* Integer subtype: ±bits */
int abs_bits = meta < 0 ? -meta : meta;
const char *tname = (meta < 0)
? (abs_bits==64? "Sint64" : (abs_bits==32? "Sint32" : (abs_bits==16? "Sint16" : "Sint8")))
: (abs_bits==64? "Uint64" : (abs_bits==32? "Uint32" : (abs_bits==16? "Uint16" : "Uint8")));
int ci = bytecode_add_constant(bc, make_string(tname));
bytecode_add_instruction(bc, OP_LOAD_CONST, ci);
*pos = peek + 1; /* consume name and ')' */
handled = 1;
}
free(vname);
/* handled remains 0 so we go to general-case below */
} else {
/* not a simple identifier-only typeof */
free(vname);
}
}
@ -628,6 +644,7 @@ static int emit_primary(Bytecode *bc, const char *src, size_t len, size_t *pos)
int ciMap2 = bytecode_add_constant(bc, make_string("Map"));
bytecode_add_instruction(bc, OP_LOAD_CONST, ciMap2); /* ["Map"] */
}
int j_end2 = bytecode_add_instruction(bc, OP_JUMP, 0);
int after_map = bc->instr_count;
/* not map: compute typeof(v) */
@ -636,6 +653,7 @@ static int emit_primary(Bytecode *bc, const char *src, size_t len, size_t *pos)
/* end */
bytecode_set_operand(bc, j_end, bc->instr_count);
bytecode_set_operand(bc, j_end2, bc->instr_count);
}
free(name);
@ -2758,53 +2776,9 @@ static void parse_simple_statement(Bytecode *bc, const char *src, size_t len, si
}
bytecode_set_operand(bc, j_skip_err, bc->instr_count);
/* range check instead of clamp */
int64_t minV = 0, maxV = 0;
if (decl_bits < 0) {
/* signed */
if (abs_bits >= 64) { minV = INT64_MIN; maxV = INT64_MAX; }
else { maxV = (1LL << (abs_bits - 1)) - 1; minV = - (1LL << (abs_bits - 1)); }
} else {
/* unsigned */
if (abs_bits >= 63) { minV = 0; maxV = INT64_MAX; }
else { minV = 0; maxV = (1LL << abs_bits) - 1; }
if (abs_bits > 0) {
bytecode_add_instruction(bc, (decl_bits < 0) ? OP_SCLAMP : OP_UCLAMP, abs_bits);
}
int ciMin = bytecode_add_constant(bc, make_int(minV));
int ciMax = bytecode_add_constant(bc, make_int(maxV));
/* if (v < min) -> error */
bytecode_add_instruction(bc, OP_DUP, 0);
bytecode_add_instruction(bc, OP_LOAD_CONST, ciMin);
bytecode_add_instruction(bc, OP_LT, 0);
int j_after_min = bytecode_add_instruction(bc, OP_JUMP_IF_FALSE, 0);
{
const char *tname = (decl_bits < 0)
? (abs_bits==64? "int64" : (abs_bits==32? "int32" : (abs_bits==16? "int16" : "int8")))
: (abs_bits==64? "uint64" : (abs_bits==32? "uint32" : (abs_bits==16? "uint16" : "uint8")));
char buf[128];
snprintf(buf, sizeof(buf), "OverflowError: value out of range for %s", tname);
int ciMsg = bytecode_add_constant(bc, make_string(buf));
bytecode_add_instruction(bc, OP_LOAD_CONST, ciMsg);
bytecode_add_instruction(bc, OP_THROW, 0);
}
bytecode_set_operand(bc, j_after_min, bc->instr_count);
/* if (v > max) -> error */
bytecode_add_instruction(bc, OP_DUP, 0);
bytecode_add_instruction(bc, OP_LOAD_CONST, ciMax);
bytecode_add_instruction(bc, OP_GT, 0);
int j_after_max = bytecode_add_instruction(bc, OP_JUMP_IF_FALSE, 0);
{
const char *tname = (decl_bits < 0)
? (abs_bits==64? "int64" : (abs_bits==32? "int32" : (abs_bits==16? "int16" : "int8")))
: (abs_bits==64? "uint64" : (abs_bits==32? "uint32" : (abs_bits==16? "uint16" : "uint8")));
char buf[128];
snprintf(buf, sizeof(buf), "OverflowError: value out of range for %s", tname);
int ciMsg = bytecode_add_constant(bc, make_string(buf));
bytecode_add_instruction(bc, OP_LOAD_CONST, ciMsg);
bytecode_add_instruction(bc, OP_THROW, 0);
}
bytecode_set_operand(bc, j_after_max, bc->instr_count);
}
}
@ -3147,50 +3121,10 @@ static void parse_simple_statement(Bytecode *bc, const char *src, size_t len, si
}
bytecode_set_operand(bc, j_skip_err, bc->instr_count);
/* range check instead of clamp */
int64_t minV = 0, maxV = 0;
if (meta < 0) {
if (abs_bits >= 64) { minV = INT64_MIN; maxV = INT64_MAX; }
else { maxV = (1LL << (abs_bits - 1)) - 1; minV = - (1LL << (abs_bits - 1)); }
} else {
if (abs_bits >= 63) { minV = 0; maxV = INT64_MAX; }
else { minV = 0; maxV = (1LL << abs_bits) - 1; }
if (abs_bits > 0) {
bytecode_add_instruction(bc, (meta < 0) ? OP_SCLAMP : OP_UCLAMP, abs_bits);
}
}
int ciMin = bytecode_add_constant(bc, make_int(minV));
int ciMax = bytecode_add_constant(bc, make_int(maxV));
bytecode_add_instruction(bc, OP_DUP, 0);
bytecode_add_instruction(bc, OP_LOAD_CONST, ciMin);
bytecode_add_instruction(bc, OP_LT, 0);
int j_after_min = bytecode_add_instruction(bc, OP_JUMP_IF_FALSE, 0);
{
const char *tname = (meta < 0)
? (abs_bits==64? "int64" : (abs_bits==32? "int32" : (abs_bits==16? "int16" : "int8")))
: (abs_bits==64? "uint64" : (abs_bits==32? "uint32" : (abs_bits==16? "uint16" : "uint8")));
char buf[128];
snprintf(buf, sizeof(buf), "OverflowError: value out of range for %s", tname);
int ciMsg2 = bytecode_add_constant(bc, make_string(buf));
bytecode_add_instruction(bc, OP_LOAD_CONST, ciMsg2);
bytecode_add_instruction(bc, OP_THROW, 0);
}
bytecode_set_operand(bc, j_after_min, bc->instr_count);
bytecode_add_instruction(bc, OP_DUP, 0);
bytecode_add_instruction(bc, OP_LOAD_CONST, ciMax);
bytecode_add_instruction(bc, OP_GT, 0);
int j_after_max = bytecode_add_instruction(bc, OP_JUMP_IF_FALSE, 0);
{
const char *tname = (meta < 0)
? (abs_bits==64? "int64" : (abs_bits==32? "int32" : (abs_bits==16? "int16" : "int8")))
: (abs_bits==64? "uint64" : (abs_bits==32? "uint32" : (abs_bits==16? "uint16" : "uint8")));
char buf[128];
snprintf(buf, sizeof(buf), "OverflowError: value out of range for %s", tname);
int ciMsg3 = bytecode_add_constant(bc, make_string(buf));
bytecode_add_instruction(bc, OP_LOAD_CONST, ciMsg3);
bytecode_add_instruction(bc, OP_THROW, 0);
}
bytecode_set_operand(bc, j_after_max, bc->instr_count);
}
/* dynamic (meta==0): no enforcement */
if (lidx >= 0) {

View file

@ -159,7 +159,7 @@ static int read_identifier_into(const char *src, size_t len, size_t *pos, char *
return 0;
}
static int64_t parse_int_literal_value(const char *src, size_t len, size_t *pos, int *ok) {
static uint64_t parse_int_literal_value(const char *src, size_t len, size_t *pos, int *ok) {
size_t p = *pos;
skip_spaces(src, len, &p);
int sign = 1;
@ -173,31 +173,31 @@ static int64_t parse_int_literal_value(const char *src, size_t len, size_t *pos,
if ((p + 1) < len && src[p] == '0' && (src[p + 1] == 'x' || src[p + 1] == 'X')) {
p += 2;
if (p >= len || !isxdigit((unsigned char)src[p])) { *ok = 0; return 0; }
int64_t val = 0;
uint64_t val = 0;
while (p < len && isxdigit((unsigned char)src[p])) {
char c = src[p];
int d = (c >= '0' && c <= '9') ? (c - '0')
: (c >= 'a' && c <= 'f') ? (c - 'a' + 10)
: (c >= 'A' && c <= 'F') ? (c - 'A' + 10)
: 0;
val = (val << 4) + d;
val = (val << 4) + (uint64_t)d;
p++;
}
*pos = p;
*ok = 1;
return sign * val;
return (uint64_t)((int64_t)sign * (int64_t)val);
}
/* Decimal fallback */
if (!isdigit((unsigned char)src[p])) { *ok = 0; return 0; }
int64_t val = 0;
uint64_t val = 0;
while (p < len && isdigit((unsigned char)src[p])) {
val = val * 10 + (src[p] - '0');
val = val * 10 + (uint64_t)(src[p] - '0');
p++;
}
*pos = p;
*ok = 1;
return sign * val;
return (uint64_t)((int64_t)sign * (int64_t)val);
}
/* === Include preprocessor ===