forked from nim-lang/c2nim
-
Notifications
You must be signed in to change notification settings - Fork 0
/
c2nim.nim
137 lines (130 loc) · 5.15 KB
/
c2nim.nim
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
#
#
# c2nim - C to Nim source converter
# (c) Copyright 2015 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
import
strutils, os, times, parseopt, compiler/llstream, compiler/ast,
compiler/renderer, compiler/options, compiler/msgs,
clex, cparse, postprocessor
const
Version = "0.9.13" # keep in sync with Nimble version. D'oh!
Usage = """
c2nim - C to Nim source converter
(c) 2016 Andreas Rumpf
Usage: c2nim [options] [optionfile(s)] inputfile(s) [options]
Optionfiles are C files with the 'c2nim' extension. These are parsed like
other C files but produce no output file.
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!)
--header import from the given header file
--cdecl annotate procs with ``{.cdecl.}``
--stdcall annotate procs with ``{.stdcall.}``
--ref convert typ* to ref typ (default: ptr typ)
--prefix:PREFIX strip prefix for the generated Nim identifiers
(multiple --prefix options are supported)
--suffix:SUFFIX strip suffix for the generated Nim identifiers
(multiple --suffix options are supported)
--assumedef:IDENT skips #ifndef sections for the given C identifier
(multiple --assumedef options are supported)
--assumendef:IDENT skips #ifdef sections for the given C identifier
(multiple --assumendef options are supported)
--skipinclude do not convert ``#include`` to ``import``
--typeprefixes generate ``T`` and ``P`` type prefixes
--nep1 follow 'NEP 1': Style Guide for Nim Code
--skipcomments do not copy comments
--ignoreRValueRefs translate C++'s ``T&&`` to ``T`` instead ``of var T``
--keepBodies keep C++'s method bodies
--concat concat the list of files into a single .nim file
--debug prints a c2nim stack trace in case of an error
--exportdll:PREFIX produce a DLL wrapping the C++ code
-v, --version write c2nim's version
-h, --help show this help
"""
proc isCppFile(s: string): bool =
splitFile(s).ext.toLowerAscii in [".cpp", ".cxx", ".hpp"]
proc parse(infile: string, options: PParserOptions; dllExport: var PNode): PNode =
var stream = llStreamOpen(infile, fmRead)
if stream == nil: rawMessage(errCannotOpenFile, infile)
let isCpp = pfCpp notin options.flags and isCppFile(infile)
var p: Parser
if isCpp: options.flags.incl pfCpp
openParser(p, infile, stream, options)
result = parseUnit(p).postprocess
closeParser(p)
if isCpp: options.flags.excl pfCpp
if options.exportPrefix.len > 0:
let dllprocs = exportAsDll(result, options.exportPrefix)
assert dllprocs.kind == nkStmtList
if dllExport.isNil:
dllExport = dllprocs
else:
for x in dllprocs: dllExport.add x
proc isC2nimFile(s: string): bool = splitFile(s).ext.toLowerAscii == ".c2nim"
proc main(infiles: seq[string], outfile: var string,
options: PParserOptions, concat: bool) =
var start = getTime()
var dllexport: PNode = nil
if concat:
var tree = newNode(nkStmtList)
for infile in infiles:
let m = parse(infile.addFileExt("h"), options, dllexport)
if not isC2nimFile(infile):
if outfile.len == 0: outfile = changeFileExt(infile, "nim")
for n in m: tree.add(n)
renderModule(tree, outfile, outfile)
else:
for infile in infiles:
let m = parse(infile, options, dllexport)
if not isC2nimFile(infile):
if outfile.len > 0:
renderModule(m, outfile, outfile)
outfile = ""
else:
let outfile = changeFileExt(infile, "nim")
renderModule(m, outfile, outfile)
if dllexport != nil:
let (path, name, _) = infiles[0].splitFile
let outfile = path / name & "_dllimpl" & ".nim"
renderModule(dllexport, outfile, outfile)
rawMessage(hintSuccessX, [$gLinesCompiled, $(getTime() - start),
formatSize(getTotalMem()), ""])
var
infiles = newSeq[string](0)
outfile = ""
concat = false
parserOptions = newParserOptions()
for kind, key, val in getopt():
case kind
of cmdArgument:
infiles.add key
of cmdLongOption, cmdShortOption:
case key.normalize
of "help", "h":
stdout.write(Usage)
quit(0)
of "version", "v":
stdout.write(Version & "\n")
quit(0)
of "o", "out": outfile = val
of "concat": concat = true
of "spliceheader":
quit "[Error] 'spliceheader' doesn't exist anymore" &
" use a list of files and --concat instead"
of "exportdll":
parserOptions.exportPrefix = val
else:
if not parserOptions.setOption(key, val):
stdout.writeLine("[Error] unknown option: " & key)
of cmdEnd: assert(false)
if infiles.len == 0:
# no filename has been given, so we show the help:
stdout.write(Usage)
else:
main(infiles, outfile, parserOptions, concat)