// Example: Hello, world
func main():
println("Hello, world!")
end
jik run examples/hello.jik
Start with the first language examples and work down for a tour of Jik, or jump directly to a topic. For detailed explanations, see the documentation.
// Example: Hello, world
func main():
println("Hello, world!")
end
jik run examples/hello.jik
// Example: values, inferred and explicit types, and assignment.
func main():
item := "notebook"
aisle: char = 'B'
quantity := 3
unit_price: double = 2.5
in_stock: bool = true
reserved: int // Without an initializer, a type's default value is used.
quantity += 1
reserved += 1
total := quantity * unit_price
eligible := in_stock and quantity >= 4
discount := 1.0 if eligible else 0.0
total = total - discount
println(item, " in aisle ", aisle)
println("quantity: ", quantity)
println("available: ", quantity - reserved)
println("discount applied: ", eligible)
println("total: ", total)
end
jik run examples/values.jik
// Example: functions and type inference through temperature conversion.
// Calls and arithmetic determine the types of this local helper.
func fahrenheit(celsius):
return celsius * 9.0 / 5.0 + 32.0
end
// An explicit signature describes the contract without inspecting callers.
func celsius(fahrenheit_value: double) -> double:
return (fahrenheit_value - 32.0) * 5.0 / 9.0
end
func show_temperature(degrees):
println(degrees, " C = ", fahrenheit(degrees), " F")
end
func main():
show_temperature(0.0)
show_temperature(20.0)
println("boiling point in C: ", celsius(212.0))
println("round trip: ", celsius(fahrenheit(20.0)))
end
jik run examples/functions.jik
// Example: filling a delivery van, then counting down to departure.
func main():
capacity := 10
loaded := 0
// Numeric ranges exclude the upper bound: weights are 1 through 6.
for weight = 1, 7:
if weight == 2:
println("skip parcel awaiting an address")
continue
elif loaded + weight > capacity:
println("next parcel will not fit")
break
else:
loaded += weight
println("loaded weight: ", loaded)
end
end
remaining := 3
while remaining > 0:
println("departing in ", remaining)
remaining -= 1
end
println("departed with ", loaded, " of ", capacity)
end
jik run examples/control_flow.jik
// Example: Command-line arguments
func main(args):
for i, arg in args:
println("Value of arg ", i, " is: ", arg)
end
end
jik run examples/cl_args.jik
Build this example and pass arguments to the executable to see them printed.
// Example: cleaning a label and inspecting its text.
use "jik/string"
func main():
raw := " Jik language "
label := string::trim(raw)
// Strings store UTF-8 bytes. Length, indexing, and slicing count bytes;
// ASCII input makes each byte here a complete character.
println("label: ", label)
println("bytes: ", len(label))
println("first character: ", label[0])
println("prefix: ", label[:3])
println("description starts at: ", string::find(label, "language"))
println("starts with Jik: ", string::starts_with(label, "Jik"))
end
jik run examples/strings.jik
// Example: correcting scores and collecting the passing results.
func main():
scores := [42, 68, 91]
scores[0] = 55
push(scores, 73)
push(scores, 0)
println("removed accidental entry: ", pop(scores))
passed: Vec[int]
for score in scores:
if score >= 60:
push(passed, score)
end
end
for index, score in scores:
println("score ", index + 1, ": ", score)
end
println("passing count: ", len(passed))
println("first two passing scores: ", passed[:2])
end
jik run examples/vectors.jik
// Example: struct construction, fields, and a uniform function call.
struct Task:
title: String
done: bool
end
func complete(task: Task):
task.done = true
end
func main():
draft := Task{}
draft.title = "Draft a proposal"
review := Task{title = "Review the proposal"}
title := "Publish the proposal"
publish := Task{title}
// This is shorthand for the ordinary function call complete(draft).
draft.complete()
println(draft.title, ": done = ", draft.done)
println(review.title, ": done = ", review.done)
println(publish.title, ": done = ", publish.done)
end
jik run examples/structs.jik
// Example: a search result that may be absent.
func find_index(values: Vec[int], target: int) -> Option[int]:
for index, value in values:
if value == target:
// The returned option is allocated in the input vector's region.
return Some{index}
end
end
return None
end
func main():
values := [12, 25, 38]
found := find_index(values, 25)
if found is Some:
// ? extracts the payload; check presence before using it.
println("25 found at index ", found?)
end
missing := find_index(values, 99)
if missing is None:
println("99 was not found")
end
end
jik run examples/options.jik
// Example: tracking stock with string keys. See options.jik for option basics.
func main():
stock := {"notebooks": 4, "pens": 12}
stock["notebooks"] = 6
stock["folders"] = 3
// Lookup returns Option[int]; a missing key has no payload to extract.
notebooks := stock["notebooks"]
if notebooks is Some:
println("notebooks available: ", notebooks?)
end
if stock["erasers"] is None:
println("erasers are not listed")
end
// Aggregate without making the output depend on dictionary iteration order.
total := 0
for name, count in stock:
total += count
end
println("product kinds: ", len(stock), ", total items: ", total)
end
jik run examples/dictionaries.jik
// Exhaustive matching over an enum.
enum TrafficLight:
RED
YELLOW
GREEN
end
func wait_seconds(light: TrafficLight) -> int:
match light:
case RED:
return 30
case YELLOW:
return 3
case GREEN:
return 0
end
end
func main():
light := TrafficLight.YELLOW
println("wait seconds: ", wait_seconds(light))
end
jik run examples/enum_match.jik
// Example: input events with enums, variants, and match.
enum Key:
ENTER
ESCAPE
end
variant Event:
KEY: Key
TEXT: String
QUIT
end
func show(event: Event):
// An exhaustive match handles every tag and can bind its payload.
match event:
case KEY{key}:
println("key: ", key)
case TEXT{text}:
println("text: ", text)
case QUIT:
println("quit requested")
end
end
func is_input(event: Event) -> bool:
// Omit payload bindings when only the tag matters.
// other handles every tag not listed explicitly.
match event:
case KEY:
return true
case TEXT:
return true
other:
return false
end
end
func main():
events := [
Event.KEY{Key.ENTER},
Event.TEXT{"hello"},
Event.QUIT{}
]
for event in events:
show(event)
// UFCS calls the ordinary function is_input(event).
println("input event: ", event.is_input())
// Member access syntax is used to extract the active variant tag.
// Accessing .TEXT on another tag results in a runtime error.
if event is TEXT:
println("text length: ", len(event.TEXT))
end
end
end
jik run examples/variants.jik
// Example: exhaustive, immutable lookup tables
enum Signal:
RED
GREEN
YELLOW
end
table SignalNames[Signal] -> String:
RED: "red"
GREEN: "green"
YELLOW: "yellow"
end
table Durations[Signal] -> int:
RED: 30
GREEN: 25
YELLOW: 5
end
table NextSignal[Signal] -> Signal:
RED: Signal.GREEN
GREEN: Signal.YELLOW
YELLOW: Signal.RED
end
func main():
signal := Signal.RED
for cycle = 0, 6:
println(SignalNames[signal], " for ", Durations[signal], " seconds")
signal = NextSignal[signal]
end
end
jik run examples/tables.jik
// Example: region ergonomics in Jik.
struct User:
name: String
labels: Vec[String]
end
func new_user(foreign name: String, r: Region) -> User:
// The returned struct and its composite field `labels` are automatically
// allocated in r, since this is the only possible valid allocation destination.
// Since `name` is marked as a foreign parameter, we still need to copy it to `r`.
return User{
name = copy(name, r),
labels = ["new", "active"]
}
end
func display_name(user: User) -> String:
if user.name == "":
// The returned string literal is allocated in user's region, since
// this is the only valid destination.
return "Anonymous"
end
return user.name
end
func add_default_label(user: User):
// The string literal is automatically allocated in user's region.
// This is also valid for other store operations involving composite values.
push(user.labels, "member")
end
func default_labels(r: Region) -> Vec[String]:
// @ selects the implicit region determined by the same-region rule.
// Here it is equivalent to [r]. With multiple participating composite arguments,
// @ selects their shared region without naming a specific argument.
// Without @, labels would be local and could not be returned.
labels := ["new", "active"]@
push(labels, "member")
return labels
end
func main():
// An omitted final Region argument automatically passes the current
// function's local region `_`.
user := new_user("Ada")
add_default_label(user)
println(display_name(user), ": ", user.labels)
println("default labels: ", default_labels())
end
jik run examples/region_ergonomics.jik
// Example: copy composite values into the caller's region before returning them.
struct Note:
text: String
pinned: bool
end
func make_note(foreign source: String, r: Region) -> Note:
// The source may belong to another region, so make an owned copy for the
// returned struct. source.copy(r) is shorthand for copy(source, r).
// The caller chooses the destination region through r.
return Note{text = source.copy(r), pinned = true}[r]
end
func default_names(r: Region) -> Vec[String]:
names := ["Ada", "Grace"]
// names is local to this helper; copying makes it safe to return.
return names.copy(r)
end
func main():
note := make_note("remember who owns this value", _)
names := default_names(_)
println(note.text, " (pinned: ", note.pinned, ")")
println("names: ", names)
end
jik run examples/regions_copy.jik
// Example: throwing functions, recovery, propagation, must, and postfix !
throws func validate_count(count):
if count < 0:
fail("count must not be negative")
end
if count > 100:
fail("count exceeds the batch limit", 17)
end
return count
end
func show_count(count):
try value := validate_count(count):
println("accepted count: ", value)
except:
println("rejected count: ", count)
println(" msg: ", error_msg())
println(" code: ", error_code())
end
end
throws func half_count(count):
// A declaration with try propagates a failure to half_count's caller.
valid := try validate_count(count)
return valid / 2
end
func main():
show_count(42)
show_count(-1)
show_count(101)
value := must validate_count(42)
println("must succeeded with value ", value)
println("postfix ! succeeded with value ", validate_count(42)!)
try half := half_count(-1):
println("half: ", half)
except:
println("propagated failure: ", error_msg())
end
end
jik run examples/error_handling.jik
// Example: Local modules and imports
use "stats" as st
func main():
xs := [3, 1, 4, 1, 5]
total := st::sum(xs)
best := st::max(xs)
println("sum is: ", total, ", max value is: ", best)
end
func sum(xs):
total := 0
for x in xs:
total += x
end
return total
end
func max(xs):
if len(xs) == 0:
return 0
end
best := xs[0]
for i = 1, len(xs):
if xs[i] > best:
best = xs[i]
end
end
return best
end
jik run examples/modules/main.jik
Keep main.jik and stats.jik together in the modules directory.
// Example: Basic use of jik/testing
use "jik/testing" as test
func square(x):
return x * x
end
func main():
ts := test::suite_new()
test::suite_assert(ts, square(0) == 0, site())
test::suite_assert(ts, square(3) == 9, site())
test::suite_assert(ts, square(-4) == 16, site())
test::suite_finish(ts)
end
jik run examples/testing_demo.jik
// Example: Foreign function interface (C interop)
@embed{C_END}
int32_t
impl_adder(int32_t x, int32_t y)
{
return x + y;
}
typedef struct impl_Point {
double x;
double y;
} impl_Point;
impl_Point *
impl_point_new(double x, double y, JikRegion *r)
{
impl_Point *p = jik_region_alloc(r, sizeof(impl_Point));
p->x = x;
p->y = y;
return p;
}
double
impl_point_x(impl_Point *p)
{
return p->x;
}
C_END
extern func impl_adder as adder(x: int, y: int) -> int
extern func toupper as toupper(ch: char) -> char
extern struct impl_Point as Point
extern func impl_point_new as point_new(x: double, y: double, r: Region) -> Point
extern func impl_point_x as point_x(p: Point) -> double
func main():
println("adder(0, 1) = ", adder(0, 1))
println("adder(5, -3) = ", adder(5, -3))
println(toupper('a'))
point := point_new(3.5, 4.25, _)
// Extern structs are opaque; UFCS calls the exported accessor function.
println("point x = ", point.point_x())
end
jik run examples/ffi_demo.jik
// Example: inspect a file using filesystem, path, and I/O utilities.
use "jik/fs"
use "jik/io"
use "jik/path"
use "jik/sys"
func main():
examples_dir := path::join([sys::cwd(), "examples"])
readme := path::join([examples_dir, "README.md"])
entries := must fs::read_dir(examples_dir)
contents := must io::read_file(readme)
println("directory exists: ", fs::is_dir(examples_dir))
println("README is a file: ", fs::is_file(readme))
println("name: ", path::basename(readme))
println("extension: ", path::extname(readme))
println("directory entries: ", len(entries))
println("README bytes: ", len(contents))
end
jik run examples/filesystem.jik
// Example: build and inspect data that can contain any byte value.
use "jik/bytes"
func main():
packet := bytes::buf_from_string("JIK")
must bytes::buf_push_int(packet, 0)
bytes::buf_push(packet, '!')
must bytes::buf_push_int(packet, 255)
data := bytes::to_bytes(packet)
header := bytes::slice(data, 0, 3)
println("header: ", must bytes::to_string_ascii(header))
println("packet bytes: ", bytes::len(data))
println("contains NUL: ", bytes::get(data, 3) == '\0')
println("hex: ", bytes::to_hex(data))
end
jik run examples/binary_data.jik
// Example: efficiently build a string from many small pieces.
use "jik/strbuf"
use "jik/string"
func main():
scores := [7, 9, 10]
line := strbuf::new("scores: ")
for index, score in scores:
if index > 0:
strbuf::append(line, ", ")
end
strbuf::append(line, string::from_int(score))
end
strbuf::append_char(line, '.')
result := strbuf::to_string(line)
println(result)
println("bytes: ", strbuf::len(line))
end
jik run examples/strbuf_demo.jik
// Example: string and vector slices, indexed iteration, and string comparison.
use "jik/string"
func main():
text := "alpha\nbeta\ngamma"
lines := string::split(text, "\n", _)
prefix := lines[:2]
middle := lines[1:3]
suffix := lines[2:]
word := text[6:10]
println("first two lines: ", prefix)
println("middle lines: ", middle)
println("last line: ", suffix[0])
println("sliced word: ", word)
for line_no, line in lines:
println(line_no + 1, ": ", line)
end
if string::compare(lines[0], lines[2]) < 0:
println(lines[0], " comes before ", lines[2])
end
end
jik run examples/text_processing.jik
// Example: Command-line parsing, generated help, and path normalization.
use "jik/argparse"
use "jik/path"
func main(args):
parser := argparse::new("copy-plan", _)
parser.add_positional("source", "File to copy.")
parser.add_positional("destination", "Planned destination.")
parser.add_option("--verbose", "-v", "Print the normalized paths.")
if len(args) == 1 or args[1] == "--help":
println(parser.format_help())
else:
try result := parser.parse(args[1:]):
source := path::normalize(result.positionals["source"]?, _)
destination := path::normalize(result.positionals["destination"]?, _)
if result.options["--verbose"] is Some:
println("source: ", source)
println("destination: ", destination)
end
println("would copy ", source, " to ", destination)
except:
println(error_msg())
println("\n", parser.format_help())
end
end
end
jik run examples/argparse_demo.jik
Run without arguments for help. To supply arguments, build it and run the executable.
// Example: Run a child process and inspect captured stdout/stderr bytes.
use "jik/bytes"
use "jik/process"
use "jik/sys"
COMPILER := ".\\jik.exe" if sys::platform() == "windows" else "./jik"
func main():
res := must process::capture(COMPILER, ["version"], _)
println("exit code: ", res.code)
println("stdout bytes: ", bytes::len(res.out))
println("stderr bytes: ", bytes::len(res.err))
if bytes::len(res.out) > 0:
println("stdout text: ", must bytes::to_string_ascii(res.out, _))
end
end
jik run examples/process_capture.jik
Run from the compiler repository root; this example starts the local Jik executable.
// Example: Fibonacci, recursive and iterative
func fib(n):
if n < 2:
return n
end
return fib(n - 1) + fib(n - 2)
end
func fib_iter(n, r):
// Allocate result in region "r"
res := [n of 0][r]
if n < 2:
return res
end
res[0] = 0
res[1] = 1
for i = 2, n:
res[i] = res[i - 1] + res[i - 2]
end
return res
end
func main():
for i = 0, 10:
print("fib (", i, ") = ", fib(i), "\n")
end
nums := fib_iter(10, _)
print("fib_iter: ", nums)
end
jik run examples/fib.jik
// Example: Sieve of Eratosthenes
func sieve(n, r):
is_prime := [n + 1 of true][r]
is_prime[0] = false
is_prime[1] = false
p := 2
while p * p <= n:
if is_prime[p]:
i := p * p
while i <= n:
is_prime[i] = false
i = i + p
end
end
p = p + 1
end
return is_prime
end
func main():
n := 100
res := sieve(n, _)
println("Primes up to ", n, ":")
for i = 2, n + 1:
if res[i]:
print(i, ", ")
end
end
end
jik run examples/primes.jik
// Example: Count lines, words, and bytes in a text file.
use "jik/io"
use "jik/char"
struct Counts:
lines: int
words: int
bytes: int
end
func count_text(s: String, r: Region) -> Counts:
lines := 0
words := 0
bytes := len(s)
in_word := false
for i = 0, len(s):
c := s[i]
if c == '\n':
lines += 1
end
if char::isspace(c):
in_word = false
elif not in_word:
words += 1
in_word = true
end
end
return Counts{lines, words, bytes}[r]
end
func main(args):
if len(args) < 2:
println("usage: word_count <file>")
else:
path := args[1]
text := must io::read_file(path, _)
counts := count_text(text, _)
println("file: ", path)
println("lines: ", counts.lines)
println("words: ", counts.words)
println("bytes: ", counts.bytes)
end
end
jik run examples/word_count.jik
To read a file, build this example and pass a file path to the executable.
use "jik/math" as math
struct NewtonResult:
root: double
steps: int
converged: bool
xs: Vec[double]
end
func f(x: double) -> double:
// We want f(x) = 0 <=> cos(x) - x = 0 <=> cos(x) = x
return math::cos(x) - x
end
func df(x: double) -> double:
// f'(x) = -sin(x) - 1
return -math::sin(x) - 1.0
end
func newton_cos_minus_x(x0: double, tol: double, max_steps: int, r: Region) -> NewtonResult:
xs: Vec[double][r]
x := x0
push(xs, x)
step := 0
while step < max_steps:
fx := f(x)
if math::abs(fx) <= tol:
break
end
dfx := df(x)
if math::abs(dfx) <= 1e-14:
// Derivative too small: would amplify error or divide by ~0.
break
end
x = x - fx / dfx
push(xs, x)
step = step + 1
end
// Check the returned estimate, including the final permitted update.
converged := math::abs(f(x)) <= tol
return NewtonResult{
root = x,
steps = len(xs) - 1,
converged,
xs
}[r]
end
func main():
res := newton_cos_minus_x(1.0, 1e-12, 40, _)
print("converged: ", res.converged, "\n")
print("steps: ", res.steps, "\n")
print("root: ", res.root, "\n")
print("f(root): ", f(res.root), "\n")
print("\n")
print("iteration history:", "\n")
for i, x in res.xs:
println(" ", i, ": x = ", x, ", f(x) = ", f(x))
end
end
jik run examples/newton.jik
// Example: Dijkstra's algorithm
INF := 1_000_000_000
func dijkstra_calc(w: Vec[Vec[int]], src: int):
n := len(w)
// The returned distances live in w's region (@ is equivalent to [.w]).
dist := [n of INF]@
// Scratch storage stays local and is reclaimed when this function returns.
used := [n of false]
dist[src] = 0
for i = 0, n:
best_v := -1
best_d := INF
for v = 0, n:
if not used[v] and dist[v] < best_d:
best_d = dist[v]
best_v = v
end
end
if best_v == -1:
break
end
used[best_v] = true
for to = 0, n:
wt := w[best_v][to]
if wt < INF:
if dist[best_v] + wt < dist[to]:
dist[to] = dist[best_v] + wt
end
end
end
end
return dist
end
func main():
w := [
[0, 10, 3, INF, INF],
[INF, 0, 1, 2, INF],
[INF, 4, 0, 8, 2],
[INF, INF, INF, 0, 7],
[INF, INF, INF, 9, 0]
]
d := dijkstra_calc(w, 0)
print(d)
end
jik run examples/dijkstra.jik
// Example: Conway's Game of Life in the terminal.
// Note (terminal rendering):
// This program clears the screen once with `cls`/`clear`, then uses ANSI cursor-home
// sequences to redraw in-place. This works in most Unix-like terminals and in ANSI-capable Windows terminals
// (e.g., Windows Terminal). If your terminal prints the escape codes literally, it does not support
// ANSI sequences.
use "jik/sys" as sys
use "jik/rand" as rand
use "jik/strbuf"
W := 80
H := 40
STEPS := 400
DELAY_MS := 60
DENSITY_PERCENT := 15 // initial probability of life: 0 - 100
func clear_screen():
p := sys::platform(_)
if p == "windows":
sys::system("cls")
else:
sys::system("clear")
end
end
func wrap(i: int, max: int) -> int:
if i < 0:
return i + max
elif i >= max:
return i - max
else:
return i
end
end
func count_neighbors(g: Vec[Vec[bool]], x: int, y: int, w: int, h: int) -> int:
c := 0
for dy = -1, 2:
for dx = -1, 2:
if dx == 0 and dy == 0:
continue
end
nx := wrap(x + dx, w)
ny := wrap(y + dy, h)
if g[ny][nx]:
c += 1
end
end
end
return c
end
func render(g: Vec[Vec[bool]], step: int, w: int, h: int):
print("\x1b[HGame of Life (step ", step, ")\n")
// Reuse the buffer for each row; clear retains its allocated capacity.
line := strbuf::new("")
for y = 0, h:
row := g[y]
line.clear()
for x = 0, w:
line.append_char('#' if row[x] else '.')
end
line.append_char('\n')
line.print()
end
print("\x1b[?25h")
end
func randomize(g: Vec[Vec[bool]], rng: rand::Rng, w: int, h: int):
for y = 0, h:
for x = 0, w:
r := rand::next_int(rng) % 100
g[y][x] = (r < DENSITY_PERCENT)
end
end
end
func step_life(curr: Vec[Vec[bool]], next: Vec[Vec[bool]], w: int, h: int):
for y = 0, h:
for x = 0, w:
n := count_neighbors(curr, x, y, w, h)
alive := curr[y][x]
if alive:
next[y][x] = (n == 2) or (n == 3)
else:
next[y][x] = (n == 3)
end
end
end
end
func main():
clear_screen()
rng := rand::new_time(_)
curr := [H of [W of false]]
next := [H of [W of false]]
randomize(curr, rng, W, H)
step := 0
while step < STEPS:
render(curr, step, W, H)
step_life(curr, next, W, H)
tmp := curr
curr = next
next = tmp
sys::sleep(DELAY_MS)
step += 1
end
end
jik run examples/game_of_life.jik
Runs an animation in your terminal. Press Ctrl+C to stop.
// Example: Forth interpreter REPL
use "jik/std"
use "jik/string"
use "jik/char"
HELP_TEXT := """
REPL Commands:
quit - exit REPL
help - show help
Interpreter commands:
. - show the stack
? - show defined words
"""
struct ForthMachine:
stack: Vec[int]
words: Dict[Vec[String]]
end
throws func require_stack(fm: ForthMachine, count: int):
if len(fm.stack) < count:
fail("Stack underflow")
end
end
throws func eval_tokens(fm, tokens):
n := len(tokens)
ip := 0
compile_mode := false
word := [0 of ""][.fm]
word_name := ""[.fm]
while ip < n:
tok := tokens[ip]
if compile_mode and tok == ";":
compile_mode = false
fm.words[word_name] = word
ip = ip + 1
continue
elif compile_mode and tok == ":":
fail("Cannot re-enter compile mode")
elif compile_mode:
push(word, tok)
ip = ip + 1
continue
elif tok == "":
ip = ip + 1
continue
elif tok == "\n":
ip = ip + 1
continue
elif char::isdigit(tok[0]):
res := try string::to_int(tok)
push(fm.stack, res)
elif tok == ".":
print(fm.stack)
elif tok == "?":
print(fm.words)
elif tok == "+":
try require_stack(fm, 2)
rhs := pop(fm.stack)
lhs := pop(fm.stack)
push(fm.stack, lhs + rhs)
elif tok == "*":
try require_stack(fm, 2)
rhs := pop(fm.stack)
lhs := pop(fm.stack)
push(fm.stack, lhs * rhs)
elif tok == "dup":
try require_stack(fm, 1)
push(fm.stack, fm.stack[len(fm.stack) - 1])
elif tok == "drop":
try require_stack(fm, 1)
pop(fm.stack)
elif tok == ":":
compile_mode = true
word = [0 of ""][.fm]
ip = ip + 1
// split preserves empty fields, so allow spaces before the name.
while ip < n and tokens[ip] == "":
ip += 1
end
if ip >= n:
fail("Expected a word name after ':'")
end
word_name = tokens[ip]
if word_name == ":":
fail("Cannot re-enter compile mode")
elif word_name == ";":
fail("Expected a word name after ':'")
end
else:
w := fm.words[tok]
if w is None:
fail("Unknown word")
else:
// Propagate failures from user-defined words to the REPL.
try eval_tokens(fm, w?)
end
end
ip = ip + 1
end
end
throws func run(fm, code):
tokens := string::split(code, " ", .fm)
try eval_tokens(fm, tokens)
end
func main():
fm := ForthMachine{}
print("Welcome! Type quit to quit, help for help.")
while true:
print("\n> ")
code := std::input(_)
if code == "quit":
break
elif code == "help":
print(HELP_TEXT)
continue
end
try run(fm, code):
except:
println("Error: ", error_msg())
break
end
end
end
jik run examples/forth.jik
Starts an interactive Forth interpreter in your terminal.