Files
Nim/compiler/jsgen.nim
2013-05-01 14:48:40 +02:00

1520 lines
52 KiB
Nim

#
#
# The Nimrod Compiler
# (c) Copyright 2013 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
# This is the EMCAScript (also known as JavaScript) code generator.
# Soon also a Luajit code generator. ;-)
import
ast, astalgo, strutils, hashes, trees, platform, magicsys, extccomp,
options, nversion, nimsets, msgs, crc, bitsets, idents, lists, types, os,
times, ropes, math, passes, ccgutils, wordrecg, renderer, rodread, rodutils,
intsets, cgmeth
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: PRope # result part; index if this is an
# (address, index)-tuple
address: PRope # 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: PRope
forwarded: seq[PSym]
generatedSyms: TIntSet
typeInfoGenerated: TIntSet
PGlobals = ref TGlobals
PProc = ref TProc
TProc = object
procDef: PNode
prc: PSym
locals, body: PRope
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.} =
(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): PRope {.inline.} =
result = a.res
when false:
if a.typ != etyBaseIndex:
result = a.res
else:
result = ropef("$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, tyVar, tyRef, tyPtr, tyBigNum, tyVarargs}
proc mapType(typ: PType): TJSTypeKind =
let t = skipTypes(typ, abstractInst)
case t.kind
of tyVar, tyRef, tyPtr:
if skipTypes(t.sons[0], 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, tyGenericInvokation, tyNone,
tyForward, tyEmpty, tyExpr, tyStmt, tyTypeDesc, tyTypeClass:
result = etyNone
of tyProc: result = etyProc
of tyCString: result = etyString
proc mangle(name: string): string =
result = ""
for i in countup(0, len(name) - 1):
case name[i]
of 'A'..'Z':
add(result, chr(ord(name[i]) - ord('A') + ord('a')))
of '_':
nil
of 'a'..'z', '0'..'9':
add(result, name[i])
else: add(result, 'X' & toHex(ord(name[i]), 2))
proc mangleName(s: PSym): PRope =
result = s.loc.r
if result == nil:
result = toRope(mangle(s.name.s))
app(result, "_")
app(result, toRope(s.id))
s.loc.r = result
proc makeJSString(s: string): PRope = strutils.escape(s).toRope
include jstypes
proc gen(p: PProc, n: PNode, r: var TCompRes)
proc genStmt(p: PProc, n: PNode)
proc genProc(oldProc: PProc, prc: PSym): PRope
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):
app(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): PRope =
inc(p.unique)
result = ropef("Tmp$1", [toRope(p.unique)])
appf(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 = ropef("($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.appf("if (!$1) $2 = false; else {" |
"if not $1 then $2 = false; else", x.rdLoc, r.rdLoc)
gen(p, b, y)
p.body.appf("$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 = ropef("($1 || $2)" | "($1 or $2)", [x.rdLoc, y.rdLoc])
else:
r.res = p.getTemp
r.kind = resVal
gen(p, a, x)
p.body.appf("if ($1) $2 = true; else {" |
"if $1 then $2 = true; else", x.rdLoc, r.rdLoc)
gen(p, b, y)
p.body.appf("$2 = $1; }" |
"$2 = $1 end", y.rdLoc, r.rdLoc)
type
TMagicFrmt = array[0..3, string]
const # magic checked op; magic unchecked op; checked op; unchecked op
ops: array[mAddi..mStrToStr, TMagicFrmt] = [
["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
["", "", "($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)"], # MinI64
["nimMax", "nimMax", "nimMax($1, $2)", "nimMax($1, $2)"], # MaxI64
["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)"], # EqProc
["", "", "($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
["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)"], # UnaryPlusI64
["", "", "~($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 = ropef(frmt, [x.rdLoc, y.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 = ropef(frmt, [r.rdLoc])
r.kind = resExpr
proc arith(p: PProc, n: PNode, r: var TCompRes, op: TMagic) =
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 = ropef(ops[op][i + 2], [x.rdLoc, y.rdLoc])
else:
gen(p, n.sons[1], r)
r.res = ropef(ops[op][i + 2], [r.rdLoc])
r.kind = resExpr
proc genLineDir(p: PProc, n: PNode) =
let line = toLinenumber(n.info)
if optLineDir in p.Options:
appf(p.body, "// line $2 \"$1\"$n",
[toRope(toFilename(n.info)), toRope(line)])
if {optStackTrace, optEndb} * p.Options == {optStackTrace, optEndb} and
((p.prc == nil) or sfPure notin p.prc.flags):
useMagic(p, "endb")
appf(p.body, "endb($1);$n", [toRope(line)])
elif ({optLineTrace, optStackTrace} * p.Options ==
{optLineTrace, optStackTrace}) and
((p.prc == nil) or not (sfPure in p.prc.flags)):
appf(p.body, "F.line = $1;$n", [toRope(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.toRope
appf(p.body, "L$1: while (true) {$n" | "while true do$n", labl)
gen(p, n.sons[0], cond)
appf(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])
appf(p.body, "}$n" | "end$n", [])
setlen(p.blocks, length)
proc moveInto(p: PProc, src: var TCompRes, dest: TCompRes) =
if src.kind != resNone:
if dest.kind != resNone:
p.body.appf("$1 = $2;$n", dest.rdLoc, src.rdLoc)
else:
p.body.appf("$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)
inc(p.unique)
var safePoint = ropef("Tmp$1", [toRope(p.unique)])
appf(p.body,
"var $1 = {prev: excHandler, exc: null};$n" & "excHandler = $1;$n",
[safePoint])
if optStackTrace in p.Options: app(p.body, "framePtr = F;" & tnl)
app(p.body, "try {" & tnl)
var length = sonsLen(n)
var a: TCompRes
gen(p, n.sons[0], a)
if not isEmptyType(n.typ):
r.kind = resVal
r.res = getTemp(p)
moveInto(p, a, r)
var i = 1
if length > 1 and n.sons[i].kind == nkExceptBranch:
appf(p.body, "} catch (EXC) {$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: app(p.body, "else {" & tnl)
gen(p, n.sons[i].sons[0], a)
moveInto(p, a, r)
if i > 1: app(p.body, '}' & tnl)
else:
var orExpr: PRope = 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: app(orExpr, "||")
appf(orExpr, "isObj($1.exc.m_type, $2)",
[safePoint, genTypeInfo(p, n.sons[i].sons[j].typ)])
if i > 1: app(p.body, "else ")
appf(p.body, "if ($1.exc && ($2)) {$n", [safePoint, orExpr])
gen(p, n.sons[i].sons[blen - 1], a)
moveInto(p, a, r)
appf(p.body, "}$n")
inc(i)
app(p.body, "} finally {" & tnl & "excHandler = excHandler.prev;" & tnl)
if i < length and n.sons[i].kind == nkFinally:
gen(p, n.sons[i].sons[0], a)
moveInto(p, a, r)
app(p.body, '}' & tnl)
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")
appf(p.body, "raiseException($1, $2);$n",
[a.rdLoc, makeJSString(typ.sym.name.s)])
else:
useMagic(p, "reraiseException")
app(p.body, "reraiseException();" & tnl)
proc genCase(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")
appf(p.body, "switch (toJSStr($1)) {$n", [cond.rdLoc])
else:
appf(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)
appf(p.body, "case $1: ", [cond.rdLoc])
Inc(v.intVal)
else:
if stringSwitch:
case e.kind
of nkStrLit..nkTripleStrLit: appf(p.body, "case $1: ",
[makeJSString(e.strVal)])
else: InternalError(e.info, "jsgen.genCaseStmt: 2")
else:
gen(p, e, cond)
appf(p.body, "case $1: ", [cond.rdLoc])
gen(p, lastSon(it), stmt)
moveInto(p, stmt, r)
appf(p.body, "$nbreak;$n")
of nkElse:
appf(p.body, "default: $n")
gen(p, it.sons[0], stmt)
moveInto(p, stmt, r)
appf(p.body, "break;$n")
else: internalError(it.info, "jsgen.genCaseStmt")
appf(p.body, "}$n")
proc genBlock(p: PProc, n: PNode, r: var TCompRes) =
var
idx, labl: int
sym: PSym
inc(p.unique)
idx = len(p.blocks)
if n.sons[0].kind != nkEmpty:
# named block?
if (n.sons[0].kind != nkSym): InternalError(n.info, "genBlock")
sym = n.sons[0].sym
sym.loc.k = locOther
sym.loc.a = idx
setlen(p.blocks, idx + 1)
p.blocks[idx].id = - p.unique # negative because it isn't used yet
labl = p.unique
appf(p.body, "L$1: do {$n", toRope(labl))
gen(p, n.sons[1], r)
appf(p.body, "} while(false);$n")
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.loc.a
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
appf(p.body, "break L$1;$n", [toRope(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: app(p.body, n.sons[i].strVal)
of nkSym: app(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:
appf(p.body, "else {$n", [])
inc(toClose)
gen(p, it.sons[0], cond)
appf(p.body, "if ($1) {$n", cond.rdLoc)
gen(p, it.sons[1], stmt)
else:
# else part:
appf(p.body, "else {$n")
gen(p, it.sons[0], stmt)
moveInto(p, stmt, r)
appf(p.body, "}$n")
app(p.body, repeatChar(toClose, '}') & tnl)
proc generateHeader(p: PProc, typ: PType): PRope =
result = nil
for i in countup(1, sonsLen(typ.n) - 1):
if result != nil: app(result, ", ")
assert(typ.n.sons[i].kind == nkSym)
var param = typ.n.sons[i].sym
if isCompileTimeOnly(param.typ): continue
var name = mangleName(param)
app(result, name)
if mapType(param.typ) == etyBaseIndex:
app(result, ", ")
app(result, name)
app(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:
appf(p.body, "$1 = $2;$n", [a.rdLoc, b.rdLoc])
else:
useMagic(p, "NimCopy")
appf(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")
appf(p.body, "$1 = $2; $3 = $4;$n", [a.address, b.address, a.res, b.res])
else:
appf(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)
let tmp = ropef("Tmp$1", [toRope(p.unique)])
case mapType(skipTypes(n.sons[1].typ, abstractVar))
of etyBaseIndex:
inc(p.unique)
let tmp2 = ropef("Tmp$1", [toRope(p.unique)])
if a.typ != etyBaseIndex or b.typ != etyBaseIndex:
internalError(n.info, "genSwap")
appf(p.body, "var $1 = $2; $2 = $3; $3 = $1;$n", [tmp, a.address, b.address])
appf(p.body, "var $1 = $2; $2 = $3; $3 = $1", [tmp2, a.res, b.res])
else:
appf(p.body, "var $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(ropeToStr(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 = ropef("$1.Field$2", [r.res, getFieldPosition(n.sons[1]).toRope])
else:
if n.sons[1].kind != nkSym: InternalError(n.sons[1].info, "genFieldAddr")
var f = n.sons[1].sym
if f.loc.r == nil: f.loc.r = mangleName(f)
r.res = ropef("$1.$2", [r.res, f.loc.r])
r.kind = resExpr
proc genCheckedFieldAddr(p: PProc, n: PNode, r: var TCompRes) =
genFieldAddr(p, n.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
gen(p, n.sons[0], a)
gen(p, n.sons[1], b)
InternalAssert a.typ != etyBaseIndex and b.typ != etyBaseIndex
r.address = a.res
var typ = skipTypes(n.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(n.sons[1]):
useMagic(p, "chckIndx")
r.res = ropef("chckIndx($1, $2, $3.length)-$2",
[b.res, toRope(first), a.res])
elif first != 0:
r.res = ropef("($1)-$2", [b.res, toRope(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.sons[0], 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 = ropef("$1[$2]", [r.address, r.res])
r.address = nil
r.kind = resExpr
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
if mapType(n.typ) == etyObject:
# make addr() a no-op:
r.typ = etyNone
r.res = s.loc.r
r.address = nil
elif sfGlobal in s.flags:
# globals are always indirect accessible
r.typ = etyBaseIndex
r.address = toRope("Globals")
r.res = makeJSString(ropeToStr(s.loc.r))
elif sfAddrTaken in s.flags:
r.typ = etyBaseIndex
r.address = s.loc.r
r.res = toRope("0")
else:
InternalError(n.info, "genAddr: 4")
else: InternalError(n.info, "genAddr: 2")
of nkCheckedFieldExpr:
genCheckedFieldAddr(p, n, r)
of nkDotExpr:
genFieldAddr(p, n, r)
of nkBracketExpr:
var ty = skipTypes(n.sons[0].typ, abstractVarRange)
if ty.kind in {tyRef, tyPtr}: ty = skipTypes(ty.sons[0], 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 & ')')
else: InternalError(n.info, "genAddr")
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)
var k = mapType(s.typ)
if k == etyBaseIndex:
r.typ = etyBaseIndex
if {sfAddrTaken, sfGlobal} * s.flags != {}:
r.address = ropef("$1[0]", [s.loc.r])
r.res = ropef("$1[1]", [s.loc.r])
else:
r.address = s.loc.r
r.res = con(s.loc.r, "_Idx")
elif k != etyObject and sfAddrTaken in s.flags:
r.res = ropef("$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 isGenericRoutine(s) or
{sfImportc, sfInfixCall} * s.flags != {}:
nil
elif s.kind == skMethod and s.getBody.kind == nkEmpty:
# we cannot produce code for the dispatcher yet:
nil
elif sfForward in s.flags:
p.g.forwarded.add(s)
elif 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: app(owner.locals, newp)
else: app(p.g.code, newp)
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 = ropef("$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:
app(r.res, a.address)
app(r.res, ", ")
app(r.res, a.res)
else:
app(r.res, a.res)
proc genArgs(p: PProc, n: PNode, r: var TCompRes) =
app(r.res, "(")
for i in countup(1, sonsLen(n) - 1):
let it = n.sons[i]
if it.typ.isCompileTimeOnly: continue
if i > 1: app(r.res, ", ")
genArg(p, it, r)
app(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 = ropef("$1[$2]", [r.address, r.res])
r.address = nil
r.typ = etyNone
app(r.res, ".")
var op: TCompRes
gen(p, n.sons[0], op)
app(r.res, op.res)
app(r.res, "(")
for i in countup(2, sonsLen(n) - 1):
if i > 2: app(r.res, ", ")
genArg(p, n.sons[i], r)
app(r.res, ")")
r.kind = resExpr
proc genEcho(p: PProc, n: PNode, r: var TCompRes) =
useMagic(p, "rawEcho")
app(r.res, "rawEcho")
genArgs(p, n, r)
proc putToSeq(s: string, indirect: bool): PRope =
result = toRope(s)
if indirect: result = ropef("[$1]", [result])
proc createVar(p: PProc, typ: PType, indirect: bool): PRope
proc createRecordVarAux(p: PProc, rec: PNode, c: var int): PRope =
result = nil
case rec.kind
of nkRecList:
for i in countup(0, sonsLen(rec) - 1):
app(result, createRecordVarAux(p, rec.sons[i], c))
of nkRecCase:
app(result, createRecordVarAux(p, rec.sons[0], c))
for i in countup(1, sonsLen(rec) - 1):
app(result, createRecordVarAux(p, lastSon(rec.sons[i]), c))
of nkSym:
if c > 0: app(result, ", ")
app(result, mangleName(rec.sym))
app(result, ": ")
app(result, createVar(p, rec.sym.typ, false))
inc(c)
else: InternalError(rec.info, "createRecordVarAux")
proc createVar(p: PProc, typ: PType, indirect: bool): PRope =
var t = skipTypes(typ, abstractInst)
case t.kind
of tyInt..tyInt64, 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 = toRope("{}")
of tyBool:
result = putToSeq("false", indirect)
of tyArray, tyArrayConstr:
var length = int(lengthOrd(t))
var e = elemType(t)
if length > 32:
useMagic(p, "ArrayConstr")
result = ropef("ArrayConstr($1, $2, $3)", [toRope(length),
createVar(p, e, false), genTypeInfo(p, e)])
else:
result = toRope("[")
var i = 0
while i < length:
if i > 0: app(result, ", ")
app(result, createVar(p, e, false))
inc(i)
app(result, "]")
of tyTuple:
result = toRope("{")
for i in 0.. <t.sonslen:
if i > 0: app(result, ", ")
appf(result, "Field$1: $2", i.toRope, createVar(p, t.sons[i], false))
app(result, "}")
of tyObject:
result = toRope("{")
var c = 0
if tfFinal notin t.flags or t.sons[0] != nil:
inc(c)
appf(result, "m_type: $1", [genTypeInfo(p, t)])
while t != nil:
app(result, createRecordVarAux(p, t.n, c))
t = t.sons[0]
app(result, "}")
of tyVar, tyPtr, tyRef:
if mapType(t) == etyBaseIndex: result = putToSeq("[null, 0]", indirect)
else: result = putToSeq("null", indirect)
of tySequence, tyString, tyCString, tyPointer, tyProc:
result = putToSeq("null", indirect)
else:
internalError("createVar: " & $t.kind)
result = nil
proc isIndirect(v: PSym): bool =
result = (sfAddrTaken in v.flags) and (mapType(v.typ) != etyObject) and
v.kind notin {skProc, skConverter, skMethod, skIterator}
proc genVarInit(p: PProc, v: PSym, n: PNode) =
var
a: TCompRes
s: PRope
if n.kind == nkEmpty:
appf(p.body, "var $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 = ropef("NimCopy($1, $2)", [a.res, genTypeInfo(p, n.typ)])
of etyBaseIndex:
if (a.typ != etyBaseIndex): InternalError(n.info, "genVarInit")
if {sfAddrTaken, sfGlobal} * v.flags != {}:
appf(p.body, "var $1 = [$2, $3];$n", [v.loc.r, a.address, a.res])
else:
appf(p.body, "var $1 = $2; var $1_Idx = $3;$n", [
v.loc.r, a.address, a.res])
return
else:
s = a.res
if isIndirect(v): appf(p.body, "var $1 = [$2];$n", [v.loc.r, s])
else: appf(p.body, "var $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: continue
assert(a.kind == nkIdentDefs)
assert(a.sons[0].kind == nkSym)
var v = a.sons[0].sym
if lfNoDecl in v.loc.flags: continue
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)
app(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]
appf(p.body, "$1 = $2;$n", [a.res, createVar(p, t, true)])
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]
appf(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)")
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.app(ropef("[$1].concat(", [a.res]))
else:
r.res.app(ropef("($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.app(ropef("[$1],", [a.res]))
else:
r.res.app(ropef("$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.app(ropef("[$1, 0])", [a.res]))
else:
r.res.app(ropef("$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 = ropef("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 = ropef("($1.m_type == $2)", [x.res, genTypeInfo(p, t)])
else:
useMagic(p, "isObj")
r.res = ropef("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)
appf(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: PRope
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 mPred:
# XXX: range checking?
if not (optOverflowCheck in p.Options): binaryExpr(p, n, r, "", "$1 - $2")
else: binaryExpr(p, n, r, "subInt", "subInt($1, $2)")
of mSucc:
# XXX: range checking?
if not (optOverflowCheck in p.Options): binaryExpr(p, n, r, "", "$1 - $2")
else: binaryExpr(p, n, r, "addInt", "addInt($1, $2)")
of mAppendStrCh: binaryExpr(p, n, r, "addChar", "addChar($1, $2)")
of mAppendStrStr:
if skipTypes(n.sons[1].typ, abstractVarRange).kind == tyCString:
binaryExpr(p, n, r, "", "$1 += $2")
else:
binaryExpr(p, n, r, "", "$1 = ($1.slice(0,-1)).concat($2)")
# XXX: make a copy of $2, because of Javascript's sucking semantics
of mAppendSeqElem: binaryExpr(p, n, r, "", "$1.push($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 = toRope(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.length-1)")
of mLengthSeq, mLengthOpenArray, mLengthArray:
unaryExpr(p, n, r, "", "$1.length")
of mHigh:
if skipTypes(n.sons[0].typ, abstractVar).kind == tyString:
unaryExpr(p, n, r, "", "($1.length-2)")
else:
unaryExpr(p, n, r, "", "($1.length-1)")
of mInc:
if not (optOverflowCheck in p.Options): binaryExpr(p, n, r, "", "$1 += $2")
else: binaryExpr(p, n, r, "addInt", "$1 = addInt($1, $2)")
of ast.mDec:
if not (optOverflowCheck in 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")
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 mNLen..mNError:
localError(n.info, errCannotGenerateCodeForX, n.sons[0].sym.name.s)
of mNewSeq: genNewSeq(p, n)
of mOf: genOf(p, n, r)
of mReset: genReset(p, n)
of mEcho: genEcho(p, n, r)
of mSlurp, mStaticExec:
localError(n.info, errXMustBeCompileTime, n.sons[0].sym.name.s)
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 = toRope("SetConstr(")
r.kind = resExpr
for i in countup(0, sonsLen(n) - 1):
if i > 0: app(r.res, ", ")
var it = n.sons[i]
if it.kind == nkRange:
gen(p, it.sons[0], a)
gen(p, it.sons[1], b)
appf(r.res, "[$1, $2]", [a.res, b.res])
else:
gen(p, it, a)
app(r.res, a.res)
app(r.res, ")")
proc genArrayConstr(p: PProc, n: PNode, r: var TCompRes) =
var a: TCompRes
r.res = toRope("[")
r.kind = resExpr
for i in countup(0, sonsLen(n) - 1):
if i > 0: app(r.res, ", ")
gen(p, n.sons[i], a)
app(r.res, a.res)
app(r.res, "]")
proc genTupleConstr(p: PProc, n: PNode, r: var TCompRes) =
var a: TCompRes
r.res = toRope("{")
r.kind = resExpr
for i in countup(0, sonsLen(n) - 1):
if i > 0: app(r.res, ", ")
var it = n.sons[i]
if it.kind == nkExprColonExpr: it = it.sons[1]
gen(p, it, a)
appf(r.res, "Field$1: $2", [i.toRope, a.res])
r.res.app("}")
proc genObjConstr(p: PProc, n: PNode, r: var TCompRes) =
# XXX inheritance?
var a: TCompRes
r.res = toRope("{")
r.kind = resExpr
for i in countup(0, sonsLen(n) - 1):
if i > 0: app(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)
appf(r.res, "$1: $2", [f.loc.r, a.res])
r.res.app("}")
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) and (src.kind == tyBool):
r.res = ropef("(($1)? 1:0)", [r.res])
r.kind = resExpr
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 = ropef("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 = ropef("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 = ropef("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)
app(p.body, "break BeforeRet;" & tnl)
proc genProcBody(p: PProc, prc: PSym): PRope =
if optStackTrace in prc.options:
result = ropef("var F={procname:$1,prev:framePtr,filename:$2,line:0};$n" &
"framePtr = F;$n", [makeJSString(prc.owner.name.s & '.' & prc.name.s),
makeJSString(toFilename(prc.info))])
else:
result = nil
if p.beforeRetNeeded:
appf(result, "BeforeRet: do {$n$1} while (false); $n", [p.body])
else:
app(result, p.body)
if prc.typ.callConv == ccSysCall:
result = ropef("try {$n$1} catch (e) {$n" &
" alert(\"Unhandled exception:\\n\" + e.message + \"\\n\"$n}", [result])
if optStackTrace in prc.options:
app(result, "framePtr = framePtr.prev;" & tnl)
proc genProc(oldProc: PProc, prc: PSym): PRope =
var
resultSym: PSym
name, returnStmt, resultAsgn, header: PRope
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 = ropef("var $# = $#;$n", [mangleName(resultSym),
createVar(p, resultSym.typ, isIndirect(resultSym))])
gen(p, prc.ast.sons[resultPos], a)
returnStmt = ropef("return $#;$n", [a.res])
genStmt(p, prc.getBody)
result = ropef("function $#($#) {$n$#$#$#$#}$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: appf(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 = toRope(n.intVal)
of nkNilLit:
if isEmptyType(n.typ):
nil
elif mapType(n.typ) == etyBaseIndex:
r.typ = etyBaseIndex
r.address = toRope"null"
r.res = toRope"0"
else:
r.res = toRope"null"
of nkStrLit..nkTripleStrLit:
if skipTypes(n.typ, abstractVarRange).kind == tyString:
useMagic(p, "cstrToNimstr")
r.res = ropef("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 = toRope"NaN"
elif f == 0.0: r.res = toRope"0.0"
elif f == 0.5 * f:
if f > 0.0: r.res = toRope"Infinity"
else: r.res = toRope"-Infinity"
else: r.res = toRope(f.ToStrMaxPrecision)
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: nil
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 or isGenericRoutine(s): nil
elif not p.g.generatedSyms.containsOrIncl(s.id):
app(p.locals, genProc(p, s))
of nkMetaNode: gen(p, n.sons[0], r)
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: nil
of nkForStmt, nkParForStmt:
internalError(n.info, "for statement not eliminated")
of nkCaseStmt: genCase(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: nil
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")
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(): PRope =
result = ropef("/* Generated by the Nimrod Compiler v$1 */$n" &
"/* (c) 2013 Andreas Rumpf */$n$n" & "$nvar Globals = this;$n" &
"var framePtr = null;$n" & "var excHandler = null;$n",
[toRope(versionAsString)])
proc genModule(p: PProc, n: PNode) =
if optStackTrace in p.options:
appf(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:
appf(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)
app(p.g.code, p.locals)
app(p.g.code, p.body)
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:
for prc in globals.forwarded:
if not globals.generatedSyms.containsOrIncl(prc.id):
var p = newProc(globals, m, nil, m.module.options)
app(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)
app(p.g.code, genProc(p, prc))
# write the file:
var code = con(globals.typeInfo, globals.code)
var outfile = changeFileExt(completeCFilePath(m.module.filename), "js")
discard writeRopeIfNotEqual(con(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)