feat: +13 tests (184/233, 79%) — case, hex/radix, protocol/record stubs, doseq destructuring
This commit is contained in:
+215
-12
@@ -256,6 +256,15 @@ proc runtimeName(op: string): string =
|
||||
of "volatile!": "cljVolatileBang"
|
||||
of "volatile-mutable": "cljVolatileMutableQ"
|
||||
of "deliver": "cljDeliver"
|
||||
of "var?": "cljIsVar"
|
||||
of "ifn?": "cljIsIfn"
|
||||
of "ancestors": "cljAncestors"
|
||||
of "descendants": "cljDescendants"
|
||||
of "parents": "cljParents"
|
||||
of "isa?": "cljIsa"
|
||||
of "promise": "cljPromise"
|
||||
of "future": "cljFuture"
|
||||
of "create-ns": "cljCreateNs"
|
||||
of "doall": "cljDoall"
|
||||
of "dorun": "cljDorun"
|
||||
of "drop-last": "cljDropLast"
|
||||
@@ -487,7 +496,6 @@ proc emitSpecialForm(items: seq[CljVal], indent: int): string =
|
||||
if items.len < 4:
|
||||
raise newException(EmitterError, "defn requires name, params, and body")
|
||||
var name = items[1]
|
||||
# Strip metadata: (with-meta sym meta) -> sym
|
||||
if name.kind == ckList and name.items.len == 3 and
|
||||
name.items[0].kind == ckSymbol and name.items[0].symName == "with-meta":
|
||||
name = name.items[1]
|
||||
@@ -504,15 +512,25 @@ proc emitSpecialForm(items: seq[CljVal], indent: int): string =
|
||||
addToScope(p.symName)
|
||||
paramNames.add(mangleName(p.symName) & ": CljVal")
|
||||
let body = items[3..^1]
|
||||
var bodyCode = ""
|
||||
if body.len == 1:
|
||||
bodyCode = emitExpr(body[0], indent + 1)
|
||||
else:
|
||||
bodyCode = emitBlock(body, indent + 1)
|
||||
popScope()
|
||||
let procName = mangleName(name.symName)
|
||||
let exportMarker = if emitLibMode: "*" else: ""
|
||||
return sp & "proc " & procName & exportMarker & "(" & paramNames.join(", ") & "): CljVal =\n" & bodyCode
|
||||
if indent == 0:
|
||||
var bodyCode = ""
|
||||
if body.len == 1:
|
||||
bodyCode = emitExpr(body[0], indent + 1)
|
||||
else:
|
||||
bodyCode = emitBlock(body, indent + 1)
|
||||
return sp & "proc " & procName & exportMarker & "(" & paramNames.join(", ") & "): CljVal =\n" & bodyCode
|
||||
else:
|
||||
var bodyCode = ""
|
||||
if body.len == 1:
|
||||
bodyCode = emitExpr(body[0], indent + 2)
|
||||
else:
|
||||
bodyCode = emitBlock(body, indent + 2)
|
||||
return sp & "(block:\n" &
|
||||
indentStr(indent + 1) & "proc " & procName & "(" & paramNames.join(", ") & "): CljVal =\n" & bodyCode & "\n" &
|
||||
indentStr(indent + 1) & procName & ")"
|
||||
|
||||
of "defn-":
|
||||
if items.len < 4:
|
||||
@@ -841,7 +859,11 @@ proc emitSpecialForm(items: seq[CljVal], indent: int): string =
|
||||
if varSym.kind != ckSymbol:
|
||||
raise newException(EmitterError, "when-var-exists requires a symbol as first argument")
|
||||
let symName = resolveNsAlias(varSym.symName)
|
||||
let exists = runtimeName(symName).len > 0 or isLocalVar(symName)
|
||||
let isSpecial = symName in ["case", "defprotocol", "defrecord", "deftype", "defmulti", "defmethod",
|
||||
"var", "bound-fn", "bound-fn*", "promise", "delay", "future",
|
||||
"sorted-map-by", "sorted-set-by", "compare-and-set!",
|
||||
"empty", "delay"]
|
||||
let exists = runtimeName(symName).len > 0 or isLocalVar(symName) or isSpecial
|
||||
if exists:
|
||||
let body = items[2..^1]
|
||||
if body.len == 1:
|
||||
@@ -1003,6 +1025,160 @@ proc emitSpecialForm(items: seq[CljVal], indent: int): string =
|
||||
raise newException(EmitterError, "constantly requires exactly 1 argument")
|
||||
return sp & "cljConstantly(" & emitExpr(items[1], 0) & ")"
|
||||
|
||||
of "var":
|
||||
if items.len != 2:
|
||||
raise newException(EmitterError, "var requires exactly 1 argument")
|
||||
let target = items[1]
|
||||
if target.kind == ckSymbol:
|
||||
return sp & "cljVar(" & emitExpr(target, 0) & ")"
|
||||
return sp & "cljVar(" & emitExpr(target, 0) & ")"
|
||||
|
||||
of "defprotocol":
|
||||
if items.len < 2:
|
||||
raise newException(EmitterError, "defprotocol requires a name")
|
||||
let pname = items[1]
|
||||
if pname.kind != ckSymbol:
|
||||
raise newException(EmitterError, "defprotocol name must be a symbol")
|
||||
return sp & "let " & mangleName(pname.symName) & " = cljProtocol(\"" & pname.symName & "\")"
|
||||
|
||||
of "defrecord":
|
||||
if items.len < 3:
|
||||
raise newException(EmitterError, "defrecord requires name, fields, and optional protocols")
|
||||
let rname = items[1]
|
||||
if rname.kind != ckSymbol:
|
||||
raise newException(EmitterError, "defrecord name must be a symbol")
|
||||
let mangled = mangleName(rname.symName)
|
||||
let fields = items[2]
|
||||
var fieldNames: seq[string] = @[]
|
||||
if fields.kind == ckVector:
|
||||
for f in fields.items:
|
||||
if f.kind == ckSymbol:
|
||||
fieldNames.add(f.symName)
|
||||
var paramParts: seq[string] = @[]
|
||||
for i, fn in fieldNames:
|
||||
paramParts.add(mangleName(fn) & ": CljVal")
|
||||
var mapParts: seq[string] = @[]
|
||||
for fn in fieldNames:
|
||||
mapParts.add("cljKeyword(\"" & fn & "\")")
|
||||
mapParts.add(mangleName(fn))
|
||||
return sp & "proc " & mangled & "(" & paramParts.join(", ") & "): CljVal =\n" &
|
||||
indentStr(indent + 1) & "cljHashMap(@[" & mapParts.join(", ") & "])"
|
||||
|
||||
of "deftype":
|
||||
if items.len < 3:
|
||||
raise newException(EmitterError, "deftype requires name, fields, and optional protocols")
|
||||
let tname = items[1]
|
||||
if tname.kind != ckSymbol:
|
||||
raise newException(EmitterError, "deftype name must be a symbol")
|
||||
return sp & "let " & mangleName(tname.symName) & " = cljTypeConstructor(\"" & tname.symName & "\", cljVector(@[]))"
|
||||
|
||||
of "defmulti":
|
||||
if items.len < 3:
|
||||
raise newException(EmitterError, "defmulti requires name and dispatch function")
|
||||
let mname = items[1]
|
||||
if mname.kind != ckSymbol:
|
||||
raise newException(EmitterError, "defmulti name must be a symbol")
|
||||
let dispatchFn = emitExpr(items[2], 0)
|
||||
return sp & "let " & mangleName(mname.symName) & " = cljMultiFn(\"" & mname.symName & "\", " & dispatchFn & ")"
|
||||
|
||||
of "defmethod":
|
||||
if items.len < 4:
|
||||
raise newException(EmitterError, "defmethod requires name, dispatch-val, params, and body")
|
||||
let mname = items[1]
|
||||
if mname.kind != ckSymbol:
|
||||
raise newException(EmitterError, "defmethod name must be a symbol")
|
||||
var params = items[3]
|
||||
if params.kind != ckVector:
|
||||
raise newException(EmitterError, "defmethod params must be a vector")
|
||||
var paramNames: seq[string] = @[]
|
||||
for p in params.items:
|
||||
if p.kind != ckSymbol:
|
||||
raise newException(EmitterError, "defmethod params must be symbols")
|
||||
paramNames.add(mangleName(p.symName) & ": CljVal")
|
||||
let body = items[4..^1]
|
||||
var bodyCode = ""
|
||||
if body.len == 1:
|
||||
bodyCode = emitExpr(body[0], indent + 1)
|
||||
else:
|
||||
bodyCode = emitBlock(body, indent + 1)
|
||||
return sp & "proc " & mangleName(mname.symName) & "_impl(" & paramNames.join(", ") & "): CljVal =\n" & bodyCode
|
||||
|
||||
of "case":
|
||||
if items.len < 2:
|
||||
raise newException(EmitterError, "case requires expression and clauses")
|
||||
let caseExpr = emitExpr(items[1], 0)
|
||||
var ci = 2
|
||||
var lines: seq[string] = @[]
|
||||
lines.add(sp & "block:")
|
||||
lines.add(indentStr(indent + 1) & "let case_expr_val = " & caseExpr)
|
||||
var first = true
|
||||
while ci < items.len:
|
||||
let clause = items[ci]
|
||||
if ci + 1 >= items.len:
|
||||
lines.add(indentStr(indent + 1) & "else: " & emitExpr(clause, indent + 1))
|
||||
break
|
||||
let resultExpr = emitExpr(items[ci + 1], indent + 1)
|
||||
if clause.kind == ckList or clause.kind == ckVector:
|
||||
var subItems: seq[CljVal] = @[]
|
||||
if clause.kind == ckList: subItems = clause.items
|
||||
elif clause.kind == ckVector: subItems = clause.items
|
||||
var condParts: seq[string] = @[]
|
||||
for s in subItems:
|
||||
if s.kind == ckList:
|
||||
let quoted = emitQuotedForm(s)
|
||||
condParts.add("cljIsTruthy(cljMultiEqual2(case_expr_val, " & quoted & "))")
|
||||
else:
|
||||
condParts.add("cljIsTruthy(cljMultiEqual2(case_expr_val, " & emitExpr(s, 0) & "))")
|
||||
let cond = condParts.join(" or ")
|
||||
if first:
|
||||
lines.add(indentStr(indent + 1) & "if " & cond & ":")
|
||||
lines.add(indentStr(indent + 2) & resultExpr)
|
||||
first = false
|
||||
else:
|
||||
lines.add(indentStr(indent + 1) & "elif " & cond & ":")
|
||||
lines.add(indentStr(indent + 2) & resultExpr)
|
||||
ci += 2
|
||||
elif clause.kind == ckSymbol and clause.symName == "default":
|
||||
lines.add(indentStr(indent + 1) & "else:")
|
||||
lines.add(indentStr(indent + 2) & resultExpr)
|
||||
ci += 2
|
||||
else:
|
||||
let cond = "cljIsTruthy(cljMultiEqual2(case_expr_val, " & emitExpr(clause, 0) & "))"
|
||||
if first:
|
||||
lines.add(indentStr(indent + 1) & "if " & cond & ":")
|
||||
lines.add(indentStr(indent + 2) & resultExpr)
|
||||
first = false
|
||||
else:
|
||||
lines.add(indentStr(indent + 1) & "elif " & cond & ":")
|
||||
lines.add(indentStr(indent + 2) & resultExpr)
|
||||
ci += 2
|
||||
if lines.len <= 2:
|
||||
return sp & "cljNil()"
|
||||
if not lines[^1].strip().startsWith("else:"):
|
||||
lines.add(indentStr(indent + 1) & "else: cljNil()")
|
||||
return lines.join("\n")
|
||||
|
||||
of "bound-fn":
|
||||
# Delegate to fn for now (full dynamic binding not supported in compiled mode)
|
||||
let fnForm = cljList(@[cljSymbol("fn")] & items[1..^1])
|
||||
return emitExpr(fnForm, indent)
|
||||
|
||||
of "bound-fn*":
|
||||
let fnForm = cljList(@[cljSymbol("fn")] & items[1..^1])
|
||||
return emitExpr(fnForm, indent)
|
||||
|
||||
of "promise":
|
||||
if items.len < 1: return sp & "cljPromise()"
|
||||
return sp & "cljPromise()"
|
||||
|
||||
of "->var":
|
||||
if items.len != 2:
|
||||
raise newException(EmitterError, "->var requires exactly 1 argument")
|
||||
let target = items[1]
|
||||
if target.kind == ckSymbol:
|
||||
return sp & "cljVar(" & emitExpr(target, 0) & ")"
|
||||
return sp & "cljVar(" & emitExpr(target, 0) & ")"
|
||||
|
||||
of "group-by":
|
||||
if items.len != 3:
|
||||
raise newException(EmitterError, "group-by requires exactly 2 arguments")
|
||||
@@ -1226,6 +1402,8 @@ proc emitSpecialForm(items: seq[CljVal], indent: int): string =
|
||||
"array-map", "inf", "nan",
|
||||
"float", "int", "double", "long", "short", "byte",
|
||||
"boolean", "num", "number",
|
||||
"make-hierarchy", "derive", "underive", "ancestors",
|
||||
"descendants", "parents", "isa?", "promise", "create-ns", "future",
|
||||
"drop-last", "shuffle", "repeatedly", "fnil", "intern",
|
||||
"println-str", "prn-str", "binding", "aset",
|
||||
"volatile!", "deliver", "doall", "dorun",
|
||||
@@ -1242,7 +1420,11 @@ proc emitSpecialForm(items: seq[CljVal], indent: int): string =
|
||||
var args: seq[string] = @[]
|
||||
for i in 1..<items.len:
|
||||
args.add(emitExpr(items[i], indent))
|
||||
return sp & mangleName(op) & "(" & args.join(", ") & ")"
|
||||
# Handle record constructor: Foo. -> strip trailing dot
|
||||
var callOp = op
|
||||
if callOp.endsWith("."):
|
||||
callOp = callOp[0..^2]
|
||||
return sp & mangleName(callOp) & "(" & args.join(", ") & ")"
|
||||
|
||||
proc emitQuotedForm*(v: CljVal): string =
|
||||
case v.kind
|
||||
@@ -1294,7 +1476,25 @@ proc emitExpr*(v: CljVal, indent: int = 0): string =
|
||||
let symName = resolveNsAlias(v.symName)
|
||||
let rn = runtimeName(symName)
|
||||
if rn.len > 0 and not isLocalVar(symName):
|
||||
return sp & "cljFn(proc(args: seq[CljVal]): CljVal = " & rn & "(args))"
|
||||
# Check if the runtime function is variadic (takes seq[CljVal])
|
||||
let variadic = symName in ["+", "-", "*", "/", "=", ">", "<", ">=", "<=", "not=",
|
||||
"println", "prn", "print", "str", "pr-str",
|
||||
"concat", "min", "max", "merge", "interleave",
|
||||
"zipmap", "hash-map", "hash-set", "sorted-map", "sorted-set",
|
||||
"array-map", "inf", "nan",
|
||||
"float", "int", "double", "long", "short", "byte",
|
||||
"boolean", "num", "number",
|
||||
"make-hierarchy", "derive", "underive", "ancestors",
|
||||
"descendants", "parents", "isa?", "promise", "create-ns",
|
||||
"drop-last", "shuffle", "repeatedly", "fnil", "intern",
|
||||
"println-str", "prn-str", "binding", "aset",
|
||||
"volatile!", "deliver", "doall", "dorun",
|
||||
"to-array", "vector", "rand", "rand-int",
|
||||
"rand-nth", "random-sample"]
|
||||
if variadic:
|
||||
return sp & "cljFn(proc(args: seq[CljVal]): CljVal = " & rn & "(args))"
|
||||
else:
|
||||
return sp & "cljFn(proc(args: seq[CljVal]): CljVal = " & rn & "(args[0]))"
|
||||
if isLocalVar(symName):
|
||||
return sp & mangleName(symName)
|
||||
return sp & "cljSymbol(\"" & symName & "\")"
|
||||
@@ -1349,7 +1549,10 @@ proc emitBlock(items: seq[CljVal], indent: int, useResult: bool = false): string
|
||||
let lastLine = if lastNl == -1: trimmed else: trimmed[lastNl+1..^1].strip()
|
||||
# Prepend discard before the first non-empty line
|
||||
if firstNl == -1:
|
||||
code = indentStr(indent) & "discard " & stripped
|
||||
if stripped.startsWith("let ") or stripped.startsWith("proc ") or stripped.startsWith("var "):
|
||||
code = indentStr(indent) & stripped
|
||||
else:
|
||||
code = indentStr(indent) & "discard " & stripped
|
||||
else:
|
||||
# Wrap multi-line expression in proc so discard can apply
|
||||
let indentedCode = indentCode(code, 1)
|
||||
@@ -1395,7 +1598,7 @@ proc emitProgramInternal(forms: seq[CljVal]): string =
|
||||
let subLast = isLast and (j == subForms.len - 1)
|
||||
worklist.add((subForms[j], subLast))
|
||||
return
|
||||
let isDef = headSym and headName in ["def", "defn", "defn-"]
|
||||
let isDef = headSym and headName in ["def", "defn", "defn-", "defprotocol", "defrecord", "deftype", "defmulti", "defmethod", "var"]
|
||||
let isMacro = headSym and headName in ["defmacro"]
|
||||
let isNs = headSym and headName == "ns"
|
||||
if isNs:
|
||||
|
||||
+13
-9
@@ -394,19 +394,23 @@ proc initMacros*() =
|
||||
let name = bindings.items[0]
|
||||
let coll = bindings.items[1]
|
||||
let gs = cljSymbol(gensymName("ds_"))
|
||||
cljList(@[cljSymbol("let"), cljVector(@[gs, coll]),
|
||||
cljList(@[cljSymbol("loop"), cljVector(@[name, cljList(@[cljSymbol("first"), gs]),
|
||||
gs, cljList(@[cljSymbol("rest"), gs])]),
|
||||
cljList(@[cljSymbol("when"), name] & body),
|
||||
cljList(@[cljSymbol("recur"), cljList(@[cljSymbol("first"), gs]),
|
||||
cljList(@[cljSymbol("rest"), gs])])])])
|
||||
if name.kind == ckVector:
|
||||
let destructured = cljList(@[cljSymbol("let"), name, cljList(@[cljSymbol("first"), gs])])
|
||||
let loopBody = cljList(@[cljSymbol("when"), gs] & @[destructured] & body)
|
||||
let recurBody = cljList(@[cljSymbol("recur"), cljList(@[cljSymbol("rest"), gs])])
|
||||
let loopForm = cljList(@[cljSymbol("loop"), cljVector(@[gs, cljList(@[cljSymbol("seq"), gs])]), loopBody, recurBody])
|
||||
result = cljList(@[cljSymbol("let"), cljVector(@[gs, coll]), loopForm])
|
||||
else:
|
||||
let loopBody = cljList(@[cljSymbol("when"), name] & body)
|
||||
let recurBody = cljList(@[cljSymbol("recur"), cljList(@[cljSymbol("first"), gs]), cljList(@[cljSymbol("rest"), gs])])
|
||||
let loopForm = cljList(@[cljSymbol("loop"), cljVector(@[name, cljList(@[cljSymbol("first"), gs]), gs, cljList(@[cljSymbol("rest"), gs])]), loopBody, recurBody])
|
||||
result = cljList(@[cljSymbol("let"), cljVector(@[gs, coll]), loopForm])
|
||||
else:
|
||||
# Nested doseq
|
||||
let name = bindings.items[0]
|
||||
let coll = bindings.items[1]
|
||||
let restBindings = cljVector(bindings.items[2..^1])
|
||||
cljList(@[cljSymbol("doseq"), cljVector(@[name, coll]),
|
||||
cljList(@[cljSymbol("doseq"), restBindings] & body)])
|
||||
let innerDoseq = cljList(@[cljSymbol("doseq"), restBindings] & body)
|
||||
result = cljList(@[cljSymbol("doseq"), cljVector(@[name, coll]), innerDoseq])
|
||||
)
|
||||
|
||||
# dotimes
|
||||
|
||||
@@ -56,6 +56,14 @@ proc readNumberOrSym(s: string, i: var int): string =
|
||||
var start = i
|
||||
# handle negative sign or standalone operators
|
||||
if s[i] == '-' or s[i] == '+':
|
||||
# Check for negative hex: -0xFF
|
||||
if i + 3 < s.len and s[i+1] == '0' and (s[i+2] == 'x' or s[i+2] == 'X'):
|
||||
inc i # skip sign
|
||||
inc i # skip 0
|
||||
inc i # skip x
|
||||
while i < s.len and s[i] in {'0'..'9', 'a'..'f', 'A'..'F'}:
|
||||
inc i
|
||||
return s[start..<i]
|
||||
# Check for negative float without leading zero: -.5
|
||||
if i + 2 < s.len and s[i+1] == '.' and s[i+2] in Digits:
|
||||
inc i # skip sign
|
||||
@@ -99,6 +107,23 @@ proc readNumberOrSym(s: string, i: var int): string =
|
||||
inc i
|
||||
return s[start..<i]
|
||||
elif s[i] in Digits or (s[i] == '.' and i + 1 < s.len and s[i+1] in Digits):
|
||||
# Handle hex literals: 0x1a, 0xFF, etc.
|
||||
if s[i] == '0' and i + 1 < s.len and (s[i+1] == 'x' or s[i+1] == 'X'):
|
||||
inc i
|
||||
inc i
|
||||
while i < s.len and s[i] in {'0'..'9', 'a'..'f', 'A'..'F'}:
|
||||
inc i
|
||||
return s[start..<i]
|
||||
# Handle radix literals: 2r101, 16rFF, etc.
|
||||
if s[i] in Digits:
|
||||
var j = i + 1
|
||||
while j < s.len and s[j] in Digits:
|
||||
inc j
|
||||
if j < s.len and s[j] == 'r' and j + 1 < s.len and s[j+1] in {'0'..'9', 'a'..'z', 'A'..'Z'}:
|
||||
i = j + 1
|
||||
while i < s.len and s[i] in {'0'..'9', 'a'..'z', 'A'..'Z'}:
|
||||
inc i
|
||||
return s[start..<i]
|
||||
if s[i] == '.':
|
||||
inc i # skip leading dot
|
||||
while i < s.len and s[i] in Digits:
|
||||
@@ -143,6 +168,58 @@ proc readAtom(s: string, i: var int): CljVal =
|
||||
if numTok.len > 1 and (numTok[^1] == 'N' or numTok[^1] == 'M'):
|
||||
numTok = numTok[0..^2]
|
||||
|
||||
# Handle hex literals: 0xFF, -0x1a, etc.
|
||||
var isNeg = false
|
||||
var hexTok = numTok
|
||||
if hexTok.len >= 1 and hexTok[0] == '-':
|
||||
isNeg = true
|
||||
hexTok = hexTok[1..^1]
|
||||
if hexTok.len >= 3 and hexTok[0] == '0' and (hexTok[1] == 'x' or hexTok[1] == 'X'):
|
||||
try:
|
||||
var hexVal = 0'i64
|
||||
for j in 2..<hexTok.len:
|
||||
let c = hexTok[j]
|
||||
hexVal = hexVal * 16 + (
|
||||
if c in '0'..'9': cast[int64](ord(c) - ord('0'))
|
||||
elif c in 'a'..'f': cast[int64](ord(c) - ord('a') + 10)
|
||||
else: cast[int64](ord(c) - ord('A') + 10)
|
||||
)
|
||||
if isNeg:
|
||||
hexVal = -hexVal
|
||||
return cljInt(hexVal)
|
||||
except CatchableError:
|
||||
return cljSymbol(tok)
|
||||
|
||||
# Handle radix literals: 2r101, 16rFF, -8r77, etc.
|
||||
if tok.len >= 4:
|
||||
var radixTok = tok
|
||||
var radixNeg = false
|
||||
if radixTok[0] == '-' or radixTok[0] == '+':
|
||||
radixNeg = (radixTok[0] == '-')
|
||||
radixTok = radixTok[1..^1]
|
||||
# Find 'r' separator
|
||||
let rPos = radixTok.find('r')
|
||||
if rPos > 0 and rPos < radixTok.len - 1:
|
||||
try:
|
||||
let radix = parseInt(radixTok[0..<rPos])
|
||||
if radix >= 2 and radix <= 36:
|
||||
var val = 0'i64
|
||||
for j in rPos+1..<radixTok.len:
|
||||
let c = radixTok[j]
|
||||
let digit =
|
||||
if c in '0'..'9': ord(c) - ord('0')
|
||||
elif c in 'a'..'z': ord(c) - ord('a') + 10
|
||||
elif c in 'A'..'Z': ord(c) - ord('A') + 10
|
||||
else: -1
|
||||
if digit < 0 or digit >= radix:
|
||||
raise newException(ReaderError, "invalid digit")
|
||||
val = val * radix + digit
|
||||
if radixNeg:
|
||||
val = -val
|
||||
return cljInt(val)
|
||||
except CatchableError:
|
||||
return cljSymbol(tok)
|
||||
|
||||
# number
|
||||
var isFloat = false
|
||||
var isNumber = true
|
||||
|
||||
Reference in New Issue
Block a user