jac-coder 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- jac_coder/__init__.jac +0 -0
- jac_coder/api.jac +82 -0
- jac_coder/cli_entry.py +25 -0
- jac_coder/config.jac +36 -0
- jac_coder/context.jac +17 -0
- jac_coder/data/examples/ai_agent.md +90 -0
- jac_coder/data/examples/blog_app.md +386 -0
- jac_coder/data/examples/core_patterns.md +321 -0
- jac_coder/data/examples/todo_app.md +321 -0
- jac_coder/data/reference/ai.md +131 -0
- jac_coder/data/reference/backend.md +215 -0
- jac_coder/data/reference/frontend.md +271 -0
- jac_coder/data/reference/osp.md +229 -0
- jac_coder/data/reference/pitfalls.md +141 -0
- jac_coder/data/reference/syntax.md +159 -0
- jac_coder/data/rules/core_jac.md +559 -0
- jac_coder/data/rules/fullstack.md +362 -0
- jac_coder/data/rules/workflow.md +88 -0
- jac_coder/events.jac +110 -0
- jac_coder/impl/api.impl.jac +399 -0
- jac_coder/impl/config.impl.jac +163 -0
- jac_coder/impl/context.impl.jac +117 -0
- jac_coder/impl/mcp_manager.impl.jac +380 -0
- jac_coder/impl/memory.impl.jac +247 -0
- jac_coder/impl/nodes.impl.jac +259 -0
- jac_coder/impl/permission.impl.jac +62 -0
- jac_coder/impl/walkers.impl.jac +298 -0
- jac_coder/mcp_manager.jac +35 -0
- jac_coder/memory.jac +15 -0
- jac_coder/nodes.jac +306 -0
- jac_coder/permission.jac +19 -0
- jac_coder/serve_entry.jac +30 -0
- jac_coder/server.jac +324 -0
- jac_coder/tool/__init__.jac +17 -0
- jac_coder/tool/checked.jac +10 -0
- jac_coder/tool/delegation.jac +23 -0
- jac_coder/tool/filesystem.jac +25 -0
- jac_coder/tool/git.jac +18 -0
- jac_coder/tool/guarded.jac +23 -0
- jac_coder/tool/impl/checked.impl.jac +38 -0
- jac_coder/tool/impl/delegation.impl.jac +157 -0
- jac_coder/tool/impl/filesystem.impl.jac +781 -0
- jac_coder/tool/impl/git.impl.jac +115 -0
- jac_coder/tool/impl/guarded.impl.jac +72 -0
- jac_coder/tool/impl/jac_analyzer.impl.jac +593 -0
- jac_coder/tool/impl/jac_docs.impl.jac +136 -0
- jac_coder/tool/impl/jac_tools.impl.jac +79 -0
- jac_coder/tool/impl/mcp.impl.jac +32 -0
- jac_coder/tool/impl/preview.impl.jac +233 -0
- jac_coder/tool/impl/question.impl.jac +29 -0
- jac_coder/tool/impl/scaffold.impl.jac +231 -0
- jac_coder/tool/impl/search.impl.jac +85 -0
- jac_coder/tool/impl/shell.impl.jac +89 -0
- jac_coder/tool/impl/task.impl.jac +12 -0
- jac_coder/tool/impl/think.impl.jac +4 -0
- jac_coder/tool/impl/todo.impl.jac +58 -0
- jac_coder/tool/impl/validate.impl.jac +236 -0
- jac_coder/tool/impl/web.impl.jac +91 -0
- jac_coder/tool/jac_analyzer.jac +21 -0
- jac_coder/tool/jac_docs.jac +9 -0
- jac_coder/tool/jac_tools.jac +11 -0
- jac_coder/tool/mcp.jac +17 -0
- jac_coder/tool/preview.jac +31 -0
- jac_coder/tool/question.jac +7 -0
- jac_coder/tool/scaffold.jac +10 -0
- jac_coder/tool/search.jac +14 -0
- jac_coder/tool/shell.jac +12 -0
- jac_coder/tool/task.jac +9 -0
- jac_coder/tool/think.jac +5 -0
- jac_coder/tool/todo.jac +12 -0
- jac_coder/tool/validate.jac +11 -0
- jac_coder/tool/vision.jac +17 -0
- jac_coder/tool/web.jac +10 -0
- jac_coder/util/__init__.jac +18 -0
- jac_coder/util/colors.jac +20 -0
- jac_coder/util/impl/sandbox.impl.jac +38 -0
- jac_coder/util/impl/tool_output.impl.jac +208 -0
- jac_coder/util/sandbox.jac +8 -0
- jac_coder/util/tool_output.jac +29 -0
- jac_coder/walkers.jac +67 -0
- jac_coder-0.1.0.dist-info/METADATA +9 -0
- jac_coder-0.1.0.dist-info/RECORD +85 -0
- jac_coder-0.1.0.dist-info/WHEEL +5 -0
- jac_coder-0.1.0.dist-info/entry_points.txt +3 -0
- jac_coder-0.1.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
# Object-Spatial Programming — Nodes, Edges, Walkers
|
|
2
|
+
|
|
3
|
+
## Node Definition
|
|
4
|
+
|
|
5
|
+
Nodes are data containers that live in the graph. Connected to `root`, they auto-persist (SQLite).
|
|
6
|
+
|
|
7
|
+
```jac
|
|
8
|
+
node Session {
|
|
9
|
+
has id: str = "";
|
|
10
|
+
has chat_history: list[dict] = [];
|
|
11
|
+
|
|
12
|
+
def postinit {
|
|
13
|
+
if not self.id { self.id = str(uuid4()); }
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
node City {
|
|
18
|
+
has name: str;
|
|
19
|
+
has population: int = 0;
|
|
20
|
+
}
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Edge Definition
|
|
24
|
+
|
|
25
|
+
Edges define typed relationships between nodes.
|
|
26
|
+
|
|
27
|
+
```jac
|
|
28
|
+
edge Road {
|
|
29
|
+
has distance: float = 0.0;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
edge Knows {
|
|
33
|
+
has since: int;
|
|
34
|
+
has strength: float = 1.0;
|
|
35
|
+
}
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Graph Construction and Connection
|
|
39
|
+
|
|
40
|
+
```jac
|
|
41
|
+
# Connect with default edge
|
|
42
|
+
a ++> b;
|
|
43
|
+
|
|
44
|
+
# Connect with typed edge
|
|
45
|
+
alice +>:Knows(since=2020):+> bob;
|
|
46
|
+
|
|
47
|
+
# Create + connect in one step (returns list — use [0])
|
|
48
|
+
new_node = (here ++> Session(id="abc"))[0];
|
|
49
|
+
|
|
50
|
+
# Chain connections
|
|
51
|
+
root ++> Router() ++> BuildHandler();
|
|
52
|
+
|
|
53
|
+
# Connect to root (auto-persists)
|
|
54
|
+
root ++> City(name="NYC", population=8_300_000);
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## Graph Querying
|
|
58
|
+
|
|
59
|
+
```jac
|
|
60
|
+
# Query all connected nodes of a type
|
|
61
|
+
sessions = [-->][?:Session];
|
|
62
|
+
cities = [root-->][?:City];
|
|
63
|
+
|
|
64
|
+
# Query with filter condition
|
|
65
|
+
active = [-->][?:Session](?status == "active");
|
|
66
|
+
big_cities = [root-->][?:City](?population > 1_000_000);
|
|
67
|
+
|
|
68
|
+
# Query by edge type
|
|
69
|
+
friends = [->:Knows:->];
|
|
70
|
+
|
|
71
|
+
# Chained query
|
|
72
|
+
handlers = [root-->][?:Router]-->[?:BuildHandler];
|
|
73
|
+
|
|
74
|
+
# Delete edge
|
|
75
|
+
a del--> b;
|
|
76
|
+
|
|
77
|
+
# Delete node
|
|
78
|
+
del node;
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
## Walker Definition
|
|
82
|
+
|
|
83
|
+
Walkers traverse the graph and execute abilities on each visited node.
|
|
84
|
+
|
|
85
|
+
```jac
|
|
86
|
+
walker Explorer {
|
|
87
|
+
has visited: list[str] = [];
|
|
88
|
+
|
|
89
|
+
can visit_city with City entry {
|
|
90
|
+
self.visited.append(here.name);
|
|
91
|
+
print(f"Visiting {here.name}");
|
|
92
|
+
visit [-->]; # continue to connected nodes
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
# Spawn walker from root
|
|
97
|
+
result = root spawn Explorer();
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
## Walker Traversal Commands
|
|
101
|
+
|
|
102
|
+
```jac
|
|
103
|
+
visit [-->]; # Visit all outgoing nodes
|
|
104
|
+
visit [-->][?:City]; # Visit only City nodes
|
|
105
|
+
visit [<--]; # Back-traverse (incoming)
|
|
106
|
+
visit [-->][?:Router] else { # Fallback if none found
|
|
107
|
+
router = (here ++> Router())[0];
|
|
108
|
+
visit router;
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
report {"data": value}; # Emit data from walker
|
|
112
|
+
disengage; # Stop walker entirely
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
## Context Keywords
|
|
116
|
+
|
|
117
|
+
| Keyword | Meaning | Used In |
|
|
118
|
+
|---------|---------|---------|
|
|
119
|
+
| `here` | Current node being visited | Walker abilities |
|
|
120
|
+
| `visitor` | The walker visiting this node | Node abilities |
|
|
121
|
+
| `self` | The archetype instance | Any method |
|
|
122
|
+
| `root` | Graph root node | Anywhere |
|
|
123
|
+
|
|
124
|
+
```jac
|
|
125
|
+
# Walker ability — here is the node, self is the walker
|
|
126
|
+
walker Collector {
|
|
127
|
+
has items: list = [];
|
|
128
|
+
can collect with DataNode entry {
|
|
129
|
+
self.items.append(here.value);
|
|
130
|
+
visit [-->];
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
# Node ability — here is this node, visitor is the walker
|
|
135
|
+
node DataNode {
|
|
136
|
+
has value: int;
|
|
137
|
+
can respond with Collector entry {
|
|
138
|
+
print(f"Walker has {len(visitor.items)} items");
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
## disengage vs return
|
|
144
|
+
|
|
145
|
+
- `return` — exits current ability only. Walker continues to next queued node.
|
|
146
|
+
- `disengage` — stops walker entirely. No more traversal.
|
|
147
|
+
|
|
148
|
+
```jac
|
|
149
|
+
walker Search {
|
|
150
|
+
has target: str;
|
|
151
|
+
can check with Item entry {
|
|
152
|
+
if here.name == self.target {
|
|
153
|
+
report here;
|
|
154
|
+
disengage; # found it, stop everything
|
|
155
|
+
}
|
|
156
|
+
visit [-->]; # keep searching
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
## Walker Spawn and Reports
|
|
162
|
+
|
|
163
|
+
```jac
|
|
164
|
+
# Spawn walker and get result
|
|
165
|
+
result = root spawn GetData();
|
|
166
|
+
data = result.reports[0]; # Access first reported value
|
|
167
|
+
|
|
168
|
+
# Walker that reports
|
|
169
|
+
walker GetData {
|
|
170
|
+
can fetch with Root entry {
|
|
171
|
+
items = [{"id": str(i.id)} for i in [-->][?:Item]];
|
|
172
|
+
report {"items": items};
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
## Lazy Graph Creation Pattern
|
|
178
|
+
|
|
179
|
+
```jac
|
|
180
|
+
visit [-->][?:Router] else {
|
|
181
|
+
router = (here ++> Router())[0];
|
|
182
|
+
visit router;
|
|
183
|
+
};
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
## Walker as REST Endpoint
|
|
187
|
+
|
|
188
|
+
```jac
|
|
189
|
+
walker :pub search_items {
|
|
190
|
+
has query: str;
|
|
191
|
+
static has as_query: list = ["query"]; # for GET params
|
|
192
|
+
can find with Root entry {
|
|
193
|
+
matches = [i for i in [-->][?:Item] if self.query in i.name];
|
|
194
|
+
report {"results": matches};
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
## Parent-Child Graph Patterns
|
|
200
|
+
|
|
201
|
+
```jac
|
|
202
|
+
node Category { has name: str; }
|
|
203
|
+
node Item { has title: str; }
|
|
204
|
+
|
|
205
|
+
# Hierarchy: root → Category → Item
|
|
206
|
+
def:pub add_category(name: str) -> dict {
|
|
207
|
+
cat = (root ++> Category(name=name))[0];
|
|
208
|
+
return {"name": cat.name};
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
def:pub add_item(category_name: str, title: str) -> dict {
|
|
212
|
+
for cat in [root-->][?:Category] {
|
|
213
|
+
if cat.name == category_name {
|
|
214
|
+
item = (cat ++> Item(title=title))[0];
|
|
215
|
+
return {"title": item.title};
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
return {"error": "Category not found"};
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
def:pub get_items_by_category(category_name: str) -> list {
|
|
222
|
+
for cat in [root-->][?:Category] {
|
|
223
|
+
if cat.name == category_name {
|
|
224
|
+
return [{"title": i.title} for i in [cat-->][?:Item]];
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
return [];
|
|
228
|
+
}
|
|
229
|
+
```
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
# Runtime Gotchas — Passes Compilation, Crashes at Runtime
|
|
2
|
+
|
|
3
|
+
## JS Constructors Need Reflect.construct
|
|
4
|
+
|
|
5
|
+
In .cl.jac, `ClassName()` without `new` returns wrong type or throws. Jac has no `new` keyword.
|
|
6
|
+
|
|
7
|
+
```jac
|
|
8
|
+
# WRONG — Date() returns string, not Date object
|
|
9
|
+
year = Date().getFullYear(); # CRASH
|
|
10
|
+
|
|
11
|
+
# RIGHT
|
|
12
|
+
year = Reflect.construct(Date, []).getFullYear();
|
|
13
|
+
ws = Reflect.construct(WebSocket, ["ws://localhost"]);
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
**Safe statics (no Reflect needed):** `Date.now()`, `JSON.parse()`, `JSON.stringify()`, `Math.random()`, `Math.floor()`
|
|
17
|
+
|
|
18
|
+
**Always need Reflect.construct:** Date, WebSocket, TextDecoder, TextEncoder, URL, URLSearchParams, FormData, AbortController, RegExp, Error, Worker, Headers, Request, Response, Uint8Array, ArrayBuffer, Blob, File, FileReader, MutationObserver, ResizeObserver, IntersectionObserver
|
|
19
|
+
|
|
20
|
+
## Browser Global Name Conflicts
|
|
21
|
+
|
|
22
|
+
Do NOT define functions with names that shadow browser globals:
|
|
23
|
+
|
|
24
|
+
```jac
|
|
25
|
+
# WRONG — shadows window.open
|
|
26
|
+
def open(url: str) -> None { ... }
|
|
27
|
+
|
|
28
|
+
# RIGHT
|
|
29
|
+
def handleOpen(url: str) -> None { ... }
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
**Avoid as function names:** open, close, print, focus, blur, scroll, fetch, stop, find, alert, confirm, prompt
|
|
33
|
+
|
|
34
|
+
## Callback-in-Lambda Bug
|
|
35
|
+
|
|
36
|
+
Jac compiles `callback(arg)` inside a lambda as `new callback(arg)`. Crashes silently.
|
|
37
|
+
|
|
38
|
+
```jac
|
|
39
|
+
# WRONG — lambda compiles to new onMessage(msg), crashes
|
|
40
|
+
ws.onmessage = lambda(e: any) -> None { onMessage(e.data); };
|
|
41
|
+
|
|
42
|
+
# RIGHT — named handler with .call()
|
|
43
|
+
msgHandler = onMessage;
|
|
44
|
+
def handle_ws_message(e: any) -> None {
|
|
45
|
+
msgHandler.call(None, e.data);
|
|
46
|
+
}
|
|
47
|
+
ws.onmessage = handle_ws_message;
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## Undefined Property Access Crashes
|
|
51
|
+
|
|
52
|
+
The #1 runtime crash in .cl.jac. Happens when accessing property on `undefined`.
|
|
53
|
+
|
|
54
|
+
```jac
|
|
55
|
+
# WRONG — crashes if parent doesn't pass "items" prop
|
|
56
|
+
items = props.items; # undefined!
|
|
57
|
+
return <div>{[... for item in items]}</div>; # CRASH
|
|
58
|
+
|
|
59
|
+
# RIGHT — always default props
|
|
60
|
+
items = props.items or [];
|
|
61
|
+
title = props.title or "";
|
|
62
|
+
|
|
63
|
+
# WRONG — chaining on undefined
|
|
64
|
+
data = result.reports[0].items; # CRASH if reports empty
|
|
65
|
+
|
|
66
|
+
# RIGHT — guard each level
|
|
67
|
+
if result and result.reports and len(result.reports) > 0 {
|
|
68
|
+
data = result.reports[0].items or [];
|
|
69
|
+
}
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
## Number and Boolean Display in JSX
|
|
73
|
+
|
|
74
|
+
Jac doesn't auto-convert to strings in JSX.
|
|
75
|
+
|
|
76
|
+
```jac
|
|
77
|
+
# WRONG — may render nothing
|
|
78
|
+
<span>{count}</span>
|
|
79
|
+
|
|
80
|
+
# RIGHT
|
|
81
|
+
<span>{str(count)}</span>
|
|
82
|
+
<p>{str(item["price"])}</p>
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
## sv import kwargs Are Broken
|
|
86
|
+
|
|
87
|
+
In .cl.jac, kwargs compile to a single dict argument. Server gets wrong data.
|
|
88
|
+
|
|
89
|
+
```jac
|
|
90
|
+
# WRONG — server receives {"a": {"a":2, "b":4, "op":"add"}}
|
|
91
|
+
resp = await calc(a=2, b=4, op="add");
|
|
92
|
+
|
|
93
|
+
# RIGHT — positional, order matches def:pub signature
|
|
94
|
+
resp = await calc(2, 4, "add");
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
## State List Mutation Doesn't Re-render
|
|
98
|
+
|
|
99
|
+
`.append()` mutates in place — React won't detect the change.
|
|
100
|
+
|
|
101
|
+
```jac
|
|
102
|
+
# WRONG — no re-render
|
|
103
|
+
items.append(newItem);
|
|
104
|
+
|
|
105
|
+
# RIGHT — new reference triggers re-render
|
|
106
|
+
items = items + [newItem];
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
## Comments Inside JSX Break Rendering
|
|
110
|
+
|
|
111
|
+
```jac
|
|
112
|
+
# WRONG — all of these crash
|
|
113
|
+
return <div>
|
|
114
|
+
{# comment}
|
|
115
|
+
<!-- comment -->
|
|
116
|
+
{/* comment */}
|
|
117
|
+
</div>;
|
|
118
|
+
|
|
119
|
+
# RIGHT — comments above JSX only
|
|
120
|
+
# Render the list
|
|
121
|
+
return <div>...</div>;
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
## .impl.jac Parse Error Breaks Entire File
|
|
125
|
+
|
|
126
|
+
A single syntax error in an `.impl.jac` file causes ALL implementations in that file to have 0 body items. The compiler won't report which implementation failed — they all silently become empty. Always double-check syntax in impl files.
|
|
127
|
+
|
|
128
|
+
## Missing :pub on Exports
|
|
129
|
+
|
|
130
|
+
- Components in `.cl.jac` need `def:pub` to be importable
|
|
131
|
+
- Walkers need `walker :pub` for REST API
|
|
132
|
+
- `app()` in main.jac MUST be `def:pub`
|
|
133
|
+
- Hooks need `def:pub` to be importable
|
|
134
|
+
|
|
135
|
+
```jac
|
|
136
|
+
# WRONG — not importable
|
|
137
|
+
def Header() -> JsxElement { ... }
|
|
138
|
+
|
|
139
|
+
# RIGHT
|
|
140
|
+
def:pub Header() -> JsxElement { ... }
|
|
141
|
+
```
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
# Jac Language Syntax Reference
|
|
2
|
+
|
|
3
|
+
## Variables and Types
|
|
4
|
+
|
|
5
|
+
**Builtin types:** int, float, str, bool, bytes, list, tuple, set, dict, any, type, None
|
|
6
|
+
|
|
7
|
+
**Generic types:** `list[str]`, `dict[str, int]`, `set[int]`, `tuple[str, int]`, `Type | None`
|
|
8
|
+
|
|
9
|
+
```jac
|
|
10
|
+
# Field declarations in archetypes
|
|
11
|
+
obj Example {
|
|
12
|
+
has name: str; # Required field
|
|
13
|
+
has count: int = 0; # With default
|
|
14
|
+
has items: list[str] = []; # Generic type
|
|
15
|
+
has data: dict | None = None; # Optional
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
# Module-level global
|
|
19
|
+
glob config: dict = {};
|
|
20
|
+
|
|
21
|
+
# Local variables
|
|
22
|
+
x: int = 42; # Annotated
|
|
23
|
+
name = "hello"; # Type inferred
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
Type annotations are **required** on `has` fields and function signatures.
|
|
27
|
+
|
|
28
|
+
## Functions and Methods
|
|
29
|
+
|
|
30
|
+
```jac
|
|
31
|
+
# Standalone function
|
|
32
|
+
def greet(name: str, greeting: str = "Hello") -> str {
|
|
33
|
+
return f"{greeting}, {name}!";
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
# Object with methods
|
|
37
|
+
obj Person {
|
|
38
|
+
has name: str;
|
|
39
|
+
has age: int = 0;
|
|
40
|
+
|
|
41
|
+
def postinit { print(f"Created {self.name}"); }
|
|
42
|
+
def greet() -> str { return f"Hi, I'm {self.name}"; }
|
|
43
|
+
def is_adult() -> bool { return self.age >= 18; }
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
# Inheritance
|
|
47
|
+
obj Student(Person) { has grade: str = "A"; }
|
|
48
|
+
|
|
49
|
+
# Static method
|
|
50
|
+
obj Utils {
|
|
51
|
+
static def helper() -> int { return 42; }
|
|
52
|
+
}
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## Enums
|
|
56
|
+
|
|
57
|
+
```jac
|
|
58
|
+
enum Color { RED = "red", GREEN = "green", BLUE = "blue" }
|
|
59
|
+
enum Direction { NORTH, SOUTH, EAST, WEST }
|
|
60
|
+
|
|
61
|
+
# Usage
|
|
62
|
+
c = Color.RED;
|
|
63
|
+
print(c.value); # "red"
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## Control Flow
|
|
67
|
+
|
|
68
|
+
```jac
|
|
69
|
+
# if/elif/else
|
|
70
|
+
if x > 0 { print("positive"); }
|
|
71
|
+
elif x == 0 { print("zero"); }
|
|
72
|
+
else { print("negative"); }
|
|
73
|
+
|
|
74
|
+
# Inline conditional
|
|
75
|
+
result = "yes" if condition else "no";
|
|
76
|
+
|
|
77
|
+
# Loops
|
|
78
|
+
for item in items { print(item); }
|
|
79
|
+
for (i, x) in enumerate(items) { print(i, x); }
|
|
80
|
+
for (k, v) in my_dict.items() { print(k, v); }
|
|
81
|
+
while count < 10 { count += 1; }
|
|
82
|
+
|
|
83
|
+
# Match/case (uses colons, not braces)
|
|
84
|
+
match value {
|
|
85
|
+
case "add":
|
|
86
|
+
result = a + b;
|
|
87
|
+
case "sub":
|
|
88
|
+
result = a - b;
|
|
89
|
+
case _:
|
|
90
|
+
result = 0;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
# Exception handling
|
|
94
|
+
try { result = risky(); }
|
|
95
|
+
except ValueError as e { print(f"Error: {e}"); }
|
|
96
|
+
finally { cleanup(); }
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
## Comprehensions
|
|
100
|
+
|
|
101
|
+
```jac
|
|
102
|
+
squares = [x ** 2 for x in range(10)];
|
|
103
|
+
filtered = [x for x in items if x > 0];
|
|
104
|
+
mapping = {k: v for (k, v) in items.items() if v > 0};
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
## String Methods
|
|
108
|
+
|
|
109
|
+
All Python string methods work in Jac:
|
|
110
|
+
|
|
111
|
+
```jac
|
|
112
|
+
name.lower(); name.upper(); name.strip();
|
|
113
|
+
name.split(","); name.replace("old", "new");
|
|
114
|
+
name.startswith("prefix"); name.endswith("suffix");
|
|
115
|
+
",".join(items); name.find("sub");
|
|
116
|
+
f"Hello, {name}!"; # f-strings supported
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
## Access Modifiers
|
|
120
|
+
|
|
121
|
+
| Modifier | Meaning | Use Case |
|
|
122
|
+
|----------|---------|----------|
|
|
123
|
+
| `:pub` | Public | REST endpoint, importable component/hook |
|
|
124
|
+
| `:priv` | Private (auth) | Per-user endpoint, requires login |
|
|
125
|
+
| `:protect` | Protected | Module-internal helper |
|
|
126
|
+
| *(none)* | Default | Module-scoped, not exposed |
|
|
127
|
+
|
|
128
|
+
```jac
|
|
129
|
+
walker :pub get_tasks { ... } # Public API
|
|
130
|
+
walker :priv add_task { ... } # Authenticated
|
|
131
|
+
def :protect validate(x: str) -> bool { ... }
|
|
132
|
+
def:pub get_items() -> list { ... } # Public function endpoint
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
## Module Entry Point
|
|
136
|
+
|
|
137
|
+
```jac
|
|
138
|
+
with entry {
|
|
139
|
+
print("Module loaded");
|
|
140
|
+
root spawn MyWalker();
|
|
141
|
+
}
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
## Imports
|
|
145
|
+
|
|
146
|
+
```jac
|
|
147
|
+
# Python standard library
|
|
148
|
+
import from os { path }
|
|
149
|
+
import from datetime { datetime }
|
|
150
|
+
import from pathlib { Path }
|
|
151
|
+
import os;
|
|
152
|
+
import json;
|
|
153
|
+
|
|
154
|
+
# Jac modules (relative)
|
|
155
|
+
import from .submodule { Helper }
|
|
156
|
+
import from jac_coder.nodes { Session }
|
|
157
|
+
|
|
158
|
+
# NEVER use import:py — it's deprecated
|
|
159
|
+
```
|