-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathfull-spec.ktg
More file actions
1971 lines (1641 loc) · 61.8 KB
/
full-spec.ktg
File metadata and controls
1971 lines (1641 loc) · 61.8 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
Kintsugi [
name: 'full-spec
date: 2026-03-29
file: %script-spec.ktg
version: 0.4.0
]
; ============================================================
; Learn Kintsugi in Y Minutes
; ============================================================
;
; Kintsugi is a homoiconic, dynamically-typed programming
; language with rich built-in datatypes, preprocessing,
; and a number of useful facilities powered by DSLs called
; "dialects".
;
; Influenced by REBOL, Red, Ren-C, Common Lisp, D, Python,
; Kotlin, and Raku.
;
; Headers:
; Kintsugi [...] - marks a file as an entrypoint (with metadata)
; (no header) - module (loaded via import)
;
; The header determines prelude inclusion when compiling.
; Compilation is always explicit via -c flag.
;
; File extensions:
; .ktg - For all source files.
;
; ============================================================
; ============================================================
; DATATYPES
; ============================================================
; Kintsugi has many built-in datatypes. Every value carries
; its type at runtime. Type names are suffixed with !
; --- Scalars ---
my-int: 42 ; integer!
negative: -7 ; integer!
my-float: 3.14 ; float!
my-pair: 100x200 ; pair! (2D coordinates)
my-version: 1.2.3 ; tuple! (versions, colors, IPs)
; --- Logic ---
; true and false are literals. on/off/yes/no are words bound
; to logic values -- available as aliases in normal code, but
; usable as keywords inside dialects without collision.
a: true ; logic!
b: false
c: on ; same as true
d: off ; same as false
e: yes ; same as true
f: no ; same as false
; --- None ---
empty: none ; none! (null/nil)
; --- Temporal ---
my-date: 2026-03-15 ; date!
my-time: 14:30:00 ; time!
; --- Text ---
my-string: "Hello, world" ; string!
escaped: "Line one\nLine two" ; \n is newline escape
; --- Identifiers & Resources ---
my-file: %path/to/file.txt ; file!
my-url: https://example.com/api ; url!
my-email: [email protected] ; email!
; --- Money ---
; money! is a precise numeric type stored as integer cents.
; No float drift -- $0.10 + $0.20 is exactly $0.30.
; Negative money displays with leading minus: -$0.50, -$19.99
my-money: $19.99 ; money! (1999 cents)
probe $0.00 - $0.50 ; -$0.50 (sign preserved)
; --- Composite ---
my-block: [1 2 "three" 4.0] ; block! (the universal container)
nested: [[1 2] [3 4] [5 6]] ; blocks nest freely
code-as-data: [print "I am data"] ; code is just a block
my-paren: (1 + 2) ; paren! evaluates immediately: 3
; --- Internal types ---
; These are not constructed directly in user code, but
; are returned by type and used internally:
; native! -- built-in function
; op! -- operator (infix symbol)
; type! -- type name value (integer!, string!, etc.)
; context! -- mutable key-value scope
; object! -- frozen object with typed field specs
; set! -- unordered unique collection
; map! -- hash-based key-value store
; ============================================================
; VARIABLES & WORDS
; ============================================================
; Words are the primary identifiers. A prefix or suffix
; controls how the word evaluates.
;
; word - evaluate / lookup
; word: - set-word (bind a value)
; :word - get-word (get without calling)
; 'word - lit-word (the symbol itself)
; @word - meta-word (lifecycle hooks / metamethods)
;
; The same system applies to paths:
; path/to - evaluate / lookup
; path/to: - set-path (assign through path)
; path/:var - dynamic index (evaluate var, use as index)
; :path/to - get-path (get without calling)
; 'path/to - lit-path (the path as data)
name: "Kintsugi" ; set-word (bind a value)
print name ; word (evaluate / lookup)
ref: :name ; get-word (get without calling)
sym: 'name ; lit-word (the symbol itself)
; Words can contain letters, digits, hyphens, ?, !, _, ~
is-valid?: true
my-variable!: 42
; --- Sigils ---
; Every prefix, suffix, and punctuation character in Kintsugi
; has a fixed meaning. Nothing is arbitrary.
;
; Word prefixes (change how the word evaluates):
; : - get-word (retrieve value without calling)
; ' - lit-word (the symbol itself, unevaluated)
; @ - meta-word (lifecycle hooks, runtime metadata)
;
; Word suffixes (naming conventions enforced by type):
; : - set-word (bind a value)
; ! - type name (integer!, string!, user!, etc.)
; ? - predicate (returns logic! -- empty?, none?, odd?)
;
; Operators:
; = - value equality (cross-type for numbers)
; == - strict equality (same type and same value)
; <> - not-equal
;
; Other sigils:
; % - file path (%path/to/file.txt)
; $ - money ($19.99)
; / - path separator or refinement (obj/field, func/refine)
; ============================================================
; EVALUATION MODEL & ARITHMETIC
; ============================================================
; Kintsugi evaluates left to right with no operator precedence.
; Use parens to control evaluation order. Operators are just
; infix functions -- none is special.
x: 10
y: 3
print x + y ; 13
print x - y ; 7
print x * y ; 30
print x % y ; 1
print 2 + 3 * 4 ; 20 (left to right, NOT 14)
print 2 + (3 * 4) ; 14 (parens force order)
; --- Division ---
; Division returns integer when exact, float otherwise.
print 10 / 2 ; 5 (integer! -- exact)
print 10 / 3 ; 3.333... (float! -- inexact)
; Use round for explicit rounding
print round/down 10 / 3 ; 3 (truncate toward zero)
print round 3.7 ; 4 (nearest)
print round/up 3.2 ; 4 (away from zero)
; --- Comparisons ---
print 5 > 3 ; true
print 5 < 3 ; false
print 5 = 5 ; true
print 5 <> 3 ; true
print 5 >= 5 ; true
print 3 <= 5 ; true
; --- Numeric cross-type ---
print 42 = 42.0 ; true (= compares by value across types)
print 42 == 42.0 ; false (== is strict -- requires same type)
print 42 == 42 ; true (same type, same value)
; --- Logic ---
; and/or are infix operators. They do NOT short-circuit --
; both sides are always evaluated because the evaluator must
; consume the right operand.
; Only false and none are falsy. 0, "", and [] are truthy.
print not 1 = 2 ; true
print 1 = 1 and 2 = 2 ; true
print 1 = 2 or 2 = 2 ; true
; all?/any? -- block-based short-circuit predicates
; all? returns true if every expression is truthy, false otherwise.
; any? returns true if at least one is truthy, false otherwise.
print all? [1 2 3] ; true
print all? [1 false 3] ; false
print any? [false none 42] ; true
; --- Money arithmetic ---
print $10.00 + $5.50 ; $15.50
print $10.00 - $3.25 ; $6.75
print $10.00 * 3 ; $30.00
print $10.00 / 4 ; $2.50
print $10.00 > $5.00 ; true
; --- Pair arithmetic ---
print 100x200 + 10x20 ; 110x220
print 100x200 - 10x20 ; 90x180
; --- String concatenation ---
; Use rejoin to concatenate strings. + on strings is an error.
print rejoin ["hello" " world"] ; hello world
; ============================================================
; CONTROL FLOW
; ============================================================
; if -- evaluates block when condition is truthy
print if true [42] ; 42
; either -- two branches, always returns a value
sign: function [n] [
either n > 0 [1] [
either n < 0 [-1] [0]
]
]
print sign 5 ; 1
print sign -3 ; -1
print sign 0 ; 0
; unless -- negated if
print unless false ["ran"] ; ran
; --- Loops ---
; loop is the only looping construct. See LOOP DIALECT below.
; For conditional loops, use loop with if/break:
; "while" pattern
n: 1
factorial: 1
loop [
if n > 5 [break]
factorial: factorial * n
n: n + 1
]
print factorial ; 120
; "until" pattern
counter: 0
loop [
counter: counter + 1
if counter = 10 [break]
]
print counter ; 10
; ============================================================
; FUNCTIONS
; ============================================================
; The spec block inside 'function' is a dialect. Words,
; type blocks, refinements, and return: have special meaning.
;
; Type annotations are enforced at runtime -- passing the
; wrong type raises a type error. A future compiler can
; trust these annotations for static optimization.
; Basic definition
add: function [a b] [a + b]
print add 3 4 ; 7
; Type constraints (enforced at call time)
add-typed: function [a [integer!] b [integer!] return: [integer!]] [
a + b
]
print add-typed 3 4 ; 7
; add-typed "hi" 3 ; ERROR: a expects integer!, got string!
; Typesets for polymorphism
double: function [value [number!]] [
value * 2
]
print double 5 ; 10
print double 2.5 ; 5.0
; Refinements add optional behavior
greet: function [name [string!] /loud] [
message: join "Hello, " name
if loud [message: uppercase message]
message
]
print greet "world" ; Hello, world
print greet/loud "world" ; HELLO, WORLD
; Refinements with parameters
format-val: function [val /pad size [integer!]] [
either pad [
rejoin [to string! val " (padded to " to string! size ")"]
] [
to string! val
]
]
print format-val 42 ; 42
print format-val/pad 42 10 ; 42 (padded to 10)
; Multiple refinements
announce: function [msg [string!] /loud /prefix tag [string!]] [
result: msg
if prefix [result: join tag result]
if loud [result: uppercase result]
result
]
print announce "hello" ; hello
print announce/loud "hello" ; HELLO
print announce/prefix "hello" "[!] " ; [!] hello
; Inactive refinements default to false, params to none
check-ref: function [/active] [active]
print check-ref ; false
print check-ref/active ; true
; Functions are first-class values
operation: :add ; get-word grabs the function value
print operation 3 4 ; 7
; Higher-order -- pass functions with get-word
print apply :add [3 4] ; 7
; Early return
clamp: function [val lo hi] [
if val < lo [return lo]
if val > hi [return hi]
val
]
print clamp 5 1 10 ; 5
print clamp -3 1 10 ; 1
print clamp 15 1 10 ; 10
; Closures capture their defining context
make-adder: function [n] [function [x] [x + n]]
add5: make-adder 5
print add5 10 ; 15
; function with no parameters
greet-world: function [] [print "Hello, world!"]
greet-world ; Hello, world!
; does -- shorthand for function [] [body]
say-hi: does [print "hi"]
say-hi ; hi
; ============================================================
; LOOP DIALECT
; ============================================================
; loop is the only looping construct. The block inside loop
; is a dialect when it starts with 'for' or 'from'.
; Otherwise, the entire block is the body (infinite loop).
;
; Refinements:
; loop - side effects only, returns none
; loop/collect - gathers body results into a block
; loop/fold - accumulates: first var is accumulator
; loop/partition - body is predicate; values go into
; [truthy falsy] based on result
;
; Dialect keywords:
; for - declare iteration variable(s)
; in - the data to iterate over
; from - counting start value
; to - counting end value
; by - step value (default 1 or -1 based on direction)
; when - guard clause (filter)
; do - required before body block
; it - implicit variable when for is omitted
; --- Basic iteration ---
loop [for [item] in [1 2 3 4 5] do [print item]]
; Multi-line:
loop [
for [item] in [6 7 8]
do [print item]
]
; --- Iterating strings ---
; for/in works on strings too, yielding one character per iteration.
loop [for [c] in "abc" do [print c]]
; a
; b
; c
; --- Counting ---
loop [for [n] from 1 to 5 do [print n]]
; 1
; 2
; 3
; 4
; 5
; Counting with step
loop [from 0 to 10 by 5 do [print it]]
; 0
; 5
; 10
; Counting down (direction inferred)
loop [from 5 to 1 do [print it]]
; 5
; 4
; 3
; 2
; 1
; --- Collecting ---
squares: loop/collect [for [n] from 1 to 5 do [n * n]]
probe squares ; 1 4 9 16 25
; --- Filtering ---
evens: loop/collect [
for [n] in [1 2 3 4 5 6 7 8]
when [even? n]
do [n]
]
probe evens ; 2 4 6 8
; --- Folding ---
total: loop/fold [for [acc n] from 1 to 10 do [acc + n]]
print total ; 55
; --- Partitioning ---
set [evens odds] loop/partition [for [x] from 1 to 8 do [even? x]]
probe evens ; 2 4 6 8
probe odds ; 1 3 5 7
; --- Break inside loop ---
partial: loop/collect [
for [x] from 1 to 5 do [
if x = 4 [break]
x * 10
]
]
probe partial ; 10 20 30
; --- Loop variables are scoped ---
; After the loop, iteration variables do not exist in the
; parent scope. This prevents accidental leaks.
; Use loop/fold or loop/collect to get values out.
; loop [for [i] from 1 to 5 do [i]]
; print i ; ERROR: i has no value
; ============================================================
; MATCH DIALECT
; ============================================================
; Pattern matching with destructuring. First match wins.
;
; Pattern elements:
; literal - matches exactly (integers, strings, etc.)
; bare word - captures/binds the value at this position
; type! - matches any value of that type
; (expr) - evaluate expression, match against result
; 'word - matches the literal word
; _ - wildcard (matches anything, doesn't bind)
; when - guard clause
; default - fallback if nothing matched
match [1 2 3] [
[1 2 3] [print "got 1 2 3"]
[1 _ 3] [print "1, anything, 3"]
default [print "nothing matched"]
]
; got 1 2 3
; Destructuring
match [10 20] [
[0 0] [print "origin"]
[x 0] [print rejoin ["x-axis at " x]]
[0 y] [print rejoin ["y-axis at " y]]
[x y] [print rejoin [x ", " y]]
]
; 10, 20
; Guards
match 25 [
[n] when [n < 13] [print "child"]
[n] when [n < 20] [print "teenager"]
[n] when [n < 65] [print "adult"]
[_] [print "senior"]
]
; adult
; ============================================================
; SERIES OPERATIONS
; ============================================================
; Blocks and strings are both "series" types. Many operations
; work on both. Indices are 1-based.
; --- Block operations ---
data: [10 20 30 40 50]
print length data ; 5
print empty? [] ; true
print pick data 3 ; 30
print first data ; 10
print second data ; 20
print last data ; 50
; Membership and lookup
print has? data 30 ; true
print find data 30 ; 3 (1-based)
print has? "hello world" "world" ; true
; Select -- key-value lookup in flat blocks
config: [width 800 height 600 depth 32]
print select config 'width ; 800
print select config 'depth ; 32
; Mutation (blocks are mutable, shared by reference)
buf: [1 2 3]
append buf 4 ; [1 2 3 4]
insert buf 0 1 ; insert 0 at position 1
remove buf 1 ; remove at position 1
print length buf ; 4
; Copy creates an independent block
original: [1 2 3]
clone: copy original
append clone 99
print length original ; 3 (unaffected)
; Sort mutates in place
nums: [3 1 4 1 5 9 2 6]
sort nums
print first nums ; 1
print last nums ; 9
; Sort also works on strings (returns a new sorted string)
print sort "hello" ; ehllo
; insert and remove on strings return new strings (strings are immutable)
print insert "helo" "l" 4 ; hello
print remove "hello" 2 ; hllo
; Multiple assignment (block destructuring)
set [a b c] [1 2 3]
print a ; 1
print b ; 2
print c ; 3
; Context destructuring (by name, not position)
ctx: context [name: "Ray" age: 30]
set [name age] ctx
print name ; Ray
print age ; 30
; @rest captures remaining elements into a block
set [first-item @rest] [10 20 30 40 50]
print first-item ; 10
probe rest ; [20 30 40 50]
; --- capture: declarative keyword extraction ---
; capture scans a block for keywords defined by meta-words in a schema.
; @name — greedy: captures everything until next keyword
; @name/N — exact: captures N values after keyword
parts: capture [source [42] then [it + 1] then [it * 2] retries 3] [
@source @then @retries
]
print parts/source ; [42]
probe parts/then ; [[it + 1] [it * 2]]
print parts/retries ; 3
; capture enables user-defined dialects — a function that takes
; a block and interprets its keywords:
;
; entity: function [spec [block!]] [
; parts: capture spec [@name @hp @attack @defense]
; ctx: context []
; ctx/name: parts/name
; ctx/hp: parts/hp
; ctx/max-hp: parts/hp
; ctx
; ]
; warrior: entity [name "Warrior" hp 100 attack 15 defense 10]
; Dynamic path indexing -- use :var in a path to evaluate the variable
items: [10 20 30]
i: 2
print items/:i ; 20
; Dynamic set-path
items/:i: 99
print items/:i ; 99
; Works with nested paths
board: context [cells: [1 2 3]]
pos: 3
print board/cells/:pos ; 3
; For computed indices, use pick
print pick items (i + 1) ; 30
; Reduce -- evaluate a block, return results as a block
x: 10
result: reduce [x + 1 x * 2 "hello"]
print first result ; 11
print second result ; 20
; --- String operations ---
; Strings are immutable. Double-quoted. Multiline strings
; use curly braces. Concatenation uses join (two values) or
; rejoin (block of values).
; --- Multiline strings ---
message: {
Hello,
This is a multiline string.
It preserves line breaks.
}
; --- Interpolation via rejoin ---
; rejoin evaluates a block and joins all results into a string.
name: "Ray"
age: 30
print rejoin ["Hello, " name "!"] ; Hello, Ray!
print rejoin [name " is " age " years old"] ; Ray is 30 years old
print rejoin ["2 + 2 = " (2 + 2)] ; 2 + 2 = 4
; rejoin/with — join with a delimiter
print rejoin/with [1 2 3 4 5] ", " ; 1, 2, 3, 4, 5
print rejoin/with ["a" "b" "c"] " - " ; a - b - c
; --- String functions ---
print length "hello" ; 5
print join "hello" " world" ; hello world
print trim " hello " ; hello
print find "hello world" "world" ; 7
print has? "hello world" "world" ; true
print split "a,b,c" "," ; ["a" "b" "c"]
print uppercase "hello" ; HELLO
print lowercase "HELLO" ; hello
print replace "hello world" "world" "Ray" ; hello Ray
print replace "aabaa" "a" "x" ; xxbxx (replace-all by default)
; --- Series operations on strings ---
; first, second, last, pick, find, reverse work on both
; blocks and strings. They are the unified series interface.
print first "hello" ; h
print second "hello" ; e
print last "hello" ; o
print pick "hello" 3 ; l
; find -- returns 1-based index or none
print find "hello world" "world" ; 7
print find "hello" "xyz" ; none
print find [10 20 30] 20 ; 2
; find/where -- first element matching a predicate
print find/where [1 2 3 4 5] function [x] [x > 3] ; 4
; reverse -- returns new value
print reverse "hello" ; olleh
print reverse [1 2 3] ; [3 2 1]
; append -- mutates blocks in place. Splices by default.
; Strings are immutable — use rejoin or join to build new strings:
; rejoin ["hello" " " "world"] → "hello world"
; join "hello" " world" → "hello world"
spec-blk: [1 2]
append spec-blk [3 4] ; splice: [1 2 3 4]
append/only spec-blk [5 6] ; single element: [1 2 3 4 [5 6]]
; subset extracts a range (1-based start, length). Works on strings and blocks.
print subset "hello world" 7 5 ; world
probe subset [10 20 30 40 50] 2 3 ; [20 30 40]
; --- String predicates ---
print starts-with? "hello world" "hello" ; true
print ends-with? "hello world" "world" ; true
print starts-with? "hello" "xyz" ; false
; --- Escape sequences ---
; \n = newline, \" = quote, \\ = backslash, \t = tab
print "line one\nline two"
; --- Padding ---
; pad fills to a width. Left-pad by default, /right for right-pad.
; Works on strings and blocks.
print pad "42" 5 "0" ; 00042
print pad/right "hi" 6 "." ; hi....
probe pad [1 2] 5 0 ; [0 0 0 1 2]
probe pad/right [1] 4 none ; [1 none none none]
; --- Character codes ---
; byte: string -> integer (first byte's code)
; char: integer -> string (0-255 range)
print byte "A" ; 65
print char 65 ; A
print char (byte "Z") ; Z
; ============================================================
; DATE & TIME FACILITIES
; ============================================================
; now returns the current Unix epoch as integer!.
; time and date are context objects with accessor functions.
;
; now -- integer! (Unix epoch seconds)
; time/now -- time! (current local time)
; date/now -- date! (current local date)
; time/hours t -- integer! (hour component)
; time/minutes t -- integer! (minute component)
; time/seconds t -- integer! (second component)
; time/to-seconds t -- integer! (total seconds since midnight)
; time/from-epoch n -- time! (time from epoch seconds)
; date/from-epoch n -- date! (date from epoch seconds)
; date/year d -- integer! (year component)
; date/month d -- integer! (month 1-12)
; date/day d -- integer! (day of month)
; Live examples:
epoch: now
print type epoch ; integer!
t: time/now
print type t ; time!
print time/hours t ; (current hour)
print time/minutes t ; (current minute)
d: date/now
print type d ; date!
print date/year d ; (current year)
; ============================================================
; COLLECTIONS: MAP & SET
; ============================================================
; --- Map ---
; map! is a hash-based key-value store for fast lookup.
; Create via make
m: make map! [name: "Ray" age: 30]
; Access via path
print m/name ; Ray
print m/age ; 30
; Predicates
print has? m "name" ; true
print length m ; 2
; --- Set & Charset ---
; set! is an unordered unique collection. charset creates a
; set! from a string of characters (custom character classes
; for use in parse).
vowels: charset "aeiou"
consonants: charset "bcdfghjklmnpqrstvwxyz"
alphanumeric: union (charset "abcdefghijklmnopqrstuvwxyz") (charset "0123456789")
; Sets work with parse rules as character classes.
; result: @parse "hello" [some vowels | some consonants]
; union and intersect combine sets
combined: union vowels consonants
; overlap: intersect (charset "abc") (charset "bcd")
; ============================================================
; TYPE SYSTEM
; ============================================================
; Gradual typing. No implicit coercion. Three operations:
;
; is? type! value -- check (returns logic!)
; to type! value -- convert (scalars only, throws on failure)
; [param [type!]] -- constrain (in function specs)
;
; Type annotations on function params are enforced at runtime.
; --- @const annotation ---
; @const marks a binding as constant. In Script mode, it behaves
; as a normal assignment. In Lua 5.4, it emits the <const> attribute.
; @const MAX-SPEED: 100
; ; In Lua 5.4: local MAX_SPEED <const> = 100
@const MAX-SPEED: 100
print MAX-SPEED ; 100
; --- Introspection ---
print type 42 ; integer!
print type "hello" ; string!
print type [1 2 3] ; block!
; --- Type predicates ---
; Every type has a predicate (name?), generated from type name.
print integer? 42 ; true
print float? 3.14 ; true
print string? "hello" ; true
print logic? true ; true
print none? none ; true
print block? [1 2 3] ; true
print pair? 100x200 ; true
print tuple? 1.2.3 ; true
print date? 2026-03-15 ; true
print time? 14:30:00 ; true
print file? %path/to/file ; true
print url? https://example.com ; true
print email? [email protected] ; true
print money? $19.99 ; true
; function? returns true for both function! and native!
print function? :add ; true
print function? :print ; true
; context? and object? check value kind
print context? context [x: 1] ; true
print object? object [field/optional [x [integer!] 0]] ; true
; frozen? checks if a value is an object! (frozen)
print frozen? context [x: 1] ; false
Point: object [field/optional [x [integer!] 0]]
print frozen? :Point ; true
; Union predicates
print number? 3.14 ; true (integer! or float!)
print number? 42 ; true
; --- Type checking (is?) ---
; is? checks whether a value matches a type. Works with
; built-in types, union types, and custom types.
print is? integer! 42 ; true
print is? string! 42 ; false
print is? number! 3.14 ; true
print is? number! 42 ; true
; --- Type conversion (to) ---
; to never coerces silently. Every conversion is explicit.
; If it can fail (string parsing), it throws a type error.
print to integer! "42" ; 42
print to integer! 3.7 ; 3 (truncate toward zero)
print to string! 42 ; 42
print to float! 7 ; 7.0
print to block! "hello" ; ["hello"]
; Numbers and logic don't cross!
; to integer! true ; ERROR
; to logic! 0 ; ERROR
; Word conversions
print to string! 'hello ; hello
to word! "hello" ; => word!
to set-word! "x" ; => set-word!
to lit-word! "name" ; => lit-word!
to get-word! "name" ; => get-word!
to meta-word! "enter" ; => meta-word!
; Money conversions
print to money! 42 ; $42.00
print to money! "19.99" ; $19.99
print to integer! $19.99 ; 1999 (cents)
print to float! $19.99 ; 19.99 (dollars)
; Time conversions
print to integer! 14:30:00 ; 52200 (total seconds)
; Pair/tuple from blocks
print to pair! [10 20] ; 10x20
; Logic from string
print to logic! "true" ; true
print to logic! "false" ; false
; to logic! 0 ; ERROR
; Logic from none
print to logic! none ; false
; --- Math utilities ---
print min 3 7 ; 3
print max 3 7 ; 7
print abs -42 ; 42
print negate 5 ; -5
print odd? 7 ; true
print even? 8 ; true
print sqrt 9 ; 3.0
print floor 3.7 ; 3
print ceil 3.2 ; 4
print pow 2 10 ; 1024
print log10 100 ; 2.0
; --- Trig (radians) ---
print sin 0 ; 0.0
print cos 0 ; 1.0
print pi ; 3.141592653589793
print to-degrees pi ; 180.0
print to-radians 180 ; 3.141592653589793
; --- Random ---
random/seed 42
print random 1.0 ; (float in [0, 1))
print random/int 100 ; (integer in [1, 100])
print random/int/range 1 6 ; (integer in [1, 6])
print random/choice [10 20 30] ; (one of 10, 20, 30)
; ============================================================
; ERROR HANDLING
; ============================================================
; Errors are raised with 'error'. Protected calls with 'try'.
; try returns a context! with path access:
; result/ok -- logic! (success or failure)
; result/value -- the return value (on success)
; result/kind -- lit-word! error kind (on failure)
; result/message -- string! error message (on failure)
; result/data -- any value attached to the error
; --- Raising errors ---
; error <kind:lit-word> <message:string> <data:any|none>
safe-divide: function [x y] [
if y = 0 [error 'math "cannot divide by zero" none]
x / y
]
; --- try (protected call) ---
result: try [safe-divide 10 2]
print result/ok ; true
print result/value ; 5
result: try [safe-divide 10 0]
print result/ok ; false
print result/kind ; math
print result/message ; cannot divide by zero
; --- Pattern: check and branch on result ---
result: try [safe-divide 10 0]
either result/ok [
print rejoin ["Got: " result/value]
] [
print rejoin ["Error: " result/message]
]
; Error: cannot divide by zero
; --- Rethrow ---
; rethrow re-raises an error from a try result context.
; Useful when you catch, inspect, then decide to propagate.
outer: try [
inner: try [error 'user "boom" none]
unless inner/ok [
print rejoin ["caught: " inner/kind]
rethrow inner
]
]
print outer/ok ; false
print outer/message ; boom
; --- Standard error kinds ---
; 'type -- wrong type passed to function
; 'arity -- too few arguments
; 'undefined -- word has no binding
; 'math -- division by zero, overflow
; 'range -- index out of bounds
; 'parse -- parse failed
; 'load -- file not found, circular dependency