# ============================================================
# ============================================================
# Jac is a superset of Python with graph-native programming,
# object-spatial walkers, and brace-delimited blocks.
# Run a file with: jac <filename>
# ============================================================
# ============================================================
# ============================================================
# ============================================================
# Every Jac program starts from a `with entry` block.
# You can have multiple; they run in order.
# Use :__main__ to run only when this is the main module
print("Only when run directly");
# ============================================================
# ============================================================
x: int = 42; # Typed variable
name = "Jac"; # Type inferred
# Jac has the same built-in types as Python:
# int, float, str, bool, list, tuple, set, dict, bytes, any
# ============================================================
# ============================================================
# Import specific items from a module
import from math { sqrt, pi, log as logarithm }
import from .sibling { helper_func }
import from ..parent.mod { SomeClass }
# Include merges a module's namespace into the current scope
# ============================================================
# ============================================================
# Functions use `def`, braces for body, and semicolons
def greet(name: str) -> str {
return f"Hello, {name}!";
# Default parameters and multiple return values
def divmod_example(a: int, b: int = 2) -> tuple[int, int] {
# No-arg functions still need parentheses
# Abstract function (declaration only, no body)
# Function with all param types (positional-only, regular, *args, kw-only, **kwargs)
regular: str = "default",
# ============================================================
# ============================================================
# --- if / elif / else (no parens needed, braces required) ---
for item in ["a", "b", "c"] {
# --- for-to-by loop (C-style iteration) ---
# Syntax: for VAR = START to CONDITION by STEP { ... }
for i = 0 to i < 10 by i += 2 {
print(i); # 0, 2, 4, 6, 8
# --- while loop (with optional else) ---
print("Loop completed normally");
# --- break, continue, skip ---
# ============================================================
# Match (Python-style pattern matching)
# ============================================================
# ============================================================
# Switch (C-style, with fall-through)
# ============================================================
def check_fruit(fruit: str) {
print("banana or orange (fall-through)");
# ============================================================
# ============================================================
fruits = ["apple", "banana", "cherry"];
print(fruits[0]); # apple
print(fruits[1:3]); # ["banana", "cherry"]
print(fruits[-1]); # cherry
person = {"name": "Alice", "age": 25};
(x, y) = point; # Tuple unpacking
colors = {"red", "green", "blue"};
squares = [i ** 2 for i in range(5)];
evens = [i for i in range(10) if i % 2 == 0];
name_map = {name: len(name) for name in ["alice", "bob"]};
(first, *rest) = [1, 2, 3, 4];
# ============================================================
# Objects (obj) vs Classes (class)
# ============================================================
# `obj` is like a Python dataclass -- fields are per-instance,
# auto-generates __init__, __eq__, __repr__, etc.
has name: str = "Unnamed",
print(f"{self.name} says Woof!");
# `class` follows standard Python class behavior
has name: str = "Unnamed";
print(f"{self.name} says Meow!");
has parent_name: str = "Unknown";
print(f"Puppy of {self.parent_name} yips!");
# Generic types with type parameters
obj Result[T, E = Exception] {
has value: T | None = None,
return self.error is None;
# ============================================================
# Has Declarations (fields)
# ============================================================
# Basic typed fields with defaults
# Static (class-level) field
static has instances: int = 0;
# Deferred initialization (set in postinit)
has computed: int by postinit;
self.computed = self.count * 2;
# ============================================================
# ============================================================
# At module level creates auth, export, and extern semantics
has:pub name: str; # Public (default)
has:priv ssn: str; # Private
has:protect age: int; # Protected
# ============================================================
# ============================================================
# Auto-valued enum members
enum Status { PENDING, ACTIVE, DONE }
print(Color.RED.value); # "red"
print(Status.ACTIVE.value); # 2
# ============================================================
# ============================================================
type JsonPrimitive = str | int | float | bool | None;
type Json = JsonPrimitive | list[Json] | dict[str, Json];
type NumberList = list[int | float];
# ============================================================
# Global Variables (glob)
# ============================================================
glob MAX_SIZE: int = 100;
glob greeting: str = "Hello";
global greeting; # Reference module-level glob
# ============================================================
# Impl Blocks (separate declaration from definition)
# ============================================================
# Declare methods (no body)
def multiply(n: int) -> int;
# Define methods separately (can be in a .impl.jac file)
impl Calculator.add(n: int) -> int {
impl Calculator.multiply(n: int) -> int {
# ============================================================
# ============================================================
# Simple lambda (untyped params, colon body)
add = lambda x, y: x + y;
# Typed lambda with return type
mul = lambda (x: int, y: int) -> int : x * y;
# Multi-statement lambda (brace body)
classify = lambda (score: int) -> str {
if score >= 90 { return "A"; }
elif score >= 80 { return "B"; }
# ============================================================
# ============================================================
# Forward pipe: value |> function
# Backward pipe: function <| value
[3, 1, 2] |> sorted |> list |> print;
# ============================================================
# ============================================================
def my_class_method(cls: type) -> str {
# ============================================================
# ============================================================
} except ZeroDivisionError as e {
print("Some other error");
print("No error occurred");
# ============================================================
# With Statement (context managers)
# ============================================================
with open("file.txt") as f {
# Multiple context managers
with open("a.txt") as a, open("b.txt") as b {
print(a.read(), b.read());
# ============================================================
# ============================================================
assert x > 0, "x must be positive";
# ============================================================
# ============================================================
# Assignment inside expressions
if (n := len("hello")) > 3 {
print(f"Long string: {n} chars");
# ============================================================
# ============================================================
return fib(n - 1) + fib(n - 2);
test "fibonacci base cases" {
test "fibonacci recursive" {
assert fib(i) == fib(i - 1) + fib(i - 2);
# ============================================================
# ============================================================
async def fetch_data() -> str {
result = await fetch_data();
# ============================================================
# Flow / Wait (concurrent tasks)
# ============================================================
import from time { sleep }
def slow_task(n: int) -> int {
# `flow` launches a concurrent task, `wait` collects results
task1 = flow slow_task(1);
task2 = flow slow_task(2);
task3 = flow slow_task(3);
print(r1, r2, r3); # 2 4 6
# ============================================================
# Null-Safe Access (?. and ?[])
# ============================================================
print(x?.append); # None (no crash)
print(x?[0]); # None (no crash)
print(y?[99]); # None (out of bounds returns None)
# ============================================================
# ============================================================
print(f"Max int: {result}");
# ============================================================
# OBJECT SPATIAL PROGRAMMING (OSP)
# ============================================================
# Jac extends the type system with graph-native constructs:
# nodes, edges, walkers, and spatial abilities.
# ============================================================
# ============================================================
# Nodes are objects that can exist in a graph
# Edges connect nodes and can carry data
# ============================================================
# ============================================================
a = Person(name="Alice", age=25);
b = Person(name="Bob", age=30);
c = Person(name="Charlie", age=28);
# --- Untyped connections ---
root ++> a; # Connect root -> a
a ++> b; # Connect a -> b
c <++ a; # Connect a -> c (backward syntax)
a <++> b; # Bidirectional a <-> b
# --- Typed connections (with edge data) ---
a +>: Friendship(since=2020) :+> b;
a +>: Friendship(since=1995) :+> c;
# --- Typed connection with field assignment ---
a +>: Friendship : since=2018 :+> b;
# ============================================================
# Edge Traversal & Filters (inside [...])
# ============================================================
# Traverse outgoing edges from root
print([root -->]); # All nodes via outgoing edges
print([root <--]); # All nodes via incoming edges
print([root <-->]); # All nodes via any edges
print([root ->:Friendship:->]); # Nodes connected by Friendship edges
# Filter by edge field values
print([root ->:Friendship:since > 2018:->]); # Nodes with since > 2018
# Get edges themselves (not nodes)
print([edge root ->:Friendship:->]); # Friendship edge objects
# ============================================================
# ============================================================
# Walkers are objects that traverse graphs.
# They have abilities that trigger on entry/exit of nodes.
has greeting: str = "Hello";
# Runs when walker enters the root node
can greet_root with Root entry {
print(f"{self.greeting} from root!");
visit [-->]; # Move to connected nodes
# Runs when walker visits any Person node
can greet_person with Person entry {
# `here` = current node, `self` = the walker
print(f"{self.greeting}, {here.name}!");
report here.name; # Collect a value (returned as list)
visit [-->]; # Continue traversal
root ++> Person(name="Alice", age=25);
root ++> Person(name="Bob", age=30);
# ============================================================
# ============================================================
can search with Person entry {
if here.name == self.target {
print(f"Found {self.target}!");
disengage; # Stop traversal immediately
# Runs when there are no more outgoing nodes
print("Reached a dead end");
# ============================================================
# ============================================================
# Nodes and edges can also have abilities that trigger
# when specific walker types visit them.
# Triggers when any Visitor walker enters this node
can on_enter with Visitor entry {
print(f"Welcome to {self.name}");
can visit_room with SecureRoom entry {
if here.clearance > self.clearance {
# ============================================================
# ============================================================
w = Greeter(greeting="Hi");
# Binary spawn: node spawn walker
# Reverse: walker spawn node
# Spawn returns reported values
results = root spawn Greeter();
# ============================================================
# ============================================================
async walker AsyncCrawler {
async can crawl with Root entry {
print(f"Crawling at depth {self.depth}");
# ============================================================
# ============================================================
# Abilities without names (auto-named by compiler)
print(f"Entered node with val={self.val}");
can with AutoNode entry {
print(f"Visiting: {here.val}");
# ============================================================
# ============================================================
# self -- the current object/walker
# here -- the current node (in walker abilities)
# visitor -- the visiting walker (in node/edge abilities)
# root -- the root node of the graph
# ============================================================
# ============================================================
# Types: str, int, float, bool, list, tuple, set, dict, bytes, any, type
# Decl: obj, class, node, edge, walker, enum, has, can, def, impl, glob, test
# Modifiers: pub, priv, protect, static, override, abs, async
# Control: if, elif, else, for, to, by, while, match, switch, case, default
# Flow: return, yield, break, continue, raise, del, assert, skip
# OSP: visit, spawn, entry, exit, disengage, report, here, visitor, root
# Async: async, await, flow, wait
# Logic: and, or, not, in, is
# Other: import, include, from, as, try, except, finally, with, lambda,
# global, nonlocal, self, super, init, postinit, type