Skip to content

Commit

Permalink
0.4.0: notable speed bump
Browse files Browse the repository at this point in the history
  • Loading branch information
disruptek committed Dec 18, 2020
1 parent 5f7616c commit 0f4e1c7
Show file tree
Hide file tree
Showing 6 changed files with 103 additions and 85 deletions.
6 changes: 3 additions & 3 deletions docs/bench.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
6 changes: 3 additions & 3 deletions docs/packed.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
155 changes: 81 additions & 74 deletions jason.nim
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,13 @@ proc escapeJson(s: string): string =
result = newStringOfCap(s.len + s.len shr 3)
escapeJson(s, result)

when false:
proc cmd(caller: NimNode; callee: NimNode): NimNode =
nnkCommand.newTree(caller, callee)

proc cmd(caller: string; callee: NimNode): NimNode =
cmd(ident caller, callee)

func jason*(node: NimNode): NimNode =
## Convenience for jason(...) in macros.
result = newCall(ident"jason", node)
Expand All @@ -109,22 +116,26 @@ macro jason*(js: Json): Json =

result = js

proc jasonify*(node: NimNode): NimNode =
proc jasonify*(node: string): NimNode =
## Convenience for Json(...) in macros.
result = combineLiterals(node)
result = newCall(ident"Json", result)
result = nnkCallStrLit.newTree(ident"Json", newLit node)

proc jasonify*(node: string): NimNode =
proc jasonify*(node: NimNode): NimNode =
## Convenience for Json(...) in macros.
result = jasonify newLit(node)
result = combineLiterals(node)
case result.kind
of nnkStrLit:
result = jasonify result.strVal
else:
result = newCall(ident"Json", result)

macro jason*(s: string): Json =
## Escapes a string to form "JSON".
runnableExamples:
let j = jason"goats"
assert $j == "\"goats\""

let escapist = bindSym "escapeJson"
let escapist = bindSym"escapeJson"
result = jasonify newCall(escapist, s)

macro jason*(b: bool): Json =
Expand All @@ -139,15 +150,15 @@ macro jason*(b: bool): Json =

func jason*(e: enum): Json =
## Render any `enum` type as a JSON integer, by default.
result = Json($ord(e))
result = Json $ord(e)

func jason*(i: SomeInteger): Json =
## Render any Nim integer as a JSON integer.
result = Json($i)
result = Json $i

func jason*(f: SomeFloat): Json =
## Render any Nim float as a JSON number.
result = Json($f)
result = Json $f

macro jason*[I, T](a: array[I, T]): Json =
## Render any Nim array as a series of JSON values.
Expand Down Expand Up @@ -179,12 +190,11 @@ macro jason*[I, T](a: array[I, T]): Json =
for index in ranger[1].intVal .. ranger[2].intVal:
if index != 0:
addString newLit"," # comma between each element
# build and invoke the array access expression `a[i]`
let access = newTree(nnkBracketExpr, a, index.newLit)
addString newCall(js, access)
# s.add jason(a[index])
addString js.newCall(nnkBracketExpr.newTree(a, newLit index))
addString newLit"]"
list.add s # final value of the stmtlist is the string itself
result = newCall(bindSym"Json", list)
result = newCall(ident"Json", list)

when false:
macro jason[T](j: T{lit|`const`}): Json =
Expand Down Expand Up @@ -216,7 +226,7 @@ else:

proc composeWithComma(parent: NimNode; js: NimNode): NimNode =
# whether we need to add a comma before the next element
let adder = bindSym "add"
let adder = bindSym"add"
var comma = gensym(nskVar, "comma")
parent.add newVarStmt(comma, ident"false") # var comma = false

Expand All @@ -233,41 +243,67 @@ proc composeWithComma(parent: NimNode; js: NimNode): NimNode =

sep

macro jason*(a: JasonArray): Json =
proc jasonSquare*(a: NimNode): NimNode =
## Render an iterable that isn't a named-tuple or object as a JSON array.
runnableExamples:
let j = jason @[1, 3, 5, 7]
assert $j == "[1,3,5,7]"

let adder = bindSym "add"
let adder = bindSym"add"
result = newStmtList()

var js = gensym(nskVar, "js")
result.add newVarStmt(js, jasonify"[") # the leading [

# make a loop over the items in the iterable
let loop = block:
# add a loop over the items in the iterable
result.add:
var index = gensym(nskForVar, "index") # make loop var
var value = gensym(nskForVar, "value") # make loop var

var body = nnkStmtList.newNimNode # make body of a loop
body.add composeWithComma(result, js) # maybe add a separator
body.add:
newIfStmt (infix(index, "!=", newLit 0), # if index != 0
adder.newCall(js, jasonify",")) # js.add Json","

body.add adder.newCall(js, value.jason) # add the json for value

var loop = nnkForStmt.newNimNode # for loop
loop.add index # add loop var
loop.add value # add loop var
loop.add a # add any iterable
loop.add body # add loop body

loop

result.add loop # add the loop
result.add adder.newCall(js, jasonify"]") # add the trailing ]
result.add js # the last statement in the stmtlist is the json

# the last statement in the statement list is the json
result.add js
proc jasonTuple(t: NimNode): NimNode =
## Render an anonymous tuple as a JSON array.
let adder = bindSym"add"
let typ = t.getTypeInst
result = newStmtList()

var js = gensym(nskVar, "js")
result.add newVarStmt(js, jasonify"[") # the leading [

for index in 0 ..< typ.len:
if index != 0:
result.add adder.newCall(js, jasonify",")
result.add adder.newCall(js, jason newCall(ident"[]", t, newLit index))

result.add adder.newCall(js, jasonify"]") # add the trailing ]
result.add js # the last statement in the stmtlist is the json

macro jason*(a: JasonArray): Json =
## Render an iterable that isn't a named-tuple or object as a JSON array.
runnableExamples:
let j = jason @[1, 3, 5, 7]
assert $j == "[1,3,5,7]"

case a.kind
of nnkTupleConstr:
result = jasonTuple a
else:
result = jasonSquare a

proc jasonCurly(o: NimNode): NimNode =
let adder = bindSym "add"
let adder = bindSym"add"
result = newStmtList()

var js = gensym(nskVar, "js")
Expand Down Expand Up @@ -298,52 +334,6 @@ proc jasonCurly(o: NimNode): NimNode =
# the last statement in the statement list is the json
result.add js

macro jason*(o: JasonObject): Json =
## Render an anonymous Nim tuple as a JSON array; objects and named
## tuples become JSON objects.
runnableExamples:
let j = jason (1, "two", 3.0)
assert $j == """[1,"two",3.0]"""
let k = jason (one: 1, two: "two", three: 3.0)
assert $k == """{"one":1,"two":"two","three":3.0}"""

let
joiner = bindSym "join"
amper = bindSym "&"
typ = o.getTypeInst
if typ.kind != nnkTupleConstr:
# use our object construction code for named tuples, objects
result = jasonCurly(o)
else:
# it is a (34, "hello")-style anonymous tuple construction
result = newStmtList()
# first, stash the tuple temporarily
let temp = gensym(nskLet, "temp")
result.add newLetStmt(temp, o)

# arr will hold a list of strings we'll concatenate at the end
var arr = newStmtList()
# this is the left-bracket of the json array syntax, `[ ... ]`
arr.add jasonify"["
# a nim array will serve as input to the join()
var inf = newNimNode(nnkBracket)
for index, sym in pairs(typ):
# create an index expression for the temporary tuple, `:tmp[n]`
var exp = newNimNode(nnkBracketExpr)
# the :tmp in :tmp[3]
exp.add temp
# the 3 in :tmp[3]
exp.add index.newLit # token[#] = token[#+1]
# the jason() in jason(:tmp[3])
inf.add exp.jason
# now join the array with commas
arr.add newCall(joiner, inf, jasonify",")
# and add the trailing "]"
arr.add jasonify"]"

# now fold the array with &
result.add nestList(amper, arr)

# i want this to be jason(o: ref Jasonable)
func jason*(o: ref): Json =
## Render a Nim `ref` as either `null` or the value to which it refers.
Expand All @@ -352,4 +342,21 @@ func jason*(o: ref): Json =
else:
result = jason o[]

macro jason*(o: JasonObject): Json =
## Render an anonymous Nim tuple as a JSON array; objects and named
## tuples become JSON objects.
runnableExamples:
let j = jason (1, "too", 3.0)
assert $j == """[1,"too",3.0]"""
let k = jason (one: 1, two: "too", three: 3.0)
assert $k == """{"one":1,"two":"too","three":3.0}"""

let js = bindSym"jason"
case o.getTypeInst.kind
of nnkTupleConstr:
result = jasonTuple o
else:
# use our object construction code for named tuples, objects
result = jasonCurly o

export jason
6 changes: 3 additions & 3 deletions jason.nimble
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
version = "0.3.2"
version = "0.4.0"
author = "disruptek"
description = "compile-time json"
license = "MIT"
Expand All @@ -13,9 +13,9 @@ proc execCmd(cmd: string) =

proc execTest(test: string) =
when getEnv("GITHUB_ACTIONS", "false") != "true":
execCmd "nim c -r " & test
execCmd "nim c -f -r " & test
when (NimMajor, NimMinor) >= (1, 2):
execCmd "nim cpp --gc:arc -d:danger -r " & test
execCmd "nim cpp --gc:arc -d:danger -f -r " & test
else:
execCmd "nim c -r " & test
execCmd "nim cpp -r " & test
Expand Down
2 changes: 1 addition & 1 deletion testes
Submodule testes updated 12 files
+3 βˆ’80 .github/workflows/ci.yml
+0 βˆ’8 .gitmodules
+13 βˆ’2 README.md
+0 βˆ’1 bump
+0 βˆ’1 cutelog
+43 βˆ’0 docs/clean.svg
+23 βˆ’51 docs/demo.svg
+63 βˆ’50 testes.nim
+7 βˆ’5 testes.nimble
+9 βˆ’3 tests/balls.nim
+6 βˆ’0 tests/nim.cfg
+1 βˆ’2 tests/testicles.nim
13 changes: 12 additions & 1 deletion tests/test.nim
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import testes
import std/macros

import testes
import jason

type
Expand Down Expand Up @@ -52,6 +53,11 @@ testes:
check [1, 2, 3].jason == "[1,2,3]"
check @[1, 2, 3].jason == "[1,2,3]"

test "slow array":
let (x, y) = ("3", 4)
check [x, x, x].jason == Json"""["3","3","3"]"""
check [y, y, y].jason == Json"""[4,4,4]"""

test "ref":
var
x: ref int = new(int)
Expand All @@ -68,6 +74,11 @@ testes:
check dumb2.jason == Json"""{"a":1,"b":"2"}"""
check dumb3.jason == Json"""{"a":1,"b":"2"}"""

test "slow tuple":
let (x, y) = ("3", 4)
check (x, y).jason == Json"""["3",4]"""
check (a: x, b: y).jason == Json"""{"a":"3","b":4}"""

test "object":
type
A = object
Expand Down

0 comments on commit 0f4e1c7

Please sign in to comment.