ling-lang 2030.1.2

Ling - The Omniglot Systems Language
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
// examples/visualize.ling — Ling source → SVG pattern visualizer
// Usage:   ling run examples/visualize.ling <input.ling> [output.svg]
//
// Generates a dark-themed SVG where each function in the source file
// becomes a geometric pattern panel — style inspired by AI crypto-fill.

// ─── Geometry helpers ────────────────────────────────────────────────────────

fn hsl_color(hue, sat, lit) {
    bind r = floor(lit + sat * sin(hue * 6.2832))
    bind g = floor(lit + sat * sin(hue * 6.2832 + 2.0944))
    bind b = floor(lit + sat * sin(hue * 6.2832 + 4.1888))
    bind r = max(0.0, min(255.0, r))
    bind g = max(0.0, min(255.0, g))
    bind b = max(0.0, min(255.0, b))
    return "rgb(" + to_str(r) + "," + to_str(g) + "," + to_str(b) + ")"
}

fn f(n) {
    return to_str(floor(n * 10.0) / 10.0)
}

fn reg_mark(mx, my) {
    bind sz = 10.0
    bind s = "<circle cx='" + f(mx) + "' cy='" + f(my) + "' r='2.5' fill='#FFD700'/>"
    bind s = s + "<line x1='" + f(mx - sz) + "' y1='" + f(my) + "' x2='" + f(mx + sz) + "' y2='" + f(my) + "' stroke='#FFD700' stroke-width='0.5'/>"
    bind s = s + "<line x1='" + f(mx) + "' y1='" + f(my - sz) + "' x2='" + f(mx) + "' y2='" + f(my + sz) + "' stroke='#FFD700' stroke-width='0.5'/>"
    return s
}

fn detect_vtex(body) {
    if str_find(body, "vtex_rings") >= 0 { return "#00e5ff" }
    if str_find(body, "vtex_flower") >= 0 { return "#ff79c6" }
    if str_find(body, "vtex_spiral") >= 0 { return "#00ffb3" }
    if str_find(body, "vtex_lotus") >= 0 { return "#ff5e8a" }
    if str_find(body, "vtex_chakra") >= 0 { return "#bd93f9" }
    if str_find(body, "vtex_yantra") >= 0 { return "#ffb86c" }
    if str_find(body, "vtex_star") >= 0 { return "#ffd700" }
    if str_find(body, "vtex_hyperbolic") >= 0 { return "#5575c8" }
    if str_find(body, "vtex_tess") >= 0 { return "#50fa7b" }
    if str_find(body, "vtex_grid") >= 0 { return "#8be9fd" }
    if str_find(body, "vtex_halftone") >= 0 { return "#888899" }
    if str_find(body, "vtex_letter_rain") >= 0 { return "#f1fa8c" }
    if str_find(body, "audio_") >= 0 { return "#ff1493" }
    if str_find(body, "camera") >= 0 { return "#ffe066" }
    if str_find(body, "light") >= 0 { return "#ffe4b5" }
    return "#6688aa"
}

// ─── Pattern generators (each returns an SVG fragment string) ────────────────

fn pat_dot_grid(ox, oy, w, h, color, phase) {
    bind s = ""
    bind rows = 5
    bind cols = 6
    bind r = 0
    while r < rows {
        bind c = 0
        while c < cols {
            bind t = (r * cols + c) / (rows * cols)
            bind cx = ox + (c + 0.5) * w / cols
            bind cy = oy + (r + 0.5) * h / rows
            bind dr = 2.0 + 3.5 * abs(sin(t * 6.28 * 3.0 + phase * 9.42))
            bind op = 0.35 + 0.65 * abs(sin(t * 6.28 + phase * 5.0))
            bind s = s + "<circle cx='" + f(cx) + "' cy='" + f(cy) + "' r='" + f(dr) + "' fill='" + color + "' opacity='" + f(op) + "'/>"
            bind c = c + 1
        }
        bind r = r + 1
    }
    return s
}

fn pat_waveform(ox, oy, w, h, color, phase) {
    bind s = ""
    bind steps = 36
    bind pts1 = ""
    bind pts2 = ""
    bind pts3 = ""
    bind i = 0
    while i <= steps {
        bind t = i / steps
        bind px = ox + t * w
        bind sep = if i == 0 { "" } else { " " }
        bind py1 = oy + h * 0.35 + h * 0.22 * sin(t * 6.28 * 2.5 + phase * 12.56)
        bind py2 = oy + h * 0.55 + h * 0.15 * sin(t * 6.28 * 4.0 + phase * 18.84)
        bind py3 = oy + h * 0.72 + h * 0.10 * sin(t * 6.28 * 6.0 + phase * 25.0)
        bind pts1 = pts1 + sep + f(px) + "," + f(py1)
        bind pts2 = pts2 + sep + f(px) + "," + f(py2)
        bind pts3 = pts3 + sep + f(px) + "," + f(py3)
        bind i = i + 1
    }
    bind s = s + "<polyline points='" + pts1 + "' fill='none' stroke='" + color + "' stroke-width='2' opacity='0.85'/>"
    bind s = s + "<polyline points='" + pts2 + "' fill='none' stroke='" + color + "' stroke-width='1.5' opacity='0.55'/>"
    bind s = s + "<polyline points='" + pts3 + "' fill='none' stroke='" + color + "' stroke-width='1' opacity='0.30'/>"
    return s
}

fn pat_spectral_grid(ox, oy, w, h, color, phase) {
    bind s = ""
    bind rows = 8
    bind cols = 8
    bind r = 0
    while r < rows {
        bind c = 0
        while c < cols {
            bind t = (r * cols + c) / (rows * cols)
            bind active = sin(t * 6.28 * 7.0 + phase * 18.0 + r * 0.55 + c * 0.33) > 0.15
            bind elem = if active {
                "<rect x='" + f(ox + c * w / cols + 1.5) + "' y='" + f(oy + r * h / rows + 1.5) + "' width='" + f(w / cols - 3.0) + "' height='" + f(h / rows - 3.0) + "' fill='" + color + "' opacity='" + f(0.25 + 0.55 * abs(sin(t * 6.28 + phase * 6.28))) + "' rx='2'/>"
            } else {
                ""
            }
            bind s = s + elem
            bind c = c + 1
        }
        bind r = r + 1
    }
    return s
}

fn pat_radial_rings(ox, oy, w, h, color, phase) {
    bind s = ""
    bind cx = ox + w * 0.5
    bind cy = oy + h * 0.5
    bind max_r = min(w, h) * 0.44
    bind rings = 4
    bind r = 1
    while r <= rings {
        bind rad = max_r * r / rings
        bind n_dots = 6 + r * 4
        bind d = 0
        while d < n_dots {
            bind angle = d * 6.2832 / n_dots + phase * 6.2832 + r * 0.52
            bind px = cx + rad * cos(angle)
            bind py = cy + rad * sin(angle)
            bind dr = 1.2 + 2.0 * (1.0 - r / (rings + 1.0))
            bind s = s + "<circle cx='" + f(px) + "' cy='" + f(py) + "' r='" + f(dr) + "' fill='" + color + "' opacity='0.8'/>"
            bind d = d + 1
        }
        bind r = r + 1
    }
    return s
}

fn pat_barcode(ox, oy, w, h, color, phase) {
    bind s = ""
    bind n = 40
    bind bw = w / n
    bind i = 0
    while i < n {
        bind t = i / n
        bind bh = h * (0.25 + 0.75 * abs(sin(t * 6.28 * 6.0 + phase * 31.4)))
        bind bx = ox + i * bw
        bind by = oy + (h - bh) * 0.5
        bind op = 0.3 + 0.65 * abs(sin(t * 3.14 + phase * 6.28))
        bind s = s + "<rect x='" + f(bx) + "' y='" + f(by) + "' width='" + f(bw * 0.62) + "' height='" + f(bh) + "' fill='" + color + "' opacity='" + f(op) + "'/>"
        bind i = i + 1
    }
    return s
}

fn pat_branch_lines(ox, oy, w, h, color, phase) {
    bind s = ""
    bind n = 18
    bind cx = ox + w * 0.5
    bind cy = oy + h * 0.88
    bind i = 0
    while i < n {
        bind frac = i / n
        bind angle = -1.5708 + (frac - 0.5) * 2.2 + phase * 0.4
        bind len = h * (0.5 + 0.42 * abs(sin(i * 0.71 + phase * 3.14)))
        bind x2 = cx + len * cos(angle)
        bind y2 = cy + len * sin(angle)
        bind op = 0.25 + 0.6 * abs(sin(i * 0.5 + phase * 6.28))
        bind sw = 0.6 + 1.4 * abs(sin(i * 0.9 + phase * 2.0))
        bind s = s + "<line x1='" + f(cx) + "' y1='" + f(cy) + "' x2='" + f(x2) + "' y2='" + f(y2) + "' stroke='" + color + "' stroke-width='" + f(sw) + "' opacity='" + f(op) + "'/>"
        bind i = i + 1
    }
    return s
}

fn safe_id(name) {
    bind s = str_replace(name, " ", "_")
    bind s = str_replace(s, "(", "_")
    bind s = str_replace(s, ")", "_")
    bind s = str_replace(s, "{", "_")
    bind s = str_replace(s, "}", "_")
    bind s = str_replace(s, "-", "_")
    bind s = str_replace(s, ".", "_")
    return s
}

// ─── Render one function panel ───────────────────────────────────────────────

fn render_panel(fn_name, fn_body, px, py, pw, ph) {
    bind hue   = hash_str(fn_name)
    bind hue2  = hash_str(fn_name + "b")
    bind pt    = floor(hash_int(fn_name + "pt", 6))
    bind accent = detect_vtex(fn_body)

    bind fg    = hsl_color(hue, 90.0, 128.0)
    bind bg    = hsl_color(hue2, 20.0, 30.0)

    bind id    = safe_id(fn_name)
    bind s = ""

    // Panel background
    bind s = s + "<rect x='" + f(px) + "' y='" + f(py) + "' width='" + f(pw) + "' height='" + f(ph) + "' fill='" + bg + "' fill-opacity='0.18' rx='5'/>"
    bind s = s + "<rect x='" + f(px) + "' y='" + f(py) + "' width='" + f(pw) + "' height='" + f(ph) + "' fill='none' stroke='" + fg + "' stroke-width='0.8' rx='5' opacity='0.5'/>"

    // Accent stripe (vtex color)
    bind s = s + "<rect x='" + f(px) + "' y='" + f(py) + "' width='4' height='" + f(ph) + "' fill='" + accent + "' rx='3' opacity='0.9'/>"

    // Clip rect for pattern area
    bind clip_id = "clip_" + id
    bind s = s + "<clipPath id='" + clip_id + "'><rect x='" + f(px + 5.0) + "' y='" + f(py + 2.0) + "' width='" + f(pw - 7.0) + "' height='" + f(ph - 18.0) + "'/></clipPath>"
    bind s = s + "<g clip-path='url(#" + clip_id + ")'>"

    // Pattern body
    bind pattern = if pt == 0 {
        pat_dot_grid(px + 5.0, py + 2.0, pw - 7.0, ph - 18.0, fg, hue)
    } else {
        if pt == 1 {
            pat_waveform(px + 5.0, py + 2.0, pw - 7.0, ph - 18.0, fg, hue)
        } else {
            if pt == 2 {
                pat_spectral_grid(px + 5.0, py + 2.0, pw - 7.0, ph - 18.0, fg, hue)
            } else {
                if pt == 3 {
                    pat_radial_rings(px + 5.0, py + 2.0, pw - 7.0, ph - 18.0, fg, hue)
                } else {
                    if pt == 4 {
                        pat_barcode(px + 5.0, py + 2.0, pw - 7.0, ph - 18.0, fg, hue)
                    } else {
                        pat_branch_lines(px + 5.0, py + 2.0, pw - 7.0, ph - 18.0, fg, hue)
                    }
                }
            }
        }
    }
    bind s = s + pattern
    bind s = s + "</g>"

    // Function label at bottom
    bind label_y = py + ph - 5.0
    bind s = s + "<text x='" + f(px + 8.0) + "' y='" + f(label_y) + "' font-family='monospace' font-size='8.5' fill='" + accent + "' opacity='0.95'>" + fn_name + "</text>"

    return s
}

// ─── Extract basename from path ──────────────────────────────────────────────

fn extract_basename(path) {
    bind result = path
    bind searching = 1
    while searching == 1 {
        bind pos = str_find(result, "/")
        bind result = if pos >= 0 { substr(result, pos + 1, 9999) } else { result }
        bind searching = if pos >= 0 { 1 } else { 0 }
    }
    bind searching = 1
    while searching == 1 {
        bind pos = str_find(result, "\\")
        bind result = if pos >= 0 { substr(result, pos + 1, 9999) } else { result }
        bind searching = if pos >= 0 { 1 } else { 0 }
    }
    return result
}

// ─── Entry point ─────────────────────────────────────────────────────────────

bind start = do {
    // ── Parse CLI args ──────────────────────────────────────────────────────
    bind all_args = get_args()
    bind n_args = len(all_args)

    bind input  = ""
    bind output = ""
    bind i = 1
    while i < n_args {
        bind a = list_get(all_args, i)
        bind is_ling = ends_with(a, ".ling")
        bind is_self = ends_with(a, "visualize.ling")
        bind is_svg  = ends_with(a, ".svg")

        bind input = if is_ling {
            if is_self { input }
            else {
                if input == "" { a } else { input }
            }
        } else { input }

        bind output = if is_svg {
            if output == "" { a } else { output }
        } else { output }

        bind i = i + 1
    }

    if input == "" {
        print("usage: ling run examples/visualize.ling <input.ling> [output.svg]")
        return ()
    }

    bind output = if output == "" { input + ".svg" } else { output }

    // ── Read source ─────────────────────────────────────────────────────────
    bind source = read_file(input)
    bind lines  = split(source, "\n")
    bind n_lines = len(lines)

    // ── Scan for function / entry-point definitions ─────────────────────────
    bind fn_names  = list_new()
    bind fn_bodies = list_new()

    bind current_fn   = ""
    bind current_body = ""
    bind i = 0

    while i < n_lines {
        bind line     = list_get(lines, i)
        bind trimmed  = trim(line)

        // Detect function start: "fn name(" or Thai/Chinese variants
        bind is_fn = starts_with(trimmed, "fn ")
        bind is_th = starts_with(trimmed, "ฟังก์ชัน ")
        bind is_zh = starts_with(trimmed, "函 ")
        bind is_ko = starts_with(trimmed, "함수 ")
        bind is_new_def = is_fn || is_th || is_zh || is_ko

        // Save the previous function when starting a new definition
        bind fn_names = if is_new_def {
            if current_fn != "" { list_push(fn_names, current_fn) } else { fn_names }
        } else { fn_names }
        bind fn_bodies = if is_new_def {
            if current_fn != "" { list_push(fn_bodies, current_body) } else { fn_bodies }
        } else { fn_bodies }

        // Compute name prefix offset (number of Unicode chars in keyword + space)
        // ฟังก์ชัน = 8 chars + 1 space = 9; 함수 = 2 chars + 1 space = 3
        bind kw_len = if is_fn { 3 }
            else {
                if is_th { 9 }
                else {
                    if is_zh { 2 }
                    else { 3 }
                }
            }

        // Extract new function name
        bind pos_open = str_find(trimmed, "(")
        bind raw_name = if pos_open >= 0 {
            substr(trimmed, kw_len, pos_open - kw_len)
        } else {
            substr(trimmed, kw_len, 60)
        }
        bind new_fn_name = trim(raw_name)

        // Update state
        bind current_fn   = if is_new_def { new_fn_name } else { current_fn }
        bind current_body = if is_new_def { "" } else {
            if current_fn != "" { current_body + " " + trimmed } else { current_body }
        }

        bind i = i + 1
    }

    // Save the last function
    bind fn_names  = if current_fn != "" { list_push(fn_names, current_fn) } else { fn_names }
    bind fn_bodies = if current_fn != "" { list_push(fn_bodies, current_body) } else { fn_bodies }

    bind n_fns = len(fn_names)
    print("found " + to_str(n_fns) + " functions in " + input)

    // ── SVG layout ──────────────────────────────────────────────────────────
    bind cols    = 4.0
    bind cell_w  = 200.0
    bind cell_h  = 200.0
    bind pad     = 10.0
    bind sidebar = 170.0

    bind rows    = ceil(n_fns / cols)
    bind total_w = sidebar + pad + cols * (cell_w + pad)
    bind total_h = 60.0 + rows * (cell_h + pad) + 50.0

    // ── Build SVG ───────────────────────────────────────────────────────────
    bind svg = ""
    bind svg = svg + "<svg xmlns='http://www.w3.org/2000/svg' width='" + f(total_w) + "' height='" + f(total_h) + "'>\n"

    // Defs: glow filter + background grid
    bind svg = svg + "<defs>"
    bind svg = svg + "<filter id='glow' x='-20%' y='-20%' width='140%' height='140%'>"
    bind svg = svg + "<feGaussianBlur stdDeviation='3' result='b'/>"
    bind svg = svg + "<feMerge><feMergeNode in='b'/><feMergeNode in='SourceGraphic'/></feMerge>"
    bind svg = svg + "</filter>"
    bind svg = svg + "<pattern id='bg_grid' width='20' height='20' patternUnits='userSpaceOnUse'>"
    bind svg = svg + "<path d='M 20 0 L 0 0 0 20' fill='none' stroke='#ffffff' stroke-width='0.2' opacity='0.06'/>"
    bind svg = svg + "</pattern>"
    bind svg = svg + "</defs>\n"

    // Background
    bind svg = svg + "<rect width='" + f(total_w) + "' height='" + f(total_h) + "' fill='#090912'/>\n"
    bind svg = svg + "<rect width='" + f(total_w) + "' height='" + f(total_h) + "' fill='url(#bg_grid)'/>\n"

    // Sidebar background
    bind svg = svg + "<rect x='0' y='0' width='" + f(sidebar) + "' height='" + f(total_h) + "' fill='#0c0c1f'/>\n"

    // Title
    bind basename = extract_basename(input)
    bind svg = svg + "<text x='12' y='34' font-family='monospace' font-size='16' fill='#e8e8f8' font-weight='bold'>" + basename + "</text>\n"
    bind svg = svg + "<text x='12' y='50' font-family='monospace' font-size='10' fill='#6688aa'>" + to_str(n_fns) + " functions</text>\n"

    // Sidebar legend header
    bind svg = svg + "<text x='8' y='74' font-family='monospace' font-size='9' fill='#aaaacc' font-weight='bold'>PATTERNS</text>\n"

    bind legend_items = list_new()
    bind legend_items = list_push(legend_items, "dot grid")
    bind legend_items = list_push(legend_items, "waveform")
    bind legend_items = list_push(legend_items, "spectral")
    bind legend_items = list_push(legend_items, "radial")
    bind legend_items = list_push(legend_items, "barcode")
    bind legend_items = list_push(legend_items, "branches")

    bind li = 0
    while li < 6 {
        bind lhue = li / 6.0
        bind lc = hsl_color(lhue, 100.0, 128.0)
        bind ly = 88.0 + li * 16.0
        bind svg = svg + "<rect x='8' y='" + f(ly - 6.0) + "' width='10' height='10' fill='" + lc + "' rx='2' opacity='0.8'/>"
        bind svg = svg + "<text x='22' y='" + f(ly + 2.0) + "' font-family='monospace' font-size='8.5' fill='" + lc + "'>" + list_get(legend_items, li) + "</text>\n"
        bind li = li + 1
    }

    // ── Render function panels ──────────────────────────────────────────────
    bind i = 0
    while i < n_fns {
        bind fn_name = list_get(fn_names, i)
        bind fn_body = list_get(fn_bodies, i)

        bind col_idx = i % cols
        bind row_idx = floor(i / cols)
        bind px = sidebar + pad + col_idx * (cell_w + pad)
        bind py = 60.0 + row_idx * (cell_h + pad)

        bind panel = render_panel(fn_name, fn_body, px, py, cell_w, cell_h)
        bind svg = svg + panel + "\n"

        bind i = i + 1
    }

    // ── Registration marks (corners) ────────────────────────────────────────
    bind svg = svg + reg_mark(12.0, 12.0)
    bind svg = svg + reg_mark(total_w - 12.0, 12.0)
    bind svg = svg + reg_mark(12.0, total_h - 12.0)
    bind svg = svg + reg_mark(total_w - 12.0, total_h - 12.0)

    // ── Footer ──────────────────────────────────────────────────────────────
    bind svg = svg + "<text x='" + f(total_w * 0.5) + "' y='" + f(total_h - 8.0) + "' text-anchor='middle' font-family='monospace' font-size='8' fill='#333355'>ling visualize · " + basename + "</text>\n"

    bind svg = svg + "</svg>"

    // ── Write output ────────────────────────────────────────────────────────
    write_file(output, svg)
    print("written: " + output)
}