151 lines
4.2 KiB
Nim
151 lines
4.2 KiB
Nim
#
|
|
#
|
|
# The Nimrod Compiler
|
|
# (c) Copyright 2014 Andreas Rumpf
|
|
#
|
|
# See the file "copying.txt", included in this
|
|
# distribution, for details about the copyright.
|
|
#
|
|
|
|
# This file implements the new "batch compiler" that can translate Nimrod
|
|
# code into POSIX shell scripts or Windows ``.bat`` files. Only a small
|
|
# subset of Nimrod can be translated. This compiler also acts as a new
|
|
# "hacking the Nimrod compiler" tutorial.
|
|
|
|
|
|
## ==============================
|
|
## Let's hack the Nimrod compiler
|
|
## ==============================
|
|
|
|
## Starting with version 0.9.6 the compiler's internals have been cleaned up
|
|
## quite a bit. However, still lot's of weird modules need to be imported
|
|
## before we can start:
|
|
|
|
import
|
|
llstream, strutils, ast, astalgo, lexer, syntaxes, renderer, options, msgs,
|
|
os, condsyms, rodread, rodwrite, times,
|
|
wordrecg, sem, semdata, idents, passes, docgen, extccomp,
|
|
cgen, jsgen, json, nversion,
|
|
platform, nimconf, importer, passaux, depends, vm, vmdef, types, idgen,
|
|
tables, docgen2, service, parser, modules
|
|
|
|
proc `=~`(s: PSym; name: string): bool =
|
|
let a = name.split('.')
|
|
var s = s
|
|
for i in countdown(a.high, a.low):
|
|
if s == nil or not identEq(s.name, a[i]): return false
|
|
s = s.owner
|
|
return true
|
|
|
|
type
|
|
ShellTarget = enum
|
|
targetBatch, targetSh
|
|
Context = ref object
|
|
target: ShellTarget
|
|
code: string
|
|
|
|
proc `=~`(n: PNode; name: string): bool =
|
|
result = n.kind == nkSym and match(n.sym, name)
|
|
|
|
proc gen(c: Context; n: PNode) =
|
|
template `~`(x): expr = op =~ x
|
|
template `|`(a, b): expr = (if c.target == targetBatch: a else: b)
|
|
template add(x) = c.code.add(x)
|
|
template addf(x, y) = c.code.addf(x, y)
|
|
|
|
case n.kind
|
|
of nkIfStmt, nkIfExpr:
|
|
|
|
of nkForStmt:
|
|
|
|
of nkCallKinds:
|
|
let op = n[0]
|
|
if ~"stdlib.os.changeDir":
|
|
addf("" | "",
|
|
elif ~"stdlib.os.createDir":
|
|
addf("mkdir $#",
|
|
|
|
proc semanticPasses() =
|
|
registerPass verbosePass
|
|
registerPass semPass
|
|
|
|
proc commandCompileToSH =
|
|
#incl(gGlobalOptions, optSafeCode)
|
|
setTarget(osJS, cpuJS)
|
|
#initDefines()
|
|
defineSymbol("nimrod") # 'nimrod' is always defined
|
|
defineSymbol("batch")
|
|
semanticPasses()
|
|
registerPass(JSgenPass)
|
|
compileProject()
|
|
|
|
proc wantMainModule =
|
|
if gProjectFull.len == 0:
|
|
if optMainModule.len == 0:
|
|
fatal(gCmdLineInfo, errCommandExpectsFilename)
|
|
else:
|
|
gProjectName = optMainModule
|
|
gProjectFull = gProjectPath / gProjectName
|
|
|
|
gProjectMainIdx = addFileExt(gProjectFull, NimExt).fileInfoIdx
|
|
|
|
proc requireMainModuleOption =
|
|
if optMainModule.len == 0:
|
|
fatal(gCmdLineInfo, errMainModuleMustBeSpecified)
|
|
else:
|
|
gProjectName = optMainModule
|
|
gProjectFull = gProjectPath / gProjectName
|
|
|
|
gProjectMainIdx = addFileExt(gProjectFull, NimExt).fileInfoIdx
|
|
|
|
proc mainCommand* =
|
|
# In "nimrod serve" scenario, each command must reset the registered passes
|
|
clearPasses()
|
|
gLastCmdTime = epochTime()
|
|
appendStr(searchPaths, options.libpath)
|
|
if gProjectFull.len != 0:
|
|
# current path is always looked first for modules
|
|
prependStr(searchPaths, gProjectPath)
|
|
setId(100)
|
|
passes.gIncludeFile = includeModule
|
|
passes.gImportModule = importModule
|
|
case command.normalize
|
|
of "c", "cc", "compile", "compiletoc":
|
|
# compile means compileToC currently
|
|
gCmd = cmdCompileToC
|
|
wantMainModule()
|
|
commandCompileToC()
|
|
of "cpp", "compiletocpp":
|
|
extccomp.cExt = ".cpp"
|
|
gCmd = cmdCompileToCpp
|
|
if cCompiler == ccGcc: setCC("gcc")
|
|
wantMainModule()
|
|
defineSymbol("cpp")
|
|
commandCompileToC()
|
|
of "objc", "compiletooc":
|
|
extccomp.cExt = ".m"
|
|
gCmd = cmdCompileToOC
|
|
wantMainModule()
|
|
defineSymbol("objc")
|
|
commandCompileToC()
|
|
of "run":
|
|
gCmd = cmdRun
|
|
wantMainModule()
|
|
when hasTinyCBackend:
|
|
extccomp.setCC("tcc")
|
|
commandCompileToC()
|
|
else:
|
|
rawMessage(errInvalidCommandX, command)
|
|
of "js", "compiletojs":
|
|
gCmd = cmdCompileToJS
|
|
wantMainModule()
|
|
commandCompileToJS()
|
|
else:
|
|
rawMessage(errInvalidCommandX, command)
|
|
|
|
if (msgs.gErrorCounter == 0 and
|
|
gCmd notin {cmdInterpret, cmdRun, cmdDump} and
|
|
gVerbosity > 0):
|
|
rawMessage(hintSuccessX, [$gLinesCompiled,
|
|
formatFloat(epochTime() - gLastCmdTime, ffDecimal, 3),
|
|
formatSize(getTotalMem())])
|