Skip to content

Commit

Permalink
started working on language api
Browse files Browse the repository at this point in the history
  • Loading branch information
Nimaoth committed Dec 30, 2023
1 parent 1495793 commit 5837d81
Show file tree
Hide file tree
Showing 8 changed files with 194 additions and 33 deletions.
1 change: 1 addition & 0 deletions model/lang/lang-api.ast-model
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"id":"baffffff923169072e1f68f0","languages":["62e53399564d29f77293450e","656f7f67d077504f640d8716"],"models":["654fbb281446e19b38225265"],"rootNodes":[{"id":"baffffff923169072e1f6916","class":"62e5339e564d29f772934541","children":[["62e5339e564d29f772934540",[{"id":"bbffffffbd770a185fef6034","class":"62e5339f564d29f77293454d","properties":[["62e5339c564d29f772934529","Id"]],"children":[["62e5339f564d29f77293454b",[]],["62e5339f564d29f77293454a",[{"id":"bbffffffbd770a185fef607e","class":"654fbb281446e19b38225201","children":[["654fbb281446e19b38225207",[]],["654fbb281446e19b38225202",[{"id":"bbffffffbd770a185fef60d8","class":"654fbb281446e19b38225203","properties":[["62e5339c564d29f772934529","a"]],"children":[["654fbb281446e19b38225205",[{"id":"bbffffffbd770a185fef612d","class":"62e53397564d29f772934501"}]],["654fbb281446e19b38225206",[]]]},{"id":"bbffffffbd770a185fef6184","class":"654fbb281446e19b38225203","properties":[["62e5339c564d29f772934529","b"]],"children":[["654fbb281446e19b38225205",[{"id":"bbffffffbd770a185fef61d3","class":"62e53397564d29f772934501"}]],["654fbb281446e19b38225206",[]]]},{"id":"bbffffffbd770a185fef6230","class":"654fbb281446e19b38225203","properties":[["62e5339c564d29f772934529","c"]],"children":[["654fbb281446e19b38225205",[{"id":"bbffffffbd770a185fef6285","class":"62e53397564d29f772934501"}]],["654fbb281446e19b38225206",[]]]}]]]}]]]},{"id":"bbffffffbd770a185fef5ffa","class":"62e5339b564d29f772934526"},{"id":"baffffff923169072e1f6923","class":"62e5339f564d29f77293454d","properties":[["62e5339c564d29f772934529","AstNode"]],"children":[["62e5339f564d29f77293454b",[]],["62e5339f564d29f77293454a",[{"id":"baffffff923169072e1f694d","class":"654fbb281446e19b38225201","children":[["654fbb281446e19b38225207",[]],["654fbb281446e19b38225202",[{"id":"baffffff923169072e1f6974","class":"654fbb281446e19b38225203","properties":[["62e5339c564d29f772934529","index"]],"children":[["654fbb281446e19b38225205",[{"id":"baffffff923169072e1f6998","class":"62e53397564d29f772934501"}]],["654fbb281446e19b38225206",[]]]}]]]}]]]},{"id":"baffffff923169072e1f69d6","class":"62e5339b564d29f772934526"},{"id":"baffffff923169072e1f6a0d","class":"62e5339f564d29f77293454d","properties":[["62e5339c564d29f772934529","node parent"]],"children":[["62e5339f564d29f77293454b",[]],["62e5339f564d29f77293454a",[{"id":"baffffff923169072e1f6a48","class":"656f7f67d077504f640d8713","properties":[["656f7f67d077504f640d8714","node-parent"]],"children":[["656f7f67d077504f640d8715",[{"id":"baffffff923169072e1f6aa3","class":"62e5339a564d29f77293451c","children":[["62e53399564d29f772934511",[{"id":"baffffff923169072e1f6cc4","class":"62e533a0564d29f772934550","references":[["62e533a0564d29f77293454f","baffffff923169072e1f6923"]]}]],["62e53399564d29f772934510",[{"id":"baffffff923169072e1f6c4f","class":"62e533a0564d29f772934550","references":[["62e533a0564d29f77293454f","baffffff923169072e1f6923"]]}]]]}]]]}]]]},{"id":"bbffffffbd770a185fef5f50","class":"62e5339f564d29f77293454d","properties":[["62e5339c564d29f772934529","node id"]],"children":[["62e5339f564d29f77293454b",[]],["62e5339f564d29f77293454a",[{"id":"bbffffffbd770a185fef6363","class":"656f7f67d077504f640d8713","properties":[["656f7f67d077504f640d8714","node-id"]],"children":[["656f7f67d077504f640d8715",[{"id":"bbffffffbd770a185fef63dd","class":"62e5339a564d29f77293451c","children":[["62e53399564d29f772934511",[{"id":"bbffffffbd770a185fef644b","class":"62e533a0564d29f772934550","references":[["62e533a0564d29f77293454f","baffffff923169072e1f6923"]]}]],["62e53399564d29f772934510",[{"id":"bbffffffbd770a185fef64d4","class":"62e533a0564d29f772934550","references":[["62e533a0564d29f77293454f","bbffffffbd770a185fef6034"]]}]]]}]]]}]]]},{"id":"bbffffffbd770a185fef6764","class":"62e5339f564d29f77293454d","properties":[["62e5339c564d29f772934529","id to string"]],"children":[["62e5339f564d29f77293454b",[]],["62e5339f564d29f77293454a",[{"id":"bbffffffbd770a185fef6765","class":"656f7f67d077504f640d8713","properties":[["656f7f67d077504f640d8714","id-to-string"]],"children":[["656f7f67d077504f640d8715",[{"id":"bbffffffbd770a185fef6766","class":"62e5339a564d29f77293451c","children":[["62e53399564d29f772934511",[{"id":"bbffffffbd770a185fef68ab","class":"62e533a0564d29f772934550","references":[["62e533a0564d29f77293454f","bbffffffbd770a185fef6034"]]}]],["62e53399564d29f772934510",[{"id":"bbffffffbd770a185fef69b1","class":"62e53397564d29f772934502"}]]]}]]]}]]]}]]]}]}
14 changes: 10 additions & 4 deletions model/playground.ast-project
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
{
"models": {
"model/array.ast-model": "654fbb281446e19b38225265",
"model/test-language.ast-model": "654fbb281446e19b38225240",
"model/cell-builder.ast-model": "82ffffff9afd1f08150838c9",
"model/lang/lang-api.ast-model": "baffffff923169072e1f68f0",
"model/lang/property-validator.ast-model": "8cffffff484c682b4a6c4637",

"model/a.ast-model": "083671ebb3cd003f6c8faaf3",

"model/aoc_day1.ast-model": "656f7f67d077504f640d8727",
"model/aoc_day2.ast-model": "656f7f67d077504f640d8728",
"model/aoc_day3.ast-model": "656f7f67d077504f640d8729",
Expand All @@ -20,6 +21,11 @@
"model/aoc_day14.ast-model": "690ec4a65acc63703ad0a1c6",
"model/aoc_day15.ast-model": "6dc7461475f1fd6b6fbb16d6",
"model/aoc_day16.ast-model": "7312c6bed7330a3d487534c3",
"model/aoc_day17.ast-model": "77a10a8453091e0b0069ad5d"
"model/aoc_day17.ast-model": "77a10a8453091e0b0069ad5d",

"model/array.ast-model": "654fbb281446e19b38225265",
"model/cell-builder.ast-model": "82ffffff9afd1f08150838c9",
"model/test-language.ast-model": "654fbb281446e19b38225240",
"model/test-language-playground.ast-model": "83ffffff498d873d63c0237c"
}
}
2 changes: 1 addition & 1 deletion model/test-language.ast-model

Large diffs are not rendered by default.

63 changes: 60 additions & 3 deletions src/ast/lang/lang_language.nim
Original file line number Diff line number Diff line change
Expand Up @@ -709,6 +709,45 @@ proc intToString(a: int32): cstring =
let res = $a
return res.cstring

proc langApiNodeParent(module: WasmModule, retNodeHandlePtr: WasmPtr, nodeHandlePtr: WasmPtr) =
# debugf"langApiNodeParent {retNodeHandlePtr}, {nodeHandlePtr}"
let nodeIndex = module.getInt32(nodeHandlePtr)
let node = gNodeRegistry.getNode(nodeIndex).getOr:
log lvlError, fmt"Invalid node handle: {nodeIndex}"
module.setInt32(retNodeHandlePtr, 0)
return

# debugf"baseNodeParent: {retNodeHandlePtr}, {nodeHandlePtr}, {nodeIndex}, {node}"
if node.parent.isNil:
module.setInt32(retNodeHandlePtr, 0)
return

let parentIndex = gNodeRegistry.getNodeIndex(node.parent)
module.setInt32(retNodeHandlePtr, parentIndex)

proc langApiNodeId(module: WasmModule, retPtr: WasmPtr, nodeIndexPtr: WasmPtr) =
# debugf"langApiNodeId {retPtr}, {nodeIndexPtr}"
let nodeIndex = module.getInt32(nodeIndexPtr)
let node = gNodeRegistry.getNode(nodeIndex).getOr:
log lvlError, fmt"Invalid node handle: {nodeIndex}"
module.setInt32(retPtr, 0)
module.setInt32(retPtr + 4, 0)
module.setInt32(retPtr + 8, 0)
return

let (a, b, c) = node.id.Id.deconstruct
module.setInt32(retPtr, a)
module.setInt32(retPtr + 4, b)
module.setInt32(retPtr + 8, c)

proc langApiIdToString(module: WasmModule, idPtr: WasmPtr): string =
# debugf"langApiIdToString {idPtr}"
let a = module.getInt32(idPtr)
let b = module.getInt32(idPtr + 4)
let c = module.getInt32(idPtr + 8)
let id = construct(a, b, c)
return $id

##### end of temp stuff

proc updateLanguageFromModel*(language: Language, model: Model, updateBuilder: bool = true, ctx = ModelComputationContextBase.none): Future[bool] {.async.} =
Expand Down Expand Up @@ -764,6 +803,8 @@ proc updateLanguageFromModel*(language: Language, model: Model, updateBuilder: b

let binary = compiler.compileToBinary()

var imports = newSeq[WasmImports]()

var imp = WasmImports(namespace: "env")
imp.addFunction("print_i32", printI32)
imp.addFunction("print_u32", printU32)
Expand All @@ -775,9 +816,16 @@ proc updateLanguageFromModel*(language: Language, model: Model, updateBuilder: b
imp.addFunction("print_string", printString)
imp.addFunction("print_line", printLine)
imp.addFunction("intToString", intToString)
imports.add imp

var baseImports = WasmImports(namespace: "base")
baseImports.addFunction("node-parent", langApiNodeParent)
baseImports.addFunction("node-id", langApiNodeId)
baseImports.addFunction("id-to-string", langApiIdToString)
imports.add baseImports

measureBlock fmt"Create wasm module for language '{model.path}'":
let module = await newWasmModule(binary.toArrayBuffer, @[imp])
let module = await newWasmModule(binary.toArrayBuffer, imports)
if module.isNone:
log lvlError, fmt"Failed to create wasm module from generated binary for {model.path}: {getCurrentExceptionMsg()}"

Expand All @@ -796,16 +844,25 @@ proc updateLanguageFromModel*(language: Language, model: Model, updateBuilder: b
if wasmModule.getSome(module):
for (class, role, functionNode) in propertyValidators:
let name = $functionNode.id
if module.findFunction(name, bool, proc(a: string): bool).getSome(validateImpl):
if module.findFunction(name, bool, proc(node: WasmPtr, a: string): bool).getSome(validateImpl):
let validator = if not language.validators.contains(class):
let validator = NodeValidator()
language.validators[class] = validator
validator
else:
language.validators[class]

proc validateImplWrapper(node: Option[AstNode], propertyValue: string): bool =
let p: WasmPtr = module.alloc(4)
let index: int32 = if node.getSome(node):
gNodeRegistry.getNodeIndex(node)
else:
0
module.setInt32(p, index)
validateImpl(p, propertyValue)

# debugf"register propertyy validator for {class}, {role}"
validator.propertyValidators[role] = PropertyValidator(kind: Custom, impl: validateImpl)
validator.propertyValidators[role] = PropertyValidator(kind: Custom, impl: validateImplWrapper)

if updateBuilder:
registerBuilder(language.id, builder)
Expand Down
39 changes: 34 additions & 5 deletions src/ast/model.nim
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ type
id*: NodeId
class*: ClassId

registryIndex: int32 # Index in the global node registry (0 if not registered)
model*: Model # gets set when inserted into a parent node which is in a model, or when inserted into a model
parent*: AstNode # gets set when inserted into a parent node
role*: RoleId # gets set when inserted into a parent node
Expand All @@ -89,14 +90,17 @@ type
references*: seq[tuple[role: RoleId, node: NodeId]]
childLists*: seq[tuple[role: RoleId, nodes: seq[AstNode]]]

AstNodeRegistry* = ref object
nodeToIndex*: Table[NodeId, int32]
nodes*: seq[AstNode]

PropertyValidatorKind* = enum Regex, Custom
PropertyValidator* = ref object
case kind*: PropertyValidatorKind
of Regex:
pattern*: Regex
of Custom:
impl*: proc(property: string): bool
impl*: proc(node: Option[AstNode], property: string): bool

NodeValidator* = ref object
propertyValidators*: ArrayTable[RoleId, PropertyValidator]
Expand Down Expand Up @@ -578,15 +582,15 @@ proc propertyDescription*(self: NodeClass, id: RoleId): Option[PropertyDescripti
let defaultNumberPattern = re"[0-9]+"
let defaultBoolPattern = re"true|false"

proc isValidPropertyValue*(language: Language, class: NodeClass, role: RoleId, value: string): bool =
proc isValidPropertyValue*(language: Language, class: NodeClass, role: RoleId, value: string, node: Option[AstNode] = AstNode.none): bool =
# debugf"isValidPropertyValue {class.name} ({class.id}) {role} '{value}'"
if language.validators.contains(class.id):
if language.validators[class.id].propertyValidators.tryGet(role).getSome(validator):
case validator.kind
of Regex:
return value.match(validator.pattern)
of Custom:
return validator.impl(value)
return validator.impl(node, value)

if class.propertyDescription(role).getSome(desc):
case desc.typ
Expand Down Expand Up @@ -1271,7 +1275,7 @@ proc jsonToAstNode*(json: JsonNode, model: Model, opt = Joptions()): Option[AstN
let role = entry[0].jsonTo RoleId
for childJson in entry[1]:
if childJson.jsonToAstNode(model, opt).getSome(childNode):
node.add(role, childNode)
node.forceAddChild(role, childNode)
else:
log(lvlError, fmt"Failed to parse node from json")

Expand Down Expand Up @@ -1382,4 +1386,29 @@ proc loadFromJson*(model: Model, path: string, json: JsonNode,
else:
log(lvlWarn, fmt"Missing root nodes")

return true
return true

var gNodeRegistry* = AstNodeRegistry()
gNodeRegistry.nodes.add nil

proc getNode*(registry: AstNodeRegistry, index: int32): Option[AstNode] =
if index <= 0 or index >= registry.nodes.len:
log lvlError, fmt"getNode: index {index} out of range"
return AstNode.none
return registry.nodes[index].some

proc registerNode*(registry: AstNodeRegistry, node: AstNode): int32 =
if node.registryIndex != 0:
log lvlWarn, fmt"registerNode: node {node} already registered"
return node.registryIndex

result = registry.nodes.len.int32
node.registryIndex = result
registry.nodes.add node
registry.nodeToIndex[node.id] = result

proc getNodeIndex*(registry: AstNodeRegistry, node: AstNode): int32 =
if node.registryIndex != 0:
return node.registryIndex

return registry.registerNode(node)
2 changes: 1 addition & 1 deletion src/ast/model_state.nim
Original file line number Diff line number Diff line change
Expand Up @@ -335,7 +335,7 @@ proc computeValidImpl(ctx: ModelState, node: AstNode): bool =

for property in node.properties:
if property.value.kind == String:
if not language.isValidPropertyValue(class, property.role, property.value.stringValue):
if not language.isValidPropertyValue(class, property.role, property.value.stringValue, node.some):
ctx.addDiagnostic(node, fmt"Invalid property value")
result = false

Expand Down
3 changes: 3 additions & 0 deletions src/misc/id.nim
Original file line number Diff line number Diff line change
Expand Up @@ -272,4 +272,7 @@ proc fromJsonHook*(id: var Id, json: JsonNode) =
proc toJson*(id: Id, opt = initToJsonOptions()): JsonNode =
return newJString $id

proc construct*(time, fuzz, count: int32): Id =
return constructOid(time, fuzz, count).Id

proc deconstruct*(id: Id): tuple[time: int32, fuzz: int32, count: int32] {.borrow.}
Loading

0 comments on commit 5837d81

Please sign in to comment.