Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Plan to rewrite the generator scripts. #111

Closed
planetis-m opened this issue May 5, 2024 · 5 comments
Closed

Plan to rewrite the generator scripts. #111

planetis-m opened this issue May 5, 2024 · 5 comments

Comments

@planetis-m
Copy link
Owner

The scripts that are used to generate the wrapper have grown in complexity and are difficult to understand and modify. Experience has shown that trying to modify the produced nim code from a representation like, json, xml, etc, that the generators are using, leads to horrible, unmaintanable code. This is better done at the nim AST level. So to evolve the scripts, keep the maintenance cost down, make them easily extensive, the two separate tasks of parsing the C header definitions and outputting bindings, and making an idiomatic nim wrapper based on those bindings, should use two different programs. The bindings parser, can use the raylib_parser for a start, or eventually be ported to c2nim/furthark. It doesn't attempt to do any transformations and use the c types. This is necessary as upstream will not improve it to parse other headers than raylib.h correctly, it half works with rlgl.h and requires manual intervention. Another program the wrapper generator, makes use of the compiler as a library, and parses the bindings it traverses the nim ast and writes our wrapper in a file.

@planetis-m
Copy link
Owner Author

planetis-m commented May 5, 2024

mwe for the second program:

import
  compiler/[ast, parser, idents, astalgo, pathutils, condsyms, renderer,
  options, nimconf, extccomp, modulegraphs], std/[os, strutils]

# need to run ./koch checksums

proc str(n: PNode): string =
  case n.kind
  of nkStrLit..nkTripleStrLit:
    result = n.strVal
  of nkIdent:
    result = n.ident.s
  of nkSym:
    result = n.sym.name.s
  of nkOpenSymChoice, nkClosedSymChoice:
    result = n.sons[0].sym.name.s
  else:
    assert false

proc basename(n: PNode): PNode =
  case n.kind
  of nkIdent: result = n
  of nkPragmaExpr:
    result = basename(n[0])
  of nkPostfix, nkPrefix:
    result = basename(n[1])
  of nkAccQuoted:
    result = basename(n[0])
  else:
    assert false

proc eqIdent(a: string; b: string): bool =
  result = cmpIgnoreStyle(a, b) == 0

proc eqIdent(a: PNode, b: string): bool =
  result = cmpIgnoreStyle(a.basename.str, b) == 0

proc eqIdent(a, b: PNode): bool =
  result = cmpIgnoreStyle(a.basename.str, b.basename.str) == 0

proc groupMatrixFieldsByRow(node: PNode) =
  const MatN = 4 # assumes a 4x4 matrix
  case node.kind
  of nkTypeSection:
    for child in node.items:
      if child.kind == nkTypeDef and eqIdent(child[0], "Matrix"):
        let reclist = child[2][2]
        if reclist.len != MatN*MatN: return
        for i in countdown(reclist.len-1, 0, MatN):
          for j in countdown(i-1, i-MatN+1):
            reclist[i].sons.insert(reclist[j][0])
        for i in countdown(reclist.len-1, 0):
          if (i + 1) mod 4 != 0:
            reclist.sons.delete(i)
  else:
    discard
  for child in node.items:
    groupMatrixFieldsByRow(child)

proc main =
  # Create a new configuration and module graph
  let conf = newConfigRef()
  let cache = newIdentCache()
  let graph = newModuleGraph(cache, conf)
  # Initialize defines and load configurations
  condsyms.initDefines(conf.symbols)
  conf.projectName = "stdinfile"
  conf.projectFull = "stdinfile".AbsoluteFile
  conf.projectPath = canonicalizePath(conf, getCurrentDir().AbsoluteFile).AbsoluteDir
  conf.projectIsStdin = true
  loadConfigs(DefaultConfig, cache, conf, graph.idgen)
  # Initialize external compiler variables
  extccomp.initVars(conf)
  # Parse input file
  let filename = "input.nim"
  let node = parseString(readFile(filename), cache, conf)
  # Apply postprocessing steps
  node.groupMatrixFieldsByRow()
  # Render the module to an output file
  renderModule(node, filename, {renderDocComments})

main()

@planetis-m
Copy link
Owner Author

Something similar was done in https://github.com/nim-lang/c2nim/blob/araq-gobject/gobject2nim.nim

@planetis-m
Copy link
Owner Author

planetis-m commented Jun 20, 2024

I was ignoring the easiest solution which is a wrapping macro like: https://github.com/nim-lang/opengl/blob/master/src/opengl/private/errors.nim

Thanks to that you have the ability to influence the wrapper per project with defines:

image

Maybe per #102 this can be used to make raylib accept int/float types instead of int32/float32 for people that don't care.

There're helpful macros for this purpose as well: https://github.com/geekrelief/genit

@Xkonti
Copy link

Xkonti commented Jul 26, 2024

It's great that you're documenting the progress here via issues!

@planetis-m
Copy link
Owner Author

Nah waste of time, instead the existing scripts were refactored.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants