Added a lot of stdlib's to lib/ and many small fixes. (0.16.4)
This commit is contained in:
parent
17241b2401
commit
2b82a01fb5
9 changed files with 674 additions and 60 deletions
|
|
@ -1,5 +1,5 @@
|
|||
cmake_minimum_required(VERSION 3.16)
|
||||
project(fun VERSION 0.16.3 LANGUAGES C)
|
||||
project(fun VERSION 0.16.4 LANGUAGES C)
|
||||
|
||||
set(CMAKE_C_STANDARD 11)
|
||||
set(CMAKE_C_STANDARD_REQUIRED ON)
|
||||
|
|
|
|||
54
examples/stdlib_showcase.fun
Normal file
54
examples/stdlib_showcase.fun
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
#!/usr/bin/env fun
|
||||
|
||||
/*
|
||||
* Standard library showcase example for arrays, strings, math, ranges, and base64.
|
||||
*/
|
||||
|
||||
#include <arrays.fun>
|
||||
#include <strings.fun>
|
||||
#include <math.fun>
|
||||
#include <utils/range.fun>
|
||||
#include <encoding/base64.fun>
|
||||
|
||||
print("=== Arrays ===")
|
||||
arr = [1,2,2,3,4]
|
||||
print(join(array_slice(arr, 1, 3), ",")) // "2,2,3"
|
||||
print(join(array_reverse(arr), ",")) // "4,3,2,2,1"
|
||||
print(array_index_of(arr, 3)) // 3's index
|
||||
print(array_contains(arr, 5)) // 0
|
||||
print(join(array_unique(arr), ",")) // "1,2,3,4"
|
||||
print(join(array_flatten1([[1,2],[3],[4,5]]), ",")) // "1,2,3,4,5"
|
||||
|
||||
print("=== Strings ===")
|
||||
s = " Hello World \n"
|
||||
print("["+str_trim(s)+"]") // "[Hello World]"
|
||||
print(str_starts_with("foobar", "foo")) // 1
|
||||
print(str_ends_with("foobar", "bar")) // 1
|
||||
parts = str_split("a,b,c", ",")
|
||||
print(join(parts, "|")) // "a|b|c"
|
||||
print(str_replace_all("banana", "na", "NA")) // "baNANA"
|
||||
print(str_to_lower("FUN")) // "fun"
|
||||
print(str_to_upper("Fun")) // "FUN"
|
||||
print(str_repeat("ha", 3)) // "hahaha"
|
||||
|
||||
print("=== Math ===")
|
||||
print(abs(-5)) // 5
|
||||
print(clamp(42, 0, 10)) // 10
|
||||
print(gcd(54, 24)) // 6
|
||||
print(lcm(21, 6)) // 42
|
||||
print(powi(3, 5)) // 243
|
||||
print(array_min([9,2,8,3])) // 2
|
||||
print(array_max([9,2,8,3])) // 9
|
||||
print(min3(3,1,2)) // 1
|
||||
print(max3(3,1,2)) // 3
|
||||
|
||||
print("=== Range ===")
|
||||
print(join(range(5), ",")) // "0,1,2,3,4"
|
||||
print(join(range2(3, 8), ",")) // "3,4,5,6,7"
|
||||
print(join(range3(10, 0, -3), ",")) // "10,7,4,1"
|
||||
|
||||
print("=== Base64 ===")
|
||||
bytes = [0x48,0x65,0x6c,0x6c,0x6f] // "Hello"
|
||||
b64 = b64_encode_bytes(bytes)
|
||||
print(b64) // "SGVsbG8="
|
||||
print(join(b64_decode_to_bytes(b64), ",")) // "72,101,108,108,111"
|
||||
117
lib/arrays.fun
Normal file
117
lib/arrays.fun
Normal file
|
|
@ -0,0 +1,117 @@
|
|||
/*
|
||||
* This file is part of the Fun programming language.
|
||||
* https://hanez.org/project/fun/
|
||||
*
|
||||
* Copyright 2025 Johannes Findeisen
|
||||
* Licensed under the terms of the Apache-2.0 license.
|
||||
* https://opensource.org/license/apache-2-0
|
||||
*
|
||||
* Added: 2025-10-01
|
||||
*/
|
||||
|
||||
// Array utilities
|
||||
|
||||
// Return a shallow slice of arr starting at 'start' with 'count' elements (clamped)
|
||||
fun array_slice(arr, start, count)
|
||||
number n = len(arr)
|
||||
number s = start
|
||||
if (s < 0)
|
||||
s = 0
|
||||
if (s > n)
|
||||
s = n
|
||||
number c = count
|
||||
if (c < 0)
|
||||
c = 0
|
||||
if (s + c > n)
|
||||
c = n - s
|
||||
out = []
|
||||
number i = 0
|
||||
while i < c
|
||||
push(out, arr[s + i])
|
||||
i = i + 1
|
||||
return out
|
||||
|
||||
// Return a new array with elements in reverse order
|
||||
fun array_reverse(arr)
|
||||
out = []
|
||||
number i = len(arr) - 1
|
||||
while i >= 0
|
||||
push(out, arr[i])
|
||||
i = i - 1
|
||||
return out
|
||||
|
||||
// Concatenate two arrays and return a new array
|
||||
fun array_concat(a, b)
|
||||
out = []
|
||||
number i = 0
|
||||
while i < len(a)
|
||||
push(out, a[i])
|
||||
i = i + 1
|
||||
number j = 0
|
||||
while j < len(b)
|
||||
push(out, b[j])
|
||||
j = j + 1
|
||||
return out
|
||||
|
||||
// Return the index of the first occurrence of value in arr, or -1
|
||||
fun array_index_of(arr, value)
|
||||
number i = 0
|
||||
number n = len(arr)
|
||||
while i < n
|
||||
if (arr[i] == value)
|
||||
return i
|
||||
i = i + 1
|
||||
return -1
|
||||
|
||||
// Return 1 if arr contains value, else 0
|
||||
fun array_contains(arr, value)
|
||||
return array_index_of(arr, value) >= 0
|
||||
|
||||
// Return a new array with only the first occurrence of each element (stable)
|
||||
fun array_unique(arr)
|
||||
out = []
|
||||
number i = 0
|
||||
number n = len(arr)
|
||||
while i < n
|
||||
v = arr[i]
|
||||
if (array_index_of(out, v) < 0)
|
||||
push(out, v)
|
||||
i = i + 1
|
||||
return out
|
||||
|
||||
// Flatten one level: [[1,2],[3],[4,5]] -> [1,2,3,4,5]
|
||||
fun array_flatten1(arr)
|
||||
out = []
|
||||
number i = 0
|
||||
number n = len(arr)
|
||||
while i < n
|
||||
item = arr[i]
|
||||
// Treat nested arrays as flattenable
|
||||
if (typeof(item) == "Array")
|
||||
number j = 0
|
||||
number m = len(item)
|
||||
while j < m
|
||||
push(out, item[j])
|
||||
j = j + 1
|
||||
else
|
||||
push(out, item)
|
||||
i = i + 1
|
||||
return out
|
||||
|
||||
// Push value to arr only if not present (mutates arr), returns arr
|
||||
fun array_push_unique_mut(arr, value)
|
||||
if (!array_contains(arr, value))
|
||||
push(arr, value)
|
||||
return arr
|
||||
|
||||
// Return a new array that contains all elements of arr and value appended only if not present
|
||||
fun array_push_unique(arr, value)
|
||||
out = []
|
||||
number i = 0
|
||||
number n = len(arr)
|
||||
while i < n
|
||||
push(out, arr[i])
|
||||
i = i + 1
|
||||
if (!array_contains(out, value))
|
||||
push(out, value)
|
||||
return out
|
||||
|
|
@ -9,54 +9,4 @@
|
|||
* Added: 2025-10-01
|
||||
*/
|
||||
|
||||
// ASCII string to bytes (printable ASCII 0x20..0x7E)
|
||||
fun string_to_bytes_ascii(s)
|
||||
str = to_string(s)
|
||||
out = []
|
||||
number i = 0
|
||||
// ASCII printable ranges
|
||||
P1 = " !\"#$%&'()*+,-./" // 32..47
|
||||
P2 = "0123456789" // 48..57
|
||||
P3 = ":;<=>?@" // 58..64
|
||||
P4 = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" // 65..90
|
||||
P5 = "[\\]^_`" // 91..96
|
||||
P6 = "abcdefghijklmnopqrstuvwxyz" // 97..122
|
||||
P7 = "{|}~" // 123..126
|
||||
while true
|
||||
ch = substr(str, i, 1)
|
||||
if (typeof(ch) != "String" || ch == "")
|
||||
break
|
||||
number code = -1
|
||||
idx = find(P1, ch)
|
||||
if (idx >= 0)
|
||||
code = 32 + idx
|
||||
else
|
||||
idx = find(P2, ch)
|
||||
if (idx >= 0)
|
||||
code = 48 + idx
|
||||
else
|
||||
idx = find(P3, ch)
|
||||
if (idx >= 0)
|
||||
code = 58 + idx
|
||||
else
|
||||
idx = find(P4, ch)
|
||||
if (idx >= 0)
|
||||
code = 65 + idx
|
||||
else
|
||||
idx = find(P5, ch)
|
||||
if (idx >= 0)
|
||||
code = 91 + idx
|
||||
else
|
||||
idx = find(P6, ch)
|
||||
if (idx >= 0)
|
||||
code = 97 + idx
|
||||
else
|
||||
idx = find(P7, ch)
|
||||
if (idx >= 0)
|
||||
code = 123 + idx
|
||||
else
|
||||
// non-printable -> 0
|
||||
code = 0
|
||||
push(out, code)
|
||||
i = i + 1
|
||||
return out
|
||||
|
||||
|
|
|
|||
89
lib/encoding/base64.fun
Normal file
89
lib/encoding/base64.fun
Normal file
|
|
@ -0,0 +1,89 @@
|
|||
/*
|
||||
* This file is part of the Fun programming language.
|
||||
* https://hanez.org/project/fun/
|
||||
*
|
||||
* Copyright 2025 Johannes Findeisen
|
||||
* Licensed under the terms of the Apache-2.0 license.
|
||||
* https://opensource.org/license/apache-2-0
|
||||
*
|
||||
* Added: 2025-10-01
|
||||
*/
|
||||
|
||||
// Base64 encode/decode for byte arrays (RFC 4648, standard alphabet)
|
||||
|
||||
fun b64_encode_bytes(bytes)
|
||||
table = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
|
||||
out = []
|
||||
number i = 0
|
||||
number n = len(bytes)
|
||||
while i + 2 < n
|
||||
number b0 = bytes[i]
|
||||
number b1 = bytes[i + 1]
|
||||
number b2 = bytes[i + 2]
|
||||
number v = b0 * 65536 + b1 * 256 + b2
|
||||
number c0 = (v / 262144) % 64 // bits 18..23
|
||||
number c1 = (v / 4096) % 64 // bits 12..17
|
||||
number c2 = (v / 64) % 64 // bits 6..11
|
||||
number c3 = v % 64 // bits 0..5
|
||||
push(out, substr(table, c0, 1))
|
||||
push(out, substr(table, c1, 1))
|
||||
push(out, substr(table, c2, 1))
|
||||
push(out, substr(table, c3, 1))
|
||||
i = i + 3
|
||||
|
||||
number rem = n - i
|
||||
if (rem == 1)
|
||||
number b0 = bytes[i]
|
||||
number v = b0 * 65536
|
||||
number c0 = (v / 262144) % 64
|
||||
number c1 = (v / 4096) % 64
|
||||
push(out, substr(table, c0, 1))
|
||||
push(out, substr(table, c1, 1))
|
||||
push(out, "=")
|
||||
push(out, "=")
|
||||
else if (rem == 2)
|
||||
number b0 = bytes[i]
|
||||
number b1 = bytes[i + 1]
|
||||
number v = b0 * 65536 + b1 * 256
|
||||
number c0 = (v / 262144) % 64
|
||||
number c1 = (v / 4096) % 64
|
||||
number c2 = (v / 64) % 64
|
||||
push(out, substr(table, c0, 1))
|
||||
push(out, substr(table, c1, 1))
|
||||
push(out, substr(table, c2, 1))
|
||||
push(out, "=")
|
||||
|
||||
return join(out, "")
|
||||
|
||||
fun b64_decode_to_bytes(s)
|
||||
table = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
|
||||
str = to_string(s)
|
||||
out = []
|
||||
// build reverse lookup: index in table is value
|
||||
// Since we don't have a map, use find on each char
|
||||
number i = 0
|
||||
number n = len(str)
|
||||
while i < n
|
||||
// read 4 chars
|
||||
c0 = substr(str, i, 1)
|
||||
c1 = substr(str, i + 1, 1)
|
||||
c2 = substr(str, i + 2, 1)
|
||||
c3 = substr(str, i + 3, 1)
|
||||
number v0 = find(table, c0)
|
||||
number v1 = find(table, c1)
|
||||
number pad2 = (c2 == "=")
|
||||
number pad3 = (c3 == "=")
|
||||
number v2 = pad2 ? 0 : find(table, c2)
|
||||
number v3 = pad3 ? 0 : find(table, c3)
|
||||
number twentyfour = v0 * 262144 + v1 * 4096 + v2 * 64 + v3
|
||||
// bytes
|
||||
number b0 = (twentyfour / 65536) % 256
|
||||
number b1 = (twentyfour / 256) % 256
|
||||
number b2 = twentyfour % 256
|
||||
push(out, b0)
|
||||
if (!pad2)
|
||||
push(out, b1)
|
||||
if (!pad3)
|
||||
push(out, b2)
|
||||
i = i + 4
|
||||
return out
|
||||
90
lib/math.fun
Normal file
90
lib/math.fun
Normal file
|
|
@ -0,0 +1,90 @@
|
|||
/*
|
||||
* This file is part of the Fun programming language.
|
||||
* https://hanez.org/project/fun/
|
||||
*
|
||||
* Copyright 2025 Johannes Findeisen
|
||||
* Licensed under the terms of the Apache-2.0 license.
|
||||
* https://opensource.org/license/apache-2-0
|
||||
*
|
||||
* Added: 2025-10-01
|
||||
*/
|
||||
|
||||
// Math helpers
|
||||
|
||||
fun abs(x)
|
||||
if (x < 0)
|
||||
return -x
|
||||
return x
|
||||
|
||||
fun clamp(x, lo, hi)
|
||||
v = x
|
||||
if (v < lo) v = lo
|
||||
if (v > hi) v = hi
|
||||
return v
|
||||
|
||||
fun gcd(a, b)
|
||||
number x = abs(a)
|
||||
number y = abs(b)
|
||||
while y != 0
|
||||
t = x % y
|
||||
x = y
|
||||
y = t
|
||||
return x
|
||||
|
||||
fun lcm(a, b)
|
||||
if (a == 0) || (b == 0)
|
||||
return 0
|
||||
return abs(a * b) / gcd(a, b)
|
||||
|
||||
// integer exponent (exp >= 0)
|
||||
fun powi(base, exp)
|
||||
number e = exp
|
||||
if (e < 0)
|
||||
// simple fallback for negative: 0 for now (no floats)
|
||||
return 0
|
||||
number result = 1
|
||||
number b = base
|
||||
while e > 0
|
||||
if (e % 2 == 1)
|
||||
result = result * b
|
||||
b = b * b
|
||||
e = e / 2
|
||||
return result
|
||||
|
||||
// Min/Max over three numbers
|
||||
fun min3(a, b, c)
|
||||
m = a
|
||||
if (b < m) m = b
|
||||
if (c < m) m = c
|
||||
return m
|
||||
|
||||
fun max3(a, b, c)
|
||||
m = a
|
||||
if (b > m) m = b
|
||||
if (c > m) m = c
|
||||
return m
|
||||
|
||||
// Array min/max (for non-empty arrays)
|
||||
fun array_min(arr)
|
||||
number n = len(arr)
|
||||
if (n == 0)
|
||||
return 0
|
||||
m = arr[0]
|
||||
number i = 1
|
||||
while i < n
|
||||
if (arr[i] < m)
|
||||
m = arr[i]
|
||||
i = i + 1
|
||||
return m
|
||||
|
||||
fun array_max(arr)
|
||||
number n = len(arr)
|
||||
if (n == 0)
|
||||
return 0
|
||||
m = arr[0]
|
||||
number i = 1
|
||||
while i < n
|
||||
if (arr[i] > m)
|
||||
m = arr[i]
|
||||
i = i + 1
|
||||
return m
|
||||
196
lib/strings.fun
196
lib/strings.fun
|
|
@ -2,14 +2,202 @@
|
|||
* This file is part of the Fun programming language.
|
||||
* https://hanez.org/project/fun/
|
||||
*
|
||||
* Copyright 2025 Johannes Findeisen <you@hanez.org>
|
||||
* Copyright 2025 Johannes Findeisen
|
||||
* Licensed under the terms of the Apache-2.0 license.
|
||||
* https://opensource.org/license/apache-2-0
|
||||
*
|
||||
* Added: 2025-10-01
|
||||
*/
|
||||
|
||||
// This file should not contain code! It should just include all string related
|
||||
// functions from core/*.fun.
|
||||
// Extra string utilities
|
||||
|
||||
#include <core/string_to_bytes_ascii.fun>
|
||||
// Trim whitespace on the left (space, tab, CR, LF)
|
||||
fun str_ltrim(s)
|
||||
src = to_string(s)
|
||||
number i = 0
|
||||
ws = " \t\r\n"
|
||||
while i < len(src)
|
||||
ch = substr(src, i, 1)
|
||||
if (find(ws, ch) < 0)
|
||||
break
|
||||
i = i + 1
|
||||
return substr(src, i, len(src) - i)
|
||||
|
||||
// Trim whitespace on the right
|
||||
fun str_rtrim(s)
|
||||
src = to_string(s)
|
||||
number i = len(src) - 1
|
||||
ws = " \t\r\n"
|
||||
while i >= 0
|
||||
ch = substr(src, i, 1)
|
||||
if (find(ws, ch) < 0)
|
||||
break
|
||||
i = i - 1
|
||||
return substr(src, 0, i + 1)
|
||||
|
||||
// Trim both sides
|
||||
fun str_trim(s)
|
||||
return str_rtrim(str_ltrim(s))
|
||||
|
||||
// Return 1 if s starts with prefix, else 0
|
||||
fun str_starts_with(s, prefix)
|
||||
a = to_string(s)
|
||||
p = to_string(prefix)
|
||||
if (len(p) > len(a))
|
||||
return 0
|
||||
return substr(a, 0, len(p)) == p
|
||||
|
||||
// Return 1 if s ends with suffix, else 0
|
||||
fun str_ends_with(s, suffix)
|
||||
a = to_string(s)
|
||||
p = to_string(suffix)
|
||||
number la = len(a)
|
||||
number lp = len(p)
|
||||
if (lp > la)
|
||||
return 0
|
||||
return substr(a, la - lp, lp) == p
|
||||
|
||||
// Split by a single-character delimiter, returns array of strings
|
||||
fun str_split(s, delim)
|
||||
src = to_string(s)
|
||||
d = to_string(delim)
|
||||
// Use only the first character of delim
|
||||
if (len(d) == 0)
|
||||
return [src]
|
||||
dd = substr(d, 0, 1)
|
||||
parts = []
|
||||
buf = []
|
||||
number i = 0
|
||||
number n = len(src)
|
||||
while i < n
|
||||
ch = substr(src, i, 1)
|
||||
if (ch == dd)
|
||||
push(parts, join(buf, ""))
|
||||
buf = []
|
||||
else
|
||||
push(buf, ch)
|
||||
i = i + 1
|
||||
// tail
|
||||
push(parts, join(buf, ""))
|
||||
return parts
|
||||
|
||||
// Replace all occurrences of 'from' with 'to' (naive scan)
|
||||
fun str_replace_all(s, from, to)
|
||||
src = to_string(s)
|
||||
f = to_string(from)
|
||||
t = to_string(to)
|
||||
number n = len(src)
|
||||
number lf = len(f)
|
||||
if (lf == 0)
|
||||
return src
|
||||
out = []
|
||||
number i = 0
|
||||
while i < n
|
||||
if (i + lf <= n) && (substr(src, i, lf) == f)
|
||||
push(out, t)
|
||||
i = i + lf
|
||||
else
|
||||
push(out, substr(src, i, 1))
|
||||
i = i + 1
|
||||
return join(out, "")
|
||||
|
||||
// Lowercase transform for ASCII A..Z
|
||||
fun str_to_lower(s)
|
||||
src = to_string(s)
|
||||
U = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
L = "abcdefghijklmnopqrstuvwxyz"
|
||||
out = []
|
||||
number i = 0
|
||||
number n = len(src)
|
||||
while i < n
|
||||
ch = substr(src, i, 1)
|
||||
idx = find(U, ch)
|
||||
if (idx >= 0)
|
||||
push(out, substr(L, idx, 1))
|
||||
else
|
||||
push(out, ch)
|
||||
i = i + 1
|
||||
return join(out, "")
|
||||
|
||||
// Uppercase transform for ASCII a..z
|
||||
fun str_to_upper(s)
|
||||
src = to_string(s)
|
||||
U = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
L = "abcdefghijklmnopqrstuvwxyz"
|
||||
out = []
|
||||
number i = 0
|
||||
number n = len(src)
|
||||
while i < n
|
||||
ch = substr(src, i, 1)
|
||||
idx = find(L, ch)
|
||||
if (idx >= 0)
|
||||
push(out, substr(U, idx, 1))
|
||||
else
|
||||
push(out, ch)
|
||||
i = i + 1
|
||||
return join(out, "")
|
||||
|
||||
// Repeat string s 'count' times
|
||||
fun str_repeat(s, count)
|
||||
src = to_string(s)
|
||||
number c = count
|
||||
if (c <= 0)
|
||||
return ""
|
||||
parts = []
|
||||
number i = 0
|
||||
while i < c
|
||||
push(parts, src)
|
||||
i = i + 1
|
||||
return join(parts, "")
|
||||
|
||||
// ASCII string to bytes (printable ASCII 0x20..0x7E)
|
||||
fun string_to_bytes_ascii(s)
|
||||
str = to_string(s)
|
||||
out = []
|
||||
number i = 0
|
||||
// ASCII printable ranges
|
||||
P1 = " !\"#$%&'()*+,-./" // 32..47
|
||||
P2 = "0123456789" // 48..57
|
||||
P3 = ":;<=>?@" // 58..64
|
||||
P4 = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" // 65..90
|
||||
P5 = "[\\]^_`" // 91..96
|
||||
P6 = "abcdefghijklmnopqrstuvwxyz" // 97..122
|
||||
P7 = "{|}~" // 123..126
|
||||
while true
|
||||
ch = substr(str, i, 1)
|
||||
if (typeof(ch) != "String" || ch == "")
|
||||
break
|
||||
number code = -1
|
||||
idx = find(P1, ch)
|
||||
if (idx >= 0)
|
||||
code = 32 + idx
|
||||
else
|
||||
idx = find(P2, ch)
|
||||
if (idx >= 0)
|
||||
code = 48 + idx
|
||||
else
|
||||
idx = find(P3, ch)
|
||||
if (idx >= 0)
|
||||
code = 58 + idx
|
||||
else
|
||||
idx = find(P4, ch)
|
||||
if (idx >= 0)
|
||||
code = 65 + idx
|
||||
else
|
||||
idx = find(P5, ch)
|
||||
if (idx >= 0)
|
||||
code = 91 + idx
|
||||
else
|
||||
idx = find(P6, ch)
|
||||
if (idx >= 0)
|
||||
code = 97 + idx
|
||||
else
|
||||
idx = find(P7, ch)
|
||||
if (idx >= 0)
|
||||
code = 123 + idx
|
||||
else
|
||||
// non-printable -> 0
|
||||
code = 0
|
||||
push(out, code)
|
||||
i = i + 1
|
||||
return out
|
||||
|
|
|
|||
48
lib/utils/range.fun
Normal file
48
lib/utils/range.fun
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
/*
|
||||
* This file is part of the Fun programming language.
|
||||
* https://hanez.org/project/fun/
|
||||
*
|
||||
* Copyright 2025 Johannes Findeisen
|
||||
* Licensed under the terms of the Apache-2.0 license.
|
||||
* https://opensource.org/license/apache-2-0
|
||||
*
|
||||
* Added: 2025-10-01
|
||||
*/
|
||||
|
||||
// Range utilities (end-exclusive)
|
||||
|
||||
// range(n) -> [0, 1, ..., n-1]
|
||||
fun range(n)
|
||||
out = []
|
||||
number i = 0
|
||||
while i < n
|
||||
push(out, i)
|
||||
i = i + 1
|
||||
return out
|
||||
|
||||
// range2(start, end) -> [start, start+1, ..., end-1]
|
||||
fun range2(start, end)
|
||||
out = []
|
||||
number i = start
|
||||
while i < end
|
||||
push(out, i)
|
||||
i = i + 1
|
||||
return out
|
||||
|
||||
// range3(start, end, step) with positive or negative step (non-zero)
|
||||
fun range3(start, end, step)
|
||||
out = []
|
||||
number s = step
|
||||
if (s == 0)
|
||||
return out
|
||||
if (s > 0)
|
||||
number i = start
|
||||
while i < end
|
||||
push(out, i)
|
||||
i = i + s
|
||||
else
|
||||
number j = start
|
||||
while j > end
|
||||
push(out, j)
|
||||
j = j + s
|
||||
return out
|
||||
86
src/parser.c
86
src/parser.c
|
|
@ -1639,9 +1639,58 @@ static int emit_or_expr(Bytecode *bc, const char *src, size_t len, size_t *pos)
|
|||
return 1;
|
||||
}
|
||||
|
||||
/* conditional operator (ternary) with right associativity:
|
||||
Parses: logical_or ('?' conditional ':' conditional)? */
|
||||
static int emit_conditional(Bytecode *bc, const char *src, size_t len, size_t *pos) {
|
||||
/* parse condition (logical OR precedence or higher) */
|
||||
if (!emit_or_expr(bc, src, len, pos)) return 0;
|
||||
|
||||
for (;;) {
|
||||
skip_spaces(src, len, pos);
|
||||
if (!(*pos < len && src[*pos] == '?')) break;
|
||||
(*pos)++; /* consume '?' */
|
||||
|
||||
/* If condition is false -> jump to false arm */
|
||||
int jmp_false = bytecode_add_instruction(bc, OP_JUMP_IF_FALSE, 0);
|
||||
|
||||
/* true arm (right-assoc: allow nested ternaries) */
|
||||
skip_spaces(src, len, pos);
|
||||
if (!emit_conditional(bc, src, len, pos)) {
|
||||
parser_fail(*pos, "Expected expression after '?'");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* After true arm, unconditionally skip false arm */
|
||||
int jmp_end = bytecode_add_instruction(bc, OP_JUMP, 0);
|
||||
|
||||
/* false arm label */
|
||||
bytecode_set_operand(bc, jmp_false, bc->instr_count);
|
||||
|
||||
/* require ':' */
|
||||
skip_spaces(src, len, pos);
|
||||
if (!(*pos < len && src[*pos] == ':')) {
|
||||
parser_fail(*pos, "Expected ':' in conditional expression");
|
||||
return 0;
|
||||
}
|
||||
(*pos)++; /* consume ':' */
|
||||
|
||||
/* false arm (right-assoc) */
|
||||
skip_spaces(src, len, pos);
|
||||
if (!emit_conditional(bc, src, len, pos)) {
|
||||
parser_fail(*pos, "Expected expression after ':'");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* end label */
|
||||
bytecode_set_operand(bc, jmp_end, bc->instr_count);
|
||||
/* loop to allow chaining like a ? b : c ? d : e (right-assoc) */
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* top-level expression */
|
||||
static int emit_expression(Bytecode *bc, const char *src, size_t len, size_t *pos) {
|
||||
return emit_or_expr(bc, src, len, pos);
|
||||
return emit_conditional(bc, src, len, pos);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -3336,12 +3385,41 @@ static void parse_block(Bytecode *bc, const char *src, size_t len, size_t *pos,
|
|||
int ci = bytecode_add_constant(bc, make_int(0));
|
||||
bytecode_add_instruction(bc, OP_LOAD_CONST, ci);
|
||||
}
|
||||
/* end of condition: ignore any trailing until EOL */
|
||||
skip_to_eol(src, len, pos);
|
||||
|
||||
/* conditional jump over this clause's block */
|
||||
/* Decide between inline single-statement and indented block on next line */
|
||||
size_t ppeek = *pos;
|
||||
/* skip spaces after condition */
|
||||
while (ppeek < len && src[ppeek] == ' ') ppeek++;
|
||||
int inline_stmt = 0;
|
||||
if (ppeek < len) {
|
||||
if (src[ppeek] == '\r' || src[ppeek] == '\n') {
|
||||
inline_stmt = 0; /* EOL -> no inline body */
|
||||
} else if (ppeek + 1 < len && src[ppeek] == '/' && src[ppeek + 1] == '/') {
|
||||
inline_stmt = 0; /* line comment -> no inline body */
|
||||
} else if (ppeek + 1 < len && src[ppeek] == '/' && src[ppeek + 1] == '*') {
|
||||
inline_stmt = 0; /* block comment at EOL -> no inline body */
|
||||
} else {
|
||||
inline_stmt = 1; /* there's code after condition on same line */
|
||||
}
|
||||
}
|
||||
|
||||
/* conditional jump over this clause's inline/body */
|
||||
int jmp_false = bytecode_add_instruction(bc, OP_JUMP_IF_FALSE, 0);
|
||||
|
||||
if (inline_stmt) {
|
||||
/* Compile a single inline statement on the same line:
|
||||
if (cond) <statement> */
|
||||
*pos = ppeek;
|
||||
parse_simple_statement(bc, src, len, pos);
|
||||
/* Skip the inline body when condition is false */
|
||||
bytecode_set_operand(bc, jmp_false, bc->instr_count);
|
||||
/* one-liner form has no else/elseif on the same line; end the chain */
|
||||
break;
|
||||
}
|
||||
|
||||
/* No inline statement on this line: consume up to EOL and parse indented block or else-if/else */
|
||||
skip_to_eol(src, len, pos);
|
||||
|
||||
/* parse nested block if next line is indented */
|
||||
int next_indent = 0;
|
||||
size_t look_next = *pos;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue