Added SHA-1 class to Fun's standard libs. (0.16.1)
This commit is contained in:
parent
a56a351da6
commit
71682293fe
5 changed files with 331 additions and 202 deletions
|
|
@ -1,5 +1,5 @@
|
||||||
cmake_minimum_required(VERSION 3.16)
|
cmake_minimum_required(VERSION 3.16)
|
||||||
project(fun VERSION 0.16.0 LANGUAGES C)
|
project(fun VERSION 0.16.1 LANGUAGES C)
|
||||||
|
|
||||||
set(CMAKE_C_STANDARD 11)
|
set(CMAKE_C_STANDARD 11)
|
||||||
set(CMAKE_C_STANDARD_REQUIRED ON)
|
set(CMAKE_C_STANDARD_REQUIRED ON)
|
||||||
|
|
|
||||||
48
examples/sha1_demo.fun
Normal file
48
examples/sha1_demo.fun
Normal file
|
|
@ -0,0 +1,48 @@
|
||||||
|
#!/usr/bin/env fun
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of the Fun programming language.
|
||||||
|
* https://hanez.org/project/fun/
|
||||||
|
*
|
||||||
|
* Copyright 2025 Johannes Findeisen <you@hanez.org>
|
||||||
|
* Licensed under the terms of the Apache-2.0 license.
|
||||||
|
* https://opensource.org/license/apache-2-0
|
||||||
|
*
|
||||||
|
* Added: 2025-10-01
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* SHA-1 usage demo
|
||||||
|
*
|
||||||
|
* Run without installing:
|
||||||
|
* FUN_LIB_DIR="$(pwd)/lib" ./build/fun examples/sha1_demo.fun
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <crypt/sha1.fun> as sha1
|
||||||
|
|
||||||
|
print("=== SHA-1 demo ===")
|
||||||
|
|
||||||
|
s = "abc"
|
||||||
|
hex_abc = "616263" // "abc" in hex
|
||||||
|
empty = ""
|
||||||
|
|
||||||
|
// Hash ASCII string directly
|
||||||
|
d1 = sha1.SHA1().sha1_str(s)
|
||||||
|
print("SHA-1('abc') = " + d1)
|
||||||
|
|
||||||
|
// Hash pre-encoded hex bytes
|
||||||
|
d2 = sha1.SHA1().sha1_hex(hex_abc)
|
||||||
|
print("SHA-1(616263 hex) = " + d2)
|
||||||
|
|
||||||
|
// Empty string
|
||||||
|
d3 = sha1.SHA1().sha1_str(empty)
|
||||||
|
print("SHA-1('') = " + d3)
|
||||||
|
|
||||||
|
print("=== done ===")
|
||||||
|
|
||||||
|
/* Expected output:
|
||||||
|
=== SHA-1 demo ===
|
||||||
|
SHA-1('abc') = e6cd2bcee460c45d41565ac877d866a159a16e19
|
||||||
|
SHA-1(616263 hex) = e6cd2bcee460c45d41565ac877d866a159a16e19
|
||||||
|
SHA-1('') = da39a3ee5e6b4b0d3255bfef95601890afd80709
|
||||||
|
=== done ===
|
||||||
|
|
@ -1,187 +0,0 @@
|
||||||
/*
|
|
||||||
* This file is part of the Fun programming language.
|
|
||||||
* https://hanez.org/project/fun/
|
|
||||||
*
|
|
||||||
* Copyright 2025 Johannes Findeisen <you@hanez.org>
|
|
||||||
* Licensed under the terms of the Apache-2.0 license.
|
|
||||||
* https://opensource.org/license/apache-2-0
|
|
||||||
*
|
|
||||||
* Added: 2025-09-29
|
|
||||||
*/
|
|
||||||
|
|
||||||
// lib/crypt/crc32.fun
|
|
||||||
// Pure Fun CRC32 (IEEE 802.3, polynomial 0xEDB88320, reflected) operating on hex-string input.
|
|
||||||
//
|
|
||||||
// Public API (class):
|
|
||||||
// crc = CRC32()
|
|
||||||
// crc.crc32_hex(hexStr) -> digest hex string (lowercase, 8 hex chars)
|
|
||||||
//
|
|
||||||
// Example:
|
|
||||||
// print(CRC32().crc32_hex("616263")) // "abc" => 352441c2
|
|
||||||
|
|
||||||
class CRC32()
|
|
||||||
// hex helpers
|
|
||||||
fun hex_val(this, ch)
|
|
||||||
if (ch == "0")
|
|
||||||
return 0
|
|
||||||
else if (ch == "1")
|
|
||||||
return 1
|
|
||||||
else if (ch == "2")
|
|
||||||
return 2
|
|
||||||
else if (ch == "3")
|
|
||||||
return 3
|
|
||||||
else if (ch == "4")
|
|
||||||
return 4
|
|
||||||
else if (ch == "5")
|
|
||||||
return 5
|
|
||||||
else if (ch == "6")
|
|
||||||
return 6
|
|
||||||
else if (ch == "7")
|
|
||||||
return 7
|
|
||||||
else if (ch == "8")
|
|
||||||
return 8
|
|
||||||
else if (ch == "9")
|
|
||||||
return 9
|
|
||||||
else if (ch == "a" || ch == "A")
|
|
||||||
return 10
|
|
||||||
else if (ch == "b" || ch == "B")
|
|
||||||
return 11
|
|
||||||
else if (ch == "c" || ch == "C")
|
|
||||||
return 12
|
|
||||||
else if (ch == "d" || ch == "D")
|
|
||||||
return 13
|
|
||||||
else if (ch == "e" || ch == "E")
|
|
||||||
return 14
|
|
||||||
else if (ch == "f" || ch == "F")
|
|
||||||
return 15
|
|
||||||
else
|
|
||||||
return 0
|
|
||||||
|
|
||||||
fun byte_from_hex_pair(this, hh)
|
|
||||||
hi = this.hex_val(substr(hh, 0, 1))
|
|
||||||
lo = this.hex_val(substr(hh, 1, 1))
|
|
||||||
return hi * 16 + lo
|
|
||||||
|
|
||||||
fun from_hex(this, hex)
|
|
||||||
// ensure we operate on a string
|
|
||||||
hex = to_string(hex)
|
|
||||||
arr = []
|
|
||||||
number i = 0
|
|
||||||
while true
|
|
||||||
ch1 = substr(hex, i, 1)
|
|
||||||
if (typeof(ch1) != "String")
|
|
||||||
break
|
|
||||||
if (ch1 == "")
|
|
||||||
break
|
|
||||||
ch2 = substr(hex, i + 1, 1)
|
|
||||||
if (typeof(ch2) != "String")
|
|
||||||
break
|
|
||||||
if (ch2 == "")
|
|
||||||
break
|
|
||||||
pair = join([ch1, ch2], "")
|
|
||||||
number b = this.byte_from_hex_pair(pair)
|
|
||||||
push(arr, b)
|
|
||||||
i = i + 2
|
|
||||||
return arr
|
|
||||||
|
|
||||||
fun two_hex(this, n)
|
|
||||||
n = n % 256
|
|
||||||
hexd = "0123456789abcdef"
|
|
||||||
number hi = n / 16
|
|
||||||
number lo = n % 16
|
|
||||||
ch1 = substr(hexd, hi, 1)
|
|
||||||
ch2 = substr(hexd, lo, 1)
|
|
||||||
return join([ch1, ch2], "")
|
|
||||||
|
|
||||||
fun u32(this, x)
|
|
||||||
m = 4294967296
|
|
||||||
while x < 0
|
|
||||||
x = x + m
|
|
||||||
while x >= m
|
|
||||||
x = x - m
|
|
||||||
return x
|
|
||||||
|
|
||||||
// Core per-byte CRC (bit-by-bit, reflected), polynomial 0xEDB88320
|
|
||||||
fun crc32_bytes(this, bytes)
|
|
||||||
crc = 4294967295 // 0xFFFFFFFF initial
|
|
||||||
poly = 3988292384 // 0xEDB88320
|
|
||||||
number i = 0
|
|
||||||
L = len(bytes)
|
|
||||||
while i < L
|
|
||||||
// crc ^= byte
|
|
||||||
crc = bxor(crc, bytes[i])
|
|
||||||
// 8 times
|
|
||||||
j = 0
|
|
||||||
while j < 8
|
|
||||||
if (band(crc, 1) == 1)
|
|
||||||
// crc = (crc >> 1) ^ poly
|
|
||||||
crc = bxor(shr(crc, 1), poly)
|
|
||||||
else
|
|
||||||
// crc >>= 1
|
|
||||||
crc = shr(crc, 1)
|
|
||||||
j = j + 1
|
|
||||||
i = i + 1
|
|
||||||
// final xor
|
|
||||||
crc = bxor(crc, 4294967295)
|
|
||||||
return this.u32(crc)
|
|
||||||
|
|
||||||
fun crc32_hex(this, hexStr)
|
|
||||||
// Process hex input directly with only core built-ins; no method calls on 'this'
|
|
||||||
hex = to_string(hexStr)
|
|
||||||
hexd = "0123456789abcdef"
|
|
||||||
|
|
||||||
// CRC state
|
|
||||||
number crc = 4294967295 // 0xFFFFFFFF
|
|
||||||
number poly = 3988292384 // 0xEDB88320
|
|
||||||
|
|
||||||
// Iterate two hex nibbles at a time
|
|
||||||
number i = 0
|
|
||||||
while true
|
|
||||||
ch1 = substr(hex, i, 1)
|
|
||||||
if (typeof(ch1) != "String" || ch1 == "")
|
|
||||||
break
|
|
||||||
ch2 = substr(hex, i + 1, 1)
|
|
||||||
if (typeof(ch2) != "String" || ch2 == "")
|
|
||||||
break
|
|
||||||
|
|
||||||
// Convert two hex chars to a byte without helper calls
|
|
||||||
// find returns index of substring or -1; ensure lowercase
|
|
||||||
h1 = find(hexd, to_string(ch1))
|
|
||||||
h2 = find(hexd, to_string(ch2))
|
|
||||||
if (h1 < 0 || h2 < 0)
|
|
||||||
// invalid hex -> stop
|
|
||||||
break
|
|
||||||
number b = h1 * 16 + h2
|
|
||||||
|
|
||||||
// crc ^= b
|
|
||||||
crc = bxor(crc, b)
|
|
||||||
// 8 rounds
|
|
||||||
number j = 0
|
|
||||||
while j < 8
|
|
||||||
if (band(crc, 1) == 1)
|
|
||||||
crc = bxor(shr(crc, 1), poly)
|
|
||||||
else
|
|
||||||
crc = shr(crc, 1)
|
|
||||||
j = j + 1
|
|
||||||
|
|
||||||
i = i + 2
|
|
||||||
|
|
||||||
// final xor
|
|
||||||
crc = bxor(crc, 4294967295)
|
|
||||||
|
|
||||||
// Format result as 8 hex chars big-endian without calling this.two_hex
|
|
||||||
number b3 = (crc / 16777216) % 256
|
|
||||||
number b2 = (crc / 65536) % 256
|
|
||||||
number b1 = (crc / 256) % 256
|
|
||||||
number b0 = crc % 256
|
|
||||||
|
|
||||||
// encode a single byte to two hex chars using hexd and substr
|
|
||||||
fun b2hex(byte)
|
|
||||||
number hi = (byte / 16) % 16
|
|
||||||
number lo = (byte % 16)
|
|
||||||
c1 = substr(hexd, hi, 1)
|
|
||||||
c2 = substr(hexd, lo, 1)
|
|
||||||
return join([c1, c2], "")
|
|
||||||
|
|
||||||
parts = [ b2hex(b3), b2hex(b2), b2hex(b1), b2hex(b0) ]
|
|
||||||
return join(parts, "")
|
|
||||||
265
lib/crypt/sha1.fun
Normal file
265
lib/crypt/sha1.fun
Normal file
|
|
@ -0,0 +1,265 @@
|
||||||
|
/*
|
||||||
|
* 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
|
||||||
|
*/
|
||||||
|
|
||||||
|
// lib/crypt/sha1.fun
|
||||||
|
// Pure Fun SHA-1 implementation operating on hex-string or ASCII inputs.
|
||||||
|
//
|
||||||
|
// Public API (class):
|
||||||
|
// sha = SHA1()
|
||||||
|
// sha.sha1_hex(hexStr) -> digest hex string (lowercase)
|
||||||
|
// sha.sha1_str("abc") -> digest hex string of ASCII bytes
|
||||||
|
|
||||||
|
class SHA1()
|
||||||
|
// 32-bit helpers
|
||||||
|
fun u32(this, x)
|
||||||
|
m = 4294967296
|
||||||
|
while x < 0
|
||||||
|
x = x + m
|
||||||
|
while x >= m
|
||||||
|
x = x - m
|
||||||
|
return x
|
||||||
|
|
||||||
|
fun add32(this, a, b)
|
||||||
|
return this.u32(a + b)
|
||||||
|
|
||||||
|
fun rol32(this, x, s)
|
||||||
|
x = this.u32(x)
|
||||||
|
return rol(x, s)
|
||||||
|
|
||||||
|
// hex helpers
|
||||||
|
fun hex_val(this, ch)
|
||||||
|
if (ch == "0")
|
||||||
|
return 0
|
||||||
|
else if (ch == "1")
|
||||||
|
return 1
|
||||||
|
else if (ch == "2")
|
||||||
|
return 2
|
||||||
|
else if (ch == "3")
|
||||||
|
return 3
|
||||||
|
else if (ch == "4")
|
||||||
|
return 4
|
||||||
|
else if (ch == "5")
|
||||||
|
return 5
|
||||||
|
else if (ch == "6")
|
||||||
|
return 6
|
||||||
|
else if (ch == "7")
|
||||||
|
return 7
|
||||||
|
else if (ch == "8")
|
||||||
|
return 8
|
||||||
|
else if (ch == "9")
|
||||||
|
return 9
|
||||||
|
else if (ch == "a" || ch == "A")
|
||||||
|
return 10
|
||||||
|
else if (ch == "b" || ch == "B")
|
||||||
|
return 11
|
||||||
|
else if (ch == "c" || ch == "C")
|
||||||
|
return 12
|
||||||
|
else if (ch == "d" || ch == "D")
|
||||||
|
return 13
|
||||||
|
else if (ch == "e" || ch == "E")
|
||||||
|
return 14
|
||||||
|
else if (ch == "f" || ch == "F")
|
||||||
|
return 15
|
||||||
|
else
|
||||||
|
return 0
|
||||||
|
|
||||||
|
fun byte_from_hex_pair(this, hh)
|
||||||
|
hi = this.hex_val(substr(hh, 0, 1))
|
||||||
|
lo = this.hex_val(substr(hh, 1, 1))
|
||||||
|
return hi * 16 + lo
|
||||||
|
|
||||||
|
fun from_hex(this, hex)
|
||||||
|
arr = []
|
||||||
|
i = 0
|
||||||
|
n = len(hex)
|
||||||
|
while i + 1 < n
|
||||||
|
b = this.byte_from_hex_pair(substr(hex, i, 2))
|
||||||
|
push(arr, b)
|
||||||
|
i = i + 2
|
||||||
|
return arr
|
||||||
|
|
||||||
|
fun two_hex(this, n)
|
||||||
|
n = n % 256
|
||||||
|
d = ["0","1","2","3","4","5","6","7","8","9","a","b","c","d","e","f"]
|
||||||
|
hi = n / 16
|
||||||
|
lo = n % 16
|
||||||
|
parts = [d[hi], d[lo]]
|
||||||
|
return join(parts, "")
|
||||||
|
|
||||||
|
fun bytes_to_hex(this, arr)
|
||||||
|
i = 0
|
||||||
|
out = []
|
||||||
|
while i < len(arr)
|
||||||
|
push(out, this.two_hex(arr[i]))
|
||||||
|
i = i + 1
|
||||||
|
return join(out, "")
|
||||||
|
|
||||||
|
// padding (SHA-1): append 0x80, then zeros until length ≡ 56 (mod 64), then 64-bit length big-endian
|
||||||
|
fun pad_bytes(this, bytes)
|
||||||
|
L = len(bytes)
|
||||||
|
out = []
|
||||||
|
i = 0
|
||||||
|
while i < L
|
||||||
|
push(out, bytes[i])
|
||||||
|
i = i + 1
|
||||||
|
push(out, 128)
|
||||||
|
while (len(out) % 64) != 56
|
||||||
|
push(out, 0)
|
||||||
|
len_bits = L * 8
|
||||||
|
j = 7
|
||||||
|
while j >= 0
|
||||||
|
b = (len_bits / pow(2, 8 * (7 - j))) % 256
|
||||||
|
push(out, b)
|
||||||
|
j = j - 1
|
||||||
|
return out
|
||||||
|
|
||||||
|
fun word32_be(this, b0, b1, b2, b3)
|
||||||
|
return this.u32(b0 * 16777216 + b1 * 65536 + b2 * 256 + b3)
|
||||||
|
|
||||||
|
// process 512-bit block
|
||||||
|
fun process_block(this, state, block)
|
||||||
|
// W[80]
|
||||||
|
W = []
|
||||||
|
t = 0
|
||||||
|
while t < 16
|
||||||
|
j = t * 4
|
||||||
|
push(W, this.word32_be(block[j], block[j+1], block[j+2], block[j+3]))
|
||||||
|
t = t + 1
|
||||||
|
while t < 80
|
||||||
|
wt = bxor(bxor(bxor(W[t-3], W[t-8]), W[t-14]), W[t-16])
|
||||||
|
wt = this.rol32(wt, 1)
|
||||||
|
push(W, wt)
|
||||||
|
t = t + 1
|
||||||
|
|
||||||
|
a = state[0]
|
||||||
|
b = state[1]
|
||||||
|
c = state[2]
|
||||||
|
d = state[3]
|
||||||
|
e = state[4]
|
||||||
|
|
||||||
|
t = 0
|
||||||
|
while t < 80
|
||||||
|
if (t < 20)
|
||||||
|
f = bor(band(b, c), band(bnot(b), d))
|
||||||
|
k = 1518500249 // 0x5A827999
|
||||||
|
else if (t < 40)
|
||||||
|
f = bxor(b, bxor(c, d))
|
||||||
|
k = 1859775393 // 0x6ED9EBA1
|
||||||
|
else if (t < 60)
|
||||||
|
f = bor(bor(band(b, c), band(b, d)), band(c, d))
|
||||||
|
k = 2400959708 // 0x8F1BBCDC
|
||||||
|
else
|
||||||
|
f = bxor(b, bxor(c, d))
|
||||||
|
k = 3395469782 // 0xCA62C1D6
|
||||||
|
|
||||||
|
temp = this.u32(this.u32(this.rol32(a, 5) + f) + this.u32(this.u32(e + k) + W[t]))
|
||||||
|
e = d
|
||||||
|
d = c
|
||||||
|
c = this.rol32(b, 30)
|
||||||
|
b = a
|
||||||
|
a = temp
|
||||||
|
t = t + 1
|
||||||
|
|
||||||
|
state[0] = this.add32(state[0], a)
|
||||||
|
state[1] = this.add32(state[1], b)
|
||||||
|
state[2] = this.add32(state[2], c)
|
||||||
|
state[3] = this.add32(state[3], d)
|
||||||
|
state[4] = this.add32(state[4], e)
|
||||||
|
return state
|
||||||
|
|
||||||
|
fun sha1_bytes(this, bytes)
|
||||||
|
// initial H
|
||||||
|
H = [ 1732584193, 4023233417, 2562383102, 271733878, 3285377520 ]
|
||||||
|
data = this.pad_bytes(bytes)
|
||||||
|
N = len(data)
|
||||||
|
off = 0
|
||||||
|
while off < N
|
||||||
|
block = []
|
||||||
|
i = 0
|
||||||
|
while i < 64
|
||||||
|
push(block, data[off + i])
|
||||||
|
i = i + 1
|
||||||
|
H = this.process_block(H, block)
|
||||||
|
off = off + 64
|
||||||
|
|
||||||
|
// output 20-byte big-endian
|
||||||
|
out = []
|
||||||
|
j = 0
|
||||||
|
while j < 5
|
||||||
|
v = H[j]
|
||||||
|
push(out, (v / 16777216) % 256)
|
||||||
|
push(out, (v / 65536) % 256)
|
||||||
|
push(out, (v / 256) % 256)
|
||||||
|
push(out, v % 256)
|
||||||
|
j = j + 1
|
||||||
|
return out
|
||||||
|
|
||||||
|
fun sha1_hex(this, hexStr)
|
||||||
|
bytes = this.from_hex(hexStr)
|
||||||
|
digest = this.sha1_bytes(bytes)
|
||||||
|
return this.bytes_to_hex(digest)
|
||||||
|
|
||||||
|
// ASCII string to bytes (printable ASCII 0x20..0x7E)
|
||||||
|
fun string_to_bytes_ascii(this, 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
|
||||||
|
|
||||||
|
fun sha1_str(this, str)
|
||||||
|
bytes = this.string_to_bytes_ascii(str)
|
||||||
|
digest = this.sha1_bytes(bytes)
|
||||||
|
return this.bytes_to_hex(digest)
|
||||||
21
src/parser.c
21
src/parser.c
|
|
@ -1135,6 +1135,8 @@ static int emit_primary(Bytecode *bc, const char *src, size_t len, size_t *pos)
|
||||||
int gi = sym_index(name);
|
int gi = sym_index(name);
|
||||||
bytecode_add_instruction(bc, OP_LOAD_GLOBAL, gi);
|
bytecode_add_instruction(bc, OP_LOAD_GLOBAL, gi);
|
||||||
}
|
}
|
||||||
|
/* Track namespace alias only for the initial receiver; after any call it's no longer an alias value */
|
||||||
|
int __ns_ctx = is_ns_alias(name);
|
||||||
/* parse arguments */
|
/* parse arguments */
|
||||||
(*pos)++; /* '(' */
|
(*pos)++; /* '(' */
|
||||||
int argc = 0;
|
int argc = 0;
|
||||||
|
|
@ -1213,20 +1215,15 @@ static int emit_primary(Bytecode *bc, const char *src, size_t len, size_t *pos)
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* If base identifier is a namespace alias -> treat as plain function call: no implicit 'this' */
|
/* After a function call, the receiver is a value (not a namespace alias);
|
||||||
int is_ns = is_ns_alias(name);
|
always treat dot-call as a method with implicit 'this'. */
|
||||||
|
int is_ns = 0;
|
||||||
|
|
||||||
if (!is_ns) {
|
|
||||||
/* Method sugar with implicit 'this' */
|
/* Method sugar with implicit 'this' */
|
||||||
bytecode_add_instruction(bc, OP_DUP, 0);
|
bytecode_add_instruction(bc, OP_DUP, 0);
|
||||||
bytecode_add_instruction(bc, OP_LOAD_CONST, kci);
|
bytecode_add_instruction(bc, OP_LOAD_CONST, kci);
|
||||||
bytecode_add_instruction(bc, OP_INDEX_GET, 0); /* -> stack: obj, func */
|
bytecode_add_instruction(bc, OP_INDEX_GET, 0); /* -> stack: obj, func */
|
||||||
bytecode_add_instruction(bc, OP_SWAP, 0); /* -> stack: func, obj (this) */
|
bytecode_add_instruction(bc, OP_SWAP, 0); /* -> stack: func, obj (this) */
|
||||||
} else {
|
|
||||||
/* Plain property function call: obj["mname"] -> func */
|
|
||||||
bytecode_add_instruction(bc, OP_LOAD_CONST, kci);
|
|
||||||
bytecode_add_instruction(bc, OP_INDEX_GET, 0); /* -> stack: func */
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Consume '(' and parse args */
|
/* Consume '(' and parse args */
|
||||||
*pos = callp + 1;
|
*pos = callp + 1;
|
||||||
|
|
@ -1265,6 +1262,8 @@ static int emit_primary(Bytecode *bc, const char *src, size_t len, size_t *pos)
|
||||||
int gi = sym_index(name);
|
int gi = sym_index(name);
|
||||||
bytecode_add_instruction(bc, OP_LOAD_GLOBAL, gi);
|
bytecode_add_instruction(bc, OP_LOAD_GLOBAL, gi);
|
||||||
}
|
}
|
||||||
|
/* track namespace alias only for the initial receiver; after any call it's no longer an alias value */
|
||||||
|
int __ns_ctx = is_ns_alias(name);
|
||||||
/* postfix indexing, slice, and dot access/method calls */
|
/* postfix indexing, slice, and dot access/method calls */
|
||||||
for (;;) {
|
for (;;) {
|
||||||
skip_spaces(src, len, pos);
|
skip_spaces(src, len, pos);
|
||||||
|
|
@ -1318,7 +1317,8 @@ static int emit_primary(Bytecode *bc, const char *src, size_t len, size_t *pos)
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
int is_ns = is_ns_alias(name);
|
/* Use initial alias context for only the first dot-call; reset after call */
|
||||||
|
int is_ns = __ns_ctx;
|
||||||
|
|
||||||
if (!is_ns) {
|
if (!is_ns) {
|
||||||
/* Method sugar with implicit 'this' */
|
/* Method sugar with implicit 'this' */
|
||||||
|
|
@ -1345,6 +1345,9 @@ static int emit_primary(Bytecode *bc, const char *src, size_t len, size_t *pos)
|
||||||
if (!consume_char(src, len, pos, ')')) { parser_fail(*pos, "Expected ')' after arguments"); free(mname); free(name); return 0; }
|
if (!consume_char(src, len, pos, ')')) { parser_fail(*pos, "Expected ')' after arguments"); free(mname); free(name); return 0; }
|
||||||
|
|
||||||
bytecode_add_instruction(bc, OP_CALL, is_ns ? argc : (argc + 1));
|
bytecode_add_instruction(bc, OP_CALL, is_ns ? argc : (argc + 1));
|
||||||
|
/* After any call, the receiver is now a value, not a namespace alias */
|
||||||
|
__ns_ctx = 0;
|
||||||
|
|
||||||
free(mname);
|
free(mname);
|
||||||
continue;
|
continue;
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue