Merge pull request #1 from nim-lang/devel

pull-23-04-18
This commit is contained in:
Michael Voronin 2018-04-23 11:35:56 +03:00 • committed by GitHub
commit 17d3c6f3f3
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
81 changed files with 1074 additions and 1398 deletions

View file

@ -11,6 +11,10 @@
to deal with! to deal with!
- Indexing into a ``cstring`` for the JS target is now mapped - Indexing into a ``cstring`` for the JS target is now mapped
to ``charCodeAt``. to ``charCodeAt``.
- Assignments that would "slice" an object into its supertype are not prevented
at runtime. Use ``ref object`` with inheritance rather than ``object`` with
inheritance to prevent this issue.
#### Breaking changes in the standard library #### Breaking changes in the standard library
@ -27,6 +31,7 @@
- ``proc `-`*(a, b: Time): int64`` in the ``times`` module has changed return type - ``proc `-`*(a, b: Time): int64`` in the ``times`` module has changed return type
to ``times.Duration`` in order to support higher time resolutions. to ``times.Duration`` in order to support higher time resolutions.
The proc is no longer deprecated. The proc is no longer deprecated.
- ``posix.Timeval.tv_sec`` has changed type to ``posix.Time``.
#### Breaking changes in the compiler #### Breaking changes in the compiler

View file

@ -1042,9 +1042,9 @@ proc newNode*(kind: TNodeKind): PNode =
new(result) new(result)
result.kind = kind result.kind = kind
#result.info = UnknownLineInfo() inlined: #result.info = UnknownLineInfo() inlined:
result.info.fileIndex = int32(-1) result.info.fileIndex = InvalidFileIdx
result.info.col = int16(-1) result.info.col = int16(-1)
result.info.line = int16(-1) result.info.line = uint16(0)
when defined(useNodeIds): when defined(useNodeIds):
result.id = gNodeId result.id = gNodeId
if result.id == nodeIdToDebug: if result.id == nodeIdToDebug:
@ -1116,13 +1116,13 @@ proc linkTo*(s: PSym, t: PType): PSym {.discardable.} =
s.typ = t s.typ = t
result = s result = s
template fileIdx*(c: PSym): int32 = template fileIdx*(c: PSym): FileIndex =
# XXX: this should be used only on module symbols # XXX: this should be used only on module symbols
c.position.int32 c.position.FileIndex
template filename*(c: PSym): string = template filename*(c: PSym): string =
# XXX: this should be used only on module symbols # XXX: this should be used only on module symbols
c.position.int32.toFilename c.position.FileIndex.toFilename
proc appendToModule*(m: PSym, n: PNode) = proc appendToModule*(m: PSym, n: PNode) =
## The compiler will use this internally to add nodes that will be ## The compiler will use this internally to add nodes that will be

View file

@ -72,24 +72,25 @@ proc bitSetContains(x, y: TBitSet): bool =
result = true result = true
# Number of set bits for all values of int8 # Number of set bits for all values of int8
const populationCount: array[low(int8)..high(int8), int8] = [ const populationCount: array[low(int8)..high(int8), int8] = block:
1.int8, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, var arr: array[low(int8)..high(int8), int8]
2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, proc countSetBits(x: int8): int8 =
3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, return
2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, ( x and 0b00000001'i8) +
3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, ((x and 0b00000010'i8) shr 1) +
3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, ((x and 0b00000100'i8) shr 2) +
4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, ((x and 0b00001000'i8) shr 3) +
0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4, ((x and 0b00010000'i8) shr 4) +
1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, ((x and 0b00100000'i8) shr 5) +
1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, ((x and 0b01000000'i8) shr 6) +
2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, ((x and 0b10000000'i8) shr 7)
1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, for it in low(int8)..high(int8):
3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7 arr[it] = countSetBits(it)
]
arr
proc bitSetCard(x: TBitSet): BiggestInt = proc bitSetCard(x: TBitSet): BiggestInt =
for it in x: for it in x:

View file

@ -2023,7 +2023,7 @@ template genStmtListExprImpl(exprOrStmt) {.dirty.} =
let theMacro = it[0].sym let theMacro = it[0].sym
add p.s(cpsStmts), initFrameNoDebug(p, frameName, add p.s(cpsStmts), initFrameNoDebug(p, frameName,
makeCString theMacro.name.s, makeCString theMacro.name.s,
theMacro.info.quotedFilename, it.info.line) theMacro.info.quotedFilename, it.info.line.int)
else: else:
genStmts(p, it) genStmts(p, it)
if n.len > 0: exprOrStmt if n.len > 0: exprOrStmt

View file

@ -223,7 +223,7 @@ proc genLineDir(p: BProc, t: PNode) =
line.rope, makeCString(toFilename(tt.info))) line.rope, makeCString(toFilename(tt.info)))
elif ({optLineTrace, optStackTrace} * p.options == elif ({optLineTrace, optStackTrace} * p.options ==
{optLineTrace, optStackTrace}) and {optLineTrace, optStackTrace}) and
(p.prc == nil or sfPure notin p.prc.flags) and tt.info.fileIndex >= 0: (p.prc == nil or sfPure notin p.prc.flags) and tt.info.fileIndex != InvalidFileIDX:
if freshLineInfo(p, tt.info): if freshLineInfo(p, tt.info):
linefmt(p, cpsStmts, "nimln_($1, $2);$n", linefmt(p, cpsStmts, "nimln_($1, $2);$n",
line.rope, tt.info.quotedFilename) line.rope, tt.info.quotedFilename)
@ -1301,7 +1301,7 @@ proc resetCgenModules*(g: BModuleList) =
for m in cgenModules(g): resetModule(m) for m in cgenModules(g): resetModule(m)
proc rawNewModule(g: BModuleList; module: PSym): BModule = proc rawNewModule(g: BModuleList; module: PSym): BModule =
result = rawNewModule(g, module, module.position.int32.toFullPath) result = rawNewModule(g, module, module.position.FileIndex.toFullPath)
proc newModule(g: BModuleList; module: PSym): BModule = proc newModule(g: BModuleList; module: PSym): BModule =
# we should create only one cgen module for each module sym # we should create only one cgen module for each module sym
@ -1311,7 +1311,7 @@ proc newModule(g: BModuleList; module: PSym): BModule =
if (optDeadCodeElim in gGlobalOptions): if (optDeadCodeElim in gGlobalOptions):
if (sfDeadCodeElim in module.flags): if (sfDeadCodeElim in module.flags):
internalError("added pending module twice: " & module.filename) internalError("added pending module twice: " & toFilename(FileIndex module.position))
template injectG(config) {.dirty.} = template injectG(config) {.dirty.} =
if graph.backend == nil: if graph.backend == nil:

View file

@ -77,6 +77,14 @@ proc writeAdvancedUsage(pass: TCmdLinePass) =
{msgStdout}) {msgStdout})
msgQuit(0) msgQuit(0)
proc writeFullhelp(pass: TCmdLinePass) =
if pass == passCmd1:
msgWriteln(`%`(HelpMessage, [VersionAsString,
platform.OS[platform.hostOS].name,
CPU[platform.hostCPU].name]) & Usage & AdvancedUsage,
{msgStdout})
msgQuit(0)
proc writeVersionInfo(pass: TCmdLinePass) = proc writeVersionInfo(pass: TCmdLinePass) =
if pass == passCmd1: if pass == passCmd1:
msgWriteln(`%`(HelpMessage, [VersionAsString, msgWriteln(`%`(HelpMessage, [VersionAsString,
@ -311,7 +319,7 @@ proc trackDirty(arg: string, info: TLineInfo) =
localError(info, errInvalidNumber, a[2]) localError(info, errInvalidNumber, a[2])
let dirtyOriginalIdx = a[1].fileInfoIdx let dirtyOriginalIdx = a[1].fileInfoIdx
if dirtyOriginalIdx >= 0: if dirtyOriginalIdx.int32 >= 0:
msgs.setDirtyFile(dirtyOriginalIdx, a[0]) msgs.setDirtyFile(dirtyOriginalIdx, a[0])
gTrackPos = newLineInfo(dirtyOriginalIdx, line, column) gTrackPos = newLineInfo(dirtyOriginalIdx, line, column)
@ -611,6 +619,9 @@ proc processSwitch(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
of "advanced": of "advanced":
expectNoArg(switch, arg, pass, info) expectNoArg(switch, arg, pass, info)
writeAdvancedUsage(pass) writeAdvancedUsage(pass)
of "fullhelp":
expectNoArg(switch, arg, pass, info)
writeFullhelp(pass)
of "help", "h": of "help", "h":
expectNoArg(switch, arg, pass, info) expectNoArg(switch, arg, pass, info)
helpOnError(pass) helpOnError(pass)
@ -702,7 +713,6 @@ proc processSwitch(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
expectNoArg(switch, arg, pass, info) expectNoArg(switch, arg, pass, info)
useNimNamespace = true useNimNamespace = true
defineSymbol("cppCompileToNamespace") defineSymbol("cppCompileToNamespace")
else: else:
if strutils.find(switch, '.') >= 0: options.setConfigVar(switch, arg) if strutils.find(switch, '.') >= 0: options.setConfigVar(switch, arg)
else: invalidCmdLineOption(pass, switch, info) else: invalidCmdLineOption(pass, switch, info)

View file

@ -544,7 +544,7 @@ proc genJsonItem(d: PDoc, n, nameNode: PNode, k: TSymKind): JsonNode =
initTokRender(r, n, {renderNoBody, renderNoComments, renderDocComments}) initTokRender(r, n, {renderNoBody, renderNoComments, renderDocComments})
result = %{ "name": %name, "type": %($k), "line": %n.info.line, result = %{ "name": %name, "type": %($k), "line": %n.info.line.int,
"col": %n.info.col} "col": %n.info.col}
if comm != nil and comm != "": if comm != nil and comm != "":
result["description"] = %comm result["description"] = %comm
@ -618,7 +618,7 @@ proc generateJson*(d: PDoc, n: PNode) =
of nkCommentStmt: of nkCommentStmt:
if n.comment != nil and startsWith(n.comment, "##"): if n.comment != nil and startsWith(n.comment, "##"):
let stripped = n.comment.substr(2).strip let stripped = n.comment.substr(2).strip
d.add %{ "comment": %stripped, "line": %n.info.line, d.add %{ "comment": %stripped, "line": %n.info.line.int,
"col": %n.info.col } "col": %n.info.col }
of nkProcDef: of nkProcDef:
when useEffectSystem: documentRaises(n) when useEffectSystem: documentRaises(n)
@ -790,7 +790,7 @@ proc writeOutputJson*(d: PDoc, filename, outExt: string,
discard "fixme: error report" discard "fixme: error report"
proc commandDoc*() = proc commandDoc*() =
var ast = parseFile(gProjectMainIdx, newIdentCache()) var ast = parseFile(gProjectMainIdx.FileIndex, newIdentCache())
if ast == nil: return if ast == nil: return
var d = newDocumentor(gProjectFull, options.gConfigVars) var d = newDocumentor(gProjectFull, options.gConfigVars)
d.hasToc = true d.hasToc = true
@ -840,7 +840,7 @@ proc commandRst2TeX*() =
commandRstAux(gProjectFull, TexExt) commandRstAux(gProjectFull, TexExt)
proc commandJson*() = proc commandJson*() =
var ast = parseFile(gProjectMainIdx, newIdentCache()) var ast = parseFile(gProjectMainIdx.FileIndex, newIdentCache())
if ast == nil: return if ast == nil: return
var d = newDocumentor(gProjectFull, options.gConfigVars) var d = newDocumentor(gProjectFull, options.gConfigVars)
d.hasToc = true d.hasToc = true
@ -855,7 +855,7 @@ proc commandJson*() =
writeRope(content, getOutFile(gProjectFull, JsonExt), useWarning = false) writeRope(content, getOutFile(gProjectFull, JsonExt), useWarning = false)
proc commandTags*() = proc commandTags*() =
var ast = parseFile(gProjectMainIdx, newIdentCache()) var ast = parseFile(gProjectMainIdx.FileIndex, newIdentCache())
if ast == nil: return if ast == nil: return
var d = newDocumentor(gProjectFull, options.gConfigVars) var d = newDocumentor(gProjectFull, options.gConfigVars)
d.hasToc = true d.hasToc = true

View file

@ -106,8 +106,9 @@ proc evalTemplateArgs(n: PNode, s: PSym; fromHlo: bool): PNode =
for i in 1 .. genericParams: for i in 1 .. genericParams:
result.addSon n.sons[givenRegularParams + i] result.addSon n.sons[givenRegularParams + i]
# to prevent endless recursion in template instantiation
const evalTemplateLimit* = 1000
var evalTemplateCounter* = 0 var evalTemplateCounter* = 0
# to prevent endless recursion in templates instantiation
proc wrapInComesFrom*(info: TLineInfo; sym: PSym; res: PNode): PNode = proc wrapInComesFrom*(info: TLineInfo; sym: PSym; res: PNode): PNode =
when true: when true:
@ -133,7 +134,7 @@ proc wrapInComesFrom*(info: TLineInfo; sym: PSym; res: PNode): PNode =
proc evalTemplate*(n: PNode, tmpl, genSymOwner: PSym; fromHlo=false): PNode = proc evalTemplate*(n: PNode, tmpl, genSymOwner: PSym; fromHlo=false): PNode =
inc(evalTemplateCounter) inc(evalTemplateCounter)
if evalTemplateCounter > 100: if evalTemplateCounter > evalTemplateLimit:
globalError(n.info, errTemplateInstantiationTooNested) globalError(n.info, errTemplateInstantiationTooNested)
result = n result = n

View file

@ -35,7 +35,7 @@ const
proc newLine(p: var TTmplParser) = proc newLine(p: var TTmplParser) =
llStreamWrite(p.outp, repeat(')', p.emitPar)) llStreamWrite(p.outp, repeat(')', p.emitPar))
p.emitPar = 0 p.emitPar = 0
if p.info.line > int16(1): llStreamWrite(p.outp, "\n") if p.info.line > uint16(1): llStreamWrite(p.outp, "\n")
if p.pendingExprLine: if p.pendingExprLine:
llStreamWrite(p.outp, spaces(2)) llStreamWrite(p.outp, spaces(2))
p.pendingExprLine = false p.pendingExprLine = false
@ -212,9 +212,9 @@ proc filterTmpl*(stdin: PLLStream, filename: string, call: PNode): PLLStream =
p.x = newStringOfCap(120) p.x = newStringOfCap(120)
# do not process the first line which contains the directive: # do not process the first line which contains the directive:
if llStreamReadLine(p.inp, p.x): if llStreamReadLine(p.inp, p.x):
p.info.line = p.info.line + int16(1) p.info.line = p.info.line + 1'u16
while llStreamReadLine(p.inp, p.x): while llStreamReadLine(p.inp, p.x):
p.info.line = p.info.line + int16(1) p.info.line = p.info.line + 1'u16
parseLine(p) parseLine(p)
newLine(p) newLine(p)
result = p.outp result = p.outp

View file

@ -44,7 +44,7 @@ proc applyPatterns(c: PContext, n: PNode): PNode =
assert x.kind in {nkStmtList, nkCall} assert x.kind in {nkStmtList, nkCall}
# better be safe than sorry, so check evalTemplateCounter too: # better be safe than sorry, so check evalTemplateCounter too:
inc(evalTemplateCounter) inc(evalTemplateCounter)
if evalTemplateCounter > 100: if evalTemplateCounter > evalTemplateLimit:
globalError(n.info, errTemplateInstantiationTooNested) globalError(n.info, errTemplateInstantiationTooNested)
# deactivate this pattern: # deactivate this pattern:
c.patterns[i] = nil c.patterns[i] = nil

View file

@ -8,7 +8,6 @@
# #
# This is the JavaScript code generator. # This is the JavaScript code generator.
# Also a PHP code generator. ;-)
discard """ discard """
The JS code generator contains only 2 tricks: The JS code generator contains only 2 tricks:
@ -38,11 +37,8 @@ import
from modulegraphs import ModuleGraph from modulegraphs import ModuleGraph
type type
TTarget = enum
targetJS, targetPHP
TJSGen = object of TPassContext TJSGen = object of TPassContext
module: PSym module: PSym
target: TTarget
sigConflicts: CountTable[SigHash] sigConflicts: CountTable[SigHash]
BModule = ref TJSGen BModule = ref TJSGen
@ -92,16 +88,12 @@ type
module: BModule module: BModule
g: PGlobals g: PGlobals
beforeRetNeeded: bool beforeRetNeeded: bool
target: TTarget # duplicated here for faster dispatching
unique: int # for temp identifier generation unique: int # for temp identifier generation
blocks: seq[TBlock] blocks: seq[TBlock]
extraIndent: int extraIndent: int
up: PProc # up the call chain; required for closure support up: PProc # up the call chain; required for closure support
declaredGlobals: IntSet declaredGlobals: IntSet
template `|`(a, b: untyped): untyped {.dirty.} =
(if p.target == targetJS: a else: b)
var indent = "\t".rope var indent = "\t".rope
proc indentLine(p: PProc, r: Rope): Rope = proc indentLine(p: PProc, r: Rope): Rope =
@ -157,11 +149,8 @@ proc newProc(globals: PGlobals, module: BModule, procDef: PNode,
module: module, module: module,
procDef: procDef, procDef: procDef,
g: globals, g: globals,
target: module.target,
extraIndent: int(procDef != nil)) extraIndent: int(procDef != nil))
if procDef != nil: result.prc = procDef.sons[namePos].sym if procDef != nil: result.prc = procDef.sons[namePos].sym
if result.target == targetPHP:
result.declaredGlobals = initIntSet()
proc declareGlobal(p: PProc; id: int; r: Rope) = proc declareGlobal(p: PProc; id: int; r: Rope) =
if p.prc != nil and not p.declaredGlobals.containsOrIncl(id): if p.prc != nil and not p.declaredGlobals.containsOrIncl(id):
@ -208,8 +197,7 @@ proc mapType(typ: PType): TJSTypeKind =
of tyUnused, tyOptAsRef: internalError("mapType") of tyUnused, tyOptAsRef: internalError("mapType")
proc mapType(p: PProc; typ: PType): TJSTypeKind = proc mapType(p: PProc; typ: PType): TJSTypeKind =
if p.target == targetPHP: result = etyObject result = mapType(typ)
else: result = mapType(typ)
proc mangleName(m: BModule, s: PSym): Rope = proc mangleName(m: BModule, s: PSym): Rope =
proc validJsName(name: string): bool = proc validJsName(name: string): bool =
@ -236,7 +224,7 @@ proc mangleName(m: BModule, s: PSym): Rope =
if result == nil: if result == nil:
if s.kind == skField and s.name.s.validJsName: if s.kind == skField and s.name.s.validJsName:
result = rope(s.name.s) result = rope(s.name.s)
elif m.target == targetJS or s.kind == skTemp: elif s.kind == skTemp:
result = rope(mangle(s.name.s)) result = rope(mangle(s.name.s))
else: else:
var x = newStringOfCap(s.name.s.len) var x = newStringOfCap(s.name.s.len)
@ -314,7 +302,7 @@ proc useMagic(p: PProc, name: string) =
proc isSimpleExpr(p: PProc; n: PNode): bool = proc isSimpleExpr(p: PProc; n: PNode): bool =
# calls all the way down --> can stay expression based # calls all the way down --> can stay expression based
if n.kind in nkCallKinds+{nkBracketExpr, nkDotExpr, nkPar, nkTupleConstr} or if n.kind in nkCallKinds+{nkBracketExpr, nkDotExpr, nkPar, nkTupleConstr} or
(p.target == targetJS and n.kind in {nkObjConstr, nkBracket, nkCurly}): (n.kind in {nkObjConstr, nkBracket, nkCurly}):
for c in n: for c in n:
if not p.isSimpleExpr(c): return false if not p.isSimpleExpr(c): return false
result = true result = true
@ -323,12 +311,9 @@ proc isSimpleExpr(p: PProc; n: PNode): bool =
proc getTemp(p: PProc, defineInLocals: bool = true): Rope = proc getTemp(p: PProc, defineInLocals: bool = true): Rope =
inc(p.unique) inc(p.unique)
if p.target == targetJS:
result = "Tmp$1" % [rope(p.unique)] result = "Tmp$1" % [rope(p.unique)]
if defineInLocals: if defineInLocals:
add(p.locals, p.indentLine("var $1;$n" % [result])) add(p.locals, p.indentLine("var $1;$n" % [result]))
else:
result = "$$Tmp$1" % [rope(p.unique)]
proc genAnd(p: PProc, a, b: PNode, r: var TCompRes) = proc genAnd(p: PProc, a, b: PNode, r: var TCompRes) =
assert r.kind == resNone assert r.kind == resNone
@ -477,15 +462,9 @@ proc unsignedTrimmerJS(size: BiggestInt): Rope =
of 4: rope">>> 0" of 4: rope">>> 0"
else: rope"" else: rope""
proc unsignedTrimmerPHP(size: BiggestInt): Rope =
case size
of 1: rope"& 0xff"
of 2: rope"& 0xffff"
of 4: rope"& 0xffffffff"
else: rope""
template unsignedTrimmer(size: BiggestInt): Rope = template unsignedTrimmer(size: BiggestInt): Rope =
size.unsignedTrimmerJS | size.unsignedTrimmerPHP size.unsignedTrimmerJS
proc binaryUintExpr(p: PProc, n: PNode, r: var TCompRes, op: string, proc binaryUintExpr(p: PProc, n: PNode, r: var TCompRes, op: string,
reassign = false) = reassign = false) =
@ -533,45 +512,17 @@ proc arith(p: PProc, n: PNode, r: var TCompRes, op: TMagic) =
of mMulU: binaryUintExpr(p, n, r, "*") of mMulU: binaryUintExpr(p, n, r, "*")
of mDivU: binaryUintExpr(p, n, r, "/") of mDivU: binaryUintExpr(p, n, r, "/")
of mDivI: of mDivI:
if p.target == targetPHP:
var x, y: TCompRes
gen(p, n.sons[1], x)
gen(p, n.sons[2], y)
r.res = "intval($1 / $2)" % [x.rdLoc, y.rdLoc]
else:
arithAux(p, n, r, op) arithAux(p, n, r, op)
of mModI: of mModI:
if p.target == targetPHP:
var x, y: TCompRes
gen(p, n.sons[1], x)
gen(p, n.sons[2], y)
r.res = "($1 % $2)" % [x.rdLoc, y.rdLoc]
else:
arithAux(p, n, r, op) arithAux(p, n, r, op)
of mShrI: of mShrI:
var x, y: TCompRes var x, y: TCompRes
gen(p, n.sons[1], x) gen(p, n.sons[1], x)
gen(p, n.sons[2], y) gen(p, n.sons[2], y)
let trimmer = unsignedTrimmer(n[1].typ.skipTypes(abstractRange).size) let trimmer = unsignedTrimmer(n[1].typ.skipTypes(abstractRange).size)
if p.target == targetPHP:
# XXX prevent multi evaluations
r.res = "(($1 $2) >= 0) ? (($1 $2) >> $3) : ((($1 $2) & 0x7fffffff) >> $3) | (0x40000000 >> ($3 - 1))" % [x.rdLoc, trimmer, y.rdLoc]
else:
r.res = "(($1 $2) >>> $3)" % [x.rdLoc, trimmer, y.rdLoc] r.res = "(($1 $2) >>> $3)" % [x.rdLoc, trimmer, y.rdLoc]
of mCharToStr, mBoolToStr, mIntToStr, mInt64ToStr, mFloatToStr, of mCharToStr, mBoolToStr, mIntToStr, mInt64ToStr, mFloatToStr,
mCStrToStr, mStrToStr, mEnumToStr: mCStrToStr, mStrToStr, mEnumToStr:
if p.target == targetPHP:
if op == mEnumToStr:
var x: TCompRes
gen(p, n.sons[1], x)
r.res = "$#[$#]" % [genEnumInfoPHP(p, n.sons[1].typ), x.rdLoc]
elif op == mCharToStr:
var x: TCompRes
gen(p, n.sons[1], x)
r.res = "chr($#)" % [x.rdLoc]
else:
gen(p, n.sons[1], r)
else:
arithAux(p, n, r, op) arithAux(p, n, r, op)
else: else:
arithAux(p, n, r, op) arithAux(p, n, r, op)
@ -591,7 +542,7 @@ proc genLineDir(p: PProc, n: PNode) =
useMagic(p, "endb") useMagic(p, "endb")
lineF(p, "endb($1);$n", [rope(line)]) lineF(p, "endb($1);$n", [rope(line)])
elif hasFrameInfo(p): elif hasFrameInfo(p):
lineF(p, "F.line = $1;$n" | "$$F['line'] = $1;$n", [rope(line)]) lineF(p, "F.line = $1;$n", [rope(line)])
proc genWhileStmt(p: PProc, n: PNode) = proc genWhileStmt(p: PProc, n: PNode) =
var var
@ -604,12 +555,12 @@ proc genWhileStmt(p: PProc, n: PNode) =
p.blocks[length].id = -p.unique p.blocks[length].id = -p.unique
p.blocks[length].isLoop = true p.blocks[length].isLoop = true
let labl = p.unique.rope let labl = p.unique.rope
lineF(p, "L$1: while (true) {$n" | "while (true) {$n", [labl]) lineF(p, "L$1: while (true) {$n", [labl])
p.nested: gen(p, n.sons[0], cond) p.nested: gen(p, n.sons[0], cond)
lineF(p, "if (!$1) break L$2;$n" | "if (!$1) goto L$2;$n", lineF(p, "if (!$1) break L$2;$n",
[cond.res, labl]) [cond.res, labl])
p.nested: genStmt(p, n.sons[1]) p.nested: genStmt(p, n.sons[1])
lineF(p, "}$n" | "}L$#:;$n", [labl]) lineF(p, "}$n", [labl])
setLen(p.blocks, length) setLen(p.blocks, length)
proc moveInto(p: PProc, src: var TCompRes, dest: TCompRes) = proc moveInto(p: PProc, src: var TCompRes, dest: TCompRes) =
@ -653,26 +604,22 @@ proc genTry(p: PProc, n: PNode, r: var TCompRes) =
var i = 1 var i = 1
var length = sonsLen(n) var length = sonsLen(n)
var catchBranchesExist = length > 1 and n.sons[i].kind == nkExceptBranch var catchBranchesExist = length > 1 and n.sons[i].kind == nkExceptBranch
if catchBranchesExist and p.target == targetJS: if catchBranchesExist:
add(p.body, "++excHandler;" & tnl) add(p.body, "++excHandler;" & tnl)
var tmpFramePtr = rope"F" var tmpFramePtr = rope"F"
if optStackTrace notin p.options: if optStackTrace notin p.options:
tmpFramePtr = p.getTemp(true) tmpFramePtr = p.getTemp(true)
line(p, tmpFramePtr & " = framePtr;" & tnl) line(p, tmpFramePtr & " = framePtr;" & tnl)
lineF(p, "try {$n", []) lineF(p, "try {$n", [])
if p.target == targetPHP and p.globals == nil:
p.globals = "global $lastJSError; global $prevJSError;".rope
var a: TCompRes var a: TCompRes
gen(p, n.sons[0], a) gen(p, n.sons[0], a)
moveInto(p, a, r) moveInto(p, a, r)
var generalCatchBranchExists = false var generalCatchBranchExists = false
let dollar = rope(if p.target == targetJS: "" else: "$") let dollar = rope("")
if p.target == targetJS and catchBranchesExist: if catchBranchesExist:
addf(p.body, "--excHandler;$n} catch (EXC) {$n var prevJSError = lastJSError;$n" & addf(p.body, "--excHandler;$n} catch (EXC) {$n var prevJSError = lastJSError;$n" &
" lastJSError = EXC;$n --excHandler;$n", []) " lastJSError = EXC;$n --excHandler;$n", [])
line(p, "framePtr = $1;$n" % [tmpFramePtr]) line(p, "framePtr = $1;$n" % [tmpFramePtr])
elif p.target == targetPHP:
lineF(p, "} catch (Exception $$EXC) {$n $$prevJSError = $$lastJSError;$n $$lastJSError = $$EXC;$n", [])
while i < length and n.sons[i].kind == nkExceptBranch: while i < length and n.sons[i].kind == nkExceptBranch:
let blen = sonsLen(n.sons[i]) let blen = sonsLen(n.sons[i])
if blen == 1: if blen == 1:
@ -704,18 +651,10 @@ proc genTry(p: PProc, n: PNode, r: var TCompRes) =
line(p, indent & "reraiseException();" & tnl) line(p, indent & "reraiseException();" & tnl)
line(p, "}" & tnl) line(p, "}" & tnl)
addf(p.body, "$1lastJSError = $1prevJSError;$n", [dollar]) addf(p.body, "$1lastJSError = $1prevJSError;$n", [dollar])
if p.target == targetJS:
line(p, "} finally {" & tnl) line(p, "} finally {" & tnl)
line(p, "framePtr = $1;$n" % [tmpFramePtr]) line(p, "framePtr = $1;$n" % [tmpFramePtr])
if p.target == targetPHP:
# XXX ugly hack for PHP codegen
line(p, "}" & tnl)
if i < length and n.sons[i].kind == nkFinally: if i < length and n.sons[i].kind == nkFinally:
genStmt(p, n.sons[i].sons[0]) genStmt(p, n.sons[i].sons[0])
if p.target == targetPHP:
# XXX ugly hack for PHP codegen
line(p, "if($lastJSError) throw($lastJSError);" & tnl)
if p.target == targetJS:
line(p, "}" & tnl) line(p, "}" & tnl)
proc genRaiseStmt(p: PProc, n: PNode) = proc genRaiseStmt(p: PProc, n: PNode) =
@ -737,7 +676,7 @@ proc genCaseJS(p: PProc, n: PNode, r: var TCompRes) =
genLineDir(p, n) genLineDir(p, n)
gen(p, n.sons[0], cond) gen(p, n.sons[0], cond)
let stringSwitch = skipTypes(n.sons[0].typ, abstractVar).kind == tyString let stringSwitch = skipTypes(n.sons[0].typ, abstractVar).kind == tyString
if stringSwitch and p.target == targetJS: if stringSwitch:
useMagic(p, "toJSStr") useMagic(p, "toJSStr")
lineF(p, "switch (toJSStr($1)) {$n", [cond.rdLoc]) lineF(p, "switch (toJSStr($1)) {$n", [cond.rdLoc])
else: else:
@ -789,12 +728,12 @@ proc genBlock(p: PProc, n: PNode, r: var TCompRes) =
sym.loc.k = locOther sym.loc.k = locOther
sym.position = idx+1 sym.position = idx+1
let labl = p.unique let labl = p.unique
lineF(p, "L$1: do {$n" | "", [labl.rope]) lineF(p, "L$1: do {$n", [labl.rope])
setLen(p.blocks, idx + 1) setLen(p.blocks, idx + 1)
p.blocks[idx].id = - p.unique # negative because it isn't used yet p.blocks[idx].id = - p.unique # negative because it isn't used yet
gen(p, n.sons[1], r) gen(p, n.sons[1], r)
setLen(p.blocks, idx) setLen(p.blocks, idx)
lineF(p, "} while(false);$n" | "$nL$#:;$n", [labl.rope]) lineF(p, "} while(false);$n", [labl.rope])
proc genBreakStmt(p: PProc, n: PNode) = proc genBreakStmt(p: PProc, n: PNode) =
var idx: int var idx: int
@ -812,7 +751,7 @@ proc genBreakStmt(p: PProc, n: PNode) =
if idx < 0 or not p.blocks[idx].isLoop: if idx < 0 or not p.blocks[idx].isLoop:
internalError(n.info, "no loop to break") internalError(n.info, "no loop to break")
p.blocks[idx].id = abs(p.blocks[idx].id) # label is used p.blocks[idx].id = abs(p.blocks[idx].id) # label is used
lineF(p, "break L$1;$n" | "goto L$1;$n", [rope(p.blocks[idx].id)]) lineF(p, "break L$1;$n", [rope(p.blocks[idx].id)])
proc genAsmOrEmitStmt(p: PProc, n: PNode) = proc genAsmOrEmitStmt(p: PProc, n: PNode) =
genLineDir(p, n) genLineDir(p, n)
@ -826,7 +765,6 @@ proc genAsmOrEmitStmt(p: PProc, n: PNode) =
let v = it.sym let v = it.sym
# for backwards compatibility we don't deref syms here :-( # for backwards compatibility we don't deref syms here :-(
if v.kind in {skVar, skLet, skTemp, skConst, skResult, skParam, skForVar}: if v.kind in {skVar, skLet, skTemp, skConst, skResult, skParam, skForVar}:
if p.target == targetPHP: p.body.add "$"
p.body.add mangleName(p.module, v) p.body.add mangleName(p.module, v)
else: else:
var r: TCompRes var r: TCompRes
@ -869,24 +807,11 @@ proc generateHeader(p: PProc, typ: PType): Rope =
if isCompileTimeOnly(param.typ): continue if isCompileTimeOnly(param.typ): continue
if result != nil: add(result, ", ") if result != nil: add(result, ", ")
var name = mangleName(p.module, param) var name = mangleName(p.module, param)
if p.target == targetJS:
add(result, name) add(result, name)
if mapType(param.typ) == etyBaseIndex: if mapType(param.typ) == etyBaseIndex:
add(result, ", ") add(result, ", ")
add(result, name) add(result, name)
add(result, "_Idx") add(result, "_Idx")
elif not (i == 1 and param.name.s == "this"):
let k = param.typ.skipTypes({tyGenericInst, tyAlias, tySink}).kind
if k in {tyVar, tyRef, tyPtr, tyLent, tyPointer}:
add(result, "&")
add(result, "$")
add(result, name)
# XXX I think something like this is needed for PHP to really support
# ptr "inside" strings and seq
#if mapType(param.typ) == etyBaseIndex:
# add(result, ", $")
# add(result, name)
# add(result, "_Idx")
proc countJsParams(typ: PType): int = proc countJsParams(typ: PType): int =
for i in countup(1, sonsLen(typ.n) - 1): for i in countup(1, sonsLen(typ.n) - 1):
@ -906,21 +831,10 @@ const
proc needsNoCopy(p: PProc; y: PNode): bool = proc needsNoCopy(p: PProc; y: PNode): bool =
result = (y.kind in nodeKindsNeedNoCopy) or result = (y.kind in nodeKindsNeedNoCopy) or
(skipTypes(y.typ, abstractInst).kind in {tyRef, tyPtr, tyLent, tyVar}) or (skipTypes(y.typ, abstractInst).kind in {tyRef, tyPtr, tyLent, tyVar})
p.target == targetPHP
proc genAsgnAux(p: PProc, x, y: PNode, noCopyNeeded: bool) = proc genAsgnAux(p: PProc, x, y: PNode, noCopyNeeded: bool) =
var a, b: TCompRes var a, b: TCompRes
if p.target == targetPHP and x.kind == nkBracketExpr and
x[0].typ.skipTypes(abstractVar).kind in {tyString, tyCString}:
var c: TCompRes
gen(p, x[0], a)
gen(p, x[1], b)
gen(p, y, c)
lineF(p, "$#[$#] = chr($#);$n", [a.rdLoc, b.rdLoc, c.rdLoc])
return
var xtyp = mapType(p, x.typ) var xtyp = mapType(p, x.typ)
if x.kind == nkHiddenDeref and x.sons[0].kind == nkCall and xtyp != etyObject: if x.kind == nkHiddenDeref and x.sons[0].kind == nkCall and xtyp != etyObject:
@ -968,7 +882,7 @@ proc genAsgnAux(p: PProc, x, y: PNode, noCopyNeeded: bool) =
proc genAsgn(p: PProc, n: PNode) = proc genAsgn(p: PProc, n: PNode) =
genLineDir(p, n) genLineDir(p, n)
genAsgnAux(p, n.sons[0], n.sons[1], noCopyNeeded=p.target == targetPHP) genAsgnAux(p, n.sons[0], n.sons[1], noCopyNeeded=false)
proc genFastAsgn(p: PProc, n: PNode) = proc genFastAsgn(p: PProc, n: PNode) =
genLineDir(p, n) genLineDir(p, n)
@ -991,12 +905,10 @@ proc genSwap(p: PProc, n: PNode) =
let tmp2 = p.getTemp(false) let tmp2 = p.getTemp(false)
if a.typ != etyBaseIndex or b.typ != etyBaseIndex: if a.typ != etyBaseIndex or b.typ != etyBaseIndex:
internalError(n.info, "genSwap") internalError(n.info, "genSwap")
lineF(p, "var $1 = $2; $2 = $3; $3 = $1;$n" | lineF(p, "var $1 = $2; $2 = $3; $3 = $1;$n",
"$1 = $2; $2 = $3; $3 = $1;$n",
[tmp, a.address, b.address]) [tmp, a.address, b.address])
tmp = tmp2 tmp = tmp2
lineF(p, "var $1 = $2; $2 = $3; $3 = $1;" | lineF(p, "var $1 = $2; $2 = $3; $3 = $1;",
"$1 = $2; $2 = $3; $3 = $1;",
[tmp, a.res, b.res]) [tmp, a.res, b.res])
proc getFieldPosition(f: PNode): int = proc getFieldPosition(f: PNode): int =
@ -1011,10 +923,7 @@ proc genFieldAddr(p: PProc, n: PNode, r: var TCompRes) =
let b = if n.kind == nkHiddenAddr: n.sons[0] else: n let b = if n.kind == nkHiddenAddr: n.sons[0] else: n
gen(p, b.sons[0], a) gen(p, b.sons[0], a)
if skipTypes(b.sons[0].typ, abstractVarRange).kind == tyTuple: if skipTypes(b.sons[0].typ, abstractVarRange).kind == tyTuple:
if p.target == targetJS:
r.res = makeJSString("Field" & $getFieldPosition(b.sons[1])) r.res = makeJSString("Field" & $getFieldPosition(b.sons[1]))
else:
r.res = getFieldPosition(b.sons[1]).rope
else: else:
if b.sons[1].kind != nkSym: internalError(b.sons[1].info, "genFieldAddr") if b.sons[1].kind != nkSym: internalError(b.sons[1].info, "genFieldAddr")
var f = b.sons[1].sym var f = b.sons[1].sym
@ -1029,19 +938,13 @@ proc genFieldAccess(p: PProc, n: PNode, r: var TCompRes) =
gen(p, n.sons[0], r) gen(p, n.sons[0], r)
let otyp = skipTypes(n.sons[0].typ, abstractVarRange) let otyp = skipTypes(n.sons[0].typ, abstractVarRange)
if otyp.kind == tyTuple: if otyp.kind == tyTuple:
r.res = ("$1.Field$2" | "$1[$2]") % r.res = ("$1.Field$2") %
[r.res, getFieldPosition(n.sons[1]).rope] [r.res, getFieldPosition(n.sons[1]).rope]
else: else:
if n.sons[1].kind != nkSym: internalError(n.sons[1].info, "genFieldAccess") if n.sons[1].kind != nkSym: internalError(n.sons[1].info, "genFieldAccess")
var f = n.sons[1].sym var f = n.sons[1].sym
if f.loc.r == nil: f.loc.r = mangleName(p.module, f) if f.loc.r == nil: f.loc.r = mangleName(p.module, f)
if p.target == targetJS:
r.res = "$1.$2" % [r.res, f.loc.r] r.res = "$1.$2" % [r.res, f.loc.r]
else:
if {sfImportc, sfExportc} * f.flags != {}:
r.res = "$1->$2" % [r.res, f.loc.r]
else:
r.res = "$1['$2']" % [r.res, f.loc.r]
r.kind = resExpr r.kind = resExpr
proc genAddr(p: PProc, n: PNode, r: var TCompRes) proc genAddr(p: PProc, n: PNode, r: var TCompRes)
@ -1069,12 +972,6 @@ proc genArrayAddr(p: PProc, n: PNode, r: var TCompRes) =
else: first = 0 else: first = 0
if optBoundsCheck in p.options: if optBoundsCheck in p.options:
useMagic(p, "chckIndx") useMagic(p, "chckIndx")
if p.target == targetPHP:
if typ.kind != tyString:
r.res = "chckIndx($1, $2, count($3)-1)-$2" % [b.res, rope(first), a.res]
else:
r.res = "chckIndx($1, $2, strlen($3))-$2" % [b.res, rope(first), a.res]
else:
r.res = "chckIndx($1, $2, $3.length+$2-1)-$2" % [b.res, rope(first), a.res] r.res = "chckIndx($1, $2, $3.length+$2-1)-$2" % [b.res, rope(first), a.res]
elif first != 0: elif first != 0:
r.res = "($1)-$2" % [b.res, rope(first)] r.res = "($1)-$2" % [b.res, rope(first)]
@ -1089,27 +986,11 @@ proc genArrayAccess(p: PProc, n: PNode, r: var TCompRes) =
of tyArray, tyOpenArray, tySequence, tyString, tyCString, tyVarargs: of tyArray, tyOpenArray, tySequence, tyString, tyCString, tyVarargs:
genArrayAddr(p, n, r) genArrayAddr(p, n, r)
of tyTuple: of tyTuple:
if p.target == targetPHP:
genFieldAccess(p, n, r)
return
genFieldAddr(p, n, r) genFieldAddr(p, n, r)
else: internalError(n.info, "expr(nkBracketExpr, " & $ty.kind & ')') else: internalError(n.info, "expr(nkBracketExpr, " & $ty.kind & ')')
r.typ = etyNone r.typ = etyNone
if r.res == nil: internalError(n.info, "genArrayAccess") if r.res == nil: internalError(n.info, "genArrayAccess")
if p.target == targetPHP: if ty.kind == tyCString:
if n.sons[0].kind in nkCallKinds+{nkStrLit..nkTripleStrLit}:
useMagic(p, "nimAt")
if ty.kind in {tyString, tyCString}:
# XXX this needs to be more like substr($1,$2)
r.res = "ord(nimAt($1, $2))" % [r.address, r.res]
else:
r.res = "nimAt($1, $2)" % [r.address, r.res]
elif ty.kind in {tyString, tyCString}:
# XXX this needs to be more like substr($1,$2)
r.res = "ord(@$1[$2])" % [r.address, r.res]
else:
r.res = "$1[$2]" % [r.address, r.res]
elif ty.kind == tyCString:
r.res = "$1.charCodeAt($2)" % [r.address, r.res] r.res = "$1.charCodeAt($2)" % [r.address, r.res]
else: else:
r.res = "$1[$2]" % [r.address, r.res] r.res = "$1[$2]" % [r.address, r.res]
@ -1122,7 +1003,7 @@ template isIndirect(x: PSym): bool =
#(mapType(v.typ) != etyObject) and #(mapType(v.typ) != etyObject) and
{sfImportc, sfVolatile, sfExportc} * v.flags == {} and {sfImportc, sfVolatile, sfExportc} * v.flags == {} and
v.kind notin {skProc, skFunc, skConverter, skMethod, skIterator, v.kind notin {skProc, skFunc, skConverter, skMethod, skIterator,
skConst, skTemp, skLet} and p.target == targetJS) skConst, skTemp, skLet})
proc genAddr(p: PProc, n: PNode, r: var TCompRes) = proc genAddr(p: PProc, n: PNode, r: var TCompRes) =
case n.sons[0].kind case n.sons[0].kind
@ -1138,8 +1019,6 @@ proc genAddr(p: PProc, n: PNode, r: var TCompRes) =
r.typ = etyNone r.typ = etyNone
if isIndirect(s): if isIndirect(s):
r.res = s.loc.r & "[0]" r.res = s.loc.r & "[0]"
elif p.target == targetPHP:
r.res = "&" & s.loc.r
else: else:
r.res = s.loc.r r.res = s.loc.r
r.address = nil r.address = nil
@ -1180,15 +1059,7 @@ proc genAddr(p: PProc, n: PNode, r: var TCompRes) =
else: internalError(n.sons[0].info, "genAddr: " & $n.sons[0].kind) else: internalError(n.sons[0].info, "genAddr: " & $n.sons[0].kind)
proc thisParam(p: PProc; typ: PType): PType = proc thisParam(p: PProc; typ: PType): PType =
if p.target == targetPHP: discard
# XXX Might be very useful for the JS backend too?
let typ = skipTypes(typ, abstractInst)
assert(typ.kind == tyProc)
if 1 < sonsLen(typ.n):
assert(typ.n.sons[1].kind == nkSym)
let param = typ.n.sons[1].sym
if param.name.s == "this":
result = param.typ.skipTypes(abstractVar)
proc attachProc(p: PProc; content: Rope; s: PSym) = proc attachProc(p: PProc; content: Rope; s: PSym) =
let otyp = thisParam(p, s.typ) let otyp = thisParam(p, s.typ)
@ -1220,7 +1091,6 @@ proc genSym(p: PProc, n: PNode, r: var TCompRes) =
of skVar, skLet, skParam, skTemp, skResult, skForVar: of skVar, skLet, skParam, skTemp, skResult, skForVar:
if s.loc.r == nil: if s.loc.r == nil:
internalError(n.info, "symbol has no generated name: " & s.name.s) internalError(n.info, "symbol has no generated name: " & s.name.s)
if p.target == targetJS:
let k = mapType(p, s.typ) let k = mapType(p, s.typ)
if k == etyBaseIndex: if k == etyBaseIndex:
r.typ = etyBaseIndex r.typ = etyBaseIndex
@ -1234,24 +1104,13 @@ proc genSym(p: PProc, n: PNode, r: var TCompRes) =
r.res = "$1[0]" % [s.loc.r] r.res = "$1[0]" % [s.loc.r]
else: else:
r.res = s.loc.r r.res = s.loc.r
else:
r.res = "$" & s.loc.r
if sfGlobal in s.flags:
p.declareGlobal(s.id, r.res)
of skConst: of skConst:
genConstant(p, s) genConstant(p, s)
if s.loc.r == nil: if s.loc.r == nil:
internalError(n.info, "symbol has no generated name: " & s.name.s) internalError(n.info, "symbol has no generated name: " & s.name.s)
if p.target == targetJS:
r.res = s.loc.r r.res = s.loc.r
else:
r.res = "$" & s.loc.r
p.declareGlobal(s.id, r.res)
of skProc, skFunc, skConverter, skMethod: of skProc, skFunc, skConverter, skMethod:
discard mangleName(p.module, s) discard mangleName(p.module, s)
if p.target == targetPHP and r.kind != resCallee:
r.res = makeJsString($s.loc.r)
else:
r.res = s.loc.r r.res = s.loc.r
if lfNoDecl in s.loc.flags or s.magic != mNone or if lfNoDecl in s.loc.flags or s.magic != mNone or
{sfImportc, sfInfixCall} * s.flags != {}: {sfImportc, sfInfixCall} * s.flags != {}:
@ -1421,10 +1280,8 @@ proc genInfixCall(p: PProc, n: PNode, r: var TCompRes) =
r.res = "$1[$2]" % [r.address, r.res] r.res = "$1[$2]" % [r.address, r.res]
r.address = nil r.address = nil
r.typ = etyNone r.typ = etyNone
add(r.res, "." | "->") add(r.res, ".")
var op: TCompRes var op: TCompRes
if p.target == targetPHP:
op.kind = resCallee
gen(p, n.sons[0], op) gen(p, n.sons[0], op)
add(r.res, op.res) add(r.res, op.res)
genArgs(p, n, r, 2) genArgs(p, n, r, 2)
@ -1433,28 +1290,21 @@ proc genCall(p: PProc, n: PNode, r: var TCompRes) =
if n.sons[0].kind == nkSym and thisParam(p, n.sons[0].typ) != nil: if n.sons[0].kind == nkSym and thisParam(p, n.sons[0].typ) != nil:
genInfixCall(p, n, r) genInfixCall(p, n, r)
return return
if p.target == targetPHP:
r.kind = resCallee
gen(p, n.sons[0], r) gen(p, n.sons[0], r)
genArgs(p, n, r) genArgs(p, n, r)
proc genEcho(p: PProc, n: PNode, r: var TCompRes) = proc genEcho(p: PProc, n: PNode, r: var TCompRes) =
let n = n[1].skipConv let n = n[1].skipConv
internalAssert n.kind == nkBracket internalAssert n.kind == nkBracket
if p.target == targetJS:
useMagic(p, "toJSStr") # Used in rawEcho useMagic(p, "toJSStr") # Used in rawEcho
useMagic(p, "rawEcho") useMagic(p, "rawEcho")
elif n.len == 0: add(r.res, "rawEcho(")
r.kind = resExpr
add(r.res, """print("\n")""")
return
add(r.res, "rawEcho(" | "print(")
for i in countup(0, sonsLen(n) - 1): for i in countup(0, sonsLen(n) - 1):
let it = n.sons[i] let it = n.sons[i]
if it.typ.isCompileTimeOnly: continue if it.typ.isCompileTimeOnly: continue
if i > 0: add(r.res, ", " | ".") if i > 0: add(r.res, ", ")
genArgNoParam(p, it, r) genArgNoParam(p, it, r)
add(r.res, ")" | """."\n")""") add(r.res, ")")
r.kind = resExpr r.kind = resExpr
proc putToSeq(s: string, indirect: bool): Rope = proc putToSeq(s: string, indirect: bool): Rope =
@ -1474,10 +1324,7 @@ proc createRecordVarAux(p: PProc, rec: PNode, excludedFieldIDs: IntSet, output:
of nkSym: of nkSym:
if rec.sym.id notin excludedFieldIDs: if rec.sym.id notin excludedFieldIDs:
if output.len > 0: output.add(", ") if output.len > 0: output.add(", ")
if p.target == targetJS:
output.addf("$#: ", [mangleName(p.module, rec.sym)]) output.addf("$#: ", [mangleName(p.module, rec.sym)])
else:
output.addf("'$#' => ", [mangleName(p.module, rec.sym)])
output.add(createVar(p, rec.sym.typ, false)) output.add(createVar(p, rec.sym.typ, false))
else: internalError(rec.info, "createRecordVarAux") else: internalError(rec.info, "createRecordVarAux")
@ -1485,7 +1332,7 @@ proc createObjInitList(p: PProc, typ: PType, excludedFieldIDs: IntSet, output: v
var t = typ var t = typ
if objHasTypeField(t): if objHasTypeField(t):
if output.len > 0: output.add(", ") if output.len > 0: output.add(", ")
addf(output, "m_type: $1" | "'m_type' => $#", [genTypeInfo(p, t)]) addf(output, "m_type: $1", [genTypeInfo(p, t)])
while t != nil: while t != nil:
t = t.skipTypes(skipPtrs) t = t.skipTypes(skipPtrs)
createRecordVarAux(p, t.n, excludedFieldIDs, output) createRecordVarAux(p, t.n, excludedFieldIDs, output)
@ -1514,32 +1361,31 @@ proc createVar(p: PProc, typ: PType, indirect: bool): Rope =
of tyRange, tyGenericInst, tyAlias, tySink: of tyRange, tyGenericInst, tyAlias, tySink:
result = createVar(p, lastSon(typ), indirect) result = createVar(p, lastSon(typ), indirect)
of tySet: of tySet:
result = putToSeq("{}" | "array()", indirect) result = putToSeq("{}", indirect)
of tyBool: of tyBool:
result = putToSeq("false", indirect) result = putToSeq("false", indirect)
of tyArray: of tyArray:
let length = int(lengthOrd(t)) let length = int(lengthOrd(t))
let e = elemType(t) let e = elemType(t)
let jsTyp = arrayTypeForElemType(e) let jsTyp = arrayTypeForElemType(e)
if not jsTyp.isNil and p.target == targetJS: if not jsTyp.isNil:
result = "new $1($2)" % [rope(jsTyp), rope(length)] result = "new $1($2)" % [rope(jsTyp), rope(length)]
elif length > 32: elif length > 32:
useMagic(p, "arrayConstr") useMagic(p, "arrayConstr")
# XXX: arrayConstr depends on nimCopy. This line shouldn't be necessary. # XXX: arrayConstr depends on nimCopy. This line shouldn't be necessary.
if p.target == targetJS: useMagic(p, "nimCopy") useMagic(p, "nimCopy")
result = "arrayConstr($1, $2, $3)" % [rope(length), result = "arrayConstr($1, $2, $3)" % [rope(length),
createVar(p, e, false), genTypeInfo(p, e)] createVar(p, e, false), genTypeInfo(p, e)]
else: else:
result = rope("[" | "array(") result = rope("[")
var i = 0 var i = 0
while i < length: while i < length:
if i > 0: add(result, ", ") if i > 0: add(result, ", ")
add(result, createVar(p, e, false)) add(result, createVar(p, e, false))
inc(i) inc(i)
add(result, "]" | ")") add(result, "]")
if indirect: result = "[$1]" % [result] if indirect: result = "[$1]" % [result]
of tyTuple: of tyTuple:
if p.target == targetJS:
result = rope("{") result = rope("{")
for i in 0..<t.sonsLen: for i in 0..<t.sonsLen:
if i > 0: add(result, ", ") if i > 0: add(result, ", ")
@ -1547,16 +1393,10 @@ proc createVar(p: PProc, typ: PType, indirect: bool): Rope =
createVar(p, t.sons[i], false)]) createVar(p, t.sons[i], false)])
add(result, "}") add(result, "}")
if indirect: result = "[$1]" % [result] if indirect: result = "[$1]" % [result]
else:
result = rope("array(")
for i in 0..<t.sonsLen:
if i > 0: add(result, ", ")
add(result, createVar(p, t.sons[i], false))
add(result, ")")
of tyObject: of tyObject:
var initList: Rope var initList: Rope
createObjInitList(p, t, initIntSet(), initList) createObjInitList(p, t, initIntSet(), initList)
result = ("{$1}" | "array($#)") % [initList] result = ("{$1}") % [initList]
if indirect: result = "[$1]" % [result] if indirect: result = "[$1]" % [result]
of tyVar, tyPtr, tyLent, tyRef: of tyVar, tyPtr, tyLent, tyRef:
if mapType(p, t) == etyBaseIndex: if mapType(p, t) == etyBaseIndex:
@ -1597,7 +1437,7 @@ proc genVarInit(p: PProc, v: PSym, n: PNode) =
varCode = v.constraint.strVal varCode = v.constraint.strVal
if n.kind == nkEmpty: if n.kind == nkEmpty:
lineF(p, varCode & " = $3;$n" | "$$$2 = $3;$n", lineF(p, varCode & " = $3;$n",
[returnType, varName, createVar(p, v.typ, isIndirect(v))]) [returnType, varName, createVar(p, v.typ, isIndirect(v))])
if v.typ.kind in {tyVar, tyPtr, tyLent, tyRef} and mapType(p, v.typ) == etyBaseIndex: if v.typ.kind in {tyVar, tyPtr, tyLent, tyRef} and mapType(p, v.typ) == etyBaseIndex:
lineF(p, "var $1_Idx = 0;$n", [varName]) lineF(p, "var $1_Idx = 0;$n", [varName])
@ -1632,7 +1472,7 @@ proc genVarInit(p: PProc, v: PSym, n: PNode) =
if isIndirect(v): if isIndirect(v):
lineF(p, varCode & " = [$3];$n", [returnType, v.loc.r, s]) lineF(p, varCode & " = [$3];$n", [returnType, v.loc.r, s])
else: else:
lineF(p, varCode & " = $3;$n" | "$$$2 = $3;$n", [returnType, v.loc.r, s]) lineF(p, varCode & " = $3;$n", [returnType, v.loc.r, s])
if useReloadingGuard: if useReloadingGuard:
lineF(p, "}$n") lineF(p, "}$n")
@ -1665,18 +1505,14 @@ proc genNew(p: PProc, n: PNode) =
var a: TCompRes var a: TCompRes
gen(p, n.sons[1], a) gen(p, n.sons[1], a)
var t = skipTypes(n.sons[1].typ, abstractVar).sons[0] var t = skipTypes(n.sons[1].typ, abstractVar).sons[0]
if p.target == targetJS:
lineF(p, "$1 = $2;$n", [a.res, createVar(p, t, false)]) lineF(p, "$1 = $2;$n", [a.res, createVar(p, t, false)])
else:
lineF(p, "$3 = $2; $1 = &$3;$n", [a.res, createVar(p, t, false), getTemp(p)])
proc genNewSeq(p: PProc, n: PNode) = proc genNewSeq(p: PProc, n: PNode) =
var x, y: TCompRes var x, y: TCompRes
gen(p, n.sons[1], x) gen(p, n.sons[1], x)
gen(p, n.sons[2], y) gen(p, n.sons[2], y)
let t = skipTypes(n.sons[1].typ, abstractVar).sons[0] let t = skipTypes(n.sons[1].typ, abstractVar).sons[0]
lineF(p, "$1 = new Array($2); for (var i=0;i<$2;++i) {$1[i]=$3;}" | lineF(p, "$1 = new Array($2); for (var i=0;i<$2;++i) {$1[i]=$3;}", [
"$1 = array(); for ($$i=0;$$i<$2;++$$i) {$1[]=$3;}", [
x.rdLoc, y.rdLoc, createVar(p, t, false)]) x.rdLoc, y.rdLoc, createVar(p, t, false)])
proc genOrd(p: PProc, n: PNode, r: var TCompRes) = proc genOrd(p: PProc, n: PNode, r: var TCompRes) =
@ -1708,21 +1544,6 @@ proc genConStrStr(p: PProc, n: PNode, r: var TCompRes) =
else: else:
r.res.add("$1)" % [a.res]) r.res.add("$1)" % [a.res])
proc genConStrStrPHP(p: PProc, n: PNode, r: var TCompRes) =
var a: TCompRes
gen(p, n.sons[1], a)
r.kind = resExpr
if skipTypes(n.sons[1].typ, abstractVarRange).kind == tyChar:
r.res.add("chr($1)" % [a.res])
else:
r.res.add(a.res)
for i in countup(2, sonsLen(n) - 1):
gen(p, n.sons[i], a)
if skipTypes(n.sons[i].typ, abstractVarRange).kind == tyChar:
r.res.add(".chr($1)" % [a.res])
else:
r.res.add(".$1" % [a.res])
proc genToArray(p: PProc; n: PNode; r: var TCompRes) = proc genToArray(p: PProc; n: PNode; r: var TCompRes) =
# we map mArray to PHP's array constructor, a mild hack: # we map mArray to PHP's array constructor, a mild hack:
var a, b: TCompRes var a, b: TCompRes
@ -1766,9 +1587,6 @@ proc genReprAux(p: PProc, n: PNode, r: var TCompRes, magic: string, typ: Rope =
add(r.res, ")") add(r.res, ")")
proc genRepr(p: PProc, n: PNode, r: var TCompRes) = proc genRepr(p: PProc, n: PNode, r: var TCompRes) =
if p.target == targetPHP:
localError(n.info, "'repr' not available for PHP backend")
return
let t = skipTypes(n.sons[1].typ, abstractVarRange) let t = skipTypes(n.sons[1].typ, abstractVarRange)
case t.kind: case t.kind:
of tyInt..tyInt64, tyUInt..tyUInt64: of tyInt..tyInt64, tyUInt..tyUInt64:
@ -1828,23 +1646,16 @@ proc genMagic(p: PProc, n: PNode, r: var TCompRes) =
if not (optOverflowCheck in p.options): unaryExpr(p, n, r, "", "$1 - 1") if not (optOverflowCheck in p.options): unaryExpr(p, n, r, "", "$1 - 1")
else: unaryExpr(p, n, r, "subInt", "subInt($1, 1)") else: unaryExpr(p, n, r, "subInt", "subInt($1, 1)")
of mAppendStrCh: of mAppendStrCh:
if p.target == targetJS:
binaryExpr(p, n, r, "addChar", binaryExpr(p, n, r, "addChar",
"if ($1 != null) { addChar($1, $2); } else { $1 = [$2, 0]; }") "if ($1 != null) { addChar($1, $2); } else { $1 = [$2, 0]; }")
else:
binaryExpr(p, n, r, "", "$1 .= chr($2)")
of mAppendStrStr: of mAppendStrStr:
if p.target == targetJS:
if skipTypes(n.sons[1].typ, abstractVarRange).kind == tyCString: if skipTypes(n.sons[1].typ, abstractVarRange).kind == tyCString:
binaryExpr(p, n, r, "", "if ($1 != null) { $1 += $2; } else { $1 = $2; }") binaryExpr(p, n, r, "", "if ($1 != null) { $1 += $2; } else { $1 = $2; }")
else: else:
binaryExpr(p, n, r, "", binaryExpr(p, n, r, "",
"if ($1 != null) { $1 = ($1.slice(0, -1)).concat($2); } else { $1 = $2;}") "if ($1 != null) { $1 = ($1.slice(0, -1)).concat($2); } else { $1 = $2;}")
# XXX: make a copy of $2, because of Javascript's sucking semantics # XXX: make a copy of $2, because of Javascript's sucking semantics
else:
binaryExpr(p, n, r, "", "$1 .= $2;")
of mAppendSeqElem: of mAppendSeqElem:
if p.target == targetJS:
var x, y: TCompRes var x, y: TCompRes
gen(p, n.sons[1], x) gen(p, n.sons[1], x)
gen(p, n.sons[2], y) gen(p, n.sons[2], y)
@ -1857,28 +1668,14 @@ proc genMagic(p: PProc, n: PNode, r: var TCompRes) =
[c, y.rdLoc, genTypeInfo(p, n[2].typ)]) [c, y.rdLoc, genTypeInfo(p, n[2].typ)])
r.res = "if ($1 != null) { $1.push($2); } else { $1 = [$2]; }" % [x.rdLoc, c] r.res = "if ($1 != null) { $1.push($2); } else { $1 = [$2]; }" % [x.rdLoc, c]
r.kind = resExpr r.kind = resExpr
else:
binaryExpr(p, n, r, "", "$1[] = $2")
of mConStrStr: of mConStrStr:
if p.target == targetJS:
genConStrStr(p, n, r) genConStrStr(p, n, r)
else:
genConStrStrPHP(p, n, r)
of mEqStr: of mEqStr:
if p.target == targetJS:
binaryExpr(p, n, r, "eqStrings", "eqStrings($1, $2)") binaryExpr(p, n, r, "eqStrings", "eqStrings($1, $2)")
else:
binaryExpr(p, n, r, "", "($1 == $2)")
of mLeStr: of mLeStr:
if p.target == targetJS:
binaryExpr(p, n, r, "cmpStrings", "(cmpStrings($1, $2) <= 0)") binaryExpr(p, n, r, "cmpStrings", "(cmpStrings($1, $2) <= 0)")
else:
binaryExpr(p, n, r, "", "($1 <= $2)")
of mLtStr: of mLtStr:
if p.target == targetJS:
binaryExpr(p, n, r, "cmpStrings", "(cmpStrings($1, $2) < 0)") binaryExpr(p, n, r, "cmpStrings", "(cmpStrings($1, $2) < 0)")
else:
binaryExpr(p, n, r, "", "($1 < $2)")
of mIsNil: unaryExpr(p, n, r, "", "($1 === null)") of mIsNil: unaryExpr(p, n, r, "", "($1 === null)")
of mEnumToStr: genRepr(p, n, r) of mEnumToStr: genRepr(p, n, r)
of mNew, mNewFinalize: genNew(p, n) of mNew, mNewFinalize: genNew(p, n)
@ -1886,24 +1683,20 @@ proc genMagic(p: PProc, n: PNode, r: var TCompRes) =
of mChr, mArrToSeq: gen(p, n.sons[1], r) # nothing to do of mChr, mArrToSeq: gen(p, n.sons[1], r) # nothing to do
of mOrd: genOrd(p, n, r) of mOrd: genOrd(p, n, r)
of mLengthStr: of mLengthStr:
if p.target == targetJS and n.sons[1].typ.skipTypes(abstractInst).kind == tyCString: if n.sons[1].typ.skipTypes(abstractInst).kind == tyCString:
unaryExpr(p, n, r, "", "($1 != null ? $1.length : 0)") unaryExpr(p, n, r, "", "($1 != null ? $1.length : 0)")
else: else:
unaryExpr(p, n, r, "", "($1 != null ? $1.length-1 : 0)" | unaryExpr(p, n, r, "", "($1 != null ? $1.length-1 : 0)")
"strlen($1)") of mXLenStr: unaryExpr(p, n, r, "", "$1.length-1")
of mXLenStr: unaryExpr(p, n, r, "", "$1.length-1" | "strlen($1)")
of mLengthSeq, mLengthOpenArray, mLengthArray: of mLengthSeq, mLengthOpenArray, mLengthArray:
unaryExpr(p, n, r, "", "($1 != null ? $1.length : 0)" | unaryExpr(p, n, r, "", "($1 != null ? $1.length : 0)")
"count($1)")
of mXLenSeq: of mXLenSeq:
unaryExpr(p, n, r, "", "$1.length" | "count($1)") unaryExpr(p, n, r, "", "$1.length")
of mHigh: of mHigh:
if skipTypes(n.sons[1].typ, abstractVar).kind == tyString: if skipTypes(n.sons[1].typ, abstractVar).kind == tyString:
unaryExpr(p, n, r, "", "($1 != null ? ($1.length-2) : -1)" | unaryExpr(p, n, r, "", "($1 != null ? ($1.length-2) : -1)")
"(strlen($1)-1)")
else: else:
unaryExpr(p, n, r, "", "($1 != null ? ($1.length-1) : -1)" | unaryExpr(p, n, r, "", "($1 != null ? ($1.length-1) : -1)")
"(count($1)-1)")
of mInc: of mInc:
if n[1].typ.skipTypes(abstractRange).kind in tyUInt .. tyUInt64: if n[1].typ.skipTypes(abstractRange).kind in tyUInt .. tyUInt64:
binaryUintExpr(p, n, r, "+", true) binaryUintExpr(p, n, r, "+", true)
@ -1917,7 +1710,7 @@ proc genMagic(p: PProc, n: PNode, r: var TCompRes) =
if optOverflowCheck notin p.options: binaryExpr(p, n, r, "", "$1 -= $2") if optOverflowCheck notin p.options: binaryExpr(p, n, r, "", "$1 -= $2")
else: binaryExpr(p, n, r, "subInt", "$1 = subInt($1, $2)") else: binaryExpr(p, n, r, "subInt", "$1 = subInt($1, $2)")
of mSetLengthStr: of mSetLengthStr:
binaryExpr(p, n, r, "", "$1.length = $2+1; $1[$1.length-1] = 0" | "$1 = substr($1, 0, $2)") binaryExpr(p, n, r, "", "$1.length = $2+1; $1[$1.length-1] = 0")
of mSetLengthSeq: of mSetLengthSeq:
var x, y: TCompRes var x, y: TCompRes
gen(p, n.sons[1], x) gen(p, n.sons[1], x)
@ -1934,50 +1727,23 @@ proc genMagic(p: PProc, n: PNode, r: var TCompRes) =
of mPlusSet: binaryExpr(p, n, r, "SetPlus", "SetPlus($1, $2)") of mPlusSet: binaryExpr(p, n, r, "SetPlus", "SetPlus($1, $2)")
of mMinusSet: binaryExpr(p, n, r, "SetMinus", "SetMinus($1, $2)") of mMinusSet: binaryExpr(p, n, r, "SetMinus", "SetMinus($1, $2)")
of mIncl: binaryExpr(p, n, r, "", "$1[$2] = true") of mIncl: binaryExpr(p, n, r, "", "$1[$2] = true")
of mExcl: binaryExpr(p, n, r, "", "delete $1[$2]" | "unset $1[$2]") of mExcl: binaryExpr(p, n, r, "", "delete $1[$2]")
of mInSet: of mInSet:
if p.target == targetJS:
binaryExpr(p, n, r, "", "($1[$2] != undefined)") binaryExpr(p, n, r, "", "($1[$2] != undefined)")
else:
let s = n.sons[1]
if s.kind == nkCurly:
var a, b, x: TCompRes
gen(p, n.sons[2], x)
r.res = rope("(")
r.kind = resExpr
for i in countup(0, sonsLen(s) - 1):
if i > 0: add(r.res, " || ")
var it = s.sons[i]
if it.kind == nkRange:
gen(p, it.sons[0], a)
gen(p, it.sons[1], b)
addf(r.res, "($1 >= $2 && $1 <= $3)", [x.res, a.res, b.res,])
else:
gen(p, it, a)
addf(r.res, "($1 == $2)", [x.res, a.res])
add(r.res, ")")
else:
binaryExpr(p, n, r, "", "isset($1[$2])")
of mNewSeq: genNewSeq(p, n) of mNewSeq: genNewSeq(p, n)
of mNewSeqOfCap: unaryExpr(p, n, r, "", "[]" | "array()") of mNewSeqOfCap: unaryExpr(p, n, r, "", "[]")
of mOf: genOf(p, n, r) of mOf: genOf(p, n, r)
of mReset: genReset(p, n) of mReset: genReset(p, n)
of mEcho: genEcho(p, n, r) of mEcho: genEcho(p, n, r)
of mNLen..mNError, mSlurp, mStaticExec: of mNLen..mNError, mSlurp, mStaticExec:
localError(n.info, errXMustBeCompileTime, n.sons[0].sym.name.s) localError(n.info, errXMustBeCompileTime, n.sons[0].sym.name.s)
of mCopyStr: of mCopyStr:
binaryExpr(p, n, r, "", "($1.slice($2))" | "substr($1, $2)") binaryExpr(p, n, r, "", "($1.slice($2))")
of mCopyStrLast: of mCopyStrLast:
if p.target == targetJS:
ternaryExpr(p, n, r, "", "($1.slice($2, ($3)+1).concat(0))") ternaryExpr(p, n, r, "", "($1.slice($2, ($3)+1).concat(0))")
else:
ternaryExpr(p, n, r, "nimSubstr", "nimSubstr($#, $#, $#)")
of mNewString: unaryExpr(p, n, r, "mnewString", "mnewString($1)") of mNewString: unaryExpr(p, n, r, "mnewString", "mnewString($1)")
of mNewStringOfCap: of mNewStringOfCap:
if p.target == targetJS:
unaryExpr(p, n, r, "mnewString", "mnewString(0)") unaryExpr(p, n, r, "mnewString", "mnewString(0)")
else:
unaryExpr(p, n, r, "", "''")
of mDotDot: of mDotDot:
genProcForSymIfNeeded(p, n.sons[0].sym) genProcForSymIfNeeded(p, n.sons[0].sym)
genCall(p, n, r) genCall(p, n, r)
@ -1985,8 +1751,7 @@ proc genMagic(p: PProc, n: PNode, r: var TCompRes) =
useMagic(p, "nimParseBiggestFloat") useMagic(p, "nimParseBiggestFloat")
genCall(p, n, r) genCall(p, n, r)
of mArray: of mArray:
if p.target == targetPHP: genToArray(p, n, r) genCall(p, n, r)
else: genCall(p, n, r)
else: else:
genCall(p, n, r) genCall(p, n, r)
#else internalError(e.info, 'genMagic: ' + magicToStr[op]); #else internalError(e.info, 'genMagic: ' + magicToStr[op]);
@ -2003,13 +1768,13 @@ proc genSetConstr(p: PProc, n: PNode, r: var TCompRes) =
if it.kind == nkRange: if it.kind == nkRange:
gen(p, it.sons[0], a) gen(p, it.sons[0], a)
gen(p, it.sons[1], b) gen(p, it.sons[1], b)
addf(r.res, "[$1, $2]" | "array($#,$#)", [a.res, b.res]) addf(r.res, "[$1, $2]", [a.res, b.res])
else: else:
gen(p, it, a) gen(p, it, a)
add(r.res, a.res) add(r.res, a.res)
add(r.res, ")") add(r.res, ")")
# emit better code for constant sets: # emit better code for constant sets:
if p.target == targetJS and isDeepConstExpr(n): if isDeepConstExpr(n):
inc(p.g.unique) inc(p.g.unique)
let tmp = rope("ConstSet") & rope(p.g.unique) let tmp = rope("ConstSet") & rope(p.g.unique)
addf(p.g.constants, "var $1 = $2;$n", [tmp, r.res]) addf(p.g.constants, "var $1 = $2;$n", [tmp, r.res])
@ -2017,25 +1782,25 @@ proc genSetConstr(p: PProc, n: PNode, r: var TCompRes) =
proc genArrayConstr(p: PProc, n: PNode, r: var TCompRes) = proc genArrayConstr(p: PProc, n: PNode, r: var TCompRes) =
var a: TCompRes var a: TCompRes
r.res = rope("[" | "array(") r.res = rope("[")
r.kind = resExpr r.kind = resExpr
for i in countup(0, sonsLen(n) - 1): for i in countup(0, sonsLen(n) - 1):
if i > 0: add(r.res, ", ") if i > 0: add(r.res, ", ")
gen(p, n.sons[i], a) gen(p, n.sons[i], a)
add(r.res, a.res) add(r.res, a.res)
add(r.res, "]" | ")") add(r.res, "]")
proc genTupleConstr(p: PProc, n: PNode, r: var TCompRes) = proc genTupleConstr(p: PProc, n: PNode, r: var TCompRes) =
var a: TCompRes var a: TCompRes
r.res = rope("{" | "array(") r.res = rope("{")
r.kind = resExpr r.kind = resExpr
for i in countup(0, sonsLen(n) - 1): for i in countup(0, sonsLen(n) - 1):
if i > 0: add(r.res, ", ") if i > 0: add(r.res, ", ")
var it = n.sons[i] var it = n.sons[i]
if it.kind == nkExprColonExpr: it = it.sons[1] if it.kind == nkExprColonExpr: it = it.sons[1]
gen(p, it, a) gen(p, it, a)
addf(r.res, "Field$#: $#" | "$2", [i.rope, a.res]) addf(r.res, "Field$#: $#", [i.rope, a.res])
r.res.add("}" | ")") r.res.add("}")
proc genObjConstr(p: PProc, n: PNode, r: var TCompRes) = proc genObjConstr(p: PProc, n: PNode, r: var TCompRes) =
var a: TCompRes var a: TCompRes
@ -2059,10 +1824,10 @@ proc genObjConstr(p: PProc, n: PNode, r: var TCompRes) =
else: else:
useMagic(p, "nimCopy") useMagic(p, "nimCopy")
a.res = "nimCopy(null, $1, $2)" % [a.rdLoc, genTypeInfo(p, typ)] a.res = "nimCopy(null, $1, $2)" % [a.rdLoc, genTypeInfo(p, typ)]
addf(initList, "$#: $#" | "'$#' => $#" , [f.loc.r, a.res]) addf(initList, "$#: $#", [f.loc.r, a.res])
let t = skipTypes(n.typ, abstractInst + skipPtrs) let t = skipTypes(n.typ, abstractInst + skipPtrs)
createObjInitList(p, t, fieldIDs, initList) createObjInitList(p, t, fieldIDs, initList)
r.res = ("{$1}" | "array($#)") % [initList] r.res = ("{$1}") % [initList]
proc genConv(p: PProc, n: PNode, r: var TCompRes) = proc genConv(p: PProc, n: PNode, r: var TCompRes) =
var dest = skipTypes(n.typ, abstractVarRange) var dest = skipTypes(n.typ, abstractVarRange)
@ -2125,18 +1890,17 @@ proc genReturnStmt(p: PProc, n: PNode) =
genStmt(p, n.sons[0]) genStmt(p, n.sons[0])
else: else:
genLineDir(p, n) genLineDir(p, n)
lineF(p, "break BeforeRet;$n" | "goto BeforeRet;$n", []) lineF(p, "break BeforeRet;$n", [])
proc frameCreate(p: PProc; procname, filename: Rope): Rope = proc frameCreate(p: PProc; procname, filename: Rope): Rope =
let frameFmt = let frameFmt =
"var F={procname:$1,prev:framePtr,filename:$2,line:0};$n" | "var F={procname:$1,prev:framePtr,filename:$2,line:0};$n"
"global $$framePtr; $$F=array('procname'=>$#,'prev'=>$$framePtr,'filename'=>$#,'line'=>0);$n"
result = p.indentLine(frameFmt % [procname, filename]) result = p.indentLine(frameFmt % [procname, filename])
result.add p.indentLine(ropes.`%`("framePtr = F;$n" | "$$framePtr = &$$F;$n", [])) result.add p.indentLine(ropes.`%`("framePtr = F;$n", []))
proc frameDestroy(p: PProc): Rope = proc frameDestroy(p: PProc): Rope =
result = p.indentLine rope(("framePtr = F.prev;" | "$framePtr = $F['prev'];") & tnl) result = p.indentLine rope(("framePtr = F.prev;") & tnl)
proc genProcBody(p: PProc, prc: PSym): Rope = proc genProcBody(p: PProc, prc: PSym): Rope =
if hasFrameInfo(p): if hasFrameInfo(p):
@ -2146,15 +1910,12 @@ proc genProcBody(p: PProc, prc: PSym): Rope =
else: else:
result = nil result = nil
if p.beforeRetNeeded: if p.beforeRetNeeded:
if p.target == targetJS:
result.add p.indentLine(~"BeforeRet: do {$n") result.add p.indentLine(~"BeforeRet: do {$n")
result.add p.body result.add p.body
result.add p.indentLine(~"} while (false);$n") result.add p.indentLine(~"} while (false);$n")
else:
addF(result, "$# BeforeRet:;$n", [p.body])
else: else:
add(result, p.body) add(result, p.body)
if prc.typ.callConv == ccSysCall and p.target == targetJS: if prc.typ.callConv == ccSysCall:
result = ("try {$n$1} catch (e) {$n" & result = ("try {$n$1} catch (e) {$n" &
" alert(\"Unhandled exception:\\n\" + e.message + \"\\n\"$n}") % [result] " alert(\"Unhandled exception:\\n\" + e.message + \"\\n\"$n}") % [result]
if hasFrameInfo(p): if hasFrameInfo(p):
@ -2182,7 +1943,7 @@ proc genProc(oldProc: PProc, prc: PSym): Rope =
resultSym = prc.ast.sons[resultPos].sym resultSym = prc.ast.sons[resultPos].sym
let mname = mangleName(p.module, resultSym) let mname = mangleName(p.module, resultSym)
let resVar = createVar(p, resultSym.typ, isIndirect(resultSym)) let resVar = createVar(p, resultSym.typ, isIndirect(resultSym))
resultAsgn = p.indentLine(("var $# = $#;$n" | "$$$# = $#;$n") % [mname, resVar]) resultAsgn = p.indentLine(("var $# = $#;$n") % [mname, resVar])
if resultSym.typ.kind in {tyVar, tyPtr, tyLent, tyRef} and if resultSym.typ.kind in {tyVar, tyPtr, tyLent, tyRef} and
mapType(p, resultSym.typ) == etyBaseIndex: mapType(p, resultSym.typ) == etyBaseIndex:
resultAsgn.add p.indentLine("var $#_Idx = 0;$n" % [mname]) resultAsgn.add p.indentLine("var $#_Idx = 0;$n" % [mname])
@ -2267,8 +2028,7 @@ proc genCast(p: PProc, n: PNode, r: var TCompRes) =
elif fromUint: elif fromUint:
if src.size == 4 and dest.size == 4: if src.size == 4 and dest.size == 4:
# XXX prevent multi evaluations # XXX prevent multi evaluations
r.res = "($1|0)" % [r.res] | r.res = "($1|0)" % [r.res]
"($1>(float)2147483647?(int)$1-4294967296:$1)" % [r.res]
else: else:
let trimmer = unsignedTrimmer(dest.size) let trimmer = unsignedTrimmer(dest.size)
let minuend = case dest.size let minuend = case dest.size
@ -2304,8 +2064,7 @@ proc gen(p: PProc, n: PNode, r: var TCompRes) =
r.res = rope"null" r.res = rope"null"
r.kind = resExpr r.kind = resExpr
of nkStrLit..nkTripleStrLit: of nkStrLit..nkTripleStrLit:
if skipTypes(n.typ, abstractVarRange).kind == tyString and if skipTypes(n.typ, abstractVarRange).kind == tyString:
p.target == targetJS:
useMagic(p, "makeNimstrLit") useMagic(p, "makeNimstrLit")
r.res = "makeNimstrLit($1)" % [makeJSString(n.strVal)] r.res = "makeNimstrLit($1)" % [makeJSString(n.strVal)]
else: else:
@ -2342,10 +2101,7 @@ proc gen(p: PProc, n: PNode, r: var TCompRes) =
of nkObjConstr: genObjConstr(p, n, r) of nkObjConstr: genObjConstr(p, n, r)
of nkHiddenStdConv, nkHiddenSubConv, nkConv: genConv(p, n, r) of nkHiddenStdConv, nkHiddenSubConv, nkConv: genConv(p, n, r)
of nkAddr, nkHiddenAddr: of nkAddr, nkHiddenAddr:
if p.target == targetJS:
genAddr(p, n, r) genAddr(p, n, r)
else:
gen(p, n.sons[0], r)
of nkDerefExpr, nkHiddenDeref: genDeref(p, n, r) of nkDerefExpr, nkHiddenDeref: genDeref(p, n, r)
of nkBracketExpr: genArrayAccess(p, n, r) of nkBracketExpr: genArrayAccess(p, n, r)
of nkDotExpr: genFieldAccess(p, n, r) of nkDotExpr: genFieldAccess(p, n, r)
@ -2422,8 +2178,7 @@ proc newModule(module: PSym): BModule =
if globals == nil: if globals == nil:
globals = newGlobals() globals = newGlobals()
proc genHeader(target: TTarget): Rope = proc genHeader(): Rope =
if target == targetJS:
result = ( result = (
"/* Generated by the Nim Compiler v$1 */$n" & "/* Generated by the Nim Compiler v$1 */$n" &
"/* (c) " & copyrightYear & " Andreas Rumpf */$n$n" & "/* (c) " & copyrightYear & " Andreas Rumpf */$n$n" &
@ -2439,14 +2194,6 @@ proc genHeader(target: TTarget): Rope =
"if (typeof Float32Array === 'undefined') Float32Array = Array;$n" & "if (typeof Float32Array === 'undefined') Float32Array = Array;$n" &
"if (typeof Float64Array === 'undefined') Float64Array = Array;$n") % "if (typeof Float64Array === 'undefined') Float64Array = Array;$n") %
[rope(VersionAsString)] [rope(VersionAsString)]
else:
result = ("<?php$n" &
"/* Generated by the Nim Compiler v$1 */$n" &
"/* (c) " & copyrightYear & " Andreas Rumpf */$n$n" &
"$$framePtr = null;$n" &
"$$excHandler = 0;$n" &
"$$lastJSError = null;$n") %
[rope(VersionAsString)]
proc genModule(p: PProc, n: PNode) = proc genModule(p: PProc, n: PNode) =
if optStackTrace in p.options: if optStackTrace in p.options:
@ -2513,8 +2260,8 @@ proc myClose(graph: ModuleGraph; b: PPassContext, n: PNode): PNode =
result = myProcess(b, n) result = myProcess(b, n)
var m = BModule(b) var m = BModule(b)
if sfMainModule in m.module.flags: if sfMainModule in m.module.flags:
let ext = if m.target == targetJS: "js" else: "php" let ext = "js"
let f = if globals.classes.len == 0: m.module.filename let f = if globals.classes.len == 0: toFilename(FileIndex m.module.position)
else: "nimsystem" else: "nimsystem"
let code = wholeCode(graph, m) let code = wholeCode(graph, m)
let outfile = let outfile =
@ -2523,7 +2270,7 @@ proc myClose(graph: ModuleGraph; b: PPassContext, n: PNode): PNode =
else: getCurrentDir() / options.outFile else: getCurrentDir() / options.outFile
else: else:
changeFileExt(completeCFilePath(f), ext) changeFileExt(completeCFilePath(f), ext)
discard writeRopeIfNotEqual(genHeader(m.target) & code, outfile) discard writeRopeIfNotEqual(genHeader() & code, outfile)
for obj, content in items(globals.classes): for obj, content in items(globals.classes):
genClass(obj, content, ext) genClass(obj, content, ext)
@ -2533,7 +2280,6 @@ proc myOpenCached(graph: ModuleGraph; s: PSym, rd: PRodReader): PPassContext =
proc myOpen(graph: ModuleGraph; s: PSym; cache: IdentCache): PPassContext = proc myOpen(graph: ModuleGraph; s: PSym; cache: IdentCache): PPassContext =
var r = newModule(s) var r = newModule(s)
r.target = if gCmd == cmdCompileToPHP: targetPHP else: targetJS
result = r result = r
const JSgenPass* = makePass(myOpen, myOpenCached, myProcess, myClose) const JSgenPass* = makePass(myOpen, myOpenCached, myProcess, myClose)

View file

@ -121,26 +121,7 @@ proc genEnumInfo(p: PProc, typ: PType, name: Rope) =
addf(p.g.typeInfo, "$1.base = $2;$n", addf(p.g.typeInfo, "$1.base = $2;$n",
[name, genTypeInfo(p, typ.sons[0])]) [name, genTypeInfo(p, typ.sons[0])])
proc genEnumInfoPHP(p: PProc; t: PType): Rope =
let t = t.skipTypes({tyGenericInst, tyDistinct, tyAlias, tySink})
result = "$$NTI$1" % [rope(t.id)]
p.declareGlobal(t.id, result)
if containsOrIncl(p.g.typeInfoGenerated, t.id): return
let length = sonsLen(t.n)
var s: Rope = nil
for i in countup(0, length - 1):
if (t.n.sons[i].kind != nkSym): internalError(t.n.info, "genEnumInfo")
let field = t.n.sons[i].sym
if i > 0: add(s, ", " & tnl)
let extName = if field.ast == nil: field.name.s else: field.ast.strVal
addf(s, "$# => $#$n",
[rope(field.position), makeJSString(extName)])
prepend(p.g.typeInfo, "$$$# = $#;$n" % [result, s])
proc genTypeInfo(p: PProc, typ: PType): Rope = proc genTypeInfo(p: PProc, typ: PType): Rope =
if p.target == targetPHP:
return makeJSString(typeToString(typ, preferModuleInfo))
let t = typ.skipTypes({tyGenericInst, tyDistinct, tyAlias, tySink}) let t = typ.skipTypes({tyGenericInst, tyDistinct, tyAlias, tySink})
result = "NTI$1" % [rope(t.id)] result = "NTI$1" % [rope(t.id)]
if containsOrIncl(p.g.typeInfoGenerated, t.id): return if containsOrIncl(p.g.typeInfoGenerated, t.id): return

View file

@ -230,7 +230,7 @@ template isIterator*(owner: PSym): bool =
proc liftingHarmful(owner: PSym): bool {.inline.} = proc liftingHarmful(owner: PSym): bool {.inline.} =
## lambda lifting can be harmful for JS-like code generators. ## lambda lifting can be harmful for JS-like code generators.
let isCompileTime = sfCompileTime in owner.flags or owner.kind == skMacro let isCompileTime = sfCompileTime in owner.flags or owner.kind == skMacro
result = gCmd in {cmdCompileToPHP, cmdCompileToJS} and not isCompileTime result = gCmd == cmdCompileToJS and not isCompileTime
proc liftIterSym*(n: PNode; owner: PSym): PNode = proc liftIterSym*(n: PNode; owner: PSym): PNode =
# transforms (iter) to (let env = newClosure[iter](); (iter, env)) # transforms (iter) to (let env = newClosure[iter](); (iter, env))
@ -813,7 +813,7 @@ proc liftLambdas*(fn: PSym, body: PNode; tooEarly: var bool): PNode =
let isCompileTime = sfCompileTime in fn.flags or fn.kind == skMacro let isCompileTime = sfCompileTime in fn.flags or fn.kind == skMacro
if body.kind == nkEmpty or ( if body.kind == nkEmpty or (
gCmd in {cmdCompileToPHP, cmdCompileToJS} and not isCompileTime) or gCmd == cmdCompileToJS and not isCompileTime) or
fn.skipGenericOwner.kind != skModule: fn.skipGenericOwner.kind != skModule:
# ignore forward declaration: # ignore forward declaration:
result = body result = body

View file

@ -133,7 +133,7 @@ type
TErrorHandler* = proc (info: TLineInfo; msg: TMsgKind; arg: string) TErrorHandler* = proc (info: TLineInfo; msg: TMsgKind; arg: string)
TLexer* = object of TBaseLexer TLexer* = object of TBaseLexer
fileIdx*: int32 fileIdx*: FileIndex
indentAhead*: int # if > 0 an indendation has already been read indentAhead*: int # if > 0 an indendation has already been read
# this is needed because scanning comments # this is needed because scanning comments
# needs so much look-ahead # needs so much look-ahead
@ -222,7 +222,7 @@ proc fillToken(L: var TToken) =
L.commentOffsetA = 0 L.commentOffsetA = 0
L.commentOffsetB = 0 L.commentOffsetB = 0
proc openLexer*(lex: var TLexer, fileIdx: int32, inputstream: PLLStream; proc openLexer*(lex: var TLexer, fileIdx: FileIndex, inputstream: PLLStream;
cache: IdentCache) = cache: IdentCache) =
openBaseLexer(lex, inputstream) openBaseLexer(lex, inputstream)
lex.fileIdx = fileidx lex.fileIdx = fileidx
@ -274,7 +274,7 @@ template tokenEnd(tok, pos) {.dirty.} =
when defined(nimsuggest): when defined(nimsuggest):
let colB = getColNumber(L, pos)+1 let colB = getColNumber(L, pos)+1
if L.fileIdx == gTrackPos.fileIndex and gTrackPos.col in colA..colB and if L.fileIdx == gTrackPos.fileIndex and gTrackPos.col in colA..colB and
L.lineNumber == gTrackPos.line and gIdeCmd in {ideSug, ideCon}: L.lineNumber == gTrackPos.line.int and gIdeCmd in {ideSug, ideCon}:
L.cursor = CursorPosition.InToken L.cursor = CursorPosition.InToken
gTrackPos.col = colA.int16 gTrackPos.col = colA.int16
colA = 0 colA = 0
@ -285,9 +285,9 @@ template tokenEndIgnore(tok, pos) =
when defined(nimsuggest): when defined(nimsuggest):
let colB = getColNumber(L, pos) let colB = getColNumber(L, pos)
if L.fileIdx == gTrackPos.fileIndex and gTrackPos.col in colA..colB and if L.fileIdx == gTrackPos.fileIndex and gTrackPos.col in colA..colB and
L.lineNumber == gTrackPos.line and gIdeCmd in {ideSug, ideCon}: L.lineNumber == gTrackPos.line.int and gIdeCmd in {ideSug, ideCon}:
gTrackPos.fileIndex = trackPosInvalidFileIdx gTrackPos.fileIndex = trackPosInvalidFileIdx
gTrackPos.line = -1 gTrackPos.line = 0'u16
colA = 0 colA = 0
when defined(nimpretty): when defined(nimpretty):
tok.offsetB = L.offsetBase + pos tok.offsetB = L.offsetBase + pos
@ -299,7 +299,7 @@ template tokenEndPrevious(tok, pos) =
# the cursor in a string literal or comment: # the cursor in a string literal or comment:
let colB = getColNumber(L, pos) let colB = getColNumber(L, pos)
if L.fileIdx == gTrackPos.fileIndex and gTrackPos.col in colA..colB and if L.fileIdx == gTrackPos.fileIndex and gTrackPos.col in colA..colB and
L.lineNumber == gTrackPos.line and gIdeCmd in {ideSug, ideCon}: L.lineNumber == gTrackPos.line.int and gIdeCmd in {ideSug, ideCon}:
L.cursor = CursorPosition.BeforeToken L.cursor = CursorPosition.BeforeToken
gTrackPos = L.previousToken gTrackPos = L.previousToken
gTrackPosAttached = true gTrackPosAttached = true
@ -1003,6 +1003,10 @@ proc skip(L: var TLexer, tok: var TToken) =
var buf = L.buf var buf = L.buf
tokenBegin(tok, pos) tokenBegin(tok, pos)
tok.strongSpaceA = 0 tok.strongSpaceA = 0
when defined(nimpretty):
var hasComment = false
tok.commentOffsetA = L.offsetBase + pos
tok.commentOffsetB = tok.commentOffsetA
while true: while true:
case buf[pos] case buf[pos]
of ' ': of ' ':
@ -1021,6 +1025,7 @@ proc skip(L: var TLexer, tok: var TToken) =
inc(pos) inc(pos)
inc(indent) inc(indent)
elif buf[pos] == '#' and buf[pos+1] == '[': elif buf[pos] == '#' and buf[pos+1] == '[':
when defined(nimpretty): hasComment = true
skipMultiLineComment(L, tok, pos+2, false) skipMultiLineComment(L, tok, pos+2, false)
pos = L.bufpos pos = L.bufpos
buf = L.buf buf = L.buf
@ -1034,14 +1039,11 @@ proc skip(L: var TLexer, tok: var TToken) =
of '#': of '#':
# do not skip documentation comment: # do not skip documentation comment:
if buf[pos+1] == '#': break if buf[pos+1] == '#': break
when defined(nimpretty): when defined(nimpretty): hasComment = true
tok.commentOffsetA = L.offsetBase + pos
if buf[pos+1] == '[': if buf[pos+1] == '[':
skipMultiLineComment(L, tok, pos+2, false) skipMultiLineComment(L, tok, pos+2, false)
pos = L.bufpos pos = L.bufpos
buf = L.buf buf = L.buf
when defined(nimpretty):
tok.commentOffsetB = L.offsetBase + pos
else: else:
tokenBegin(tok, pos) tokenBegin(tok, pos)
while buf[pos] notin {CR, LF, nimlexbase.EndOfFile}: inc(pos) while buf[pos] notin {CR, LF, nimlexbase.EndOfFile}: inc(pos)
@ -1053,6 +1055,9 @@ proc skip(L: var TLexer, tok: var TToken) =
tokenEndPrevious(tok, pos-1) tokenEndPrevious(tok, pos-1)
L.bufpos = pos L.bufpos = pos
when defined(nimpretty): when defined(nimpretty):
if hasComment:
tok.commentOffsetB = L.offsetBase + pos
tok.tokType = tkComment
if gIndentationWidth <= 0: if gIndentationWidth <= 0:
gIndentationWidth = tok.indent gIndentationWidth = tok.indent
@ -1061,7 +1066,7 @@ proc rawGetTok*(L: var TLexer, tok: var TToken) =
when defined(nimsuggest): when defined(nimsuggest):
# we attach the cursor to the last *strong* token # we attach the cursor to the last *strong* token
if tok.tokType notin weakTokens: if tok.tokType notin weakTokens:
L.previousToken.line = tok.line.int16 L.previousToken.line = tok.line.uint16
L.previousToken.col = tok.col.int16 L.previousToken.col = tok.col.int16
when defined(nimsuggest): when defined(nimsuggest):
@ -1074,6 +1079,10 @@ proc rawGetTok*(L: var TLexer, tok: var TToken) =
else: else:
tok.indent = -1 tok.indent = -1
skip(L, tok) skip(L, tok)
when defined(nimpretty):
if tok.tokType == tkComment:
L.indentAhead = L.currLineIndent
return
var c = L.buf[L.bufpos] var c = L.buf[L.bufpos]
tok.line = L.lineNumber tok.line = L.lineNumber
tok.col = getColNumber(L, L.bufpos) tok.col = getColNumber(L, L.bufpos)
@ -1109,7 +1118,7 @@ proc rawGetTok*(L: var TLexer, tok: var TToken) =
tok.tokType = tkParLe tok.tokType = tkParLe
when defined(nimsuggest): when defined(nimsuggest):
if L.fileIdx == gTrackPos.fileIndex and tok.col < gTrackPos.col and if L.fileIdx == gTrackPos.fileIndex and tok.col < gTrackPos.col and
tok.line == gTrackPos.line and gIdeCmd == ideCon: tok.line == gTrackPos.line.int and gIdeCmd == ideCon:
gTrackPos.col = tok.col.int16 gTrackPos.col = tok.col.int16
of ')': of ')':
tok.tokType = tkParRi tok.tokType = tkParRi
@ -1130,7 +1139,7 @@ proc rawGetTok*(L: var TLexer, tok: var TToken) =
of '.': of '.':
when defined(nimsuggest): when defined(nimsuggest):
if L.fileIdx == gTrackPos.fileIndex and tok.col+1 == gTrackPos.col and if L.fileIdx == gTrackPos.fileIndex and tok.col+1 == gTrackPos.col and
tok.line == gTrackPos.line and gIdeCmd == ideSug: tok.line == gTrackPos.line.int and gIdeCmd == ideSug:
tok.tokType = tkDot tok.tokType = tkDot
L.cursor = CursorPosition.InToken L.cursor = CursorPosition.InToken
gTrackPos.col = tok.col.int16 gTrackPos.col = tok.col.int16

View file

@ -35,7 +35,7 @@ proc writeDepsFile(g: ModuleGraph; project: string) =
let f = open(changeFileExt(project, "deps"), fmWrite) let f = open(changeFileExt(project, "deps"), fmWrite)
for m in g.modules: for m in g.modules:
if m != nil: if m != nil:
f.writeLine(toFullPath(m.position.int32)) f.writeLine(toFullPath(m.position.FileIndex))
for k in g.inclToMod.keys: for k in g.inclToMod.keys:
if g.getModule(k).isNil: # don't repeat includes which are also modules if g.getModule(k).isNil: # don't repeat includes which are also modules
f.writeLine(k.toFullPath) f.writeLine(k.toFullPath)
@ -94,7 +94,6 @@ proc commandCompileToJS(graph: ModuleGraph; cache: IdentCache) =
defineSymbol("nimrod") # 'nimrod' is always defined defineSymbol("nimrod") # 'nimrod' is always defined
defineSymbol("ecmascript") # For backward compatibility defineSymbol("ecmascript") # For backward compatibility
defineSymbol("js") defineSymbol("js")
if gCmd == cmdCompileToPHP: defineSymbol("nimphp")
semanticPasses() semanticPasses()
registerPass(JSgenPass) registerPass(JSgenPass)
compileProject(graph, cache) compileProject(graph, cache)
@ -189,9 +188,6 @@ proc mainCommand*(graph: ModuleGraph; cache: IdentCache) =
of "js", "compiletojs": of "js", "compiletojs":
gCmd = cmdCompileToJS gCmd = cmdCompileToJS
commandCompileToJS(graph, cache) commandCompileToJS(graph, cache)
of "php":
gCmd = cmdCompileToPHP
commandCompileToJS(graph, cache)
of "doc0": of "doc0":
wantMainModule() wantMainModule()
gCmd = cmdDoc gCmd = cmdDoc
@ -269,7 +265,7 @@ proc mainCommand*(graph: ModuleGraph; cache: IdentCache) =
of "parse": of "parse":
gCmd = cmdParse gCmd = cmdParse
wantMainModule() wantMainModule()
discard parseFile(gProjectMainIdx, cache) discard parseFile(FileIndex gProjectMainIdx, cache)
of "scan": of "scan":
gCmd = cmdScan gCmd = cmdScan
wantMainModule() wantMainModule()

View file

@ -25,7 +25,7 @@
## - Its dependent module stays the same. ## - Its dependent module stays the same.
## ##
import ast, intsets, tables, options, rod import ast, intsets, tables, options, rod, msgs, hashes
type type
ModuleGraph* = ref object ModuleGraph* = ref object
@ -34,9 +34,9 @@ type
deps*: IntSet # the dependency graph or potentially its transitive closure. deps*: IntSet # the dependency graph or potentially its transitive closure.
suggestMode*: bool # whether we are in nimsuggest mode or not. suggestMode*: bool # whether we are in nimsuggest mode or not.
invalidTransitiveClosure: bool invalidTransitiveClosure: bool
inclToMod*: Table[int32, int32] # mapping of include file to the inclToMod*: Table[FileIndex, FileIndex] # mapping of include file to the
# first module that included it # first module that included it
importStack*: seq[int32] # The current import stack. Used for detecting recursive importStack*: seq[FileIndex] # The current import stack. Used for detecting recursive
# module dependencies. # module dependencies.
backend*: RootRef # minor hack so that a backend can extend this easily backend*: RootRef # minor hack so that a backend can extend this easily
config*: ConfigRef config*: ConfigRef
@ -45,6 +45,8 @@ type
owners*: seq[PSym] owners*: seq[PSym]
methods*: seq[tuple[methods: TSymSeq, dispatcher: PSym]] methods*: seq[tuple[methods: TSymSeq, dispatcher: PSym]]
proc hash*(x: FileIndex): Hash {.borrow.}
{.this: g.} {.this: g.}
proc stopCompile*(g: ModuleGraph): bool {.inline.} = proc stopCompile*(g: ModuleGraph): bool {.inline.} =
@ -56,7 +58,7 @@ proc newModuleGraph*(config: ConfigRef = nil): ModuleGraph =
result.deps = initIntSet() result.deps = initIntSet()
result.modules = @[] result.modules = @[]
result.importStack = @[] result.importStack = @[]
result.inclToMod = initTable[int32, int32]() result.inclToMod = initTable[FileIndex, FileIndex]()
if config.isNil: if config.isNil:
result.config = newConfigRef() result.config = newConfigRef()
else: else:
@ -69,35 +71,35 @@ proc resetAllModules*(g: ModuleGraph) =
deps = initIntSet() deps = initIntSet()
modules = @[] modules = @[]
importStack = @[] importStack = @[]
inclToMod = initTable[int32, int32]() inclToMod = initTable[FileIndex, FileIndex]()
usageSym = nil usageSym = nil
owners = @[] owners = @[]
methods = @[] methods = @[]
proc getModule*(g: ModuleGraph; fileIdx: int32): PSym = proc getModule*(g: ModuleGraph; fileIdx: FileIndex): PSym =
if fileIdx >= 0 and fileIdx < modules.len: if fileIdx.int32 >= 0 and fileIdx.int32 < modules.len:
result = modules[fileIdx] result = modules[fileIdx.int32]
proc dependsOn(a, b: int): int {.inline.} = (a shl 15) + b proc dependsOn(a, b: int): int {.inline.} = (a shl 15) + b
proc addDep*(g: ModuleGraph; m: PSym, dep: int32) = proc addDep*(g: ModuleGraph; m: PSym, dep: FileIndex) =
assert m.position == m.info.fileIndex assert m.position == m.info.fileIndex.int32
addModuleDep(m.info.fileIndex, dep, isIncludeFile = false) addModuleDep(m.info.fileIndex, dep, isIncludeFile = false)
if suggestMode: if suggestMode:
deps.incl m.position.dependsOn(dep) deps.incl m.position.dependsOn(dep.int)
# we compute the transitive closure later when quering the graph lazily. # we compute the transitive closure later when quering the graph lazily.
# this improve efficiency quite a lot: # this improve efficiency quite a lot:
#invalidTransitiveClosure = true #invalidTransitiveClosure = true
proc addIncludeDep*(g: ModuleGraph; module, includeFile: int32) = proc addIncludeDep*(g: ModuleGraph; module, includeFile: FileIndex) =
addModuleDep(module, includeFile, isIncludeFile = true) addModuleDep(module, includeFile, isIncludeFile = true)
discard hasKeyOrPut(inclToMod, includeFile, module) discard hasKeyOrPut(inclToMod, includeFile, module)
proc parentModule*(g: ModuleGraph; fileIdx: int32): int32 = proc parentModule*(g: ModuleGraph; fileIdx: FileIndex): FileIndex =
## returns 'fileIdx' if the file belonging to this index is ## returns 'fileIdx' if the file belonging to this index is
## directly used as a module or else the module that first ## directly used as a module or else the module that first
## references this include file. ## references this include file.
if fileIdx >= 0 and fileIdx < modules.len and modules[fileIdx] != nil: if fileIdx.int32 >= 0 and fileIdx.int32 < modules.len and modules[fileIdx.int32] != nil:
result = fileIdx result = fileIdx
else: else:
result = inclToMod.getOrDefault(fileIdx) result = inclToMod.getOrDefault(fileIdx)
@ -111,11 +113,11 @@ proc transitiveClosure(g: var IntSet; n: int) =
if g.contains(i.dependsOn(k)) and g.contains(k.dependsOn(j)): if g.contains(i.dependsOn(k)) and g.contains(k.dependsOn(j)):
g.incl i.dependsOn(j) g.incl i.dependsOn(j)
proc markDirty*(g: ModuleGraph; fileIdx: int32) = proc markDirty*(g: ModuleGraph; fileIdx: FileIndex) =
let m = getModule fileIdx let m = getModule fileIdx
if m != nil: incl m.flags, sfDirty if m != nil: incl m.flags, sfDirty
proc markClientsDirty*(g: ModuleGraph; fileIdx: int32) = proc markClientsDirty*(g: ModuleGraph; fileIdx: FileIndex) =
# we need to mark its dependent modules D as dirty right away because after # we need to mark its dependent modules D as dirty right away because after
# nimsuggest is done with this module, the module's dirty flag will be # nimsuggest is done with this module, the module's dirty flag will be
# cleared but D still needs to be remembered as 'dirty'. # cleared but D still needs to be remembered as 'dirty'.
@ -126,7 +128,7 @@ proc markClientsDirty*(g: ModuleGraph; fileIdx: int32) =
# every module that *depends* on this file is also dirty: # every module that *depends* on this file is also dirty:
for i in 0i32..<modules.len.int32: for i in 0i32..<modules.len.int32:
let m = modules[i] let m = modules[i]
if m != nil and deps.contains(i.dependsOn(fileIdx)): if m != nil and deps.contains(i.dependsOn(fileIdx.int)):
incl m.flags, sfDirty incl m.flags, sfDirty
proc isDirty*(g: ModuleGraph; m: PSym): bool = proc isDirty*(g: ModuleGraph; m: PSym): bool =

View file

@ -174,7 +174,7 @@ proc getModuleName*(n: PNode): string =
localError(n.info, errGenerated, "invalid module name: '$1'" % n.renderTree) localError(n.info, errGenerated, "invalid module name: '$1'" % n.renderTree)
result = "" result = ""
proc checkModuleName*(n: PNode; doLocalError=true): int32 = proc checkModuleName*(n: PNode; doLocalError=true): FileIndex =
# This returns the full canonical path for a given module import # This returns the full canonical path for a given module import
let modulename = n.getModuleName let modulename = n.getModuleName
let fullPath = findModule(modulename, n.info.toFullPath) let fullPath = findModule(modulename, n.info.toFullPath)

View file

@ -13,115 +13,10 @@ import
ast, astalgo, magicsys, std / sha1, rodread, msgs, cgendata, sigmatch, options, ast, astalgo, magicsys, std / sha1, rodread, msgs, cgendata, sigmatch, options,
idents, os, lexer, idgen, passes, syntaxes, llstream, modulegraphs, rod idents, os, lexer, idgen, passes, syntaxes, llstream, modulegraphs, rod
when false:
type
TNeedRecompile* = enum Maybe, No, Yes, Probing, Recompiled
THashStatus* = enum hashNotTaken, hashCached, hashHasChanged, hashNotChanged
TModuleInMemory* = object
hash*: SecureHash
deps*: seq[int32] ## XXX: slurped files are currently not tracked
needsRecompile*: TNeedRecompile
hashStatus*: THashStatus
var
gCompiledModules: seq[PSym] = @[]
gMemCacheData*: seq[TModuleInMemory] = @[]
## XXX: we should implement recycling of file IDs
## if the user keeps renaming modules, the file IDs will keep growing
gFuzzyGraphChecking*: bool # nimsuggest uses this. XXX figure out why.
proc hashChanged(fileIdx: int32): bool =
internalAssert fileIdx >= 0 and fileIdx < gMemCacheData.len
template updateStatus =
gMemCacheData[fileIdx].hashStatus = if result: hashHasChanged
else: hashNotChanged
# echo "TESTING Hash: ", fileIdx.toFilename, " ", result
case gMemCacheData[fileIdx].hashStatus
of hashHasChanged:
result = true
of hashNotChanged:
result = false
of hashCached:
let newHash = secureHashFile(fileIdx.toFullPath)
result = newHash != gMemCacheData[fileIdx].hash
gMemCacheData[fileIdx].hash = newHash
updateStatus()
of hashNotTaken:
gMemCacheData[fileIdx].hash = secureHashFile(fileIdx.toFullPath)
result = true
updateStatus()
proc doHash(fileIdx: int32) =
if gMemCacheData[fileIdx].hashStatus == hashNotTaken:
# echo "FIRST Hash: ", fileIdx.ToFilename
gMemCacheData[fileIdx].hash = secureHashFile(fileIdx.toFullPath)
proc resetModule*(fileIdx: int32) =
# echo "HARD RESETTING ", fileIdx.toFilename
if fileIdx <% gMemCacheData.len:
gMemCacheData[fileIdx].needsRecompile = Yes
if fileIdx <% gCompiledModules.len:
gCompiledModules[fileIdx] = nil
if fileIdx <% cgendata.gModules.len:
cgendata.gModules[fileIdx] = nil
proc resetModule*(module: PSym) =
let conflict = getModule(module.position.int32)
if conflict == nil: return
doAssert conflict == module
resetModule(module.position.int32)
initStrTable(module.tab)
proc resetAllModules* =
for i in 0..gCompiledModules.high:
if gCompiledModules[i] != nil:
resetModule(i.int32)
resetPackageCache()
# for m in cgenModules(): echo "CGEN MODULE FOUND"
proc resetAllModulesHard* =
resetPackageCache()
gCompiledModules.setLen 0
gMemCacheData.setLen 0
magicsys.resetSysTypes()
# XXX
#gOwners = @[]
proc checkDepMem(fileIdx: int32): TNeedRecompile =
template markDirty =
resetModule(fileIdx)
return Yes
if gFuzzyGraphChecking:
if gMemCacheData[fileIdx].needsRecompile != Maybe:
return gMemCacheData[fileIdx].needsRecompile
else:
# cycle detection: We claim that a cycle does no harm.
if gMemCacheData[fileIdx].needsRecompile == Probing:
return No
if optForceFullMake in gGlobalOptions or hashChanged(fileIdx):
markDirty()
if gMemCacheData[fileIdx].deps != nil:
gMemCacheData[fileIdx].needsRecompile = Probing
for dep in gMemCacheData[fileIdx].deps:
let d = checkDepMem(dep)
if d in {Yes, Recompiled}:
# echo fileIdx.toFilename, " depends on ", dep.toFilename, " ", d
markDirty()
gMemCacheData[fileIdx].needsRecompile = No
return No
proc resetSystemArtifacts*() = proc resetSystemArtifacts*() =
magicsys.resetSysTypes() magicsys.resetSysTypes()
proc newModule(graph: ModuleGraph; fileIdx: int32): PSym = proc newModule(graph: ModuleGraph; fileIdx: FileIndex): PSym =
# We cannot call ``newSym`` here, because we have to circumvent the ID # We cannot call ``newSym`` here, because we have to circumvent the ID
# mechanism, which we do in order to assign each module a persistent ID. # mechanism, which we do in order to assign each module a persistent ID.
new(result) new(result)
@ -144,9 +39,9 @@ proc newModule(graph: ModuleGraph; fileIdx: int32): PSym =
graph.packageSyms.strTableAdd(packSym) graph.packageSyms.strTableAdd(packSym)
result.owner = packSym result.owner = packSym
result.position = fileIdx result.position = int fileIdx
growCache graph.modules, fileIdx growCache graph.modules, int fileIdx
graph.modules[result.position] = result graph.modules[result.position] = result
incl(result.flags, sfUsed) incl(result.flags, sfUsed)
@ -158,7 +53,7 @@ proc newModule(graph: ModuleGraph; fileIdx: int32): PSym =
# strTableIncl() for error corrections: # strTableIncl() for error corrections:
discard strTableIncl(packSym.tab, result) discard strTableIncl(packSym.tab, result)
proc compileModule*(graph: ModuleGraph; fileIdx: int32; cache: IdentCache, flags: TSymFlags): PSym = proc compileModule*(graph: ModuleGraph; fileIdx: FileIndex; cache: IdentCache, flags: TSymFlags): PSym =
result = graph.getModule(fileIdx) result = graph.getModule(fileIdx)
if result == nil: if result == nil:
#growCache gMemCacheData, fileIdx #growCache gMemCacheData, fileIdx
@ -199,7 +94,7 @@ proc compileModule*(graph: ModuleGraph; fileIdx: int32; cache: IdentCache, flags
else: else:
result = gCompiledModules[fileIdx] result = gCompiledModules[fileIdx]
proc importModule*(graph: ModuleGraph; s: PSym, fileIdx: int32; proc importModule*(graph: ModuleGraph; s: PSym, fileIdx: FileIndex;
cache: IdentCache): PSym {.procvar.} = cache: IdentCache): PSym {.procvar.} =
# this is called by the semantic checking phase # this is called by the semantic checking phase
result = compileModule(graph, fileIdx, cache, {}) result = compileModule(graph, fileIdx, cache, {})
@ -210,11 +105,11 @@ proc importModule*(graph: ModuleGraph; s: PSym, fileIdx: int32;
gNotes = if s.owner.id == gMainPackageId: gMainPackageNotes gNotes = if s.owner.id == gMainPackageId: gMainPackageNotes
else: ForeignPackageNotes else: ForeignPackageNotes
proc includeModule*(graph: ModuleGraph; s: PSym, fileIdx: int32; proc includeModule*(graph: ModuleGraph; s: PSym, fileIdx: FileIndex;
cache: IdentCache): PNode {.procvar.} = cache: IdentCache): PNode {.procvar.} =
result = syntaxes.parseFile(fileIdx, cache) result = syntaxes.parseFile(fileIdx, cache)
graph.addDep(s, fileIdx) graph.addDep(s, fileIdx)
graph.addIncludeDep(s.position.int32, fileIdx) graph.addIncludeDep(s.position.FileIndex, fileIdx)
proc compileSystemModule*(graph: ModuleGraph; cache: IdentCache) = proc compileSystemModule*(graph: ModuleGraph; cache: IdentCache) =
if magicsys.systemModule == nil: if magicsys.systemModule == nil:
@ -224,16 +119,16 @@ proc compileSystemModule*(graph: ModuleGraph; cache: IdentCache) =
proc wantMainModule* = proc wantMainModule* =
if gProjectFull.len == 0: if gProjectFull.len == 0:
fatal(gCmdLineInfo, errCommandExpectsFilename) fatal(gCmdLineInfo, errCommandExpectsFilename)
gProjectMainIdx = addFileExt(gProjectFull, NimExt).fileInfoIdx gProjectMainIdx = int32 addFileExt(gProjectFull, NimExt).fileInfoIdx
passes.gIncludeFile = includeModule passes.gIncludeFile = includeModule
passes.gImportModule = importModule passes.gImportModule = importModule
proc compileProject*(graph: ModuleGraph; cache: IdentCache; proc compileProject*(graph: ModuleGraph; cache: IdentCache;
projectFileIdx = -1'i32) = projectFileIdx = InvalidFileIDX) =
wantMainModule() wantMainModule()
let systemFileIdx = fileInfoIdx(options.libpath / "system.nim") let systemFileIdx = fileInfoIdx(options.libpath / "system.nim")
let projectFile = if projectFileIdx < 0: gProjectMainIdx else: projectFileIdx let projectFile = if projectFileIdx == InvalidFileIDX: FileIndex(gProjectMainIdx) else: projectFileIdx
graph.importStack.add projectFile graph.importStack.add projectFile
if projectFile == systemFileIdx: if projectFile == systemFileIdx:
discard graph.compileModule(projectFile, cache, {sfMainModule, sfSystemModule}) discard graph.compileModule(projectFile, cache, {sfMainModule, sfSystemModule})

View file

@ -10,21 +10,24 @@
import import
options, strutils, os, tables, ropes, platform, terminal, macros options, strutils, os, tables, ropes, platform, terminal, macros
const
explanationsBaseUrl* = "https://nim-lang.org/docs/manual"
type type
TMsgKind* = enum TMsgKind* = enum
errUnknown, errInternal, errIllFormedAstX, errCannotOpenFile, errGenerated, errUnknown, errInternal, errIllFormedAstX, errCannotOpenFile, errGenerated,
errXCompilerDoesNotSupportCpp, errStringLiteralExpected, errStringLiteralExpected,
errIntLiteralExpected, errInvalidCharacterConstant, errIntLiteralExpected, errInvalidCharacterConstant,
errClosingTripleQuoteExpected, errClosingQuoteExpected, errClosingTripleQuoteExpected, errClosingQuoteExpected,
errTabulatorsAreNotAllowed, errInvalidToken, errLineTooLong, errTabulatorsAreNotAllowed, errInvalidToken,
errInvalidNumber, errInvalidNumberOctalCode, errNumberOutOfRange, errInvalidNumber, errInvalidNumberOctalCode, errNumberOutOfRange,
errNnotAllowedInCharacter, errClosingBracketExpected, errMissingFinalQuote, errNnotAllowedInCharacter, errClosingBracketExpected, errMissingFinalQuote,
errIdentifierExpected, errNewlineExpected, errInvalidModuleName, errIdentifierExpected, errNewlineExpected, errInvalidModuleName,
errOperatorExpected, errTokenExpected, errStringAfterIncludeExpected, errOperatorExpected, errTokenExpected,
errRecursiveDependencyX, errOnOrOffExpected, errNoneSpeedOrSizeExpected, errRecursiveDependencyX, errOnOrOffExpected, errNoneSpeedOrSizeExpected,
errInvalidPragma, errUnknownPragma, errInvalidDirectiveX, errInvalidPragma, errUnknownPragma, errInvalidDirectiveX,
errAtPopWithoutPush, errEmptyAsm, errInvalidIndentation, errAtPopWithoutPush, errEmptyAsm, errInvalidIndentation,
errExceptionExpected, errExceptionAlreadyHandled, errExceptionAlreadyHandled,
errYieldNotAllowedHere, errYieldNotAllowedInTryStmt, errYieldNotAllowedHere, errYieldNotAllowedInTryStmt,
errInvalidNumberOfYieldExpr, errCannotReturnExpr, errInvalidNumberOfYieldExpr, errCannotReturnExpr,
errNoReturnWithReturnTypeNotAllowed, errAttemptToRedefine, errNoReturnWithReturnTypeNotAllowed, errAttemptToRedefine,
@ -68,7 +71,7 @@ type
errWrongNumberOfArgumentsInCall, errWrongNumberOfArgumentsInCall,
errMissingGenericParamsForTemplate, errMissingGenericParamsForTemplate,
errXCannotBePassedToProcVar, errXCannotBePassedToProcVar,
errXCannotBeInParamDecl, errPragmaOnlyInHeaderOfProcX, errImplOfXNotAllowed, errPragmaOnlyInHeaderOfProcX, errImplOfXNotAllowed,
errImplOfXexpected, errNoSymbolToBorrowFromFound, errDiscardValueX, errImplOfXexpected, errNoSymbolToBorrowFromFound, errDiscardValueX,
errInvalidDiscard, errIllegalConvFromXtoY, errCannotBindXTwice, errInvalidDiscard, errIllegalConvFromXtoY, errCannotBindXTwice,
errInvalidOrderInArrayConstructor, errInvalidOrderInArrayConstructor,
@ -87,7 +90,8 @@ type
errNoReturnTypeDeclared, errNoReturnTypeDeclared,
errNoCommand, errInvalidCommandX, errXOnlyAtModuleScope, errNoCommand, errInvalidCommandX, errXOnlyAtModuleScope,
errXNeedsParamObjectType, errXNeedsParamObjectType,
errTemplateInstantiationTooNested, errInstantiationFrom, errTemplateInstantiationTooNested, errMacroInstantiationTooNested,
errInstantiationFrom,
errInvalidIndexValueForTuple, errCommandExpectsFilename, errInvalidIndexValueForTuple, errCommandExpectsFilename,
errMainModuleMustBeSpecified, errMainModuleMustBeSpecified,
errXExpected, errXExpected,
@ -144,7 +148,6 @@ const
errIllFormedAstX: "illformed AST: $1", errIllFormedAstX: "illformed AST: $1",
errCannotOpenFile: "cannot open \'$1\'", errCannotOpenFile: "cannot open \'$1\'",
errGenerated: "$1", errGenerated: "$1",
errXCompilerDoesNotSupportCpp: "\'$1\' compiler does not support C++",
errStringLiteralExpected: "string literal expected", errStringLiteralExpected: "string literal expected",
errIntLiteralExpected: "integer literal expected", errIntLiteralExpected: "integer literal expected",
errInvalidCharacterConstant: "invalid character constant", errInvalidCharacterConstant: "invalid character constant",
@ -152,7 +155,6 @@ const
errClosingQuoteExpected: "closing \" expected", errClosingQuoteExpected: "closing \" expected",
errTabulatorsAreNotAllowed: "tabulators are not allowed", errTabulatorsAreNotAllowed: "tabulators are not allowed",
errInvalidToken: "invalid token: $1", errInvalidToken: "invalid token: $1",
errLineTooLong: "line too long",
errInvalidNumber: "$1 is not a valid number", errInvalidNumber: "$1 is not a valid number",
errInvalidNumberOctalCode: "$1 is not a valid number; did you mean octal? Then use one of '0o', '0c' or '0C'.", errInvalidNumberOctalCode: "$1 is not a valid number; did you mean octal? Then use one of '0o', '0c' or '0C'.",
errNumberOutOfRange: "number $1 out of valid range", errNumberOutOfRange: "number $1 out of valid range",
@ -164,7 +166,6 @@ const
errInvalidModuleName: "invalid module name: '$1'", errInvalidModuleName: "invalid module name: '$1'",
errOperatorExpected: "operator expected, but found \'$1\'", errOperatorExpected: "operator expected, but found \'$1\'",
errTokenExpected: "\'$1\' expected", errTokenExpected: "\'$1\' expected",
errStringAfterIncludeExpected: "string after \'include\' expected",
errRecursiveDependencyX: "recursive dependency: \'$1\'", errRecursiveDependencyX: "recursive dependency: \'$1\'",
errOnOrOffExpected: "\'on\' or \'off\' expected", errOnOrOffExpected: "\'on\' or \'off\' expected",
errNoneSpeedOrSizeExpected: "\'none\', \'speed\' or \'size\' expected", errNoneSpeedOrSizeExpected: "\'none\', \'speed\' or \'size\' expected",
@ -174,7 +175,6 @@ const
errAtPopWithoutPush: "\'pop\' without a \'push\' pragma", errAtPopWithoutPush: "\'pop\' without a \'push\' pragma",
errEmptyAsm: "empty asm statement", errEmptyAsm: "empty asm statement",
errInvalidIndentation: "invalid indentation", errInvalidIndentation: "invalid indentation",
errExceptionExpected: "exception expected",
errExceptionAlreadyHandled: "exception already handled", errExceptionAlreadyHandled: "exception already handled",
errYieldNotAllowedHere: "'yield' only allowed in an iterator", errYieldNotAllowedHere: "'yield' only allowed in an iterator",
errYieldNotAllowedInTryStmt: "'yield' cannot be used within 'try' in a non-inlined iterator", errYieldNotAllowedInTryStmt: "'yield' cannot be used within 'try' in a non-inlined iterator",
@ -280,7 +280,6 @@ const
errWrongNumberOfArgumentsInCall: "wrong number of arguments in call to '$1'", errWrongNumberOfArgumentsInCall: "wrong number of arguments in call to '$1'",
errMissingGenericParamsForTemplate: "'$1' has unspecified generic parameters", errMissingGenericParamsForTemplate: "'$1' has unspecified generic parameters",
errXCannotBePassedToProcVar: "\'$1\' cannot be passed to a procvar", errXCannotBePassedToProcVar: "\'$1\' cannot be passed to a procvar",
errXCannotBeInParamDecl: "$1 cannot be declared in parameter declaration",
errPragmaOnlyInHeaderOfProcX: "pragmas are only allowed in the header of a proc; redefinition of $1", errPragmaOnlyInHeaderOfProcX: "pragmas are only allowed in the header of a proc; redefinition of $1",
errImplOfXNotAllowed: "implementation of \'$1\' is not allowed", errImplOfXNotAllowed: "implementation of \'$1\' is not allowed",
errImplOfXexpected: "implementation of \'$1\' expected", errImplOfXexpected: "implementation of \'$1\' expected",
@ -329,7 +328,8 @@ const
errInvalidCommandX: "invalid command: \'$1\'", errInvalidCommandX: "invalid command: \'$1\'",
errXOnlyAtModuleScope: "\'$1\' is only allowed at top level", errXOnlyAtModuleScope: "\'$1\' is only allowed at top level",
errXNeedsParamObjectType: "'$1' needs a parameter that has an object type", errXNeedsParamObjectType: "'$1' needs a parameter that has an object type",
errTemplateInstantiationTooNested: "template/macro instantiation too nested", errTemplateInstantiationTooNested: "template instantiation too nested, try --evalTemplateLimit:N",
errMacroInstantiationTooNested: "macro instantiation too nested, try --evalMacroLimit:N",
errInstantiationFrom: "template/generic instantiation from here", errInstantiationFrom: "template/generic instantiation from here",
errInvalidIndexValueForTuple: "invalid index value for tuple subscript", errInvalidIndexValueForTuple: "invalid index value for tuple subscript",
errCommandExpectsFilename: "command expects a filename argument", errCommandExpectsFilename: "command expects a filename argument",
@ -495,15 +495,18 @@ type
# and parsed; usually 'nil' but is used # and parsed; usually 'nil' but is used
# for 'nimsuggest' # for 'nimsuggest'
hash*: string # the checksum of the file hash*: string # the checksum of the file
when defined(nimpretty):
fullContent*: string
FileIndex* = distinct int32
TLineInfo* = object # This is designed to be as small as possible, TLineInfo* = object # This is designed to be as small as possible,
# because it is used # because it is used
# in syntax nodes. We save space here by using # in syntax nodes. We save space here by using
# two int16 and an int32. # two int16 and an int32.
# On 64 bit and on 32 bit systems this is # On 64 bit and on 32 bit systems this is
# only 8 bytes. # only 8 bytes.
line*, col*: int16 line*: uint16
fileIndex*: int32 col*: int16
fileIndex*: FileIndex
when defined(nimpretty): when defined(nimpretty):
offsetA*, offsetB*: int offsetA*, offsetB*: int
commentOffsetA*, commentOffsetB*: int commentOffsetA*, commentOffsetB*: int
@ -517,6 +520,8 @@ type
ERecoverableError* = object of ValueError ERecoverableError* = object of ValueError
ESuggestDone* = object of Exception ESuggestDone* = object of Exception
proc `==`*(a, b: FileIndex): bool {.borrow.}
const const
NotesVerbosity*: array[0..3, TNoteKinds] = [ NotesVerbosity*: array[0..3, TNoteKinds] = [
{low(TNoteKind)..high(TNoteKind)} - {warnShadowIdent, warnUninit, {low(TNoteKind)..high(TNoteKind)} - {warnShadowIdent, warnUninit,
@ -541,14 +546,14 @@ const
{low(TNoteKind)..high(TNoteKind)}] {low(TNoteKind)..high(TNoteKind)}]
const const
InvalidFileIDX* = int32(-1) InvalidFileIDX* = FileIndex(-1)
var var
ForeignPackageNotes*: TNoteKinds = {hintProcessing, warnUnknownMagic, ForeignPackageNotes*: TNoteKinds = {hintProcessing, warnUnknownMagic,
hintQuitCalled, hintExecuting} hintQuitCalled, hintExecuting}
filenameToIndexTbl = initTable[string, int32]() filenameToIndexTbl = initTable[string, FileIndex]()
fileInfos*: seq[TFileInfo] = @[] fileInfos*: seq[TFileInfo] = @[]
systemFileIdx*: int32 systemFileIdx*: FileIndex
proc toCChar*(c: char): string = proc toCChar*(c: char): string =
case c case c
@ -583,6 +588,18 @@ proc newFileInfo(fullPath, projPath: string): TFileInfo =
result.quotedFullName = fullPath.makeCString result.quotedFullName = fullPath.makeCString
if optEmbedOrigSrc in gGlobalOptions or true: if optEmbedOrigSrc in gGlobalOptions or true:
result.lines = @[] result.lines = @[]
when defined(nimpretty):
if result.fullPath.len > 0:
try:
result.fullContent = readFile(result.fullPath)
except IOError:
#rawMessage(errCannotOpenFile, result.fullPath)
# XXX fixme
result.fullContent = ""
when defined(nimpretty):
proc fileSection*(fid: FileIndex; a, b: int): string =
substr(fileInfos[fid.int].fullContent, a, b)
proc fileInfoKnown*(filename: string): bool = proc fileInfoKnown*(filename: string): bool =
var var
@ -593,7 +610,7 @@ proc fileInfoKnown*(filename: string): bool =
canon = filename canon = filename
result = filenameToIndexTbl.hasKey(canon) result = filenameToIndexTbl.hasKey(canon)
proc fileInfoIdx*(filename: string; isKnownFile: var bool): int32 = proc fileInfoIdx*(filename: string; isKnownFile: var bool): FileIndex =
var var
canon: string canon: string
pseudoPath = false pseudoPath = false
@ -611,28 +628,28 @@ proc fileInfoIdx*(filename: string; isKnownFile: var bool): int32 =
result = filenameToIndexTbl[canon] result = filenameToIndexTbl[canon]
else: else:
isKnownFile = false isKnownFile = false
result = fileInfos.len.int32 result = fileInfos.len.FileIndex
fileInfos.add(newFileInfo(canon, if pseudoPath: filename fileInfos.add(newFileInfo(canon, if pseudoPath: filename
else: canon.shortenDir)) else: canon.shortenDir))
filenameToIndexTbl[canon] = result filenameToIndexTbl[canon] = result
proc fileInfoIdx*(filename: string): int32 = proc fileInfoIdx*(filename: string): FileIndex =
var dummy: bool var dummy: bool
result = fileInfoIdx(filename, dummy) result = fileInfoIdx(filename, dummy)
proc newLineInfo*(fileInfoIdx: int32, line, col: int): TLineInfo = proc newLineInfo*(fileInfoIdx: FileIndex, line, col: int): TLineInfo =
result.fileIndex = fileInfoIdx result.fileIndex = fileInfoIdx
result.line = int16(line) result.line = uint16(line)
result.col = int16(col) result.col = int16(col)
proc newLineInfo*(filename: string, line, col: int): TLineInfo {.inline.} = proc newLineInfo*(filename: string, line, col: int): TLineInfo {.inline.} =
result = newLineInfo(filename.fileInfoIdx, line, col) result = newLineInfo(filename.fileInfoIdx, line, col)
fileInfos.add(newFileInfo("", "command line")) fileInfos.add(newFileInfo("", "command line"))
var gCmdLineInfo* = newLineInfo(int32(0), 1, 1) var gCmdLineInfo* = newLineInfo(FileIndex(0), 1, 1)
fileInfos.add(newFileInfo("", "compilation artifact")) fileInfos.add(newFileInfo("", "compilation artifact"))
var gCodegenLineInfo* = newLineInfo(int32(1), 1, 1) var gCodegenLineInfo* = newLineInfo(FileIndex(1), 1, 1)
proc raiseRecoverableError*(msg: string) {.noinline, noreturn.} = proc raiseRecoverableError*(msg: string) {.noinline, noreturn.} =
raise newException(ERecoverableError, msg) raise newException(ERecoverableError, msg)
@ -648,9 +665,9 @@ var
gMainPackageNotes*: TNoteKinds = NotesVerbosity[1] gMainPackageNotes*: TNoteKinds = NotesVerbosity[1]
proc unknownLineInfo*(): TLineInfo = proc unknownLineInfo*(): TLineInfo =
result.line = int16(-1) result.line = uint16(0)
result.col = int16(-1) result.col = int16(-1)
result.fileIndex = -1 result.fileIndex = InvalidFileIDX
type type
Severity* {.pure.} = enum ## VS Code only supports these three Severity* {.pure.} = enum ## VS Code only supports these three
@ -712,32 +729,32 @@ proc getInfoContext*(index: int): TLineInfo =
if i >=% L: result = unknownLineInfo() if i >=% L: result = unknownLineInfo()
else: result = msgContext[i] else: result = msgContext[i]
template toFilename*(fileIdx: int32): string = template toFilename*(fileIdx: FileIndex): string =
(if fileIdx < 0: "???" else: fileInfos[fileIdx].projPath) (if fileIdx.int32 < 0: "???" else: fileInfos[fileIdx.int32].projPath)
proc toFullPath*(fileIdx: int32): string = proc toFullPath*(fileIdx: FileIndex): string =
if fileIdx < 0: result = "???" if fileIdx.int32 < 0: result = "???"
else: result = fileInfos[fileIdx].fullPath else: result = fileInfos[fileIdx.int32].fullPath
proc setDirtyFile*(fileIdx: int32; filename: string) = proc setDirtyFile*(fileIdx: FileIndex; filename: string) =
assert fileIdx >= 0 assert fileIdx.int32 >= 0
fileInfos[fileIdx].dirtyFile = filename fileInfos[fileIdx.int32].dirtyFile = filename
proc setHash*(fileIdx: int32; hash: string) = proc setHash*(fileIdx: FileIndex; hash: string) =
assert fileIdx >= 0 assert fileIdx.int32 >= 0
shallowCopy(fileInfos[fileIdx].hash, hash) shallowCopy(fileInfos[fileIdx.int32].hash, hash)
proc getHash*(fileIdx: int32): string = proc getHash*(fileIdx: FileIndex): string =
assert fileIdx >= 0 assert fileIdx.int32 >= 0
shallowCopy(result, fileInfos[fileIdx].hash) shallowCopy(result, fileInfos[fileIdx.int32].hash)
proc toFullPathConsiderDirty*(fileIdx: int32): string = proc toFullPathConsiderDirty*(fileIdx: FileIndex): string =
if fileIdx < 0: if fileIdx.int32 < 0:
result = "???" result = "???"
elif not fileInfos[fileIdx].dirtyFile.isNil: elif not fileInfos[fileIdx.int32].dirtyFile.isNil:
result = fileInfos[fileIdx].dirtyFile result = fileInfos[fileIdx.int32].dirtyFile
else: else:
result = fileInfos[fileIdx].fullPath result = fileInfos[fileIdx.int32].fullPath
template toFilename*(info: TLineInfo): string = template toFilename*(info: TLineInfo): string =
info.fileIndex.toFilename info.fileIndex.toFilename
@ -746,15 +763,15 @@ template toFullPath*(info: TLineInfo): string =
info.fileIndex.toFullPath info.fileIndex.toFullPath
proc toMsgFilename*(info: TLineInfo): string = proc toMsgFilename*(info: TLineInfo): string =
if info.fileIndex < 0: if info.fileIndex.int32 < 0:
result = "???" result = "???"
elif gListFullPaths: elif gListFullPaths:
result = fileInfos[info.fileIndex].fullPath result = fileInfos[info.fileIndex.int32].fullPath
else: else:
result = fileInfos[info.fileIndex].projPath result = fileInfos[info.fileIndex.int32].projPath
proc toLinenumber*(info: TLineInfo): int {.inline.} = proc toLinenumber*(info: TLineInfo): int {.inline.} =
result = info.line result = int info.line
proc toColumn*(info: TLineInfo): int {.inline.} = proc toColumn*(info: TLineInfo): int {.inline.} =
result = info.col result = info.col
@ -771,7 +788,7 @@ proc `??`* (info: TLineInfo, filename: string): bool =
# only for debugging purposes # only for debugging purposes
result = filename in info.toFilename result = filename in info.toFilename
const trackPosInvalidFileIdx* = -2 # special marker so that no suggestions const trackPosInvalidFileIdx* = FileIndex(-2) # special marker so that no suggestions
# are produced within comments and string literals # are produced within comments and string literals
var gTrackPos*: TLineInfo var gTrackPos*: TLineInfo
var gTrackPosAttached*: bool ## whether the tracking position was attached to some var gTrackPosAttached*: bool ## whether the tracking position was attached to some
@ -910,7 +927,7 @@ proc writeContext(lastinfo: TLineInfo) =
else: else:
styledMsgWriteln(styleBright, styledMsgWriteln(styleBright,
PosFormat % [toMsgFilename(msgContext[i]), PosFormat % [toMsgFilename(msgContext[i]),
coordToStr(msgContext[i].line), coordToStr(msgContext[i].line.int),
coordToStr(msgContext[i].col+1)], coordToStr(msgContext[i].col+1)],
resetStyle, resetStyle,
getMessageStr(errInstantiationFrom, "")) getMessageStr(errInstantiationFrom, ""))
@ -978,7 +995,7 @@ proc formatMsg*(info: TLineInfo, msg: TMsgKind, arg: string): string =
of warnMin..warnMax: WarningTitle of warnMin..warnMax: WarningTitle
of hintMin..hintMax: HintTitle of hintMin..hintMax: HintTitle
else: ErrorTitle else: ErrorTitle
result = PosFormat % [toMsgFilename(info), coordToStr(info.line), result = PosFormat % [toMsgFilename(info), coordToStr(info.line.int),
coordToStr(info.col+1)] & coordToStr(info.col+1)] &
title & title &
getMessageStr(msg, arg) getMessageStr(msg, arg)
@ -1019,7 +1036,7 @@ proc liMessage(info: TLineInfo, msg: TMsgKind, arg: string,
# NOTE: currently line info line numbers start with 1, # NOTE: currently line info line numbers start with 1,
# but column numbers start with 0, however most editors expect # but column numbers start with 0, however most editors expect
# first column to be 1, so we need to +1 here # first column to be 1, so we need to +1 here
let x = PosFormat % [toMsgFilename(info), coordToStr(info.line), let x = PosFormat % [toMsgFilename(info), coordToStr(info.line.int),
coordToStr(info.col+1)] coordToStr(info.col+1)]
let s = getMessageStr(msg, arg) let s = getMessageStr(msg, arg)
@ -1032,7 +1049,7 @@ proc liMessage(info: TLineInfo, msg: TMsgKind, arg: string,
KindColor, `%`(KindFormat, kind)) KindColor, `%`(KindFormat, kind))
else: else:
styledMsgWriteln(styleBright, x, resetStyle, color, title, resetStyle, s) styledMsgWriteln(styleBright, x, resetStyle, color, title, resetStyle, s)
if msg in errMin..errMax and hintSource in gNotes: if hintSource in gNotes:
info.writeSurroundingSrc info.writeSurroundingSrc
handleError(msg, eh, s) handleError(msg, eh, s)
@ -1077,30 +1094,30 @@ template assertNotNil*(e): untyped =
template internalAssert*(e: bool) = template internalAssert*(e: bool) =
if not e: internalError($instantiationInfo()) if not e: internalError($instantiationInfo())
proc addSourceLine*(fileIdx: int32, line: string) = proc addSourceLine*(fileIdx: FileIndex, line: string) =
fileInfos[fileIdx].lines.add line.rope fileInfos[fileIdx.int32].lines.add line.rope
proc sourceLine*(i: TLineInfo): Rope = proc sourceLine*(i: TLineInfo): Rope =
if i.fileIndex < 0: return nil if i.fileIndex.int32 < 0: return nil
if not optPreserveOrigSource and fileInfos[i.fileIndex].lines.len == 0: if not optPreserveOrigSource and fileInfos[i.fileIndex.int32].lines.len == 0:
try: try:
for line in lines(i.toFullPath): for line in lines(i.toFullPath):
addSourceLine i.fileIndex, line.string addSourceLine i.fileIndex, line.string
except IOError: except IOError:
discard discard
internalAssert i.fileIndex < fileInfos.len internalAssert i.fileIndex.int32 < fileInfos.len
# can happen if the error points to EOF: # can happen if the error points to EOF:
if i.line > fileInfos[i.fileIndex].lines.len: return nil if i.line.int > fileInfos[i.fileIndex.int32].lines.len: return nil
result = fileInfos[i.fileIndex].lines[i.line-1] result = fileInfos[i.fileIndex.int32].lines[i.line.int-1]
proc quotedFilename*(i: TLineInfo): Rope = proc quotedFilename*(i: TLineInfo): Rope =
internalAssert i.fileIndex >= 0 internalAssert i.fileIndex.int32 >= 0
if optExcessiveStackTrace in gGlobalOptions: if optExcessiveStackTrace in gGlobalOptions:
result = fileInfos[i.fileIndex].quotedFullName result = fileInfos[i.fileIndex.int32].quotedFullName
else: else:
result = fileInfos[i.fileIndex].quotedName result = fileInfos[i.fileIndex.int32].quotedName
ropes.errorHandler = proc (err: RopesError, msg: string, useWarning: bool) = ropes.errorHandler = proc (err: RopesError, msg: string, useWarning: bool) =
case err case err

View file

@ -90,14 +90,6 @@ proc handleCmdLine(cache: IdentCache; config: ConfigRef) =
ex = quoteShell( ex = quoteShell(
completeCFilePath(changeFileExt(gProjectFull, "js").prependCurDir)) completeCFilePath(changeFileExt(gProjectFull, "js").prependCurDir))
execExternalProgram(findNodeJs() & " " & ex & ' ' & commands.arguments) execExternalProgram(findNodeJs() & " " & ex & ' ' & commands.arguments)
elif gCmd == cmdCompileToPHP:
var ex: string
if options.outFile.len > 0:
ex = options.outFile.prependCurDir.quoteShell
else:
ex = quoteShell(
completeCFilePath(changeFileExt(gProjectFull, "php").prependCurDir))
execExternalProgram("php " & ex & ' ' & commands.arguments)
else: else:
var binPath: string var binPath: string
if options.outFile.len > 0: if options.outFile.len > 0:

View file

@ -28,7 +28,7 @@ proc overwriteFiles*() =
let doStrip = options.getConfigVar("pretty.strip").normalize == "on" let doStrip = options.getConfigVar("pretty.strip").normalize == "on"
for i in 0 .. high(gSourceFiles): for i in 0 .. high(gSourceFiles):
if gSourceFiles[i].dirty and not gSourceFiles[i].isNimfixFile and if gSourceFiles[i].dirty and not gSourceFiles[i].isNimfixFile and
(not gOnlyMainfile or gSourceFiles[i].fileIdx == gProjectMainIdx): (not gOnlyMainfile or gSourceFiles[i].fileIdx == gProjectMainIdx.FileIndex):
let newFile = if gOverWrite: gSourceFiles[i].fullpath let newFile = if gOverWrite: gSourceFiles[i].fullpath
else: gSourceFiles[i].fullpath.changeFileExt(".pretty.nim") else: gSourceFiles[i].fullpath.changeFileExt(".pretty.nim")
try: try:
@ -95,7 +95,7 @@ proc beautifyName(s: string, k: TSymKind): string =
proc replaceInFile(info: TLineInfo; newName: string) = proc replaceInFile(info: TLineInfo; newName: string) =
loadFile(info) loadFile(info)
let line = gSourceFiles[info.fileIndex].lines[info.line-1] let line = gSourceFiles[info.fileIndex.int].lines[info.line.int-1]
var first = min(info.col.int, line.len) var first = min(info.col.int, line.len)
if first < 0: return if first < 0: return
#inc first, skipIgnoreCase(line, "proc ", first) #inc first, skipIgnoreCase(line, "proc ", first)
@ -107,8 +107,8 @@ proc replaceInFile(info: TLineInfo; newName: string) =
if differ(line, first, last, newName): if differ(line, first, last, newName):
# last-first+1 != newName.len or # last-first+1 != newName.len or
var x = line.substr(0, first-1) & newName & line.substr(last+1) var x = line.substr(0, first-1) & newName & line.substr(last+1)
system.shallowCopy(gSourceFiles[info.fileIndex].lines[info.line-1], x) system.shallowCopy(gSourceFiles[info.fileIndex.int].lines[info.line.int-1], x)
gSourceFiles[info.fileIndex].dirty = true gSourceFiles[info.fileIndex.int].dirty = true
proc checkStyle(info: TLineInfo, s: string, k: TSymKind; sym: PSym) = proc checkStyle(info: TLineInfo, s: string, k: TSymKind; sym: PSym) =
let beau = beautifyName(s, k) let beau = beautifyName(s, k)
@ -136,7 +136,7 @@ template styleCheckDef*(s: PSym) =
styleCheckDef(s.info, s, s.kind) styleCheckDef(s.info, s, s.kind)
proc styleCheckUseImpl(info: TLineInfo; s: PSym) = proc styleCheckUseImpl(info: TLineInfo; s: PSym) =
if info.fileIndex < 0: return if info.fileIndex.int < 0: return
# we simply convert it to what it looks like in the definition # we simply convert it to what it looks like in the definition
# for consistency # for consistency

View file

@ -16,13 +16,13 @@ type
lines*: seq[string] lines*: seq[string]
dirty*, isNimfixFile*: bool dirty*, isNimfixFile*: bool
fullpath*, newline*: string fullpath*, newline*: string
fileIdx*: int32 fileIdx*: FileIndex
var var
gSourceFiles*: seq[TSourceFile] = @[] gSourceFiles*: seq[TSourceFile] = @[]
proc loadFile*(info: TLineInfo) = proc loadFile*(info: TLineInfo) =
let i = info.fileIndex let i = info.fileIndex.int
if i >= gSourceFiles.len: if i >= gSourceFiles.len:
gSourceFiles.setLen(i+1) gSourceFiles.setLen(i+1)
if gSourceFiles[i].lines.isNil: if gSourceFiles[i].lines.isNil:
@ -64,7 +64,7 @@ proc differ*(line: string, a, b: int, x: string): bool =
proc replaceDeprecated*(info: TLineInfo; oldSym, newSym: PIdent) = proc replaceDeprecated*(info: TLineInfo; oldSym, newSym: PIdent) =
loadFile(info) loadFile(info)
let line = gSourceFiles[info.fileIndex].lines[info.line-1] let line = gSourceFiles[info.fileIndex.int32].lines[info.line.int-1]
var first = min(info.col.int, line.len) var first = min(info.col.int, line.len)
if first < 0: return if first < 0: return
#inc first, skipIgnoreCase(line, "proc ", first) #inc first, skipIgnoreCase(line, "proc ", first)
@ -75,8 +75,8 @@ proc replaceDeprecated*(info: TLineInfo; oldSym, newSym: PIdent) =
let last = first+identLen(line, first)-1 let last = first+identLen(line, first)-1
if cmpIgnoreStyle(line[first..last], oldSym.s) == 0: if cmpIgnoreStyle(line[first..last], oldSym.s) == 0:
var x = line.substr(0, first-1) & newSym.s & line.substr(last+1) var x = line.substr(0, first-1) & newSym.s & line.substr(last+1)
system.shallowCopy(gSourceFiles[info.fileIndex].lines[info.line-1], x) system.shallowCopy(gSourceFiles[info.fileIndex.int32].lines[info.line.int-1], x)
gSourceFiles[info.fileIndex].dirty = true gSourceFiles[info.fileIndex.int32].dirty = true
#if newSym.s == "File": writeStackTrace() #if newSym.s == "File": writeStackTrace()
proc replaceDeprecated*(info: TLineInfo; oldSym, newSym: PSym) = proc replaceDeprecated*(info: TLineInfo; oldSym, newSym: PSym) =
@ -85,10 +85,10 @@ proc replaceDeprecated*(info: TLineInfo; oldSym, newSym: PSym) =
proc replaceComment*(info: TLineInfo) = proc replaceComment*(info: TLineInfo) =
loadFile(info) loadFile(info)
let line = gSourceFiles[info.fileIndex].lines[info.line-1] let line = gSourceFiles[info.fileIndex.int32].lines[info.line.int-1]
var first = info.col.int var first = info.col.int
if line[first] != '#': inc first if line[first] != '#': inc first
var x = line.substr(0, first-1) & "discard " & line.substr(first+1).escape var x = line.substr(0, first-1) & "discard " & line.substr(first+1).escape
system.shallowCopy(gSourceFiles[info.fileIndex].lines[info.line-1], x) system.shallowCopy(gSourceFiles[info.fileIndex.int32].lines[info.line.int-1], x)
gSourceFiles[info.fileIndex].dirty = true gSourceFiles[info.fileIndex.int32].dirty = true

View file

@ -81,7 +81,6 @@ type
# **keep binary compatible** # **keep binary compatible**
cmdNone, cmdCompileToC, cmdCompileToCpp, cmdCompileToOC, cmdNone, cmdCompileToC, cmdCompileToCpp, cmdCompileToOC,
cmdCompileToJS, cmdCompileToJS,
cmdCompileToPHP,
cmdCompileToLLVM, cmdInterpret, cmdPretty, cmdDoc, cmdCompileToLLVM, cmdInterpret, cmdPretty, cmdDoc,
cmdGenDepend, cmdDump, cmdGenDepend, cmdDump,
cmdCheck, # semantic checking for whole project cmdCheck, # semantic checking for whole project

View file

@ -235,7 +235,8 @@ proc isAssignable*(owner: PSym, n: PNode; isUnsafeAddr=false): TAssignableResult
result = arLValue result = arLValue
else: else:
result = isAssignable(owner, n.sons[0], isUnsafeAddr) result = isAssignable(owner, n.sons[0], isUnsafeAddr)
if result != arNone and sfDiscriminant in n.sons[1].sym.flags: if result != arNone and n[1].kind == nkSym and
sfDiscriminant in n[1].sym.flags:
result = arDiscriminant result = arDiscriminant
of nkBracketExpr: of nkBracketExpr:
if skipTypes(n.sons[0].typ, abstractInst-{tyTypeDesc}).kind in if skipTypes(n.sons[0].typ, abstractInst-{tyTypeDesc}).kind in

View file

@ -83,7 +83,7 @@ proc getTok(p: var TParser) =
rawGetTok(p.lex, p.tok) rawGetTok(p.lex, p.tok)
p.hasProgress = true p.hasProgress = true
proc openParser*(p: var TParser, fileIdx: int32, inputStream: PLLStream, proc openParser*(p: var TParser, fileIdx: FileIndex, inputStream: PLLStream,
cache: IdentCache; cache: IdentCache;
strongSpaces=false) = strongSpaces=false) =
## Open a parser, using the given arguments to set up its internal state. ## Open a parser, using the given arguments to set up its internal state.
@ -125,6 +125,12 @@ proc rawSkipComment(p: var TParser, node: PNode) =
if p.tok.tokType == tkComment: if p.tok.tokType == tkComment:
if node != nil: if node != nil:
if node.comment == nil: node.comment = "" if node.comment == nil: node.comment = ""
when defined(nimpretty):
if p.tok.commentOffsetB > p.tok.commentOffsetA:
add node.comment, fileSection(p.lex.fileIdx, p.tok.commentOffsetA, p.tok.commentOffsetB)
else:
add node.comment, p.tok.literal
else:
add(node.comment, p.tok.literal) add(node.comment, p.tok.literal)
else: else:
parMessage(p, errInternal, "skipComment") parMessage(p, errInternal, "skipComment")

View file

@ -52,8 +52,8 @@ proc makePass*(open: TPassOpen = nil,
# the semantic checker needs these: # the semantic checker needs these:
var var
gImportModule*: proc (graph: ModuleGraph; m: PSym, fileIdx: int32; cache: IdentCache): PSym {.nimcall.} gImportModule*: proc (graph: ModuleGraph; m: PSym, fileIdx: FileIndex; cache: IdentCache): PSym {.nimcall.}
gIncludeFile*: proc (graph: ModuleGraph; m: PSym, fileIdx: int32; cache: IdentCache): PNode {.nimcall.} gIncludeFile*: proc (graph: ModuleGraph; m: PSym, fileIdx: FileIndex; cache: IdentCache): PNode {.nimcall.}
# implementation # implementation
@ -63,20 +63,6 @@ proc skipCodegen*(n: PNode): bool {.inline.} =
# error count instead. # error count instead.
result = msgs.gErrorCounter > 0 result = msgs.gErrorCounter > 0
proc astNeeded*(s: PSym): bool =
# The ``rodwrite`` module uses this to determine if the body of a proc
# needs to be stored. The passes manager frees s.sons[codePos] when
# appropriate to free the procedure body's memory. This is important
# to keep memory usage down.
if (s.kind in {skMethod, skProc, skFunc}) and
({sfCompilerProc, sfCompileTime} * s.flags == {}) and
(s.typ.callConv != ccInline) and
(s.ast.sons[genericParamsPos].kind == nkEmpty):
result = false
# XXX this doesn't really make sense with excessive CTFE
else:
result = true
const const
maxPasses = 10 maxPasses = 10
@ -153,7 +139,7 @@ proc closePassesCached(graph: ModuleGraph; a: var TPassContextArray) =
m = gPasses[i].close(graph, a[i], m) m = gPasses[i].close(graph, a[i], m)
a[i] = nil # free the memory here a[i] = nil # free the memory here
proc resolveMod(module, relativeTo: string): int32 = proc resolveMod(module, relativeTo: string): FileIndex =
let fullPath = findModule(module, relativeTo) let fullPath = findModule(module, relativeTo)
if fullPath.len == 0: if fullPath.len == 0:
result = InvalidFileIDX result = InvalidFileIDX
@ -166,7 +152,7 @@ proc processImplicits(implicits: seq[string], nodeKind: TNodeKind,
let relativeTo = m.info.toFullPath let relativeTo = m.info.toFullPath
for module in items(implicits): for module in items(implicits):
# implicit imports should not lead to a module importing itself # implicit imports should not lead to a module importing itself
if m.position != resolveMod(module, relativeTo): if m.position != resolveMod(module, relativeTo).int32:
var importStmt = newNodeI(nodeKind, gCmdLineInfo) var importStmt = newNodeI(nodeKind, gCmdLineInfo)
var str = newStrNode(nkStrLit, module) var str = newStrNode(nkStrLit, module)
str.info = gCmdLineInfo str.info = gCmdLineInfo
@ -180,7 +166,7 @@ proc processModule*(graph: ModuleGraph; module: PSym, stream: PLLStream,
p: TParsers p: TParsers
a: TPassContextArray a: TPassContextArray
s: PLLStream s: PLLStream
fileIdx = module.fileIdx fileIdx = FileIndex module.fileIdx
if module.id < 0: if module.id < 0:
# new module caching mechanism: # new module caching mechanism:
for i in 0..<gPassesLen: for i in 0..<gPassesLen:

View file

@ -19,7 +19,7 @@ proc getTok(p: var TParser) =
## `tok` member. ## `tok` member.
rawGetTok(p.lex, p.tok) rawGetTok(p.lex, p.tok)
proc openParser*(p: var TParser, fileIdx: int32, inputStream: PLLStream; proc openParser*(p: var TParser, fileIdx: FileIndex, inputStream: PLLStream;
cache: IdentCache) = cache: IdentCache) =
## Open a parser, using the given arguments to set up its internal state. ## Open a parser, using the given arguments to set up its internal state.
## ##

View file

@ -543,7 +543,7 @@ proc pragmaLine(c: PContext, n: PNode) =
else: else:
# XXX this produces weird paths which are not properly resolved: # XXX this produces weird paths which are not properly resolved:
n.info.fileIndex = msgs.fileInfoIdx(x.strVal) n.info.fileIndex = msgs.fileInfoIdx(x.strVal)
n.info.line = int16(y.intVal) n.info.line = uint16(y.intVal)
else: else:
localError(n.info, errXExpected, "tuple") localError(n.info, errXExpected, "tuple")
else: else:

View file

@ -66,12 +66,10 @@ proc searchForProcOld*(c: PContext, scope: PScope, fn: PSym): PSym =
proc searchForProcNew(c: PContext, scope: PScope, fn: PSym): PSym = proc searchForProcNew(c: PContext, scope: PScope, fn: PSym): PSym =
const flags = {ExactGenericParams, ExactTypeDescValues, const flags = {ExactGenericParams, ExactTypeDescValues,
ExactConstraints, IgnoreCC} ExactConstraints, IgnoreCC}
var it: TIdentIter var it: TIdentIter
result = initIdentIter(it, scope.symbols, fn.name) result = initIdentIter(it, scope.symbols, fn.name)
while result != nil: while result != nil:
if result.kind == fn.kind and sameType(result.typ, fn.typ, flags): if result.kind == fn.kind: #and sameType(result.typ, fn.typ, flags):
case equalParams(result.typ.n, fn.typ.n) case equalParams(result.typ.n, fn.typ.n)
of paramsEqual: of paramsEqual:
if (sfExported notin result.flags) and (sfExported in fn.flags): if (sfExported notin result.flags) and (sfExported in fn.flags):
@ -85,11 +83,8 @@ proc searchForProcNew(c: PContext, scope: PScope, fn: PSym): PSym =
return return
of paramsNotEqual: of paramsNotEqual:
discard discard
result = nextIdentIter(it, scope.symbols) result = nextIdentIter(it, scope.symbols)
return nil
proc searchForProc*(c: PContext, scope: PScope, fn: PSym): PSym = proc searchForProc*(c: PContext, scope: PScope, fn: PSym): PSym =
result = searchForProcNew(c, scope, fn) result = searchForProcNew(c, scope, fn)
when false: when false:

View file

@ -39,8 +39,7 @@ type
inPragma: int inPragma: int
when defined(nimpretty): when defined(nimpretty):
pendingNewlineCount: int pendingNewlineCount: int
origContent: string fid*: FileIndex
# We render the source code in a two phases: The first # We render the source code in a two phases: The first
# determines how long the subtree will likely be, the second # determines how long the subtree will likely be, the second
@ -354,13 +353,13 @@ proc ulitAux(g: TSrcGen; n: PNode, x: BiggestInt, size: int): string =
proc atom(g: TSrcGen; n: PNode): string = proc atom(g: TSrcGen; n: PNode): string =
when defined(nimpretty): when defined(nimpretty):
let comment = if n.info.commentOffsetA < n.info.commentOffsetB: let comment = if n.info.commentOffsetA < n.info.commentOffsetB:
" " & substr(g.origContent, n.info.commentOffsetA, n.info.commentOffsetB) " " & fileSection(g.fid, n.info.commentOffsetA, n.info.commentOffsetB)
else: else:
"" ""
if n.info.offsetA <= n.info.offsetB: if n.info.offsetA <= n.info.offsetB:
# for some constructed tokens this can not be the case and we're better # for some constructed tokens this can not be the case and we're better
# off to not mess with the offset then. # off to not mess with the offset then.
return substr(g.origContent, n.info.offsetA, n.info.offsetB) & comment return fileSection(g.fid, n.info.offsetA, n.info.offsetB) & comment
var f: float32 var f: float32
case n.kind case n.kind
of nkEmpty: result = "" of nkEmpty: result = ""
@ -1460,17 +1459,13 @@ proc renderTree*(n: PNode, renderFlags: TRenderFlags = {}): string =
proc `$`*(n: PNode): string = n.renderTree proc `$`*(n: PNode): string = n.renderTree
proc renderModule*(n: PNode, infile, outfile: string, proc renderModule*(n: PNode, infile, outfile: string,
renderFlags: TRenderFlags = {}) = renderFlags: TRenderFlags = {};
fid = FileIndex(-1)) =
var var
f: File f: File
g: TSrcGen g: TSrcGen
initSrcGen(g, renderFlags) initSrcGen(g, renderFlags)
when defined(nimpretty): g.fid = fid
try:
g.origContent = readFile(infile)
except IOError:
rawMessage(errCannotOpenFile, infile)
for i in countup(0, sonsLen(n) - 1): for i in countup(0, sonsLen(n) - 1):
gsub(g, n.sons[i]) gsub(g, n.sons[i])
optNL(g) optNL(g)

View file

@ -135,11 +135,11 @@ proc hasIncludes(n:PNode): bool =
if a.kind == nkIncludeStmt: if a.kind == nkIncludeStmt:
return true return true
proc includeModule*(graph: ModuleGraph; s: PSym, fileIdx: int32; proc includeModule*(graph: ModuleGraph; s: PSym, fileIdx: FileIndex;
cache: IdentCache): PNode {.procvar.} = cache: IdentCache): PNode {.procvar.} =
result = syntaxes.parseFile(fileIdx, cache) result = syntaxes.parseFile(fileIdx, cache)
graph.addDep(s, fileIdx) graph.addDep(s, fileIdx)
graph.addIncludeDep(s.position.int32, fileIdx) graph.addIncludeDep(FileIndex s.position, fileIdx)
proc expandIncludes(graph: ModuleGraph, module: PSym, n: PNode, proc expandIncludes(graph: ModuleGraph, module: PSym, n: PNode,
modulePath: string, includedFiles: var IntSet, modulePath: string, includedFiles: var IntSet,
@ -153,13 +153,13 @@ proc expandIncludes(graph: ModuleGraph, module: PSym, n: PNode,
for i in 0..<a.len: for i in 0..<a.len:
var f = checkModuleName(a.sons[i]) var f = checkModuleName(a.sons[i])
if f != InvalidFileIDX: if f != InvalidFileIDX:
if containsOrIncl(includedFiles, f): if containsOrIncl(includedFiles, f.int):
localError(a.info, errRecursiveDependencyX, f.toFilename) localError(a.info, errRecursiveDependencyX, f.toFilename)
else: else:
let nn = includeModule(graph, module, f, cache) let nn = includeModule(graph, module, f, cache)
let nnn = expandIncludes(graph, module, nn, modulePath, let nnn = expandIncludes(graph, module, nn, modulePath,
includedFiles, cache) includedFiles, cache)
excl(includedFiles, f) excl(includedFiles, f.int)
for b in nnn: for b in nnn:
result.add b result.add b
else: else:
@ -429,7 +429,7 @@ proc reorder*(graph: ModuleGraph, n: PNode, module: PSym, cache: IdentCache): PN
if n.hasForbiddenPragma: if n.hasForbiddenPragma:
return n return n
var includedFiles = initIntSet() var includedFiles = initIntSet()
let mpath = module.fileIdx.toFullPath let mpath = module.fileIdx.FileIndex.toFullPath
let n = expandIncludes(graph, module, n, mpath, let n = expandIncludes(graph, module, n, mpath,
includedFiles, cache).splitSections includedFiles, cache).splitSections
result = newNodeI(nkStmtList, n.info) result = newNodeI(nkStmtList, n.info)

View file

@ -9,124 +9,18 @@
## This module implements the canonalization for the various caching mechanisms. ## This module implements the canonalization for the various caching mechanisms.
import ast, idgen import ast, idgen, msgs
when not defined(nimSymbolfiles): when not defined(nimSymbolfiles):
template setupModuleCache* = discard template setupModuleCache* = discard
template storeNode*(module: PSym; n: PNode) = discard template storeNode*(module: PSym; n: PNode) = discard
template loadNode*(module: PSym; index: var int): PNode = PNode(nil) template loadNode*(module: PSym; index: var int): PNode = PNode(nil)
template getModuleId*(fileIdx: int32; fullpath: string): int = getID() template getModuleId*(fileIdx: FileIndex; fullpath: string): int = getID()
template addModuleDep*(module, fileIdx: int32; isIncludeFile: bool) = discard template addModuleDep*(module, fileIdx: FileIndex; isIncludeFile: bool) = discard
template storeRemaining*(module: PSym) = discard template storeRemaining*(module: PSym) = discard
else: else:
include rodimpl include rodimpl
when false:
type
BlobWriter* = object
buf: string
pos: int
SerializationAction = enum acRead, acWrite
# Varint implementation inspired by SQLite.
proc rdVaruint64(z: ptr UncheckedArray[byte]; n: int; pResult: var uint64): int =
if z[0] <= 240:
pResult = z[0]
return 1
if z[0] <= 248:
if n < 2: return 0
pResult = (z[0] - 241) * 256 + z[1] + 240
return 2
if n < z[0]-246: return 0
if z[0] == 249:
pResult = 2288 + 256*z[1] + z[2]
return 3
if z[0] == 250:
pResult = (z[1] shl 16u64) + (z[2] shl 8u64) + z[3]
return 4
let x = (z[1] shl 24) + (z[2] shl 16) + (z[3] shl 8) + z[4]
if z[0] == 251:
pResult = x
return 5
if z[0] == 252:
pResult = (((uint64)x) shl 8) + z[5]
return 6
if z[0] == 253:
pResult = (((uint64)x) shl 16) + (z[5] shl 8) + z[6]
return 7
if z[0] == 254:
pResult = (((uint64)x) shl 24) + (z[5] shl 16) + (z[6] shl 8) + z[7]
return 8
pResult = (((uint64)x) shl 32) +
(0xffffffff & ((z[5] shl 24) + (z[6] shl 16) + (z[7] shl 8) + z[8]))
return 9
proc varintWrite32(z: ptr UncheckedArray[byte]; y: uint32) =
z[0] = uint8(y shr 24)
z[1] = uint8(y shr 16)
z[2] = uint8(y shr 8)
z[3] = uint8(y)
proc sqlite4PutVarint64(z: ptr UncheckedArray[byte], x: uint64): int =
## Write a varint into z. The buffer z must be at least 9 characters
## long to accommodate the largest possible varint. Returns the number of
## bytes used.
if x <= 240:
z[0] = uint8 x
return 1
if x <= 2287:
y = uint32(x - 240)
z[0] = uint8(y shr 8 + 241)
z[1] = uint8(y and 255)
return 2
if x <= 67823:
y = uint32(x - 2288)
z[0] = 249
z[1] = uint8(y shr 8)
z[2] = uint8(y and 255)
return 3
let y = uint32 x
let w = uint32(x shr 32)
if w == 0:
if y <= 16777215:
z[0] = 250
z[1] = uint8(y shr 16)
z[2] = uint8(y shr 8)
z[3] = uint8(y)
return 4
z[0] = 251
varintWrite32(z+1, y)
return 5
if w <= 255:
z[0] = 252
z[1] = uint8 w
varintWrite32(z+2, y)
return 6
if w <= 65535:
z[0] = 253
z[1] = uint8(w shr 8)
z[2] = uint8 w
varintWrite32(z+3, y)
return 7
if w <= 16777215:
z[0] = 254
z[1] = uint8(w shr 16)
z[2] = uint8(w shr 8)
z[3] = uint8 w
varintWrite32(z+4, y)
return 8
z[0] = 255
varintWrite32(z+1, w)
varintWrite32(z+5, y)
return 9
template field(x: BiggestInt; action: SerializationAction) =
when action == acRead:
readBiggestInt(x)
else:
writeBiggestInt()

View file

@ -126,8 +126,8 @@ type
s: cstring # mmap'ed file contents s: cstring # mmap'ed file contents
options: TOptions options: TOptions
reason: TReasonForRecompile reason: TReasonForRecompile
modDeps: seq[int32] modDeps: seq[FileIndex]
files: seq[int32] files: seq[FileIndex]
dataIdx: int # offset of start of data section dataIdx: int # offset of start of data section
convertersIdx: int # offset of start of converters section convertersIdx: int # offset of start of converters section
initIdx, interfIdx, compilerProcsIdx, methodsIdx: int initIdx, interfIdx, compilerProcsIdx, methodsIdx: int
@ -163,11 +163,11 @@ proc decodeLineInfo(r: PRodReader, info: var TLineInfo) =
else: info.col = int16(decodeVInt(r.s, r.pos)) else: info.col = int16(decodeVInt(r.s, r.pos))
if r.s[r.pos] == ',': if r.s[r.pos] == ',':
inc(r.pos) inc(r.pos)
if r.s[r.pos] == ',': info.line = -1'i16 if r.s[r.pos] == ',': info.line = 0'u16
else: info.line = int16(decodeVInt(r.s, r.pos)) else: info.line = uint16(decodeVInt(r.s, r.pos))
if r.s[r.pos] == ',': if r.s[r.pos] == ',':
inc(r.pos) inc(r.pos)
info = newLineInfo(r.files[decodeVInt(r.s, r.pos)], info.line, info.col) info = newLineInfo(r.files[decodeVInt(r.s, r.pos)], int info.line, info.col)
proc skipNode(r: PRodReader) = proc skipNode(r: PRodReader) =
assert r.s[r.pos] == '(' assert r.s[r.pos] == '('
@ -586,7 +586,7 @@ proc cmdChangeTriggersRecompilation(old, new: TCommands): bool =
# new command forces us to consider it here :-) # new command forces us to consider it here :-)
case old case old
of cmdCompileToC, cmdCompileToCpp, cmdCompileToOC, of cmdCompileToC, cmdCompileToCpp, cmdCompileToOC,
cmdCompileToJS, cmdCompileToPHP, cmdCompileToLLVM: cmdCompileToJS, cmdCompileToLLVM:
if new in {cmdDoc, cmdCheck, cmdIdeTools, cmdPretty, cmdDef, if new in {cmdDoc, cmdCheck, cmdIdeTools, cmdPretty, cmdDef,
cmdInteractive}: cmdInteractive}:
return false return false
@ -861,29 +861,29 @@ proc loadMethods(r: PRodReader) =
r.methods.add(rrGetSym(r, d, unknownLineInfo())) r.methods.add(rrGetSym(r, d, unknownLineInfo()))
if r.s[r.pos] == ' ': inc(r.pos) if r.s[r.pos] == ' ': inc(r.pos)
proc getHash*(fileIdx: int32): SecureHash = proc getHash*(fileIdx: FileIndex): SecureHash =
if fileIdx <% gMods.len and gMods[fileIdx].hashDone: if fileIdx.int32 <% gMods.len and gMods[fileIdx.int32].hashDone:
return gMods[fileIdx].hash return gMods[fileIdx.int32].hash
result = secureHashFile(fileIdx.toFullPath) result = secureHashFile(fileIdx.toFullPath)
if fileIdx >= gMods.len: setLen(gMods, fileIdx+1) if fileIdx.int32 >= gMods.len: setLen(gMods, fileIdx.int32+1)
gMods[fileIdx].hash = result gMods[fileIdx.int32].hash = result
template growCache*(cache, pos) = template growCache*(cache, pos) =
if cache.len <= pos: cache.setLen(pos+1) if cache.len <= pos: cache.setLen(pos+1)
proc checkDep(fileIdx: int32; cache: IdentCache): TReasonForRecompile = proc checkDep(fileIdx: FileIndex; cache: IdentCache): TReasonForRecompile =
assert fileIdx != InvalidFileIDX assert fileIdx != InvalidFileIDX
growCache gMods, fileIdx growCache gMods, fileIdx.int32
if gMods[fileIdx].reason != rrEmpty: if gMods[fileIdx.int32].reason != rrEmpty:
# reason has already been computed for this module: # reason has already been computed for this module:
return gMods[fileIdx].reason return gMods[fileIdx.int32].reason
let filename = fileIdx.toFilename let filename = fileIdx.toFilename
var hash = getHash(fileIdx) var hash = getHash(fileIdx)
gMods[fileIdx].reason = rrNone # we need to set it here to avoid cycles gMods[fileIdx.int32].reason = rrNone # we need to set it here to avoid cycles
result = rrNone result = rrNone
var rodfile = toGeneratedFile(filename.withPackageName, RodExt) var rodfile = toGeneratedFile(filename.withPackageName, RodExt)
var r = newRodReader(rodfile, hash, fileIdx, cache) var r = newRodReader(rodfile, hash, fileIdx.int32, cache)
if r == nil: if r == nil:
result = (if existsFile(rodfile): rrRodInvalid else: rrRodDoesNotExist) result = (if existsFile(rodfile): rrRodInvalid else: rrRodDoesNotExist)
else: else:
@ -907,19 +907,18 @@ proc checkDep(fileIdx: int32; cache: IdentCache): TReasonForRecompile =
# recompilation is necessary: # recompilation is necessary:
if r != nil: memfiles.close(r.memfile) if r != nil: memfiles.close(r.memfile)
r = nil r = nil
gMods[fileIdx].rd = r gMods[fileIdx.int32].rd = r
gMods[fileIdx].reason = result # now we know better gMods[fileIdx.int32].reason = result # now we know better
proc handleSymbolFile*(module: PSym; cache: IdentCache): PRodReader = proc handleSymbolFile*(module: PSym; cache: IdentCache): PRodReader =
let fileIdx = module.fileIdx
if gSymbolFiles in {disabledSf, writeOnlySf, v2Sf}: if gSymbolFiles in {disabledSf, writeOnlySf, v2Sf}:
module.id = getID() module.id = getID()
return nil return nil
idgen.loadMaxIds(options.gProjectPath / options.gProjectName) idgen.loadMaxIds(options.gProjectPath / options.gProjectName)
let fileIdx = FileIndex module.fileIdx
discard checkDep(fileIdx, cache) discard checkDep(fileIdx, cache)
if gMods[fileIdx].reason == rrEmpty: internalError("handleSymbolFile") if gMods[fileIdx.int32].reason == rrEmpty: internalError("handleSymbolFile")
result = gMods[fileIdx].rd result = gMods[fileIdx.int32].rd
if result != nil: if result != nil:
module.id = result.moduleID module.id = result.moduleID
result.syms[module.id] = module result.syms[module.id] = module
@ -1153,7 +1152,7 @@ proc viewFile(rodfile: string) =
if r.s[r.pos] == '\x0A': if r.s[r.pos] == '\x0A':
inc(r.pos) inc(r.pos)
inc(r.line) inc(r.line)
outf.write(w, " ", inclHash, "\n") outf.write(w.int32, " ", inclHash, "\n")
if r.s[r.pos] == ')': inc(r.pos) if r.s[r.pos] == ')': inc(r.pos)
outf.write(")\n") outf.write(")\n")
of "DEPS": of "DEPS":
@ -1163,7 +1162,7 @@ proc viewFile(rodfile: string) =
let v = int32(decodeVInt(r.s, r.pos)) let v = int32(decodeVInt(r.s, r.pos))
r.modDeps.add(r.files[v]) r.modDeps.add(r.files[v])
if r.s[r.pos] == ' ': inc(r.pos) if r.s[r.pos] == ' ': inc(r.pos)
outf.write(" ", r.files[v]) outf.write(" ", r.files[v].int32)
outf.write("\n") outf.write("\n")
of "INTERF", "COMPILERPROCS": of "INTERF", "COMPILERPROCS":
inc r.pos, 2 inc r.pos, 2

View file

@ -55,7 +55,7 @@ proc fileIdx(w: PRodWriter, filename: string): int =
w.files[result] = filename w.files[result] = filename
template filename*(w: PRodWriter): string = template filename*(w: PRodWriter): string =
w.module.filename toFilename(FileIndex w.module.position)
proc newRodWriter(hash: SecureHash, module: PSym; cache: IdentCache): PRodWriter = proc newRodWriter(hash: SecureHash, module: PSym; cache: IdentCache): PRodWriter =
new(result) new(result)
@ -125,14 +125,14 @@ proc encodeNode(w: PRodWriter, fInfo: TLineInfo, n: PNode,
result.add('?') result.add('?')
encodeVInt(n.info.col, result) encodeVInt(n.info.col, result)
result.add(',') result.add(',')
encodeVInt(n.info.line, result) encodeVInt(int n.info.line, result)
result.add(',') result.add(',')
encodeVInt(fileIdx(w, toFullPath(n.info)), result) encodeVInt(fileIdx(w, toFullPath(n.info)), result)
elif fInfo.line != n.info.line: elif fInfo.line != n.info.line:
result.add('?') result.add('?')
encodeVInt(n.info.col, result) encodeVInt(n.info.col, result)
result.add(',') result.add(',')
encodeVInt(n.info.line, result) encodeVInt(int n.info.line, result)
elif fInfo.col != n.info.col: elif fInfo.col != n.info.col:
result.add('?') result.add('?')
encodeVInt(n.info.col, result) encodeVInt(n.info.col, result)
@ -303,7 +303,7 @@ proc encodeSym(w: PRodWriter, s: PSym, result: var string) =
result.add('?') result.add('?')
if s.info.col != -1'i16: encodeVInt(s.info.col, result) if s.info.col != -1'i16: encodeVInt(s.info.col, result)
result.add(',') result.add(',')
if s.info.line != -1'i16: encodeVInt(s.info.line, result) if s.info.line != 0'u16: encodeVInt(int s.info.line, result)
result.add(',') result.add(',')
encodeVInt(fileIdx(w, toFullPath(s.info)), result) encodeVInt(fileIdx(w, toFullPath(s.info)), result)
if s.owner != nil: if s.owner != nil:
@ -642,7 +642,7 @@ proc process(c: PPassContext, n: PNode): PNode =
proc myOpen(g: ModuleGraph; module: PSym; cache: IdentCache): PPassContext = proc myOpen(g: ModuleGraph; module: PSym; cache: IdentCache): PPassContext =
if module.id < 0: internalError("rodwrite: module ID not set") if module.id < 0: internalError("rodwrite: module ID not set")
var w = newRodWriter(rodread.getHash module.fileIdx, module, cache) var w = newRodWriter(rodread.getHash FileIndex module.position, module, cache)
rawAddInterfaceSym(w, module) rawAddInterfaceSym(w, module)
result = w result = w

View file

@ -377,7 +377,7 @@ proc semAfterMacroCall(c: PContext, call, macroResult: PNode,
## reassigned, and binding the unbound identifiers that the macro output ## reassigned, and binding the unbound identifiers that the macro output
## contains. ## contains.
inc(evalTemplateCounter) inc(evalTemplateCounter)
if evalTemplateCounter > 100: if evalTemplateCounter > evalTemplateLimit:
globalError(s.info, errTemplateInstantiationTooNested) globalError(s.info, errTemplateInstantiationTooNested)
c.friendModules.add(s.owner.getModule) c.friendModules.add(s.owner.getModule)

View file

@ -144,7 +144,7 @@ proc makeInstPair*(s: PSym, inst: PInstantiation): TInstantiationPair =
proc filename*(c: PContext): string = proc filename*(c: PContext): string =
# the module's filename # the module's filename
return c.module.filename return toFilename(FileIndex c.module.position)
proc scopeDepth*(c: PContext): int {.inline.} = proc scopeDepth*(c: PContext): int {.inline.} =
result = if c.currentScope != nil: c.currentScope.depthLevel result = if c.currentScope != nil: c.currentScope.depthLevel

View file

@ -526,6 +526,14 @@ proc analyseIfAddressTakenInCall(c: PContext, n: PNode) =
if isAssignable(c, it) notin {arLValue, arLocalLValue}: if isAssignable(c, it) notin {arLValue, arLocalLValue}:
if it.kind != nkHiddenAddr: if it.kind != nkHiddenAddr:
localError(it.info, errVarForOutParamNeededX, $it) localError(it.info, errVarForOutParamNeededX, $it)
# bug #5113: disallow newSeq(result) where result is a 'var T':
if n[0].sym.magic in {mNew, mNewFinalize, mNewSeq}:
var arg = n[1] #.skipAddr
if arg.kind == nkHiddenDeref: arg = arg[0]
if arg.kind == nkSym and arg.sym.kind == skResult and
arg.typ.skipTypes(abstractInst).kind in {tyVar, tyLent}:
localError(n.info, errXStackEscape, renderTree(n[1], {renderNoComments}))
return return
for i in countup(1, sonsLen(n) - 1): for i in countup(1, sonsLen(n) - 1):
if n.sons[i].kind == nkHiddenCallConv: if n.sons[i].kind == nkHiddenCallConv:
@ -1334,11 +1342,11 @@ proc takeImplicitAddr(c: PContext, n: PNode; isLent: bool): PNode =
let root = exprRoot(n) let root = exprRoot(n)
if root != nil and root.owner == c.p.owner: if root != nil and root.owner == c.p.owner:
if root.kind in {skLet, skVar, skTemp} and sfGlobal notin root.flags: if root.kind in {skLet, skVar, skTemp} and sfGlobal notin root.flags:
localError(n.info, "'$1' escapes its stack frame; context: '$2'" % [ localError(n.info, "'$1' escapes its stack frame; context: '$2'; see $3/var_t_return.html" % [
root.name.s, renderTree(n, {renderNoComments})]) root.name.s, renderTree(n, {renderNoComments}), explanationsBaseUrl])
elif root.kind == skParam and root.position != 0: elif root.kind == skParam and root.position != 0:
localError(n.info, "'$1' is not the first parameter; context: '$2'" % [ localError(n.info, "'$1' is not the first parameter; context: '$2'; see $3/var_t_return.html" % [
root.name.s, renderTree(n, {renderNoComments})]) root.name.s, renderTree(n, {renderNoComments}), explanationsBaseUrl])
case n.kind case n.kind
of nkHiddenAddr, nkAddr: return n of nkHiddenAddr, nkAddr: return n
of nkHiddenDeref, nkDerefExpr: return n.sons[0] of nkHiddenDeref, nkDerefExpr: return n.sons[0]

View file

@ -483,6 +483,19 @@ proc newSymNodeTypeDesc*(s: PSym; info: TLineInfo): PNode =
proc getConstExpr(m: PSym, n: PNode): PNode = proc getConstExpr(m: PSym, n: PNode): PNode =
result = nil result = nil
proc getSrcTimestamp(): DateTime =
try:
result = utc(fromUnix(parseInt(getEnv("SOURCE_DATE_EPOCH",
"not a number"))))
except ValueError:
# Environment variable malformed.
# https://reproducible-builds.org/specs/source-date-epoch/: "If the
# value is malformed, the build process SHOULD exit with a non-zero
# error code", which this doesn't do. This uses local time, because
# that maintains compatibility with existing usage.
result = local(getTime())
case n.kind case n.kind
of nkSym: of nkSym:
var s = n.sym var s = n.sym
@ -492,8 +505,10 @@ proc getConstExpr(m: PSym, n: PNode): PNode =
of skConst: of skConst:
case s.magic case s.magic
of mIsMainModule: result = newIntNodeT(ord(sfMainModule in m.flags), n) of mIsMainModule: result = newIntNodeT(ord(sfMainModule in m.flags), n)
of mCompileDate: result = newStrNodeT(times.getDateStr(), n) of mCompileDate: result = newStrNodeT(format(getSrcTimestamp(),
of mCompileTime: result = newStrNodeT(times.getClockStr(), n) "yyyy-MM-dd"), n)
of mCompileTime: result = newStrNodeT(format(getSrcTimestamp(),
"HH:mm:ss"), n)
of mCpuEndian: result = newIntNodeT(ord(CPU[targetCPU].endian), n) of mCpuEndian: result = newIntNodeT(ord(CPU[targetCPU].endian), n)
of mHostOS: result = newStrNodeT(toLowerAscii(platform.OS[targetOS].name), n) of mHostOS: result = newStrNodeT(toLowerAscii(platform.OS[targetOS].name), n)
of mHostCPU: result = newStrNodeT(platform.CPU[targetCPU].name.toLowerAscii, n) of mHostCPU: result = newStrNodeT(platform.CPU[targetCPU].name.toLowerAscii, n)

View file

@ -779,20 +779,15 @@ proc semRaise(c: PContext, n: PNode): PNode =
result = n result = n
checkSonsLen(n, 1) checkSonsLen(n, 1)
if n[0].kind != nkEmpty: if n[0].kind != nkEmpty:
n[0] = semExprWithType(c, n[0]) n[0] = semExprWithType(c, n[0])
let typ = n[0].typ let typ = n[0].typ
if not isImportedException(typ): if not isImportedException(typ):
if typ.kind != tyRef or typ.lastSon.kind != tyObject: if typ.kind != tyRef or typ.lastSon.kind != tyObject:
localError(n.info, errExprCannotBeRaised) localError(n.info, errExprCannotBeRaised)
if not isException(typ.lastSon): if not isException(typ.lastSon):
localError(n.info, "raised object of type $1 does not inherit from Exception", localError(n.info, "raised object of type $1 does not inherit from Exception",
[typeToString(typ)]) [typeToString(typ)])
proc addGenericParamListToScope(c: PContext, n: PNode) = proc addGenericParamListToScope(c: PContext, n: PNode) =
if n.kind != nkGenericParams: illFormedAst(n) if n.kind != nkGenericParams: illFormedAst(n)
for i in countup(0, sonsLen(n)-1): for i in countup(0, sonsLen(n)-1):
@ -1100,12 +1095,12 @@ proc semAllTypeSections(c: PContext; n: PNode): PNode =
for i in 0..<n.len: for i in 0..<n.len:
var f = checkModuleName(n.sons[i]) var f = checkModuleName(n.sons[i])
if f != InvalidFileIDX: if f != InvalidFileIDX:
if containsOrIncl(c.includedFiles, f): if containsOrIncl(c.includedFiles, f.int):
localError(n.info, errRecursiveDependencyX, f.toFilename) localError(n.info, errRecursiveDependencyX, f.toFilename)
else: else:
let code = gIncludeFile(c.graph, c.module, f, c.cache) let code = gIncludeFile(c.graph, c.module, f, c.cache)
gatherStmts c, code, result gatherStmts c, code, result
excl(c.includedFiles, f) excl(c.includedFiles, f.int)
of nkStmtList: of nkStmtList:
for i in 0 ..< n.len: for i in 0 ..< n.len:
gatherStmts(c, n.sons[i], result) gatherStmts(c, n.sons[i], result)
@ -1789,11 +1784,11 @@ proc evalInclude(c: PContext, n: PNode): PNode =
for i in countup(0, sonsLen(n) - 1): for i in countup(0, sonsLen(n) - 1):
var f = checkModuleName(n.sons[i]) var f = checkModuleName(n.sons[i])
if f != InvalidFileIDX: if f != InvalidFileIDX:
if containsOrIncl(c.includedFiles, f): if containsOrIncl(c.includedFiles, f.int):
localError(n.info, errRecursiveDependencyX, f.toFilename) localError(n.info, errRecursiveDependencyX, f.toFilename)
else: else:
addSon(result, semStmt(c, gIncludeFile(c.graph, c.module, f, c.cache))) addSon(result, semStmt(c, gIncludeFile(c.graph, c.module, f, c.cache)))
excl(c.includedFiles, f) excl(c.includedFiles, f.int)
proc setLine(n: PNode, info: TLineInfo) = proc setLine(n: PNode, info: TLineInfo) =
for i in 0 ..< safeLen(n): setLine(n.sons[i], info) for i in 0 ..< safeLen(n): setLine(n.sons[i], info)

View file

@ -415,7 +415,7 @@ when defined(nimsuggest):
# Since TLineInfo defined a == operator that doesn't include the column, # Since TLineInfo defined a == operator that doesn't include the column,
# we map TLineInfo to a unique int here for this lookup table: # we map TLineInfo to a unique int here for this lookup table:
proc infoToInt(info: TLineInfo): int64 = proc infoToInt(info: TLineInfo): int64 =
info.fileIndex + info.line.int64 shl 32 + info.col.int64 shl 48 info.fileIndex.int64 + info.line.int64 shl 32 + info.col.int64 shl 48
proc addNoDup(s: PSym; info: TLineInfo) = proc addNoDup(s: PSym; info: TLineInfo) =
# ensure nothing gets too slow: # ensure nothing gets too slow:
@ -574,7 +574,7 @@ proc suggestEnum*(c: PContext; n: PNode; t: PType) =
if outputs.len > 0: suggestQuit() if outputs.len > 0: suggestQuit()
proc suggestSentinel*(c: PContext) = proc suggestSentinel*(c: PContext) =
if gIdeCmd != ideSug or c.module.position != gTrackPos.fileIndex: return if gIdeCmd != ideSug or c.module.position != gTrackPos.fileIndex.int32: return
if c.compilesContextId > 0: return if c.compilesContextId > 0: return
inc(c.compilesContextId) inc(c.compilesContextId)
var outputs: Suggestions = @[] var outputs: Suggestions = @[]

View file

@ -138,7 +138,7 @@ proc evalPipe(p: var TParsers, n: PNode, filename: string,
else: else:
result = applyFilter(p, n, filename, result) result = applyFilter(p, n, filename, result)
proc openParsers*(p: var TParsers, fileIdx: int32, inputstream: PLLStream; proc openParsers*(p: var TParsers, fileIdx: FileIndex, inputstream: PLLStream;
cache: IdentCache) = cache: IdentCache) =
var s: PLLStream var s: PLLStream
p.skin = skinStandard p.skin = skinStandard
@ -155,7 +155,7 @@ proc openParsers*(p: var TParsers, fileIdx: int32, inputstream: PLLStream;
proc closeParsers*(p: var TParsers) = proc closeParsers*(p: var TParsers) =
parser.closeParser(p.parser) parser.closeParser(p.parser)
proc parseFile*(fileIdx: int32; cache: IdentCache): PNode {.procvar.} = proc parseFile*(fileIdx: FileIndex; cache: IdentCache): PNode {.procvar.} =
var var
p: TParsers p: TParsers
f: File f: File

View file

@ -777,8 +777,8 @@ proc equalParams(a, b: PNode): TParamsEquality =
return paramsNotEqual # paramsIncompatible; return paramsNotEqual # paramsIncompatible;
# continue traversal! If not equal, we can return immediately; else # continue traversal! If not equal, we can return immediately; else
# it stays incompatible # it stays incompatible
if not sameTypeOrNil(a.sons[0].typ, b.sons[0].typ, {ExactTypeDescValues}): if not sameTypeOrNil(a.typ, b.typ, {ExactTypeDescValues}):
if (a.sons[0].typ == nil) or (b.sons[0].typ == nil): if (a.typ == nil) or (b.typ == nil):
result = paramsNotEqual # one proc has a result, the other not is OK result = paramsNotEqual # one proc has a result, the other not is OK
else: else:
result = paramsIncompatible # overloading by different result = paramsIncompatible # overloading by different
@ -970,7 +970,7 @@ proc sameTypeAux(x, y: PType, c: var TSameTypeClosure): bool =
result = sameFlags(a, b) result = sameFlags(a, b)
of tyGenericParam: of tyGenericParam:
result = sameChildrenAux(a, b, c) and sameFlags(a, b) result = sameChildrenAux(a, b, c) and sameFlags(a, b)
if result and ExactGenericParams in c.flags: if result and {ExactGenericParams, ExactTypeDescValues} * c.flags != {}:
result = a.sym.position == b.sym.position result = a.sym.position == b.sym.position
of tyGenericInvocation, tyGenericBody, tySequence, of tyGenericInvocation, tyGenericBody, tySequence,
tyOpenArray, tySet, tyRef, tyPtr, tyVar, tyLent, tySink, tyOpenArray, tySet, tyRef, tyPtr, tyVar, tyLent, tySink,

View file

@ -1381,7 +1381,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
of opcNGetLine: of opcNGetLine:
decodeB(rkNode) decodeB(rkNode)
let n = regs[rb].node let n = regs[rb].node
regs[ra].node = newIntNode(nkIntLit, n.info.line) regs[ra].node = newIntNode(nkIntLit, n.info.line.int)
regs[ra].node.info = n.info regs[ra].node.info = n.info
regs[ra].node.typ = n.typ regs[ra].node.typ = n.typ
of opcNGetColumn: of opcNGetColumn:
@ -1397,8 +1397,8 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
let bNode = regs[rc].node let bNode = regs[rc].node
# these are cstring to prevent string copy, and cmpIgnoreStyle from # these are cstring to prevent string copy, and cmpIgnoreStyle from
# takes cstring arguments # takes cstring arguments
var aStrVal: cstring var aStrVal: cstring = nil
var bStrVal: cstring var bStrVal: cstring = nil
# extract strVal from argument ``a`` # extract strVal from argument ``a``
case aNode.kind case aNode.kind
of {nkStrLit..nkTripleStrLit}: of {nkStrLit..nkTripleStrLit}:
@ -1407,8 +1407,10 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
aStrVal = aNode.ident.s.cstring aStrVal = aNode.ident.s.cstring
of nkSym: of nkSym:
aStrVal = aNode.sym.name.s.cstring aStrVal = aNode.sym.name.s.cstring
of nkOpenSymChoice, nkClosedSymChoice:
aStrVal = aNode[0].sym.name.s.cstring
else: else:
stackTrace(c, tos, pc, errFieldXNotFound, "strVal") discard
# extract strVal from argument ``b`` # extract strVal from argument ``b``
case bNode.kind case bNode.kind
of {nkStrLit..nkTripleStrLit}: of {nkStrLit..nkTripleStrLit}:
@ -1417,11 +1419,17 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
bStrVal = bNode.ident.s.cstring bStrVal = bNode.ident.s.cstring
of nkSym: of nkSym:
bStrVal = bNode.sym.name.s.cstring bStrVal = bNode.sym.name.s.cstring
of nkOpenSymChoice, nkClosedSymChoice:
bStrVal = bNode[0].sym.name.s.cstring
else: else:
stackTrace(c, tos, pc, errFieldXNotFound, "strVal") discard
# set result # set result
regs[ra].intVal = regs[ra].intVal =
if aStrVal != nil and bStrVal != nil:
ord(idents.cmpIgnoreStyle(aStrVal,bStrVal,high(int)) == 0) ord(idents.cmpIgnoreStyle(aStrVal,bStrVal,high(int)) == 0)
else:
0
of opcStrToIdent: of opcStrToIdent:
decodeB(rkNode) decodeB(rkNode)
if regs[rb].node.kind notin {nkStrLit..nkTripleStrLit}: if regs[rb].node.kind notin {nkStrLit..nkTripleStrLit}:
@ -1513,7 +1521,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
let x = newNodeI(TNodeKind(int(k)), let x = newNodeI(TNodeKind(int(k)),
if cc.kind != nkNilLit: if cc.kind != nkNilLit:
cc.info cc.info
elif c.comesFromHeuristic.line > -1: elif c.comesFromHeuristic.line != 0'u16:
c.comesFromHeuristic c.comesFromHeuristic
elif c.callsite != nil and c.callsite.safeLen > 1: elif c.callsite != nil and c.callsite.safeLen > 1:
c.callsite[1].info c.callsite[1].info
@ -1685,7 +1693,7 @@ proc evalConstExprAux(module: PSym; cache: IdentCache; prc: PSym, n: PNode,
newSeq(tos.slots, c.prc.maxSlots) newSeq(tos.slots, c.prc.maxSlots)
#for i in 0 ..< c.prc.maxSlots: tos.slots[i] = newNode(nkEmpty) #for i in 0 ..< c.prc.maxSlots: tos.slots[i] = newNode(nkEmpty)
result = rawExecute(c, start, tos).regToNode result = rawExecute(c, start, tos).regToNode
if result.info.line < 0: result.info = n.info if result.info.col < 0: result.info = n.info
proc evalConstExpr*(module: PSym; cache: IdentCache, e: PNode): PNode = proc evalConstExpr*(module: PSym; cache: IdentCache, e: PNode): PNode =
result = evalConstExprAux(module, cache, nil, e, emConst) result = evalConstExprAux(module, cache, nil, e, emConst)
@ -1721,14 +1729,16 @@ iterator genericParamsInMacroCall*(macroSym: PSym, call: PNode): (PSym, PNode) =
let posInCall = macroSym.typ.len + i let posInCall = macroSym.typ.len + i
yield (genericParam, call[posInCall]) yield (genericParam, call[posInCall])
# to prevent endless recursion in macro instantiation
const evalMacroLimit = 1000
var evalMacroCounter: int var evalMacroCounter: int
proc evalMacroCall*(module: PSym; cache: IdentCache, n, nOrig: PNode, proc evalMacroCall*(module: PSym; cache: IdentCache, n, nOrig: PNode,
sym: PSym): PNode = sym: PSym): PNode =
# XXX globalError() is ugly here, but I don't know a better solution for now # XXX globalError() is ugly here, but I don't know a better solution for now
inc(evalMacroCounter) inc(evalMacroCounter)
if evalMacroCounter > 100: if evalMacroCounter > evalMacroLimit:
globalError(n.info, errTemplateInstantiationTooNested) globalError(n.info, errMacroInstantiationTooNested)
# immediate macros can bypass any type and arity checking so we check the # immediate macros can bypass any type and arity checking so we check the
# arity here too: # arity here too:
@ -1738,7 +1748,7 @@ proc evalMacroCall*(module: PSym; cache: IdentCache, n, nOrig: PNode,
setupGlobalCtx(module, cache) setupGlobalCtx(module, cache)
var c = globalCtx var c = globalCtx
c.comesFromHeuristic.line = -1 c.comesFromHeuristic.line = 0'u16
c.callsite = nOrig c.callsite = nOrig
let start = genProc(c, sym) let start = genProc(c, sym)

View file

@ -36,7 +36,7 @@ Options:
--app:console|gui|lib|staticlib --app:console|gui|lib|staticlib
generate a console app|GUI app|DLL|static library generate a console app|GUI app|DLL|static library
-r, --run run the compiled program with given arguments -r, --run run the compiled program with given arguments
--advanced show advanced command line switches --fullhelp show all command line switches
-h, --help show this help -h, --help show this help
Note, single letter options that take an argument require a colon. E.g. -p:PATH. Note, single letter options that take an argument require a colon. E.g. -p:PATH.

View file

@ -3487,17 +3487,17 @@ returned value is an l-value and can be modified by the caller:
.. code-block:: nim .. code-block:: nim
var g = 0 var g = 0
proc WriteAccessToG(): var int = proc writeAccessToG(): var int =
result = g result = g
WriteAccessToG() = 6 writeAccessToG() = 6
assert g == 6 assert g == 6
It is a compile time error if the implicitly introduced pointer could be It is a compile time error if the implicitly introduced pointer could be
used to access a location beyond its lifetime: used to access a location beyond its lifetime:
.. code-block:: nim .. code-block:: nim
proc WriteAccessToG(): var int = proc writeAccessToG(): var int =
var g = 0 var g = 0
result = g # Error! result = g # Error!
@ -3512,6 +3512,24 @@ In the standard library every name of a routine that returns a ``var`` type
starts with the prefix ``m`` per convention. starts with the prefix ``m`` per convention.
.. include:: manual/var_t_return.rst
Future directions
~~~~~~~~~~~~~~~~~
Later versions of Nim can be more precise about the borrowing rule with
a syntax like:
.. code-block:: nim
proc foo(other: Y; container: var X): var T from container
Here ``var T from container`` explicitly exposes that the
location is deviated from the second parameter (called
'container' in this case). The syntax ``var T from p`` specifies a type
``varTy[T, 2]`` which is incompatible with ``varTy[T, 1]``.
Overloading of the subscript operator Overloading of the subscript operator
------------------------------------- -------------------------------------
@ -5241,15 +5259,21 @@ chance to convert it into a sequence.
Macros Macros
====== ======
A macro is a special kind of low level template. Macros can be used A macro is a special function that is executed at compile-time.
to implement `domain specific languages`:idx:. Normally the input for a macro is an abstract syntax
tree (AST) of the code that is passed to it. The macro can then do
transformations on it and return the transformed AST. The
transformed AST is then passed to the compiler as if the macro
invocation would have been replaced by its result in the source
code. This can be used to implement `domain specific
languages`:idx:.
While macros enable advanced compile-time code transformations, they While macros enable advanced compile-time code transformations, they
cannot change Nim's syntax. However, this is no real restriction because cannot change Nim's syntax. However, this is no real restriction because
Nim's syntax is flexible enough anyway. Nim's syntax is flexible enough anyway.
To write macros, one needs to know how the Nim concrete syntax is converted To write macros, one needs to know how the Nim concrete syntax is converted
to an abstract syntax tree. to an AST.
There are two ways to invoke a macro: There are two ways to invoke a macro:
(1) invoking a macro like a procedure call (`expression macros`) (1) invoking a macro like a procedure call (`expression macros`)
@ -5269,19 +5293,21 @@ variable number of arguments:
# ``macros`` module: # ``macros`` module:
import macros import macros
macro debug(n: varargs[untyped]): untyped = macro debug(args: varargs[untyped]): untyped =
# `n` is a Nim AST that contains the whole macro invocation # `args` is a collection of `NimNode` values that each contain the
# this macro returns a list of statements: # AST for an argument of the macro. A macro always has to
result = newNimNode(nnkStmtList, n) # return a `NimNode`. A node of kind `nnkStmtList` is suitable for
# this use case.
result = nnkStmtList.newTree()
# iterate over any argument that is passed to this macro: # iterate over any argument that is passed to this macro:
for i in 0..n.len-1: for n in args:
# add a call to the statement list that writes the expression; # add a call to the statement list that writes the expression;
# `toStrLit` converts an AST to its string representation: # `toStrLit` converts an AST to its string representation:
add(result, newCall("write", newIdentNode("stdout"), toStrLit(n[i]))) result.add newCall("write", newIdentNode("stdout"), newLit(n.repr))
# add a call to the statement list that writes ": " # add a call to the statement list that writes ": "
add(result, newCall("write", newIdentNode("stdout"), newStrLitNode(": "))) result.add newCall("write", newIdentNode("stdout"), newLit(": "))
# add a call to the statement list that writes the expressions value: # add a call to the statement list that writes the expressions value:
add(result, newCall("writeLine", newIdentNode("stdout"), n[i])) result.add newCall("writeLine", newIdentNode("stdout"), n)
var var
a: array[0..10, int] a: array[0..10, int]
@ -8187,5 +8213,3 @@ validation errors:
If the taint mode is turned off, ``TaintedString`` is simply an alias for If the taint mode is turned off, ``TaintedString`` is simply an alias for
``string``. ``string``.

View file

@ -0,0 +1,20 @@
Memory safety for returning by ``var T`` is ensured by a simple borrowing
rule: If ``result`` does not refer to a location pointing to the heap
(that is in ``result = X`` the ``X`` involves a ``ptr`` or ``ref`` access)
then it has to be deviated by the routine's first parameter:
.. code-block:: nim
proc forward[T](x: var T): var T =
result = x # ok, deviated from the first parameter.
proc p(param: var int): var int =
var x: int
# we know 'forward' provides a view into the location deviated by
# its first argument 'x'.
result = forward(x) # Error: location is derived from ``x``
# which is not p's first parameter and lives
# on the stack.
In other words, the lifetime of what ``result`` points to is attached to the
lifetime of the first parameter and that is enough knowledge to verify
memory safety at the callsite.

View file

@ -706,8 +706,7 @@ proc treeRepr*(n: NimNode): string {.compileTime, benign.} =
res.add(($n.kind).substr(3)) res.add(($n.kind).substr(3))
case n.kind case n.kind
of nnkEmpty: discard # same as nil node in this representation of nnkEmpty, nnkNilLit: discard # same as nil node in this representation
of nnkNilLit: res.add(" nil")
of nnkCharLit..nnkInt64Lit: res.add(" " & $n.intVal) of nnkCharLit..nnkInt64Lit: res.add(" " & $n.intVal)
of nnkFloatLit..nnkFloat64Lit: res.add(" " & $n.floatVal) of nnkFloatLit..nnkFloat64Lit: res.add(" " & $n.floatVal)
of nnkStrLit..nnkTripleStrLit, nnkIdent, nnkSym: of nnkStrLit..nnkTripleStrLit, nnkIdent, nnkSym:
@ -730,8 +729,7 @@ proc lispRepr*(n: NimNode): string {.compileTime, benign.} =
add(result, "(") add(result, "(")
case n.kind case n.kind
of nnkEmpty: discard # same as nil node in this representation of nnkEmpty, nnkNilLit: discard # same as nil node in this representation
of nnkNilLit: add(result, "nil")
of nnkCharLit..nnkInt64Lit: add(result, $n.intVal) of nnkCharLit..nnkInt64Lit: add(result, $n.intVal)
of nnkFloatLit..nnkFloat64Lit: add(result, $n.floatVal) of nnkFloatLit..nnkFloat64Lit: add(result, $n.floatVal)
of nnkStrLit..nnkTripleStrLit, nnkCommentStmt, nnkident, nnkSym: of nnkStrLit..nnkTripleStrLit, nnkCommentStmt, nnkident, nnkSym:
@ -766,7 +764,7 @@ proc astGenRepr*(n: NimNode): string {.compileTime, benign.} =
## See also `repr`, `treeRepr`, and `lispRepr`. ## See also `repr`, `treeRepr`, and `lispRepr`.
const const
NodeKinds = {nnkEmpty, nnkNilLit, nnkIdent, nnkSym, nnkNone, nnkCommentStmt} NodeKinds = {nnkEmpty, nnkIdent, nnkSym, nnkNone, nnkCommentStmt}
LitKinds = {nnkCharLit..nnkInt64Lit, nnkFloatLit..nnkFloat64Lit, nnkStrLit..nnkTripleStrLit} LitKinds = {nnkCharLit..nnkInt64Lit, nnkFloatLit..nnkFloat64Lit, nnkStrLit..nnkTripleStrLit}
proc traverse(res: var string, level: int, n: NimNode) {.benign.} = proc traverse(res: var string, level: int, n: NimNode) {.benign.} =
@ -775,12 +773,13 @@ proc astGenRepr*(n: NimNode): string {.compileTime, benign.} =
res.add("new" & ($n.kind).substr(3) & "Node(") res.add("new" & ($n.kind).substr(3) & "Node(")
elif n.kind in LitKinds: elif n.kind in LitKinds:
res.add("newLit(") res.add("newLit(")
elif n.kind == nnkNilLit:
res.add("newNilLit()")
else: else:
res.add($n.kind) res.add($n.kind)
case n.kind case n.kind
of nnkEmpty: discard of nnkEmpty, nnkNilLit: discard
of nnkNilLit: res.add("nil")
of nnkCharLit: res.add("'" & $chr(n.intVal) & "'") of nnkCharLit: res.add("'" & $chr(n.intVal) & "'")
of nnkIntLit..nnkInt64Lit: res.add($n.intVal) of nnkIntLit..nnkInt64Lit: res.add($n.intVal)
of nnkFloatLit..nnkFloat64Lit: res.add($n.floatVal) of nnkFloatLit..nnkFloat64Lit: res.add($n.floatVal)
@ -1289,7 +1288,11 @@ proc customPragmaNode(n: NimNode): NimNode =
typ = n.getTypeInst() typ = n.getTypeInst()
if typ.typeKind == ntyTypeDesc: if typ.typeKind == ntyTypeDesc:
return typ[1].getImpl()[0][1] let impl = typ[1].getImpl()
if impl[0].kind == nnkPragmaExpr:
return impl[0][1]
else:
return impl[0] # handle types which don't have macro at all
if n.kind == nnkSym: # either an variable or a proc if n.kind == nnkSym: # either an variable or a proc
let impl = n.getImpl() let impl = n.getImpl()

View file

@ -101,8 +101,8 @@ when defined(windows):
from winlean import TimeVal, SocketHandle, FD_SET, FD_ZERO, TFdSet, from winlean import TimeVal, SocketHandle, FD_SET, FD_ZERO, TFdSet,
FD_ISSET, select FD_ISSET, select
else: else:
from posix import TimeVal, SocketHandle, FD_SET, FD_ZERO, TFdSet, from posix import TimeVal, Time, Suseconds, SocketHandle, FD_SET, FD_ZERO,
FD_ISSET, select TFdSet, FD_ISSET, select
type type
DelegateObj* = object DelegateObj* = object
@ -556,6 +556,10 @@ proc send*(sock: AsyncSocket, data: string) =
proc timeValFromMilliseconds(timeout = 500): Timeval = proc timeValFromMilliseconds(timeout = 500): Timeval =
if timeout != -1: if timeout != -1:
var seconds = timeout div 1000 var seconds = timeout div 1000
when defined(posix):
result.tv_sec = seconds.Time
result.tv_usec = ((timeout - seconds * 1000) * 1000).Suseconds
else:
result.tv_sec = seconds.int32 result.tv_sec = seconds.int32
result.tv_usec = ((timeout - seconds * 1000) * 1000).int32 result.tv_usec = ((timeout - seconds * 1000) * 1000).int32

View file

@ -953,6 +953,10 @@ when defined(ssl):
proc timeValFromMilliseconds(timeout = 500): Timeval = proc timeValFromMilliseconds(timeout = 500): Timeval =
if timeout != -1: if timeout != -1:
var seconds = timeout div 1000 var seconds = timeout div 1000
when defined(posix):
result.tv_sec = seconds.Time
result.tv_usec = ((timeout - seconds * 1000) * 1000).Suseconds
else:
result.tv_sec = seconds.int32 result.tv_sec = seconds.int32
result.tv_usec = ((timeout - seconds * 1000) * 1000).int32 result.tv_usec = ((timeout - seconds * 1000) * 1000).int32

View file

@ -351,8 +351,8 @@ type
Timeval* {.importc: "struct timeval", header: "<sys/select.h>", Timeval* {.importc: "struct timeval", header: "<sys/select.h>",
final, pure.} = object ## struct timeval final, pure.} = object ## struct timeval
tv_sec*: clong ## Seconds. tv_sec*: Time ## Seconds.
tv_usec*: clong ## Microseconds. tv_usec*: Suseconds ## Microseconds.
TFdSet* {.importc: "fd_set", header: "<sys/select.h>", TFdSet* {.importc: "fd_set", header: "<sys/select.h>",
final, pure.} = object final, pure.} = object
abi: array[1024 div (8 * sizeof(clong)), clong] abi: array[1024 div (8 * sizeof(clong)), clong]

View file

@ -335,8 +335,8 @@ type
Timeval* {.importc: "struct timeval", header: "<sys/select.h>", Timeval* {.importc: "struct timeval", header: "<sys/select.h>",
final, pure.} = object ## struct timeval final, pure.} = object ## struct timeval
tv_sec*: int ## Seconds. tv_sec*: Time ## Seconds.
tv_usec*: int ## Microseconds. tv_usec*: Suseconds ## Microseconds.
TFdSet* {.importc: "fd_set", header: "<sys/select.h>", TFdSet* {.importc: "fd_set", header: "<sys/select.h>",
final, pure.} = object final, pure.} = object
Mcontext* {.importc: "mcontext_t", header: "<ucontext.h>", Mcontext* {.importc: "mcontext_t", header: "<ucontext.h>",

View file

@ -168,6 +168,15 @@ proc wakeupWorkerToProcessQueue(w: ptr Worker) =
signal(w.q.empty) signal(w.q.empty)
signal(w.taskArrived) signal(w.taskArrived)
proc attach(fv: FlowVarBase; i: int): bool =
acquire(fv.cv.L)
if fv.cv.counter <= 0:
fv.idx = i
result = true
else:
result = false
release(fv.cv.L)
proc finished(fv: FlowVarBase) = proc finished(fv: FlowVarBase) =
doAssert fv.ai.isNil, "flowVar is still attached to an 'awaitAny'" doAssert fv.ai.isNil, "flowVar is still attached to an 'awaitAny'"
# we have to protect against the rare cases where the owner of the flowVar # we have to protect against the rare cases where the owner of the flowVar
@ -245,26 +254,27 @@ proc `^`*[T](fv: FlowVar[T]): T =
proc awaitAny*(flowVars: openArray[FlowVarBase]): int = proc awaitAny*(flowVars: openArray[FlowVarBase]): int =
## awaits any of the given flowVars. Returns the index of one flowVar for ## awaits any of the given flowVars. Returns the index of one flowVar for
## which a value arrived. A flowVar only supports one call to 'awaitAny' at ## which a value arrived. A flowVar only supports one call to 'awaitAny' at
## the same time. That means if you await([a,b]) and await([b,c]) the second ## the same time. That means if you awaitAny([a,b]) and awaitAny([b,c]) the second
## call will only await 'c'. If there is no flowVar left to be able to wait ## call will only await 'c'. If there is no flowVar left to be able to wait
## on, -1 is returned. ## on, -1 is returned.
## **Note**: This results in non-deterministic behaviour and so should be ## **Note**: This results in non-deterministic behaviour and should be avoided.
## avoided.
var ai: AwaitInfo var ai: AwaitInfo
ai.cv.initSemaphore() ai.cv.initSemaphore()
var conflicts = 0 var conflicts = 0
result = -1
for i in 0 .. flowVars.high: for i in 0 .. flowVars.high:
if cas(addr flowVars[i].ai, nil, addr ai): if cas(addr flowVars[i].ai, nil, addr ai):
flowVars[i].idx = i if not attach(flowVars[i], i):
result = i
break
else: else:
inc conflicts inc conflicts
if conflicts < flowVars.len: if conflicts < flowVars.len:
if result < 0:
await(ai.cv) await(ai.cv)
result = ai.idx result = ai.idx
for i in 0 .. flowVars.high: for i in 0 .. flowVars.high:
discard cas(addr flowVars[i].ai, addr ai, nil) discard cas(addr flowVars[i].ai, addr ai, nil)
else:
result = -1
destroySemaphore(ai.cv) destroySemaphore(ai.cv)
proc isReady*(fv: FlowVarBase): bool = proc isReady*(fv: FlowVarBase): bool =

View file

@ -616,8 +616,12 @@ proc setBlocking*(s: SocketHandle, blocking: bool) =
proc timeValFromMilliseconds(timeout = 500): Timeval = proc timeValFromMilliseconds(timeout = 500): Timeval =
if timeout != -1: if timeout != -1:
var seconds = timeout div 1000 var seconds = timeout div 1000
when useWinVersion:
result.tv_sec = seconds.int32 result.tv_sec = seconds.int32
result.tv_usec = ((timeout - seconds * 1000) * 1000).int32 result.tv_usec = ((timeout - seconds * 1000) * 1000).int32
else:
result.tv_sec = seconds.Time
result.tv_usec = ((timeout - seconds * 1000) * 1000).Suseconds
proc createFdSet(fd: var TFdSet, s: seq[SocketHandle], m: var int) = proc createFdSet(fd: var TFdSet, s: seq[SocketHandle], m: var int) =
FD_ZERO(fd) FD_ZERO(fd)

View file

@ -104,6 +104,10 @@ proc none*(T: typedesc): Option[T] =
# the default is the none type # the default is the none type
discard discard
proc none*[T]: Option[T] =
## Alias for ``none(T)``.
none(T)
proc isSome*[T](self: Option[T]): bool {.inline.} = proc isSome*[T](self: Option[T]): bool {.inline.} =
when T is SomePointer: when T is SomePointer:
self.val != nil self.val != nil
@ -290,3 +294,7 @@ when isMainModule:
let tmp = option(intref) let tmp = option(intref)
check(sizeof(tmp) == sizeof(ptr int)) check(sizeof(tmp) == sizeof(ptr int))
test "none[T]":
check(none[int]().isNone)
check(none(int) == none[int]())

View file

@ -188,7 +188,7 @@ proc getLastModificationTime*(file: string): times.Time {.rtl, extern: "nos$1".}
var f: WIN32_FIND_DATA var f: WIN32_FIND_DATA
var h = findFirstFile(file, f) var h = findFirstFile(file, f)
if h == -1'i32: raiseOSError(osLastError()) if h == -1'i32: raiseOSError(osLastError())
result = fromUnix(winTimeToUnixTime(rdFileTime(f.ftLastWriteTime)).int64) result = fromWinTime(rdFileTime(f.ftLastWriteTime))
findClose(h) findClose(h)
proc getLastAccessTime*(file: string): times.Time {.rtl, extern: "nos$1".} = proc getLastAccessTime*(file: string): times.Time {.rtl, extern: "nos$1".} =
@ -201,7 +201,7 @@ proc getLastAccessTime*(file: string): times.Time {.rtl, extern: "nos$1".} =
var f: WIN32_FIND_DATA var f: WIN32_FIND_DATA
var h = findFirstFile(file, f) var h = findFirstFile(file, f)
if h == -1'i32: raiseOSError(osLastError()) if h == -1'i32: raiseOSError(osLastError())
result = fromUnix(winTimeToUnixTime(rdFileTime(f.ftLastAccessTime)).int64) result = fromWinTime(rdFileTime(f.ftLastAccessTime))
findClose(h) findClose(h)
proc getCreationTime*(file: string): times.Time {.rtl, extern: "nos$1".} = proc getCreationTime*(file: string): times.Time {.rtl, extern: "nos$1".} =
@ -218,7 +218,7 @@ proc getCreationTime*(file: string): times.Time {.rtl, extern: "nos$1".} =
var f: WIN32_FIND_DATA var f: WIN32_FIND_DATA
var h = findFirstFile(file, f) var h = findFirstFile(file, f)
if h == -1'i32: raiseOSError(osLastError()) if h == -1'i32: raiseOSError(osLastError())
result = fromUnix(winTimeToUnixTime(rdFileTime(f.ftCreationTime)).int64) result = fromWinTime(rdFileTime(f.ftCreationTime))
findClose(h) findClose(h)
proc fileNewer*(a, b: string): bool {.rtl, extern: "nos$1".} = proc fileNewer*(a, b: string): bool {.rtl, extern: "nos$1".} =
@ -329,20 +329,21 @@ proc expandFilename*(filename: string): string {.rtl, extern: "nos$1",
c_free(cast[pointer](r)) c_free(cast[pointer](r))
when defined(Windows): when defined(Windows):
proc openHandle(path: string, followSymlink=true): Handle = proc openHandle(path: string, followSymlink=true, writeAccess=false): Handle =
var flags = FILE_FLAG_BACKUP_SEMANTICS or FILE_ATTRIBUTE_NORMAL var flags = FILE_FLAG_BACKUP_SEMANTICS or FILE_ATTRIBUTE_NORMAL
if not followSymlink: if not followSymlink:
flags = flags or FILE_FLAG_OPEN_REPARSE_POINT flags = flags or FILE_FLAG_OPEN_REPARSE_POINT
let access = if writeAccess: GENERIC_WRITE else: 0'i32
when useWinUnicode: when useWinUnicode:
result = createFileW( result = createFileW(
newWideCString(path), 0'i32, newWideCString(path), access,
FILE_SHARE_DELETE or FILE_SHARE_READ or FILE_SHARE_WRITE, FILE_SHARE_DELETE or FILE_SHARE_READ or FILE_SHARE_WRITE,
nil, OPEN_EXISTING, flags, 0 nil, OPEN_EXISTING, flags, 0
) )
else: else:
result = createFileA( result = createFileA(
path, 0'i32, path, access,
FILE_SHARE_DELETE or FILE_SHARE_READ or FILE_SHARE_WRITE, FILE_SHARE_DELETE or FILE_SHARE_READ or FILE_SHARE_WRITE,
nil, OPEN_EXISTING, flags, 0 nil, OPEN_EXISTING, flags, 0
) )
@ -1511,16 +1512,14 @@ template rawToFormalFileInfo(rawInfo, path, formalInfo): untyped =
## 'rawInfo' is either a 'TBY_HANDLE_FILE_INFORMATION' structure on Windows, ## 'rawInfo' is either a 'TBY_HANDLE_FILE_INFORMATION' structure on Windows,
## or a 'Stat' structure on posix ## or a 'Stat' structure on posix
when defined(Windows): when defined(Windows):
template toTime(e: FILETIME): untyped {.gensym.} =
fromUnix(winTimeToUnixTime(rdFileTime(e)).int64) # local templates default to bind semantics
template merge(a, b): untyped = a or (b shl 32) template merge(a, b): untyped = a or (b shl 32)
formalInfo.id.device = rawInfo.dwVolumeSerialNumber formalInfo.id.device = rawInfo.dwVolumeSerialNumber
formalInfo.id.file = merge(rawInfo.nFileIndexLow, rawInfo.nFileIndexHigh) formalInfo.id.file = merge(rawInfo.nFileIndexLow, rawInfo.nFileIndexHigh)
formalInfo.size = merge(rawInfo.nFileSizeLow, rawInfo.nFileSizeHigh) formalInfo.size = merge(rawInfo.nFileSizeLow, rawInfo.nFileSizeHigh)
formalInfo.linkCount = rawInfo.nNumberOfLinks formalInfo.linkCount = rawInfo.nNumberOfLinks
formalInfo.lastAccessTime = toTime(rawInfo.ftLastAccessTime) formalInfo.lastAccessTime = fromWinTime(rdFileTime(rawInfo.ftLastAccessTime))
formalInfo.lastWriteTime = toTime(rawInfo.ftLastWriteTime) formalInfo.lastWriteTime = fromWinTime(rdFileTime(rawInfo.ftLastWriteTime))
formalInfo.creationTime = toTime(rawInfo.ftCreationTime) formalInfo.creationTime = fromWinTime(rdFileTime(rawInfo.ftCreationTime))
# Retrieve basic permissions # Retrieve basic permissions
if (rawInfo.dwFileAttributes and FILE_ATTRIBUTE_READONLY) != 0'i32: if (rawInfo.dwFileAttributes and FILE_ATTRIBUTE_READONLY) != 0'i32:
@ -1654,3 +1653,18 @@ proc isHidden*(path: string): bool =
result = (fileName[0] == '.') and (fileName[3] != '.') result = (fileName[0] == '.') and (fileName[3] != '.')
{.pop.} {.pop.}
proc setLastModificationTime*(file: string, t: times.Time) =
## Sets the `file`'s last modification time. `OSError` is raised in case of
## an error.
when defined(posix):
let unixt = posix.Time(t.toUnix)
var timevals = [Timeval(tv_sec: unixt), Timeval(tv_sec: unixt)] # [last access, last modification]
if utimes(file, timevals.addr) != 0: raiseOSError(osLastError())
else:
let h = openHandle(path = file, writeAccess = true)
if h == INVALID_HANDLE_VALUE: raiseOSError(osLastError())
var ft = t.toWinTime.toFILETIME
let res = setFileTime(h, nil, nil, ft.addr)
discard h.closeHandle
if res == 0'i32: raiseOSError(osLastError())

View file

@ -1001,12 +1001,20 @@ elif not defined(useNimRtl):
{.pop} {.pop}
proc close(p: Process) = proc close(p: Process) =
if p.inStream != nil: close(p.inStream)
if p.outStream != nil: close(p.outStream)
if p.errStream != nil: close(p.errStream)
if poParentStreams notin p.options: if poParentStreams notin p.options:
if p.inStream != nil:
close(p.inStream)
else:
discard close(p.inHandle) discard close(p.inHandle)
if p.outStream != nil:
close(p.outStream)
else:
discard close(p.outHandle) discard close(p.outHandle)
if p.errStream != nil:
close(p.errStream)
else:
discard close(p.errHandle) discard close(p.errHandle)
proc suspend(p: Process) = proc suspend(p: Process) =
@ -1281,7 +1289,7 @@ elif not defined(useNimRtl):
proc select(readfds: var seq[Process], timeout = 500): int = proc select(readfds: var seq[Process], timeout = 500): int =
var tv: Timeval var tv: Timeval
tv.tv_sec = 0 tv.tv_sec = posix.Time(0)
tv.tv_usec = timeout * 1000 tv.tv_usec = timeout * 1000
var rd: TFdSet var rd: TFdSet

View file

@ -1647,11 +1647,15 @@ proc replace*(s, sub: string, by = ""): string {.noSideEffect,
let last = s.high let last = s.high
var i = 0 var i = 0
while true: while true:
var j = find(a, s, sub, i, last) let j = find(a, s, sub, i, last)
if j < 0: break if j < 0: break
add result, substr(s, i, j - 1) add result, substr(s, i, j - 1)
add result, by add result, by
i = j + len(sub) if sub.len == 0:
if i < s.len: add result, s[i]
i = j + 1
else:
i = j + sub.len
# copy the rest: # copy the rest:
add result, substr(s, i) add result, substr(s, i)
@ -1680,6 +1684,7 @@ proc replaceWord*(s, sub: string, by = ""): string {.noSideEffect,
initSkipTable(a, sub) initSkipTable(a, sub)
var i = 0 var i = 0
let last = s.high let last = s.high
let sublen = max(sub.len, 1)
while true: while true:
var j = find(a, s, sub, i, last) var j = find(a, s, sub, i, last)
if j < 0: break if j < 0: break
@ -1688,7 +1693,7 @@ proc replaceWord*(s, sub: string, by = ""): string {.noSideEffect,
(j+sub.len >= s.len or s[j+sub.len] notin wordChars): (j+sub.len >= s.len or s[j+sub.len] notin wordChars):
add result, substr(s, i, j - 1) add result, substr(s, i, j - 1)
add result, by add result, by
i = j + len(sub) i = j + sublen
else: else:
add result, substr(s, i, j) add result, substr(s, i, j)
i = j + 1 i = j + 1
@ -2546,6 +2551,9 @@ when isMainModule:
doAssert "-ld a-ldz -ld".replaceWord("-ld") == " a-ldz " doAssert "-ld a-ldz -ld".replaceWord("-ld") == " a-ldz "
doAssert "-lda-ldz -ld abc".replaceWord("-ld") == "-lda-ldz abc" doAssert "-lda-ldz -ld abc".replaceWord("-ld") == "-lda-ldz abc"
doAssert "-lda-ldz -ld abc".replaceWord("") == "lda-ldz ld abc"
doAssert "oo".replace("", "abc") == "abcoabcoabc"
type MyEnum = enum enA, enB, enC, enuD, enE type MyEnum = enum enA, enB, enC, enuD, enE
doAssert parseEnum[MyEnum]("enu_D") == enuD doAssert parseEnum[MyEnum]("enu_D") == enuD

View file

@ -557,8 +557,8 @@ proc setForegroundColor*(f: File, fg: ForegroundColor, bright=false) =
when defined(windows): when defined(windows):
let h = conHandle(f) let h = conHandle(f)
var old = getAttributes(h) and not FOREGROUND_RGB var old = getAttributes(h) and not FOREGROUND_RGB
if bright: old = if bright: old or FOREGROUND_INTENSITY
old = old or FOREGROUND_INTENSITY else: old and not(FOREGROUND_INTENSITY)
const lookup: array[ForegroundColor, int] = [ const lookup: array[ForegroundColor, int] = [
0, 0,
(FOREGROUND_RED), (FOREGROUND_RED),
@ -579,8 +579,8 @@ proc setBackgroundColor*(f: File, bg: BackgroundColor, bright=false) =
when defined(windows): when defined(windows):
let h = conHandle(f) let h = conHandle(f)
var old = getAttributes(h) and not BACKGROUND_RGB var old = getAttributes(h) and not BACKGROUND_RGB
if bright: old = if bright: old or BACKGROUND_INTENSITY
old = old or BACKGROUND_INTENSITY else: old and not(BACKGROUND_INTENSITY)
const lookup: array[BackgroundColor, int] = [ const lookup: array[BackgroundColor, int] = [
0, 0,
(BACKGROUND_RED), (BACKGROUND_RED),

View file

@ -191,6 +191,7 @@ const
secondsInHour = 60*60 secondsInHour = 60*60
secondsInDay = 60*60*24 secondsInDay = 60*60*24
minutesInHour = 60 minutesInHour = 60
rateDiff = 10000000'i64 # 100 nsecs
# The number of hectonanoseconds between 1601/01/01 (windows epoch) # The number of hectonanoseconds between 1601/01/01 (windows epoch)
# and 1970/01/01 (unix epoch). # and 1970/01/01 (unix epoch).
epochDiff = 116444736000000000'i64 epochDiff = 116444736000000000'i64
@ -352,7 +353,9 @@ proc `$`*(dur: Duration): string =
parts.add $quantity & " " & unitStrings[unit] & "s" parts.add $quantity & " " & unitStrings[unit] & "s"
result = "" result = ""
if parts.len == 1: if parts.len == 0:
result.add "0 nanoseconds"
elif parts.len == 1:
result = parts[0] result = parts[0]
elif parts.len == 2: elif parts.len == 2:
result = parts[0] & " and " & parts[1] result = parts[0] & " and " & parts[1]
@ -371,6 +374,21 @@ proc toUnix*(t: Time): int64 {.benign, tags: [], raises: [], noSideEffect.} =
## Convert ``t`` to a unix timestamp (seconds since ``1970-01-01T00:00:00Z``). ## Convert ``t`` to a unix timestamp (seconds since ``1970-01-01T00:00:00Z``).
t.seconds t.seconds
proc fromWinTime*(win: int64): Time =
## Convert a Windows file time (100-nanosecond intervals since ``1601-01-01T00:00:00Z``)
## to a ``Time``.
let hnsecsSinceEpoch = (win - epochDiff)
var seconds = hnsecsSinceEpoch div rateDiff
var nanos = ((hnsecsSinceEpoch mod rateDiff) * 100).int
if nanos < 0:
nanos += convert(Seconds, Nanoseconds, 1)
seconds -= 1
result = initTime(seconds, nanos)
proc toWinTime*(t: Time): int64 =
## Convert ``t`` to a Windows file time (100-nanosecond intervals since ``1601-01-01T00:00:00Z``).
result = t.seconds * rateDiff + epochDiff + t.nanoseconds div 100
proc isLeapYear*(year: int): bool = proc isLeapYear*(year: int): bool =
## Returns true if ``year`` is a leap year. ## Returns true if ``year`` is a leap year.
year mod 4 == 0 and (year mod 100 != 0 or year mod 400 == 0) year mod 4 == 0 and (year mod 100 != 0 or year mod 400 == 0)
@ -853,10 +871,7 @@ proc getTime*(): Time {.tags: [TimeEffect], benign.} =
elif defined(windows): elif defined(windows):
var f: FILETIME var f: FILETIME
getSystemTimeAsFileTime(f) getSystemTimeAsFileTime(f)
let nanosSinceEpoch = (rdFileTime(f) - epochDiff) * 100 result = fromWinTime(rdFileTime(f))
let seconds = convert(Nanoseconds, Seconds, nanosSinceEpoch)
let nanos = (nanosSinceEpoch mod convert(Seconds, Nanoseconds, 1)).int
result = initTime(seconds, nanos)
proc now*(): DateTime {.tags: [TimeEffect], benign.} = proc now*(): DateTime {.tags: [TimeEffect], benign.} =
## Get the current time as a ``DateTime`` in the local timezone. ## Get the current time as a ``DateTime`` in the local timezone.
@ -1713,17 +1728,6 @@ when not defined(JS):
var var
clocksPerSec {.importc: "CLOCKS_PER_SEC", nodecl.}: int clocksPerSec {.importc: "CLOCKS_PER_SEC", nodecl.}: int
const
rateDiff = 10000000'i64 # 100 nsecs
proc unixTimeToWinTime*(time: CTime): int64 =
## converts a UNIX `Time` (``time_t``) to a Windows file time
result = int64(time) * rateDiff + epochDiff
proc winTimeToUnixTime*(time: int64): CTime =
## converts a Windows time to a UNIX `Time` (``time_t``)
result = CTime((time - epochDiff) div rateDiff)
when not defined(useNimRtl): when not defined(useNimRtl):
proc cpuTime*(): float {.rtl, extern: "nt$1", tags: [TimeEffect].} = proc cpuTime*(): float {.rtl, extern: "nt$1", tags: [TimeEffect].} =
## gets time spent that the CPU spent to run the current process in ## gets time spent that the CPU spent to run the current process in
@ -1748,7 +1752,7 @@ when not defined(JS):
when defined(posix): when defined(posix):
var a: Timeval var a: Timeval
gettimeofday(a) gettimeofday(a)
result = toFloat(a.tv_sec) + toFloat(a.tv_usec)*0.00_0001 result = toBiggestFloat(a.tv_sec.int64) + toFloat(a.tv_usec)*0.00_0001
elif defined(windows): elif defined(windows):
var f: winlean.FILETIME var f: winlean.FILETIME
getSystemTimeAsFileTime(f) getSystemTimeAsFileTime(f)
@ -1765,6 +1769,19 @@ when defined(JS):
# Deprecated procs # Deprecated procs
when not defined(JS):
proc unixTimeToWinTime*(time: CTime): int64 {.deprecated: "Use toWinTime instead".} =
## Converts a UNIX `Time` (``time_t``) to a Windows file time
##
## **Deprecated:** use ``toWinTime`` instead.
result = int64(time) * rateDiff + epochDiff
proc winTimeToUnixTime*(time: int64): CTime {.deprecated: "Use fromWinTime instead".} =
## Converts a Windows time to a UNIX `Time` (``time_t``)
##
## **Deprecated:** use ``fromWinTime`` instead.
result = CTime((time - epochDiff) div rateDiff)
proc initInterval*(seconds, minutes, hours, days, months, proc initInterval*(seconds, minutes, hours, days, months,
years: int = 0): TimeInterval {.deprecated.} = years: int = 0): TimeInterval {.deprecated.} =
## **Deprecated since v0.18.0:** use ``initTimeInterval`` instead. ## **Deprecated since v0.18.0:** use ``initTimeInterval`` instead.

View file

@ -2404,7 +2404,7 @@ proc `==` *[T](x, y: seq[T]): bool {.noSideEffect.} =
if x.isNil and y.isNil: if x.isNil and y.isNil:
return true return true
else: else:
when not defined(JS) or defined(nimphp): when not defined(JS):
proc seqToPtr[T](x: seq[T]): pointer {.inline, nosideeffect.} = proc seqToPtr[T](x: seq[T]): pointer {.inline, nosideeffect.} =
result = cast[pointer](x) result = cast[pointer](x)
else: else:
@ -2766,9 +2766,6 @@ type
when defined(JS): when defined(JS):
proc add*(x: var string, y: cstring) {.asmNoStackFrame.} = proc add*(x: var string, y: cstring) {.asmNoStackFrame.} =
when defined(nimphp):
asm """`x` .= `y`;"""
else:
asm """ asm """
var len = `x`[0].length-1; var len = `x`[0].length-1;
for (var i = 0; i < `y`.length; ++i) { for (var i = 0; i < `y`.length; ++i) {
@ -4139,17 +4136,18 @@ template doAssertRaises*(exception, code: untyped): typed =
runnableExamples: runnableExamples:
doAssertRaises(ValueError): doAssertRaises(ValueError):
raise newException(ValueError, "Hello World") raise newException(ValueError, "Hello World")
var wrong = false
try: try:
block:
code code
raiseAssert(astToStr(exception) & " wasn't raised by:\n" & astToStr(code)) wrong = true
except exception: except exception:
discard discard
except Exception as exc: except Exception as exc:
raiseAssert(astToStr(exception) & raiseAssert(astToStr(exception) &
" wasn't raised, another error was raised instead by:\n"& " wasn't raised, another error was raised instead by:\n"&
astToStr(code)) astToStr(code))
if wrong:
raiseAssert(astToStr(exception) & " wasn't raised by:\n" & astToStr(code))
when defined(cpp) and appType != "lib" and not defined(js) and when defined(cpp) and appType != "lib" and not defined(js) and
not defined(nimscript) and hostOS != "standalone": not defined(nimscript) and hostOS != "standalone":

View file

@ -79,8 +79,12 @@ proc genericAssignAux(dest, src: pointer, mt: PNimType, shallow: bool) =
GenericSeqSize), GenericSeqSize),
mt.base, shallow) mt.base, shallow)
of tyObject: of tyObject:
if mt.base != nil: var it = mt.base
genericAssignAux(dest, src, mt.base, shallow) # don't use recursion here on the PNimType because the subtype
# check should only be done at the very end:
while it != nil:
genericAssignAux(dest, src, it.node, shallow)
it = it.base
genericAssignAux(dest, src, mt.node, shallow) genericAssignAux(dest, src, mt.node, shallow)
# we need to copy m_type field for tyObject, as it could be empty for # we need to copy m_type field for tyObject, as it could be empty for
# sequence reallocations: # sequence reallocations:
@ -89,6 +93,8 @@ proc genericAssignAux(dest, src: pointer, mt: PNimType, shallow: bool) =
# if p of TB: # if p of TB:
# var tbObj = TB(p) # var tbObj = TB(p)
# tbObj of TC # needs to be false! # tbObj of TC # needs to be false!
#c_fprintf(stdout, "%s %s\n", pint[].name, mt.name)
chckObjAsgn(cast[ptr PNimType](src)[], mt)
pint[] = mt # cast[ptr PNimType](src)[] pint[] = mt # cast[ptr PNimType](src)[]
of tyTuple: of tyTuple:
genericAssignAux(dest, src, mt.node, shallow) genericAssignAux(dest, src, mt.node, shallow)

View file

@ -48,9 +48,6 @@ proc nimCharToStr(x: char): string {.compilerproc.} =
result[0] = x result[0] = x
proc isNimException(): bool {.asmNoStackFrame.} = proc isNimException(): bool {.asmNoStackFrame.} =
when defined(nimphp):
asm "return isset(`lastJSError`['m_type']);"
else:
asm "return `lastJSError`.m_type;" asm "return `lastJSError`.m_type;"
proc getCurrentException*(): ref Exception {.compilerRtl, benign.} = proc getCurrentException*(): ref Exception {.compilerRtl, benign.} =
@ -61,7 +58,6 @@ proc getCurrentExceptionMsg*(): string =
if isNimException(): if isNimException():
return cast[Exception](lastJSError).msg return cast[Exception](lastJSError).msg
else: else:
when not defined(nimphp):
var msg: cstring var msg: cstring
{.emit: """ {.emit: """
if (`lastJSError`.message !== undefined) { if (`lastJSError`.message !== undefined) {
@ -140,9 +136,6 @@ proc raiseException(e: ref Exception, ename: cstring) {.
e.name = ename e.name = ename
if excHandler == 0: if excHandler == 0:
unhandledException(e) unhandledException(e)
when defined(nimphp):
asm """throw new Exception($`e`["message"]);"""
else:
when NimStackTrace: when NimStackTrace:
e.trace = rawWriteStackTrace() e.trace = rawWriteStackTrace()
asm "throw `e`;" asm "throw `e`;"
@ -173,22 +166,6 @@ proc raiseFieldError(f: string) {.compilerproc, noreturn.} =
raise newException(FieldError, f & " is not accessible") raise newException(FieldError, f & " is not accessible")
proc setConstr() {.varargs, asmNoStackFrame, compilerproc.} = proc setConstr() {.varargs, asmNoStackFrame, compilerproc.} =
when defined(nimphp):
asm """
$args = func_get_args();
$result = array();
foreach ($args as $x) {
if (is_array($x)) {
for ($j = $x[0]; $j <= $x[1]; $j++) {
$result[$j] = true;
}
} else {
$result[$x] = true;
}
}
return $result;
"""
else:
asm """ asm """
var result = {}; var result = {};
for (var i = 0; i < arguments.length; ++i) { for (var i = 0; i < arguments.length; ++i) {
@ -205,9 +182,6 @@ proc setConstr() {.varargs, asmNoStackFrame, compilerproc.} =
""" """
proc makeNimstrLit(c: cstring): string {.asmNoStackFrame, compilerproc.} = proc makeNimstrLit(c: cstring): string {.asmNoStackFrame, compilerproc.} =
when defined(nimphp):
{.emit: """return `c`;""".}
else:
{.emit: """ {.emit: """
var ln = `c`.length; var ln = `c`.length;
var result = new Array(ln + 1); var result = new Array(ln + 1);
@ -220,9 +194,6 @@ proc makeNimstrLit(c: cstring): string {.asmNoStackFrame, compilerproc.} =
""".} """.}
proc cstrToNimstr(c: cstring): string {.asmNoStackFrame, compilerproc.} = proc cstrToNimstr(c: cstring): string {.asmNoStackFrame, compilerproc.} =
when defined(nimphp):
{.emit: """return `c`;""".}
else:
{.emit: """ {.emit: """
var ln = `c`.length; var ln = `c`.length;
var result = new Array(ln); var result = new Array(ln);
@ -261,9 +232,6 @@ proc cstrToNimstr(c: cstring): string {.asmNoStackFrame, compilerproc.} =
""".} """.}
proc toJSStr(s: string): cstring {.asmNoStackFrame, compilerproc.} = proc toJSStr(s: string): cstring {.asmNoStackFrame, compilerproc.} =
when defined(nimphp):
{.emit: """return `s`;""".}
else:
asm """ asm """
var len = `s`.length-1; var len = `s`.length-1;
var asciiPart = new Array(len); var asciiPart = new Array(len);
@ -295,11 +263,6 @@ proc toJSStr(s: string): cstring {.asmNoStackFrame, compilerproc.} =
""" """
proc mnewString(len: int): string {.asmNoStackFrame, compilerproc.} = proc mnewString(len: int): string {.asmNoStackFrame, compilerproc.} =
when defined(nimphp):
asm """
return str_repeat(chr(0),`len`);
"""
else:
asm """ asm """
var result = new Array(`len`+1); var result = new Array(`len`+1);
result[0] = 0; result[0] = 0;
@ -307,24 +270,8 @@ proc mnewString(len: int): string {.asmNoStackFrame, compilerproc.} =
return result; return result;
""" """
when defined(nimphp):
proc nimAt(x: string; i: int): string {.asmNoStackFrame, compilerproc.} =
asm """
return `x`[`i`];
"""
when defined(nimphp):
proc nimSubstr(s: string; a, b: int): string {.
asmNoStackFrame, compilerproc.} =
asm """return substr(`s`,`a`,`b`-`a`+1);"""
proc SetCard(a: int): int {.compilerproc, asmNoStackFrame.} = proc SetCard(a: int): int {.compilerproc, asmNoStackFrame.} =
# argument type is a fake # argument type is a fake
when defined(nimphp):
asm """
return count(`a`);
"""
else:
asm """ asm """
var result = 0; var result = 0;
for (var elem in `a`) { ++result; } for (var elem in `a`) { ++result; }
@ -332,13 +279,6 @@ proc SetCard(a: int): int {.compilerproc, asmNoStackFrame.} =
""" """
proc SetEq(a, b: int): bool {.compilerproc, asmNoStackFrame.} = proc SetEq(a, b: int): bool {.compilerproc, asmNoStackFrame.} =
when defined(nimphp):
asm """
foreach (`a` as $elem=>$_) { if (!isset(`b`[$elem])) return false; }
foreach (`b` as $elem=>$_) { if (!isset(`a`[$elem])) return false; }
return true;
"""
else:
asm """ asm """
for (var elem in `a`) { if (!`b`[elem]) return false; } for (var elem in `a`) { if (!`b`[elem]) return false; }
for (var elem in `b`) { if (!`a`[elem]) return false; } for (var elem in `b`) { if (!`a`[elem]) return false; }
@ -346,12 +286,6 @@ proc SetEq(a, b: int): bool {.compilerproc, asmNoStackFrame.} =
""" """
proc SetLe(a, b: int): bool {.compilerproc, asmNoStackFrame.} = proc SetLe(a, b: int): bool {.compilerproc, asmNoStackFrame.} =
when defined(nimphp):
asm """
foreach (`a` as $elem=>$_) { if (!isset(`b`[$elem])) return false; }
return true;
"""
else:
asm """ asm """
for (var elem in `a`) { if (!`b`[elem]) return false; } for (var elem in `a`) { if (!`b`[elem]) return false; }
return true; return true;
@ -361,15 +295,6 @@ proc SetLt(a, b: int): bool {.compilerproc.} =
result = SetLe(a, b) and not SetEq(a, b) result = SetLe(a, b) and not SetEq(a, b)
proc SetMul(a, b: int): int {.compilerproc, asmNoStackFrame.} = proc SetMul(a, b: int): int {.compilerproc, asmNoStackFrame.} =
when defined(nimphp):
asm """
var $result = array();
foreach (`a` as $elem=>$_) {
if (isset(`b`[$elem])) { $result[$elem] = true; }
}
return $result;
"""
else:
asm """ asm """
var result = {}; var result = {};
for (var elem in `a`) { for (var elem in `a`) {
@ -379,14 +304,6 @@ proc SetMul(a, b: int): int {.compilerproc, asmNoStackFrame.} =
""" """
proc SetPlus(a, b: int): int {.compilerproc, asmNoStackFrame.} = proc SetPlus(a, b: int): int {.compilerproc, asmNoStackFrame.} =
when defined(nimphp):
asm """
var $result = array();
foreach (`a` as $elem=>$_) { $result[$elem] = true; }
foreach (`b` as $elem=>$_) { $result[$elem] = true; }
return $result;
"""
else:
asm """ asm """
var result = {}; var result = {};
for (var elem in `a`) { result[elem] = true; } for (var elem in `a`) { result[elem] = true; }
@ -395,15 +312,6 @@ proc SetPlus(a, b: int): int {.compilerproc, asmNoStackFrame.} =
""" """
proc SetMinus(a, b: int): int {.compilerproc, asmNoStackFrame.} = proc SetMinus(a, b: int): int {.compilerproc, asmNoStackFrame.} =
when defined(nimphp):
asm """
$result = array();
foreach (`a` as $elem=>$_) {
if (!isset(`b`[$elem])) { $result[$elem] = true; }
}
return $result;
"""
else:
asm """ asm """
var result = {}; var result = {};
for (var elem in `a`) { for (var elem in `a`) {
@ -425,13 +333,6 @@ proc cmpStrings(a, b: string): int {.asmNoStackFrame, compilerProc.} =
""" """
proc cmp(x, y: string): int = proc cmp(x, y: string): int =
when defined(nimphp):
asm """
if(`x` < `y`) `result` = -1;
elseif (`x` > `y`) `result` = 1;
else `result` = 0;
"""
else:
return cmpStrings(x, y) return cmpStrings(x, y)
proc eqStrings(a, b: string): bool {.asmNoStackFrame, compilerProc.} = proc eqStrings(a, b: string): bool {.asmNoStackFrame, compilerProc.} =
@ -467,7 +368,7 @@ elif not defined(nimOldEcho):
console.log(buf); console.log(buf);
""" """
elif not defined(nimphp): else:
proc ewriteln(x: cstring) = proc ewriteln(x: cstring) =
var node : JSRef var node : JSRef
{.emit: "`node` = document.getElementsByTagName('body')[0];".} {.emit: "`node` = document.getElementsByTagName('body')[0];".}
@ -493,11 +394,6 @@ elif not defined(nimphp):
# Arithmetic: # Arithmetic:
proc addInt(a, b: int): int {.asmNoStackFrame, compilerproc.} = proc addInt(a, b: int): int {.asmNoStackFrame, compilerproc.} =
when defined(nimphp):
asm """
return `a` + `b`;
"""
else:
asm """ asm """
var result = `a` + `b`; var result = `a` + `b`;
if (result > 2147483647 || result < -2147483648) `raiseOverflow`(); if (result > 2147483647 || result < -2147483648) `raiseOverflow`();
@ -505,11 +401,6 @@ proc addInt(a, b: int): int {.asmNoStackFrame, compilerproc.} =
""" """
proc subInt(a, b: int): int {.asmNoStackFrame, compilerproc.} = proc subInt(a, b: int): int {.asmNoStackFrame, compilerproc.} =
when defined(nimphp):
asm """
return `a` - `b`;
"""
else:
asm """ asm """
var result = `a` - `b`; var result = `a` - `b`;
if (result > 2147483647 || result < -2147483648) `raiseOverflow`(); if (result > 2147483647 || result < -2147483648) `raiseOverflow`();
@ -517,11 +408,6 @@ proc subInt(a, b: int): int {.asmNoStackFrame, compilerproc.} =
""" """
proc mulInt(a, b: int): int {.asmNoStackFrame, compilerproc.} = proc mulInt(a, b: int): int {.asmNoStackFrame, compilerproc.} =
when defined(nimphp):
asm """
return `a` * `b`;
"""
else:
asm """ asm """
var result = `a` * `b`; var result = `a` * `b`;
if (result > 2147483647 || result < -2147483648) `raiseOverflow`(); if (result > 2147483647 || result < -2147483648) `raiseOverflow`();
@ -529,11 +415,6 @@ proc mulInt(a, b: int): int {.asmNoStackFrame, compilerproc.} =
""" """
proc divInt(a, b: int): int {.asmNoStackFrame, compilerproc.} = proc divInt(a, b: int): int {.asmNoStackFrame, compilerproc.} =
when defined(nimphp):
asm """
return trunc(`a` / `b`);
"""
else:
asm """ asm """
if (`b` == 0) `raiseDivByZero`(); if (`b` == 0) `raiseDivByZero`();
if (`b` == -1 && `a` == 2147483647) `raiseOverflow`(); if (`b` == -1 && `a` == 2147483647) `raiseOverflow`();
@ -541,11 +422,6 @@ proc divInt(a, b: int): int {.asmNoStackFrame, compilerproc.} =
""" """
proc modInt(a, b: int): int {.asmNoStackFrame, compilerproc.} = proc modInt(a, b: int): int {.asmNoStackFrame, compilerproc.} =
when defined(nimphp):
asm """
return `a` % `b`;
"""
else:
asm """ asm """
if (`b` == 0) `raiseDivByZero`(); if (`b` == 0) `raiseDivByZero`();
if (`b` == -1 && `a` == 2147483647) `raiseOverflow`(); if (`b` == -1 && `a` == 2147483647) `raiseOverflow`();
@ -553,11 +429,6 @@ proc modInt(a, b: int): int {.asmNoStackFrame, compilerproc.} =
""" """
proc addInt64(a, b: int): int {.asmNoStackFrame, compilerproc.} = proc addInt64(a, b: int): int {.asmNoStackFrame, compilerproc.} =
when defined(nimphp):
asm """
return `a` + `b`;
"""
else:
asm """ asm """
var result = `a` + `b`; var result = `a` + `b`;
if (result > 9223372036854775807 if (result > 9223372036854775807
@ -566,11 +437,6 @@ proc addInt64(a, b: int): int {.asmNoStackFrame, compilerproc.} =
""" """
proc subInt64(a, b: int): int {.asmNoStackFrame, compilerproc.} = proc subInt64(a, b: int): int {.asmNoStackFrame, compilerproc.} =
when defined(nimphp):
asm """
return `a` - `b`;
"""
else:
asm """ asm """
var result = `a` - `b`; var result = `a` - `b`;
if (result > 9223372036854775807 if (result > 9223372036854775807
@ -579,11 +445,6 @@ proc subInt64(a, b: int): int {.asmNoStackFrame, compilerproc.} =
""" """
proc mulInt64(a, b: int): int {.asmNoStackFrame, compilerproc.} = proc mulInt64(a, b: int): int {.asmNoStackFrame, compilerproc.} =
when defined(nimphp):
asm """
return `a` * `b`;
"""
else:
asm """ asm """
var result = `a` * `b`; var result = `a` * `b`;
if (result > 9223372036854775807 if (result > 9223372036854775807
@ -592,11 +453,6 @@ proc mulInt64(a, b: int): int {.asmNoStackFrame, compilerproc.} =
""" """
proc divInt64(a, b: int): int {.asmNoStackFrame, compilerproc.} = proc divInt64(a, b: int): int {.asmNoStackFrame, compilerproc.} =
when defined(nimphp):
asm """
return trunc(`a` / `b`);
"""
else:
asm """ asm """
if (`b` == 0) `raiseDivByZero`(); if (`b` == 0) `raiseDivByZero`();
if (`b` == -1 && `a` == 9223372036854775807) `raiseOverflow`(); if (`b` == -1 && `a` == 9223372036854775807) `raiseOverflow`();
@ -604,11 +460,6 @@ proc divInt64(a, b: int): int {.asmNoStackFrame, compilerproc.} =
""" """
proc modInt64(a, b: int): int {.asmNoStackFrame, compilerproc.} = proc modInt64(a, b: int): int {.asmNoStackFrame, compilerproc.} =
when defined(nimphp):
asm """
return `a` % `b`;
"""
else:
asm """ asm """
if (`b` == 0) `raiseDivByZero`(); if (`b` == 0) `raiseDivByZero`();
if (`b` == -1 && `a` == 9223372036854775807) `raiseOverflow`(); if (`b` == -1 && `a` == 9223372036854775807) `raiseOverflow`();
@ -767,16 +618,6 @@ proc genericReset(x: JSRef, ti: PNimType): JSRef {.compilerproc.} =
else: else:
discard discard
when defined(nimphp):
proc arrayConstr(len: int, value: string, typ: string): JSRef {.
asmNoStackFrame, compilerproc.} =
# types are fake
asm """
$result = array();
for ($i = 0; $i < `len`; $i++) $result[] = `value`;
return $result;
"""
else:
proc arrayConstr(len: int, value: JSRef, typ: PNimType): JSRef {. proc arrayConstr(len: int, value: JSRef, typ: PNimType): JSRef {.
asmNoStackFrame, compilerproc.} = asmNoStackFrame, compilerproc.} =
# types are fake # types are fake

View file

@ -1086,3 +1086,14 @@ proc ConvertThreadToFiberEx*(param: pointer, flags: int32): pointer {.stdcall, d
proc DeleteFiber*(fiber: pointer): void {.stdcall, discardable, dynlib: "kernel32", importc.} proc DeleteFiber*(fiber: pointer): void {.stdcall, discardable, dynlib: "kernel32", importc.}
proc SwitchToFiber*(fiber: pointer): void {.stdcall, discardable, dynlib: "kernel32", importc.} proc SwitchToFiber*(fiber: pointer): void {.stdcall, discardable, dynlib: "kernel32", importc.}
proc GetCurrentFiber*(): pointer {.stdcall, importc, header: "Windows.h".} proc GetCurrentFiber*(): pointer {.stdcall, importc, header: "Windows.h".}
proc toFILETIME*(t: int64): FILETIME =
## Convert the Windows file time timestamp ``t`` to ``FILETIME``.
result = FILETIME(dwLowDateTime: cast[DWORD](t), dwHighDateTime: DWORD(t shr 32))
type
LPFILETIME* = ptr FILETIME
proc setFileTime*(hFile: HANDLE, lpCreationTime: LPFILETIME,
lpLastAccessTime: LPFILETIME, lpLastWriteTime: LPFILETIME): WINBOOL
{.stdcall, dynlib: "kernel32", importc: "SetFileTime".}

View file

@ -13,14 +13,8 @@ cat
cat cat
dog dog
dog dog
dog value
cat value
dog value
cat value
dog dog
dog dog
dog value
cat value
dog 1 dog 1
dog 2 dog 2
''' '''
@ -243,6 +237,7 @@ reject modifiesCovariantArray(dogRefsArray.addr)
var dogValues = @[vdog, vdog] var dogValues = @[vdog, vdog]
var dogValuesArray = [vdog, vdog] var dogValuesArray = [vdog, vdog]
when false:
var animalValues = @[Animal(vdog), Animal(vcat)] var animalValues = @[Animal(vdog), Animal(vcat)]
var animalValuesArray = [Animal(vdog), Animal(vcat)] var animalValuesArray = [Animal(vdog), Animal(vcat)]
@ -260,7 +255,6 @@ modifiesDerivedOperArray dogRefs
reject modifiesDerivedOperArray(dogValues) reject modifiesDerivedOperArray(dogValues)
reject modifiesDerivedOperArray(animalRefs) reject modifiesDerivedOperArray(animalRefs)
wantsNonCovariantOperArray animalValues
reject wantsNonCovariantOperArray(animalRefs) reject wantsNonCovariantOperArray(animalRefs)
reject wantsNonCovariantOperArray(dogRefs) reject wantsNonCovariantOperArray(dogRefs)
reject wantsNonCovariantOperArray(dogValues) reject wantsNonCovariantOperArray(dogValues)

11
tests/errmsgs/t1154.nim Normal file
View file

@ -0,0 +1,11 @@
discard """
errormsg: "invalid type: 'expr' in this context: 'proc (a: varargs[expr])' for proc"
line: 8
"""
import typetraits
proc foo(a:varargs[expr]) =
echo a[0].type.name
foo(1)

View file

@ -0,0 +1,9 @@
discard """
errormsg: "overloaded 'x' leads to ambiguous calls"
line: 9
"""
# bug #6393
proc x(): int = 7
proc x(): string = "strange"

View file

@ -27,11 +27,19 @@ import strutils
template assertNot(arg: untyped): untyped = template assertNot(arg: untyped): untyped =
assert(not(arg)) assert(not(arg))
proc foo(arg: int): void =
discard
proc foo(arg: float): void =
discard
static: static:
## test eqIdent ## test eqIdent
let a = "abc_def" let a = "abc_def"
let b = "abcDef" let b = "abcDef"
let c = "AbcDef" let c = "AbcDef"
let d = nnkBracketExpr.newTree() # not an identifier at all
assert eqIdent( a , b ) assert eqIdent( a , b )
assert eqIdent(newIdentNode(a), b ) assert eqIdent(newIdentNode(a), b )
@ -62,3 +70,12 @@ static:
assertNot eqIdent(genSym(nskLet, c), newIdentNode( b)) assertNot eqIdent(genSym(nskLet, c), newIdentNode( b))
assertNot eqIdent(newIdentNode( c), genSym(nskLet, b)) assertNot eqIdent(newIdentNode( c), genSym(nskLet, b))
assertNot eqIdent(genSym(nskLet, c), genSym(nskLet, b)) assertNot eqIdent(genSym(nskLet, c), genSym(nskLet, b))
# eqIdent on non identifier at all
assertNot eqIdent(a,d)
# eqIdent on sym choice
let fooSym = bindSym"foo"
assert fooSym.kind in {nnkOpenSymChoice, nnkClosedSymChoice}
assert fooSym.eqIdent("fOO")
assertNot fooSym.eqIdent("bar")

View file

@ -1,9 +1,7 @@
discard """ discard """
output: '''tbObj of TC false output: '''tbObj of TC true
false
true true
5 5'''
false'''
""" """
# bug #1053 # bug #1053
@ -20,10 +18,10 @@ type
proc test(p: TA) = proc test(p: TA) =
#echo "p of TB ", p of TB #echo "p of TB ", p of TB
if p of TB: if p of TB:
var tbObj = TB(p) #var tbObj = TB(p)
# tbObj is actually no longer compatible with TC: # tbObj is actually no longer compatible with TC:
echo "tbObj of TC ", tbObj of TC echo "tbObj of TC ", p of TC
var v = TC() var v = TC()
v.a = 1 v.a = 1
@ -48,8 +46,8 @@ proc isMyObject(obj: TObject) =
asd.x = 5 asd.x = 5
var asdCopy = TObject(asd) #var asdCopy = TObject(asd)
echo asdCopy of MyObject #echo asdCopy of MyObject
isMyObject(asd) isMyObject(asd)
isMyObject(asdCopy) #isMyObject(asdCopy)

View file

@ -0,0 +1,24 @@
discard """
outputsub: '''ObjectAssignmentError'''
exitcode: "1"
"""
# bug #7637
type
Fruit = object of RootObj
name*: string
Apple = object of Fruit
Pear = object of Fruit
method eat(f: Fruit) {.base.} =
raise newException(Exception, "PURE VIRTUAL CALL")
method eat(f: Apple) =
echo "fruity"
method eat(f: Pear) =
echo "juicy"
let basket = [Apple(name:"a"), Pear(name:"b")]
eat(basket[0])

View file

@ -9,8 +9,8 @@ discard """
(k: kindA, a: (x: "abc", z: [1, 8, 3]), method: ()) (k: kindA, a: (x: "abc", z: [1, 8, 3]), method: ())
(k: kindA, a: (x: "abc", z: [1, 9, 3]), method: ()) (k: kindA, a: (x: "abc", z: [1, 9, 3]), method: ())
(k: kindA, a: (x: "abc", z: [1, 10, 3]), method: ()) (k: kindA, a: (x: "abc", z: [1, 10, 3]), method: ())
(x: 123) (y: 0, x: 123)
(x: 123) (y: 678, x: 123)
(z: 89, y: 0, x: 128) (z: 89, y: 0, x: 128)
(y: 678, x: 123) (y: 678, x: 123)
(y: 678, x: 123) (y: 678, x: 123)
@ -33,7 +33,6 @@ type
`method`: TEmpty # bug #1791 `method`: TEmpty # bug #1791
proc `$`[T](s: seq[T]): string = proc `$`[T](s: seq[T]): string =
# XXX why is that not in the stdlib?
result = "[" result = "["
for i, x in s: for i, x in s:
if i > 0: result.add(", ") if i > 0: result.add(", ")
@ -59,7 +58,7 @@ type
# inherited fields are ignored, so no fields are set # inherited fields are ignored, so no fields are set
when true: when true:
var var
o: A o: B
o = B(x: 123) o = B(x: 123)
echo o echo o
o = B(y: 678, x: 123) o = B(y: 678, x: 123)

24
tests/osproc/tclose.nim Normal file
View file

@ -0,0 +1,24 @@
discard """
exitcode: 0
"""
when defined(linux):
import osproc, os
proc countFds(): int =
result = 0
for i in walkDir("/proc/self/fd"):
result += 1
let initCount = countFds()
let p = osproc.startProcess("echo", options={poUsePath})
assert countFds() == initCount + 3
p.close
assert countFds() == initCount
let p1 = osproc.startProcess("echo", options={poUsePath})
discard p1.inputStream
assert countFds() == initCount + 3
p.close
assert countFds() == initCount

View file

@ -11,7 +11,7 @@ proc parseInt(x: float): int {.noSideEffect.} = discard
proc parseInt(x: bool): int {.noSideEffect.} = discard proc parseInt(x: bool): int {.noSideEffect.} = discard
proc parseInt(x: float32): int {.noSideEffect.} = discard proc parseInt(x: float32): int {.noSideEffect.} = discard
proc parseInt(x: int8): int {.noSideEffect.} = discard proc parseInt(x: int8): int {.noSideEffect.} = discard
proc parseInt(x: TFile): int {.noSideEffect.} = discard proc parseInt(x: File): int {.noSideEffect.} = discard
proc parseInt(x: char): int {.noSideEffect.} = discard proc parseInt(x: char): int {.noSideEffect.} = discard
proc parseInt(x: int16): int {.noSideEffect.} = discard proc parseInt(x: int16): int {.noSideEffect.} = discard

View file

@ -0,0 +1,35 @@
discard """
output: '''true'''
"""
# bug #7638
import threadpool, os, strformat
proc timer(d: int): int =
#echo fmt"sleeping {d}"
sleep(d)
#echo fmt"done {d}"
return d
var durations = [1000, 2000, 3000, 4000, 5000]
var tasks: seq[FlowVarBase] = @[]
var results: seq[int] = @[]
for i in 0 .. durations.high:
tasks.add spawn timer(durations[i])
var index = awaitAny(tasks)
while index != -1:
results.add ^cast[FlowVar[int]](tasks[index])
tasks.del(index)
#echo repr results
index = awaitAny(tasks)
doAssert results.len == 5
doAssert 1000 in results
doAssert 2000 in results
doAssert 3000 in results
doAssert 4000 in results
doAssert 5000 in results
sync()
echo "true"

View file

@ -51,6 +51,9 @@ block: # A bit more advanced case
static: assert(hasCustomPragma(myproc, alternativeKey)) static: assert(hasCustomPragma(myproc, alternativeKey))
const hasFieldCustomPragma = s.field.hasCustomPragma(defaultValue)
static: assert(hasFieldCustomPragma == false)
# pragma on an object # pragma on an object
static: static:
assert Subfield.hasCustomPragma(defaultValue) assert Subfield.hasCustomPragma(defaultValue)
@ -71,6 +74,8 @@ block: # ref types
MyFile {.defaultValue: "closed".} = ref object MyFile {.defaultValue: "closed".} = ref object
path {.defaultValue: "invalid".}: string path {.defaultValue: "invalid".}: string
TypeWithoutPragma = object
var s = NodeRef() var s = NodeRef()
const const
@ -91,7 +96,7 @@ block: # ref types
var ptrS = NodePtr(nil) var ptrS = NodePtr(nil)
const const
ptrRightSerKey = getCustomPragmaVal(s.right, serializationKey) ptrRightSerKey = getCustomPragmaVal(ptrS.right, serializationKey)
static: static:
assert ptrRightSerKey == "r" assert ptrRightSerKey == "r"
@ -103,6 +108,9 @@ block: # ref types
assert fileDefVal == "closed" assert fileDefVal == "closed"
assert filePathDefVal == "invalid" assert filePathDefVal == "invalid"
static:
assert TypeWithoutPragma.hasCustomPragma(defaultValue) == false
block: block:
type type
VariantKind = enum VariantKind = enum

View file

@ -410,3 +410,10 @@ suite "ttimes":
let day = 24.hours let day = 24.hours
let tomorrow = now + day let tomorrow = now + day
check tomorrow - now == initDuration(days = 1) check tomorrow - now == initDuration(days = 1)
test "fromWinTime/toWinTime":
check 0.fromUnix.toWinTime.fromWinTime.toUnix == 0
check (-1).fromWinTime.nanoseconds == convert(Seconds, Nanoseconds, 1) - 100
check -1.fromWinTime.toWinTime == -1
# One nanosecond is discarded due to differences in time resolution
check initTime(0, 101).toWinTime.fromWinTime.nanoseconds == 100

1
tests/template/i2416.nim Normal file
View file

@ -0,0 +1 @@
template i2416*() = echo "i2416"

2
tests/template/t2416.nim Normal file
View file

@ -0,0 +1,2 @@
import i2416
i2416()

View file

@ -0,0 +1,9 @@
discard """
line: 9
errormsg: "address of 'result' may not escape its stack frame"
"""
# bug #5113
proc makeSeqVar(size: Natural): var seq[int] =
newSeq(result, size)

View file

@ -24,7 +24,7 @@ const
Usage: Usage:
nimpretty [options] file.nim nimpretty [options] file.nim
Options: Options:
--backup:ON|OFF create a backup file before overwritting (default: ON) --backup:on|off create a backup file before overwritting (default: ON)
--version show the version --version show the version
--help show this help --help show this help
""" """
@ -43,7 +43,7 @@ proc prettyPrint(infile: string) =
let fileIdx = fileInfoIdx(infile) let fileIdx = fileInfoIdx(infile)
let tree = parseFile(fileIdx, newIdentCache()) let tree = parseFile(fileIdx, newIdentCache())
let outfile = changeFileExt(infile, ".pretty.nim") let outfile = changeFileExt(infile, ".pretty.nim")
renderModule(tree, infile, outfile, {}) renderModule(tree, infile, outfile, {}, fileIdx)
proc main = proc main =
var infile: string var infile: string

View file

@ -31,7 +31,7 @@ file: ticker.html
[Documentation] [Documentation]
doc: "endb.rst;intern.txt;apis.txt;lib.rst;manual.rst;tut1.rst;tut2.rst;nimc.rst;overview.rst;filters.rst" doc: "endb.rst;intern.txt;apis.txt;lib.rst;manual.rst;tut1.rst;tut2.rst;nimc.rst;overview.rst;filters.rst"
doc: "tools.txt;niminst.rst;nimgrep.rst;gc.rst;estp.rst;idetools.rst;docgen.rst;koch.rst;backends.txt" doc: "tools.txt;niminst.rst;nimgrep.rst;gc.rst;estp.rst;idetools.rst;docgen.rst;koch.rst;backends.txt"
doc: "nimfix.rst;nimsuggest.rst;nep1.rst;nims.rst;contributing.rst" doc: "nimfix.rst;nimsuggest.rst;nep1.rst;nims.rst;contributing.rst;manual/*.rst"
pdf: "manual.rst;lib.rst;tut1.rst;tut2.rst;nimc.rst;niminst.rst;gc.rst" pdf: "manual.rst;lib.rst;tut1.rst;tut2.rst;nimc.rst;niminst.rst;gc.rst"
srcdoc2: "system.nim;system/nimscript;pure/ospaths" srcdoc2: "system.nim;system/nimscript;pure/ospaths"
srcdoc2: "core/macros;pure/marshal;core/typeinfo" srcdoc2: "core/macros;pure/marshal;core/typeinfo"