Skip to content

Commit 57f879e

Browse files
gh-43: Make FUNC declarations type-first.
1 parent 90d3b91 commit 57f879e

File tree

15 files changed

+126
-124
lines changed

15 files changed

+126
-124
lines changed

docs/SPECIFICATION.html

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -277,10 +277,9 @@
277277
278278
279279
## 5. Functions
280+
Functions are defined using the `FUNC` keyword with explicit parameter and return types. The canonical positional-only form is `FUNC R: name(T1: arg1, T2: arg2, ..., TN: argN){ block }`, where each `Tk` and `R` is `INT`, `FLT`, `STR`, `TNS`, or `FUNC`. Parameters MAY also declare a call-time default value using `Tk: arg = expr`. A parameter without a default is positional; a parameter with a default is keyword-capable. Positional parameters MUST appear before any parameters with defaults. Defining a function binds `name` to a callable body with the specified typed formal parameters. Function names MUST NOT conflict with the names of built-in operators or functions.
280281
281-
Functions are defined using the `FUNC` keyword with explicit parameter and return types. The canonical positional-only form is `FUNC name(T1: arg1, T2: arg2, ..., TN: argN):R{ block }`, where each `Tk` and `R` is `INT`, `FLT`, `STR`, `TNS`, or `FUNC`. Parameters MAY also declare a call-time default value using `Tk: arg = expr`. A parameter without a default is positional; a parameter with a default is keyword-capable. Positional parameters MUST appear before any parameters with defaults. Defining a function binds `name` to a callable body with the specified typed formal parameters. Function names MUST NOT conflict with the names of built-in operators or functions.
282-
283-
In addition to named functions, the language provides an anonymous function literal form `LAMBDA` which constructs a `FUNC` value without binding it to a function name in the global function table. The syntax is `LAMBDA(T1: arg1, T2: arg2, ..., TN: argN):R{ block }`. Parameter typing, default-value rules, call semantics, and return-type rules are the same as for `FUNC`. Evaluating a `LAMBDA` expression captures (closes over) the current lexical environment, producing a first-class `FUNC` value that can be assigned to variables, stored in tensors, passed as an argument, or returned.
282+
In addition to named functions, the language provides an anonymous function literal form `LAMBDA` which constructs a `FUNC` value without binding it to a function name in the global function table. The canonical form for lambdas is `LAMBDA R: (T1: arg1, T2: arg2, ..., TN: argN){ block }`. Parameter typing, default-value rules, call semantics, and return-type rules are the same as for `FUNC`. Evaluating a `LAMBDA` expression captures (closes over) the current lexical environment, producing a first-class `FUNC` value that can be assigned to variables, stored in tensors, passed as an argument, or returned.
284283
285284
A user-defined function is called with the same syntax as a built-in: `callee(expr1, expr2, ..., exprN)`. The callee MAY be any expression that evaluates to `FUNC`, including identifiers, tensor elements, or intermediate expressions. Calls MAY supply zero or more positional arguments (left-to-right) followed by zero or more keyword arguments of the form `param=expr`. Keyword arguments can only appear after all positional arguments. At the call site, every positional argument is bound to the next positional parameter; keyword arguments MUST match the name of a parameter that declared a default value. Duplicate keyword names, supplying too many positional arguments, or providing a keyword for an unknown parameter are runtime errors. If a keyword-capable parameter is omitted from the call, its default expression is evaluated at call time in the function's lexical environment after earlier parameters have been bound. The evaluated default MUST match the parameter's declared type. Arguments are evaluated left-to-right. The function body executes in a new environment (activation record) that closes over the defining environment. If a `RETURN(v)` statement is executed, the function terminates immediately and yields `v`; the returned value MUST match the declared return type. If control reaches the end of the body without `RETURN`, the function returns a default value of the declared return type (0 for `INT`, 0.0 for `FLT`, "" for `STR`). Functions whose return type is `TNS` or `FUNC` MUST execute an explicit `RETURN` of the declared type; reaching the end of the body without returning is a runtime error for `TNS`- or `FUNC`-returning functions.
286285

lib/csprng.pre

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -22,14 +22,14 @@ INT: ch_counter = 0
2222
TNS: ch_buf = TNS([10000], 0) ! 16 words
2323
INT: ch_buf_pos = TLEN(ch_buf, 1) ! set to buffer length to force refill
2424

25-
FUNC ROTL32(INT: x, INT: n):INT{
25+
FUNC INT: ROTL32(INT: x, INT: n){
2626
INT: x32 = BAND(x, MASK32)
2727
INT: left = BAND(SHL(x32, n), MASK32)
2828
INT: right = SHR(x32, SUB(100000, n)) ! 32 - n
2929
RETURN( BAND(BOR(left, right), MASK32) )
3030
}
3131

32-
FUNC QR(TNS: s, INT: ia, INT: ib, INT: ic, INT: id):INT{
32+
FUNC INT: QR(TNS: s, INT: ia, INT: ib, INT: ic, INT: id){
3333
INT: a = s[ia]
3434
INT: b = s[ib]
3535
INT: c = s[ic]
@@ -51,7 +51,7 @@ FUNC QR(TNS: s, INT: ia, INT: ib, INT: ic, INT: id):INT{
5151
RETURN(0)
5252
}
5353

54-
FUNC CHACHA_BLOCK():INT{
54+
FUNC INT: CHACHA_BLOCK(){
5555
TNS: k = ch_key
5656
TNS: n = ch_nonce
5757
TNS: state = [ ^
@@ -104,14 +104,14 @@ FUNC CHACHA_BLOCK():INT{
104104
RETURN(0)
105105
}
106106

107-
FUNC REFILL_BUF():INT{
107+
FUNC INT: REFILL_BUF(){
108108
CHACHA_BLOCK()
109109
ch_buf_pos = 0
110110
ch_counter = BAND( ADD(ch_counter, 1), MASK32 )
111111
RETURN(0)
112112
}
113113

114-
FUNC DERIVE_KEY_AND_NONCE(INT: seed):INT{
114+
FUNC INT: DERIVE_KEY_AND_NONCE(INT: seed){
115115
INT: s = BAND(seed, MASK32)
116116
FOR(i, 1000){ ! 8 key words (1000 == 8)
117117
s = BAND( ADD( MUL(s, ^
@@ -132,15 +132,15 @@ FUNC DERIVE_KEY_AND_NONCE(INT: seed):INT{
132132
RETURN(0)
133133
}
134134

135-
FUNC SEED(INT: seed):INT{
135+
FUNC INT: SEED(INT: seed){
136136
DERIVE_KEY_AND_NONCE(seed)
137137
ch_counter = 0
138138
ch_buf = TNS([10000], 0)
139139
ch_buf_pos = TLEN(ch_buf, 1) ! set to buffer length to force refill
140140
RETURN(ch_counter)
141141
}
142142

143-
FUNC NEXT():INT{
143+
FUNC INT: NEXT(){
144144
IF( GTE(ch_buf_pos, TLEN(ch_buf, 1)) ){ ! buffer length
145145
REFILL_BUF()
146146
}
@@ -149,12 +149,12 @@ FUNC NEXT():INT{
149149
RETURN(v)
150150
}
151151

152-
FUNC RANGE(INT: max):INT{
152+
FUNC INT: RANGE(INT: max){
153153
ASSERT( GT(max, 0) )
154154
RETURN( MOD(NEXT(), max) )
155155
}
156156

157-
FUNC RANGE_MIN_MAX(INT: min, INT: max):INT{
157+
FUNC INT: RANGE_MIN_MAX(INT: min, INT: max){
158158
ASSERT( LTE(min, max) )
159159
INT: range = SUB(max, min)
160160
IF( EQ(range, 0) ){ RETURN(min) }

lib/diff.pre

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,11 @@
55
! CONTEXT(modified: STR, source: STR):STR
66
! SIDE_BY_SIDE(modified: STR, source: STR):STR
77

8-
FUNC _append_line(STR: acc, STR: line):STR{
8+
FUNC STR: _append_line(STR: acc, STR: line){
99
IF( EQ(acc, "") ){ RETURN(line) } ELSE { RETURN( JOIN(acc, "\n", line) ) }
1010
}
1111

12-
FUNC UNIFIED(STR: mod, STR: src):STR{
12+
FUNC STR: UNIFIED(STR: mod, STR: src){
1313
TNS: a = SPLIT(mod, "\n")
1414
TNS: b = SPLIT(src, "\n")
1515
INT: la = TLEN(a, 1)
@@ -41,15 +41,15 @@ FUNC UNIFIED(STR: mod, STR: src):STR{
4141
RETURN(out)
4242
}
4343

44-
FUNC CONTEXT(STR: mod, STR: src):STR{
44+
FUNC STR: CONTEXT(STR: mod, STR: src){
4545
! For simplicity, use the same output as UNIFIED but with a ' ' prefix
4646
STR: u = UNIFIED(mod, src)
4747
IF( EQ(u, "") ){ RETURN("") }
4848
! Prepend a simple header to resemble context diff
4949
RETURN( JOIN("*** a", "\n", u) )
5050
}
5151

52-
FUNC SIDE_BY_SIDE(STR: mod, STR: src):STR{
52+
FUNC STR: SIDE_BY_SIDE(STR: mod, STR: src){
5353
TNS: a = SPLIT(mod, "\n")
5454
TNS: b = SPLIT(src, "\n")
5555
INT: la = TLEN(a, 1)

lib/gui/init.pre

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,18 +16,18 @@
1616
! WINDOW_WIDTH(INT: handle):INT
1717
! WINDOW_HEIGHT(INT: handle):INT
1818

19-
FUNC SCREEN_WIDTH():INT{
19+
FUNC INT: SCREEN_WIDTH(){
2020
RETURN(gui.SCREEN()[1])
2121
}
2222

23-
FUNC SCREEN_HEIGHT():INT{
23+
FUNC INT: SCREEN_HEIGHT(){
2424
RETURN(gui.SCREEN()[10])
2525
}
2626

27-
FUNC WINDOW_WIDTH(INT: handle):INT{
27+
FUNC INT: WINDOW_WIDTH(INT: handle){
2828
RETURN(gui.WINDOW(handle)[1])
2929
}
3030

31-
FUNC WINDOW_HEIGHT(INT: handle):INT{
31+
FUNC INT: WINDOW_HEIGHT(INT: handle){
3232
RETURN(gui.WINDOW(handle)[10])
3333
}

lib/image/init.pre

Lines changed: 24 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@
5050

5151
IMPORT(path)
5252

53-
FUNC LOAD(STR: img_path):TNS{
53+
FUNC TNS: LOAD(STR: img_path){
5454
STR: ext = path.EXTNAME(img_path)
5555
IF(EQ(ext, "png")){
5656
RETURN(image.LOAD_PNG(img_path))
@@ -63,67 +63,67 @@ FUNC LOAD(STR: img_path):TNS{
6363
}
6464
}
6565

66-
FUNC WIDTH(TNS: img):INT{
66+
FUNC INT: WIDTH(TNS: img){
6767
RETURN(TLEN(img, 1))
6868
}
6969

70-
FUNC HEIGHT(TNS: img):INT{
70+
FUNC INT: HEIGHT(TNS: img){
7171
RETURN(TLEN(img, 10))
7272
}
7373

74-
FUNC CHANNELS(TNS: img):INT{
74+
FUNC INT: CHANNELS(TNS: img){
7575
RETURN(TLEN(img, 11))
7676
}
7777

78-
FUNC R(TNS: img):TNS{
78+
FUNC TNS: R(TNS: img){
7979
RETURN(img[*, *, 1])
8080
}
8181

82-
FUNC G(TNS: img):TNS{
82+
FUNC TNS: G(TNS: img){
8383
RETURN(img[*, *, 10])
8484
}
8585

86-
FUNC B(TNS: img):TNS{
86+
FUNC TNS: B(TNS: img){
8787
RETURN(img[*, *, 11])
8888
}
8989

90-
FUNC A(TNS: img):TNS{
90+
FUNC TNS: A(TNS: img){
9191
RETURN(img[*, *, 100])
9292
}
9393

94-
FUNC PIXEL(TNS: img, INT: x, INT: y):TNS{
94+
FUNC TNS: PIXEL(TNS: img, INT: x, INT: y){
9595
RETURN(img[x,y,*])
9696
}
9797

98-
FUNC PIXEL_R(TNS: img, INT: x, INT: y):INT{
98+
FUNC INT: PIXEL_R(TNS: img, INT: x, INT: y){
9999
RETURN(img[x,y,1])
100100
}
101101

102-
FUNC PIXEL_G(TNS: img, INT: x, INT: y):INT{
102+
FUNC INT: PIXEL_G(TNS: img, INT: x, INT: y){
103103
RETURN(img[x,y,10])
104104
}
105105

106-
FUNC PIXEL_B(TNS: img, INT: x, INT: y):INT{
106+
FUNC INT: PIXEL_B(TNS: img, INT: x, INT: y){
107107
RETURN(img[x,y,11])
108108
}
109109

110-
FUNC PIXEL_A(TNS: img, INT: x, INT: y):INT{
110+
FUNC INT: PIXEL_A(TNS: img, INT: x, INT: y){
111111
RETURN(img[x,y,100])
112112
}
113113

114-
FUNC FLIP_V(TNS: img):TNS{
114+
FUNC TNS: FLIP_V(TNS: img){
115115
RETURN(TFLIP(img, 1))
116116
}
117117

118-
FUNC FLIP_H(TNS: img):TNS{
118+
FUNC TNS: FLIP_H(TNS: img){
119119
RETURN(TFLIP(img, 10))
120120
}
121121

122-
FUNC INVERT(TNS: img):TNS{
122+
FUNC TNS: INVERT(TNS: img){
123123
RETURN(MSUB(TNS(SHAPE(img), 111111111), img))
124124
}
125125

126-
FUNC RECT(TNS: img, INT: x, INT: y, INT: width, INT: height, TNS: color, INT: fill, INT: thickness):TNS{
126+
FUNC TNS: RECT(TNS: img, INT: x, INT: y, INT: width, INT: height, TNS: color, INT: fill, INT: thickness){
127127
TNS: pts = [ ^
128128
[x, y], ^
129129
[ADD(x, SUB(width, 1)), y], ^
@@ -134,34 +134,34 @@ FUNC RECT(TNS: img, INT: x, INT: y, INT: width, INT: height, TNS: color, INT: fi
134134
RETURN(image.POLYGON(img, pts, color, fill, thickness))
135135
}
136136

137-
FUNC RECTANGLE(TNS: img, INT: x, INT: y, INT: width, INT: height, TNS: color, INT: fill, INT: thickness):TNS{
137+
FUNC TNS: RECTANGLE(TNS: img, INT: x, INT: y, INT: width, INT: height, TNS: color, INT: fill, INT: thickness){
138138
RETURN(RECT(img, x, y, width, height, color, fill, thickness))
139139
}
140140

141-
FUNC FILL_RECT(TNS: img, INT: x, INT: y, INT: width, INT: height, TNS: color):TNS{
141+
FUNC TNS: FILL_RECT(TNS: img, INT: x, INT: y, INT: width, INT: height, TNS: color){
142142
RETURN(RECT(img, x, y, width, height, color, 1, 1))
143143
}
144144

145-
FUNC FILL_ELLIPSE(TNS: img, TNS: center, INT: rx, INT: ry, TNS: color):TNS{
145+
FUNC TNS: FILL_ELLIPSE(TNS: img, TNS: center, INT: rx, INT: ry, TNS: color){
146146
RETURN(image.ELLIPSE(img, center, rx, ry, color, 1, 1))
147147
}
148148

149-
FUNC SQUARE(TNS: img, INT: x, INT: y, INT: size, TNS: color, INT: fill, INT: thickness):TNS{
149+
FUNC TNS: SQUARE(TNS: img, INT: x, INT: y, INT: size, TNS: color, INT: fill, INT: thickness){
150150
RETURN(RECT(img, x, y, size, size, color, fill, thickness))
151151
}
152152

153-
FUNC CIRCLE(TNS: img, TNS: center, INT: radius, TNS: color, INT: fill, INT: thickness):TNS{
153+
FUNC TNS: CIRCLE(TNS: img, TNS: center, INT: radius, TNS: color, INT: fill, INT: thickness){
154154
RETURN(image.ELLIPSE(img, center, radius, radius, color, fill, thickness))
155155
}
156156

157-
FUNC CROP(TNS: img, TNS: corners):TNS{
157+
FUNC TNS: CROP(TNS: img, TNS: corners){
158158
IF(NOT(EQ(SHAPE(corners), [100, 10]))){
159159
THROW("CROP corners must be a 100x10 tensor: [[tl_x, tl_y], [tr_x, tr_y], [bl_x, bl_y], [br_x, br_y]]")
160160
}
161161
RETURN(img[MIN(corners[*, 1])-MAX(corners[*, 1]), MIN(corners[*, 10])-MAX(corners[*, 10]), *])
162162
}
163163

164-
FUNC SHOW(TNS: img):INT{
164+
FUNC INT: SHOW(TNS: img){
165165
! Save the image to the provided path and open it with the system default
166166
! viewer on Windows. `img_path` is treated as the target file path.
167167
STR: img_path = "C:/Windows/Temp/tmp_img.png"

lib/numbers.pre

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ MAP: digits = < ^
7373

7474
MAP: rev = INV(digits) ! reverse mapping of digits
7575

76-
FUNC VALUE(STR: num, INT: base):INT{
76+
FUNC INT: VALUE(STR: num, INT: base){
7777
IF(GT(base, 1000000)){
7878
THROW("Base ", STR(base), " not supported. Max base is 0b1000000")
7979
}
@@ -112,7 +112,7 @@ FUNC VALUE(STR: num, INT: base):INT{
112112
POP(value)
113113
}
114114

115-
FUNC CONVERT(STR: num, INT: from, INT: to):STR{
115+
FUNC STR: CONVERT(STR: num, INT: from, INT: to){
116116
! Support optional fractional part (radix point) without breaking
117117
! existing integer-only behavior. If `num` contains a '.', split
118118
! into integer and fractional parts and convert each appropriately.

lib/path.pre

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -12,15 +12,15 @@
1212
! STR: script
1313
! STR: script_dir
1414

15-
FUNC NORMALIZE_PATH(STR: path):STR{
15+
FUNC STR: NORMALIZE_PATH(STR: path){
1616
RETURN(REPLACE(path,"\\","/"))
1717
}
1818

19-
FUNC WINPATH(STR: path):STR{
19+
FUNC STR: WINPATH(STR: path){
2020
RETURN(REPLACE(path,"/","\\"))
2121
}
2222

23-
FUNC BASEPATH(STR: path):STR{ ! returns directory portion of path
23+
FUNC STR: BASEPATH(STR: path){ ! returns directory portion of path
2424
STR: npath = NORMALIZE_PATH(path)
2525
STR: base = BASENAME(npath)
2626
IF(EQ(base, "")){
@@ -30,14 +30,14 @@ FUNC BASEPATH(STR: path):STR{ ! returns directory portion of path
3030
POP(dir)
3131
}
3232

33-
FUNC BASENAME(STR: path):STR{ ! returns filename portion of path
33+
FUNC STR: BASENAME(STR: path){ ! returns filename portion of path
3434
TNS: tpath = SPLIT(NORMALIZE_PATH(path), "/")
3535
STR: basename = tpath[MAX(TLEN(tpath,1),1)]
3636
DEL(tpath)
3737
POP(basename)
3838
}
3939

40-
FUNC SPLITEXT(STR: path):TNS{ ! returns [name without ext, ext]
40+
FUNC TNS: SPLITEXT(STR: path){ ! returns [name without ext, ext]
4141
STR: npath = NORMALIZE_PATH(path)
4242
STR: base = BASENAME(npath)
4343
STR: dir = BASEPATH(npath)
@@ -69,15 +69,15 @@ FUNC SPLITEXT(STR: path):TNS{ ! returns [name without ext, ext]
6969
POP(result)
7070
}
7171

72-
FUNC EXTNAME(STR: path):STR{ ! returns extension portion of path
72+
FUNC STR: EXTNAME(STR: path){ ! returns extension portion of path
7373
RETURN(SPLITEXT(path)[10])
7474
}
7575

76-
FUNC DELEXT(STR: path):STR{ ! returns path without extension
76+
FUNC STR: DELEXT(STR: path){ ! returns path without extension
7777
RETURN(SPLITEXT(path)[1])
7878
}
7979

80-
FUNC TEMPFILE(STR: local_path):STR{
80+
FUNC STR: TEMPFILE(STR: local_path){
8181
IF(EQ(OS(),"win")){
8282
STR: temp = "C:/Windows/Temp/"
8383
} ELSEIF(EQ(OS(),"mac")){

lib/prime.pre

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
! IS_MERSENNE_PRIME(INT: p) -> INT
88
! FACTOR(INT: n) -> TNS
99

10-
FUNC IS_PRIME(INT: n):INT{
10+
FUNC INT: IS_PRIME(INT: n){
1111
IF(LTE(n, 1)){
1212
RETURN(0)
1313
}
@@ -28,7 +28,7 @@ FUNC IS_PRIME(INT: n):INT{
2828
RETURN(1)
2929
}
3030

31-
FUNC NEXT_PRIME(INT: start):INT{
31+
FUNC INT: NEXT_PRIME(INT: start){
3232
INT: n = ADD(start, 1)
3333
WHILE(1){
3434
IF(IS_PRIME(n)){
@@ -38,7 +38,7 @@ FUNC NEXT_PRIME(INT: start):INT{
3838
}
3939
}
4040

41-
FUNC PREV_PRIME(INT: start):INT{
41+
FUNC INT: PREV_PRIME(INT: start){
4242
INT: n = SUB(start, 1)
4343
WHILE(GT(n, 1)){
4444
IF(IS_PRIME(n)){
@@ -49,7 +49,7 @@ FUNC PREV_PRIME(INT: start):INT{
4949
RETURN(0)
5050
}
5151

52-
FUNC IS_MERSENNE_PRIME(INT: p):INT{
52+
FUNC INT: IS_MERSENNE_PRIME(INT: p){
5353
IF(IS_PRIME(p)){
5454
INT: exp = POW(10, p)
5555
INT: mersenne = SUB(exp, 1)
@@ -62,7 +62,7 @@ FUNC IS_MERSENNE_PRIME(INT: p):INT{
6262
}
6363
}
6464

65-
FUNC FACTOR(INT: n):TNS{
65+
FUNC TNS: FACTOR(INT: n){
6666
IF(LTE(n, 1)){
6767
TNS: empty = [0]
6868
RETURN(empty)

0 commit comments

Comments
 (0)