summary refs log blame commit diff stats
path: root/rod/options.nim
blob: 514c3db3be360a301dbfa42e08c389b28f5a5269 (plain) (tree)
54 ^
1
2
3
4


                               
                                         











                                                                         


                                               


                                                                             

                                                           


















                                                                                 
                                                       










                                                                             
                                                                          





































                                                                                
                                                                               


































                                                           
                                 

                                   
                                 


                                                         
                                 


                                                  

                                                          




















                                                                               


                    
                                        
























                                                             
#
#
#           The Nimrod Compiler
#        (c) Copyright 2010 Andreas Rumpf
#
#    See the file "copying.txt", included in this
#    distribution, for details about the copyright.
#

import 
  os, lists, strutils, nstrtabs

type                          # please make sure we have under 32 options
                              # (improves code efficiency a lot!)
  TOption* = enum             # **keep binary compatible**
    optNone, optObjCheck, optFieldCheck, optRangeCheck, optBoundsCheck, 
    optOverflowCheck, optNilCheck,
    optNaNCheck, optInfCheck,
    optAssert, optLineDir, optWarns, optHints, 
    optOptimizeSpeed, optOptimizeSize, optStackTrace, # stack tracing support
    optLineTrace,             # line tracing support (includes stack tracing)
    optEndb,                  # embedded debugger
    optByRef,                 # use pass by ref for objects
                              # (for interfacing with C)
    optCheckpoints,           # check for checkpoints (used for debugging)
    optProfiler               # profiler turned on
  TOptions* = set[TOption]
  TGlobalOption* = enum 
    gloptNone, optForceFullMake, optBoehmGC, optRefcGC, optDeadCodeElim, 
    optListCmd, optCompileOnly, optNoLinking, optSafeCode, # only allow safe code
    optCDebug,                # turn on debugging information
    optGenDynLib,             # generate a dynamic library
    optGenGuiApp,             # generate a GUI application
    optGenScript,             # generate a script file to compile the *.c files
    optGenMapping,            # generate a mapping file
    optRun,                   # run the compiled project
    optSymbolFiles,           # use symbol files for speeding up compilation
    optSkipConfigFile,        # skip the general config file
    optSkipProjConfigFile,    # skip the project's config file
    optNoMain                 # do not generate a "main" proc
  TGlobalOptions* = set[TGlobalOption]
  TCommands* = enum           # Nimrod's commands
    cmdNone, cmdCompileToC, cmdCompileToCpp, cmdCompileToEcmaScript, 
    cmdCompileToLLVM, cmdInterpret, cmdPretty, cmdDoc, 
    cmdGenDepend, cmdListDef, cmdCheck, # semantic checking for whole project
    cmdParse,                 # parse a single file (for debugging)
    cmdScan,                  # scan a single file (for debugging)
    cmdDebugTrans,            # debug a transformation pass
    cmdRst2html,              # convert a reStructuredText file to HTML
    cmdRst2tex,               # convert a reStructuredText file to TeX
    cmdInteractive            # start interactive session
  TStringSeq* = seq[string]

const 
  ChecksOptions* = {optObjCheck, optFieldCheck, optRangeCheck, optNilCheck, 
    optOverflowCheck, optBoundsCheck, optAssert, optNaNCheck, optInfCheck}

var 
  gOptions*: TOptions = {optObjCheck, optFieldCheck, optRangeCheck, 
                         optBoundsCheck, optOverflowCheck, optAssert, optWarns, 
                         optHints, optStackTrace, optLineTrace}
  gGlobalOptions*: TGlobalOptions = {optRefcGC}
  gExitcode*: int8
  searchPaths*: TLinkedList
  outFile*: string = ""
  gIndexFile*: string = ""
  gCmd*: TCommands = cmdNone  # the command
  gVerbosity*: int            # how verbose the compiler is
  gNumberOfProcessors*: int   # number of processors

proc FindFile*(f: string): string

const 
  genSubDir* = "nimcache"
  NimExt* = "nim"
  RodExt* = "rod"
  HtmlExt* = "html"
  TexExt* = "tex"
  IniExt* = "ini"
  DocConfig* = "nimdoc.cfg"
  DocTexConfig* = "nimdoc.tex.cfg"

proc completeGeneratedFilePath*(f: string, createSubDir: bool = true): string
proc toGeneratedFile*(path, ext: string): string
  # converts "/home/a/mymodule.nim", "rod" to "/home/a/nimcache/mymodule.rod"
proc getPrefixDir*(): string
  # gets the application directory

# additional configuration variables:
var 
  gConfigVars*: PStringTable
  libpath*: string = ""
  projectPath*: string = ""
  gKeepComments*: bool = true # whether the parser needs to keep comments
  gImplicitMods*: TStringSeq = @[] # modules that are to be implicitly imported

proc existsConfigVar*(key: string): bool
proc getConfigVar*(key: string): string
proc setConfigVar*(key, val: string)
proc addImplicitMod*(filename: string)
proc getOutFile*(filename, ext: string): string
proc binaryStrSearch*(x: openarray[string], y: string): int
# implementation

proc existsConfigVar(key: string): bool = 
  result = hasKey(gConfigVars, key)

proc getConfigVar(key: string): string = 
  result = nstrtabs.get(gConfigVars, key)

proc setConfigVar(key, val: string) = 
  nstrtabs.put(gConfigVars, key, val)

proc getOutFile(filename, ext: string): string = 
  if options.outFile != "": result = options.outFile
  else: result = changeFileExt(filename, ext)
  
proc addImplicitMod(filename: string) = 
  var length: int
  length = len(gImplicitMods)
  setlen(gImplicitMods, length + 1)
  gImplicitMods[length] = filename

proc getPrefixDir(): string = 
  result = SplitPath(getApplicationDir()).head

proc shortenDir(dir: string): string = 
  # returns the interesting part of a dir
  var prefix = getPrefixDir() & dirSep
  if startsWith(dir, prefix): 
    return copy(dir, len(prefix))
  prefix = getCurrentDir() & dirSep
  if startsWith(dir, prefix): 
    return copy(dir, len(prefix))
  prefix = projectPath & dirSep #writeln(output, prefix);
                                #writeln(output, dir);
  if startsWith(dir, prefix): 
    return copy(dir, len(prefix))
  result = dir

proc removeTrailingDirSep(path: string): string = 
  if (len(path) > 0) and (path[len(path) - 1] == dirSep): 
    result = copy(path, 0, len(path) - 2)
  else: 
    result = path
  
proc toGeneratedFile(path, ext: string): string = 
  var (head, tail) = splitPath(path)
  if len(head) > 0: head = shortenDir(head & dirSep)
  result = joinPath([projectPath, genSubDir, head, changeFileExt(tail, ext)])

proc completeGeneratedFilePath(f: string, createSubDir: bool = true): string = 
  var (head, tail) = splitPath(f)
  if len(head) > 0: head = removeTrailingDirSep(shortenDir(head & dirSep))
  var subdir = joinPath([projectPath, genSubDir, head])
  if createSubDir: 
    try: 
      createDir(subdir)
    except EOS: 
      writeln(stdout, "cannot create directory: " & subdir)
      quit(1)
  result = joinPath(subdir, tail)

proc rawFindFile(f: string): string = 
  if ExistsFile(f): 
    result = f
  else: 
    var it = PStrEntry(SearchPaths.head)
    while it != nil: 
      result = JoinPath(it.data, f)
      if ExistsFile(result): return 
      it = PStrEntry(it.Next)
    result = ""

proc FindFile(f: string): string = 
  result = rawFindFile(f)
  if len(result) == 0: result = rawFindFile(toLower(f))
  
proc binaryStrSearch(x: openarray[string], y: string): int = 
  var a = 0
  var b = len(x) - 1
  while a <= b: 
    var mid = (a + b) div 2
    var c = cmpIgnoreCase(x[mid], y)
    if c < 0: 
      a = mid + 1
    elif c > 0: 
      b = mid - 1
    else: 
      return mid
  result = - 1

gConfigVars = newStringTable([], modeStyleInsensitive)
span class="w"> = @[y.sons[0]] else: x.sons[0] = y.sons[0] else: if x.kind notin {nkEmpty..nkNilLit}: move(x.sons, y.sons) # this seems to be the best way to model the reference semantics # of PNimrodNode: template asgnRef(x, y: expr) = moveConst(x, y) proc copyValue(src: PNode): PNode = if src == nil or nfIsRef in src.flags: return src result = newNode(src.kind) result.info = src.info result.typ = src.typ result.flags = src.flags * PersistentNodeFlags when defined(useNodeIds): if result.id == nodeIdToDebug: echo "COMES FROM ", src.id case src.kind of nkCharLit..nkUInt64Lit: result.intVal = src.intVal of nkFloatLit..nkFloat128Lit: result.floatVal = src.floatVal of nkSym: result.sym = src.sym of nkIdent: result.ident = src.ident of nkStrLit..nkTripleStrLit: result.strVal = src.strVal else: newSeq(result.sons, sonsLen(src)) for i in countup(0, sonsLen(src) - 1): result.sons[i] = copyValue(src.sons[i]) proc asgnComplex(x, y: PNode) = if x.kind != y.kind: myreset(x) x.kind = y.kind x.typ = y.typ case x.kind of nkCharLit..nkInt64Lit: x.intVal = y.intVal of nkFloatLit..nkFloat64Lit: x.floatVal = y.floatVal of nkStrLit..nkTripleStrLit: x.strVal = y.strVal of nkIdent: x.ident = y.ident of nkSym: x.sym = y.sym of nkMetaNode: if x.sons.isNil: x.sons = @[y.sons[0]] else: x.sons[0] = y.sons[0] else: if x.kind notin {nkEmpty..nkNilLit}: let y = y.copyValue for i in countup(0, sonsLen(y) - 1): addSon(x, y.sons[i]) template getstr(a: expr): expr = (if a.kind in {nkStrLit..nkTripleStrLit}: a.strVal else: $chr(int(a.intVal))) proc pushSafePoint(f: PStackFrame; pc: int) = if f.safePoints.isNil: f.safePoints = @[] f.safePoints.add(pc) proc popSafePoint(f: PStackFrame) = discard f.safePoints.pop() proc cleanUpOnException(c: PCtx; tos: PStackFrame; regs: TNodeSeq): int = let raisedType = c.currentExceptionA.typ.skipTypes(abstractPtrs) var f = tos while true: while f.safePoints.isNil or f.safePoints.len == 0: f = f.next if f.isNil: return -1 var pc2 = f.safePoints[f.safePoints.high] var nextExceptOrFinally = -1 if c.code[pc2].opcode == opcExcept: nextExceptOrFinally = pc2 + c.code[pc2].regBx - wordExcess inc pc2 while c.code[pc2].opcode == opcExcept: let exceptType = c.types[c.code[pc2].regBx-wordExcess].skipTypes( abstractPtrs) if inheritanceDiff(exceptType, raisedType) <= 0: # mark exception as handled but keep it in B for # the getCurrentException() builtin: c.currentExceptionB = c.currentExceptionA c.currentExceptionA = nil # execute the corresponding handler: return pc2 inc pc2 if nextExceptOrFinally >= 0: pc2 = nextExceptOrFinally if c.code[pc2].opcode == opcFinally: # execute the corresponding handler, but don't quit walking the stack: return pc2 # not the right one: discard f.safePoints.pop proc cleanUpOnReturn(c: PCtx; f: PStackFrame): int = if f.safePoints.isNil: return -1 for s in f.safePoints: var pc = s while c.code[pc].opcode == opcExcept: pc = pc + c.code[pc].regBx - wordExcess if c.code[pc].opcode == opcFinally: return pc return -1 proc opConv*(dest, src: PNode, typ: PType): bool = if typ.kind == tyString: if dest.kind != nkStrLit: myreset(dest) dest.kind = nkStrLit case src.typ.skipTypes(abstractRange).kind of tyEnum: dest.strVal = ordinalValToString(src) of tyInt..tyInt64, tyUInt..tyUInt64: dest.strVal = $src.intVal of tyBool: dest.strVal = if src.intVal == 0: "false" else: "true" of tyFloat..tyFloat128: dest.strVal = $src.floatVal of tyString, tyCString: dest.strVal = src.strVal of tyChar: dest.strVal = $chr(src.intVal) else: internalError("cannot convert to string " & typ.typeToString) else: case skipTypes(typ, abstractRange).kind of tyInt..tyInt64: if dest.kind != nkIntLit: myreset(dest); dest.kind = nkIntLit case skipTypes(src.typ, abstractRange).kind of tyFloat..tyFloat64: dest.intVal = system.toInt(src.floatVal) else: dest.intVal = src.intVal if dest.intVal < firstOrd(typ) or dest.intVal > lastOrd(typ): return true of tyUInt..tyUInt64: if dest.kind != nkIntLit: myreset(dest); dest.kind = nkIntLit case skipTypes(src.typ, abstractRange).kind of tyFloat..tyFloat64: dest.intVal = system.toInt(src.floatVal) else: dest.intVal = src.intVal and ((1 shl typ.size)-1) of tyFloat..tyFloat64: if dest.kind != nkFloatLit: myreset(dest); dest.kind = nkFloatLit case skipTypes(src.typ, abstractRange).kind of tyInt..tyInt64, tyUInt..tyUInt64, tyEnum, tyBool, tyChar: dest.floatVal = toFloat(src.intVal.int) else: dest.floatVal = src.floatVal else: asgnComplex(dest, src) proc compile(c: PCtx, s: PSym): int = result = vmgen.genProc(c, s) #c.echoCode proc regsContents(regs: TNodeSeq) = for i in 0.. <regs.len: echo "Register ", i #debug regs[i] proc rawExecute(c: PCtx, start: int, tos: PStackFrame): PNode = var pc = start var tos = tos var regs: TNodeSeq # alias to tos.slots for performance move(regs, tos.slots) #echo "NEW RUN ------------------------" while true: #{.computedGoto.} let instr = c.code[pc] let ra = instr.regA #echo "PC ", pc, " ", c.code[pc].opcode, " ra ", ra #message(c.debug[pc], warnUser, "gah") case instr.opcode of opcEof: return regs[ra] of opcRet: # XXX perform any cleanup actions pc = tos.comesFrom tos = tos.next let retVal = regs[0] if tos.isNil: #echo "RET ", retVal.rendertree return retVal move(regs, tos.slots) assert c.code[pc].opcode in {opcIndCall, opcIndCallAsgn} if c.code[pc].opcode == opcIndCallAsgn: regs[c.code[pc].regA] = retVal #echo "RET2 ", retVal.rendertree, " ", c.code[pc].regA of opcYldYoid: assert false of opcYldVal: assert false of opcAsgnInt: decodeB(nkIntLit) regs[ra].intVal = regs[rb].intVal of opcAsgnStr: decodeB(nkStrLit) regs[ra].strVal = regs[rb].strVal of opcAsgnFloat: decodeB(nkFloatLit) regs[ra].floatVal = regs[rb].floatVal of opcAsgnComplex: asgnComplex(regs[ra], regs[instr.regB]) of opcAsgnRef: asgnRef(regs[ra], regs[instr.regB]) of opcWrGlobalRef: asgnRef(c.globals.sons[instr.regBx-wordExcess-1], regs[ra]) of opcWrGlobal: asgnComplex(c.globals.sons[instr.regBx-wordExcess-1], regs[ra]) of opcLdArr: # a = b[c] let rb = instr.regB let rc = instr.regC let idx = regs[rc].intVal.int # XXX what if the array is not 0-based? -> codegen should insert a sub assert regs[rb].kind != nkMetaNode let src = regs[rb] if src.kind notin {nkEmpty..nkNilLit} and idx <% src.len: asgnComplex(regs[ra], src.sons[idx]) else: stackTrace(c, tos, pc, errIndexOutOfBounds) of opcLdStrIdx: decodeBC(nkIntLit) let idx = regs[rc].intVal.int if idx <=% regs[rb].strVal.len: regs[ra].intVal = regs[rb].strVal[idx].ord else: stackTrace(c, tos, pc, errIndexOutOfBounds) of opcWrArr: # a[b] = c let rb = instr.regB let rc = instr.regC let idx = regs[rb].intVal.int if idx <% regs[ra].len: asgnComplex(regs[ra].sons[idx], regs[rc]) else: stackTrace(c, tos, pc, errIndexOutOfBounds) of opcWrArrRef: let rb = instr.regB let rc = instr.regC let idx = regs[rb].intVal.int if idx <% regs[ra].len: asgnRef(regs[ra].sons[idx], regs[rc]) else: stackTrace(c, tos, pc, errIndexOutOfBounds) of opcLdObj: # a = b.c let rb = instr.regB let rc = instr.regC # XXX this creates a wrong alias #Message(c.debug[pc], warnUser, $regs[rb].len & " " & $rc) asgnComplex(regs[ra], regs[rb].sons[rc]) of opcWrObj: # a.b = c let rb = instr.regB let rc = instr.regC #if regs[ra].isNil or regs[ra].sons.isNil or rb >= len(regs[ra]): # debug regs[ra] # debug regs[rc] # echo "RB ", rb # internalError(c.debug[pc], "argl") asgnComplex(regs[ra].sons[rb], regs[rc]) of opcWrObjRef: let rb = instr.regB let rc = instr.regC asgnRef(regs[ra].sons[rb], regs[rc]) of opcWrStrIdx: decodeBC(nkStrLit) let idx = regs[rb].intVal.int if idx <% regs[ra].strVal.len: regs[ra].strVal[idx] = chr(regs[rc].intVal) else: stackTrace(c, tos, pc, errIndexOutOfBounds) of opcAddr: decodeB(nkRefTy) if regs[ra].len == 0: regs[ra].add regs[rb] else: regs[ra].sons[0] = regs[rb] of opcDeref: # a = b[] let rb = instr.regB if regs[rb].kind == nkNilLit: stackTrace(c, tos, pc, errNilAccess) assert regs[rb].kind == nkRefTy # XXX this is not correct regs[ra] = regs[rb].sons[0] of opcAddInt: decodeBC(nkIntLit) regs[ra].intVal = regs[rb].intVal + regs[rc].intVal of opcAddImmInt: decodeBImm(nkIntLit) regs[ra].intVal = regs[rb].intVal + imm of opcSubInt: decodeBC(nkIntLit) regs[ra].intVal = regs[rb].intVal - regs[rc].intVal of opcSubImmInt: decodeBImm(nkIntLit) regs[ra].intVal = regs[rb].intVal - imm of opcLenSeq: decodeBImm(nkIntLit) #assert regs[rb].kind == nkBracket # also used by mNLen: regs[ra].intVal = regs[rb].skipMeta.len - imm of opcLenStr: decodeBImm(nkIntLit) assert regs[rb].kind == nkStrLit regs[ra].intVal = regs[rb].strVal.len - imm of opcIncl: decodeB(nkCurly) if not inSet(regs[ra], regs[rb]): addSon(regs[ra], copyTree(regs[rb])) of opcInclRange: decodeBC(nkCurly) var r = newNode(nkRange) r.add regs[rb] r.add regs[rc] addSon(regs[ra], r.copyTree) of opcExcl: decodeB(nkCurly) var b = newNodeIT(nkCurly, regs[rb].info, regs[rb].typ) addSon(b, regs[rb]) var r = diffSets(regs[ra], b) discardSons(regs[ra]) for i in countup(0, sonsLen(r) - 1): addSon(regs[ra], r.sons[i]) of opcCard: decodeB(nkIntLit) regs[ra].intVal = nimsets.cardSet(regs[rb]) of opcMulInt: decodeBC(nkIntLit) regs[ra].intVal = regs[rb].intVal * regs[rc].intVal of opcDivInt: decodeBC(nkIntLit) regs[ra].intVal = regs[rb].intVal div regs[rc].intVal of opcModInt: decodeBC(nkIntLit) regs[ra].intVal = regs[rb].intVal mod regs[rc].intVal of opcAddFloat: decodeBC(nkFloatLit) regs[ra].floatVal = regs[rb].floatVal + regs[rc].floatVal of opcSubFloat: decodeBC(nkFloatLit) regs[ra].floatVal = regs[rb].floatVal - regs[rc].floatVal of opcMulFloat: decodeBC(nkFloatLit) regs[ra].floatVal = regs[rb].floatVal * regs[rc].floatVal of opcDivFloat: decodeBC(nkFloatLit) regs[ra].floatVal = regs[rb].floatVal / regs[rc].floatVal of opcShrInt: decodeBC(nkIntLit) regs[ra].intVal = regs[rb].intVal shr regs[rc].intVal of opcShlInt: decodeBC(nkIntLit) regs[ra].intVal = regs[rb].intVal shl regs[rc].intVal of opcBitandInt: decodeBC(nkIntLit) regs[ra].intVal = regs[rb].intVal and regs[rc].intVal of opcBitorInt: decodeBC(nkIntLit) regs[ra].intVal = regs[rb].intVal or regs[rc].intVal of opcBitxorInt: decodeBC(nkIntLit) regs[ra].intVal = regs[rb].intVal xor regs[rc].intVal of opcAddu: decodeBC(nkIntLit) regs[ra].intVal = regs[rb].intVal +% regs[rc].intVal of opcSubu: decodeBC(nkIntLit) regs[ra].intVal = regs[rb].intVal -% regs[rc].intVal of opcMulu: decodeBC(nkIntLit) regs[ra].intVal = regs[rb].intVal *% regs[rc].intVal of opcDivu: decodeBC(nkIntLit) regs[ra].intVal = regs[rb].intVal /% regs[rc].intVal of opcModu: decodeBC(nkIntLit) regs[ra].intVal = regs[rb].intVal %% regs[rc].intVal of opcEqInt: decodeBC(nkIntLit) regs[ra].intVal = ord(regs[rb].intVal == regs[rc].intVal) of opcLeInt: decodeBC(nkIntLit) regs[ra].intVal = ord(regs[rb].intVal <= regs[rc].intVal) of opcLtInt: decodeBC(nkIntLit) regs[ra].intVal = ord(regs[rb].intVal < regs[rc].intVal) of opcEqFloat: decodeBC(nkIntLit) regs[ra].intVal = ord(regs[rb].floatVal == regs[rc].floatVal) of opcLeFloat: decodeBC(nkIntLit) regs[ra].intVal = ord(regs[rb].floatVal <= regs[rc].floatVal) of opcLtFloat: decodeBC(nkIntLit) regs[ra].intVal = ord(regs[rb].floatVal < regs[rc].floatVal) of opcLeu: decodeBC(nkIntLit) regs[ra].intVal = ord(regs[rb].intVal <=% regs[rc].intVal) of opcLtu: decodeBC(nkIntLit) regs[ra].intVal = ord(regs[rb].intVal <% regs[rc].intVal) of opcEqRef: decodeBC(nkIntLit) regs[ra].intVal = ord((regs[rb].kind == nkNilLit and regs[rc].kind == nkNilLit) or regs[rb].sons == regs[rc].sons) of opcEqNimrodNode: decodeBC(nkIntLit) regs[ra].intVal = ord(regs[rb].skipMeta == regs[rc].skipMeta) of opcXor: decodeBC(nkIntLit) regs[ra].intVal = ord(regs[rb].intVal != regs[rc].intVal) of opcNot: decodeB(nkIntLit) assert regs[rb].kind == nkIntLit regs[ra].intVal = 1 - regs[rb].intVal of opcUnaryMinusInt: decodeB(nkIntLit) assert regs[rb].kind == nkIntLit regs[ra].intVal = -regs[rb].intVal of opcUnaryMinusFloat: decodeB(nkFloatLit) assert regs[rb].kind == nkFloatLit regs[ra].floatVal = -regs[rb].floatVal of opcBitnotInt: decodeB(nkIntLit) assert regs[rb].kind == nkIntLit regs[ra].intVal = not regs[rb].intVal of opcEqStr: decodeBC(nkIntLit) regs[ra].intVal = ord(regs[rb].strVal == regs[rc].strVal) of opcLeStr: decodeBC(nkIntLit) regs[ra].intVal = ord(regs[rb].strVal <= regs[rc].strVal) of opcLtStr: decodeBC(nkIntLit) regs[ra].intVal = ord(regs[rb].strVal < regs[rc].strVal) of opcLeSet: decodeBC(nkIntLit) regs[ra].intVal = ord(containsSets(regs[rb], regs[rc])) of opcEqSet: decodeBC(nkIntLit) regs[ra].intVal = ord(equalSets(regs[rb], regs[rc])) of opcLtSet: decodeBC(nkIntLit) let a = regs[rb] let b = regs[rc] regs[ra].intVal = ord(containsSets(a, b) and not equalSets(a, b)) of opcMulSet: decodeBC(nkCurly) move(regs[ra].sons, nimsets.intersectSets(regs[rb], regs[rc]).sons) of opcPlusSet: decodeBC(nkCurly) move(regs[ra].sons, nimsets.unionSets(regs[rb], regs[rc]).sons) of opcMinusSet: decodeBC(nkCurly) move(regs[ra].sons, nimsets.diffSets(regs[rb], regs[rc]).sons) of opcSymdiffSet: decodeBC(nkCurly) move(regs[ra].sons, nimsets.symdiffSets(regs[rb], regs[rc]).sons) of opcConcatStr: decodeBC(nkStrLit) regs[ra].strVal = getstr(regs[rb]) for i in rb+1..rb+rc-1: regs[ra].strVal.add getstr(regs[i]) of opcAddStrCh: decodeB(nkStrLit) regs[ra].strVal.add(regs[rb].intVal.chr) of opcAddStrStr: decodeB(nkStrLit) regs[ra].strVal.add(regs[rb].strVal) of opcAddSeqElem: decodeB(nkBracket) regs[ra].add(copyTree(regs[rb])) of opcEcho: let rb = instr.regB for i in ra..ra+rb-1: #if regs[i].kind != nkStrLit: debug regs[i] write(stdout, regs[i].strVal) writeln(stdout, "") of opcContainsSet: decodeBC(nkIntLit) regs[ra].intVal = ord(inSet(regs[rb], regs[rc])) of opcSubStr: decodeBC(nkStrLit) inc pc assert c.code[pc].opcode == opcSubStr let rd = c.code[pc].regA regs[ra].strVal = substr(regs[rb].strVal, regs[rc].intVal.int, regs[rd].intVal.int) of opcRangeChck: let rb = instr.regB let rc = instr.regC if not (leValueConv(regs[rb], regs[ra]) and leValueConv(regs[ra], regs[rc])): stackTrace(c, tos, pc, errGenerated, msgKindToString(errIllegalConvFromXtoY) % [ "unknown type" , "unknown type"]) of opcIndCall, opcIndCallAsgn: # dest = call regStart, n; where regStart = fn, arg1, ... let rb = instr.regB let rc = instr.regC let isClosure = regs[rb].kind == nkPar let prc = if not isClosure: regs[rb].sym else: regs[rb].sons[0].sym if sfImportc in prc.flags: if allowFFI notin c.features: globalError(c.debug[pc], errGenerated, "VM not allowed to do FFI") # we pass 'tos.slots' instead of 'regs' so that the compiler can keep # 'regs' in a register: when hasFFI: let prcValue = c.globals.sons[prc.position-1] if prcValue.kind == nkEmpty: globalError(c.debug[pc], errGenerated, "canot run " & prc.name.s) let newValue = callForeignFunction(prcValue, prc.typ, tos.slots, rb+1, rc-1, c.debug[pc]) if newValue.kind != nkEmpty: assert instr.opcode == opcIndCallAsgn asgnRef(regs[ra], newValue) else: globalError(c.debug[pc], errGenerated, "VM not built with FFI support") elif prc.kind != skTemplate: let newPc = compile(c, prc) #echo "new pc ", newPc, " calling: ", prc.name.s var newFrame = PStackFrame(prc: prc, comesFrom: pc, next: tos) newSeq(newFrame.slots, prc.offset) if not isEmptyType(prc.typ.sons[0]) or prc.kind == skMacro: newFrame.slots[0] = getNullValue(prc.typ.sons[0], prc.info) # pass every parameter by var (the language definition allows this): for i in 1 .. rc-1: newFrame.slots[i] = regs[rb+i] if isClosure: newFrame.slots[rc] = regs[rb].sons[1] # allocate the temporaries: for i in rc+ord(isClosure) .. <prc.offset: newFrame.slots[i] = newNode(nkEmpty) tos = newFrame move(regs, newFrame.slots) # -1 for the following 'inc pc' pc = newPc-1 else: # for 'getAst' support we need to support template expansion here: let genSymOwner = if tos.next != nil and tos.next.prc != nil: tos.next.prc else: c.module var macroCall = newNodeI(nkCall, c.debug[pc]) macroCall.add(newSymNode(prc)) for i in 1 .. rc-1: macroCall.add(regs[rb+i].skipMeta) let a = evalTemplate(macroCall, prc, genSymOwner) ensureKind(nkMetaNode) setMeta(regs[ra], a) of opcTJmp: # jump Bx if A != 0 let rbx = instr.regBx - wordExcess - 1 # -1 for the following 'inc pc' if regs[ra].intVal != 0: inc pc, rbx of opcFJmp: # jump Bx if A == 0 let rbx = instr.regBx - wordExcess - 1 # -1 for the following 'inc pc' if regs[ra].intVal == 0: inc pc, rbx of opcJmp: # jump Bx let rbx = instr.regBx - wordExcess - 1 # -1 for the following 'inc pc' inc pc, rbx of opcBranch: # we know the next instruction is a 'fjmp': let branch = c.constants[instr.regBx-wordExcess] var cond = false for j in countup(0, sonsLen(branch) - 2): if overlap(regs[ra], branch.sons[j]): cond = true break assert c.code[pc+1].opcode == opcFJmp inc pc # we skip this instruction so that the final 'inc(pc)' skips # the following jump if not cond: let instr2 = c.code[pc] let rbx = instr2.regBx - wordExcess - 1 # -1 for the following 'inc pc' inc pc, rbx of opcTry: let rbx = instr.regBx - wordExcess tos.pushSafePoint(pc + rbx) of opcExcept: # just skip it; it's followed by a jump; # we'll execute in the 'raise' handler discard of opcFinally: # just skip it; it's followed by the code we need to execute anyway tos.popSafePoint() of opcFinallyEnd: if c.currentExceptionA != nil: # we are in a cleanup run: pc = cleanUpOnException(c, tos, regs)-1 if pc < 0: bailOut(c, tos) return of opcRaise: let raised = regs[ra] c.currentExceptionA = raised c.exceptionInstr = pc # -1 because of the following 'inc' pc = cleanUpOnException(c, tos, regs) - 1 if pc < 0: bailOut(c, tos) return of opcNew: let typ = c.types[instr.regBx - wordExcess] regs[ra] = getNullValue(typ, regs[ra].info) regs[ra].flags.incl nfIsRef of opcNewSeq: let typ = c.types[instr.regBx - wordExcess] inc pc ensureKind(nkBracket) let instr2 = c.code[pc] let rb = instr2.regA regs[ra].typ = typ newSeq(regs[ra].sons, rb) for i in 0 .. <rb: regs[ra].sons[i] = getNullValue(typ, regs[ra].info) of opcNewStr: decodeB(nkStrLit) regs[ra].strVal = newString(regs[rb].intVal.int) of opcLdImmInt: # dest = immediate value decodeBx(nkIntLit) regs[ra].intVal = rbx of opcLdNull: let typ = c.types[instr.regBx - wordExcess] regs[ra] = getNullValue(typ, c.debug[pc]) of opcLdConst: let rb = instr.regBx - wordExcess if regs[ra].isNil: regs[ra] = copyTree(c.constants.sons[rb]) else: moveConst(regs[ra], c.constants.sons[rb]) of opcAsgnConst: let rb = instr.regBx - wordExcess if regs[ra].isNil: regs[ra] = copyTree(c.constants.sons[rb]) else: asgnComplex(regs[ra], c.constants.sons[rb]) of opcLdGlobal: let rb = instr.regBx - wordExcess - 1 if regs[ra].isNil: regs[ra] = copyTree(c.globals.sons[rb]) else: asgnComplex(regs[ra], c.globals.sons[rb]) of opcRepr: decodeB(nkStrLit) regs[ra].strVal = renderTree(regs[rb].skipMeta, {renderNoComments}) of opcQuit: if c.mode in {emRepl, emStaticExpr, emStaticStmt}: message(c.debug[pc], hintQuitCalled) quit(int(getOrdValue(regs[ra]))) else: return nil of opcSetLenStr: decodeB(nkStrLit) regs[ra].strVal.setLen(regs[rb].getOrdValue.int) of opcOf: decodeBC(nkIntLit) let typ = c.types[regs[rc].intVal.int] regs[ra].intVal = ord(inheritanceDiff(regs[rb].typ, typ) >= 0) of opcIs: decodeBC(nkIntLit) let t1 = regs[rb].typ.skipTypes({tyTypeDesc}) let t2 = c.types[regs[rc].intVal.int] # XXX: This should use the standard isOpImpl let match = if t2.kind == tyTypeClass: true else: sameType(t1, t2) regs[ra].intVal = ord(match) of opcSetLenSeq: decodeB(nkBracket) let newLen = regs[rb].getOrdValue.int setLen(regs[ra].sons, newLen) of opcSwap, opcReset: internalError(c.debug[pc], "too implement") of opcIsNil: decodeB(nkIntLit) regs[ra].intVal = ord(regs[rb].skipMeta.kind == nkNilLit) of opcNBindSym: decodeBx(nkMetaNode) setMeta(regs[ra], copyTree(c.constants.sons[rbx])) of opcNChild: decodeBC(nkMetaNode) if regs[rb].kind != nkMetaNode: internalError(c.debug[pc], "no MetaNode") let idx = regs[rc].intVal.int let src = regs[rb].uast if src.kind notin {nkEmpty..nkNilLit} and idx <% src.len: setMeta(regs[ra], src.sons[idx]) else: stackTrace(c, tos, pc, errIndexOutOfBounds) of opcNSetChild: decodeBC(nkMetaNode) let idx = regs[rb].intVal.int var dest = regs[ra].uast if dest.kind notin {nkEmpty..nkNilLit} and idx <% dest.len: dest.sons[idx] = regs[rc].uast else: stackTrace(c, tos, pc, errIndexOutOfBounds) of opcNAdd: decodeBC(nkMetaNode) var u = regs[rb].uast u.add(regs[rc].uast) setMeta(regs[ra], u) of opcNAddMultiple: decodeBC(nkMetaNode) let x = regs[rc] var u = regs[rb].uast # XXX can be optimized: for i in 0.. <x.len: u.add(x.sons[i].skipMeta) setMeta(regs[ra], u) of opcNKind: decodeB(nkIntLit) regs[ra].intVal = ord(regs[rb].uast.kind) of opcNIntVal: decodeB(nkIntLit) let a = regs[rb].uast case a.kind of nkCharLit..nkInt64Lit: regs[ra].intVal = a.intVal else: stackTrace(c, tos, pc, errFieldXNotFound, "intVal") of opcNFloatVal: decodeB(nkFloatLit) let a = regs[rb].uast case a.kind of nkFloatLit..nkFloat64Lit: regs[ra].floatVal = a.floatVal else: stackTrace(c, tos, pc, errFieldXNotFound, "floatVal") of opcNSymbol: decodeB(nkSym) let a = regs[rb].uast if a.kind == nkSym: regs[ra].sym = a.sym else: stackTrace(c, tos, pc, errFieldXNotFound, "symbol") of opcNIdent: decodeB(nkIdent) let a = regs[rb].uast if a.kind == nkIdent: regs[ra].ident = a.ident else: stackTrace(c, tos, pc, errFieldXNotFound, "ident") of opcNGetType: internalError(c.debug[pc], "unknown opcode " & $instr.opcode) of opcNStrVal: decodeB(nkStrLit) let a = regs[rb].uast case a.kind of nkStrLit..nkTripleStrLit: regs[ra].strVal = a.strVal else: stackTrace(c, tos, pc, errFieldXNotFound, "strVal") of opcSlurp: decodeB(nkStrLit) regs[ra].strVal = opSlurp(regs[rb].strVal, c.debug[pc], c.module) of opcGorge: decodeBC(nkStrLit) regs[ra].strVal = opGorge(regs[rb].strVal, regs[rc].strVal) of opcNError: stackTrace(c, tos, pc, errUser, regs[ra].strVal) of opcNWarning: message(c.debug[pc], warnUser, regs[ra].strVal) of opcNHint: message(c.debug[pc], hintUser, regs[ra].strVal) of opcParseExprToAst: decodeB(nkMetaNode) # c.debug[pc].line.int - countLines(regs[rb].strVal) ? let ast = parseString(regs[rb].strVal, c.debug[pc].toFilename, c.debug[pc].line.int) if sonsLen(ast) != 1: globalError(c.debug[pc], errExprExpected, "multiple statements") setMeta(regs[ra], ast.sons[0]) of opcParseStmtToAst: decodeB(nkMetaNode) let ast = parseString(regs[rb].strVal, c.debug[pc].toFilename, c.debug[pc].line.int) setMeta(regs[ra], ast) of opcCallSite: ensureKind(nkMetaNode) if c.callsite != nil: setMeta(regs[ra], c.callsite) else: stackTrace(c, tos, pc, errFieldXNotFound, "callsite") of opcNLineInfo: decodeB(nkStrLit) let n = regs[rb] regs[ra].strVal = n.info.toFileLineCol regs[ra].info = c.debug[pc] of opcEqIdent: decodeBC(nkIntLit) if regs[rb].kind == nkIdent and regs[rc].kind == nkIdent: regs[ra].intVal = ord(regs[rb].ident.id == regs[rc].ident.id) else: regs[ra].intVal = 0 of opcStrToIdent: decodeB(nkIdent) if regs[rb].kind notin {nkStrLit..nkTripleStrLit}: stackTrace(c, tos, pc, errFieldXNotFound, "strVal") else: regs[ra].info = c.debug[pc] regs[ra].ident = getIdent(regs[rb].strVal) of opcIdentToStr: decodeB(nkStrLit) let a = regs[rb] regs[ra].info = c.debug[pc] if a.kind == nkSym: regs[ra].strVal = a.sym.name.s elif a.kind == nkIdent: regs[ra].strVal = a.ident.s else: stackTrace(c, tos, pc, errFieldXNotFound, "ident") of opcSetType: regs[ra].typ = c.types[instr.regBx - wordExcess] of opcConv: let rb = instr.regB inc pc let typ = c.types[c.code[pc].regBx - wordExcess] if opConv(regs[ra], regs[rb], typ): stackTrace(c, tos, pc, errGenerated, msgKindToString(errIllegalConvFromXtoY) % [ "unknown type" , "unknown type"]) of opcCast: let rb = instr.regB inc pc let typ = c.types[c.code[pc].regBx - wordExcess] when hasFFI: let dest = fficast(regs[rb], typ) asgnRef(regs[ra], dest) else: globalError(c.debug[pc], "cannot evaluate cast") of opcNSetIntVal: decodeB(nkMetaNode) var dest = regs[ra].uast if dest.kind in {nkCharLit..nkInt64Lit} and regs[rb].kind in {nkCharLit..nkInt64Lit}: dest.intVal = regs[rb].intVal else: stackTrace(c, tos, pc, errFieldXNotFound, "intVal") of opcNSetFloatVal: decodeB(nkMetaNode) var dest = regs[ra].uast if dest.kind in {nkFloatLit..nkFloat64Lit} and regs[rb].kind in {nkFloatLit..nkFloat64Lit}: dest.floatVal = regs[rb].floatVal else: stackTrace(c, tos, pc, errFieldXNotFound, "floatVal") of opcNSetSymbol: decodeB(nkMetaNode) var dest = regs[ra].uast if dest.kind == nkSym and regs[rb].kind == nkSym: dest.sym = regs[rb].sym else: stackTrace(c, tos, pc, errFieldXNotFound, "symbol") of opcNSetIdent: decodeB(nkMetaNode) var dest = regs[ra].uast if dest.kind == nkIdent and regs[rb].kind == nkIdent: dest.ident = regs[rb].ident else: stackTrace(c, tos, pc, errFieldXNotFound, "ident") of opcNSetType: decodeB(nkMetaNode) let b = regs[rb].skipMeta internalAssert b.kind == nkSym and b.sym.kind == skType regs[ra].uast.typ = b.sym.typ of opcNSetStrVal: decodeB(nkMetaNode) var dest = regs[ra].uast if dest.kind in {nkStrLit..nkTripleStrLit} and regs[rb].kind in {nkStrLit..nkTripleStrLit}: dest.strVal = regs[rb].strVal else: stackTrace(c, tos, pc, errFieldXNotFound, "strVal") of opcNNewNimNode: decodeBC(nkMetaNode) var k = regs[rb].intVal if k < 0 or k > ord(high(TNodeKind)) or k == ord(nkMetaNode): internalError(c.debug[pc], "request to create a NimNode of invalid kind") let cc = regs[rc].skipMeta setMeta(regs[ra], newNodeI(TNodeKind(int(k)), if cc.kind == nkNilLit: c.debug[pc] else: cc.info)) regs[ra].sons[0].flags.incl nfIsRef of opcNCopyNimNode: decodeB(nkMetaNode) setMeta(regs[ra], copyNode(regs[rb])) of opcNCopyNimTree: decodeB(nkMetaNode) setMeta(regs[ra], copyTree(regs[rb])) of opcNDel: decodeBC(nkMetaNode) let bb = regs[rb].intVal.int for i in countup(0, regs[rc].intVal.int-1): delSon(regs[ra].uast, bb) of opcGenSym: decodeBC(nkMetaNode) let k = regs[rb].intVal let name = if regs[rc].strVal.len == 0: ":tmp" else: regs[rc].strVal if k < 0 or k > ord(high(TSymKind)): internalError(c.debug[pc], "request to create symbol of invalid kind") var sym = newSym(k.TSymKind, name.getIdent, c.module, c.debug[pc]) incl(sym.flags, sfGenSym) setMeta(regs[ra], newSymNode(sym)) of opcTypeTrait: # XXX only supports 'name' for now; we can use regC to encode the # type trait operation decodeB(nkStrLit) let typ = regs[rb].sym.typ.skipTypes({tyTypeDesc}) regs[ra].strVal = typ.typeToString(preferExported) of opcGlobalOnce: let rb = instr.regBx if c.globals.sons[rb - wordExcess - 1].kind != nkEmpty: # skip initialization instructions: while true: inc pc if c.code[pc].opcode in {opcWrGlobal, opcWrGlobalRef} and c.code[pc].regBx == rb: break of opcGlobalAlias: let rb = instr.regBx - wordExcess - 1 regs[ra] = c.globals.sons[rb] inc pc proc fixType(result, n: PNode) {.inline.} = # XXX do it deeply for complex values #if result.typ.isNil: result.typ = n.typ proc execute(c: PCtx, start: int): PNode = var tos = PStackFrame(prc: nil, comesFrom: 0, next: nil) newSeq(tos.slots, c.prc.maxSlots) for i in 0 .. <c.prc.maxSlots: tos.slots[i] = newNode(nkEmpty) result = rawExecute(c, start, tos) proc evalStmt*(c: PCtx, n: PNode) = let start = genStmt(c, n) # execute new instructions; this redundant opcEof check saves us lots # of allocations in 'execute': if c.code[start].opcode != opcEof: discard execute(c, start) proc evalExpr*(c: PCtx, n: PNode): PNode = let start = genExpr(c, n) assert c.code[start].opcode != opcEof result = execute(c, start) if not result.isNil: result = result.skipMeta fixType(result, n) # for now we share the 'globals' environment. XXX Coming soon: An API for # storing&loading the 'globals' environment to get what a component system # requires. var globalCtx: PCtx proc setupGlobalCtx(module: PSym) = if globalCtx.isNil: globalCtx = newCtx(module) else: refresh(globalCtx, module) proc myOpen(module: PSym): PPassContext = #var c = newEvalContext(module, emRepl) #c.features = {allowCast, allowFFI, allowInfiniteLoops} #pushStackFrame(c, newStackFrame()) # XXX produce a new 'globals' environment here: setupGlobalCtx(module) result = globalCtx when hasFFI: globalCtx.features = {allowFFI, allowCast} var oldErrorCount: int proc myProcess(c: PPassContext, n: PNode): PNode = # don't eval errornous code: if oldErrorCount == msgs.gErrorCounter: evalStmt(PCtx(c), n) result = emptyNode else: result = n oldErrorCount = msgs.gErrorCounter const evalPass* = makePass(myOpen, nil, myProcess, myProcess) proc evalConstExprAux(module, prc: PSym, n: PNode, mode: TEvalMode): PNode = setupGlobalCtx(module) var c = globalCtx c.mode = mode let start = genExpr(c, n, requiresValue = mode!=emStaticStmt) assert c.code[start].opcode != opcEof var tos = PStackFrame(prc: prc, comesFrom: 0, next: nil) newSeq(tos.slots, c.prc.maxSlots) for i in 0 .. <c.prc.maxSlots: tos.slots[i] = newNode(nkEmpty) result = rawExecute(c, start, tos) fixType(result, n) proc evalConstExpr*(module: PSym, e: PNode): PNode = result = evalConstExprAux(module, nil, e, emConst) proc evalStaticExpr*(module: PSym, e: PNode, prc: PSym): PNode = result = evalConstExprAux(module, prc, e, emStaticExpr) proc evalStaticStmt*(module: PSym, e: PNode, prc: PSym) = discard evalConstExprAux(module, prc, e, emStaticStmt) proc setupMacroParam(x: PNode): PNode = result = x if result.kind in {nkHiddenSubConv, nkHiddenStdConv}: result = result.sons[1] let y = result y.flags.incl nfIsRef result = newNode(nkMetaNode) result.add y result.typ = x.typ var evalMacroCounter: int proc evalMacroCall*(module: PSym, n, nOrig: PNode, sym: PSym): PNode = # XXX GlobalError() is ugly here, but I don't know a better solution for now inc(evalMacroCounter) if evalMacroCounter > 100: globalError(n.info, errTemplateInstantiationTooNested) setupGlobalCtx(module) var c = globalCtx c.callsite = nOrig let start = genProc(c, sym) var tos = PStackFrame(prc: sym, comesFrom: 0, next: nil) let maxSlots = sym.offset newSeq(tos.slots, maxSlots) # setup arguments: var L = n.safeLen if L == 0: L = 1 # This is wrong for tests/reject/tind1.nim where the passed 'else' part # doesn't end up in the parameter: #InternalAssert tos.slots.len >= L # return value: tos.slots[0] = newNodeIT(nkNilLit, n.info, sym.typ.sons[0]) # setup parameters: for i in 1 .. < min(tos.slots.len, L): tos.slots[i] = setupMacroParam(n.sons[i]) # temporary storage: for i in L .. <maxSlots: tos.slots[i] = newNode(nkEmpty) result = rawExecute(c, start, tos) if cyclicTree(result): globalError(n.info, errCyclicTree) dec(evalMacroCounter) if result != nil: result = result.skipMeta c.callsite = nil