#
#
# c2nim - C to Nimrod source converter
# (c) Copyright 2012 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
## This module implements an Ansi C parser.
## It translates a C source file into a Nimrod AST. Then the renderer can be
## used to convert the AST to its text representation.
# XXX cleanup of declaration handling.
import
os, llstream, renderer, clex, idents, strutils, pegs, ast, astalgo, msgs,
options, strtabs
type
TParserFlag = enum
pfRefs, ## use "ref" instead of "ptr" for C's typ*
pfCDecl, ## annotate procs with cdecl
pfStdCall, ## annotate procs with stdcall
pfSkipInclude, ## skip all ``#include``
pfTypePrefixes, ## all generated types start with 'T' or 'P'
pfSkipComments ## do not generate comments
TMacro {.final.} = object
name: string
params: int # number of parameters
body: seq[ref TToken] # can contain pxMacroParam tokens
TParserOptions {.final.} = object
flags: set[TParserFlag]
prefixes, suffixes: seq[string]
mangleRules: seq[tuple[pattern: TPeg, frmt: string]]
privateRules: seq[TPeg]
dynlibSym, header: string
macros: seq[TMacro]
toMangle: PStringTable
PParserOptions* = ref TParserOptions
TParser* {.final.} = object
lex: TLexer
tok: ref TToken # current token
options: PParserOptions
backtrack: seq[ref TToken]
inTypeDef: int
scopeCounter: int
hasDeadCodeElimPragma: bool
TReplaceTuple* = array[0..1, string]
proc newParserOptions*(): PParserOptions =
new(result)
result.prefixes = @[]
result.suffixes = @[]
result.macros = @[]
result.mangleRules = @[]
result.privateRules = @[]
result.flags = {}
result.dynlibSym = ""
result.header = ""
result.toMangle = newStringTable(modeCaseSensitive)
proc setOption*(parserOptions: PParserOptions, key: string, val=""): bool =
result = true
case key
of "ref": incl(parserOptions.flags, pfRefs)
of "dynlib": parserOptions.dynlibSym = val
of "header": parserOptions.header = val
of "cdecl": incl(parserOptions.flags, pfCdecl)
of "stdcall": incl(parserOptions.flags, pfStdCall)
of "prefix": parserOptions.prefixes.add(val)
of "suffix": parserOptions.suffixes.add(val)
of "skipinclude": incl(parserOptions.flags, pfSkipInclude)
of "typeprefixes": incl(parserOptions.flags, pfTypePrefixes)
of "skipcomments": incl(parserOptions.flags, pfSkipComments)
else: result = false
proc ParseUnit*(p: var TParser): PNode
proc openParser*(p: var TParser, filename: string, inputStream: PLLStream,
options = newParserOptions())
proc closeParser*(p: var TParser)
# implementation
proc OpenParser(p: var TParser, filename: string,
inputStream: PLLStream, options = newParserOptions()) =
OpenLexer(p.lex, filename, inputStream)
p.options = options
p.backtrack = @[]
new(p.tok)
proc parMessage(p: TParser, msg: TMsgKind, arg = "") =
lexMessage(p.lex, msg, arg)
proc CloseParser(p: var TParser) = CloseLexer(p.lex)
proc saveContext(p: var TParser) = p.backtrack.add(p.tok)
proc closeContext(p: var TParser) = discard p.backtrack.pop()
proc backtrackContext(p: var TParser) = p.tok = p.backtrack.pop()
proc rawGetTok(p: var TParser) =
if p.tok.next != nil:
p.tok = p.tok.next
elif p.backtrack.len == 0:
p.tok.next = nil
getTok(p.lex, p.tok[])
else:
# We need the next token and must be able to backtrack. So we need to
# allocate a new token.
var t: ref TToken
new(t)
getTok(p.lex, t[])
p.tok.next = t
p.tok = t
proc findMacro(p: TParser): int =
for i in 0..high(p.options.macros):
if p.tok.s == p.options.macros[i].name: return i
return -1
proc rawEat(p: var TParser, xkind: TTokKind) =
if p.tok.xkind == xkind: rawGetTok(p)
else: parMessage(p, errTokenExpected, TokKindToStr(xkind))
proc parseMacroArguments(p: var TParser): seq[seq[ref TToken]] =
result = @[]
result.add(@[])
var i: array[pxParLe..pxCurlyLe, int]
var L = 0
saveContext(p)
while true:
var kind = p.tok.xkind
case kind
of pxEof: rawEat(p, pxParRi)
of pxParLe, pxBracketLe, pxCurlyLe:
inc(i[kind])
result[L].add(p.tok)
of pxParRi:
# end of arguments?
if i[pxParLe] == 0 and i[pxBracketLe] == 0 and i[pxCurlyLe] == 0: break
if i[pxParLe] > 0: dec(i[pxParLe])
result[L].add(p.tok)
of pxBracketRi, pxCurlyRi:
kind = pred(kind, 3)
if i[kind] > 0: dec(i[kind])
result[L].add(p.tok)
of pxComma:
if i[pxParLe] == 0 and i[pxBracketLe] == 0 and i[pxCurlyLe] == 0:
# next argument: comma is not part of the argument
result.add(@[])
inc(L)
else:
# comma does not separate different arguments:
result[L].add(p.tok)
else:
result[L].add(p.tok)
rawGetTok(p)
closeContext(p)
proc expandMacro(p: var TParser, m: TMacro) =
rawGetTok(p) # skip macro name
var arguments: seq[seq[ref TToken]]
if m.params > 0:
rawEat(p, pxParLe)
arguments = parseMacroArguments(p)
if arguments.len != m.params: parMessage(p, errWrongNumberOfArguments)
rawEat(p, pxParRi)
# insert into the token list:
if m.body.len > 0:
var newList: ref TToken
new(newList)
var lastTok = newList
for tok in items(m.body):
if tok.xkind == pxMacroParam:
for t in items(arguments[int(tok.iNumber)]):
#echo "t: ", t^
lastTok.next = t
lastTok = t
else:
#echo "tok: ", tok^
lastTok.next = tok
lastTok = tok
lastTok.next = p.tok
p.tok = newList.next
proc getTok(p: var TParser) =
rawGetTok(p)
if p.tok.xkind == pxSymbol:
var idx = findMacro(p)
if idx >= 0:
expandMacro(p, p.options.macros[idx])
proc parLineInfo(p: TParser): TLineInfo =
result = getLineInfo(p.lex)
proc skipComAux(p: var TParser, n: PNode) =
if n != nil and n.kind != nkEmpty:
if pfSkipComments notin p.options.flags:
if n.comment == nil: n.comment = p.tok.s
else: add(n.comment, "\n" & p.tok.s)
else:
parMessage(p, warnCommentXIgnored, p.tok.s)
getTok(p)
proc skipCom(p: var TParser, n: PNode) =
while p.tok.xkind in {pxLineComment, pxStarComment}: skipcomAux(p, n)
proc skipStarCom(p: var TParser, n: PNode) =
while p.tok.xkind == pxStarComment: skipComAux(p, n)
proc getTok(p: var TParser, n: PNode) =
getTok(p)
skipCom(p, n)
proc ExpectIdent(p: TParser) =
if p.tok.xkind != pxSymbol: parMessage(p, errIdentifierExpected, $(p.tok[]))
proc Eat(p: var TParser, xkind: TTokKind, n: PNode) =
if p.tok.xkind == xkind: getTok(p, n)
else: parMessage(p, errTokenExpected, TokKindToStr(xkind))
proc Eat(p: var TParser, xkind: TTokKind) =
if p.tok.xkind == xkind: getTok(p)
else: parMessage(p, errTokenExpected, TokKindToStr(xkind))
proc Eat(p: var TParser, tok: string, n: PNode) =
if p.tok.s == tok: getTok(p, n)
else: parMessage(p, errTokenExpected, tok)
proc Opt(p: var TParser, xkind: TTokKind, n: PNode) =
if p.tok.xkind == xkind: getTok(p, n)
proc addSon(father, a, b: PNode) =
addSon(father, a)
addSon(father, b)
proc addSon(father, a, b, c: PNode) =
addSon(father, a)
addSon(father, b)
addSon(father, c)
proc newNodeP(kind: TNodeKind, p: TParser): PNode =
result = newNodeI(kind, getLineInfo(p.lex))
proc newIntNodeP(kind: TNodeKind, intVal: BiggestInt, p: TParser): PNode =
result = newNodeP(kind, p)
result.intVal = intVal
proc newFloatNodeP(kind: TNodeKind, floatVal: BiggestFloat,
p: TParser): PNode =
result = newNodeP(kind, p)
result.floatVal = floatVal
proc newStrNodeP(kind: TNodeKind, strVal: string, p: TParser): PNode =
result = newNodeP(kind, p)
result.strVal = strVal
proc newIdentNodeP(ident: PIdent, p: TParser): PNode =
result = newNodeP(nkIdent, p)
result.ident = ident
proc newIdentNodeP(ident: string, p: TParser): PNode =
result = newIdentNodeP(getIdent(ident), p)
proc mangleRules(s: string, p: TParser): string =
block mangle:
for pattern, frmt in items(p.options.mangleRules):
if s.match(pattern):
result = s.replacef(pattern, frmt)
break mangle
block prefixes:
for prefix in items(p.options.prefixes):
if s.startsWith(prefix):
result = s.substr(prefix.len)
break prefixes
result = s
block suffixes:
for suffix in items(p.options.suffixes):
if result.endsWith(suffix):
setLen(result, result.len - suffix.len)
break suffixes
proc mangleName(s: string, p: TParser): string =
if p.options.toMangle.hasKey(s): result = p.options.toMangle[s]
else: result = mangleRules(s, p)
proc isPrivate(s: string, p: TParser): bool =
for pattern in items(p.options.privateRules):
if s.match(pattern): return true
proc mangledIdent(ident: string, p: TParser): PNode =
result = newNodeP(nkIdent, p)
result.ident = getIdent(mangleName(ident, p))
proc newIdentPair(a, b: string, p: TParser): PNode =
result = newNodeP(nkExprColonExpr, p)
addSon(result, newIdentNodeP(a, p))
addSon(result, newIdentNodeP(b, p))
proc newIdentStrLitPair(a, b: string, p: TParser): PNode =
result = newNodeP(nkExprColonExpr, p)
addSon(result, newIdentNodeP(a, p))
addSon(result, newStrNodeP(nkStrLit, b, p))
proc addImportToPragma(pragmas: PNode, ident: string, p: TParser) =
addSon(pragmas, newIdentStrLitPair("importc", ident, p))
if p.options.dynlibSym.len > 0:
addSon(pragmas, newIdentPair("dynlib", p.options.dynlibSym, p))
else:
addSon(pragmas, newIdentStrLitPair("header", p.options.header, p))
proc exportSym(p: TParser, i: PNode, origName: string): PNode =
assert i.kind == nkIdent
if p.scopeCounter == 0 and not isPrivate(origName, p):
result = newNodeI(nkPostfix, i.info)
addSon(result, newIdentNode(getIdent("*"), i.info), i)
else:
result = i
proc varIdent(ident: string, p: TParser): PNode =
result = exportSym(p, mangledIdent(ident, p), ident)
if p.scopeCounter > 0: return
if p.options.dynlibSym.len > 0 or p.options.header.len > 0:
var a = result
result = newNodeP(nkPragmaExpr, p)
var pragmas = newNodeP(nkPragma, p)
addSon(result, a)
addSon(result, pragmas)
addImportToPragma(pragmas, ident, p)
proc fieldIdent(ident: string, p: TParser): PNode =
result = exportSym(p, mangledIdent(ident, p), ident)
if p.scopeCounter > 0: return
if p.options.header.len > 0:
var a = result
result = newNodeP(nkPragmaExpr, p)
var pragmas = newNodeP(nkPragma, p)
addSon(result, a)
addSon(result, pragmas)
addSon(pragmas, newIdentStrLitPair("importc", ident, p))
proc DoImport(ident: string, pragmas: PNode, p: TParser) =
if p.options.dynlibSym.len > 0 or p.options.header.len > 0:
addImportToPragma(pragmas, ident, p)
proc newBinary(opr: string, a, b: PNode, p: TParser): PNode =
result = newNodeP(nkInfix, p)
addSon(result, newIdentNodeP(getIdent(opr), p))
addSon(result, a)
addSon(result, b)
proc skipIdent(p: var TParser): PNode =
expectIdent(p)
result = mangledIdent(p.tok.s, p)
getTok(p, result)
proc skipIdentExport(p: var TParser): PNode =
expectIdent(p)
result = exportSym(p, mangledIdent(p.tok.s, p), p.tok.s)
getTok(p, result)
proc skipTypeIdentExport(p: var TParser, prefix='T'): PNode =
expectIdent(p)
var n = prefix & mangleName(p.tok.s, p)
p.options.toMangle[p.tok.s] = n
var i = newNodeP(nkIdent, p)
i.ident = getIdent(n)
result = exportSym(p, i, p.tok.s)
getTok(p, result)
proc markTypeIdent(p: var TParser, typ: PNode) =
if pfTypePrefixes in p.options.flags:
var prefix = ""
if typ == nil or typ.kind == nkEmpty:
prefix = "T"
else:
var t = typ
while t != nil and t.kind in {nkVarTy, nkPtrTy, nkRefTy}:
prefix.add('P')
t = t.sons[0]
if prefix.len == 0: prefix.add('T')
expectIdent(p)
p.options.toMangle[p.tok.s] = prefix & mangleRules(p.tok.s, p)
# --------------- parser -----------------------------------------------------
# We use this parsing rule: If it looks like a declaration, it is one. This
# avoids to build a symbol table, which can't be done reliably anyway for our
# purposes.
proc expression(p: var TParser): PNode
proc constantExpression(p: var TParser): PNode
proc assignmentExpression(p: var TParser): PNode
proc compoundStatement(p: var TParser): PNode
proc statement(p: var TParser): PNode
proc declKeyword(s: string): bool =
# returns true if it is a keyword that introduces a declaration
case s
of "extern", "static", "auto", "register", "const", "volatile", "restrict",
"inline", "__inline", "__cdecl", "__stdcall", "__syscall", "__fastcall",
"__safecall", "void", "struct", "union", "enum", "typedef",
"short", "int", "long", "float", "double", "signed", "unsigned", "char":
result = true
proc stmtKeyword(s: string): bool =
case s
of "if", "for", "while", "do", "switch", "break", "continue", "return",
"goto":
result = true
# ------------------- type desc -----------------------------------------------
proc isIntType(s: string): bool =
case s
of "short", "int", "long", "float", "double", "signed", "unsigned":
result = true
proc skipConst(p: var TParser) =
while p.tok.xkind == pxSymbol and
(p.tok.s == "const" or p.tok.s == "volatile" or p.tok.s == "restrict"):
getTok(p, nil)
proc typeAtom(p: var TParser): PNode =
skipConst(p)
ExpectIdent(p)
case p.tok.s
of "void":
result = newNodeP(nkNilLit, p) # little hack
getTok(p, nil)
of "struct", "union", "enum":
getTok(p, nil)
result = skipIdent(p)
elif isIntType(p.tok.s):
var x = ""
#getTok(p, nil)
var isUnsigned = false
while p.tok.xkind == pxSymbol and (isIntType(p.tok.s) or p.tok.s == "char"):
if p.tok.s == "unsigned":
isUnsigned = true
elif p.tok.s == "signed" or p.tok.s == "int":
nil
else:
add(x, p.tok.s)
getTok(p, nil)
if x.len == 0: x = "int"
let xx = if isUnsigned: "cu" & x else: "c" & x
result = mangledIdent(xx, p)
else:
result = mangledIdent(p.tok.s, p)
getTok(p, result)
proc newPointerTy(p: TParser, typ: PNode): PNode =
if pfRefs in p.options.flags:
result = newNodeP(nkRefTy, p)
else:
result = newNodeP(nkPtrTy, p)
result.addSon(typ)
proc pointer(p: var TParser, a: PNode): PNode =
result = a
var i = 0
skipConst(p)
while p.tok.xkind == pxStar:
inc(i)
getTok(p, result)
skipConst(p)
result = newPointerTy(p, result)
if a.kind == nkIdent and a.ident.s == "char":
if i >= 2:
result = newIdentNodeP("cstringArray", p)
for j in 1..i-2: result = newPointerTy(p, result)
elif i == 1: result = newIdentNodeP("cstring", p)
elif a.kind == nkNilLit and i > 0:
result = newIdentNodeP("pointer", p)
for j in 1..i-1: result = newPointerTy(p, result)
proc newProcPragmas(p: TParser): PNode =
result = newNodeP(nkPragma, p)
if pfCDecl in p.options.flags:
addSon(result, newIdentNodeP("cdecl", p))
elif pfStdCall in p.options.flags:
addSon(result, newIdentNodeP("stdcall", p))
proc addPragmas(father, pragmas: PNode) =
if sonsLen(pragmas) > 0: addSon(father, pragmas)
else: addSon(father, ast.emptyNode)
proc addReturnType(params, rettyp: PNode) =
if rettyp == nil: addSon(params, ast.emptyNode)
elif rettyp.kind != nkNilLit: addSon(params, rettyp)
else: addson(params, ast.emptyNode)
proc parseFormalParams(p: var TParser, params, pragmas: PNode)
proc parseTypeSuffix(p: var TParser, typ: PNode): PNode =
result = typ
while true:
case p.tok.xkind
of pxBracketLe:
getTok(p, result)
skipConst(p) # POSIX contains: ``int [restrict]``
if p.tok.xkind != pxBracketRi:
var tmp = result
var index = expression(p)
# array type:
result = newNodeP(nkBracketExpr, p)
addSon(result, newIdentNodeP("array", p))
var r = newNodeP(nkRange, p)
addSon(r, newIntNodeP(nkIntLit, 0, p))
addSon(r, newBinary("-", index, newIntNodeP(nkIntLit, 1, p), p))
addSon(result, r)
addSon(result, tmp)
else:
# pointer type:
var tmp = result
if pfRefs in p.options.flags:
result = newNodeP(nkRefTy, p)
else:
result = newNodeP(nkPtrTy, p)
result.addSon(tmp)
eat(p, pxBracketRi, result)
of pxParLe:
# function pointer:
var procType = newNodeP(nkProcTy, p)
var pragmas = newProcPragmas(p)
var params = newNodeP(nkFormalParams, p)
addReturnType(params, result)
parseFormalParams(p, params, pragmas)
addSon(procType, params)
addPragmas(procType, pragmas)
result = procType
else: break
proc typeDesc(p: var TParser): PNode =
result = pointer(p, typeAtom(p))
proc parseField(p: var TParser, kind: TNodeKind): PNode =
if p.tok.xkind == pxParLe:
getTok(p, nil)
while p.tok.xkind == pxStar: getTok(p, nil)
result = parseField(p, kind)
eat(p, pxParRi, result)
else:
expectIdent(p)
if kind == nkRecList: result = fieldIdent(p.tok.s, p)
else: result = mangledIdent(p.tok.s, p)
getTok(p, result)
proc takeOnlyFirstField(p: TParser, isUnion: bool): bool =
# if we generate an interface to a header file, *all* fields can be
# generated:
result = isUnion and p.options.header.len == 0
proc parseStructBody(p: var TParser, isUnion: bool,
kind: TNodeKind = nkRecList): PNode =
result = newNodeP(kind, p)
eat(p, pxCurlyLe, result)
while p.tok.xkind notin {pxEof, pxCurlyRi}:
var baseTyp = typeAtom(p)
while true:
var def = newNodeP(nkIdentDefs, p)
var t = pointer(p, baseTyp)
var i = parseField(p, kind)
t = parseTypeSuffix(p, t)
addSon(def, i, t, ast.emptyNode)
if not takeOnlyFirstField(p, isUnion) or sonsLen(result) < 1:
addSon(result, def)
if p.tok.xkind != pxComma: break
getTok(p, def)
eat(p, pxSemicolon, lastSon(result))
eat(p, pxCurlyRi, result)
proc structPragmas(p: TParser, name: PNode, origName: string): PNode =
assert name.kind == nkIdent
result = newNodeP(nkPragmaExpr, p)
addson(result, exportSym(p, name, origName))
var pragmas = newNodep(nkPragma, p)
addSon(pragmas, newIdentNodeP("pure", p), newIdentNodeP("final", p))
if p.options.header.len > 0:
addSon(pragmas, newIdentStrLitPair("importc", origName, p),
newIdentStrLitPair("header", p.options.header, p))
addSon(result, pragmas)
proc enumPragmas(p: TParser, name: PNode): PNode =
result = newNodeP(nkPragmaExpr, p)
addson(result, name)
var pragmas = newNodep(nkPragma, p)
var e = newNodeP(nkExprColonExpr, p)
# HACK: sizeof(cint) should be constructed as AST
addSon(e, newIdentNodeP("size", p), newIdentNodeP("sizeof(cint)", p))
addSon(pragmas, e)
addSon(result, pragmas)
proc parseStruct(p: var TParser, isUnion: bool): PNode =
result = newNodeP(nkObjectTy, p)
addSon(result, ast.emptyNode, ast.emptyNode) # no pragmas, no inheritance
if p.tok.xkind == pxCurlyLe:
addSon(result, parseStructBody(p, isUnion))
else:
addSon(result, newNodeP(nkRecList, p))
proc parseParam(p: var TParser, params: PNode) =
var typ = typeDesc(p)
# support for ``(void)`` parameter list:
if typ.kind == nkNilLit and p.tok.xkind == pxParRi: return
var name: PNode
if p.tok.xkind == pxSymbol:
name = skipIdent(p)
else:
# generate a name for the formal parameter:
var idx = sonsLen(params)+1
name = newIdentNodeP("a" & $idx, p)
typ = parseTypeSuffix(p, typ)
var x = newNodeP(nkIdentDefs, p)
addSon(x, name, typ)
if p.tok.xkind == pxAsgn:
# we support default parameters for C++:
getTok(p, x)
addSon(x, assignmentExpression(p))
else:
addSon(x, ast.emptyNode)
addSon(params, x)
proc parseFormalParams(p: var TParser, params, pragmas: PNode) =
eat(p, pxParLe, params)
while p.tok.xkind notin {pxEof, pxParRi}:
if p.tok.xkind == pxDotDotDot:
addSon(pragmas, newIdentNodeP("varargs", p))
getTok(p, pragmas)
break
parseParam(p, params)
if p.tok.xkind != pxComma: break
getTok(p, params)
eat(p, pxParRi, params)
proc parseCallConv(p: var TParser, pragmas: PNode) =
while p.tok.xkind == pxSymbol:
case p.tok.s
of "inline", "__inline": addSon(pragmas, newIdentNodeP("inline", p))
of "__cdecl": addSon(pragmas, newIdentNodeP("cdecl", p))
of "__stdcall": addSon(pragmas, newIdentNodeP("stdcall", p))
of "__syscall": addSon(pragmas, newIdentNodeP("syscall", p))
of "__fastcall": addSon(pragmas, newIdentNodeP("fastcall", p))
of "__safecall": addSon(pragmas, newIdentNodeP("safecall", p))
else: break
getTok(p, nil)
proc parseFunctionPointerDecl(p: var TParser, rettyp: PNode): PNode =
var procType = newNodeP(nkProcTy, p)
var pragmas = newProcPragmas(p)
var params = newNodeP(nkFormalParams, p)
eat(p, pxParLe, params)
addReturnType(params, rettyp)
parseCallConv(p, pragmas)
if p.tok.xkind == pxStar: getTok(p, params)
else: parMessage(p, errTokenExpected, "*")
if p.inTypeDef > 0: markTypeIdent(p, nil)
var name = skipIdentExport(p)
eat(p, pxParRi, name)
parseFormalParams(p, params, pragmas)
addSon(procType, params)
addPragmas(procType, pragmas)
if p.inTypeDef == 0:
result = newNodeP(nkVarSection, p)
var def = newNodeP(nkIdentDefs, p)
addSon(def, name, procType, ast.emptyNode)
addSon(result, def)
else:
result = newNodeP(nkTypeDef, p)
addSon(result, name, ast.emptyNode, procType)
assert result != nil
proc addTypeDef(section, name, t: PNode) =
var def = newNodeI(nkTypeDef, name.info)
addSon(def, name, ast.emptyNode, t)
addSon(section, def)
proc otherTypeDef(p: var TParser, section, typ: PNode) =
var name: PNode
var t = typ
if p.tok.xkind == pxStar:
t = pointer(p, t)
if p.tok.xkind == pxParLe:
# function pointer: typedef typ (*name)();
var x = parseFunctionPointerDecl(p, t)
name = x[0]
t = x[2]
else:
# typedef typ name;
markTypeIdent(p, t)
name = skipIdentExport(p)
t = parseTypeSuffix(p, t)
addTypeDef(section, name, t)
proc parseTrailingDefinedTypes(p: var TParser, section, typ: PNode) =
while p.tok.xkind == pxComma:
getTok(p, nil)
var newTyp = pointer(p, typ)
markTypeIdent(p, newTyp)
var newName = skipIdentExport(p)
newTyp = parseTypeSuffix(p, newTyp)
addTypeDef(section, newName, newTyp)
proc enumFields(p: var TParser): PNode =
result = newNodeP(nkEnumTy, p)
addSon(result, ast.emptyNode) # enum does not inherit from anything
while true:
var e = skipIdent(p)
if p.tok.xkind == pxAsgn:
getTok(p, e)
var c = constantExpression(p)
var a = e
e = newNodeP(nkEnumFieldDef, p)
addSon(e, a, c)
skipCom(p, e)
addSon(result, e)
if p.tok.xkind != pxComma: break
getTok(p, e)
# allow trailing comma:
if p.tok.xkind == pxCurlyRi: break
proc parseTypedefStruct(p: var TParser, result: PNode, isUnion: bool) =
getTok(p, result)
if p.tok.xkind == pxCurlyLe:
var t = parseStruct(p, isUnion)
var origName = p.tok.s
markTypeIdent(p, nil)
var name = skipIdent(p)
addTypeDef(result, structPragmas(p, name, origName), t)
parseTrailingDefinedTypes(p, result, name)
elif p.tok.xkind == pxSymbol:
# name to be defined or type "struct a", we don't know yet:
markTypeIdent(p, nil)
var origName = p.tok.s
var nameOrType = skipIdent(p)
case p.tok.xkind
of pxCurlyLe:
var t = parseStruct(p, isUnion)
if p.tok.xkind == pxSymbol:
# typedef struct tagABC {} abc, *pabc;
# --> abc is a better type name than tagABC!
markTypeIdent(p, nil)
var origName = p.tok.s
var name = skipIdent(p)
addTypeDef(result, structPragmas(p, name, origName), t)
parseTrailingDefinedTypes(p, result, name)
else:
addTypeDef(result, structPragmas(p, nameOrType, origName), t)
of pxSymbol:
# typedef struct a a?
if mangleName(p.tok.s, p) == nameOrType.ident.s:
# ignore the declaration:
getTok(p, nil)
else:
# typedef struct a b; or typedef struct a b[45];
otherTypeDef(p, result, nameOrType)
else:
otherTypeDef(p, result, nameOrType)
else:
expectIdent(p)
proc parseTypedefEnum(p: var TParser, result: PNode) =
getTok(p, result)
if p.tok.xkind == pxCurlyLe:
getTok(p, result)
var t = enumFields(p)
eat(p, pxCurlyRi, t)
var origName = p.tok.s
markTypeIdent(p, nil)
var name = skipIdent(p)
addTypeDef(result, enumPragmas(p, exportSym(p, name, origName)), t)
parseTrailingDefinedTypes(p, result, name)
elif p.tok.xkind == pxSymbol:
# name to be defined or type "enum a", we don't know yet:
markTypeIdent(p, nil)
var origName = p.tok.s
var nameOrType = skipIdent(p)
case p.tok.xkind
of pxCurlyLe:
getTok(p, result)
var t = enumFields(p)
eat(p, pxCurlyRi, t)
if p.tok.xkind == pxSymbol:
# typedef enum tagABC {} abc, *pabc;
# --> abc is a better type name than tagABC!
markTypeIdent(p, nil)
var origName = p.tok.s
var name = skipIdent(p)
addTypeDef(result, enumPragmas(p, exportSym(p, name, origName)), t)
parseTrailingDefinedTypes(p, result, name)
else:
addTypeDef(result,
enumPragmas(p, exportSym(p, nameOrType, origName)), t)
of pxSymbol:
# typedef enum a a?
if mangleName(p.tok.s, p) == nameOrType.ident.s:
# ignore the declaration:
getTok(p, nil)
else:
# typedef enum a b; or typedef enum a b[45];
otherTypeDef(p, result, nameOrType)
else:
otherTypeDef(p, result, nameOrType)
else:
expectIdent(p)
proc parseTypeDef(p: var TParser): PNode =
result = newNodeP(nkTypeSection, p)
while p.tok.xkind == pxSymbol and p.tok.s == "typedef":
getTok(p, result)
inc(p.inTypeDef)
expectIdent(p)
case p.tok.s
of "struct": parseTypedefStruct(p, result, isUnion=false)
of "union": parseTypedefStruct(p, result, isUnion=true)
of "enum": parseTypedefEnum(p, result)
else:
var t = typeAtom(p)
otherTypeDef(p, result, t)
eat(p, pxSemicolon)
dec(p.inTypeDef)
proc skipDeclarationSpecifiers(p: var TParser) =
while p.tok.xkind == pxSymbol:
case p.tok.s
of "extern", "static", "auto", "register", "const", "volatile":
getTok(p, nil)
else: break
proc parseInitializer(p: var TParser): PNode =
if p.tok.xkind == pxCurlyLe:
result = newNodeP(nkBracket, p)
getTok(p, result)
while p.tok.xkind notin {pxEof, pxCurlyRi}:
addSon(result, parseInitializer(p))
opt(p, pxComma, nil)
eat(p, pxCurlyRi, result)
else:
result = assignmentExpression(p)
proc addInitializer(p: var TParser, def: PNode) =
if p.tok.xkind == pxAsgn:
getTok(p, def)
addSon(def, parseInitializer(p))
else:
addSon(def, ast.emptyNode)
proc parseVarDecl(p: var TParser, baseTyp, typ: PNode,
origName: string): PNode =
result = newNodeP(nkVarSection, p)
var def = newNodeP(nkIdentDefs, p)
addSon(def, varIdent(origName, p))
addSon(def, parseTypeSuffix(p, typ))
addInitializer(p, def)
addSon(result, def)
while p.tok.xkind == pxComma:
getTok(p, def)
var t = pointer(p, baseTyp)
expectIdent(p)
def = newNodeP(nkIdentDefs, p)
addSon(def, varIdent(p.tok.s, p))
getTok(p, def)
addSon(def, parseTypeSuffix(p, t))
addInitializer(p, def)
addSon(result, def)
eat(p, pxSemicolon)
when false:
proc declaration(p: var TParser, father: PNode) =
# general syntax to parse is::
#
# baseType ::= typeIdent | ((struct|union|enum) ident ("{" body "}" )?
# | "{" body "}")
# declIdent ::= "(" "*" ident ")" formalParams ("=" exprNoComma)?
# | ident ((formalParams ("{" statements "}")?)|"="
# exprNoComma|(typeSuffix("=" exprNoComma)? ))?
# declaration ::= baseType (pointers)? declIdent ("," declIdent)*
var pragmas = newNodeP(nkPragma, p)
skipDeclarationSpecifiers(p)
parseCallConv(p, pragmas)
skipDeclarationSpecifiers(p)
expectIdent(p)
proc declaration(p: var TParser): PNode =
result = newNodeP(nkProcDef, p)
var pragmas = newNodeP(nkPragma, p)
skipDeclarationSpecifiers(p)
parseCallConv(p, pragmas)
skipDeclarationSpecifiers(p)
expectIdent(p)
var baseTyp = typeAtom(p)
var rettyp = pointer(p, baseTyp)
skipDeclarationSpecifiers(p)
parseCallConv(p, pragmas)
skipDeclarationSpecifiers(p)
if p.tok.xkind == pxParLe:
# Function pointer declaration: This is of course only a heuristic, but the
# best we can do here.
result = parseFunctionPointerDecl(p, rettyp)
eat(p, pxSemicolon)
return
ExpectIdent(p)
var origName = p.tok.s
getTok(p) # skip identifier
case p.tok.xkind
of pxParLe:
# really a function!
var name = mangledIdent(origName, p)
var params = newNodeP(nkFormalParams, p)
addReturnType(params, rettyp)
parseFormalParams(p, params, pragmas)
if pfCDecl in p.options.flags:
addSon(pragmas, newIdentNodeP("cdecl", p))
elif pfStdcall in p.options.flags:
addSon(pragmas, newIdentNodeP("stdcall", p))
# no pattern, no exceptions:
addSon(result, exportSym(p, name, origName), ast.emptyNode, ast.emptyNode)
addSon(result, params, pragmas, ast.emptyNode) # no exceptions
case p.tok.xkind
of pxSemicolon:
getTok(p)
addSon(result, ast.emptyNode) # nobody
if p.scopeCounter == 0: DoImport(origName, pragmas, p)
of pxCurlyLe:
addSon(result, compoundStatement(p))
else:
parMessage(p, errTokenExpected, ";")
if sonsLen(result.sons[pragmasPos]) == 0:
result.sons[pragmasPos] = ast.emptyNode
else:
result = parseVarDecl(p, baseTyp, rettyp, origName)
assert result != nil
proc createConst(name, typ, val: PNode, p: TParser): PNode =
result = newNodeP(nkConstDef, p)
addSon(result, name, typ, val)
proc enumSpecifier(p: var TParser): PNode =
saveContext(p)
getTok(p, nil) # skip "enum"
case p.tok.xkind
of pxCurlyLe:
closeContext(p)
# make a const section out of it:
result = newNodeP(nkConstSection, p)
getTok(p, result)
var i = 0
while true:
var name = skipIdentExport(p)
var val: PNode
if p.tok.xkind == pxAsgn:
getTok(p, name)
val = constantExpression(p)
if val.kind == nkIntLit: i = int(val.intVal)+1
else: parMessage(p, errXExpected, "int literal")
else:
val = newIntNodeP(nkIntLit, i, p)
inc(i)
var c = createConst(name, ast.emptyNode, val, p)
addSon(result, c)
if p.tok.xkind != pxComma: break
getTok(p, c)
# allow trailing comma:
if p.tok.xkind == pxCurlyRi: break
eat(p, pxCurlyRi, result)
eat(p, pxSemicolon)
of pxSymbol:
var origName = p.tok.s
markTypeIdent(p, nil)
result = skipIdent(p)
case p.tok.xkind
of pxCurlyLe:
closeContext(p)
var name = result
# create a type section containing the enum
result = newNodeP(nkTypeSection, p)
var t = newNodeP(nkTypeDef, p)
getTok(p, t)
var e = enumFields(p)
addSon(t, exportSym(p, name, origName), ast.emptyNode, e)
addSon(result, t)
eat(p, pxCurlyRi, result)
eat(p, pxSemicolon)
of pxSemicolon:
# just ignore ``enum X;`` for now.
closeContext(p)
getTok(p, nil)
else:
backtrackContext(p)
result = declaration(p)
else:
closeContext(p)
parMessage(p, errTokenExpected, "{")
result = ast.emptyNode
# Expressions
proc setBaseFlags(n: PNode, base: TNumericalBase) =
case base
of base10: nil
of base2: incl(n.flags, nfBase2)
of base8: incl(n.flags, nfBase8)
of base16: incl(n.flags, nfBase16)
proc unaryExpression(p: var TParser): PNode
proc isDefinitelyAType(p: var TParser): bool =
var starFound = false
var words = 0
while true:
case p.tok.xkind
of pxSymbol:
if declKeyword(p.tok.s): return true
elif starFound: return false
else: inc(words)
of pxStar:
starFound = true
of pxParRi: return words == 0 or words > 1 or starFound
else: return false
getTok(p, nil)
proc castExpression(p: var TParser): PNode =
if p.tok.xkind == pxParLe:
saveContext(p)
result = newNodeP(nkCast, p)
getTok(p, result)
var t = isDefinitelyAType(p)
backtrackContext(p)
if t:
eat(p, pxParLe, result)
var a = typeDesc(p)
eat(p, pxParRi, result)
addSon(result, a)
addSon(result, castExpression(p))
else:
# else it is just an expression in ():
result = newNodeP(nkPar, p)
eat(p, pxParLe, result)
addSon(result, expression(p))
if p.tok.xkind != pxParRi:
# ugh, it is a cast, even though it does not look like one:
result.kind = nkCast
addSon(result, castExpression(p))
eat(p, pxParRi, result)
#result = unaryExpression(p)
else:
result = unaryExpression(p)
proc primaryExpression(p: var TParser): PNode =
case p.tok.xkind
of pxSymbol:
if p.tok.s == "NULL":
result = newNodeP(nkNilLit, p)
else:
result = mangledIdent(p.tok.s, p)
getTok(p, result)
of pxIntLit:
result = newIntNodeP(nkIntLit, p.tok.iNumber, p)
setBaseFlags(result, p.tok.base)
getTok(p, result)
of pxInt64Lit:
result = newIntNodeP(nkInt64Lit, p.tok.iNumber, p)
setBaseFlags(result, p.tok.base)
getTok(p, result)
of pxFloatLit:
result = newFloatNodeP(nkFloatLit, p.tok.fNumber, p)
setBaseFlags(result, p.tok.base)
getTok(p, result)
of pxStrLit:
# Ansi C allows implicit string literal concatenations:
result = newStrNodeP(nkStrLit, p.tok.s, p)
getTok(p, result)
while p.tok.xkind == pxStrLit:
add(result.strVal, p.tok.s)
getTok(p, result)
of pxCharLit:
result = newIntNodeP(nkCharLit, ord(p.tok.s[0]), p)
getTok(p, result)
of pxParLe:
result = castExpression(p)
else:
result = ast.emptyNode
proc multiplicativeExpression(p: var TParser): PNode =
result = castExpression(p)
while true:
case p.tok.xkind
of pxStar:
var a = result
result = newNodeP(nkInfix, p)
addSon(result, newIdentNodeP("*", p), a)
getTok(p, result)
var b = castExpression(p)
addSon(result, b)
of pxSlash:
var a = result
result = newNodeP(nkInfix, p)
addSon(result, newIdentNodeP("div", p), a)
getTok(p, result)
var b = castExpression(p)
addSon(result, b)
of pxMod:
var a = result
result = newNodeP(nkInfix, p)
addSon(result, newIdentNodeP("mod", p), a)
getTok(p, result)
var b = castExpression(p)
addSon(result, b)
else: break
proc additiveExpression(p: var TParser): PNode =
result = multiplicativeExpression(p)
while true:
case p.tok.xkind
of pxPlus:
var a = result
result = newNodeP(nkInfix, p)
addSon(result, newIdentNodeP("+", p), a)
getTok(p, result)
var b = multiplicativeExpression(p)
addSon(result, b)
of pxMinus:
var a = result
result = newNodeP(nkInfix, p)
addSon(result, newIdentNodeP("-", p), a)
getTok(p, result)
var b = multiplicativeExpression(p)
addSon(result, b)
else: break
proc incdec(p: var TParser, opr: string): PNode =
result = newNodeP(nkCall, p)
addSon(result, newIdentNodeP(opr, p))
gettok(p, result)
addSon(result, unaryExpression(p))
proc unaryOp(p: var TParser, kind: TNodeKind): PNode =
result = newNodeP(kind, p)
getTok(p, result)
addSon(result, castExpression(p))
proc prefixCall(p: var TParser, opr: string): PNode =
result = newNodeP(nkPrefix, p)
addSon(result, newIdentNodeP(opr, p))
gettok(p, result)
addSon(result, castExpression(p))
proc postfixExpression(p: var TParser): PNode =
result = primaryExpression(p)
while true:
case p.tok.xkind
of pxBracketLe:
var a = result
result = newNodeP(nkBracketExpr, p)
addSon(result, a)
getTok(p, result)
var b = expression(p)
addSon(result, b)
eat(p, pxBracketRi, result)
of pxParLe:
var a = result
result = newNodeP(nkCall, p)
addSon(result, a)
getTok(p, result)
if p.tok.xkind != pxParRi:
a = assignmentExpression(p)
addSon(result, a)
while p.tok.xkind == pxComma:
getTok(p, a)
a = assignmentExpression(p)
addSon(result, a)
eat(p, pxParRi, result)
of pxDot, pxArrow:
var a = result
result = newNodeP(nkDotExpr, p)
addSon(result, a)
getTok(p, result)
addSon(result, skipIdent(p))
of pxPlusPlus:
var a = result
result = newNodeP(nkCall, p)
addSon(result, newIdentNodeP("inc", p))
gettok(p, result)
addSon(result, a)
of pxMinusMinus:
var a = result
result = newNodeP(nkCall, p)
addSon(result, newIdentNodeP("dec", p))
gettok(p, result)
addSon(result, a)
else: break
proc unaryExpression(p: var TParser): PNode =
case p.tok.xkind
of pxPlusPlus: result = incdec(p, "inc")
of pxMinusMinus: result = incdec(p, "dec")
of pxAmp: result = unaryOp(p, nkAddr)
of pxStar: result = unaryOp(p, nkBracketExpr)
of pxPlus: result = prefixCall(p, "+")
of pxMinus: result = prefixCall(p, "-")
of pxTilde: result = prefixCall(p, "not")
of pxNot: result = prefixCall(p, "not")
of pxSymbol:
if p.tok.s == "sizeof":
result = newNodeP(nkCall, p)
addSon(result, newIdentNodeP("sizeof", p))
getTok(p, result)
if p.tok.xkind == pxParLe:
getTok(p, result)
addson(result, typeDesc(p))
eat(p, pxParRi, result)
else:
addSon(result, unaryExpression(p))
else:
result = postfixExpression(p)
else: result = postfixExpression(p)
proc expression(p: var TParser): PNode =
# we cannot support C's ``,`` operator
result = assignmentExpression(p)
if p.tok.xkind == pxComma:
getTok(p, result)
parMessage(p, errOperatorExpected, ",")
proc conditionalExpression(p: var TParser): PNode
proc constantExpression(p: var TParser): PNode =
result = conditionalExpression(p)
proc lvalue(p: var TParser): PNode =
result = unaryExpression(p)
proc asgnExpr(p: var TParser, opr: string, a: PNode): PNode =
closeContext(p)
getTok(p, a)
var b = assignmentExpression(p)
result = newNodeP(nkAsgn, p)
addSon(result, a, newBinary(opr, copyTree(a), b, p))
proc incdec(p: var TParser, opr: string, a: PNode): PNode =
closeContext(p)
getTok(p, a)
var b = assignmentExpression(p)
result = newNodeP(nkCall, p)
addSon(result, newIdentNodeP(getIdent(opr), p), a, b)
proc assignmentExpression(p: var TParser): PNode =
saveContext(p)
var a = lvalue(p)
case p.tok.xkind
of pxAsgn:
closeContext(p)
getTok(p, a)
var b = assignmentExpression(p)
result = newNodeP(nkAsgn, p)
addSon(result, a, b)
of pxPlusAsgn: result = incDec(p, "inc", a)
of pxMinusAsgn: result = incDec(p, "dec", a)
of pxStarAsgn: result = asgnExpr(p, "*", a)
of pxSlashAsgn: result = asgnExpr(p, "/", a)
of pxModAsgn: result = asgnExpr(p, "mod", a)
of pxShlAsgn: result = asgnExpr(p, "shl", a)
of pxShrAsgn: result = asgnExpr(p, "shr", a)
of pxAmpAsgn: result = asgnExpr(p, "and", a)
of pxHatAsgn: result = asgnExpr(p, "xor", a)
of pxBarAsgn: result = asgnExpr(p, "or", a)
else:
backtrackContext(p)
result = conditionalExpression(p)
proc shiftExpression(p: var TParser): PNode =
result = additiveExpression(p)
while p.tok.xkind in {pxShl, pxShr}:
var op = if p.tok.xkind == pxShl: "shl" else: "shr"
getTok(p, result)
var a = result
var b = additiveExpression(p)
result = newBinary(op, a, b, p)
proc relationalExpression(p: var TParser): PNode =
result = shiftExpression(p)
# Nimrod uses ``<`` and ``<=``, etc. too:
while p.tok.xkind in {pxLt, pxLe, pxGt, pxGe}:
var op = TokKindToStr(p.tok.xkind)
getTok(p, result)
var a = result
var b = shiftExpression(p)
result = newBinary(op, a, b, p)
proc equalityExpression(p: var TParser): PNode =
result = relationalExpression(p)
# Nimrod uses ``==`` and ``!=`` too:
while p.tok.xkind in {pxEquals, pxNeq}:
var op = TokKindToStr(p.tok.xkind)
getTok(p, result)
var a = result
var b = relationalExpression(p)
result = newBinary(op, a, b, p)
proc andExpression(p: var TParser): PNode =
result = equalityExpression(p)
while p.tok.xkind == pxAmp:
getTok(p, result)
var a = result
var b = equalityExpression(p)
result = newBinary("and", a, b, p)
proc exclusiveOrExpression(p: var TParser): PNode =
result = andExpression(p)
while p.tok.xkind == pxHat:
getTok(p, result)
var a = result
var b = andExpression(p)
result = newBinary("^", a, b, p)
proc inclusiveOrExpression(p: var TParser): PNode =
result = exclusiveOrExpression(p)
while p.tok.xkind == pxBar:
getTok(p, result)
var a = result
var b = exclusiveOrExpression(p)
result = newBinary("or", a, b, p)
proc logicalAndExpression(p: var TParser): PNode =
result = inclusiveOrExpression(p)
while p.tok.xkind == pxAmpAmp:
getTok(p, result)
var a = result
var b = inclusiveOrExpression(p)
result = newBinary("and", a, b, p)
proc logicalOrExpression(p: var TParser): PNode =
result = logicalAndExpression(p)
while p.tok.xkind == pxBarBar:
getTok(p, result)
var a = result
var b = logicalAndExpression(p)
result = newBinary("or", a, b, p)
proc conditionalExpression(p: var TParser): PNode =
result = logicalOrExpression(p)
if p.tok.xkind == pxConditional:
getTok(p, result) # skip '?'
var a = result
var b = expression(p)
eat(p, pxColon, b)
var c = conditionalExpression(p)
result = newNodeP(nkIfExpr, p)
var branch = newNodeP(nkElifExpr, p)
addSon(branch, a, b)
addSon(result, branch)
branch = newNodeP(nkElseExpr, p)
addSon(branch, c)
addSon(result, branch)
# Statements
proc buildStmtList(a: PNode): PNode =
if a.kind == nkStmtList: result = a
else:
result = newNodeI(nkStmtList, a.info)
addSon(result, a)
proc nestedStatement(p: var TParser): PNode =
# careful: We need to translate:
# if (x) if (y) stmt;
# into:
# if x:
# if x:
# stmt
#
# Nimrod requires complex statements to be nested in whitespace!
const
complexStmt = {nkProcDef, nkMethodDef, nkConverterDef, nkMacroDef,
nkTemplateDef, nkIteratorDef, nkIfStmt,
nkWhenStmt, nkForStmt, nkWhileStmt, nkCaseStmt, nkVarSection,
nkConstSection, nkTypeSection, nkTryStmt, nkBlockStmt, nkStmtList,
nkCommentStmt, nkStmtListExpr, nkBlockExpr, nkStmtListType, nkBlockType}
result = statement(p)
if result.kind in complexStmt:
result = buildStmtList(result)
proc expressionStatement(p: var TParser): PNode =
# do not skip the comment after a semicolon to make a new nkCommentStmt
if p.tok.xkind == pxSemicolon:
getTok(p)
result = ast.emptyNode
else:
result = expression(p)
if p.tok.xkind == pxSemicolon: getTok(p)
else: parMessage(p, errTokenExpected, ";")
assert result != nil
proc parseIf(p: var TParser): PNode =
# we parse additional "else if"s too here for better Nimrod code
result = newNodeP(nkIfStmt, p)
while true:
getTok(p) # skip ``if``
var branch = newNodeP(nkElifBranch, p)
skipCom(p, branch)
eat(p, pxParLe, branch)
addSon(branch, expression(p))
eat(p, pxParRi, branch)
addSon(branch, nestedStatement(p))
addSon(result, branch)
if p.tok.s == "else":
getTok(p, result)
if p.tok.s != "if":
# ordinary else part:
branch = newNodeP(nkElse, p)
addSon(branch, nestedStatement(p))
addSon(result, branch)
break
else:
break
proc parseWhile(p: var TParser): PNode =
result = newNodeP(nkWhileStmt, p)
getTok(p, result)
eat(p, pxParLe, result)
addSon(result, expression(p))
eat(p, pxParRi, result)
addSon(result, nestedStatement(p))
proc parseDoWhile(p: var TParser): PNode =
# we only support ``do stmt while (0)`` as an idiom for
# ``block: stmt``
result = newNodeP(nkBlockStmt, p)
getTok(p, result) # skip "do"
addSon(result, ast.emptyNode, nestedStatement(p))
eat(p, "while", result)
eat(p, pxParLe, result)
if p.tok.xkind == pxIntLit and p.tok.iNumber == 0: getTok(p, result)
else: parMessage(p, errTokenExpected, "0")
eat(p, pxParRi, result)
if p.tok.xkind == pxSemicolon: getTok(p)
proc declarationOrStatement(p: var TParser): PNode =
if p.tok.xkind != pxSymbol:
result = expressionStatement(p)
elif declKeyword(p.tok.s):
result = declaration(p)
else:
# ordinary identifier:
saveContext(p)
getTok(p) # skip identifier to look ahead
case p.tok.xkind
of pxSymbol, pxStar:
# we parse
# a b
# a * b
# always as declarations! This is of course not correct, but good
# enough for most real world C code out there.
backtrackContext(p)
result = declaration(p)
of pxColon:
# it is only a label:
closeContext(p)
getTok(p)
result = statement(p)
else:
backtrackContext(p)
result = expressionStatement(p)
assert result != nil
proc parseTuple(p: var TParser, isUnion: bool): PNode =
result = parseStructBody(p, isUnion, nkTupleTy)
proc parseTrailingDefinedIdents(p: var TParser, result, baseTyp: PNode) =
var varSection = newNodeP(nkVarSection, p)
while p.tok.xkind notin {pxEof, pxSemicolon}:
var t = pointer(p, baseTyp)
expectIdent(p)
var def = newNodeP(nkIdentDefs, p)
addSon(def, varIdent(p.tok.s, p))
getTok(p, def)
addSon(def, parseTypeSuffix(p, t))
addInitializer(p, def)
addSon(varSection, def)
if p.tok.xkind != pxComma: break
getTok(p, def)
eat(p, pxSemicolon)
if sonsLen(varSection) > 0:
addSon(result, varSection)
proc parseStandaloneStruct(p: var TParser, isUnion: bool): PNode =
result = newNodeP(nkStmtList, p)
saveContext(p)
getTok(p, result) # skip "struct" or "union"
var origName = ""
if p.tok.xkind == pxSymbol:
markTypeIdent(p, nil)
origName = p.tok.s
getTok(p, result)
if p.tok.xkind in {pxCurlyLe, pxSemiColon}:
if origName.len > 0:
var name = mangledIdent(origName, p)
var t = parseStruct(p, isUnion)
var typeSection = newNodeP(nkTypeSection, p)
addTypeDef(typeSection, structPragmas(p, name, origName), t)
addSon(result, typeSection)
parseTrailingDefinedIdents(p, result, name)
else:
var t = parseTuple(p, isUnion)
parseTrailingDefinedIdents(p, result, t)
else:
backtrackContext(p)
result = declaration(p)
proc parseFor(p: var TParser, result: PNode) =
# 'for' '(' expression_statement expression_statement expression? ')'
# statement
getTok(p, result)
eat(p, pxParLe, result)
var initStmt = declarationOrStatement(p)
if initStmt.kind != nkEmpty:
addSon(result, initStmt)
var w = newNodeP(nkWhileStmt, p)
var condition = expressionStatement(p)
if condition.kind == nkEmpty: condition = newIdentNodeP("true", p)
addSon(w, condition)
var step = if p.tok.xkind != pxParRi: expression(p) else: ast.emptyNode
eat(p, pxParRi, step)
var loopBody = nestedStatement(p)
if step.kind != nkEmpty:
loopBody = buildStmtList(loopBody)
addSon(loopBody, step)
addSon(w, loopBody)
addSon(result, w)
proc switchStatement(p: var TParser): PNode =
result = newNodeP(nkStmtList, p)
while true:
if p.tok.xkind in {pxEof, pxCurlyRi}: break
case p.tok.s
of "break":
getTok(p, result)
eat(p, pxSemicolon, result)
break
of "return", "continue", "goto":
addSon(result, statement(p))
break
of "case", "default":
break
else: nil
addSon(result, statement(p))
if sonsLen(result) == 0:
# translate empty statement list to Nimrod's ``nil`` statement
result = newNodeP(nkNilLit, p)
proc rangeExpression(p: var TParser): PNode =
# We support GCC's extension: ``case expr...expr:``
result = constantExpression(p)
if p.tok.xkind == pxDotDotDot:
getTok(p, result)
var a = result
var b = constantExpression(p)
result = newNodeP(nkRange, p)
addSon(result, a)
addSon(result, b)
proc parseSwitch(p: var TParser): PNode =
# We cannot support Duff's device or C's crazy switch syntax. We just support
# sane usages of switch. ;-)
result = newNodeP(nkCaseStmt, p)
getTok(p, result)
eat(p, pxParLe, result)
addSon(result, expression(p))
eat(p, pxParRi, result)
eat(p, pxCurlyLe, result)
var b: PNode
while (p.tok.xkind != pxCurlyRi) and (p.tok.xkind != pxEof):
case p.tok.s
of "default":
b = newNodeP(nkElse, p)
getTok(p, b)
eat(p, pxColon, b)
of "case":
b = newNodeP(nkOfBranch, p)
while p.tok.xkind == pxSymbol and p.tok.s == "case":
getTok(p, b)
addSon(b, rangeExpression(p))
eat(p, pxColon, b)
else:
parMessage(p, errXExpected, "case")
addSon(b, switchStatement(p))
addSon(result, b)
if b.kind == nkElse: break
eat(p, pxCurlyRi)
proc addStmt(sl, a: PNode) =
# merge type sections if possible:
if a.kind != nkTypeSection or sonsLen(sl) == 0 or
lastSon(sl).kind != nkTypeSection:
addSon(sl, a)
else:
var ts = lastSon(sl)
for i in 0..sonsLen(a)-1: addSon(ts, a.sons[i])
proc embedStmts(sl, a: PNode) =
if a.kind != nkStmtList:
addStmt(sl, a)
else:
for i in 0..sonsLen(a)-1:
if a[i].kind != nkEmpty: addStmt(sl, a[i])
proc compoundStatement(p: var TParser): PNode =
result = newNodeP(nkStmtList, p)
eat(p, pxCurlyLe)
inc(p.scopeCounter)
while p.tok.xkind notin {pxEof, pxCurlyRi}:
var a = statement(p)
if a.kind == nkEmpty: break
embedStmts(result, a)
if sonsLen(result) == 0:
# translate ``{}`` to Nimrod's ``nil`` statement
result = newNodeP(nkNilLit, p)
dec(p.scopeCounter)
eat(p, pxCurlyRi)
include cpp
proc statement(p: var TParser): PNode =
case p.tok.xkind
of pxSymbol:
case p.tok.s
of "if": result = parseIf(p)
of "switch": result = parseSwitch(p)
of "while": result = parseWhile(p)
of "do": result = parseDoWhile(p)
of "for":
result = newNodeP(nkStmtList, p)
parseFor(p, result)
of "goto":
# we cannot support "goto"; in hand-written C, "goto" is most often used
# to break a block, so we convert it to a break statement with label.
result = newNodeP(nkBreakStmt, p)
getTok(p)
addSon(result, skipIdent(p))
eat(p, pxSemicolon)
of "continue":
result = newNodeP(nkContinueStmt, p)
getTok(p)
eat(p, pxSemicolon)
addSon(result, ast.emptyNode)
of "break":
result = newNodeP(nkBreakStmt, p)
getTok(p)
eat(p, pxSemicolon)
addSon(result, ast.emptyNode)
of "return":
result = newNodeP(nkReturnStmt, p)
getTok(p)
# special case for ``return (expr)`` because I hate the redundant
# parenthesis ;-)
if p.tok.xkind == pxParLe:
getTok(p, result)
addSon(result, expression(p))
eat(p, pxParRi, result)
elif p.tok.xkind != pxSemicolon:
addSon(result, expression(p))
else:
addSon(result, ast.emptyNode)
eat(p, pxSemicolon)
of "enum": result = enumSpecifier(p)
of "typedef": result = parseTypeDef(p)
of "struct": result = parseStandaloneStruct(p, isUnion=false)
of "union": result = parseStandaloneStruct(p, isUnion=true)
else: result = declarationOrStatement(p)
of pxCurlyLe:
result = compoundStatement(p)
of pxDirective, pxDirectiveParLe:
result = parseDir(p)
of pxLineComment, pxStarComment:
result = newNodeP(nkCommentStmt, p)
skipCom(p, result)
of pxSemicolon:
# empty statement:
getTok(p)
if p.tok.xkind in {pxLineComment, pxStarComment}:
result = newNodeP(nkCommentStmt, p)
skipCom(p, result)
else:
result = newNodeP(nkNilLit, p)
else:
result = expressionStatement(p)
assert result != nil
proc parseUnit(p: var TParser): PNode =
result = newNodeP(nkStmtList, p)
getTok(p) # read first token
while p.tok.xkind != pxEof:
var s = statement(p)
if s.kind != nkEmpty: embedStmts(result, s)