nimterop/nimterop/grammar.nim
2019-05-04 13:05:38 -05:00

603 lines
16 KiB
Nim

import macros, sets, strformat, strutils, tables
import regex
import "."/[getters, globals, lisp, treesitter/api]
type
Grammar = seq[tuple[grammar: string, call: proc(ast: ref Ast, node: TSNode, nimState: NimState) {.nimcall.}]]
proc genImportC(origName, nimName: string): string =
result = "importc"
if nimName != origName:
result.add &": \"{origName}\"" # used as {.importc: "foo".}
proc initGrammar(): Grammar =
# #define X Y
result.add(("""
(preproc_def
(identifier)
(preproc_arg)
)
""",
proc (ast: ref Ast, node: TSNode, nimState: NimState) =
if nimState.debug:
nimState.debugStr &= "\n# define X Y"
let
val = nimState.data[1].val.getLit()
if val.nBl:
let
name = nimState.data[0].val.getIdentifier(nskConst)
if name.nBl and nimState.identifiers.addNewIdentifer(name):
nimState.constStr &= &"\n {name}* = {val}"
))
let
typeGrammar = """
(type_qualifier?)
(primitive_type|type_identifier?)
(sized_type_specifier?
(primitive_type?)
)
(struct_specifier|union_specifier|enum_specifier?
(type_identifier)
)
"""
paramListGrammar = &"""
(parameter_list
(parameter_declaration*
{typeGrammar}
(identifier|type_identifier?)
(pointer_declarator?
(pointer_declarator!
(identifier|type_identifier)
)
(identifier|type_identifier)
)
(abstract_pointer_declarator?
(abstract_pointer_declarator?)
)
)
)
"""
funcGrammar = &"""
(function_declarator*
(identifier|type_identifier!)
(pointer_declarator
(pointer_declarator!
(type_identifier)
)
(type_identifier)
)
{paramListGrammar}
)
"""
arrGrammar = &"""
(array_declarator!
(pointer_declarator!
(pointer_declarator!
(type_identifier)
)
(type_identifier)
)
(type_identifier)
(identifier|number_literal)
)
"""
template funcParamCommon(fname, pname, ptyp, pptr, pout, count, i: untyped): untyped =
ptyp = nimState.data[i].val.getIdentifier(nskType, fname)
pptr = ""
while i+1 < nimState.data.len and nimState.data[i+1].name == "pointer_declarator":
pptr &= "ptr "
i += 1
if i+1 < nimState.data.len and nimState.data[i+1].name == "identifier":
pname = nimState.data[i+1].val.getIdentifier(nskParam, fname)
i += 2
else:
pname = "a" & $count
count += 1
i += 1
if pptr.len != 0 or ptyp != "object":
pout &= &"{pname}: {getPtrType(pptr&ptyp)}, "
# typedef int X
# typedef X Y
# typedef struct X Y
# typedef ?* Y
result.add((&"""
(type_definition
{typeGrammar}
(type_identifier!)
{arrGrammar}
(pointer_declarator!
(pointer_declarator!
(type_identifier!)
{arrGrammar}
{funcGrammar}
)
(type_identifier!)
{arrGrammar}
{funcGrammar}
)
{funcGrammar}
)
""",
proc (ast: ref Ast, node: TSNode, nimState: NimState) =
if nimState.debug:
nimState.debugStr &= "\n# typedef X Y"
var
i = 0
typ = nimState.data[i].val.getIdentifier(nskType)
name = ""
tptr = ""
aptr = ""
i += 1
while i < nimState.data.len and "pointer" in nimState.data[i].name:
case nimState.data[i].name:
of "pointer_declarator":
tptr &= "ptr "
i += 1
of "array_pointer_declarator":
aptr &= "ptr "
i += 1
if i < nimState.data.len:
name = nimState.data[i].val.getIdentifier(nskType)
i += 1
if typ.nBl and name.nBl and nimState.identifiers.addNewIdentifer(name):
if i < nimState.data.len and nimState.data[^1].name == "function_declarator":
var
fname = name
pout, pname, ptyp, pptr = ""
count = 1
while i < nimState.data.len:
if nimState.data[i].name == "function_declarator":
break
funcParamCommon(fname, pname, ptyp, pptr, pout, count, i)
if pout.len != 0 and pout[^2 .. ^1] == ", ":
pout = pout[0 .. ^3]
if tptr.len != 0 or typ != "object":
nimState.typeStr &= &"\n {name}* = proc({pout}): {getPtrType(tptr&typ)} {{.nimcall.}}"
else:
nimState.typeStr &= &"\n {name}* = proc({pout}) {{.nimcall.}}"
else:
if i < nimState.data.len and nimState.data[i].name in ["identifier", "number_literal"]:
var
flen = nimState.data[i].val
if nimState.data[i].name == "identifier":
flen = flen.getIdentifier(nskConst, name)
nimState.typeStr &= &"\n {name}* = {aptr}array[{flen}, {getPtrType(tptr&typ)}]"
else:
if name == typ:
nimState.typeStr &= &"\n {name}* = object"
else:
nimState.typeStr &= &"\n {name}* = {getPtrType(tptr&typ)}"
))
proc pDupTypeCommon(nname: string, fend: int, nimState: NimState, isEnum=false) =
if nimState.debug:
nimState.debugStr &= "\n# pDupTypeCommon()"
var
dname = nimState.data[^1].val
ndname = nimState.data[^1].val.getIdentifier(nskType)
dptr =
if fend == 2:
"ptr "
else:
""
if ndname.nBl and ndname != nname:
if isEnum:
if nimState.identifiers.addNewIdentifer(ndname):
nimState.enumStr &= &"\ntype {ndname}* = {dptr}{nname}"
else:
if nimState.identifiers.addNewIdentifer(ndname):
nimState.typeStr &=
&"\n {ndname}* {{.{genImportC(dname, ndname)}, header: {nimState.currentHeader}, bycopy.}} = {dptr}{nname}"
proc pStructCommon(ast: ref Ast, node: TSNode, name: string, fstart, fend: int, nimState: NimState) =
if nimState.debug:
nimState.debugStr &= "\n# pStructCommon"
var
nname = name.getIdentifier(nskType)
prefix = ""
union = ""
case $node.tsNodeType():
of "struct_specifier":
prefix = "struct "
of "union_specifier":
prefix = "union "
union = " {.union.}"
of "type_definition":
if node.getTSNodeNamedChildCountSansComments() != 0:
for i in 0 .. node.tsNodeNamedChildCount()-1:
let
nchild = $node.tsNodeNamedChild(i).tsNodeType()
if nchild != "comment":
case nchild:
of "struct_specifier":
if fstart == 1:
prefix = "struct "
of "union_specifier":
if fstart == 1:
prefix = "union "
union = " {.union.}"
break
if nname.nBl and nimState.identifiers.addNewIdentifer(nname):
if nimState.data.len == 1:
nimState.typeStr &= &"\n {nname}* {{.bycopy.}} = object{union}"
else:
let importCDecl = genImportC(prefix & name, nname)
nimState.typeStr &= &"\n {nname}* {{.{importCDecl}, header: {nimState.currentHeader}, bycopy.}} = object{union}"
var
i = fstart
ftyp, fname: string
fptr = ""
aptr = ""
while i < nimState.data.len-fend:
fptr = ""
aptr = ""
if nimState.data[i].name == "field_declaration":
i += 1
continue
if nimState.data[i].name notin ["field_identifier", "pointer_declarator", "array_pointer_declarator"]:
ftyp = nimState.data[i].val.getType()
i += 1
while i < nimState.data.len-fend and "pointer" in nimState.data[i].name:
case nimState.data[i].name:
of "pointer_declarator":
fptr &= "ptr "
i += 1
of "array_pointer_declarator":
aptr &= "ptr "
i += 1
fname = nimState.data[i].val.getIdentifier(nskField, nname)
if i+1 < nimState.data.len-fend and nimState.data[i+1].name in gEnumVals:
let
flen = nimState.data[i+1].val.getNimExpression()
nimState.typeStr &= &"\n {fname}*: {aptr}array[{flen}, {getPtrType(fptr&ftyp)}]"
i += 2
elif i+1 < nimState.data.len-fend and nimState.data[i+1].name == "function_declarator":
var
pout, pname, ptyp, pptr = ""
count = 1
i += 2
while i < nimState.data.len-fend:
if nimState.data[i].name == "function_declarator":
i += 1
continue
if nimState.data[i].name == "field_declaration":
break
funcParamCommon(fname, pname, ptyp, pptr, pout, count, i)
if pout.len != 0 and pout[^2 .. ^1] == ", ":
pout = pout[0 .. ^3]
if fptr.len != 0 or ftyp != "object":
nimState.typeStr &= &"\n {fname}*: proc({pout}): {getPtrType(fptr&ftyp)} {{.nimcall.}}"
else:
nimState.typeStr &= &"\n {fname}*: proc({pout}) {{.nimcall.}}"
i += 1
else:
if ftyp == "object":
nimState.typeStr &= &"\n {fname}*: pointer"
else:
nimState.typeStr &= &"\n {fname}*: {getPtrType(fptr&ftyp)}"
i += 1
if node.tsNodeType() == "type_definition" and
nimState.data[^1].name == "type_identifier" and nimState.data[^1].val.len != 0:
pDupTypeCommon(nname, fend, nimState, false)
let
fieldGrammar = &"""
(field_identifier!)
(array_declarator!
(field_identifier!)
(pointer_declarator
(pointer_declarator!
(field_identifier)
)
(field_identifier)
)
(^$1+)
)
(function_declarator+
(pointer_declarator
(pointer_declarator!
(field_identifier)
)
(field_identifier)
)
{paramListGrammar}
)
""" % gEnumVals.join("|")
fieldListGrammar = &"""
(field_declaration_list?
(field_declaration+
{typeGrammar}
(pointer_declarator!
(pointer_declarator!
{fieldGrammar}
)
{fieldGrammar}
)
{fieldGrammar}
)
)
"""
# struct X {}
result.add((&"""
(struct_specifier|union_specifier
(type_identifier)
{fieldListGrammar}
)
""",
proc (ast: ref Ast, node: TSNode, nimState: NimState) =
if nimState.debug:
nimState.debugStr &= "\n# struct X {}"
pStructCommon(ast, node, nimState.data[0].val, 1, 1, nimState)
))
# typedef struct X {}
result.add((&"""
(type_definition
(struct_specifier|union_specifier
(type_identifier?)
{fieldListGrammar}
)
(type_identifier!)
(pointer_declarator
(pointer_declarator!
(type_identifier)
)
(type_identifier)
)
)
""",
proc (ast: ref Ast, node: TSNode, nimState: NimState) =
if nimState.debug:
nimState.debugStr &= "\n# typedef struct X {}"
var
fstart = 0
fend = 1
if nimState.data[^2].name == "pointer_declarator":
fend = 2
if nimState.data.len > 1 and
nimState.data[0].name == "type_identifier" and
nimState.data[1].name notin ["field_identifier", "pointer_declarator"]:
fstart = 1
pStructCommon(ast, node, nimState.data[0].val, fstart, fend, nimState)
else:
pStructCommon(ast, node, nimState.data[^1].val, fstart, fend, nimState)
))
proc pEnumCommon(ast: ref Ast, node: TSNode, name: string, fstart, fend: int, nimState: NimState) =
if nimState.debug:
nimState.debugStr &= "\n# pEnumCommon()"
let nname =
if name.len == 0:
getUniqueIdentifier(nimState.identifiers, "Enum")
else:
name.getIdentifier(nskType)
if nname.nBl and nimState.identifiers.addNewIdentifer(nname):
nimState.enumStr &= &"\ndefineEnum({nname})"
var
i = fstart
count = 0
while i < nimState.data.len-fend:
if nimState.data[i].name == "enumerator":
i += 1
continue
let
fname = nimState.data[i].val.getIdentifier(nskEnumField)
if i+1 < nimState.data.len-fend and
nimState.data[i+1].name in gEnumVals:
if fname.nBl and nimState.identifiers.addNewIdentifer(fname):
nimState.constStr &= &"\n {fname}* = ({nimState.data[i+1].val.getNimExpression()}).{nname}"
try:
count = nimState.data[i+1].val.parseInt() + 1
except:
count += 1
i += 2
else:
if fname.nBl and nimState.identifiers.addNewIdentifer(fname):
nimState.constStr &= &"\n {fname}* = {count}.{nname}"
i += 1
count += 1
if node.tsNodeType() == "type_definition" and
nimState.data[^1].name == "type_identifier" and nimState.data[^1].val.len != 0:
pDupTypeCommon(nname, fend, nimState, true)
# enum X {}
result.add(("""
(enum_specifier
(type_identifier?)
(enumerator_list
(enumerator+
(identifier?)
(^$1+)
)
)
)
""" % gEnumVals.join("|"),
proc (ast: ref Ast, node: TSNode, nimState: NimState) =
if nimState.debug:
nimState.debugStr &= "\n# enum X {}"
var
name = ""
offset = 0
if nimState.data[0].name == "type_identifier":
name = nimState.data[0].val
offset = 1
pEnumCommon(ast, node, name, offset, 0, nimState)
))
# typedef enum {} X
result.add((&"""
(type_definition
{result[^1].grammar}
(type_identifier!)
(pointer_declarator
(pointer_declarator!
(type_identifier)
)
(type_identifier)
)
)
""",
proc (ast: ref Ast, node: TSNode, nimState: NimState) =
if nimState.debug:
nimState.debugStr &= "\n# typedef enum {}"
var
fstart = 0
fend = 1
if nimState.data[^2].name == "pointer_declarator":
fend = 2
if nimState.data[0].name == "type_identifier":
fstart = 1
pEnumCommon(ast, node, nimState.data[0].val, fstart, fend, nimState)
else:
pEnumCommon(ast, node, nimState.data[^1].val, fstart, fend, nimState)
))
# typ function(typ param1, ...)
result.add((&"""
(declaration
(storage_class_specifier?)
{typeGrammar}
(pointer_declarator!
(pointer_declarator!
{funcGrammar}
)
{funcGrammar}
)
{funcGrammar}
)
""",
proc (ast: ref Ast, node: TSNode, nimState: NimState) =
if nimState.debug:
nimState.debugStr &= "\n# typ function"
var
fptr = ""
i = 1
while i < nimState.data.len:
if nimState.data[i].name == "function_declarator":
i += 1
continue
fptr = ""
while i < nimState.data.len and nimState.data[i].name == "pointer_declarator":
fptr &= "ptr "
i += 1
var
fname = nimState.data[i].val
fnname = fname.getIdentifier(nskProc)
pout, pname, ptyp, pptr = ""
count = 1
i += 1
while i < nimState.data.len:
if nimState.data[i].name == "function_declarator":
break
funcParamCommon(fnname, pname, ptyp, pptr, pout, count, i)
if pout.len != 0 and pout[^2 .. ^1] == ", ":
pout = pout[0 .. ^3]
if fnname.nBl and nimState.identifiers.addNewIdentifer(fnname):
let ftyp = nimState.data[0].val.getIdentifier(nskType, fnname)
if fptr.len != 0 or ftyp != "object":
nimState.procStr &= &"\nproc {fnname}*({pout}): {getPtrType(fptr&ftyp)} {{.{genImportC(fname, fnname)}, header: {nimState.currentHeader}.}}"
else:
nimState.procStr &= &"\nproc {fnname}*({pout}) {{.{genImportC(fname, fnname)}, header: {nimState.currentHeader}.}}"
))
proc initRegex(ast: ref Ast) =
if ast.children.len != 0:
if not ast.recursive:
for child in ast.children:
child.initRegex()
var
reg: string
try:
reg = ast.getRegexForAstChildren()
ast.regex = reg.re()
except:
echo reg
raise newException(Exception, getCurrentExceptionMsg())
proc parseGrammar*(): AstTable =
let grammars = initGrammar()
result = newTable[string, seq[ref Ast]]()
for i in 0 .. grammars.len-1:
var
ast = grammars[i].grammar.parseLisp()
ast.tonim = grammars[i].call
ast.initRegex()
for n in ast.name.split("|"):
if n notin result:
result[n] = @[ast]
else:
result[n].add(ast)
proc printGrammar*(astTable: AstTable) =
for name in astTable.keys():
for ast in astTable[name]:
echo ast.printAst()