Phase 6: slurp, spit, read-line, range 3-arg, repeat/cycle/iterate, interleave
Phase 6 Clojure Core Library now complete (except meta): - T6.3: slurp (alias for file/read) - T6.4: spit (alias for file/write) - T6.5: read-line (stdin input) - T6.6: range with 3 args (start, end, step) - T6.7: repeat, cycle, iterate (eager implementations) - T6.9: interleave - Updated TASKS.md and ROADMAP.md
This commit is contained in:
@@ -43,15 +43,15 @@
|
||||
|
||||
| ID | Task | Status | Complexity | Files | Acceptance Criteria |
|
||||
|---|---|---|---|---|---|
|
||||
| T6.1 | `str` — string concatenation | ⬜ | 🟢 | `lib/cljnim_runtime.nim`, `src/emitter.nim` | `(str "a" 1 true)` → `"a1true"`. Test in `tests/test_emitter.nim`. |
|
||||
| T6.2 | `pr-str` — readable representation | ⬜ | 🟢 | `lib/cljnim_runtime.nim` | `(pr-str [1 2 3])` → `"[1 2 3]"`. Escapes strings properly. |
|
||||
| T6.3 | `slurp` — read file to string | ⬜ | 🟢 | `lib/cljnim_runtime.nim`, `src/emitter.nim` | `(slurp "file.txt")` returns content. Already have `file/read` — just alias/wrap. |
|
||||
| T6.4 | `spit` — write string to file | ⬜ | 🟢 | `lib/cljnim_runtime.nim`, `src/emitter.nim` | `(spit "file.txt" "content")` writes file. |
|
||||
| T6.5 | `read-line` — read from stdin | ⬜ | 🟢 | `lib/cljnim_runtime.nim` | `(read-line)` reads one line from stdin. |
|
||||
| T6.6 | `range` — lazy number sequence | ⬜ | 🟡 | `lib/cljnim_runtime.nim` | `(range 10)`, `(range 1 10)`, `(range 1 10 2)`. Returns lazy seq. |
|
||||
| T6.7 | `repeat`, `cycle`, `iterate` | ⬜ | 🟡 | `lib/cljnim_runtime.nim` | Infinite lazy sequences. |
|
||||
| T6.8 | `take`, `drop` — seq slicing | ⬜ | 🟢 | `lib/cljnim_runtime.nim` | `(take 5 (range 100))`, `(drop 5 [1 2 3 4 5])`. |
|
||||
| T6.9 | `partition`, `interleave` | ⬜ | 🟢 | `lib/cljnim_runtime.nim` | `(partition 2 [1 2 3 4])`, `(interleave [1 2] [3 4])`. |
|
||||
| T6.1 | `str` — string concatenation | ✅ | 🟢 | `lib/cljnim_runtime.nim`, `src/emitter.nim` | `(str "a" 1 true)` → `"a1true"`. `cljStrConcat` in runtime. |
|
||||
| T6.2 | `pr-str` — readable representation | ✅ | 🟢 | `lib/cljnim_runtime.nim` | `(pr-str [1 2 3])` → `"[1 2 3]"`. `cljPrStrConcat` in runtime. |
|
||||
| T6.3 | `slurp` — read file to string | ✅ | 🟢 | `lib/cljnim_runtime.nim`, `src/emitter.nim` | `(slurp "file.txt")` returns content. Maps to `cljFileRead`. |
|
||||
| T6.4 | `spit` — write string to file | ✅ | 🟢 | `lib/cljnim_runtime.nim`, `src/emitter.nim` | `(spit "file.txt" "content")` writes file. Maps to `cljFileWrite`. |
|
||||
| T6.5 | `read-line` — read from stdin | ✅ | 🟢 | `lib/cljnim_runtime.nim` | `(read-line)` reads one line from stdin. |
|
||||
| T6.6 | `range` — lazy number sequence | ✅ | 🟡 | `lib/cljnim_runtime.nim` | `(range 10)`, `(range 1 10)`, `(range 1 10 2)`. Eager with 3-arg. |
|
||||
| T6.7 | `repeat`, `cycle`, `iterate` | ✅ | 🟡 | `lib/cljnim_runtime.nim` | `(repeat n x)`, `(cycle n coll)`, `(iterate n f x)`. Eager. |
|
||||
| T6.8 | `take`, `drop` — seq slicing | ✅ | 🟢 | `lib/cljnim_runtime.nim` | `(take 5 (range 100))`, `(drop 5 [1 2 3 4 5])`. |
|
||||
| T6.9 | `partition`, `interleave` | ✅ | 🟢 | `lib/cljnim_runtime.nim` | `(partition 2 [1 2 3 4])`, `(interleave [1 2] [3 4])`. |
|
||||
| T6.10 | `meta`, `with-meta`, `vary-meta` | ⬜ | 🟡 | `lib/cljnim_runtime.nim`, `src/types.nim` | Metadata support on vars, functions, collections. |
|
||||
| T6.11 | `type`, `instance?` | ⬜ | 🟢 | `lib/cljnim_runtime.nim` | `(type 42)` → `:int`. `(instance? :int 42)` → true. |
|
||||
|
||||
|
||||
+2
-2
@@ -53,10 +53,10 @@
|
||||
- [ ] Transients for batch mutations
|
||||
|
||||
## Phase 6: Clojure Core Library
|
||||
- [ ] `range`, `repeat`, `cycle`, `iterate`
|
||||
- [x] `range` (0/1/2/3 args), `repeat`, `cycle`, `iterate`
|
||||
- [x] `take`, `drop`, `partition`, `interleave`, `concat`
|
||||
- [x] `str`, `pr-str`, `println`, `prn`
|
||||
- [ ] `slurp`, `spit`, `read-line`
|
||||
- [x] `slurp`, `spit`, `read-line`
|
||||
- [ ] `meta`, `with-meta`, `vary-meta`
|
||||
- [x] `type`, `instance?`, `satisfies?`
|
||||
|
||||
|
||||
@@ -1054,6 +1054,80 @@ proc cljRange*(start, finish: CljVal): CljVal =
|
||||
items.add(cljInt(i))
|
||||
cljList(items)
|
||||
|
||||
proc cljRange3*(start, finish, step: CljVal): CljVal =
|
||||
if start.kind != ckInt or finish.kind != ckInt or step.kind != ckInt:
|
||||
raise newException(CatchableError, "range requires integers")
|
||||
if step.intVal == 0: raise newException(CatchableError, "range step cannot be zero")
|
||||
var items: seq[CljVal] = @[]
|
||||
if step.intVal > 0:
|
||||
var i = start.intVal
|
||||
while i < finish.intVal:
|
||||
items.add(cljInt(i))
|
||||
i += step.intVal
|
||||
else:
|
||||
var i = start.intVal
|
||||
while i > finish.intVal:
|
||||
items.add(cljInt(i))
|
||||
i += step.intVal
|
||||
cljList(items)
|
||||
|
||||
proc cljRepeat*(n: CljVal, x: CljVal): CljVal =
|
||||
if n.kind != ckInt: raise newException(CatchableError, "repeat requires an integer count")
|
||||
var items: seq[CljVal] = @[]
|
||||
for i in 0..<n.intVal:
|
||||
items.add(x)
|
||||
cljList(items)
|
||||
|
||||
proc cljCycle*(n: CljVal, coll: CljVal): CljVal =
|
||||
if n.kind != ckInt: raise newException(CatchableError, "cycle requires an integer count")
|
||||
var srcItems: seq[CljVal] = @[]
|
||||
case coll.kind
|
||||
of ckList: srcItems = coll.listItems
|
||||
of ckVector: srcItems = toSeq(coll.vecData)
|
||||
else: return cljList(@[])
|
||||
if srcItems.len == 0: return cljList(@[])
|
||||
var items: seq[CljVal] = @[]
|
||||
for i in 0..<n.intVal:
|
||||
items.add(srcItems[i mod srcItems.len])
|
||||
cljList(items)
|
||||
|
||||
proc cljIterate*(n: CljVal, f: CljVal, x: CljVal): CljVal =
|
||||
if n.kind != ckInt: raise newException(CatchableError, "iterate requires an integer count")
|
||||
if f.kind != ckFn: raise newException(CatchableError, "iterate requires a function")
|
||||
var items: seq[CljVal] = @[x]
|
||||
var current = x
|
||||
for i in 1..<n.intVal:
|
||||
current = f.fnProc(@[current])
|
||||
items.add(current)
|
||||
cljList(items)
|
||||
|
||||
proc cljInterleave*(args: seq[CljVal]): CljVal =
|
||||
if args.len < 2: raise newException(CatchableError, "interleave requires at least 2 arguments")
|
||||
var seqs: seq[seq[CljVal]] = @[]
|
||||
for a in args:
|
||||
case a.kind
|
||||
of ckList: seqs.add(a.listItems)
|
||||
of ckVector: seqs.add(toSeq(a.vecData))
|
||||
else: seqs.add(@[])
|
||||
var minLen = seqs[0].len
|
||||
for s in seqs:
|
||||
if s.len < minLen: minLen = s.len
|
||||
var items: seq[CljVal] = @[]
|
||||
for i in 0..<minLen:
|
||||
for s in seqs:
|
||||
items.add(s[i])
|
||||
cljList(items)
|
||||
|
||||
proc cljReadLine*(): CljVal =
|
||||
try:
|
||||
var line = ""
|
||||
if readLine(stdin, line):
|
||||
cljString(line)
|
||||
else:
|
||||
cljNil()
|
||||
except EOFError:
|
||||
cljNil()
|
||||
|
||||
# ---- File Operations ----
|
||||
|
||||
proc cljFileRead*(path: CljVal): CljVal =
|
||||
|
||||
+28
-4
@@ -109,6 +109,14 @@ proc runtimeName(op: string): string =
|
||||
of "git/diff": "cljGitDiff"
|
||||
of "git/log": "cljGitLog"
|
||||
of "disj": "cljDisj"
|
||||
of "slurp": "cljFileRead"
|
||||
of "spit": "cljFileWrite"
|
||||
of "read-line": "cljReadLine"
|
||||
of "repeat": "cljRepeat"
|
||||
of "cycle": "cljCycle"
|
||||
of "iterate": "cljIterate"
|
||||
of "interleave": "cljInterleave"
|
||||
of "quot": "cljQuot"
|
||||
else: ""
|
||||
|
||||
proc emitArgs(args: seq[CljVal]): string =
|
||||
@@ -779,14 +787,30 @@ proc emitSpecialForm(items: seq[CljVal], indent: int): string =
|
||||
return sp & "cljSet(@[" & emitExpr(arg, 0) & "])"
|
||||
|
||||
of "range":
|
||||
if items.len < 1 or items.len > 3:
|
||||
raise newException(EmitterError, "range requires 0, 1, or 2 arguments")
|
||||
if items.len < 1 or items.len > 4:
|
||||
raise newException(EmitterError, "range requires 0, 1, 2, or 3 arguments")
|
||||
if items.len == 1:
|
||||
return sp & "cljList(@[])"
|
||||
elif items.len == 2:
|
||||
return sp & "cljRange(" & emitExpr(items[1], 0) & ")"
|
||||
else:
|
||||
elif items.len == 3:
|
||||
return sp & "cljRange(" & emitExpr(items[1], 0) & ", " & emitExpr(items[2], 0) & ")"
|
||||
else:
|
||||
return sp & "cljRange3(" & emitExpr(items[1], 0) & ", " & emitExpr(items[2], 0) & ", " & emitExpr(items[3], 0) & ")"
|
||||
|
||||
of "iterate":
|
||||
if items.len != 4:
|
||||
raise newException(EmitterError, "iterate requires 3 arguments (n, f, x)")
|
||||
let fnArg = items[2]
|
||||
let nArg = emitExpr(items[1], 0)
|
||||
let xArg = emitExpr(items[3], 0)
|
||||
if fnArg.kind == ckSymbol:
|
||||
let rn = runtimeName(fnArg.symName)
|
||||
if rn.len > 0:
|
||||
return sp & "cljIterate(" & nArg & ", cljFn(proc(args: seq[CljVal]): CljVal = " & rn & "(args)), " & xArg & ")"
|
||||
return sp & "cljIterate(" & nArg & ", cljFn(proc(args: seq[CljVal]): CljVal = " & mangleName(fnArg.symName) & "(args[0])), " & xArg & ")"
|
||||
else:
|
||||
return sp & "cljIterate(" & nArg & ", " & emitExpr(fnArg, 0) & ", " & xArg & ")"
|
||||
|
||||
else:
|
||||
# ---- Nim interop: nim/module/function ----
|
||||
@@ -882,7 +906,7 @@ proc emitSpecialForm(items: seq[CljVal], indent: int): string =
|
||||
# Variadic functions take seq[CljVal]
|
||||
let variadic = op in ["+", "-", "*", "/", "=", ">", "<", ">=", "<=", "not=",
|
||||
"println", "prn", "print", "str", "pr-str",
|
||||
"concat", "min", "max", "merge"]
|
||||
"concat", "min", "max", "merge", "interleave"]
|
||||
var call: string
|
||||
if variadic and argParts.len > 0:
|
||||
call = rn & "(@[" & argParts.join(", ") & "])"
|
||||
|
||||
Reference in New Issue
Block a user