Nim/compiler/jsgen.nim
Andreas Rumpf 25a1987517 Merge pull request #2780 from flaviut/cleanup-crc
Use SHA1 instead of CRC
2015-06-09 15:10:21 +02:00

1766 lines
62 KiB
Nim

#
#
# The Nim Compiler
# (c) Copyright 2015 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
# This is the JavaScript code generator.
# Soon also a Luajit code generator. ;-)
discard """
The JS code generator contains only 2 tricks:
Trick 1
-------
Some locations (for example 'var int') require "fat pointers" (``etyBaseIndex``)
which are pairs (array, index). The derefence operation is then 'array[index]'.
Check ``mapType`` for the details.
Trick 2
-------
It is preferable to generate '||' and '&&' if possible since that is more
idiomatic and hence should be friendlier for the JS JIT implementation. However
code like ``foo and (let bar = baz())`` cannot be translated this way. Instead
the expressions need to be transformed into statements. ``isSimpleExpr``
implements the required case distinction.
"""
import
ast, astalgo, strutils, hashes, trees, platform, magicsys, extccomp, options,
nversion, nimsets, msgs, secure_hash, bitsets, idents, lists, types, os,
times, ropes, math, passes, ccgutils, wordrecg, renderer, rodread, rodutils,
intsets, cgmeth, lowerings
type
TTarget = enum
targetJS, targetLua
TJSGen = object of TPassContext
module: PSym
BModule = ref TJSGen
TJSTypeKind = enum # necessary JS "types"
etyNone, # no type
etyNull, # null type
etyProc, # proc type
etyBool, # bool type
etyInt, # JavaScript's int
etyFloat, # JavaScript's float
etyString, # JavaScript's string
etyObject, # JavaScript's reference to an object
etyBaseIndex # base + index needed
TResKind = enum
resNone, # not set
resExpr, # is some complex expression
resVal # is a temporary/value/l-value
TCompRes = object
kind: TResKind
typ: TJSTypeKind
res: Rope # result part; index if this is an
# (address, index)-tuple
address: Rope # address of an (address, index)-tuple
TBlock = object
id: int # the ID of the label; positive means that it
# has been used (i.e. the label should be emitted)
isLoop: bool # whether it's a 'block' or 'while'
TGlobals = object
typeInfo, code: Rope
forwarded: seq[PSym]
generatedSyms: IntSet
typeInfoGenerated: IntSet
PGlobals = ref TGlobals
PProc = ref TProc
TProc = object
procDef: PNode
prc: PSym
locals, body: Rope
options: TOptions
module: BModule
g: PGlobals
beforeRetNeeded: bool
target: TTarget # duplicated here for faster dispatching
unique: int # for temp identifier generation
blocks: seq[TBlock]
up: PProc # up the call chain; required for closure support
template `|`(a, b: expr): expr {.immediate, dirty.} =
(if p.target == targetJS: a else: b)
proc newGlobals(): PGlobals =
new(result)
result.forwarded = @[]
result.generatedSyms = initIntSet()
result.typeInfoGenerated = initIntSet()
proc initCompRes(r: var TCompRes) =
r.address = nil
r.res = nil
r.typ = etyNone
r.kind = resNone
proc rdLoc(a: TCompRes): Rope {.inline.} =
result = a.res
when false:
if a.typ != etyBaseIndex:
result = a.res
else:
result = "$1[$2]" % [a.address, a.res]
proc newProc(globals: PGlobals, module: BModule, procDef: PNode,
options: TOptions): PProc =
result = PProc(
blocks: @[],
options: options,
module: module,
procDef: procDef,
g: globals)
if procDef != nil: result.prc = procDef.sons[namePos].sym
const
MappedToObject = {tyObject, tyArray, tyArrayConstr, tyTuple, tyOpenArray,
tySet, tyBigNum, tyVarargs}
proc mapType(typ: PType): TJSTypeKind =
let t = skipTypes(typ, abstractInst)
case t.kind
of tyVar, tyRef, tyPtr:
if skipTypes(t.lastSon, abstractInst).kind in MappedToObject:
result = etyObject
else:
result = etyBaseIndex
of tyPointer:
# treat a tyPointer like a typed pointer to an array of bytes
result = etyInt
of tyRange, tyDistinct, tyOrdinal, tyConst, tyMutable, tyIter, tyProxy:
result = mapType(t.sons[0])
of tyInt..tyInt64, tyUInt..tyUInt64, tyEnum, tyChar: result = etyInt
of tyBool: result = etyBool
of tyFloat..tyFloat128: result = etyFloat
of tySet: result = etyObject # map a set to a table
of tyString, tySequence: result = etyInt # little hack to get right semantics
of tyObject, tyArray, tyArrayConstr, tyTuple, tyOpenArray, tyBigNum,
tyVarargs:
result = etyObject
of tyNil: result = etyNull
of tyGenericInst, tyGenericParam, tyGenericBody, tyGenericInvocation,
tyNone, tyFromExpr, tyForward, tyEmpty, tyFieldAccessor,
tyExpr, tyStmt, tyStatic, tyTypeDesc, tyTypeClasses:
result = etyNone
of tyProc: result = etyProc
of tyCString: result = etyString
proc mangleName(s: PSym): Rope =
result = s.loc.r
if result == nil:
result = rope(mangle(s.name.s))
add(result, "_")
add(result, rope(s.id))
s.loc.r = result
proc makeJSString(s: string): Rope =
(if s.isNil: "null".rope else: strutils.escape(s).rope)
include jstypes
proc gen(p: PProc, n: PNode, r: var TCompRes)
proc genStmt(p: PProc, n: PNode)
proc genProc(oldProc: PProc, prc: PSym): Rope
proc genConstant(p: PProc, c: PSym)
proc useMagic(p: PProc, name: string) =
if name.len == 0: return
var s = magicsys.getCompilerProc(name)
if s != nil:
internalAssert s.kind in {skProc, skMethod, skConverter}
if not p.g.generatedSyms.containsOrIncl(s.id):
add(p.g.code, genProc(p, s))
else:
# we used to exclude the system module from this check, but for DLL
# generation support this sloppyness leads to hard to detect bugs, so
# we're picky here for the system module too:
if p.prc != nil: globalError(p.prc.info, errSystemNeeds, name)
else: rawMessage(errSystemNeeds, name)
proc isSimpleExpr(n: PNode): bool =
# calls all the way down --> can stay expression based
if n.kind in nkCallKinds+{nkBracketExpr, nkBracket, nkCurly, nkDotExpr, nkPar,
nkObjConstr}:
for c in n:
if not c.isSimpleExpr: return false
result = true
elif n.isAtom:
result = true
proc getTemp(p: PProc): Rope =
inc(p.unique)
result = "Tmp$1" % [rope(p.unique)]
addf(p.locals, "var $1;$n" | "local $1;$n", [result])
proc genAnd(p: PProc, a, b: PNode, r: var TCompRes) =
assert r.kind == resNone
var x, y: TCompRes
if a.isSimpleExpr and b.isSimpleExpr:
gen(p, a, x)
gen(p, b, y)
r.kind = resExpr
r.res = ("($1 && $2)" | "($1 and $2)") % [x.rdLoc, y.rdLoc]
else:
r.res = p.getTemp
r.kind = resVal
# while a and b:
# -->
# while true:
# aa
# if not a: tmp = false
# else:
# bb
# tmp = b
# tmp
gen(p, a, x)
p.body.addf("if (!$1) $2 = false; else {" |
"if not $1 then $2 = false; else", [x.rdLoc, r.rdLoc])
gen(p, b, y)
p.body.addf("$2 = $1; }" |
"$2 = $1 end", [y.rdLoc, r.rdLoc])
proc genOr(p: PProc, a, b: PNode, r: var TCompRes) =
assert r.kind == resNone
var x, y: TCompRes
if a.isSimpleExpr and b.isSimpleExpr:
gen(p, a, x)
gen(p, b, y)
r.kind = resExpr
r.res = ("($1 || $2)" | "($1 or $2)") % [x.rdLoc, y.rdLoc]
else:
r.res = p.getTemp
r.kind = resVal
gen(p, a, x)
p.body.addf("if ($1) $2 = true; else {" |
"if $1 then $2 = true; else", [x.rdLoc, r.rdLoc])
gen(p, b, y)
p.body.addf("$2 = $1; }" |
"$2 = $1 end", [y.rdLoc, r.rdLoc])
type
TMagicFrmt = array[0..3, string]
TMagicOps = array[mAddI..mStrToStr, TMagicFrmt]
const # magic checked op; magic unchecked op; checked op; unchecked op
jsOps: TMagicOps = [
["addInt", "", "addInt($1, $2)", "($1 + $2)"], # AddI
["subInt", "", "subInt($1, $2)", "($1 - $2)"], # SubI
["mulInt", "", "mulInt($1, $2)", "($1 * $2)"], # MulI
["divInt", "", "divInt($1, $2)", "Math.floor($1 / $2)"], # DivI
["modInt", "", "modInt($1, $2)", "Math.floor($1 % $2)"], # ModI
["addInt64", "", "addInt64($1, $2)", "($1 + $2)"], # AddI64
["subInt64", "", "subInt64($1, $2)", "($1 - $2)"], # SubI64
["mulInt64", "", "mulInt64($1, $2)", "($1 * $2)"], # MulI64
["divInt64", "", "divInt64($1, $2)", "Math.floor($1 / $2)"], # DivI64
["modInt64", "", "modInt64($1, $2)", "Math.floor($1 % $2)"], # ModI64
["addInt", "", "addInt($1, $2)", "($1 + $2)"], # Succ
["subInt", "", "subInt($1, $2)", "($1 - $2)"], # Pred
["", "", "($1 + $2)", "($1 + $2)"], # AddF64
["", "", "($1 - $2)", "($1 - $2)"], # SubF64
["", "", "($1 * $2)", "($1 * $2)"], # MulF64
["", "", "($1 / $2)", "($1 / $2)"], # DivF64
["", "", "($1 >>> $2)", "($1 >>> $2)"], # ShrI
["", "", "($1 << $2)", "($1 << $2)"], # ShlI
["", "", "($1 & $2)", "($1 & $2)"], # BitandI
["", "", "($1 | $2)", "($1 | $2)"], # BitorI
["", "", "($1 ^ $2)", "($1 ^ $2)"], # BitxorI
["nimMin", "nimMin", "nimMin($1, $2)", "nimMin($1, $2)"], # MinI
["nimMax", "nimMax", "nimMax($1, $2)", "nimMax($1, $2)"], # MaxI
["", "", "($1 >>> $2)", "($1 >>> $2)"], # ShrI64
["", "", "($1 << $2)", "($1 << $2)"], # ShlI64
["", "", "($1 & $2)", "($1 & $2)"], # BitandI64
["", "", "($1 | $2)", "($1 | $2)"], # BitorI64
["", "", "($1 ^ $2)", "($1 ^ $2)"], # BitxorI64
["nimMin", "nimMin", "nimMin($1, $2)", "nimMin($1, $2)"], # MinF64
["nimMax", "nimMax", "nimMax($1, $2)", "nimMax($1, $2)"], # MaxF64
["addU", "addU", "addU($1, $2)", "addU($1, $2)"], # addU
["subU", "subU", "subU($1, $2)", "subU($1, $2)"], # subU
["mulU", "mulU", "mulU($1, $2)", "mulU($1, $2)"], # mulU
["divU", "divU", "divU($1, $2)", "divU($1, $2)"], # divU
["modU", "modU", "modU($1, $2)", "modU($1, $2)"], # modU
["", "", "($1 == $2)", "($1 == $2)"], # EqI
["", "", "($1 <= $2)", "($1 <= $2)"], # LeI
["", "", "($1 < $2)", "($1 < $2)"], # LtI
["", "", "($1 == $2)", "($1 == $2)"], # EqI64
["", "", "($1 <= $2)", "($1 <= $2)"], # LeI64
["", "", "($1 < $2)", "($1 < $2)"], # LtI64
["", "", "($1 == $2)", "($1 == $2)"], # EqF64
["", "", "($1 <= $2)", "($1 <= $2)"], # LeF64
["", "", "($1 < $2)", "($1 < $2)"], # LtF64
["leU", "leU", "leU($1, $2)", "leU($1, $2)"], # leU
["ltU", "ltU", "ltU($1, $2)", "ltU($1, $2)"], # ltU
["leU64", "leU64", "leU64($1, $2)", "leU64($1, $2)"], # leU64
["ltU64", "ltU64", "ltU64($1, $2)", "ltU64($1, $2)"], # ltU64
["", "", "($1 == $2)", "($1 == $2)"], # EqEnum
["", "", "($1 <= $2)", "($1 <= $2)"], # LeEnum
["", "", "($1 < $2)", "($1 < $2)"], # LtEnum
["", "", "($1 == $2)", "($1 == $2)"], # EqCh
["", "", "($1 <= $2)", "($1 <= $2)"], # LeCh
["", "", "($1 < $2)", "($1 < $2)"], # LtCh
["", "", "($1 == $2)", "($1 == $2)"], # EqB
["", "", "($1 <= $2)", "($1 <= $2)"], # LeB
["", "", "($1 < $2)", "($1 < $2)"], # LtB
["", "", "($1 == $2)", "($1 == $2)"], # EqRef
["", "", "($1 == $2)", "($1 == $2)"], # EqUntracedRef
["", "", "($1 <= $2)", "($1 <= $2)"], # LePtr
["", "", "($1 < $2)", "($1 < $2)"], # LtPtr
["", "", "($1 == $2)", "($1 == $2)"], # EqCString
["", "", "($1 != $2)", "($1 != $2)"], # Xor
["", "", "($1 == $2)", "($1 == $2)"], # EqProc
["negInt", "", "negInt($1)", "-($1)"], # UnaryMinusI
["negInt64", "", "negInt64($1)", "-($1)"], # UnaryMinusI64
["absInt", "", "absInt($1)", "Math.abs($1)"], # AbsI
["absInt64", "", "absInt64($1)", "Math.abs($1)"], # AbsI64
["", "", "!($1)", "!($1)"], # Not
["", "", "+($1)", "+($1)"], # UnaryPlusI
["", "", "~($1)", "~($1)"], # BitnotI
["", "", "~($1)", "~($1)"], # BitnotI64
["", "", "+($1)", "+($1)"], # UnaryPlusF64
["", "", "-($1)", "-($1)"], # UnaryMinusF64
["", "", "Math.abs($1)", "Math.abs($1)"], # AbsF64
["Ze8ToI", "Ze8ToI", "Ze8ToI($1)", "Ze8ToI($1)"], # mZe8ToI
["Ze8ToI64", "Ze8ToI64", "Ze8ToI64($1)", "Ze8ToI64($1)"], # mZe8ToI64
["Ze16ToI", "Ze16ToI", "Ze16ToI($1)", "Ze16ToI($1)"], # mZe16ToI
["Ze16ToI64", "Ze16ToI64", "Ze16ToI64($1)", "Ze16ToI64($1)"], # mZe16ToI64
["Ze32ToI64", "Ze32ToI64", "Ze32ToI64($1)", "Ze32ToI64($1)"], # mZe32ToI64
["ZeIToI64", "ZeIToI64", "ZeIToI64($1)", "ZeIToI64($1)"], # mZeIToI64
["toU8", "toU8", "toU8($1)", "toU8($1)"], # toU8
["toU16", "toU16", "toU16($1)", "toU16($1)"], # toU16
["toU32", "toU32", "toU32($1)", "toU32($1)"], # toU32
["", "", "$1", "$1"], # ToFloat
["", "", "$1", "$1"], # ToBiggestFloat
["", "", "Math.floor($1)", "Math.floor($1)"], # ToInt
["", "", "Math.floor($1)", "Math.floor($1)"], # ToBiggestInt
["nimCharToStr", "nimCharToStr", "nimCharToStr($1)", "nimCharToStr($1)"],
["nimBoolToStr", "nimBoolToStr", "nimBoolToStr($1)", "nimBoolToStr($1)"], [
"cstrToNimstr", "cstrToNimstr", "cstrToNimstr(($1)+\"\")",
"cstrToNimstr(($1)+\"\")"], ["cstrToNimstr", "cstrToNimstr",
"cstrToNimstr(($1)+\"\")",
"cstrToNimstr(($1)+\"\")"], ["cstrToNimstr",
"cstrToNimstr", "cstrToNimstr(($1)+\"\")", "cstrToNimstr(($1)+\"\")"],
["cstrToNimstr", "cstrToNimstr", "cstrToNimstr($1)", "cstrToNimstr($1)"],
["", "", "$1", "$1"]]
luaOps: TMagicOps = [
["addInt", "", "addInt($1, $2)", "($1 + $2)"], # AddI
["subInt", "", "subInt($1, $2)", "($1 - $2)"], # SubI
["mulInt", "", "mulInt($1, $2)", "($1 * $2)"], # MulI
["divInt", "", "divInt($1, $2)", "Math.floor($1 / $2)"], # DivI
["modInt", "", "modInt($1, $2)", "Math.floor($1 % $2)"], # ModI
["addInt64", "", "addInt64($1, $2)", "($1 + $2)"], # AddI64
["subInt64", "", "subInt64($1, $2)", "($1 - $2)"], # SubI64
["mulInt64", "", "mulInt64($1, $2)", "($1 * $2)"], # MulI64
["divInt64", "", "divInt64($1, $2)", "Math.floor($1 / $2)"], # DivI64
["modInt64", "", "modInt64($1, $2)", "Math.floor($1 % $2)"], # ModI64
["addInt", "", "addInt($1, $2)", "($1 + $2)"], # Succ
["subInt", "", "subInt($1, $2)", "($1 - $2)"], # Pred
["", "", "($1 + $2)", "($1 + $2)"], # AddF64
["", "", "($1 - $2)", "($1 - $2)"], # SubF64
["", "", "($1 * $2)", "($1 * $2)"], # MulF64
["", "", "($1 / $2)", "($1 / $2)"], # DivF64
["", "", "($1 >>> $2)", "($1 >>> $2)"], # ShrI
["", "", "($1 << $2)", "($1 << $2)"], # ShlI
["", "", "($1 & $2)", "($1 & $2)"], # BitandI
["", "", "($1 | $2)", "($1 | $2)"], # BitorI
["", "", "($1 ^ $2)", "($1 ^ $2)"], # BitxorI
["nimMin", "nimMin", "nimMin($1, $2)", "nimMin($1, $2)"], # MinI
["nimMax", "nimMax", "nimMax($1, $2)", "nimMax($1, $2)"], # MaxI
["", "", "($1 >>> $2)", "($1 >>> $2)"], # ShrI64
["", "", "($1 << $2)", "($1 << $2)"], # ShlI64
["", "", "($1 & $2)", "($1 & $2)"], # BitandI64
["", "", "($1 | $2)", "($1 | $2)"], # BitorI64
["", "", "($1 ^ $2)", "($1 ^ $2)"], # BitxorI64
["nimMin", "nimMin", "nimMin($1, $2)", "nimMin($1, $2)"], # MinF64
["nimMax", "nimMax", "nimMax($1, $2)", "nimMax($1, $2)"], # MaxF64
["addU", "addU", "addU($1, $2)", "addU($1, $2)"], # addU
["subU", "subU", "subU($1, $2)", "subU($1, $2)"], # subU
["mulU", "mulU", "mulU($1, $2)", "mulU($1, $2)"], # mulU
["divU", "divU", "divU($1, $2)", "divU($1, $2)"], # divU
["modU", "modU", "modU($1, $2)", "modU($1, $2)"], # modU
["", "", "($1 == $2)", "($1 == $2)"], # EqI
["", "", "($1 <= $2)", "($1 <= $2)"], # LeI
["", "", "($1 < $2)", "($1 < $2)"], # LtI
["", "", "($1 == $2)", "($1 == $2)"], # EqI64
["", "", "($1 <= $2)", "($1 <= $2)"], # LeI64
["", "", "($1 < $2)", "($1 < $2)"], # LtI64
["", "", "($1 == $2)", "($1 == $2)"], # EqF64
["", "", "($1 <= $2)", "($1 <= $2)"], # LeF64
["", "", "($1 < $2)", "($1 < $2)"], # LtF64
["leU", "leU", "leU($1, $2)", "leU($1, $2)"], # leU
["ltU", "ltU", "ltU($1, $2)", "ltU($1, $2)"], # ltU
["leU64", "leU64", "leU64($1, $2)", "leU64($1, $2)"], # leU64
["ltU64", "ltU64", "ltU64($1, $2)", "ltU64($1, $2)"], # ltU64
["", "", "($1 == $2)", "($1 == $2)"], # EqEnum
["", "", "($1 <= $2)", "($1 <= $2)"], # LeEnum
["", "", "($1 < $2)", "($1 < $2)"], # LtEnum
["", "", "($1 == $2)", "($1 == $2)"], # EqCh
["", "", "($1 <= $2)", "($1 <= $2)"], # LeCh
["", "", "($1 < $2)", "($1 < $2)"], # LtCh
["", "", "($1 == $2)", "($1 == $2)"], # EqB
["", "", "($1 <= $2)", "($1 <= $2)"], # LeB
["", "", "($1 < $2)", "($1 < $2)"], # LtB
["", "", "($1 == $2)", "($1 == $2)"], # EqRef
["", "", "($1 == $2)", "($1 == $2)"], # EqUntracedRef
["", "", "($1 <= $2)", "($1 <= $2)"], # LePtr
["", "", "($1 < $2)", "($1 < $2)"], # LtPtr
["", "", "($1 == $2)", "($1 == $2)"], # EqCString
["", "", "($1 != $2)", "($1 != $2)"], # Xor
["", "", "($1 == $2)", "($1 == $2)"], # EqProc
["negInt", "", "negInt($1)", "-($1)"], # UnaryMinusI
["negInt64", "", "negInt64($1)", "-($1)"], # UnaryMinusI64
["absInt", "", "absInt($1)", "Math.abs($1)"], # AbsI
["absInt64", "", "absInt64($1)", "Math.abs($1)"], # AbsI64
["", "", "not ($1)", "not ($1)"], # Not
["", "", "+($1)", "+($1)"], # UnaryPlusI
["", "", "~($1)", "~($1)"], # BitnotI
["", "", "~($1)", "~($1)"], # BitnotI64
["", "", "+($1)", "+($1)"], # UnaryPlusF64
["", "", "-($1)", "-($1)"], # UnaryMinusF64
["", "", "Math.abs($1)", "Math.abs($1)"], # AbsF64
["Ze8ToI", "Ze8ToI", "Ze8ToI($1)", "Ze8ToI($1)"], # mZe8ToI
["Ze8ToI64", "Ze8ToI64", "Ze8ToI64($1)", "Ze8ToI64($1)"], # mZe8ToI64
["Ze16ToI", "Ze16ToI", "Ze16ToI($1)", "Ze16ToI($1)"], # mZe16ToI
["Ze16ToI64", "Ze16ToI64", "Ze16ToI64($1)", "Ze16ToI64($1)"], # mZe16ToI64
["Ze32ToI64", "Ze32ToI64", "Ze32ToI64($1)", "Ze32ToI64($1)"], # mZe32ToI64
["ZeIToI64", "ZeIToI64", "ZeIToI64($1)", "ZeIToI64($1)"], # mZeIToI64
["toU8", "toU8", "toU8($1)", "toU8($1)"], # toU8
["toU16", "toU16", "toU16($1)", "toU16($1)"], # toU16
["toU32", "toU32", "toU32($1)", "toU32($1)"], # toU32
["", "", "$1", "$1"], # ToFloat
["", "", "$1", "$1"], # ToBiggestFloat
["", "", "Math.floor($1)", "Math.floor($1)"], # ToInt
["", "", "Math.floor($1)", "Math.floor($1)"], # ToBiggestInt
["nimCharToStr", "nimCharToStr", "nimCharToStr($1)", "nimCharToStr($1)"],
["nimBoolToStr", "nimBoolToStr", "nimBoolToStr($1)", "nimBoolToStr($1)"], [
"cstrToNimstr", "cstrToNimstr", "cstrToNimstr(($1)+\"\")",
"cstrToNimstr(($1)+\"\")"], ["cstrToNimstr", "cstrToNimstr",
"cstrToNimstr(($1)+\"\")",
"cstrToNimstr(($1)+\"\")"], ["cstrToNimstr",
"cstrToNimstr", "cstrToNimstr(($1)+\"\")", "cstrToNimstr(($1)+\"\")"],
["cstrToNimstr", "cstrToNimstr", "cstrToNimstr($1)", "cstrToNimstr($1)"],
["", "", "$1", "$1"]]
proc binaryExpr(p: PProc, n: PNode, r: var TCompRes, magic, frmt: string) =
var x, y: TCompRes
useMagic(p, magic)
gen(p, n.sons[1], x)
gen(p, n.sons[2], y)
r.res = frmt % [x.rdLoc, y.rdLoc]
r.kind = resExpr
proc ternaryExpr(p: PProc, n: PNode, r: var TCompRes, magic, frmt: string) =
var x, y, z: TCompRes
useMagic(p, magic)
gen(p, n.sons[1], x)
gen(p, n.sons[2], y)
gen(p, n.sons[3], z)
r.res = frmt % [x.rdLoc, y.rdLoc, z.rdLoc]
r.kind = resExpr
proc unaryExpr(p: PProc, n: PNode, r: var TCompRes, magic, frmt: string) =
useMagic(p, magic)
gen(p, n.sons[1], r)
r.res = frmt % [r.rdLoc]
r.kind = resExpr
proc arithAux(p: PProc, n: PNode, r: var TCompRes, op: TMagic, ops: TMagicOps) =
var
x, y: TCompRes
let i = ord(optOverflowCheck notin p.options)
useMagic(p, ops[op][i])
if sonsLen(n) > 2:
gen(p, n.sons[1], x)
gen(p, n.sons[2], y)
r.res = ops[op][i + 2] % [x.rdLoc, y.rdLoc]
else:
gen(p, n.sons[1], r)
r.res = ops[op][i + 2] % [r.rdLoc]
r.kind = resExpr
proc arith(p: PProc, n: PNode, r: var TCompRes, op: TMagic) =
arithAux(p, n, r, op, jsOps | luaOps)
proc genLineDir(p: PProc, n: PNode) =
let line = toLinenumber(n.info)
if optLineDir in p.options:
addf(p.body, "// line $2 \"$1\"$n" | "-- line $2 \"$1\"$n",
[rope(toFilename(n.info)), rope(line)])
if {optStackTrace, optEndb} * p.options == {optStackTrace, optEndb} and
((p.prc == nil) or sfPure notin p.prc.flags):
useMagic(p, "endb")
addf(p.body, "endb($1);$n", [rope(line)])
elif ({optLineTrace, optStackTrace} * p.options ==
{optLineTrace, optStackTrace}) and
((p.prc == nil) or not (sfPure in p.prc.flags)):
addf(p.body, "F.line = $1;$n", [rope(line)])
proc genWhileStmt(p: PProc, n: PNode) =
var
cond: TCompRes
internalAssert isEmptyType(n.typ)
genLineDir(p, n)
inc(p.unique)
var length = len(p.blocks)
setLen(p.blocks, length + 1)
p.blocks[length].id = -p.unique
p.blocks[length].isLoop = true
let labl = p.unique.rope
addf(p.body, "L$1: while (true) {$n" | "while true do$n", [labl])
gen(p, n.sons[0], cond)
addf(p.body, "if (!$1) break L$2;$n" | "if not $1 then goto ::L$2:: end;$n",
[cond.res, labl])
genStmt(p, n.sons[1])
addf(p.body, "}$n" | "end ::L$#::$n", [labl])
setLen(p.blocks, length)
proc moveInto(p: PProc, src: var TCompRes, dest: TCompRes) =
if src.kind != resNone:
if dest.kind != resNone:
p.body.addf("$1 = $2;$n", [dest.rdLoc, src.rdLoc])
else:
p.body.addf("$1;$n", [src.rdLoc])
src.kind = resNone
src.res = nil
proc genTry(p: PProc, n: PNode, r: var TCompRes) =
# code to generate:
#
# var sp = {prev: excHandler, exc: null};
# excHandler = sp;
# try {
# stmts;
# TMP = e
# } catch (e) {
# if (e.typ && e.typ == NTI433 || e.typ == NTI2321) {
# stmts;
# } else if (e.typ && e.typ == NTI32342) {
# stmts;
# } else {
# stmts;
# }
# } finally {
# stmts;
# excHandler = excHandler.prev;
# }
genLineDir(p, n)
if not isEmptyType(n.typ):
r.kind = resVal
r.res = getTemp(p)
inc(p.unique)
var safePoint = "Tmp$1" % [rope(p.unique)]
addf(p.body,
"var $1 = {prev: excHandler, exc: null};$nexcHandler = $1;$n" |
"local $1 = pcall(",
[safePoint])
if optStackTrace in p.options: add(p.body, "framePtr = F;" & tnl)
addf(p.body, "try {$n" | "function()$n", [])
var length = sonsLen(n)
var a: TCompRes
gen(p, n.sons[0], a)
moveInto(p, a, r)
var i = 1
if p.target == targetJS and length > 1 and n.sons[i].kind == nkExceptBranch:
addf(p.body, "} catch (EXC) {$n lastJSError = EXC;$n", [])
elif p.target == targetLua:
addf(p.body, "end)$n", [])
while i < length and n.sons[i].kind == nkExceptBranch:
let blen = sonsLen(n.sons[i])
if blen == 1:
# general except section:
if i > 1: addf(p.body, "else {$n" | "else$n", [])
gen(p, n.sons[i].sons[0], a)
moveInto(p, a, r)
if i > 1: addf(p.body, "}$n" | "end$n", [])
else:
var orExpr: Rope = nil
useMagic(p, "isObj")
for j in countup(0, blen - 2):
if n.sons[i].sons[j].kind != nkType:
internalError(n.info, "genTryStmt")
if orExpr != nil: add(orExpr, "||" | " or ")
addf(orExpr, "isObj($1.exc.m_type, $2)",
[safePoint, genTypeInfo(p, n.sons[i].sons[j].typ)])
if i > 1: add(p.body, "else ")
addf(p.body, "if ($1.exc && ($2)) {$n" | "if $1.exc and ($2) then$n",
[safePoint, orExpr])
gen(p, n.sons[i].sons[blen - 1], a)
moveInto(p, a, r)
addf(p.body, "}$n" | "end$n", [])
inc(i)
if p.target == targetJS:
add(p.body, "} finally {" & tnl & "excHandler = excHandler.prev;" & tnl)
if i < length and n.sons[i].kind == nkFinally:
genStmt(p, n.sons[i].sons[0])
if p.target == targetJS:
add(p.body, "}" & tnl)
if p.target == targetLua:
# we need to repeat the finally block for Lua ...
if i < length and n.sons[i].kind == nkFinally:
genStmt(p, n.sons[i].sons[0])
proc genRaiseStmt(p: PProc, n: PNode) =
genLineDir(p, n)
if n.sons[0].kind != nkEmpty:
var a: TCompRes
gen(p, n.sons[0], a)
let typ = skipTypes(n.sons[0].typ, abstractPtrs)
useMagic(p, "raiseException")
addf(p.body, "raiseException($1, $2);$n",
[a.rdLoc, makeJSString(typ.sym.name.s)])
else:
useMagic(p, "reraiseException")
add(p.body, "reraiseException();" & tnl)
proc genCaseJS(p: PProc, n: PNode, r: var TCompRes) =
var
cond, stmt: TCompRes
genLineDir(p, n)
gen(p, n.sons[0], cond)
let stringSwitch = skipTypes(n.sons[0].typ, abstractVar).kind == tyString
if stringSwitch:
useMagic(p, "toJSStr")
addf(p.body, "switch (toJSStr($1)) {$n", [cond.rdLoc])
else:
addf(p.body, "switch ($1) {$n", [cond.rdLoc])
if not isEmptyType(n.typ):
r.kind = resVal
r.res = getTemp(p)
for i in countup(1, sonsLen(n) - 1):
let it = n.sons[i]
case it.kind
of nkOfBranch:
for j in countup(0, sonsLen(it) - 2):
let e = it.sons[j]
if e.kind == nkRange:
var v = copyNode(e.sons[0])
while v.intVal <= e.sons[1].intVal:
gen(p, v, cond)
addf(p.body, "case $1: ", [cond.rdLoc])
inc(v.intVal)
else:
if stringSwitch:
case e.kind
of nkStrLit..nkTripleStrLit: addf(p.body, "case $1: ",
[makeJSString(e.strVal)])
else: internalError(e.info, "jsgen.genCaseStmt: 2")
else:
gen(p, e, cond)
addf(p.body, "case $1: ", [cond.rdLoc])
gen(p, lastSon(it), stmt)
moveInto(p, stmt, r)
addf(p.body, "$nbreak;$n", [])
of nkElse:
addf(p.body, "default: $n", [])
gen(p, it.sons[0], stmt)
moveInto(p, stmt, r)
addf(p.body, "break;$n", [])
else: internalError(it.info, "jsgen.genCaseStmt")
addf(p.body, "}$n", [])
proc genCaseLua(p: PProc, n: PNode, r: var TCompRes) =
var
cond, stmt: TCompRes
genLineDir(p, n)
gen(p, n.sons[0], cond)
let stringSwitch = skipTypes(n.sons[0].typ, abstractVar).kind == tyString
if stringSwitch:
useMagic(p, "eqStr")
let tmp = getTemp(p)
addf(p.body, "$1 = $2;$n", [tmp, cond.rdLoc])
if not isEmptyType(n.typ):
r.kind = resVal
r.res = getTemp(p)
for i in countup(1, sonsLen(n) - 1):
let it = n.sons[i]
case it.kind
of nkOfBranch:
if i != 1: addf(p.body, "$nelsif ", [])
else: addf(p.body, "if ", [])
for j in countup(0, sonsLen(it) - 2):
if j != 0: add(p.body, " or ")
let e = it.sons[j]
if e.kind == nkRange:
var ia, ib: TCompRes
gen(p, e.sons[0], ia)
gen(p, e.sons[1], ib)
addf(p.body, "$1 >= $2 and $1 <= $3", [tmp, ia.rdLoc, ib.rdLoc])
else:
if stringSwitch:
case e.kind
of nkStrLit..nkTripleStrLit: addf(p.body, "eqStr($1, $2)",
[tmp, makeJSString(e.strVal)])
else: internalError(e.info, "jsgen.genCaseStmt: 2")
else:
gen(p, e, cond)
addf(p.body, "$1 == $2", [tmp, cond.rdLoc])
addf(p.body, " then$n", [])
gen(p, lastSon(it), stmt)
moveInto(p, stmt, r)
of nkElse:
addf(p.body, "else$n", [])
gen(p, it.sons[0], stmt)
moveInto(p, stmt, r)
else: internalError(it.info, "jsgen.genCaseStmt")
addf(p.body, "$nend$n", [])
proc genBlock(p: PProc, n: PNode, r: var TCompRes) =
inc(p.unique)
let idx = len(p.blocks)
if n.sons[0].kind != nkEmpty:
# named block?
if (n.sons[0].kind != nkSym): internalError(n.info, "genBlock")
var sym = n.sons[0].sym
sym.loc.k = locOther
sym.position = idx+1
setLen(p.blocks, idx + 1)
p.blocks[idx].id = - p.unique # negative because it isn't used yet
let labl = p.unique
addf(p.body, "L$1: do {$n" | "", [labl.rope])
gen(p, n.sons[1], r)
addf(p.body, "} while(false);$n" | "$n::L$#::$n", [labl.rope])
setLen(p.blocks, idx)
proc genBreakStmt(p: PProc, n: PNode) =
var idx: int
genLineDir(p, n)
if n.sons[0].kind != nkEmpty:
# named break?
assert(n.sons[0].kind == nkSym)
let sym = n.sons[0].sym
assert(sym.loc.k == locOther)
idx = sym.position-1
else:
# an unnamed 'break' can only break a loop after 'transf' pass:
idx = len(p.blocks) - 1
while idx >= 0 and not p.blocks[idx].isLoop: dec idx
if idx < 0 or not p.blocks[idx].isLoop:
internalError(n.info, "no loop to break")
p.blocks[idx].id = abs(p.blocks[idx].id) # label is used
addf(p.body, "break L$1;$n" | "goto ::L$1::;$n", [rope(p.blocks[idx].id)])
proc genAsmStmt(p: PProc, n: PNode) =
genLineDir(p, n)
assert(n.kind == nkAsmStmt)
for i in countup(0, sonsLen(n) - 1):
case n.sons[i].kind
of nkStrLit..nkTripleStrLit: add(p.body, n.sons[i].strVal)
of nkSym: add(p.body, mangleName(n.sons[i].sym))
else: internalError(n.sons[i].info, "jsgen: genAsmStmt()")
proc genIf(p: PProc, n: PNode, r: var TCompRes) =
var cond, stmt: TCompRes
var toClose = 0
if not isEmptyType(n.typ):
r.kind = resVal
r.res = getTemp(p)
for i in countup(0, sonsLen(n) - 1):
let it = n.sons[i]
if sonsLen(it) != 1:
if i > 0:
addf(p.body, "else {$n" | "else$n", [])
inc(toClose)
gen(p, it.sons[0], cond)
addf(p.body, "if ($1) {$n" | "if $# then$n", [cond.rdLoc])
gen(p, it.sons[1], stmt)
else:
# else part:
addf(p.body, "else {$n" | "else$n", [])
gen(p, it.sons[0], stmt)
moveInto(p, stmt, r)
addf(p.body, "}$n" | "end$n", [])
if p.target == targetJS:
add(p.body, repeat('}', toClose) & tnl)
else:
for i in 1..toClose: addf(p.body, "end$n", [])
proc generateHeader(p: PProc, typ: PType): Rope =
result = nil
for i in countup(1, sonsLen(typ.n) - 1):
assert(typ.n.sons[i].kind == nkSym)
var param = typ.n.sons[i].sym
if isCompileTimeOnly(param.typ): continue
if result != nil: add(result, ", ")
var name = mangleName(param)
add(result, name)
if mapType(param.typ) == etyBaseIndex:
add(result, ", ")
add(result, name)
add(result, "_Idx")
const
nodeKindsNeedNoCopy = {nkCharLit..nkInt64Lit, nkStrLit..nkTripleStrLit,
nkFloatLit..nkFloat64Lit, nkCurly, nkPar, nkObjConstr, nkStringToCString,
nkCStringToString, nkCall, nkPrefix, nkPostfix, nkInfix,
nkCommand, nkHiddenCallConv, nkCallStrLit}
proc needsNoCopy(y: PNode): bool =
result = (y.kind in nodeKindsNeedNoCopy) or
(skipTypes(y.typ, abstractInst).kind in {tyRef, tyPtr, tyVar})
proc genAsgnAux(p: PProc, x, y: PNode, noCopyNeeded: bool) =
var a, b: TCompRes
gen(p, x, a)
gen(p, y, b)
case mapType(x.typ)
of etyObject:
if needsNoCopy(y) or noCopyNeeded:
addf(p.body, "$1 = $2;$n", [a.rdLoc, b.rdLoc])
else:
useMagic(p, "nimCopy")
addf(p.body, "$1 = nimCopy($2, $3);$n",
[a.res, b.res, genTypeInfo(p, y.typ)])
of etyBaseIndex:
if a.typ != etyBaseIndex or b.typ != etyBaseIndex:
internalError(x.info, "genAsgn")
addf(p.body, "$1 = $2; $3 = $4;$n", [a.address, b.address, a.res, b.res])
else:
addf(p.body, "$1 = $2;$n", [a.res, b.res])
proc genAsgn(p: PProc, n: PNode) =
genLineDir(p, n)
genAsgnAux(p, n.sons[0], n.sons[1], noCopyNeeded=false)
proc genFastAsgn(p: PProc, n: PNode) =
genLineDir(p, n)
genAsgnAux(p, n.sons[0], n.sons[1], noCopyNeeded=true)
proc genSwap(p: PProc, n: PNode) =
var a, b: TCompRes
gen(p, n.sons[1], a)
gen(p, n.sons[2], b)
inc(p.unique)
var tmp = "Tmp$1" % [rope(p.unique)]
if mapType(skipTypes(n.sons[1].typ, abstractVar)) == etyBaseIndex:
inc(p.unique)
let tmp2 = "Tmp$1" % [rope(p.unique)]
if a.typ != etyBaseIndex or b.typ != etyBaseIndex:
internalError(n.info, "genSwap")
addf(p.body, "var $1 = $2; $2 = $3; $3 = $1;$n" |
"local $1 = $2; $2 = $3; $3 = $1;$n", [
tmp, a.address, b.address])
tmp = tmp2
addf(p.body, "var $1 = $2; $2 = $3; $3 = $1;" |
"local $1 = $2; $2 = $3; $3 = $1;", [tmp, a.res, b.res])
proc getFieldPosition(f: PNode): int =
case f.kind
of nkIntLit..nkUInt64Lit: result = int(f.intVal)
of nkSym: result = f.sym.position
else: internalError(f.info, "genFieldPosition")
proc genFieldAddr(p: PProc, n: PNode, r: var TCompRes) =
var a: TCompRes
r.typ = etyBaseIndex
let b = if n.kind == nkHiddenAddr: n.sons[0] else: n
gen(p, b.sons[0], a)
if skipTypes(b.sons[0].typ, abstractVarRange).kind == tyTuple:
r.res = makeJSString("Field" & $getFieldPosition(b.sons[1]))
else:
if b.sons[1].kind != nkSym: internalError(b.sons[1].info, "genFieldAddr")
var f = b.sons[1].sym
if f.loc.r == nil: f.loc.r = mangleName(f)
r.res = makeJSString($f.loc.r)
internalAssert a.typ != etyBaseIndex
r.address = a.res
r.kind = resExpr
proc genFieldAccess(p: PProc, n: PNode, r: var TCompRes) =
r.typ = etyNone
gen(p, n.sons[0], r)
if skipTypes(n.sons[0].typ, abstractVarRange).kind == tyTuple:
r.res = "$1.Field$2" % [r.res, getFieldPosition(n.sons[1]).rope]
else:
if n.sons[1].kind != nkSym: internalError(n.sons[1].info, "genFieldAccess")
var f = n.sons[1].sym
if f.loc.r == nil: f.loc.r = mangleName(f)
r.res = "$1.$2" % [r.res, f.loc.r]
r.kind = resExpr
proc genCheckedFieldAddr(p: PProc, n: PNode, r: var TCompRes) =
let m = if n.kind == nkHiddenAddr: n.sons[0] else: n
internalAssert m.kind == nkCheckedFieldExpr
genFieldAddr(p, m.sons[0], r) # XXX
proc genCheckedFieldAccess(p: PProc, n: PNode, r: var TCompRes) =
genFieldAccess(p, n.sons[0], r) # XXX
proc genArrayAddr(p: PProc, n: PNode, r: var TCompRes) =
var
a, b: TCompRes
first: BiggestInt
r.typ = etyBaseIndex
let m = if n.kind == nkHiddenAddr: n.sons[0] else: n
gen(p, m.sons[0], a)
gen(p, m.sons[1], b)
internalAssert a.typ != etyBaseIndex and b.typ != etyBaseIndex
r.address = a.res
var typ = skipTypes(m.sons[0].typ, abstractPtrs)
if typ.kind in {tyArray, tyArrayConstr}: first = firstOrd(typ.sons[0])
else: first = 0
if optBoundsCheck in p.options and not isConstExpr(m.sons[1]):
useMagic(p, "chckIndx")
r.res = "chckIndx($1, $2, $3.length)-$2" % [b.res, rope(first), a.res]
elif first != 0:
r.res = "($1)-$2" % [b.res, rope(first)]
else:
r.res = b.res
r.kind = resExpr
proc genArrayAccess(p: PProc, n: PNode, r: var TCompRes) =
var ty = skipTypes(n.sons[0].typ, abstractVarRange)
if ty.kind in {tyRef, tyPtr}: ty = skipTypes(ty.lastSon, abstractVarRange)
case ty.kind
of tyArray, tyArrayConstr, tyOpenArray, tySequence, tyString, tyCString,
tyVarargs:
genArrayAddr(p, n, r)
of tyTuple:
genFieldAddr(p, n, r)
else: internalError(n.info, "expr(nkBracketExpr, " & $ty.kind & ')')
r.typ = etyNone
if r.res == nil: internalError(n.info, "genArrayAccess")
r.res = "$1[$2]" % [r.address, r.res]
r.address = nil
r.kind = resExpr
proc isIndirect(v: PSym): bool =
result = {sfAddrTaken, sfGlobal} * v.flags != {} and
#(mapType(v.typ) != etyObject) and
{sfImportc, sfVolatile, sfExportc} * v.flags == {} and
v.kind notin {skProc, skConverter, skMethod, skIterator, skClosureIterator,
skConst, skTemp, skLet}
proc genAddr(p: PProc, n: PNode, r: var TCompRes) =
case n.sons[0].kind
of nkSym:
let s = n.sons[0].sym
if s.loc.r == nil: internalError(n.info, "genAddr: 3")
case s.kind
of skVar, skLet, skResult:
r.kind = resExpr
let jsType = mapType(n.typ)
if jsType == etyObject:
# make addr() a no-op:
r.typ = etyNone
if isIndirect(s):
r.res = s.loc.r & "[0]"
else:
r.res = s.loc.r
r.address = nil
elif {sfGlobal, sfAddrTaken} * s.flags != {} or jsType == etyBaseIndex:
# for ease of code generation, we do not distinguish between
# sfAddrTaken and sfGlobal.
r.typ = etyBaseIndex
r.address = s.loc.r
r.res = rope("0")
else:
# 'var openArray' for instance produces an 'addr' but this is harmless:
gen(p, n.sons[0], r)
#internalError(n.info, "genAddr: 4 " & renderTree(n))
else: internalError(n.info, "genAddr: 2")
of nkCheckedFieldExpr:
genCheckedFieldAddr(p, n, r)
of nkDotExpr:
if mapType(n.typ) == etyBaseIndex:
genFieldAddr(p, n.sons[0], r)
else:
genFieldAccess(p, n.sons[0], r)
of nkBracketExpr:
var ty = skipTypes(n.sons[0].typ, abstractVarRange)
if ty.kind in {tyRef, tyPtr}: ty = skipTypes(ty.lastSon, abstractVarRange)
case ty.kind
of tyArray, tyArrayConstr, tyOpenArray, tySequence, tyString, tyCString,
tyVarargs, tyChar:
genArrayAddr(p, n.sons[0], r)
of tyTuple:
genFieldAddr(p, n.sons[0], r)
else: internalError(n.sons[0].info, "expr(nkBracketExpr, " & $ty.kind & ')')
else: internalError(n.sons[0].info, "genAddr")
proc genProcForSymIfNeeded(p: PProc, s: PSym) =
if not p.g.generatedSyms.containsOrIncl(s.id):
let newp = genProc(p, s)
var owner = p
while owner != nil and owner.prc != s.owner:
owner = owner.up
if owner != nil: add(owner.locals, newp)
else: add(p.g.code, newp)
proc genSym(p: PProc, n: PNode, r: var TCompRes) =
var s = n.sym
case s.kind
of skVar, skLet, skParam, skTemp, skResult:
if s.loc.r == nil:
internalError(n.info, "symbol has no generated name: " & s.name.s)
let k = mapType(s.typ)
if k == etyBaseIndex:
r.typ = etyBaseIndex
if {sfAddrTaken, sfGlobal} * s.flags != {}:
r.address = "$1[0]" % [s.loc.r]
r.res = "$1[1]" % [s.loc.r]
else:
r.address = s.loc.r
r.res = s.loc.r & "_Idx"
elif isIndirect(s):
r.res = "$1[0]" % [s.loc.r]
else:
r.res = s.loc.r
of skConst:
genConstant(p, s)
if s.loc.r == nil:
internalError(n.info, "symbol has no generated name: " & s.name.s)
r.res = s.loc.r
of skProc, skConverter, skMethod:
discard mangleName(s)
r.res = s.loc.r
if lfNoDecl in s.loc.flags or s.magic != mNone or
{sfImportc, sfInfixCall} * s.flags != {}:
discard
elif s.kind == skMethod and s.getBody.kind == nkEmpty:
# we cannot produce code for the dispatcher yet:
discard
elif sfForward in s.flags:
p.g.forwarded.add(s)
else:
genProcForSymIfNeeded(p, s)
else:
if s.loc.r == nil:
internalError(n.info, "symbol has no generated name: " & s.name.s)
r.res = s.loc.r
r.kind = resVal
proc genDeref(p: PProc, n: PNode, r: var TCompRes) =
if mapType(n.sons[0].typ) == etyObject:
gen(p, n.sons[0], r)
else:
var a: TCompRes
gen(p, n.sons[0], a)
if a.typ != etyBaseIndex: internalError(n.info, "genDeref")
r.res = "$1[$2]" % [a.address, a.res]
proc genArg(p: PProc, n: PNode, r: var TCompRes) =
var a: TCompRes
gen(p, n, a)
if a.typ == etyBaseIndex:
add(r.res, a.address)
add(r.res, ", ")
add(r.res, a.res)
else:
add(r.res, a.res)
proc genArgs(p: PProc, n: PNode, r: var TCompRes) =
add(r.res, "(")
var hasArgs = false
for i in countup(1, sonsLen(n) - 1):
let it = n.sons[i]
if it.typ.isCompileTimeOnly: continue
if hasArgs: add(r.res, ", ")
genArg(p, it, r)
hasArgs = true
add(r.res, ")")
r.kind = resExpr
proc genCall(p: PProc, n: PNode, r: var TCompRes) =
gen(p, n.sons[0], r)
genArgs(p, n, r)
proc genInfixCall(p: PProc, n: PNode, r: var TCompRes) =
gen(p, n.sons[1], r)
if r.typ == etyBaseIndex:
if r.address == nil:
globalError(n.info, "cannot invoke with infix syntax")
r.res = "$1[$2]" % [r.address, r.res]
r.address = nil
r.typ = etyNone
add(r.res, ".")
var op: TCompRes
gen(p, n.sons[0], op)
add(r.res, op.res)
add(r.res, "(")
for i in countup(2, sonsLen(n) - 1):
if i > 2: add(r.res, ", ")
genArg(p, n.sons[i], r)
add(r.res, ")")
r.kind = resExpr
proc genEcho(p: PProc, n: PNode, r: var TCompRes) =
useMagic(p, "rawEcho")
add(r.res, "rawEcho(")
let n = n[1].skipConv
internalAssert n.kind == nkBracket
for i in countup(0, sonsLen(n) - 1):
let it = n.sons[i]
if it.typ.isCompileTimeOnly: continue
if i > 0: add(r.res, ", ")
genArg(p, it, r)
add(r.res, ")")
r.kind = resExpr
proc putToSeq(s: string, indirect: bool): Rope =
result = rope(s)
if indirect: result = "[$1]" % [result]
proc createVar(p: PProc, typ: PType, indirect: bool): Rope
proc createRecordVarAux(p: PProc, rec: PNode, c: var int): Rope =
result = nil
case rec.kind
of nkRecList:
for i in countup(0, sonsLen(rec) - 1):
add(result, createRecordVarAux(p, rec.sons[i], c))
of nkRecCase:
add(result, createRecordVarAux(p, rec.sons[0], c))
for i in countup(1, sonsLen(rec) - 1):
add(result, createRecordVarAux(p, lastSon(rec.sons[i]), c))
of nkSym:
if c > 0: add(result, ", ")
add(result, mangleName(rec.sym))
add(result, ": ")
add(result, createVar(p, rec.sym.typ, false))
inc(c)
else: internalError(rec.info, "createRecordVarAux")
proc createVar(p: PProc, typ: PType, indirect: bool): Rope =
var t = skipTypes(typ, abstractInst)
case t.kind
of tyInt..tyInt64, tyUInt..tyUInt64, tyEnum, tyChar:
result = putToSeq("0", indirect)
of tyFloat..tyFloat128:
result = putToSeq("0.0", indirect)
of tyRange, tyGenericInst:
result = createVar(p, lastSon(typ), indirect)
of tySet:
result = putToSeq("{}", indirect)
of tyBool:
result = putToSeq("false", indirect)
of tyArray, tyArrayConstr:
var length = int(lengthOrd(t))
var e = elemType(t)
if length > 32:
useMagic(p, "arrayConstr")
# XXX: arrayConstr depends on nimCopy. This line shouldn't be necessary.
useMagic(p, "nimCopy")
result = "arrayConstr($1, $2, $3)" % [rope(length),
createVar(p, e, false), genTypeInfo(p, e)]
else:
result = rope("[")
var i = 0
while i < length:
if i > 0: add(result, ", ")
add(result, createVar(p, e, false))
inc(i)
add(result, "]")
if indirect: result = "[$1]" % [result]
of tyTuple:
result = rope("{")
for i in 0.. <t.sonsLen:
if i > 0: add(result, ", ")
addf(result, "Field$1: $2" | "Field$# = $#", [i.rope,
createVar(p, t.sons[i], false)])
add(result, "}")
if indirect: result = "[$1]" % [result]
of tyObject:
result = rope("{")
var c = 0
if tfFinal notin t.flags or t.sons[0] != nil:
inc(c)
addf(result, "m_type: $1" | "m_type = $#", [genTypeInfo(p, t)])
while t != nil:
add(result, createRecordVarAux(p, t.n, c))
t = t.sons[0]
add(result, "}")
if indirect: result = "[$1]" % [result]
of tyVar, tyPtr, tyRef:
if mapType(t) == etyBaseIndex:
result = putToSeq("[null, 0]" | "{nil, 0}", indirect)
else:
result = putToSeq("null" | "nil", indirect)
of tySequence, tyString, tyCString, tyPointer, tyProc:
result = putToSeq("null" | "nil", indirect)
else:
internalError("createVar: " & $t.kind)
result = nil
proc genVarInit(p: PProc, v: PSym, n: PNode) =
var
a: TCompRes
s: Rope
if n.kind == nkEmpty:
addf(p.body, "var $1 = $2;$n" | "local $1 = $2;$n",
[mangleName(v), createVar(p, v.typ, isIndirect(v))])
else:
discard mangleName(v)
gen(p, n, a)
case mapType(v.typ)
of etyObject:
if needsNoCopy(n):
s = a.res
else:
useMagic(p, "nimCopy")
s = "nimCopy($1, $2)" % [a.res, genTypeInfo(p, n.typ)]
of etyBaseIndex:
if (a.typ != etyBaseIndex): internalError(n.info, "genVarInit")
if {sfAddrTaken, sfGlobal} * v.flags != {}:
addf(p.body, "var $1 = [$2, $3];$n" | "local $1 = {$2, $3};$n",
[v.loc.r, a.address, a.res])
else:
addf(p.body, "var $1 = $2; var $1_Idx = $3;$n" |
"local $1 = $2; local $1_Idx = $3;$n", [
v.loc.r, a.address, a.res])
return
else:
s = a.res
if isIndirect(v):
addf(p.body, "var $1 = /**/[$2];$n" | "local $1 = {$2};$n", [v.loc.r, s])
else:
addf(p.body, "var $1 = $2;$n" | "local $1 = $2;$n", [v.loc.r, s])
proc genVarStmt(p: PProc, n: PNode) =
for i in countup(0, sonsLen(n) - 1):
var a = n.sons[i]
if a.kind != nkCommentStmt:
if a.kind == nkVarTuple:
let unpacked = lowerTupleUnpacking(a, p.prc)
genStmt(p, unpacked)
else:
assert(a.kind == nkIdentDefs)
assert(a.sons[0].kind == nkSym)
var v = a.sons[0].sym
if lfNoDecl notin v.loc.flags:
genLineDir(p, a)
genVarInit(p, v, a.sons[2])
proc genConstant(p: PProc, c: PSym) =
if lfNoDecl notin c.loc.flags and not p.g.generatedSyms.containsOrIncl(c.id):
let oldBody = p.body
p.body = nil
#genLineDir(p, c.ast)
genVarInit(p, c, c.ast)
add(p.g.code, p.body)
p.body = oldBody
proc genNew(p: PProc, n: PNode) =
var a: TCompRes
gen(p, n.sons[1], a)
var t = skipTypes(n.sons[1].typ, abstractVar).sons[0]
addf(p.body, "$1 = $2;$n", [a.res, createVar(p, t, false)])
proc genNewSeq(p: PProc, n: PNode) =
var x, y: TCompRes
gen(p, n.sons[1], x)
gen(p, n.sons[2], y)
let t = skipTypes(n.sons[1].typ, abstractVar).sons[0]
addf(p.body, "$1 = new Array($2); for (var i=0;i<$2;++i) {$1[i]=$3;}", [
x.rdLoc, y.rdLoc, createVar(p, t, false)])
proc genOrd(p: PProc, n: PNode, r: var TCompRes) =
case skipTypes(n.sons[1].typ, abstractVar).kind
of tyEnum, tyInt..tyInt64, tyChar: gen(p, n.sons[1], r)
of tyBool: unaryExpr(p, n, r, "", "($1 ? 1:0)" | "toBool($#)")
else: internalError(n.info, "genOrd")
proc genConStrStr(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("[$1].concat(" % [a.res])
else:
r.res.add("($1.slice(0,-1)).concat(" % [a.res])
for i in countup(2, sonsLen(n) - 2):
gen(p, n.sons[i], a)
if skipTypes(n.sons[i].typ, abstractVarRange).kind == tyChar:
r.res.add("[$1]," % [a.res])
else:
r.res.add("$1.slice(0,-1)," % [a.res])
gen(p, n.sons[sonsLen(n) - 1], a)
if skipTypes(n.sons[sonsLen(n) - 1].typ, abstractVarRange).kind == tyChar:
r.res.add("[$1, 0])" % [a.res])
else:
r.res.add("$1)" % [a.res])
proc genRepr(p: PProc, n: PNode, r: var TCompRes) =
var t = skipTypes(n.sons[1].typ, abstractVarRange)
case t.kind
of tyInt..tyUInt64:
unaryExpr(p, n, r, "", "(\"\"+ ($1))")
of tyEnum, tyOrdinal:
gen(p, n.sons[1], r)
useMagic(p, "cstrToNimstr")
r.kind = resExpr
r.res = "cstrToNimstr($1.node.sons[$2].name)" % [genTypeInfo(p, t), r.res]
else:
# XXX:
internalError(n.info, "genRepr: Not implemented")
proc genOf(p: PProc, n: PNode, r: var TCompRes) =
var x: TCompRes
let t = skipTypes(n.sons[2].typ, abstractVarRange+{tyRef, tyPtr, tyTypeDesc})
gen(p, n.sons[1], x)
if tfFinal in t.flags:
r.res = "($1.m_type == $2)" % [x.res, genTypeInfo(p, t)]
else:
useMagic(p, "isObj")
r.res = "isObj($1.m_type, $2)" % [x.res, genTypeInfo(p, t)]
r.kind = resExpr
proc genReset(p: PProc, n: PNode) =
var x: TCompRes
useMagic(p, "genericReset")
gen(p, n.sons[1], x)
addf(p.body, "$1 = genericReset($1, $2);$n", [x.res,
genTypeInfo(p, n.sons[1].typ)])
proc genMagic(p: PProc, n: PNode, r: var TCompRes) =
var
a: TCompRes
line, filen: Rope
var op = n.sons[0].sym.magic
case op
of mOr: genOr(p, n.sons[1], n.sons[2], r)
of mAnd: genAnd(p, n.sons[1], n.sons[2], r)
of mAddI..mStrToStr: arith(p, n, r, op)
of mRepr: genRepr(p, n, r)
of mSwap: genSwap(p, n)
of mUnaryLt:
# XXX: range checking?
if not (optOverflowCheck in p.options): unaryExpr(p, n, r, "", "$1 - 1")
else: unaryExpr(p, n, r, "subInt", "subInt($1, 1)")
of mAppendStrCh: binaryExpr(p, n, r, "addChar",
"if ($1 != null) { addChar($1, $2); } else { $1 = [$2, 0]; }")
of mAppendStrStr:
if skipTypes(n.sons[1].typ, abstractVarRange).kind == tyCString:
binaryExpr(p, n, r, "", "if ($1 != null) { $1 += $2; } else { $1 = $2; }")
else:
binaryExpr(p, n, r, "",
"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
of mAppendSeqElem: binaryExpr(p, n, r, "",
"if ($1 != null) { $1.push($2); } else { $1 = [$2]; }")
of mConStrStr: genConStrStr(p, n, r)
of mEqStr: binaryExpr(p, n, r, "eqStrings", "eqStrings($1, $2)")
of mLeStr: binaryExpr(p, n, r, "cmpStrings", "(cmpStrings($1, $2) <= 0)")
of mLtStr: binaryExpr(p, n, r, "cmpStrings", "(cmpStrings($1, $2) < 0)")
of mIsNil: unaryExpr(p, n, r, "", "$1 == null")
of mEnumToStr: genRepr(p, n, r)
of mNew, mNewFinalize: genNew(p, n)
of mSizeOf: r.res = rope(getSize(n.sons[1].typ))
of mChr, mArrToSeq: gen(p, n.sons[1], r) # nothing to do
of mOrd: genOrd(p, n, r)
of mLengthStr: unaryExpr(p, n, r, "", "($1 != null ? $1.length-1 : 0)")
of mXLenStr: unaryExpr(p, n, r, "", "$1.length-1")
of mLengthSeq, mLengthOpenArray, mLengthArray:
unaryExpr(p, n, r, "", "($1 != null ? $1.length : 0)")
of mXLenSeq:
unaryExpr(p, n, r, "", "$1.length")
of mHigh:
if skipTypes(n.sons[1].typ, abstractVar).kind == tyString:
unaryExpr(p, n, r, "", "($1 != null ? ($1.length-2) : -1)")
else:
unaryExpr(p, n, r, "", "($1 != null ? ($1.length-1) : -1)")
of mInc:
if optOverflowCheck notin p.options: binaryExpr(p, n, r, "", "$1 += $2")
else: binaryExpr(p, n, r, "addInt", "$1 = addInt($1, $2)")
of ast.mDec:
if optOverflowCheck notin p.options: binaryExpr(p, n, r, "", "$1 -= $2")
else: binaryExpr(p, n, r, "subInt", "$1 = subInt($1, $2)")
of mSetLengthStr: binaryExpr(p, n, r, "", "$1.length = $2+1; $1[$1.length-1] = 0")
of mSetLengthSeq: binaryExpr(p, n, r, "", "$1.length = $2")
of mCard: unaryExpr(p, n, r, "SetCard", "SetCard($1)")
of mLtSet: binaryExpr(p, n, r, "SetLt", "SetLt($1, $2)")
of mLeSet: binaryExpr(p, n, r, "SetLe", "SetLe($1, $2)")
of mEqSet: binaryExpr(p, n, r, "SetEq", "SetEq($1, $2)")
of mMulSet: binaryExpr(p, n, r, "SetMul", "SetMul($1, $2)")
of mPlusSet: binaryExpr(p, n, r, "SetPlus", "SetPlus($1, $2)")
of mMinusSet: binaryExpr(p, n, r, "SetMinus", "SetMinus($1, $2)")
of mIncl: binaryExpr(p, n, r, "", "$1[$2] = true")
of mExcl: binaryExpr(p, n, r, "", "delete $1[$2]")
of mInSet: binaryExpr(p, n, r, "", "($1[$2] != undefined)")
of mNewSeq: genNewSeq(p, n)
of mOf: genOf(p, n, r)
of mReset: genReset(p, n)
of mEcho: genEcho(p, n, r)
of mNLen..mNError, mSlurp, mStaticExec:
localError(n.info, errXMustBeCompileTime, n.sons[0].sym.name.s)
of mCopyStr: binaryExpr(p, n, r, "", "($1.slice($2))")
of mCopyStrLast: ternaryExpr(p, n, r, "", "($1.slice($2, ($3)+1).concat(0))")
of mNewString: unaryExpr(p, n, r, "mnewString", "mnewString($1)")
of mNewStringOfCap: unaryExpr(p, n, r, "mnewString", "mnewString(0)")
of mDotDot:
genProcForSymIfNeeded(p, n.sons[0].sym)
genCall(p, n, r)
else:
genCall(p, n, r)
#else internalError(e.info, 'genMagic: ' + magicToStr[op]);
proc genSetConstr(p: PProc, n: PNode, r: var TCompRes) =
var
a, b: TCompRes
useMagic(p, "SetConstr")
r.res = rope("SetConstr(")
r.kind = resExpr
for i in countup(0, sonsLen(n) - 1):
if i > 0: add(r.res, ", ")
var it = n.sons[i]
if it.kind == nkRange:
gen(p, it.sons[0], a)
gen(p, it.sons[1], b)
addf(r.res, "[$1, $2]", [a.res, b.res])
else:
gen(p, it, a)
add(r.res, a.res)
add(r.res, ")")
proc genArrayConstr(p: PProc, n: PNode, r: var TCompRes) =
var a: TCompRes
r.res = rope("[")
r.kind = resExpr
for i in countup(0, sonsLen(n) - 1):
if i > 0: add(r.res, ", ")
gen(p, n.sons[i], a)
add(r.res, a.res)
add(r.res, "]")
proc genTupleConstr(p: PProc, n: PNode, r: var TCompRes) =
var a: TCompRes
r.res = rope("{")
r.kind = resExpr
for i in countup(0, sonsLen(n) - 1):
if i > 0: add(r.res, ", ")
var it = n.sons[i]
if it.kind == nkExprColonExpr: it = it.sons[1]
gen(p, it, a)
addf(r.res, "Field$#: $#" | "Field$# = $#", [i.rope, a.res])
r.res.add("}")
proc genObjConstr(p: PProc, n: PNode, r: var TCompRes) =
# XXX inheritance?
var a: TCompRes
r.res = rope("{")
r.kind = resExpr
for i in countup(1, sonsLen(n) - 1):
if i > 1: add(r.res, ", ")
var it = n.sons[i]
internalAssert it.kind == nkExprColonExpr
gen(p, it.sons[1], a)
var f = it.sons[0].sym
if f.loc.r == nil: f.loc.r = mangleName(f)
addf(r.res, "$#: $#" | "$# = $#" , [f.loc.r, a.res])
r.res.add("}")
proc genConv(p: PProc, n: PNode, r: var TCompRes) =
var dest = skipTypes(n.typ, abstractVarRange)
var src = skipTypes(n.sons[1].typ, abstractVarRange)
gen(p, n.sons[1], r)
if dest.kind == src.kind:
# no-op conversion
return
case dest.kind:
of tyBool:
r.res = ("(($1)? 1:0)" | "toBool($#)") % [r.res]
r.kind = resExpr
of tyInt:
r.res = "($1|0)" % [r.res]
else:
# TODO: What types must we handle here?
discard
proc upConv(p: PProc, n: PNode, r: var TCompRes) =
gen(p, n.sons[0], r) # XXX
proc genRangeChck(p: PProc, n: PNode, r: var TCompRes, magic: string) =
var a, b: TCompRes
gen(p, n.sons[0], r)
if optRangeCheck in p.options:
gen(p, n.sons[1], a)
gen(p, n.sons[2], b)
useMagic(p, "chckRange")
r.res = "chckRange($1, $2, $3)" % [r.res, a.res, b.res]
r.kind = resExpr
proc convStrToCStr(p: PProc, n: PNode, r: var TCompRes) =
# we do an optimization here as this is likely to slow down
# much of the code otherwise:
if n.sons[0].kind == nkCStringToString:
gen(p, n.sons[0].sons[0], r)
else:
gen(p, n.sons[0], r)
if r.res == nil: internalError(n.info, "convStrToCStr")
useMagic(p, "toJSStr")
r.res = "toJSStr($1)" % [r.res]
r.kind = resExpr
proc convCStrToStr(p: PProc, n: PNode, r: var TCompRes) =
# we do an optimization here as this is likely to slow down
# much of the code otherwise:
if n.sons[0].kind == nkStringToCString:
gen(p, n.sons[0].sons[0], r)
else:
gen(p, n.sons[0], r)
if r.res == nil: internalError(n.info, "convCStrToStr")
useMagic(p, "cstrToNimstr")
r.res = "cstrToNimstr($1)" % [r.res]
r.kind = resExpr
proc genReturnStmt(p: PProc, n: PNode) =
if p.procDef == nil: internalError(n.info, "genReturnStmt")
p.beforeRetNeeded = true
if (n.sons[0].kind != nkEmpty):
genStmt(p, n.sons[0])
else:
genLineDir(p, n)
addf(p.body, "break BeforeRet;$n" | "goto ::BeforeRet::;$n", [])
proc genProcBody(p: PProc, prc: PSym): Rope =
if optStackTrace in prc.options:
result = (("var F={procname:$1,prev:framePtr,filename:$2,line:0};$n" |
"local F={procname=$#,prev=framePtr,filename=$#,line=0};$n") &
"framePtr = F;$n") % [
makeJSString(prc.owner.name.s & '.' & prc.name.s),
makeJSString(toFilename(prc.info))]
else:
result = nil
if p.beforeRetNeeded:
addf(result, "BeforeRet: do {$n$1} while (false); $n" |
"$#;::BeforeRet::$n", [p.body])
else:
add(result, p.body)
if prc.typ.callConv == ccSysCall and p.target == targetJS:
result = ("try {$n$1} catch (e) {$n" &
" alert(\"Unhandled exception:\\n\" + e.message + \"\\n\"$n}") % [result]
if optStackTrace in prc.options:
add(result, "framePtr = framePtr.prev;" & tnl)
proc genProc(oldProc: PProc, prc: PSym): Rope =
var
resultSym: PSym
name, returnStmt, resultAsgn, header: Rope
a: TCompRes
#if gVerbosity >= 3:
# echo "BEGIN generating code for: " & prc.name.s
var p = newProc(oldProc.g, oldProc.module, prc.ast, prc.options)
p.target = oldProc.target
p.up = oldProc
returnStmt = nil
resultAsgn = nil
name = mangleName(prc)
header = generateHeader(p, prc.typ)
if prc.typ.sons[0] != nil and sfPure notin prc.flags:
resultSym = prc.ast.sons[resultPos].sym
resultAsgn = ("var $# = $#;$n" | "local $# = $#;$n") % [
mangleName(resultSym),
createVar(p, resultSym.typ, isIndirect(resultSym))]
gen(p, prc.ast.sons[resultPos], a)
returnStmt = "return $#;$n" % [a.res]
genStmt(p, prc.getBody)
result = ("function $#($#) {$n$#$#$#$#}$n" |
"function $#($#) $n$#$#$#$#$nend$n") %
[name, header, p.locals, resultAsgn,
genProcBody(p, prc), returnStmt]
#if gVerbosity >= 3:
# echo "END generated code for: " & prc.name.s
proc genStmt(p: PProc, n: PNode) =
var r: TCompRes
gen(p, n, r)
if r.res != nil: addf(p.body, "$#;$n", [r.res])
proc gen(p: PProc, n: PNode, r: var TCompRes) =
r.typ = etyNone
r.kind = resNone
#r.address = nil
r.res = nil
case n.kind
of nkSym:
genSym(p, n, r)
of nkCharLit..nkInt64Lit:
r.res = rope(n.intVal)
r.kind = resExpr
of nkNilLit:
if isEmptyType(n.typ):
discard
elif mapType(n.typ) == etyBaseIndex:
r.typ = etyBaseIndex
r.address = rope"null" | rope"nil"
r.res = rope"0"
r.kind = resExpr
else:
r.res = rope"null" | rope"nil"
r.kind = resExpr
of nkStrLit..nkTripleStrLit:
if skipTypes(n.typ, abstractVarRange).kind == tyString:
useMagic(p, "cstrToNimstr")
r.res = "cstrToNimstr($1)" % [makeJSString(n.strVal)]
else:
r.res = makeJSString(n.strVal)
r.kind = resExpr
of nkFloatLit..nkFloat64Lit:
let f = n.floatVal
if f != f: r.res = rope"NaN"
elif f == 0.0: r.res = rope"0.0"
elif f == 0.5 * f:
if f > 0.0: r.res = rope"Infinity"
else: r.res = rope"-Infinity"
else: r.res = rope(f.toStrMaxPrecision)
r.kind = resExpr
of nkCallKinds:
if (n.sons[0].kind == nkSym) and (n.sons[0].sym.magic != mNone):
genMagic(p, n, r)
elif n.sons[0].kind == nkSym and sfInfixCall in n.sons[0].sym.flags and
n.len >= 2:
genInfixCall(p, n, r)
else:
genCall(p, n, r)
of nkCurly: genSetConstr(p, n, r)
of nkBracket: genArrayConstr(p, n, r)
of nkPar: genTupleConstr(p, n, r)
of nkObjConstr: genObjConstr(p, n, r)
of nkHiddenStdConv, nkHiddenSubConv, nkConv: genConv(p, n, r)
of nkAddr, nkHiddenAddr: genAddr(p, n, r)
of nkDerefExpr, nkHiddenDeref: genDeref(p, n, r)
of nkBracketExpr: genArrayAccess(p, n, r)
of nkDotExpr: genFieldAccess(p, n, r)
of nkCheckedFieldExpr: genCheckedFieldAccess(p, n, r)
of nkObjDownConv: gen(p, n.sons[0], r)
of nkObjUpConv: upConv(p, n, r)
of nkCast: gen(p, n.sons[1], r)
of nkChckRangeF: genRangeChck(p, n, r, "chckRangeF")
of nkChckRange64: genRangeChck(p, n, r, "chckRange64")
of nkChckRange: genRangeChck(p, n, r, "chckRange")
of nkStringToCString: convStrToCStr(p, n, r)
of nkCStringToString: convCStrToStr(p, n, r)
of nkEmpty: discard
of nkLambdaKinds:
let s = n.sons[namePos].sym
discard mangleName(s)
r.res = s.loc.r
if lfNoDecl in s.loc.flags or s.magic != mNone: discard
elif not p.g.generatedSyms.containsOrIncl(s.id):
add(p.locals, genProc(p, s))
of nkType: r.res = genTypeInfo(p, n.typ)
of nkStmtList, nkStmtListExpr:
# this shows the distinction is nice for backends and should be kept
# in the frontend
let isExpr = not isEmptyType(n.typ)
for i in countup(0, sonsLen(n) - 1 - isExpr.ord):
genStmt(p, n.sons[i])
if isExpr:
gen(p, lastSon(n), r)
of nkBlockStmt, nkBlockExpr: genBlock(p, n, r)
of nkIfStmt, nkIfExpr: genIf(p, n, r)
of nkWhileStmt: genWhileStmt(p, n)
of nkVarSection, nkLetSection: genVarStmt(p, n)
of nkConstSection: discard
of nkForStmt, nkParForStmt:
internalError(n.info, "for statement not eliminated")
of nkCaseStmt:
if p.target == targetJS: genCaseJS(p, n, r)
else: genCaseLua(p, n, r)
of nkReturnStmt: genReturnStmt(p, n)
of nkBreakStmt: genBreakStmt(p, n)
of nkAsgn: genAsgn(p, n)
of nkFastAsgn: genFastAsgn(p, n)
of nkDiscardStmt:
if n.sons[0].kind != nkEmpty:
genLineDir(p, n)
gen(p, n.sons[0], r)
of nkAsmStmt: genAsmStmt(p, n)
of nkTryStmt: genTry(p, n, r)
of nkRaiseStmt: genRaiseStmt(p, n)
of nkTypeSection, nkCommentStmt, nkIteratorDef, nkIncludeStmt,
nkImportStmt, nkImportExceptStmt, nkExportStmt, nkExportExceptStmt,
nkFromStmt, nkTemplateDef, nkMacroDef, nkPragma: discard
of nkProcDef, nkMethodDef, nkConverterDef:
var s = n.sons[namePos].sym
if {sfExportc, sfCompilerProc} * s.flags == {sfExportc}:
genSym(p, n.sons[namePos], r)
r.res = nil
of nkGotoState, nkState:
internalError(n.info, "first class iterators not implemented")
of nkPragmaBlock: gen(p, n.lastSon, r)
else: internalError(n.info, "gen: unknown node type: " & $n.kind)
var globals: PGlobals
proc newModule(module: PSym): BModule =
new(result)
result.module = module
if globals == nil: globals = newGlobals()
proc genHeader(): Rope =
result = ("/* Generated by the Nim Compiler v$1 */$n" &
"/* (c) 2015 Andreas Rumpf */$n$n" &
"var framePtr = null;$n" &
"var excHandler = null;$n" &
"var lastJSError = null;$n") %
[rope(VersionAsString)]
proc genModule(p: PProc, n: PNode) =
if optStackTrace in p.options:
addf(p.body, "var F = {procname:$1,prev:framePtr,filename:$2,line:0};$n" &
"framePtr = F;$n", [
makeJSString("module " & p.module.module.name.s),
makeJSString(toFilename(p.module.module.info))])
genStmt(p, n)
if optStackTrace in p.options:
addf(p.body, "framePtr = framePtr.prev;$n", [])
proc myProcess(b: PPassContext, n: PNode): PNode =
if passes.skipCodegen(n): return n
result = n
var m = BModule(b)
if m.module == nil: internalError(n.info, "myProcess")
var p = newProc(globals, m, nil, m.module.options)
genModule(p, n)
add(p.g.code, p.locals)
add(p.g.code, p.body)
proc wholeCode*(m: BModule): Rope =
for prc in globals.forwarded:
if not globals.generatedSyms.containsOrIncl(prc.id):
var p = newProc(globals, m, nil, m.module.options)
add(p.g.code, genProc(p, prc))
var disp = generateMethodDispatchers()
for i in 0..sonsLen(disp)-1:
let prc = disp.sons[i].sym
if not globals.generatedSyms.containsOrIncl(prc.id):
var p = newProc(globals, m, nil, m.module.options)
add(p.g.code, genProc(p, prc))
result = globals.typeInfo & globals.code
proc myClose(b: PPassContext, n: PNode): PNode =
if passes.skipCodegen(n): return n
result = myProcess(b, n)
var m = BModule(b)
if sfMainModule in m.module.flags:
let code = wholeCode(m)
let outfile =
if options.outFile.len > 0:
if options.outFile.isAbsolute: options.outFile
else: getCurrentDir() / options.outFile
else:
changeFileExt(completeCFilePath(m.module.filename), "js")
discard writeRopeIfNotEqual(genHeader() & code, outfile)
proc myOpenCached(s: PSym, rd: PRodReader): PPassContext =
internalError("symbol files are not possible with the JS code generator")
result = nil
proc myOpen(s: PSym): PPassContext =
result = newModule(s)
const JSgenPass* = makePass(myOpen, myOpenCached, myProcess, myClose)