cn2im: basic C++ support

This commit is contained in:
Araq
2013-09-10 00:40:06 +02:00
parent 094d7fd4b1
commit db19b18241
8 changed files with 720 additions and 55 deletions

View File

@@ -19,6 +19,7 @@ c2nim - C to Nimrod source converter
Usage: c2nim [options] inputfile [options]
Options:
-o, --out:FILE set output filename
--cpp process C++ input file
--dynlib:SYMBOL import from dynlib: SYMBOL will be used for the import
--header:HEADER_FILE import from a HEADER_FILE (discouraged!)
--cdecl annotate procs with ``{.cdecl.}``
@@ -31,6 +32,8 @@ Options:
--skipinclude do not convert ``#include`` to ``import``
--typeprefixes generate ``T`` and ``P`` type prefixes
--skipcomments do not copy comments
--ignoreRValueRefs translate C++'s ``T&&`` to ``T`` instead ``of var T``
--keepBodies keep C++'s method bodies
-v, --version write c2nim's version
-h, --help show this help
"""

View File

@@ -82,6 +82,8 @@ type
pxParLe, pxBracketLe, pxCurlyLe, # this order is important
pxParRi, pxBracketRi, pxCurlyRi, # for macro argument parsing!
pxComma, pxSemiColon, pxColon,
pxAngleRi # '>' but determined to be the end of a
# template's angle bracket
TTokKinds* = set[TTokKind]
type
@@ -202,6 +204,7 @@ proc TokKindToStr*(k: TTokKind): string =
of pxColon: result = ":"
of pxCurlyLe: result = "{"
of pxCurlyRi: result = "}"
of pxAngleRi: result = "> [end of template]"
proc `$`(tok: TToken): string =
case tok.xkind

View File

@@ -11,7 +11,11 @@
## 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.
# TODO
# - implement handling of '::'
# - C++'s "operator" still needs some love
# - ignore 'using' statements
# - support '#if' in classes
import
os, llstream, renderer, clex, idents, strutils, pegs, ast, astalgo, msgs,
@@ -24,14 +28,17 @@ type
pfStdCall, ## annotate procs with stdcall
pfSkipInclude, ## skip all ``#include``
pfTypePrefixes, ## all generated types start with 'T' or 'P'
pfSkipComments ## do not generate comments
pfSkipComments, ## do not generate comments
pfCpp, ## process C++
pfIgnoreRValueRefs, ## transform C++'s 'T&&' to 'T'
pfKeepBodies ## do not skip C++ method bodies
TMacro {.final.} = object
TMacro = object
name: string
params: int # number of parameters
body: seq[ref TToken] # can contain pxMacroParam tokens
TParserOptions {.final.} = object
TParserOptions = object
flags: set[TParserFlag]
prefixes, suffixes: seq[string]
mangleRules: seq[tuple[pattern: TPeg, frmt: string]]
@@ -41,7 +48,7 @@ type
toMangle: PStringTable
PParserOptions* = ref TParserOptions
TParser* {.final.} = object
TParser* = object
lex: TLexer
tok: ref TToken # current token
options: PParserOptions
@@ -49,6 +56,7 @@ type
inTypeDef: int
scopeCounter: int
hasDeadCodeElimPragma: bool
currentClass: PNode # type that needs to be added as 'this' parameter
TReplaceTuple* = array[0..1, string]
@@ -66,7 +74,7 @@ proc newParserOptions*(): PParserOptions =
proc setOption*(parserOptions: PParserOptions, key: string, val=""): bool =
result = true
case key
case key.normalize
of "ref": incl(parserOptions.flags, pfRefs)
of "dynlib": parserOptions.dynlibSym = val
of "header": parserOptions.header = val
@@ -77,6 +85,9 @@ proc setOption*(parserOptions: PParserOptions, key: string, val=""): bool =
of "skipinclude": incl(parserOptions.flags, pfSkipInclude)
of "typeprefixes": incl(parserOptions.flags, pfTypePrefixes)
of "skipcomments": incl(parserOptions.flags, pfSkipComments)
of "cpp": incl(parserOptions.flags, pfCpp)
of "keepbodies": incl(parserOptions.flags, pfKeepBodies)
of "ignorervaluerefs": incl(parserOptions.flags, pfIgnoreRValueRefs)
else: result = false
proc ParseUnit*(p: var TParser): PNode
@@ -94,6 +105,7 @@ proc OpenParser(p: var TParser, filename: string,
new(p.tok)
proc parMessage(p: TParser, msg: TMsgKind, arg = "") =
#assert false
lexMessage(p.lex, msg, arg)
proc CloseParser(p: var TParser) = CloseLexer(p.lex)
@@ -116,6 +128,13 @@ proc rawGetTok(p: var TParser) =
p.tok.next = t
p.tok = t
proc insertAngleRi(currentToken: ref TToken) =
var t: ref TToken
new(t)
t.xkind = pxAngleRi
t.next = currentToken.next
currentToken.next = 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
@@ -346,6 +365,14 @@ 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 DoImportCpp(ident: string, pragmas: PNode, p: TParser) =
if p.options.dynlibSym.len > 0 or p.options.header.len > 0:
addSon(pragmas, newIdentStrLitPair("importcpp", 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 newBinary(opr: string, a, b: PNode, p: TParser): PNode =
result = newNodeP(nkInfix, p)
addSon(result, newIdentNodeP(getIdent(opr), p))
@@ -396,7 +423,7 @@ proc assignmentExpression(p: var TParser): PNode
proc compoundStatement(p: var TParser): PNode
proc statement(p: var TParser): PNode
proc declKeyword(s: string): bool =
proc declKeyword(p: TParser, s: string): bool =
# returns true if it is a keyword that introduces a declaration
case s
of "extern", "static", "auto", "register", "const", "volatile", "restrict",
@@ -404,6 +431,8 @@ proc declKeyword(s: string): bool =
"__safecall", "void", "struct", "union", "enum", "typedef",
"short", "int", "long", "float", "double", "signed", "unsigned", "char":
result = true
of "class":
result = p.options.flags.contains(pfCpp)
proc stmtKeyword(s: string): bool =
case s
@@ -423,6 +452,59 @@ proc skipConst(p: var TParser) =
(p.tok.s == "const" or p.tok.s == "volatile" or p.tok.s == "restrict"):
getTok(p, nil)
proc isTemplateAngleBracket(p: var TParser): bool =
if pfCpp notin p.options.flags: return false
saveContext(p)
getTok(p, nil) # skip "<"
var i: array[pxParLe..pxCurlyLe, int]
var angles = 0
while true:
let kind = p.tok.xkind
case kind
of pxEof: break
of pxParLe, pxBracketLe, pxCurlyLe: inc(i[kind])
of pxGt, pxAngleRi:
# end of arguments?
if i[pxParLe] == 0 and i[pxBracketLe] == 0 and i[pxCurlyLe] == 0 and
angles == 0:
# mark as end token:
p.tok.xkind = pxAngleRi
result = true;
break
if angles > 0: dec(angles)
of pxShr:
# >> can end a template too:
if i[pxParLe] == 0 and i[pxBracketLe] == 0 and i[pxCurlyLe] == 0 and
angles == 1:
p.tok.xkind = pxAngleRi
insertAngleRi(p.tok)
result = true
break
if angles > 1: dec(angles, 2)
of pxLt: inc(angles)
of pxParRi, pxBracketRi, pxCurlyRi:
let kind = pred(kind, 3)
if i[kind] > 0: dec(i[kind])
else: break
of pxSemicolon: break
else: discard
getTok(p, nil)
backtrackContext(p)
proc optAngle(p: var TParser, n: PNode): PNode =
if p.tok.xkind == pxLt and isTemplateAngleBracket(p):
getTok(p)
result = newNodeP(nkBracketExpr, p)
result.add(n)
while true:
let a = assignmentExpression(p)
if not a.isNil: result.add(a)
if p.tok.xkind != pxComma: break
getTok(p)
eat(p, pxAngleRi)
else:
result = n
proc typeAtom(p: var TParser): PNode =
skipConst(p)
ExpectIdent(p)
@@ -451,6 +533,7 @@ proc typeAtom(p: var TParser): PNode =
else:
result = mangledIdent(p.tok.s, p)
getTok(p, result)
result = optAngle(p, result)
proc newPointerTy(p: TParser, typ: PNode): PNode =
if pfRefs in p.options.flags:
@@ -458,16 +541,31 @@ proc newPointerTy(p: TParser, typ: PNode): PNode =
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)
while true:
if p.tok.xkind == pxStar:
inc(i)
getTok(p, result)
skipConst(p)
result = newPointerTy(p, result)
elif p.tok.xkind == pxAmp and pfCpp in p.options.flags:
getTok(p, result)
skipConst(p)
let b = result
result = newNodeP(nkVarTy, p)
result.add(b)
elif p.tok.xkind == pxAmpAmp and pfCpp in p.options.flags:
getTok(p, result)
skipConst(p)
if pfIgnoreRvalueRefs notin p.options.flags:
let b = result
result = newNodeP(nkVarTy, p)
result.add(b)
else: break
if a.kind == nkIdent and a.ident.s == "char":
if i >= 2:
result = newIdentNodeP("cstringArray", p)
@@ -553,9 +651,9 @@ 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 =
kind: TNodeKind = nkRecList): PNode =
result = newNodeP(kind, p)
eat(p, pxCurlyLe, result)
while p.tok.xkind notin {pxEof, pxCurlyRi}:
@@ -682,7 +780,7 @@ proc addTypeDef(section, name, t: PNode) =
proc otherTypeDef(p: var TParser, section, typ: PNode) =
var name: PNode
var t = typ
if p.tok.xkind == pxStar:
if p.tok.xkind in {pxStar, pxAmp, pxAmpAmp}:
t = pointer(p, t)
if p.tok.xkind == pxParLe:
# function pointer: typedef typ (*name)();
@@ -819,6 +917,12 @@ proc parseTypeDef(p: var TParser): PNode =
of "struct": parseTypedefStruct(p, result, isUnion=false)
of "union": parseTypedefStruct(p, result, isUnion=true)
of "enum": parseTypedefEnum(p, result)
of "class":
if pfCpp in p.options.flags:
parseTypedefStruct(p, result, isUnion=false)
else:
var t = typeAtom(p)
otherTypeDef(p, result, t)
else:
var t = typeAtom(p)
otherTypeDef(p, result, t)
@@ -871,22 +975,16 @@ proc parseVarDecl(p: var TParser, baseTyp, typ: PNode,
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 declarationName(p: var TParser): string =
expectIdent(p)
result = p.tok.s
getTok(p) # skip identifier
while p.tok.xkind == pxScope and pfCpp in p.options.flags:
getTok(p) # skip "::"
expectIdent(p)
result.add("::")
result.add(p.tok.s)
getTok(p)
proc declaration(p: var TParser): PNode =
result = newNodeP(nkProcDef, p)
@@ -908,17 +1006,17 @@ proc declaration(p: var TParser): PNode =
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:
var origName = declarationName(p)
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 pfCpp in p.options.flags and p.tok.xkind == pxSymbol and
p.tok.s == "const":
addSon(pragmas, newIdentNodeP("noSideEffect", p))
if pfCDecl in p.options.flags:
addSon(pragmas, newIdentNodeP("cdecl", p))
elif pfStdcall in p.options.flags:
@@ -1027,10 +1125,10 @@ proc isDefinitelyAType(p: var TParser): bool =
while true:
case p.tok.xkind
of pxSymbol:
if declKeyword(p.tok.s): return true
if declKeyword(p, p.tok.s): return true
elif starFound: return false
else: inc(words)
of pxStar:
of pxStar, pxAmp, pxAmpAmp:
starFound = true
of pxParRi: return words == 0 or words > 1 or starFound
else: return false
@@ -1205,6 +1303,10 @@ proc postfixExpression(p: var TParser): PNode =
addSon(result, newIdentNodeP("dec", p))
gettok(p, result)
addSon(result, a)
of pxLt:
if isTemplateAngleBracket(p):
result = optAngle(p, result)
else: break
else: break
proc unaryExpression(p: var TParser): PNode =
@@ -1222,9 +1324,24 @@ proc unaryExpression(p: var TParser): PNode =
result = newNodeP(nkCall, p)
addSon(result, newIdentNodeP("sizeof", p))
getTok(p, result)
if p.tok.xkind == pxParLe:
if p.tok.xkind == pxParLe:
getTok(p, result)
addson(result, typeDesc(p))
addSon(result, typeDesc(p))
eat(p, pxParRi, result)
else:
addSon(result, unaryExpression(p))
elif p.tok.s == "new" or p.tok.s == "delete" and pfCpp in p.options.flags:
var opr = p.tok.s
result = newNodeP(nkCall, p)
getTok(p, result)
if p.tok.xkind == pxBracketLe:
getTok(p)
eat(p, pxBracketRi)
opr.add("Array")
addSon(result, newIdentNodeP(opr, p))
if p.tok.xkind == pxParLe:
getTok(p, result)
addSon(result, typeDesc(p))
eat(p, pxParRi, result)
else:
addSon(result, unaryExpression(p))
@@ -1455,14 +1572,14 @@ proc parseDoWhile(p: var TParser): PNode =
proc declarationOrStatement(p: var TParser): PNode =
if p.tok.xkind != pxSymbol:
result = expressionStatement(p)
elif declKeyword(p.tok.s):
elif declKeyword(p, 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:
case p.tok.xkind
of pxSymbol, pxStar, pxLt, pxAmp, pxAmpAmp:
# we parse
# a b
# a * b
@@ -1500,7 +1617,7 @@ proc parseTrailingDefinedIdents(p: var TParser, result, baseTyp: PNode) =
if sonsLen(varSection) > 0:
addSon(result, varSection)
proc parseStandaloneStruct(p: var TParser, isUnion: bool): PNode =
proc parseStandaloneStruct(p: var TParser, isUnion: bool): PNode =
result = newNodeP(nkStmtList, p)
saveContext(p)
getTok(p, result) # skip "struct" or "union"
@@ -1630,11 +1747,288 @@ proc compoundStatement(p: var TParser): PNode =
if a.kind == nkEmpty: break
embedStmts(result, a)
if sonsLen(result) == 0:
# translate ``{}`` to Nimrod's ``nil`` statement
result = newNodeP(nkNilLit, p)
# translate ``{}`` to Nimrod's ``discard`` statement
result = newNodeP(nkDiscardStmt, p)
result.add(ast.emptyNode)
dec(p.scopeCounter)
eat(p, pxCurlyRi)
proc skipInheritKeyw(p: var TParser) =
if p.tok.xkind == pxSymbol and (p.tok.s == "private" or
p.tok.s == "protected" or
p.tok.s == "public"):
getTok(p)
proc parseConstructor(p: var TParser, pragmas: PNode,
isDestructor=false): PNode =
var origName = p.tok.s
getTok(p)
result = newNodeP(nkProcDef, p)
var rettyp = if isDestructor: newNodeP(nkNilLit, p)
else: mangledIdent(origName, p)
let oname = if isDestructor: "destroy" & origName
else: "construct" & origName
var name = mangledIdent(oname, p)
var params = newNodeP(nkFormalParams, p)
addReturnType(params, rettyp)
if p.tok.xkind == pxParLe:
parseFormalParams(p, params, pragmas)
if p.tok.xkind == pxSymbol and p.tok.s == "const":
addSon(pragmas, newIdentNodeP("noSideEffect", p))
if pfCDecl in p.options.flags:
addSon(pragmas, newIdentNodeP("cdecl", p))
elif pfStdcall in p.options.flags:
addSon(pragmas, newIdentNodeP("stdcall", p))
if p.tok.xkind == pxColon:
# skip initializer list:
while true:
getTok(p)
discard expression(p)
if p.tok.xkind != pxComma: break
# no pattern, no exceptions:
addSon(result, exportSym(p, name, origName), ast.emptyNode, ast.emptyNode)
addSon(result, params, pragmas, ast.emptyNode) # no exceptions
addSon(result, ast.emptyNode) # no body
case p.tok.xkind
of pxSemicolon: getTok(p)
of pxCurlyLe:
let body = compoundStatement(p)
if pfKeepBodies in p.options.flags:
result.sons[bodyPos] = body
else:
parMessage(p, errTokenExpected, ";")
if result.sons[bodyPos].kind == nkEmpty:
DoImport((if isDestructor: "~" else: "") & origName, pragmas, p)
elif isDestructor:
addSon(pragmas, newIdentNodeP("destructor", p))
if sonsLen(result.sons[pragmasPos]) == 0:
result.sons[pragmasPos] = ast.emptyNode
proc parseMethod(p: var TParser, origName: string, rettyp, pragmas: PNode,
isStatic: bool): PNode =
result = newNodeP(nkProcDef, p)
var params = newNodeP(nkFormalParams, p)
addReturnType(params, rettyp)
var thisDef = newNodeP(nkIdentDefs, p)
if not isStatic:
# declare 'this':
var t = newNodeP(nkVarTy, p)
t.add(p.currentClass)
addSon(thisDef, newIdentNodeP("this", p), t, ast.emptyNode)
params.add(thisDef)
parseFormalParams(p, params, pragmas)
if p.tok.xkind == pxSymbol and p.tok.s == "const":
addSon(pragmas, newIdentNodeP("noSideEffect", p))
getTok(p, result)
if not isStatic:
# fix the type of the 'this' parameter:
thisDef.sons[1] = thisDef.sons[1].sons[0]
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:
let methodName = newIdentNodeP(origName, p)
addSon(result, exportSym(p, methodName, origName),
ast.emptyNode, ast.emptyNode)
addSon(result, params, pragmas, ast.emptyNode) # no exceptions
addSon(result, ast.emptyNode) # no body
case p.tok.xkind
of pxSemicolon: getTok(p)
of pxCurlyLe:
let body = compoundStatement(p)
if pfKeepBodies in p.options.flags:
result.sons[bodyPos] = body
else:
parMessage(p, errTokenExpected, ";")
if result.sons[bodyPos].kind == nkEmpty:
if isStatic: DoImport(origName, pragmas, p)
else: DoImportCpp(origName, pragmas, p)
if sonsLen(result.sons[pragmasPos]) == 0:
result.sons[pragmasPos] = ast.emptyNode
proc parseStandaloneClass(p: var TParser, isStruct: bool): PNode
proc followedByParLe(p: var TParser): bool =
saveContext(p)
getTok(p) # skip Identifier
result = p.tok.xkind == pxParLe
backtrackContext(p)
proc parseOperator(p: var TParser, origName: var string): bool =
getTok(p) # skip 'operator' keyword
case p.tok.xkind
of pxAmp..pxArrow:
# ordinary operator symbol:
origName.add(TokKindToStr(p.tok.xkind))
getTok(p)
of pxSymbol:
if p.tok.s == "new" or p.tok.s == "delete":
origName.add(p.tok.s)
getTok(p)
if p.tok.xkind == pxBracketLe:
getTok(p)
eat(p, pxBracketRi)
origName.add("[]")
else:
# type converter
let x = typeAtom(p)
if x.kind == nkIdent:
origName.add(x.ident.s)
else:
parMessage(p, errGenerated, "operator symbol expected")
result = true
of pxParLe:
getTok(p)
eat(p, pxParRi)
origName.add("()")
of pxBracketLe:
getTok(p)
eat(p, pxBracketRi)
origName.add("[]")
else:
parMessage(p, errGenerated, "operator symbol expected")
proc parseClass(p: var TParser; isStruct: bool; stmtList: PNode): PNode =
result = newNodeP(nkObjectTy, p)
addSon(result, ast.emptyNode, ast.emptyNode) # no pragmas, no inheritance
var recList = newNodeP(nkRecList, p)
addSon(result, recList)
if p.tok.xkind == pxColon:
getTok(p, result)
skipInheritKeyw(p)
var baseTyp = typeAtom(p)
var inh = newNodeP(nkOfInherit, p)
inh.add(baseTyp)
if p.tok.xkind == pxComma:
parMessage(p, errGenerated, "multiple inheritance is not supported")
while p.tok.xkind == pxComma:
getTok(p)
skipInheritKeyw(p)
discard typeAtom(p)
result.sons[0] = inh
eat(p, pxCurlyLe, result)
var private = not isStruct
var pragmas = newNodeP(nkPragma, p)
while p.tok.xkind notin {pxEof, pxCurlyRi}:
skipCom(p, stmtList)
if p.tok.xkind == pxSymbol and (p.tok.s == "private" or
p.tok.s == "protected"):
getTok(p, result)
eat(p, pxColon, result)
private = true
elif p.tok.xkind == pxSymbol and p.tok.s == "public":
getTok(p, result)
eat(p, pxColon, result)
private = false
if p.tok.xkind == pxSymbol and p.tok.s == "friend":
# we skip friend declarations:
while p.tok.xkind notin {pxEof, pxSemicolon}: getTok(p)
eat(p, pxSemicolon)
elif p.tok.xkind == pxSymbol and p.tok.s == "enum":
let x = enumSpecifier(p)
if not private or pfKeepBodies in p.options.flags: stmtList.add(x)
elif p.tok.xkind == pxSymbol and p.tok.s == "typedef":
let x = parseTypeDef(p)
if not private or pfKeepBodies in p.options.flags: stmtList.add(x)
elif p.tok.xkind == pxSymbol and(p.tok.s == "struct" or p.tok.s == "class"):
let x = parseStandaloneClass(p, isStruct=p.tok.s == "struct")
if not private or pfKeepBodies in p.options.flags: stmtList.add(x)
elif p.tok.xkind == pxSymbol and p.tok.s == "union":
let x = parseStandaloneStruct(p, isUnion=true)
if not private or pfKeepBodies in p.options.flags: stmtList.add(x)
else:
if pragmas.len != 0: pragmas = newNodeP(nkPragma, p)
parseCallConv(p, pragmas)
var isStatic = false
if p.tok.xkind == pxSymbol and p.tok.s == "virtual":
getTok(p, stmtList)
if p.tok.xkind == pxSymbol and p.tok.s == "explicit":
getTok(p, stmtList)
if p.tok.xkind == pxSymbol and p.tok.s == "static":
getTok(p, stmtList)
isStatic = true
parseCallConv(p, pragmas)
if p.tok.xkind == pxSymbol and p.tok.s == p.currentClass.ident.s and
followedByParLe(p):
# constructor
let cons = parseConstructor(p, pragmas)
if not private or pfKeepBodies in p.options.flags: stmtList.add(cons)
elif p.tok.xkind == pxTilde:
# destructor
getTok(p, stmtList)
if p.tok.xkind == pxSymbol and p.tok.s == p.currentClass.ident.s:
let des = parseConstructor(p, pragmas, isDestructor=true)
if not private or pfKeepBodies in p.options.flags: stmtList.add(des)
else:
parMessage(p, errGenerated, "invalid destructor")
else:
# field declaration or method:
var baseTyp = typeAtom(p)
while true:
var def = newNodeP(nkIdentDefs, p)
var t = pointer(p, baseTyp)
let canBeMethod = p.tok.xkind != pxParLe
var origName: string
if p.tok.xkind == pxSymbol:
origName = p.tok.s
if p.tok.s == "operator":
var isConverter = parseOperator(p, origName)
let meth = parseMethod(p, origName, t, pragmas, isStatic)
if not private or pfKeepBodies in p.options.flags:
if isConverter: meth.kind = nkConverterDef
stmtList.add(meth)
break
var i = parseField(p, nkRecList)
if canBeMethod and p.tok.xkind == pxParLe:
let meth = parseMethod(p, origName, t, pragmas, isStatic)
if not private or pfKeepBodies in p.options.flags:
stmtList.add(meth)
else:
t = parseTypeSuffix(p, t)
addSon(def, i, t, ast.emptyNode)
if not isStatic: addSon(recList, def)
if p.tok.xkind != pxComma: break
getTok(p, def)
if p.tok.xkind == pxSemicolon:
getTok(p, lastSon(recList))
eat(p, pxCurlyRi, result)
proc parseStandaloneClass(p: var TParser, isStruct: bool): PNode =
result = newNodeP(nkStmtList, p)
saveContext(p)
getTok(p, result) # skip "class" or "struct"
var origName = ""
let oldClass = p.currentClass
if p.tok.xkind == pxSymbol:
markTypeIdent(p, nil)
origName = p.tok.s
getTok(p, result)
p.currentClass = mangledIdent(origName, p)
else:
p.currentClass = nil
if p.tok.xkind in {pxCurlyLe, pxSemiColon, pxColon}:
if origName.len > 0:
var typeSection = newNodeP(nkTypeSection, p)
addSon(result, typeSection)
var name = mangledIdent(origName, p)
var t = parseClass(p, isStruct, result)
addTypeDef(typeSection, structPragmas(p, name, origName), t)
parseTrailingDefinedIdents(p, result, name)
else:
var t = parseTuple(p, isUnion=false)
parseTrailingDefinedIdents(p, result, t)
else:
backtrackContext(p)
result = declaration(p)
p.currentClass = oldClass
include cpp
proc statement(p: var TParser): PNode =
@@ -1681,14 +2075,29 @@ proc statement(p: var TParser): PNode =
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)
of "union": result = parseStandaloneStruct(p, isUnion=true)
of "struct":
if pfCpp in p.options.flags:
result = parseStandaloneClass(p, isStruct=true)
else:
result = parseStandaloneStruct(p, isUnion=false)
of "class":
if pfCpp in p.options.flags:
result = parseStandaloneClass(p, isStruct=false)
else:
result = declarationOrStatement(p)
of "namespace":
if pfCpp in p.options.flags:
while p.tok.xkind notin {pxEof, pxCurlyLe}: getTok(p)
result = compoundStatement(p)
else:
result = declarationOrStatement(p)
else: result = declarationOrStatement(p)
of pxCurlyLe:
result = compoundStatement(p)
of pxDirective, pxDirectiveParLe:
result = parseDir(p)
of pxLineComment, pxStarComment:
of pxLineComment, pxStarComment:
result = newNodeP(nkCommentStmt, p)
skipCom(p, result)
of pxSemicolon:
@@ -1703,10 +2112,10 @@ proc statement(p: var TParser): PNode =
result = expressionStatement(p)
assert result != nil
proc parseUnit(p: var TParser): PNode =
proc parseUnit(p: var TParser): PNode =
result = newNodeP(nkStmtList, p)
getTok(p) # read first token
while p.tok.xkind != pxEof:
while p.tok.xkind != pxEof:
var s = statement(p)
if s.kind != nkEmpty: embedStmts(result, s)

View File

@@ -26,7 +26,7 @@ proc skipLine(p: var TParser) =
proc parseDefineBody(p: var TParser, tmplDef: PNode): string =
if p.tok.xkind == pxCurlyLe or
(p.tok.xkind == pxSymbol and (
declKeyword(p.tok.s) or stmtKeyword(p.tok.s))):
declKeyword(p, p.tok.s) or stmtKeyword(p.tok.s))):
addSon(tmplDef, statement(p))
result = "stmt"
elif p.tok.xkind in {pxLineComment, pxNewLine}:

View File

@@ -0,0 +1,241 @@
/////////////////////////////////////////////////////////////////////////////
// Name: wx/matrix.h
// Purpose: wxTransformMatrix class. NOT YET USED
// Author: Chris Breeze, Julian Smart
// Modified by: Klaas Holwerda
// Created: 01/02/97
// RCS-ID: $Id$
// Copyright: (c) Julian Smart, Chris Breeze
// Licence: wxWindows licence
/////////////////////////////////////////////////////////////////////////////
#ifndef _WX_MATRIXH__
#define _WX_MATRIXH__
//! headerfiles="matrix.h wx/object.h"
#include "wx/object.h"
#include "wx/math.h"
//! codefiles="matrix.cpp"
// A simple 3x3 matrix. This may be replaced by a more general matrix
// class some day.
//
// Note: this is intended to be used in wxDC at some point to replace
// the current system of scaling/translation. It is not yet used.
#def WXDLLIMPEXP_CORE
#header "wxmatrix.h"
//:definition
// A 3x3 matrix to do 2D transformations.
// It can be used to map data to window coordinates,
// and also for manipulating your own data.
// For example drawing a picture (composed of several primitives)
// at a certain coordinate and angle within another parent picture.
// At all times m_isIdentity is set if the matrix itself is an Identity matrix.
// It is used where possible to optimize calculations.
class WXDLLIMPEXP_CORE wxTransformMatrix: public wxObject<string, string<ubyte>>
{
public:
wxTransformMatrix(void);
wxTransformMatrix(const wxTransformMatrix& mat);
~wxTransformMatrix(void);
//get the value in the matrix at col,row
//rows are horizontal (second index of m_matrix member)
//columns are vertical (first index of m_matrix member)
double GetValue(int col, int row) const;
//set the value in the matrix at col,row
//rows are horizontal (second index of m_matrix member)
//columns are vertical (first index of m_matrix member)
void SetValue(int col, int row, double value);
void operator = (const wxTransformMatrix& mat);
bool operator == (const wxTransformMatrix& mat) const;
bool operator != (const wxTransformMatrix& mat) const;
//multiply every element by t
wxTransformMatrix& operator*=(const double& t);
//divide every element by t
wxTransformMatrix& operator/=(const double& t);
//add matrix m to this t
wxTransformMatrix& operator+=(const wxTransformMatrix& m);
//subtract matrix m from this
wxTransformMatrix& operator-=(const wxTransformMatrix& m);
//multiply matrix m with this
wxTransformMatrix& operator*=(const wxTransformMatrix& m);
// constant operators
//multiply every element by t and return result
wxTransformMatrix operator*(const double& t) const;
//divide this matrix by t and return result
wxTransformMatrix operator/(const double& t) const;
//add matrix m to this and return result
wxTransformMatrix operator+(const wxTransformMatrix& m) const;
//subtract matrix m from this and return result
wxTransformMatrix operator-(const wxTransformMatrix& m) const;
//multiply this by matrix m and return result
wxTransformMatrix operator*(const wxTransformMatrix& m) const;
wxTransformMatrix operator-() const;
//rows are horizontal (second index of m_matrix member)
//columns are vertical (first index of m_matrix member)
double& operator()(int col, int row);
//rows are horizontal (second index of m_matrix member)
//columns are vertical (first index of m_matrix member)
double operator()(int col, int row) const;
// Invert matrix
bool Invert(void);
// Make into identity matrix
bool Identity(void);
// Is the matrix the identity matrix?
// Only returns a flag, which is set whenever an operation
// is done.
inline bool IsIdentity(void) const { return m_isIdentity; }
// This does an actual check.
inline bool IsIdentity1(void) const ;
//Scale by scale (isotropic scaling i.e. the same in x and y):
//!ex:
//!code: | scale 0 0 |
//!code: matrix' = | 0 scale 0 | x matrix
//!code: | 0 0 scale |
bool Scale(double scale);
//Scale with center point and x/y scale
//
//!ex:
//!code: | xs 0 xc(1-xs) |
//!code: matrix' = | 0 ys yc(1-ys) | x matrix
//!code: | 0 0 1 |
wxTransformMatrix& Scale(const double &xs, const double &ys,const double &xc, const double &yc);
// mirror a matrix in x, y
//!ex:
//!code: | -1 0 0 |
//!code: matrix' = | 0 -1 0 | x matrix
//!code: | 0 0 1 |
wxTransformMatrix<float>& Mirror(bool x=true, bool y=false);
// Translate by dx, dy:
//!ex:
//!code: | 1 0 dx |
//!code: matrix' = | 0 1 dy | x matrix
//!code: | 0 0 1 |
bool Translate(double x, double y);
// Rotate clockwise by the given number of degrees:
//!ex:
//!code: | cos sin 0 |
//!code: matrix' = | -sin cos 0 | x matrix
//!code: | 0 0 1 |
bool Rotate(double angle);
//Rotate counter clockwise with point of rotation
//
//!ex:
//!code: | cos(r) -sin(r) x(1-cos(r))+y(sin(r)|
//!code: matrix' = | sin(r) cos(r) y(1-cos(r))-x(sin(r)| x matrix
//!code: | 0 0 1 |
wxTransformMatrix& Rotate(const double &r, const double &x, const double &y);
// Transform X value from logical to device
inline double TransformX(double x) const;
// Transform Y value from logical to device
inline double TransformY(double y) const;
// Transform a point from logical to device coordinates
bool TransformPoint(double x, double y, double& tx, double& ty) const;
// Transform a point from device to logical coordinates.
// Example of use:
// wxTransformMatrix mat = dc.GetTransformation();
// mat.Invert();
// mat.InverseTransformPoint(x, y, x1, y1);
// OR (shorthand:)
// dc.LogicalToDevice(x, y, x1, y1);
// The latter is slightly less efficient if we're doing several
// conversions, since the matrix is inverted several times.
// N.B. 'this' matrix is the inverse at this point
bool InverseTransformPoint(double x, double y, double& tx, double& ty) const;
double Get_scaleX();
double Get_scaleY();
double GetRotation();
void SetRotation(double rotation);
public:
double m_matrix[3][3];
bool m_isIdentity;
};
/*
Chris Breeze reported, that
some functions of wxTransformMatrix cannot work because it is not
known if he matrix has been inverted. Be careful when using it.
// Transform X value from logical to device
// warning: this function can only be used for this purpose
// because no rotation is involved when mapping logical to device coordinates
// mirror and scaling for x and y will be part of the matrix
// if you have a matrix that is rotated, eg a shape containing a matrix to place
// it in the logical coordinate system, use TransformPoint
inline double wxTransformMatrix::TransformX(double x) const
{
//normally like this, but since no rotation is involved (only mirror and scale)
//we can do without Y -> m_matrix[1]{0] is -sin(rotation angle) and therefore zero
//(x * m_matrix[0][0] + y * m_matrix[1][0] + m_matrix[2][0]))
return (m_isIdentity ? x : (x * m_matrix[0][0] + m_matrix[2][0]));
}
// Transform Y value from logical to device
// warning: this function can only be used for this purpose
// because no rotation is involved when mapping logical to device coordinates
// mirror and scaling for x and y will be part of the matrix
// if you have a matrix that is rotated, eg a shape containing a matrix to place
// it in the logical coordinate system, use TransformPoint
inline double wxTransformMatrix::TransformY(double y) const
{
//normally like this, but since no rotation is involved (only mirror and scale)
//we can do without X -> m_matrix[0]{1] is sin(rotation angle) and therefore zero
//(x * m_matrix[0][1] + y * m_matrix[1][1] + m_matrix[2][1]))
return (m_isIdentity ? y : (y * m_matrix[1][1] + m_matrix[2][1]));
}
// Is the matrix the identity matrix?
// Each operation checks whether the result is still the identity matrix and sets a flag.
inline bool wxTransformMatrix::IsIdentity1(void) const
{
return
( wxIsSameDouble(m_matrix[0][0], 1.0) &&
wxIsSameDouble(m_matrix[1][1], 1.0) &&
wxIsSameDouble(m_matrix[2][2], 1.0) &&
wxIsSameDouble(m_matrix[1][0], 0.0) &&
wxIsSameDouble(m_matrix[2][0], 0.0) &&
wxIsSameDouble(m_matrix[0][1], 0.0) &&
wxIsSameDouble(m_matrix[2][1], 0.0) &&
wxIsSameDouble(m_matrix[0][2], 0.0) &&
wxIsSameDouble(m_matrix[1][2], 0.0) );
}
// Calculates the determinant of a 2 x 2 matrix
inline double wxCalculateDet(double a11, double a21, double a12, double a22)
{
return a11 * a22 - a12 * a21;
}
*/
#endif // _WX_MATRIXH__

View File

@@ -74,6 +74,7 @@ coordinate coord
rectangle rect
point point
symbol sym
literal lit
string str
identifier ident
indentation indent

View File

@@ -4471,7 +4471,7 @@ proc with no side effects:
destructor pragma
-----------------
The `destructor` pragma is used to mark a proc to act as a type destructor.
The `destructor`:idx: pragma is used to mark a proc to act as a type destructor.
The proc must have a single parameter with a concrete type (the name of a
generic type is allowed too).

View File

@@ -15,6 +15,7 @@ Library Additions
-----------------
- Added ``macros.genSym`` builtin for AST generation.
- Added ``macros.newLit`` procs for easier AST generation.
Changes affecting backwards compatibility
@@ -56,6 +57,13 @@ Language Additions
- Support for user-defined type classes have been added.
Tools improvements
------------------
- c2nim can deal with a subset of C++. Use the ``--cpp`` command line option
to activate.
2013-05-20 New website design!
==============================