Skip to content

Commit

Permalink
implement accessor storage for json nodes
Browse files Browse the repository at this point in the history
Signed-off-by: George Lemon <georgelemon@protonmail.com>
  • Loading branch information
georgelemon committed Aug 1, 2023
1 parent 8e522fb commit 83ab67e
Show file tree
Hide file tree
Showing 5 changed files with 181 additions and 142 deletions.
81 changes: 71 additions & 10 deletions src/bropkg/engine/ast.nim
Original file line number Diff line number Diff line change
Expand Up @@ -249,9 +249,25 @@ type
nodes*: seq[Node]
selectors*: CritBitTree[Node]
stack*: ScopeTable
sourcePath*: string
meta: Meta
## Count lines and columns when using Macros

# fwd declarations
proc newStream*(node: JsonNode): Node
proc newString*(sVal: string): Node

proc newInt*(iVal: string): Node
proc newInt*(iVal: int): Node

proc newFloat*(fVal: string): Node
proc newFloat*(fVal: float): Node

proc newBool*(bVal: string): Node
proc newBool*(bVal: bool): Node

# proc newNull(): Node

when not defined release:
proc `$`*(node: Node): string =
# print nodes while in dev mode
Expand Down Expand Up @@ -299,7 +315,30 @@ proc getInfixCalcOp*(kind: TokenKind, isInfixInfix: bool): MathOp =
of tkMod: mMod
else: invalidCalcOp

proc toString*(v: JsonNode): string =
# Return a stringified version of JSON `v`
case v.kind:
of JString: v.str
of JInt: $(v.num)
of JFloat: $(v.fnum)
of JObject,
JArray: $(v)
of JNull: "null"
of JBool: $(v.bval)

proc toNode(v: JsonNode): Node =
case v.kind
of JString: newString(v.str)
of JInt: newInt(v.num.int)
of JFloat: newFloat(v.fnum)
of JObject,
JArray: newStream(v)
# of JNull: "null"
of JBool: newBool(v.bval)
else: nil

proc walkAccessorStorage*(node: Node, index: string, scope: ScopeTable): Node =
# walk trough a Node tree using `index`
# todo catch IndexDefect
case node.nt:
of ntAccessor:
Expand All @@ -318,6 +357,14 @@ proc walkAccessorStorage*(node: Node, index: string, scope: ScopeTable): Node =
of ntVariable:
return walkAccessorStorage(node.varValue, index, scope)
# result = node.varValue.itemsVal[parseInt(index)]
of ntStream:
case node.streamContent.kind:
of JObject:
result = toNode(node.streamContent[index])
of JArray:
result = toNode(node.streamContent[parseInt(index)])
else:
result = toNode(node.streamContent)
else: discard

proc call*(node: Node, scope: ScopeTable): Node =
Expand Down Expand Up @@ -409,15 +456,27 @@ proc newInt*(iVal: string): Node =
## Create a new ntInt node
result = Node(nt: ntInt, iVal: parseInt iVal)

proc newInt*(iVal: int): Node =
## Create a new ntInt node
result = Node(nt: ntInt, iVal: iVal)

proc newFloat*(fVal: string): Node =
## Create a new ntFloat node
result = Node(nt: ntFloat, fVal: parseFloat fVal)

proc newFloat*(fVal: float): Node =
## Create a new ntFloat node
result = Node(nt: ntFloat, fVal: fVal)

proc newBool*(bVal: string): Node =
## Create a new ntbool node
assert bVal in ["true", "false"]
result = Node(nt: ntBool, bVal: parseBool bVal)

proc newBool*(bVal: bool): Node =
## Create a new ntbool node
result = Node(nt: ntBool, bVal: bVal)

proc newColor*(cVal: string): Node =
## Create a new ntColor node
result = Node(nt: ntColor, cVal: cVal)
Expand All @@ -430,10 +489,6 @@ proc newSize*(size: int, unit: Units): Node =

proc newInfo*(node: Node): Node = Node(nt: ntInfo, nodeType: node.getNodeType)

# proc newJson*(jsonVal: JsonNode): Node =
# ## Create a new ntJsonValue node
# result = Node(nt: ntJsonValue, jsonVal: jsonVal)

proc newStream*(src: string): Node =
## Create a new Stream node from YAML or JSON
try:
Expand All @@ -444,16 +499,19 @@ proc newStream*(src: string): Node =
except JsonParsingError:
echo "internal error"

proc newStream*(jsonNode: JsonNode): Node = Node(nt: ntStream, streamContent: jsonNode) ## Create a new Stream node from `jsonNode`
proc newStream*(node: JsonNode): Node =
## Create a new Stream from `node`
Node(nt: ntStream, streamContent: node)

proc newObject*(): Node = Node(nt: ntObject) ## Create a new ntObject node
proc newArray*(): Node = Node(nt: ntArray) ## Create a new ntArray node

proc newAccessor*(accType: NodeType, accStorage: Node): Node =
## Create a new `ntAccessor` Node
assert accType in {ntArray, ntObject}
assert accStorage.nt in {ntVariable, ntAccessor, ntArray, ntObject} # a var type of array or anonymous arrays/objects
assert accStorage.nt in {ntVariable, ntStream, ntAccessor, ntArray, ntObject} # a var type of array or anonymous arrays/objects
if accStorage.nt == ntVariable:
assert accStorage.varValue.nt in {ntArray, ntObject}
assert accStorage.varValue.nt in {ntArray, ntObject, ntStream}
case accType:
of ntArray:
result = Node(nt: ntAccessor, accessorType: ntArray, accessorStorage: accStorage)
Expand All @@ -471,15 +529,18 @@ proc newFnCall*[N: Node](node: N, args: seq[N], ident, name: string): Node =
stackIdent: ident, stackIdentName: name,
stackReturnType: node.fnReturnType)

const allowedInfixTokens = {ntColor, ntString, ntInt,
ntBool, ntFloat, ntCall, ntCallStack}

proc newInfix*(infixLeft, infixRight: Node, infixOp: InfixOp): Node =
## Create a new ntInfix node
assert infixLeft.nt in {ntColor, ntString, ntInt, ntBool, ntFloat, ntCall, ntCallStack}
assert infixRight.nt in {ntColor, ntString, ntInt, ntBool, ntFloat, ntCall, ntCallStack}
assert infixLeft.nt in allowedInfixTokens
assert infixRight.nt in allowedInfixTokens
result = Node(nt: ntInfix, infixLeft: infixLeft, infixRight: infixRight, infixOp: infixOp)

proc newInfix*(infixLeft: Node): Node =
## Create a new ntInfix node
assert infixLeft.nt in {ntColor, ntString, ntInt, ntBool, ntFloat, ntCall, ntCallStack}
assert infixLeft.nt in allowedInfixTokens
result = Node(nt: ntInfix, infixLeft: infixLeft)

proc newInfixCalc*(infixLeft: Node): Node =
Expand Down
137 changes: 68 additions & 69 deletions src/bropkg/engine/compiler.nim
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
import pkg/jsony
import std/[tables, strutils, macros, sequtils, json,
critbits, algorithm, oids, terminal, enumutils]
import ./ast, ./sourcemap
import ./ast, ./sourcemap, ./logging

type
Warning* = tuple[msg: string, line, col: int]
Expand All @@ -18,6 +18,8 @@ type
minify: bool
stack: Table[int, Node]
warnings*: seq[Warning]
when compileOption("app", "console"):
logger*: Logger

var strNL, strCL, strCR: string

Expand All @@ -26,6 +28,8 @@ proc write(c: var Compiler, node: Node, scope: ScopeTable = nil, data: Node = ni
proc getSelectorGroup(c: var Compiler, node: Node, scope: ScopeTable = nil, parent: Node = nil): string
proc handleInnerNode(c: var Compiler, node, parent: Node, scope: ScopeTable = nil, length: int, ix: var int)
proc handleCallStack(c: var Compiler, node: Node, scope: ScopeTable): Node
proc getValue(c: var Compiler, v: Node, scope: ScopeTable): Node


# eval
include ./eval
Expand Down Expand Up @@ -70,6 +74,8 @@ proc getTypeInfo(node: Node): string =
proc getCSS*(c: Compiler): string =
result = c.css

proc hasErrors*(c: Compiler): bool = c.logger.errorLogs.len > 0

proc prefix(node: Node): string =
# Prefix CSS selector, `.myclass` for ntClassSelector,
# `#myId` for `ntIDSelector` and so on
Expand Down Expand Up @@ -113,82 +119,65 @@ proc dumpHook*(s: var string, v: Node) =

template handleVariableValue(varNode: Node, scope: ScopeTable) {.dirty.} =
if varNode.varValue != nil:
case varNode.varValue.nt:
of ntStream:
result = c.getValue(varNode.varValue, nil)
else:
result = c.getValue(varNode.varValue, nil)
else:
case scope[varNode.varName].varValue.nt:
of ntStream:
result = c.getValue(scope[varNode.varName].varValue, nil)
else:
result = c.getValue(scope[varNode.varName].varValue, nil)

proc getValue(c: var Compiler, val: Node, scope: ScopeTable): Node # fwd declaration
return c.getValue(varNode.varValue, nil)
result = c.getValue(scope[varNode.varName].varValue, nil)

proc toString(c: var Compiler, val: Node, scope: ScopeTable = nil): string =
# Return stringified version of `val`
proc toString(c: var Compiler, v: Node, scope: ScopeTable = nil): string =
# Return stringified version of `v`
result =
case val.nt
of ntString: val.sVal
of ntFloat: $(val.fVal)
of ntInt: $(val.iVal)
of ntBool: $(val.bVal)
of ntColor: $(val.cVal)
of ntArray: jsony.toJson(val.itemsVal)
of ntObject: jsony.toJson(val.pairsVal)
case v.nt
of ntString: v.sVal
of ntFloat: $(v.fVal)
of ntInt: $(v.iVal)
of ntBool: $(v.bVal)
of ntColor: $(v.cVal)
of ntArray: jsony.toJson(v.itemsVal)
of ntObject: jsony.toJson(v.pairsVal)
of ntAccQuoted:
var accValues: seq[string]
for accVar in val.accVars:
for accVar in v.accVars:
add accValues, accVar.callIdent[1..^1] # variable name without `$`
add accValues, c.toString(c.getValue(accVar, scope))
val.accVal.format(accValues)
v.accVal.format(accValues)
of ntStream:
case val.streamContent.kind:
of JString: val.streamContent.str
of JInt: $(val.streamContent.num)
of JFloat: $(val.streamContent.fnum)
of JObject,
JArray: $(val.streamContent)
of JNull: "null"
of JBool: $(val.streamContent.bval)
toString(v.streamContent)
else: ""

proc getValue(c: var Compiler, val: Node, scope: ScopeTable): Node =
case val.nt
# of ntInfix:
# $(evalInfix(val.infixLeft, val.infixRight, val.infixOp, scope))
proc getValue(c: var Compiler, v: Node, scope: ScopeTable): Node =
case v.nt
of ntCall:
if val.callNode != nil:
case val.callNode.nt
if v.callNode != nil:
case v.callNode.nt
of ntAccessor:
var x: Node
if val.callNode.accessorType == ntArray:
if v.callNode.accessorType == ntArray:
# handle `ntArray` storages
x = walkAccessorStorage(val.callNode.accessorStorage, val.callNode.accessorKey, scope)
x = walkAccessorStorage(v.callNode.accessorStorage, v.callNode.accessorKey, scope)
return c.getValue(x, scope)
# handle `ntObject` storages
x = walkAccessorStorage(val.callNode.accessorStorage, val.callNode.accessorKey, scope)
return c.getValue(x, scope)
try:
x = walkAccessorStorage(v.callNode.accessorStorage, v.callNode.accessorKey, scope)
result = c.getValue(x, scope)
except KeyError as e:
newError(c.logger, internalError, 0, 0, true, e.msg)
of ntVariable:
handleVariableValue(val.callNode, scope)
handleVariableValue(v.callNode, scope)
of ntReturn:
result = c.handleCallStack(val.callNode, scope)
result = c.handleCallStack(v.callNode, scope)
else: discard
else:
return c.getValue(scope[val.callIdent], nil)
return c.getValue(scope[v.callIdent], nil)
of ntCallStack:
result = c.handleCallStack(val, scope)
result = c.handleCallStack(v, scope)
of ntVariable:
handleVariableValue(val, scope)
handleVariableValue(v, scope)
else:
result = val
result = v

proc getValue(c: var Compiler, vals: seq[Node], scope: ScopeTable): string =
var strVal: seq[string]
for val in vals:
add strVal, c.toString(c.getValue(val, scope))
for v in vals:
add strVal, c.toString(c.getValue(v, scope))
result = strVal.join(" ") # todo make it work with a valid separator (space, colon)

proc getProperty(c: var Compiler, n: Node, k: string, i: var int,
Expand Down Expand Up @@ -294,32 +283,38 @@ proc handleCommand(c: var Compiler, node: Node, scope: ScopeTable = nil) =
let meta = " (" & $(node.cmdMeta.line) & ":" & $(node.cmdMeta.pos) & ") "
case node.cmdValue.nt:
of ntInfix:
let output = c.evalInfix(node.cmdValue.infixLeft, node.cmdValue.infixRight, node.cmdValue.infixOp, scope)
stdout.styledWriteLine(fgGreen, "Debug", fgDefault, meta, fgDefault, getTypeInfo(node.cmdValue) & "\n" & $(output))
let output = c.evalInfix(node.cmdValue.infixLeft,
node.cmdValue.infixRight, node.cmdValue.infixOp, scope)
stdout.styledWriteLine(fgGreen, "Debug", fgDefault, meta, fgDefault,
getTypeInfo(node.cmdValue) & "\n" & $(output))
of ntMathStmt:
let total = c.evalMathInfix(node.cmdValue.mathLeft, node.cmdValue.mathRight, node.cmdValue.mathInfixOp, scope)
let total = c.evalMathInfix(node.cmdValue.mathLeft,
node.cmdValue.mathRight, node.cmdValue.mathInfixOp, scope)
let output =
if total.nt == ntInt:
$(total.iVal)
else:
$(total.fVal)
stdout.styledWriteLine(fgGreen, "Debug", fgDefault, meta, fgDefault, getTypeInfo(node.cmdValue) & "\n" & $(output))
stdout.styledWriteLine(fgGreen, "Debug", fgDefault, meta, fgDefault,
getTypeInfo(node.cmdValue) & "\n" & $(output))
of ntInfo:
stdout.styledWriteLine(fgGreen, "Debug", fgDefault, meta, fgMagenta, "[[" & $(node.cmdValue.nodeType) & "]]")
stdout.styledWriteLine(fgGreen, "Debug", fgDefault, meta, fgMagenta,
"[[" & $(node.cmdValue.nodeType) & "]]")
else:
let varValue = c.getValue(node.cmdValue, scope)
var output: string
case varValue.nt:
of ntMathStmt:
# let total = evalMathInfix(varValue.mathLeft, varValue.mathRight, varValue.mathInfixOp, scope)
output =
if varValue.mathResult.nt == ntInt:
$(varValue.mathResult.iVal)
else:
$(varValue.mathResult.fVal)
else:
output = c.toString(varValue)
stdout.styledWriteLine(fgGreen, "Debug", fgDefault, meta, fgMagenta, getTypeInfo(node.cmdValue) & "\n", fgDefault, output)
if likely(varValue != nil):
var output: string
case varValue.nt:
of ntMathStmt:
output =
if varValue.mathResult.nt == ntInt:
$(varValue.mathResult.iVal)
else:
$(varValue.mathResult.fVal)
else:
output = c.toString(varValue)
stdout.styledWriteLine(fgGreen, "Debug", fgDefault, meta, fgMagenta,
getTypeInfo(node.cmdValue) & "\n", fgDefault, output)

proc handleCallStack(c: var Compiler, node: Node, scope: ScopeTable): Node =
var
Expand Down Expand Up @@ -414,6 +409,8 @@ proc len*(c: var Compiler): int = c.program.nodes.len

proc newCompiler*(p: Program, minify = false): Compiler =
var c = Compiler(program: p, minify: minify)
when compileOption("app", "console"):
c.logger = Logger(filePath: p.sourcePath)
strCL = "{"
strCR = "}"
if minify == false:
Expand All @@ -435,6 +432,8 @@ proc newCompiler*(p: Program, minify = false): Compiler =
setLen strNL, 0
setLen strCL, 0
setLen strCR, 0
if unlikely(c.hasErrors):
setLen(c.css, 0)

proc toCSS*(p: Program, minify = false): string =
newCompiler(p, minify).getCSS

0 comments on commit 83ab67e

Please sign in to comment.