0x-lang 0.1.5 → 0.1.7
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.
- package/README.md +455 -204
- package/dist/generators/react.js +129 -18
- package/dist/generators/react.js.map +1 -1
- package/dist/generators/shared.d.ts +1 -0
- package/dist/generators/shared.js +5 -0
- package/dist/generators/shared.js.map +1 -1
- package/dist/generators/svelte.js +64 -19
- package/dist/generators/svelte.js.map +1 -1
- package/dist/generators/vue.js +67 -21
- package/dist/generators/vue.js.map +1 -1
- package/dist/parser.js +43 -9
- package/dist/parser.js.map +1 -1
- package/dist/tokenizer.js +3 -3
- package/dist/tokenizer.js.map +1 -1
- package/package.json +5 -1
package/README.md
CHANGED
|
@@ -1,137 +1,317 @@
|
|
|
1
1
|
<p align="center">
|
|
2
|
-
<img src="website/assets/
|
|
2
|
+
<img src="website/assets/0xlogo.svg" alt="0x Logo" width="100" height="100" />
|
|
3
3
|
</p>
|
|
4
4
|
|
|
5
5
|
<h1 align="center">0x</h1>
|
|
6
|
-
<p align="center"><strong>The AI-First Programming Language</strong></p>
|
|
7
6
|
|
|
8
7
|
<p align="center">
|
|
9
|
-
18 lines
|
|
10
|
-
|
|
8
|
+
<strong>Write 18 lines. Get 96 lines of production React.</strong><br/>
|
|
9
|
+
A programming language that compiles to React, Vue 3, and Svelte 5.
|
|
11
10
|
</p>
|
|
12
11
|
|
|
13
12
|
<p align="center">
|
|
14
|
-
<a href="https://www.npmjs.com/package/0x-lang"><img src="https://img.shields.io/npm/v/0x-lang.svg?style=flat-square&color=
|
|
15
|
-
<a href="https://
|
|
13
|
+
<a href="https://www.npmjs.com/package/0x-lang"><img src="https://img.shields.io/npm/v/0x-lang.svg?style=flat-square&color=00e5cc&label=npm" alt="npm" /></a>
|
|
14
|
+
<a href="https://www.npmjs.com/package/0x-lang"><img src="https://img.shields.io/npm/dm/0x-lang.svg?style=flat-square&color=7c3aed&label=downloads" alt="downloads" /></a>
|
|
16
15
|
<a href="https://opensource.org/licenses/ISC"><img src="https://img.shields.io/badge/license-ISC-blue.svg?style=flat-square" alt="License" /></a>
|
|
16
|
+
<a href="https://smithery.ai/server/0x"><img src="https://smithery.ai/badge/0x" alt="Smithery" /></a>
|
|
17
|
+
</p>
|
|
18
|
+
|
|
19
|
+
<p align="center">
|
|
20
|
+
<a href="https://0xlang.com">Website</a> · <a href="#quick-start">Quick Start</a> · <a href="#language-reference">Docs</a> · <a href="https://www.npmjs.com/package/0x-lang">npm</a> · <a href="mcp-server/">MCP Server</a> · <a href="https://server.smithery.ai/hankim/ox-lang">Smithery Server</a>
|
|
17
21
|
</p>
|
|
18
22
|
|
|
19
23
|
---
|
|
20
24
|
|
|
21
|
-
##
|
|
25
|
+
## What is this?
|
|
22
26
|
|
|
23
|
-
|
|
27
|
+
0x is a tiny language for building UI. You describe what you want, and the compiler outputs production-ready React, Vue, or Svelte.
|
|
24
28
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
| **Todo App** | 24 lines | 136 lines | **82%** |
|
|
29
|
-
| **Dashboard** | 37 lines | ~185 lines | **80%** |
|
|
30
|
-
| **Chat App** | 31 lines | ~155 lines | **80%** |
|
|
31
|
-
| **E-commerce** | 44 lines | ~210 lines | **79%** |
|
|
29
|
+
```python
|
|
30
|
+
page Counter:
|
|
31
|
+
state count: int = 0
|
|
32
32
|
|
|
33
|
-
|
|
33
|
+
fn increment():
|
|
34
|
+
count += 1
|
|
35
|
+
|
|
36
|
+
layout col gap=16 padding=24 center:
|
|
37
|
+
text "Counter" size=2xl bold
|
|
38
|
+
text "{count}" size=4xl color=cyan
|
|
39
|
+
button "+1" style=primary -> increment()
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
That's a complete component. Run `0x build counter.ai --target react` and you get a working React component with `useState`, event handlers, and full styling. Same source also compiles to Vue 3 and Svelte 5.
|
|
34
43
|
|
|
35
|
-
|
|
44
|
+
**Why?** Most frontend code is boilerplate — imports, hook calls, JSX wrappers, style objects, closing tags. 0x skips all of that. You write what matters, the compiler handles the rest.
|
|
45
|
+
|
|
46
|
+
## The numbers
|
|
47
|
+
|
|
48
|
+
| Component | 0x | React | You save |
|
|
49
|
+
|:---|:---:|:---:|:---:|
|
|
50
|
+
| Counter | 18 lines | 96 lines | **81%** |
|
|
51
|
+
| Todo App | 24 lines | 136 lines | **82%** |
|
|
52
|
+
| Dashboard | 37 lines | ~185 lines | **80%** |
|
|
53
|
+
| Chat App | 31 lines | ~155 lines | **80%** |
|
|
54
|
+
| E-commerce | 44 lines | ~210 lines | **79%** |
|
|
55
|
+
|
|
56
|
+
> Compared against production TypeScript React with full inline styling, types, and component structure.
|
|
36
57
|
|
|
37
58
|
## Quick Start
|
|
38
59
|
|
|
39
60
|
```bash
|
|
40
|
-
# Install globally
|
|
41
61
|
npm install -g 0x-lang
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
Create a file called `hello.ai`:
|
|
65
|
+
|
|
66
|
+
```python
|
|
67
|
+
page Hello:
|
|
68
|
+
state name: str = "World"
|
|
69
|
+
|
|
70
|
+
layout col gap=16 padding=24 center:
|
|
71
|
+
text "Hello, {name}!" size=3xl bold
|
|
72
|
+
input name placeholder="Your name"
|
|
73
|
+
```
|
|
42
74
|
|
|
43
|
-
|
|
44
|
-
|
|
75
|
+
Compile it:
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
# Pick your framework
|
|
79
|
+
0x build hello.ai --target react
|
|
80
|
+
0x build hello.ai --target vue
|
|
81
|
+
0x build hello.ai --target svelte
|
|
82
|
+
|
|
83
|
+
# Or all three at once
|
|
84
|
+
0x build hello.ai --target react,vue,svelte
|
|
45
85
|
```
|
|
46
86
|
|
|
47
|
-
###
|
|
87
|
+
### Scaffold a project
|
|
48
88
|
|
|
49
89
|
```bash
|
|
50
|
-
|
|
90
|
+
0x init my-app
|
|
51
91
|
cd my-app
|
|
92
|
+
0x build src/counter.ai --target react
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
### Watch mode
|
|
96
|
+
|
|
97
|
+
```bash
|
|
98
|
+
0x dev counter.ai --target react
|
|
52
99
|
```
|
|
53
100
|
|
|
54
|
-
|
|
101
|
+
File changes auto-recompile.
|
|
102
|
+
|
|
103
|
+
---
|
|
55
104
|
|
|
56
|
-
|
|
105
|
+
## Examples
|
|
57
106
|
|
|
58
|
-
|
|
107
|
+
### Todo App — 24 lines
|
|
59
108
|
|
|
60
109
|
```python
|
|
61
|
-
page
|
|
62
|
-
|
|
63
|
-
derived doubled = count * 2
|
|
110
|
+
page Todo:
|
|
111
|
+
type Item = {id: int, text: str, done: bool}
|
|
64
112
|
|
|
65
|
-
|
|
66
|
-
|
|
113
|
+
state items: list[Item] = []
|
|
114
|
+
state input: str = ""
|
|
115
|
+
derived remaining = items.filter(i => !i.done).length
|
|
67
116
|
|
|
68
|
-
fn
|
|
69
|
-
|
|
117
|
+
fn add():
|
|
118
|
+
if input.trim() != "":
|
|
119
|
+
items.push({id: Date.now(), text: input, done: false})
|
|
120
|
+
input = ""
|
|
70
121
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
122
|
+
fn remove(id: int):
|
|
123
|
+
items = items.filter(i => i.id != id)
|
|
124
|
+
|
|
125
|
+
layout col gap=16 padding=24 maxWidth=600 margin=auto:
|
|
126
|
+
text "Todo ({remaining} remaining)" size=2xl bold
|
|
75
127
|
|
|
76
128
|
layout row gap=8:
|
|
77
|
-
|
|
78
|
-
button "
|
|
79
|
-
|
|
129
|
+
input input placeholder="What needs to be done?"
|
|
130
|
+
button "Add" style=primary -> add()
|
|
131
|
+
|
|
132
|
+
for item in items:
|
|
133
|
+
layout row gap=8 center:
|
|
134
|
+
toggle item.done
|
|
135
|
+
text item.text strike={item.done}
|
|
136
|
+
button "Delete" style=danger size=sm -> remove(item.id)
|
|
137
|
+
|
|
138
|
+
if items.length == 0:
|
|
139
|
+
text "Nothing to do" color=#999 center
|
|
80
140
|
```
|
|
81
141
|
|
|
82
|
-
|
|
142
|
+
Types, filtering, conditional rendering, two-way binding — all handled. Compiles to 136 lines of production React.
|
|
83
143
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
144
|
+
### Dashboard with API calls — 37 lines
|
|
145
|
+
|
|
146
|
+
```python
|
|
147
|
+
page Dashboard:
|
|
148
|
+
type Metric = {label: str, value: float, change: float}
|
|
87
149
|
|
|
88
|
-
|
|
89
|
-
|
|
150
|
+
state metrics: list[Metric] = []
|
|
151
|
+
state period: str = "week"
|
|
152
|
+
state loading: bool = true
|
|
153
|
+
|
|
154
|
+
api getMetrics = GET "/api/metrics"
|
|
155
|
+
|
|
156
|
+
on mount:
|
|
157
|
+
metrics = await getMetrics(period: period)
|
|
158
|
+
loading = false
|
|
90
159
|
|
|
91
|
-
|
|
92
|
-
|
|
160
|
+
watch period:
|
|
161
|
+
loading = true
|
|
162
|
+
metrics = await getMetrics(period: period)
|
|
163
|
+
loading = false
|
|
93
164
|
|
|
94
|
-
|
|
95
|
-
|
|
165
|
+
layout col gap=24 padding=32:
|
|
166
|
+
layout row between center:
|
|
167
|
+
text "Dashboard" size=3xl bold
|
|
168
|
+
select period options=["day", "week", "month", "year"]
|
|
169
|
+
|
|
170
|
+
if loading:
|
|
171
|
+
text "Loading..." center
|
|
172
|
+
else:
|
|
173
|
+
layout grid cols=3 gap=16:
|
|
174
|
+
for metric in metrics:
|
|
175
|
+
component MetricCard(metric)
|
|
176
|
+
|
|
177
|
+
component MetricCard:
|
|
178
|
+
prop metric: Metric
|
|
179
|
+
|
|
180
|
+
derived isPositive = metric.change >= 0
|
|
181
|
+
derived changeColor = isPositive ? "green" : "red"
|
|
182
|
+
|
|
183
|
+
layout col gap=8 padding=24 radius=12 shadow=md bg=white:
|
|
184
|
+
text metric.label size=sm color=#666
|
|
185
|
+
text "{metric.value}" size=2xl bold
|
|
186
|
+
text "{isPositive ? '↑' : '↓'} {metric.change}%" size=sm color={changeColor}
|
|
96
187
|
```
|
|
97
188
|
|
|
98
|
-
|
|
189
|
+
Async data fetching, loading states, watchers, grid layouts, reusable child components.
|
|
99
190
|
|
|
100
|
-
|
|
101
|
-
|
|
191
|
+
### Chat App — 31 lines
|
|
192
|
+
|
|
193
|
+
```python
|
|
194
|
+
page Chat:
|
|
195
|
+
type Message = {id: int, text: str, sender: str, time: datetime}
|
|
196
|
+
|
|
197
|
+
state messages: list[Message] = []
|
|
198
|
+
state input: str = ""
|
|
199
|
+
state username: str = "Me"
|
|
200
|
+
|
|
201
|
+
fn send():
|
|
202
|
+
if input.trim() != "":
|
|
203
|
+
messages.push({id: Date.now(), text: input, sender: username, time: now()})
|
|
204
|
+
input = ""
|
|
205
|
+
|
|
206
|
+
layout col height=100vh:
|
|
207
|
+
layout row center padding=16 bg=#075e54:
|
|
208
|
+
text "Chat" size=lg bold color=white
|
|
209
|
+
|
|
210
|
+
layout col gap=8 padding=16 scroll=y grow=1:
|
|
211
|
+
for msg in messages:
|
|
212
|
+
layout row {msg.sender == username ? "end" : "start"}:
|
|
213
|
+
layout col padding=12 radius=12 maxWidth="70%" bg={msg.sender == username ? "#dcf8c6" : "white"} shadow=sm:
|
|
214
|
+
text msg.text
|
|
215
|
+
text msg.time.format("HH:mm") size=xs color=#999 end
|
|
216
|
+
|
|
217
|
+
layout row gap=8 padding=16 bg=#f0f0f0:
|
|
218
|
+
input input placeholder="Type a message..." grow=1
|
|
219
|
+
button "Send" style=primary -> send()
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
### E-commerce — 44 lines
|
|
223
|
+
|
|
224
|
+
```python
|
|
225
|
+
page Shop:
|
|
226
|
+
type Product = {id: int, name: str, price: float, image: str}
|
|
227
|
+
type CartItem = {product: Product, qty: int}
|
|
228
|
+
|
|
229
|
+
state products: list[Product] = []
|
|
230
|
+
state cart: list[CartItem] = []
|
|
231
|
+
state search: str = ""
|
|
232
|
+
state loading: bool = true
|
|
233
|
+
|
|
234
|
+
derived filteredProducts = products.filter(p => p.name.includes(search))
|
|
235
|
+
derived cartTotal = cart.reduce((sum, item) => sum + item.product.price * item.qty, 0)
|
|
236
|
+
derived cartCount = cart.reduce((sum, item) => sum + item.qty, 0)
|
|
237
|
+
|
|
238
|
+
api getProducts = GET "/api/products"
|
|
239
|
+
|
|
240
|
+
on mount:
|
|
241
|
+
products = await getProducts()
|
|
242
|
+
loading = false
|
|
243
|
+
|
|
244
|
+
fn addToCart(product: Product):
|
|
245
|
+
existing = cart.find(item => item.product.id == product.id)
|
|
246
|
+
if existing:
|
|
247
|
+
existing.qty += 1
|
|
248
|
+
else:
|
|
249
|
+
cart.push({product: product, qty: 1})
|
|
250
|
+
|
|
251
|
+
fn removeFromCart(productId: int):
|
|
252
|
+
cart = cart.filter(item => item.product.id != productId)
|
|
253
|
+
|
|
254
|
+
layout col gap=24 padding=32:
|
|
255
|
+
layout row between center:
|
|
256
|
+
text "Shop" size=3xl bold
|
|
257
|
+
layout row gap=8 center:
|
|
258
|
+
text "Cart ({cartCount})" size=lg
|
|
259
|
+
text "${cartTotal}" size=lg bold color=#e74c3c
|
|
260
|
+
|
|
261
|
+
input search placeholder="Search products..."
|
|
262
|
+
|
|
263
|
+
if loading:
|
|
264
|
+
text "Loading..." center
|
|
265
|
+
else:
|
|
266
|
+
layout grid cols=3 gap=16:
|
|
267
|
+
for product in filteredProducts:
|
|
268
|
+
layout col gap=8 padding=16 radius=12 shadow=md bg=white:
|
|
269
|
+
image product.image width="100%" height=200
|
|
270
|
+
text product.name size=lg bold
|
|
271
|
+
text "${product.price}" size=md color=#e74c3c
|
|
272
|
+
button "Add to Cart" style=primary -> addToCart(product)
|
|
102
273
|
```
|
|
103
274
|
|
|
275
|
+
More examples in [`examples/`](examples/).
|
|
276
|
+
|
|
277
|
+
---
|
|
278
|
+
|
|
104
279
|
## Language Reference
|
|
105
280
|
|
|
106
|
-
###
|
|
281
|
+
### Top-level declarations
|
|
107
282
|
|
|
108
283
|
```python
|
|
109
|
-
page PageName:
|
|
110
|
-
component
|
|
111
|
-
app AppName:
|
|
284
|
+
page PageName: # Page component (has routing)
|
|
285
|
+
component ComponentName: # Reusable component
|
|
286
|
+
app AppName: # App root
|
|
112
287
|
```
|
|
113
288
|
|
|
114
|
-
### State
|
|
289
|
+
### State
|
|
115
290
|
|
|
116
291
|
```python
|
|
117
|
-
state count: int = 0
|
|
118
|
-
|
|
119
|
-
|
|
292
|
+
state count: int = 0 # Reactive state
|
|
293
|
+
state items: list[Item] = [] # Typed list
|
|
294
|
+
derived total = price * quantity # Auto-computed
|
|
295
|
+
prop title: str = "Default" # External prop
|
|
120
296
|
```
|
|
121
297
|
|
|
122
298
|
### Types
|
|
123
299
|
|
|
124
300
|
```python
|
|
125
301
|
type Item = {id: int, text: str, done: bool}
|
|
126
|
-
|
|
302
|
+
type User = {name: str, email: str, age: optional[int]}
|
|
127
303
|
```
|
|
128
304
|
|
|
305
|
+
Built-in: `int`, `float`, `str`, `bool`, `list[T]`, `optional[T]`, `datetime`, `object`
|
|
306
|
+
|
|
129
307
|
### Functions
|
|
130
308
|
|
|
131
309
|
```python
|
|
132
|
-
fn
|
|
133
|
-
|
|
134
|
-
|
|
310
|
+
fn increment():
|
|
311
|
+
count += 1
|
|
312
|
+
|
|
313
|
+
fn add(text: str):
|
|
314
|
+
items.push({id: Date.now(), text: text, done: false})
|
|
135
315
|
|
|
136
316
|
async fn fetchData():
|
|
137
317
|
data = await api.getData()
|
|
@@ -140,25 +320,47 @@ async fn fetchData():
|
|
|
140
320
|
### Layout
|
|
141
321
|
|
|
142
322
|
```python
|
|
143
|
-
layout col gap=16 padding=24:
|
|
144
|
-
layout row gap=8 center:
|
|
145
|
-
layout grid cols=3 gap=16:
|
|
323
|
+
layout col gap=16 padding=24: # Vertical (flexbox column)
|
|
324
|
+
layout row gap=8 center: # Horizontal (flexbox row)
|
|
325
|
+
layout grid cols=3 gap=16: # CSS Grid
|
|
146
326
|
```
|
|
147
327
|
|
|
328
|
+
Props: `gap`, `padding`, `margin`, `center`, `between`, `end`, `wrap`, `grow`, `maxWidth`, `bg`, `radius`, `shadow`, `scroll`
|
|
329
|
+
|
|
148
330
|
### UI Elements
|
|
149
331
|
|
|
150
332
|
```python
|
|
151
|
-
text "Hello" size=2xl bold color=#333
|
|
152
|
-
text "{variable}" size=lg
|
|
153
|
-
button "Click" style=primary -> action()
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
333
|
+
text "Hello" size=2xl bold color=#333 # Text
|
|
334
|
+
text "{variable}" size=lg # Dynamic text
|
|
335
|
+
button "Click" style=primary -> action() # Button with handler
|
|
336
|
+
button "Set" -> count = 0 # Inline mutation
|
|
337
|
+
input binding placeholder="Type..." # Two-way binding
|
|
338
|
+
toggle binding # Toggle switch
|
|
339
|
+
select binding options=["a", "b", "c"] # Dropdown
|
|
340
|
+
image src width=100 height=100 # Image
|
|
341
|
+
link "Click here" href="/path" # Link
|
|
342
|
+
```
|
|
343
|
+
|
|
344
|
+
### Styling
|
|
345
|
+
|
|
346
|
+
```python
|
|
347
|
+
text "Big" size=2xl bold color=blue # Inline props
|
|
348
|
+
button "Go" style=primary size=lg rounded=full # Presets
|
|
349
|
+
|
|
350
|
+
style card: # Named style block
|
|
351
|
+
padding: 24
|
|
352
|
+
radius: 12
|
|
353
|
+
shadow: md
|
|
354
|
+
bg: white
|
|
355
|
+
|
|
356
|
+
layout col .card: # Apply named style
|
|
357
|
+
text "Inside a card"
|
|
159
358
|
```
|
|
160
359
|
|
|
161
|
-
|
|
360
|
+
Sizes: `xs`, `sm`, `md`, `lg`, `xl`, `2xl`, `3xl`, `4xl`<br/>
|
|
361
|
+
Button styles: `primary`, `secondary`, `danger`, `outline`, `ghost`
|
|
362
|
+
|
|
363
|
+
### Control flow
|
|
162
364
|
|
|
163
365
|
```python
|
|
164
366
|
if condition:
|
|
@@ -171,8 +373,8 @@ else:
|
|
|
171
373
|
for item in items:
|
|
172
374
|
text item.name
|
|
173
375
|
|
|
174
|
-
show isVisible:
|
|
175
|
-
hide isHidden:
|
|
376
|
+
show isVisible: # Show when true
|
|
377
|
+
hide isHidden: # Hide when true
|
|
176
378
|
```
|
|
177
379
|
|
|
178
380
|
### Lifecycle & Watchers
|
|
@@ -184,165 +386,168 @@ on mount:
|
|
|
184
386
|
on destroy:
|
|
185
387
|
cleanup()
|
|
186
388
|
|
|
187
|
-
watch
|
|
188
|
-
|
|
389
|
+
watch selectedId:
|
|
390
|
+
details = await fetchDetails(selectedId)
|
|
189
391
|
```
|
|
190
392
|
|
|
191
|
-
### API
|
|
393
|
+
### API calls
|
|
192
394
|
|
|
193
395
|
```python
|
|
194
396
|
api getData = GET "/api/data"
|
|
195
|
-
api
|
|
397
|
+
api createItem = POST "/api/items"
|
|
398
|
+
api updateItem = PUT "/api/items/{id}"
|
|
399
|
+
api deleteItem = DELETE "/api/items/{id}"
|
|
400
|
+
```
|
|
196
401
|
|
|
197
|
-
|
|
402
|
+
### Validation
|
|
403
|
+
|
|
404
|
+
```python
|
|
405
|
+
check items.length <= 500 "Max 500 items"
|
|
406
|
+
check price >= 0 "Price must be positive"
|
|
198
407
|
```
|
|
199
408
|
|
|
200
|
-
|
|
409
|
+
### Events
|
|
201
410
|
|
|
202
411
|
```python
|
|
203
|
-
|
|
204
|
-
|
|
412
|
+
button "Save" -> save() # Click
|
|
413
|
+
button "Reset" -> count = 0 # Inline
|
|
414
|
+
input query @keypress=onKeyPress # Keyboard
|
|
415
|
+
```
|
|
205
416
|
|
|
206
|
-
|
|
207
|
-
state input: str = ""
|
|
208
|
-
derived remaining = items.filter(i => !i.done).length
|
|
417
|
+
---
|
|
209
418
|
|
|
210
|
-
|
|
211
|
-
if input.trim() != "":
|
|
212
|
-
items.push({id: Date.now(), text: input, done: false})
|
|
213
|
-
input = ""
|
|
419
|
+
## Programmatic API
|
|
214
420
|
|
|
215
|
-
|
|
216
|
-
items = items.filter(i => i.id != id)
|
|
421
|
+
Use 0x as a library in your own tools:
|
|
217
422
|
|
|
218
|
-
|
|
219
|
-
|
|
423
|
+
```typescript
|
|
424
|
+
import { compile } from '0x-lang';
|
|
220
425
|
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
426
|
+
const source = `
|
|
427
|
+
page Hello:
|
|
428
|
+
state name: str = "World"
|
|
429
|
+
layout col:
|
|
430
|
+
text "Hello, {name}!" size=2xl bold
|
|
431
|
+
`;
|
|
224
432
|
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
text item.text strike={item.done}
|
|
229
|
-
button "Delete" style=danger size=sm -> remove(item.id)
|
|
433
|
+
const react = compile(source, { target: 'react' });
|
|
434
|
+
const vue = compile(source, { target: 'vue' });
|
|
435
|
+
const svelte = compile(source, { target: 'svelte' });
|
|
230
436
|
|
|
231
|
-
|
|
232
|
-
|
|
437
|
+
console.log(react.code); // Full React component
|
|
438
|
+
console.log(react.lineCount); // Line count
|
|
233
439
|
```
|
|
234
440
|
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
## Benchmarks
|
|
441
|
+
### Pipeline access
|
|
238
442
|
|
|
239
|
-
|
|
443
|
+
```typescript
|
|
444
|
+
import { tokenize, parse, validate, generateReact } from '0x-lang';
|
|
240
445
|
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
446
|
+
const tokens = tokenize(source); // Lexical tokens
|
|
447
|
+
const ast = parse(source); // Abstract syntax tree
|
|
448
|
+
const result = validate(ast); // Errors & warnings
|
|
449
|
+
const output = generateReact(ast); // React JSX string
|
|
244
450
|
```
|
|
245
451
|
|
|
246
|
-
|
|
247
|
-
|---------|----------|-------------|---------|
|
|
248
|
-
| Counter | 18 | 96 | **81%** |
|
|
249
|
-
| Todo | 24 | 136 | **82%** |
|
|
250
|
-
| Dashboard | 37 | ~185 | **80%** |
|
|
251
|
-
| Chat | 31 | ~155 | **80%** |
|
|
252
|
-
| E-commerce | 44 | ~210 | **79%** |
|
|
253
|
-
|
|
254
|
-
> Lines of code compared against production TypeScript React with full inline styling and component structure.
|
|
452
|
+
### Exports
|
|
255
453
|
|
|
256
|
-
|
|
454
|
+
| Import path | What you get |
|
|
455
|
+
|:---|:---|
|
|
456
|
+
| `0x-lang` | Everything — compile, parse, tokenize, validate, generators |
|
|
457
|
+
| `0x-lang/compiler` | `compile()` only |
|
|
458
|
+
| `0x-lang/tokenizer` | `tokenize()` only |
|
|
459
|
+
| `0x-lang/parser` | `parse()` only |
|
|
460
|
+
| `0x-lang/validator` | `validate()` only |
|
|
461
|
+
| `0x-lang/generators/react` | `generateReact()` |
|
|
462
|
+
| `0x-lang/generators/vue` | `generateVue()` |
|
|
463
|
+
| `0x-lang/generators/svelte` | `generateSvelte()` |
|
|
257
464
|
|
|
258
|
-
|
|
465
|
+
---
|
|
259
466
|
|
|
260
|
-
|
|
261
|
-
cd mcp-server
|
|
262
|
-
npm install
|
|
263
|
-
npm start
|
|
264
|
-
```
|
|
467
|
+
## MCP Server
|
|
265
468
|
|
|
266
|
-
|
|
469
|
+
0x has a built-in MCP server so AI tools can compile 0x directly. The AI writes compact 0x internally and returns production framework code — the user never touches 0x.
|
|
267
470
|
|
|
268
|
-
|
|
471
|
+
### Setup
|
|
269
472
|
|
|
270
|
-
|
|
271
|
-
|
|
473
|
+
<details>
|
|
474
|
+
<summary><strong>Claude Desktop</strong></summary>
|
|
272
475
|
|
|
273
|
-
|
|
274
|
-
page Hello:
|
|
275
|
-
state name: str = "World"
|
|
276
|
-
layout col:
|
|
277
|
-
text "Hello, {name}!" size=2xl bold
|
|
278
|
-
`;
|
|
476
|
+
Add to `~/Library/Application Support/Claude/claude_desktop_config.json`:
|
|
279
477
|
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
478
|
+
```json
|
|
479
|
+
{
|
|
480
|
+
"mcpServers": {
|
|
481
|
+
"0x": {
|
|
482
|
+
"command": "npx",
|
|
483
|
+
"args": ["-y", "0x-lang-mcp-server"]
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
}
|
|
284
487
|
```
|
|
285
488
|
|
|
286
|
-
|
|
489
|
+
</details>
|
|
287
490
|
|
|
288
|
-
|
|
289
|
-
|
|
491
|
+
<details>
|
|
492
|
+
<summary><strong>Cursor</strong></summary>
|
|
493
|
+
|
|
494
|
+
Add to `.cursor/mcp.json`:
|
|
290
495
|
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
496
|
+
```json
|
|
497
|
+
{
|
|
498
|
+
"mcpServers": {
|
|
499
|
+
"0x": {
|
|
500
|
+
"command": "npx",
|
|
501
|
+
"args": ["-y", "0x-lang-mcp-server"]
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
}
|
|
297
505
|
```
|
|
298
506
|
|
|
299
|
-
|
|
507
|
+
</details>
|
|
300
508
|
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
0x dev <file.ai> --target <target>
|
|
304
|
-
0x bench <file.ai>
|
|
305
|
-
0x init [project-name]
|
|
509
|
+
<details>
|
|
510
|
+
<summary><strong>Smithery</strong></summary>
|
|
306
511
|
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
--output, -o Output directory (default: ./dist/)
|
|
310
|
-
--help, -h Show help
|
|
512
|
+
```bash
|
|
513
|
+
npx @smithery/cli install 0x
|
|
311
514
|
```
|
|
312
515
|
|
|
313
|
-
|
|
516
|
+
Or use the hosted server directly: https://server.smithery.ai/hankim/ox-lang
|
|
517
|
+
|
|
518
|
+
</details>
|
|
519
|
+
|
|
520
|
+
### Tools
|
|
521
|
+
|
|
522
|
+
| Tool | What it does |
|
|
523
|
+
|:---|:---|
|
|
524
|
+
| `0x_compile` | Compile 0x source → React / Vue / Svelte |
|
|
525
|
+
| `0x_reference` | Get the full syntax reference |
|
|
526
|
+
| `0x_examples` | Browse example code (counter, todo, chat, dashboard, ecommerce) |
|
|
527
|
+
|
|
528
|
+
### How it works
|
|
314
529
|
|
|
315
530
|
```
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
└── Svelte 5
|
|
531
|
+
You: "Build me a todo app in React"
|
|
532
|
+
AI: [0x_reference] → writes 30 lines of 0x → [0x_compile] → 140 lines of React
|
|
533
|
+
You: Gets a working React component
|
|
320
534
|
```
|
|
321
535
|
|
|
322
|
-
The compiler
|
|
536
|
+
The AI generates ~30 lines of 0x internally. The compiler outputs ~140 lines of production code. Fewer tokens generated, fewer mistakes made, better results delivered.
|
|
323
537
|
|
|
324
|
-
|
|
325
|
-
2. **Parser** — Recursive descent parser producing a typed AST
|
|
326
|
-
3. **Validator** — Static analysis (circular deps, unused state, type checks)
|
|
327
|
-
4. **Generators** — Target-specific code generation (React/Vue/Svelte)
|
|
538
|
+
See [mcp-server/](mcp-server/) for full docs.
|
|
328
539
|
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
0x is purpose-built for AI-assisted development ("vibe coding"). When AI agents use 0x instead of writing raw React/Vue/Svelte:
|
|
540
|
+
---
|
|
332
541
|
|
|
333
|
-
|
|
334
|
-
- **Zero structural decisions** — No hallucination-prone choices (hooks vs classes, CSS-in-JS vs Tailwind, etc.)
|
|
335
|
-
- **Multi-framework output** — One generation covers React, Vue, AND Svelte users
|
|
336
|
-
- **MCP integration** — Claude and Cursor can compile 0x directly via the built-in MCP server
|
|
337
|
-
- **Deterministic output** — Same 0x input always produces the same framework output
|
|
542
|
+
## For AI agent builders
|
|
338
543
|
|
|
339
|
-
If you're building
|
|
544
|
+
If you're building tools that generate UI code, 0x works well as an intermediate representation:
|
|
340
545
|
|
|
341
546
|
```typescript
|
|
342
|
-
// AI agent workflow example
|
|
343
547
|
import { compile } from '0x-lang/compiler';
|
|
344
548
|
|
|
345
|
-
|
|
549
|
+
// Your AI generates compact 0x
|
|
550
|
+
const aiOutput = `
|
|
346
551
|
page Dashboard:
|
|
347
552
|
state metrics: list[object] = []
|
|
348
553
|
|
|
@@ -352,44 +557,90 @@ page Dashboard:
|
|
|
352
557
|
layout col gap=24 padding=32:
|
|
353
558
|
text "Dashboard" size=3xl bold
|
|
354
559
|
layout grid cols=3 gap=16:
|
|
355
|
-
|
|
560
|
+
for metric in metrics:
|
|
356
561
|
layout col padding=20 bg=white rounded=lg shadow:
|
|
357
|
-
text "{
|
|
358
|
-
text "{
|
|
562
|
+
text "{metric.label}" size=sm color=gray
|
|
563
|
+
text "{metric.value}" size=2xl bold
|
|
359
564
|
`;
|
|
360
565
|
|
|
361
|
-
// Compile to
|
|
362
|
-
const react
|
|
363
|
-
const vue
|
|
364
|
-
const svelte = compile(
|
|
566
|
+
// Compile to whatever the user needs
|
|
567
|
+
const react = compile(aiOutput, { target: 'react' });
|
|
568
|
+
const vue = compile(aiOutput, { target: 'vue' });
|
|
569
|
+
const svelte = compile(aiOutput, { target: 'svelte' });
|
|
365
570
|
```
|
|
366
571
|
|
|
367
|
-
|
|
572
|
+
Why this matters for AI:
|
|
368
573
|
|
|
369
|
-
|
|
574
|
+
- **80% fewer output tokens** — less generation cost, lower latency
|
|
575
|
+
- **One syntax, zero decisions** — no "which React pattern?" hallucinations
|
|
576
|
+
- **Multi-framework** — one generation covers React, Vue, and Svelte users
|
|
577
|
+
- **Deterministic** — same input always produces the same output
|
|
578
|
+
- **Validated** — the compiler catches errors before they reach the user
|
|
579
|
+
|
|
580
|
+
---
|
|
581
|
+
|
|
582
|
+
## Architecture
|
|
583
|
+
|
|
584
|
+
```
|
|
585
|
+
Source (.ai)
|
|
586
|
+
↓
|
|
587
|
+
Tokenizer ─── Indentation-aware lexical analysis
|
|
588
|
+
↓
|
|
589
|
+
Parser ────── Recursive descent → typed AST
|
|
590
|
+
↓
|
|
591
|
+
Validator ─── Circular deps · unused state · type checks
|
|
592
|
+
↓
|
|
593
|
+
Generator
|
|
594
|
+
├── React ─── JSX + hooks + CSS-in-JS
|
|
595
|
+
├── Vue ───── SFC + Composition API + scoped styles
|
|
596
|
+
└── Svelte ── Runes ($state, $derived) + styles
|
|
597
|
+
```
|
|
598
|
+
|
|
599
|
+
~9,800 lines of TypeScript. Zero runtime dependencies.
|
|
600
|
+
|
|
601
|
+
| Module | Lines | |
|
|
602
|
+
|:---|---:|:---|
|
|
603
|
+
| Parser | 3,560 | Recursive descent, full AST |
|
|
604
|
+
| React Generator | 2,423 | JSX + hooks + memo + styling |
|
|
605
|
+
| AST Types | 1,065 | TypeScript definitions |
|
|
606
|
+
| Vue Generator | 822 | SFC with `<script setup>` and `ref()` |
|
|
607
|
+
| Svelte Generator | 743 | Svelte 5 with `$state()` and `$derived()` |
|
|
608
|
+
| Validator | 355 | Static analysis + error reporting |
|
|
609
|
+
| Tokenizer | 348 | Indentation-aware lexer |
|
|
610
|
+
| CLI | 223 | build · dev · bench · init |
|
|
611
|
+
|
|
612
|
+
## CLI
|
|
613
|
+
|
|
614
|
+
```
|
|
615
|
+
0x build <file.ai> --target <target> [--output <dir>]
|
|
616
|
+
0x dev <file.ai> --target <target>
|
|
617
|
+
0x bench <file.ai>
|
|
618
|
+
0x init [project-name]
|
|
619
|
+
|
|
620
|
+
Flags:
|
|
621
|
+
--target, -t react, vue, svelte (comma-separated)
|
|
622
|
+
--output, -o Output directory (default: ./dist/)
|
|
623
|
+
--help, -h Show help
|
|
624
|
+
```
|
|
625
|
+
|
|
626
|
+
## Contributing
|
|
370
627
|
|
|
371
628
|
```bash
|
|
372
|
-
# Clone the repo
|
|
373
629
|
git clone https://github.com/hankimis/0x-lang.git
|
|
374
630
|
cd 0x-lang
|
|
375
|
-
|
|
376
|
-
# Install dependencies
|
|
377
631
|
npm install
|
|
378
|
-
|
|
379
|
-
# Run tests
|
|
380
|
-
npm test
|
|
381
|
-
|
|
382
|
-
# Build
|
|
632
|
+
npm test # 2,600+ lines of tests
|
|
383
633
|
npm run build
|
|
384
634
|
```
|
|
385
635
|
|
|
636
|
+
Tests cover tokenizer, parser, validator, all three generators, benchmarks, and integration. See [CONTRIBUTING.md](CONTRIBUTING.md).
|
|
637
|
+
|
|
386
638
|
## License
|
|
387
639
|
|
|
388
|
-
[ISC](LICENSE) —
|
|
640
|
+
[ISC](LICENSE) — free for everything.
|
|
389
641
|
|
|
390
642
|
---
|
|
391
643
|
|
|
392
644
|
<p align="center">
|
|
393
|
-
<
|
|
394
|
-
<a href="https://0xlang.com">Website</a> · <a href="https://github.com/hankimis/0x-lang">GitHub</a> · <a href="https://www.npmjs.com/package/0x-lang">npm</a>
|
|
645
|
+
<a href="https://0xlang.com">0xlang.com</a> · <a href="https://github.com/hankimis/0x-lang">GitHub</a> · <a href="https://www.npmjs.com/package/0x-lang">npm</a> · <a href="https://smithery.ai/server/0x">Smithery</a> · <a href="https://server.smithery.ai/hankim/ox-lang">Smithery Server</a>
|
|
395
646
|
</p>
|