summary refs log tree commit diff stats
path: root/compiler/tccgen.nim
blob: 0082dfcbb35b077cc9fdcbdae0ee37fc4136f09f (plain) (blame)
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
#
#
#           The Nim Compiler
#        (c) Copyright 2012 Andreas Rumpf
#
#    See the file "copying.txt", included in this
#    distribution, for details about the copyright.
#

import
  os, strutils, options, msgs, tinyc

{.compile: "../tinyc/libtcc.c".}

proc tinyCErrorHandler(closure: pointer, msg: cstring) {.cdecl.} = 
  rawMessage(errGenerated, $msg)
  
proc initTinyCState: PccState = 
  result = openCCState()
  setErrorFunc(result, nil, tinyCErrorHandler)

var
  gTinyC = initTinyCState()
  libIncluded = false

proc addFile(filename: string) =  
  if addFile(gTinyC, filename) != 0'i32:
    rawMessage(errCannotOpenFile, filename)

proc setupEnvironment = 
  when defined(amd64):
    defineSymbol(gTinyC, "__x86_64__", nil)
  elif defined(i386):
    defineSymbol(gTinyC, "__i386__", nil)  
  when defined(linux):
    defineSymbol(gTinyC, "__linux__", nil)
    defineSymbol(gTinyC, "__linux", nil)
  var nimrodDir = getPrefixDir()

  addIncludePath(gTinyC, libpath)
  when defined(windows): 
    addSysincludePath(gTinyC, nimrodDir / "tinyc/win32/include")
  addSysincludePath(gTinyC, nimrodDir / "tinyc/include")
  when defined(windows): 
    defineSymbol(gTinyC, "_WIN32", nil)
    # we need Mingw's headers too:
    var gccbin = getConfigVar("gcc.path") % ["nimrod", nimrodDir]
    addSysincludePath(gTinyC, gccbin /../ "include")
    #addFile(nimrodDir / r"tinyc\win32\wincrt1.o")
    addFile(nimrodDir / r"tinyc\win32\alloca86.o")
    addFile(nimrodDir / r"tinyc\win32\chkstk.o")
    #addFile(nimrodDir / r"tinyc\win32\crt1.o")

    #addFile(nimrodDir / r"tinyc\win32\dllcrt1.o")
    #addFile(nimrodDir / r"tinyc\win32\dllmain.o")
    addFile(nimrodDir / r"tinyc\win32\libtcc1.o")
    
    #addFile(nimrodDir / r"tinyc\win32\lib\crt1.c")
    #addFile(nimrodDir / r"tinyc\lib\libtcc1.c")
  else:
    addSysincludePath(gTinyC, "/usr/include")
    when defined(amd64):
      addSysincludePath(gTinyC, "/usr/include/x86_64-linux-gnu")

proc compileCCode*(ccode: string) = 
  if not libIncluded:
    libIncluded = true
    setupEnvironment()
  discard compileString(gTinyC, ccode)
  
proc run*(args: string) =
  var s = @[cstring(gProjectName)] & map(split(args), proc(x: string): cstring = cstring(x))
  var err = tinyc.run(gTinyC, cint(len(s)), cast[cstringArray](addr(s[0]))) != 0'i32
  closeCCState(gTinyC)
  if err: rawMessage(errExecutionOfProgramFailed, "")
">wAnd): ppGetTok(L, tok) # skip "and" var b = parseAtom(L, tok) result = result and b proc parseExpr(L: var TLexer, tok: var TToken): bool = result = parseAndExpr(L, tok) while tok.ident.id == ord(wOr): ppGetTok(L, tok) # skip "or" var b = parseAndExpr(L, tok) result = result or b proc EvalppIf(L: var TLexer, tok: var TToken): bool = ppGetTok(L, tok) # skip 'if' or 'elif' result = parseExpr(L, tok) if tok.tokType == tkColon: ppGetTok(L, tok) else: lexMessage(L, errTokenExpected, "\':\'") var condStack: seq[bool] = @[] proc doEnd(L: var TLexer, tok: var TToken) = if high(condStack) < 0: lexMessage(L, errTokenExpected, "@if") ppGetTok(L, tok) # skip 'end' setlen(condStack, high(condStack)) type TJumpDest = enum jdEndif, jdElseEndif proc jumpToDirective(L: var TLexer, tok: var TToken, dest: TJumpDest) proc doElse(L: var TLexer, tok: var TToken) = if high(condStack) < 0: lexMessage(L, errTokenExpected, "@if") ppGetTok(L, tok) if tok.tokType == tkColon: ppGetTok(L, tok) if condStack[high(condStack)]: jumpToDirective(L, tok, jdEndif) proc doElif(L: var TLexer, tok: var TToken) = if high(condStack) < 0: lexMessage(L, errTokenExpected, "@if") var res = EvalppIf(L, tok) if condStack[high(condStack)] or not res: jumpToDirective(L, tok, jdElseEndif) else: condStack[high(condStack)] = true proc jumpToDirective(L: var TLexer, tok: var TToken, dest: TJumpDest) = var nestedIfs = 0 while True: if (tok.ident != nil) and (tok.ident.s == "@"): ppGetTok(L, tok) case whichKeyword(tok.ident) of wIf: Inc(nestedIfs) of wElse: if (dest == jdElseEndif) and (nestedIfs == 0): doElse(L, tok) break of wElif: if (dest == jdElseEndif) and (nestedIfs == 0): doElif(L, tok) break of wEnd: if nestedIfs == 0: doEnd(L, tok) break if nestedIfs > 0: Dec(nestedIfs) else: nil ppGetTok(L, tok) elif tok.tokType == tkEof: lexMessage(L, errTokenExpected, "@end") else: ppGetTok(L, tok) proc parseDirective(L: var TLexer, tok: var TToken) = ppGetTok(L, tok) # skip @ case whichKeyword(tok.ident) of wIf: setlen(condStack, len(condStack) + 1) var res = EvalppIf(L, tok) condStack[high(condStack)] = res if not res: jumpToDirective(L, tok, jdElseEndif) of wElif: doElif(L, tok) of wElse: doElse(L, tok) of wEnd: doEnd(L, tok) of wWrite: ppGetTok(L, tok) msgs.MsgWriteln(tokToStr(tok)) ppGetTok(L, tok) else: case tok.ident.s.normalize of "putenv": ppGetTok(L, tok) var key = tokToStr(tok) ppGetTok(L, tok) os.putEnv(key, tokToStr(tok)) ppGetTok(L, tok) of "prependenv": ppGetTok(L, tok) var key = tokToStr(tok) ppGetTok(L, tok) os.putEnv(key, tokToStr(tok) & os.getenv(key)) ppGetTok(L, tok) of "appendenv": ppGetTok(L, tok) var key = tokToStr(tok) ppGetTok(L, tok) os.putEnv(key, os.getenv(key) & tokToStr(tok)) ppGetTok(L, tok) else: lexMessage(L, errInvalidDirectiveX, tokToStr(tok)) proc confTok(L: var TLexer, tok: var TToken) = ppGetTok(L, tok) while tok.ident != nil and tok.ident.s == "@": parseDirective(L, tok) # else: give the token to the parser proc checkSymbol(L: TLexer, tok: TToken) = if tok.tokType notin {tkSymbol..pred(tkIntLit), tkStrLit..tkTripleStrLit}: lexMessage(L, errIdentifierExpected, tokToStr(tok)) proc parseAssignment(L: var TLexer, tok: var TToken) = if tok.ident.id == getIdent("-").id or tok.ident.id == getIdent("--").id: confTok(L, tok) # skip unnecessary prefix var info = getLineInfo(L) # safe for later in case of an error checkSymbol(L, tok) var s = tokToStr(tok) confTok(L, tok) # skip symbol var val = "" while tok.tokType == tkDot: add(s, '.') confTok(L, tok) checkSymbol(L, tok) add(s, tokToStr(tok)) confTok(L, tok) if tok.tokType == tkBracketLe: # BUGFIX: val, not s! # BUGFIX: do not copy '['! confTok(L, tok) checkSymbol(L, tok) add(val, tokToStr(tok)) confTok(L, tok) if tok.tokType == tkBracketRi: confTok(L, tok) else: lexMessage(L, errTokenExpected, "\']\'") add(val, ']') if tok.tokType in {tkColon, tkEquals}: if len(val) > 0: add(val, ':') confTok(L, tok) # skip ':' or '=' checkSymbol(L, tok) add(val, tokToStr(tok)) confTok(L, tok) # skip symbol while tok.ident != nil and tok.ident.id == getIdent("&").id: confTok(L, tok) checkSymbol(L, tok) add(val, tokToStr(tok)) confTok(L, tok) processSwitch(s, val, passPP, info) proc readConfigFile(filename: string) = var L: TLexer tok: TToken stream: PLLStream stream = LLStreamOpen(filename, fmRead) if stream != nil: initToken(tok) openLexer(L, filename, stream) tok.tokType = tkEof # to avoid a pointless warning confTok(L, tok) # read in the first token while tok.tokType != tkEof: parseAssignment(L, tok) if len(condStack) > 0: lexMessage(L, errTokenExpected, "@end") closeLexer(L) if gVerbosity >= 1: rawMessage(hintConf, filename) proc getUserConfigPath(filename: string): string = result = joinPath(getConfigDir(), filename) proc getSystemConfigPath(filename: string): string = # try standard configuration file (installation did not distribute files # the UNIX way) result = joinPath([getPrefixDir(), "config", filename]) if not ExistsFile(result): result = "/etc/" & filename proc LoadConfigs*(cfg: string) = # set default value (can be overwritten): if libpath == "": # choose default libpath: var prefix = getPrefixDir() if prefix == "/usr": libpath = "/usr/lib/nimrod" elif prefix == "/usr/local": libpath = "/usr/local/lib/nimrod" else: libpath = joinPath(prefix, "lib") if optSkipConfigFile notin gGlobalOptions: readConfigFile(getSystemConfigPath(cfg)) if optSkipUserConfigFile notin gGlobalOptions: readConfigFile(getUserConfigPath(cfg)) var pd = if gProjectPath.len > 0: gProjectPath else: getCurrentDir() if optSkipParentConfigFiles notin gGlobalOptions: for dir in parentDirs(pd, fromRoot=true, inclusive=false): readConfigFile(dir / cfg) if optSkipProjConfigFile notin gGlobalOptions: readConfigFile(pd / cfg) if gProjectName.len != 0: var conffile = changeFileExt(gProjectFull, "cfg") if conffile != pd / cfg and existsFile(conffile): readConfigFile(conffile) rawMessage(warnConfigDeprecated, conffile) # new project wide config file: readConfigFile(changeFileExt(gProjectFull, "nimrod.cfg"))