#
#
# The Nimrod Compiler
# (c) Copyright 2011 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.
# **Invariant: each expression only occurs once in the generated
# code!**
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
proc ecmasgenPass*(): TPass
# implementation
type
TEcmasGen = object of TPassContext
filename*: string
module*: PSym
BModule = ref TEcmasGen
TEcmasTypeKind = enum
etyNone, # no type
etyNull, # null type
etyProc, # proc type
etyBool, # bool type
etyInt, # Ecmascript's int
etyFloat, # Ecmascript's float
etyString, # Ecmascript's string
etyObject, # Ecmascript's reference to an object
etyBaseIndex # base + index needed
TCompRes{.final.} = object
kind*: TEcmasTypeKind
com*: PRope # computation part
# address if this is a (address, index)-tuple
res*: PRope # result part; index if this is an
# (address, index)-tuple
TBlock{.final.} = object
id*: int # the ID of the label; positive means that it
# has been used (i.e. the label should be emitted)
nestedTryStmts*: int # how many try statements is it nested into
TGlobals{.final.} = object
typeInfo*, code*: PRope
typeInfoGenerated*: TIntSet
PGlobals = ref TGlobals
TProc{.final.} = object
procDef*: PNode
prc*: PSym
data*: PRope
options*: TOptions
module*: BModule
globals*: PGlobals
BeforeRetNeeded*: bool
nestedTryStmts*: int
unique*: int
blocks*: seq[TBlock]
proc newGlobals(): PGlobals =
new(result)
result.typeInfoGenerated = initIntSet()
proc initCompRes(r: var TCompRes) =
r.com = nil
r.res = nil
r.kind = etyNone
proc initProc(p: var TProc, globals: PGlobals, module: BModule, procDef: PNode,
options: TOptions) =
p.blocks = @[]
p.options = options
p.module = module
p.procDef = procDef
p.globals = globals
if procDef != nil: p.prc = procDef.sons[namePos].sym
const
MappedToObject = {tyObject, tyArray, tyArrayConstr, tyTuple, tyOpenArray,
tySet, tyVar, tyRef, tyPtr}
proc mapType(typ: PType): TEcmasTypeKind =
var 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: result = mapType(t.sons[0])
of tyInt..tyInt64, 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:
result = etyObject
of tyNil: result = etyNull
of tyGenericInst, tyGenericParam, tyGenericBody, tyGenericInvokation, tyNone,
tyForward, tyEmpty, tyExpr, tyStmt, tyTypeDesc:
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 genTypeInfo(p: var TProc, typ: PType): PRope
proc genObjectFields(p: var TProc, typ: PType, n: PNode): PRope =
var
s, u: PRope
length: int
field: PSym
b: PNode
result = nil
case n.kind
of nkRecList:
length = sonsLen(n)
if length == 1:
result = genObjectFields(p, typ, n.sons[0])
else:
s = nil
for i in countup(0, length - 1):
if i > 0: app(s, ", " & tnl)
app(s, genObjectFields(p, typ, n.sons[i]))
result = ropef("{kind: 2, len: $1, offset: 0, " &
"typ: null, name: null, sons: [$2]}", [toRope(length), s])
of nkSym:
field = n.sym
s = genTypeInfo(p, field.typ)
result = ropef("{kind: 1, offset: \"$1\", len: 0, " &
"typ: $2, name: $3, sons: null}",
[mangleName(field), s, makeCString(field.name.s)])
of nkRecCase:
length = sonsLen(n)
if (n.sons[0].kind != nkSym): InternalError(n.info, "genObjectFields")
field = n.sons[0].sym
s = genTypeInfo(p, field.typ)
for i in countup(1, length - 1):
b = n.sons[i] # branch
u = nil
case b.kind
of nkOfBranch:
if sonsLen(b) < 2:
internalError(b.info, "genObjectFields; nkOfBranch broken")
for j in countup(0, sonsLen(b) - 2):
if u != nil: app(u, ", ")
if b.sons[j].kind == nkRange:
appf(u, "[$1, $2]", [toRope(getOrdValue(b.sons[j].sons[0])),
toRope(getOrdValue(b.sons[j].sons[1]))])
else:
app(u, toRope(getOrdValue(b.sons[j])))
of nkElse:
u = toRope(lengthOrd(field.typ))
else: internalError(n.info, "genObjectFields(nkRecCase)")
if result != nil: app(result, ", " & tnl)
appf(result, "[SetConstr($1), $2]",
[u, genObjectFields(p, typ, lastSon(b))])
result = ropef("{kind: 3, offset: \"$1\", len: $3, " &
"typ: $2, name: $4, sons: [$5]}", [mangleName(field), s,
toRope(lengthOrd(field.typ)), makeCString(field.name.s), result])
else: internalError(n.info, "genObjectFields")
proc genObjectInfo(p: var TProc, typ: PType, name: PRope) =
var s = ropef("var $1 = {size: 0, kind: $2, base: null, node: null, " &
"finalizer: null};$n", [name, toRope(ord(typ.kind))])
prepend(p.globals.typeInfo, s)
appf(p.globals.typeInfo, "var NNI$1 = $2;$n",
[toRope(typ.id), genObjectFields(p, typ, typ.n)])
appf(p.globals.typeInfo, "$1.node = NNI$2;$n", [name, toRope(typ.id)])
if (typ.kind == tyObject) and (typ.sons[0] != nil):
appf(p.globals.typeInfo, "$1.base = $2;$n",
[name, genTypeInfo(p, typ.sons[0])])
proc genEnumInfo(p: var TProc, typ: PType, name: PRope) =
var
s, n: PRope
length: int
field: PSym
length = sonsLen(typ.n)
s = nil
for i in countup(0, length - 1):
if (typ.n.sons[i].kind != nkSym): InternalError(typ.n.info, "genEnumInfo")
field = typ.n.sons[i].sym
if i > 0: app(s, ", " & tnl)
appf(s, "{kind: 1, offset: $1, typ: $2, name: $3, len: 0, sons: null}",
[toRope(field.position), name, makeCString(field.name.s)])
n = ropef("var NNI$1 = {kind: 2, offset: 0, typ: null, " &
"name: null, len: $2, sons: [$3]};$n", [toRope(typ.id), toRope(length), s])
s = ropef("var $1 = {size: 0, kind: $2, base: null, node: null, " &
"finalizer: null};$n", [name, toRope(ord(typ.kind))])
prepend(p.globals.typeInfo, s)
app(p.globals.typeInfo, n)
appf(p.globals.typeInfo, "$1.node = NNI$2;$n", [name, toRope(typ.id)])
if typ.sons[0] != nil:
appf(p.globals.typeInfo, "$1.base = $2;$n",
[name, genTypeInfo(p, typ.sons[0])])
proc genTypeInfo(p: var TProc, typ: PType): PRope =
var t = typ
if t.kind == tyGenericInst: t = lastSon(t)
result = ropef("NTI$1", [toRope(t.id)])
if ContainsOrIncl(p.globals.TypeInfoGenerated, t.id): return
case t.kind
of tyDistinct:
result = genTypeInfo(p, typ.sons[0])
of tyPointer, tyProc, tyBool, tyChar, tyCString, tyString, tyInt..tyFloat128:
var s = ropef(
"var $1 = {size: 0,kind: $2,base: null,node: null,finalizer: null};$n",
[result, toRope(ord(t.kind))])
prepend(p.globals.typeInfo, s)
of tyVar, tyRef, tyPtr, tySequence, tyRange, tySet:
var s = ropef(
"var $1 = {size: 0,kind: $2,base: null,node: null,finalizer: null};$n",
[result, toRope(ord(t.kind))])
prepend(p.globals.typeInfo, s)
appf(p.globals.typeInfo, "$1.base = $2;$n",
[result, genTypeInfo(p, typ.sons[0])])
of tyArrayConstr, tyArray:
var s = ropef(
"var $1 = {size: 0,kind: $2,base: null,node: null,finalizer: null};$n",
[result, toRope(ord(t.kind))])
prepend(p.globals.typeInfo, s)
appf(p.globals.typeInfo, "$1.base = $2;$n",
[result, genTypeInfo(p, typ.sons[1])])
of tyEnum: genEnumInfo(p, t, result)
of tyObject, tyTuple: genObjectInfo(p, t, result)
else: InternalError("genTypeInfo(" & $t.kind & ')')
proc gen(p: var TProc, n: PNode, r: var TCompRes)
proc genStmt(p: var TProc, n: PNode, r: var TCompRes)
proc useMagic(p: var TProc, ident: string) =
nil
# to implement
proc mergeExpr(a, b: PRope): PRope =
if (a != nil):
if b != nil: result = ropef("($1, $2)", [a, b])
else: result = a
else:
result = b
proc mergeExpr(r: TCompRes): PRope =
result = mergeExpr(r.com, r.res)
proc mergeStmt(r: TCompRes): PRope =
if r.res == nil: result = r.com
elif r.com == nil: result = r.res
else: result = ropef("$1$2", [r.com, r.res])
proc genAnd(p: var TProc, a, b: PNode, r: var TCompRes) =
var x, y: TCompRes
gen(p, a, x)
gen(p, b, y)
r.res = ropef("($1 && $2)", [mergeExpr(x), mergeExpr(y)])
proc genOr(p: var TProc, a, b: PNode, r: var TCompRes) =
var x, y: TCompRes
gen(p, a, x)
gen(p, b, y)
r.res = ropef("($1 || $2)", [mergeExpr(x), mergeExpr(y)])
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
["AddU64", "AddU64", "AddU64($1, $2)", "AddU64($1, $2)"], # AddU64
["SubU64", "SubU64", "SubU64($1, $2)", "SubU64($1, $2)"], # SubU64
["MulU64", "MulU64", "MulU64($1, $2)", "MulU64($1, $2)"], # MulU64
["DivU64", "DivU64", "DivU64($1, $2)", "DivU64($1, $2)"], # DivU64
["ModU64", "ModU64", "ModU64($1, $2)", "ModU64($1, $2)"], # ModU64
["", "", "($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: var TProc, n: PNode, r: var TCompRes, magic, frmt: string) =
var x, y: TCompRes
if magic != "": useMagic(p, magic)
gen(p, n.sons[1], x)
gen(p, n.sons[2], y)
r.res = ropef(frmt, [x.res, y.res])
r.com = mergeExpr(x.com, y.com)
proc binaryStmt(p: var TProc, n: PNode, r: var TCompRes, magic, frmt: string) =
var x, y: TCompRes
if magic != "": useMagic(p, magic)
gen(p, n.sons[1], x)
gen(p, n.sons[2], y)
if x.com != nil: appf(r.com, "$1;$n", [x.com])
if y.com != nil: appf(r.com, "$1;$n", [y.com])
appf(r.com, frmt, [x.res, y.res])
proc unaryExpr(p: var TProc, n: PNode, r: var TCompRes, magic, frmt: string) =
if magic != "": useMagic(p, magic)
gen(p, n.sons[1], r)
r.res = ropef(frmt, [r.res])
proc arith(p: var TProc, n: PNode, r: var TCompRes, op: TMagic) =
var
x, y: TCompRes
i: int
if optOverflowCheck in p.options: i = 0
else: i = 1
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.res, y.res])
r.com = mergeExpr(x.com, y.com)
else:
gen(p, n.sons[1], r)
r.res = ropef(ops[op][i + 2], [r.res])
proc genLineDir(p: var TProc, n: PNode, r: var TCompRes) =
var line: int
line = toLinenumber(n.info)
if optLineDir in p.Options:
appf(r.com, "// line $2 \"$1\"$n",
[toRope(toFilename(n.info)), toRope(line)])
if ({optStackTrace, optEndb} * p.Options == {optStackTrace, optEndb}) and
((p.prc == nil) or not (sfPure in p.prc.flags)):
useMagic(p, "endb")
appf(r.com, "endb($1);$n", [toRope(line)])
elif ({optLineTrace, optStackTrace} * p.Options ==
{optLineTrace, optStackTrace}) and
((p.prc == nil) or not (sfPure in p.prc.flags)):
appf(r.com, "F.line = $1;$n", [toRope(line)])
proc finishTryStmt(p: var TProc, r: var TCompRes, howMany: int) =
for i in countup(1, howMany):
app(r.com, "excHandler = excHandler.prev;" & tnl)
proc genWhileStmt(p: var TProc, n: PNode, r: var TCompRes) =
var
cond, stmt: TCompRes
length, labl: int
genLineDir(p, n, r)
inc(p.unique)
length = len(p.blocks)
setlen(p.blocks, length + 1)
p.blocks[length].id = - p.unique
p.blocks[length].nestedTryStmts = p.nestedTryStmts
labl = p.unique
gen(p, n.sons[0], cond)
genStmt(p, n.sons[1], stmt)
if p.blocks[length].id > 0:
appf(r.com, "L$3: while ($1) {$n$2}$n",
[mergeExpr(cond), mergeStmt(stmt), toRope(labl)])
else:
appf(r.com, "while ($1) {$n$2}$n", [mergeExpr(cond), mergeStmt(stmt)])
setlen(p.blocks, length)
proc genTryStmt(p: var TProc, n: PNode, r: var TCompRes) =
# code to generate:
#
# var sp = {prev: excHandler, exc: null};
# excHandler = sp;
# try {
# stmts;
# } 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;
# }
#
var
i, length, blen: int
safePoint, orExpr, epart: PRope
a: TCompRes
genLineDir(p, n, r)
inc(p.unique)
safePoint = ropef("Tmp$1", [toRope(p.unique)])
appf(r.com,
"var $1 = {prev: excHandler, exc: null};$n" & "excHandler = $1;$n",
[safePoint])
if optStackTrace in p.Options: app(r.com, "framePtr = F;" & tnl)
app(r.com, "try {" & tnl)
length = sonsLen(n)
inc(p.nestedTryStmts)
genStmt(p, n.sons[0], a)
app(r.com, mergeStmt(a))
i = 1
epart = nil
while (i < length) and (n.sons[i].kind == nkExceptBranch):
blen = sonsLen(n.sons[i])
if blen == 1:
# general except section:
if i > 1: app(epart, "else {" & tnl)
genStmt(p, n.sons[i].sons[0], a)
app(epart, mergeStmt(a))
if i > 1: app(epart, '}' & tnl)
else:
orExpr = nil
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, "($1.exc.m_type == $2)",
[safePoint, genTypeInfo(p, n.sons[i].sons[j].typ)])
if i > 1: app(epart, "else ")
appf(epart, "if ($1.exc && $2) {$n", [safePoint, orExpr])
genStmt(p, n.sons[i].sons[blen - 1], a)
appf(epart, "$1}$n", [mergeStmt(a)])
inc(i)
if epart != nil: appf(r.com, "} catch (EXC) {$n$1", [epart])
finishTryStmt(p, r, p.nestedTryStmts)
dec(p.nestedTryStmts)
app(r.com, "} finally {" & tnl & "excHandler = excHandler.prev;" & tnl)
if (i < length) and (n.sons[i].kind == nkFinally):
genStmt(p, n.sons[i].sons[0], a)
app(r.com, mergeStmt(a))
app(r.com, '}' & tnl)
proc genRaiseStmt(p: var TProc, n: PNode, r: var TCompRes) =
var
a: TCompRes
typ: PType
genLineDir(p, n, r)
if n.sons[0].kind != nkEmpty:
gen(p, n.sons[0], a)
if a.com != nil: appf(r.com, "$1;$n", [a.com])
typ = skipTypes(n.sons[0].typ, abstractPtrs)
useMagic(p, "raiseException")
appf(r.com, "raiseException($1, $2);$n",
[a.res, makeCString(typ.sym.name.s)])
else:
useMagic(p, "reraiseException")
app(r.com, "reraiseException();" & tnl)
proc genCaseStmt(p: var TProc, n: PNode, r: var TCompRes) =
var
cond, stmt: TCompRes
it, e, v: PNode
stringSwitch: bool
genLineDir(p, n, r)
gen(p, n.sons[0], cond)
if cond.com != nil: appf(r.com, "$1;$n", [cond.com])
stringSwitch = skipTypes(n.sons[0].typ, abstractVar).kind == tyString
if stringSwitch:
useMagic(p, "toEcmaStr")
appf(r.com, "switch (toEcmaStr($1)) {$n", [cond.res])
else:
appf(r.com, "switch ($1) {$n", [cond.res])
for i in countup(1, sonsLen(n) - 1):
it = n.sons[i]
case it.kind
of nkOfBranch:
for j in countup(0, sonsLen(it) - 2):
e = it.sons[j]
if e.kind == nkRange:
v = copyNode(e.sons[0])
while (v.intVal <= e.sons[1].intVal):
gen(p, v, cond)
if cond.com != nil: internalError(v.info, "ecmasgen.genCaseStmt")
appf(r.com, "case $1: ", [cond.res])
Inc(v.intVal)
else:
gen(p, e, cond)
if cond.com != nil: internalError(e.info, "ecmasgen.genCaseStmt")
if stringSwitch:
case e.kind
of nkStrLit..nkTripleStrLit: appf(r.com, "case $1: ",
[makeCString(e.strVal)])
else: InternalError(e.info, "ecmasgen.genCaseStmt: 2")
else:
appf(r.com, "case $1: ", [cond.res])
genStmt(p, lastSon(it), stmt)
appf(r.com, "$n$1break;$n", [mergeStmt(stmt)])
of nkElse:
genStmt(p, it.sons[0], stmt)
appf(r.com, "default: $n$1break;$n", [mergeStmt(stmt)])
else: internalError(it.info, "ecmasgen.genCaseStmt")
appf(r.com, "}$n", [])
proc genStmtListExpr(p: var TProc, n: PNode, r: var TCompRes)
proc genBlock(p: var TProc, 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
p.blocks[idx].nestedTryStmts = p.nestedTryStmts
labl = p.unique
if n.kind == nkBlockExpr: genStmtListExpr(p, n.sons[1], r)
else: genStmt(p, n.sons[1], r)
if p.blocks[idx].id > 0:
# label has been used:
r.com = ropef("L$1: do {$n$2} while(false);$n", [toRope(labl), r.com])
setlen(p.blocks, idx)
proc genBreakStmt(p: var TProc, n: PNode, r: var TCompRes) =
var
idx: int
sym: PSym
genLineDir(p, n, r)
idx = len(p.blocks) - 1
if n.sons[0].kind != nkEmpty:
# named break?
assert(n.sons[0].kind == nkSym)
sym = n.sons[0].sym
assert(sym.loc.k == locOther)
idx = sym.loc.a
p.blocks[idx].id = abs(p.blocks[idx].id) # label is used
finishTryStmt(p, r, p.nestedTryStmts - p.blocks[idx].nestedTryStmts)
appf(r.com, "break L$1;$n", [toRope(p.blocks[idx].id)])
proc genAsmStmt(p: var TProc, n: PNode, r: var TCompRes) =
genLineDir(p, n, r)
assert(n.kind == nkAsmStmt)
for i in countup(0, sonsLen(n) - 1):
case n.sons[i].Kind
of nkStrLit..nkTripleStrLit: app(r.com, n.sons[i].strVal)
of nkSym: app(r.com, mangleName(n.sons[i].sym))
else: InternalError(n.sons[i].info, "ecmasgen: genAsmStmt()")
proc genIfStmt(p: var TProc, n: PNode, r: var TCompRes) =
var
toClose: int
cond, stmt: TCompRes
it: PNode
toClose = 0
for i in countup(0, sonsLen(n) - 1):
it = n.sons[i]
if sonsLen(it) != 1:
gen(p, it.sons[0], cond)
genStmt(p, it.sons[1], stmt)
if i > 0:
appf(r.com, "else {$n", [])
inc(toClose)
if cond.com != nil: appf(r.com, "$1;$n", [cond.com])
appf(r.com, "if ($1) {$n$2}", [cond.res, mergeStmt(stmt)])
else:
# else part:
genStmt(p, it.sons[0], stmt)
appf(r.com, "else {$n$1}$n", [mergeStmt(stmt)])
app(r.com, repeatChar(toClose, '}') & tnl)
proc genIfExpr(p: var TProc, n: PNode, r: var TCompRes) =
var
toClose: int
cond, stmt: TCompRes
it: PNode
toClose = 0
for i in countup(0, sonsLen(n) - 1):
it = n.sons[i]
if sonsLen(it) != 1:
gen(p, it.sons[0], cond)
gen(p, it.sons[1], stmt)
if i > 0:
app(r.res, ": (")
inc(toClose)
r.com = mergeExpr(r.com, cond.com)
r.com = mergeExpr(r.com, stmt.com)
appf(r.res, "($1) ? ($2)", [cond.res, stmt.res])
else:
# else part:
gen(p, it.sons[0], stmt)
r.com = mergeExpr(r.com, stmt.com)
appf(r.res, ": ($1)", [stmt.res])
app(r.res, repeatChar(toClose, ')'))
proc generateHeader(p: var TProc, typ: PType): PRope =
var
param: PSym
name: PRope
result = nil
for i in countup(1, sonsLen(typ.n) - 1):
if result != nil: app(result, ", ")
assert(typ.n.sons[i].kind == nkSym)
param = typ.n.sons[i].sym
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, nkStringToCString,
nkCStringToString, nkCall, 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: var TProc, x, y: PNode, r: var TCompRes,
noCopyNeeded: bool) =
var a, b: TCompRes
gen(p, x, a)
gen(p, y, b)
case mapType(x.typ)
of etyObject:
if a.com != nil: appf(r.com, "$1;$n", [a.com])
if b.com != nil: appf(r.com, "$1;$n", [b.com])
if needsNoCopy(y) or noCopyNeeded:
appf(r.com, "$1 = $2;$n", [a.res, b.res])
else:
useMagic(p, "NimCopy")
appf(r.com, "$1 = NimCopy($2, $3);$n",
[a.res, b.res, genTypeInfo(p, y.typ)])
of etyBaseIndex:
if (a.kind != etyBaseIndex) or (b.kind != etyBaseIndex):
internalError(x.info, "genAsgn")
appf(r.com, "$1 = $2; $3 = $4;$n", [a.com, b.com, a.res, b.res])
else:
if a.com != nil: appf(r.com, "$1;$n", [a.com])
if b.com != nil: appf(r.com, "$1;$n", [b.com])
appf(r.com, "$1 = $2;$n", [a.res, b.res])
proc genAsgn(p: var TProc, n: PNode, r: var TCompRes) =
genLineDir(p, n, r)
genAsgnAux(p, n.sons[0], n.sons[1], r, false)
proc genFastAsgn(p: var TProc, n: PNode, r: var TCompRes) =
genLineDir(p, n, r)
genAsgnAux(p, n.sons[0], n.sons[1], r, true)
proc genSwap(p: var TProc, n: PNode, r: var TCompRes) =
var a, b: TCompRes
gen(p, n.sons[1], a)
gen(p, n.sons[2], b)
inc(p.unique)
var tmp = ropef("Tmp$1", [toRope(p.unique)])
case mapType(n.sons[1].typ)
of etyBaseIndex:
inc(p.unique)
var tmp2 = ropef("Tmp$1", [toRope(p.unique)])
if (a.kind != etyBaseIndex) or (b.kind != etyBaseIndex):
internalError(n.info, "genSwap")
appf(r.com, "var $1 = $2; $2 = $3; $3 = $1;$n", [tmp, a.com, b.com])
appf(r.com, "var $1 = $2; $2 = $3; $3 = $1", [tmp2, a.res, b.res])
else:
if a.com != nil: appf(r.com, "$1;$n", [a.com])
if b.com != nil: appf(r.com, "$1;$n", [b.com])
appf(r.com, "var $1 = $2; $2 = $3; $3 = $1", [tmp, a.res, b.res])
proc genFieldAddr(p: var TProc, n: PNode, r: var TCompRes) =
var a: TCompRes
r.kind = etyBaseIndex
gen(p, n.sons[0], a)
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 = makeCString(ropeToStr(f.loc.r))
r.com = mergeExpr(a)
proc genFieldAccess(p: var TProc, n: PNode, r: var TCompRes) =
r.kind = etyNone
gen(p, n.sons[0], r)
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])
proc genCheckedFieldAddr(p: var TProc, n: PNode, r: var TCompRes) =
genFieldAddr(p, n.sons[0], r) # XXX
proc genCheckedFieldAccess(p: var TProc, n: PNode, r: var TCompRes) =
genFieldAccess(p, n.sons[0], r) # XXX
proc genArrayAddr(p: var TProc, n: PNode, r: var TCompRes) =
var
a, b: TCompRes
first: biggestInt
r.kind = etyBaseIndex
gen(p, n.sons[0], a)
gen(p, n.sons[1], b)
r.com = mergeExpr(a)
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")
b.res = ropef("chckIndx($1, $2, $3.length)-$2",
[b.res, toRope(first), a.res])
# XXX: BUG: a.res evaluated twice!
elif first != 0:
b.res = ropef("($1)-$2", [b.res, toRope(first)])
r.res = mergeExpr(b)
proc genArrayAccess(p: var TProc, n: PNode, r: var TCompRes) =
genArrayAddr(p, n, r)
r.kind = etyNone
r.res = ropef("$1[$2]", [r.com, r.res])
r.com = nil
proc genAddr(p: var TProc, n: PNode, r: var TCompRes) =
var s: PSym
case n.sons[0].kind
of nkSym:
s = n.sons[0].sym
if s.loc.r == nil: InternalError(n.info, "genAddr: 3")
case s.kind
of skVar, skResult:
if mapType(n.typ) == etyObject:
# make addr() a no-op:
r.kind = etyNone
r.res = s.loc.r
r.com = nil
elif sfGlobal in s.flags:
# globals are always indirect accessible
r.kind = etyBaseIndex
r.com = toRope("Globals")
r.res = makeCString(ropeToStr(s.loc.r))
elif sfAddrTaken in s.flags:
r.kind = etyBaseIndex
r.com = 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:
genArrayAddr(p, n, r)
else: InternalError(n.info, "genAddr")
proc genSym(p: var TProc, n: PNode, r: var TCompRes) =
var s = n.sym
if s.loc.r == nil:
InternalError(n.info, "symbol has no generated name: " & s.name.s)
case s.kind
of skVar, skParam, skTemp, skResult:
var k = mapType(s.typ)
if k == etyBaseIndex:
r.kind = etyBaseIndex
if {sfAddrTaken, sfGlobal} * s.flags != {}:
r.com = ropef("$1[0]", [s.loc.r])
r.res = ropef("$1[1]", [s.loc.r])
else:
r.com = 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
else: r.res = s.loc.r
proc genDeref(p: var TProc, n: PNode, r: var TCompRes) =
var a: TCompRes
if mapType(n.sons[0].typ) == etyObject:
gen(p, n.sons[0], r)
else:
gen(p, n.sons[0], a)
if a.kind != etyBaseIndex: InternalError(n.info, "genDeref")
r.res = ropef("$1[$2]", [a.com, a.res])
proc genArgs(p: var TProc, n: PNode, r: var TCompRes) =
app(r.res, "(")
for i in countup(1, sonsLen(n) - 1):
if i > 1: app(r.res, ", ")
var a: TCompRes
gen(p, n.sons[i], a)
if a.kind == etyBaseIndex:
app(r.res, a.com)
app(r.res, ", ")
app(r.res, a.res)
else:
app(r.res, mergeExpr(a))
app(r.res, ")")
proc genCall(p: var TProc, n: PNode, r: var TCompRes) =
gen(p, n.sons[0], r)
genArgs(p, n, r)
proc genEcho(p: var TProc, n: PNode, r: var TCompRes) =
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: var TProc, typ: PType, indirect: bool): PRope
proc createRecordVarAux(p: var TProc, 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: var TProc, 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("{")
var c = 0
app(result, createRecordVarAux(p, t.n, c))
app(result, "}")
of tyObject:
result = toRope("{")
var c = 0
if not (tfFinal in 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)
proc genVarInit(p: var TProc, v: PSym, n: PNode, r: var TCompRes) =
var
a: TCompRes
s: PRope
if n.kind == nkEmpty:
appf(r.com, "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 a.com != nil: appf(r.com, "$1;$n", [a.com])
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.kind != etyBaseIndex): InternalError(n.info, "genVarInit")
if {sfAddrTaken, sfGlobal} * v.flags != {}:
appf(r.com, "var $1 = [$2, $3];$n", [v.loc.r, a.com, a.res])
else:
appf(r.com, "var $1 = $2; var $1_Idx = $3;$n", [v.loc.r, a.com, a.res])
return
else:
if a.com != nil: appf(r.com, "$1;$n", [a.com])
s = a.res
if isIndirect(v): appf(r.com, "var $1 = [$2];$n", [v.loc.r, s])
else: appf(r.com, "var $1 = $2;$n", [v.loc.r, s])
proc genVarStmt(p: var TProc, n: PNode, r: var TCompRes) =
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, r)
genVarInit(p, v, a.sons[2], r)
proc genConstStmt(p: var TProc, n: PNode, r: var TCompRes) =
genLineDir(p, n, r)
for i in countup(0, sonsLen(n) - 1):
if n.sons[i].kind == nkCommentStmt: continue
assert(n.sons[i].kind == nkConstDef)
var c = n.sons[i].sons[0].sym
if (c.ast != nil) and (c.typ.kind in ConstantDataTypes) and
not (lfNoDecl in c.loc.flags):
genLineDir(p, n.sons[i], r)
genVarInit(p, c, c.ast, r)
proc genNew(p: var TProc, n: PNode, r: var TCompRes) =
var a: TCompRes
gen(p, n.sons[1], a)
var t = skipTypes(n.sons[1].typ, abstractVar).sons[0]
if a.com != nil: appf(r.com, "$1;$n", [a.com])
appf(r.com, "$1 = $2;$n", [a.res, createVar(p, t, true)])
proc genOrd(p: var TProc, 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: var TProc, n: PNode, r: var TCompRes) =
var a, b: TCompRes
gen(p, n.sons[1], a)
gen(p, n.sons[2], b)
r.com = mergeExpr(a.com, b.com)
if skipTypes(n.sons[1].typ, abstractVarRange).kind == tyChar:
a.res = ropef("[$1, 0]", [a.res])
if skipTypes(n.sons[2].typ, abstractVarRange).kind == tyChar:
b.res = ropef("[$1, 0]", [b.res])
r.res = ropef("($1.slice(0,-1)).concat($2)", [a.res, b.res])
proc genMagic(p: var TProc, 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) #mRepr: genRepr(p, n, r);
of mSwap: genSwap(p, n, r)
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: binaryStmt(p, n, r, "addChar", "$1 = addChar($1, $2)")
of mAppendStrStr:
binaryStmt(p, n, r, "", "$1 = ($1.slice(0,-1)).concat($2)")
# XXX: make a copy of $2, because of EMCAScript's sucking semantics
of mAppendSeqElem: binaryStmt(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 mAssert:
if (optAssert in p.Options):
useMagic(p, "internalAssert")
gen(p, n.sons[1], a)
line = toRope(toLinenumber(n.info))
filen = makeCString(ToFilename(n.info))
appf(r.com, "if (!($3)) internalAssert($1, $2)",
[filen, line, mergeExpr(a)])
of mNew, mNewFinalize: genNew(p, n, r)
of mSizeOf: r.res = toRope(getSize(n.sons[1].typ))
of mChr: 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): binaryStmt(p, n, r, "", "$1 += $2")
else: binaryStmt(p, n, r, "addInt", "$1 = addInt($1, $2)")
of ast.mDec:
if not (optOverflowCheck in p.Options): binaryStmt(p, n, r, "", "$1 -= $2")
else: binaryStmt(p, n, r, "subInt", "$1 = subInt($1, $2)")
of mSetLengthStr: binaryStmt(p, n, r, "", "$1.length = ($2)-1")
of mSetLengthSeq: binaryStmt(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: binaryStmt(p, n, r, "", "$1[$2] = true")
of mExcl: binaryStmt(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: binaryStmt(p, n, r, "", "$1 = new Array($2)")
of mEcho: genEcho(p, n, r)
else:
genCall(p, n, r)
#else internalError(e.info, 'genMagic: ' + magicToStr[op]);
proc genSetConstr(p: var TProc, n: PNode, r: var TCompRes) =
var
a, b: TCompRes
useMagic(p, "SetConstr")
r.res = toRope("SetConstr(")
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)
r.com = mergeExpr(r.com, mergeExpr(a.com, b.com))
appf(r.res, "[$1, $2]", [a.res, b.res])
else:
gen(p, it, a)
r.com = mergeExpr(r.com, a.com)
app(r.res, a.res)
app(r.res, ")")
proc genArrayConstr(p: var TProc, n: PNode, r: var TCompRes) =
var a: TCompRes
r.res = toRope("[")
for i in countup(0, sonsLen(n) - 1):
if i > 0: app(r.res, ", ")
gen(p, n.sons[i], a)
r.com = mergeExpr(r.com, a.com)
app(r.res, a.res)
app(r.res, "]")
proc genRecordConstr(p: var TProc, n: PNode, r: var TCompRes) =
var a: TCompRes
var i = 0
var length = sonsLen(n)
r.res = toRope("{")
while i < length:
if i > 0: app(r.res, ", ")
if (n.sons[i].kind != nkSym):
internalError(n.sons[i].info, "genRecordConstr")
gen(p, n.sons[i + 1], a)
r.com = mergeExpr(r.com, a.com)
appf(r.res, "$1: $2", [mangleName(n.sons[i].sym), a.res])
inc(i, 2)
proc genConv(p: var TProc, 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])
proc upConv(p: var TProc, n: PNode, r: var TCompRes) =
gen(p, n.sons[0], r) # XXX
proc genRangeChck(p: var TProc, 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)
r.com = mergeExpr(r.com, mergeExpr(a.com, b.com))
useMagic(p, "chckRange")
r.res = ropef("chckRange($1, $2, $3)", [r.res, a.res, b.res])
proc convStrToCStr(p: var TProc, 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, "toEcmaStr")
r.res = ropef("toEcmaStr($1)", [r.res])
proc convCStrToStr(p: var TProc, 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])
proc genReturnStmt(p: var TProc, n: PNode, r: var TCompRes) =
var a: TCompRes
if p.procDef == nil: InternalError(n.info, "genReturnStmt")
p.BeforeRetNeeded = true
if (n.sons[0].kind != nkEmpty):
genStmt(p, n.sons[0], a)
if a.com != nil: appf(r.com, "$1;$n", mergeStmt(a))
else:
genLineDir(p, n, r)
finishTryStmt(p, r, p.nestedTryStmts)
app(r.com, "break BeforeRet;" & tnl)
proc genProcBody(p: var TProc, prc: PSym, r: TCompRes): PRope =
if optStackTrace in prc.options:
result = ropef("var F={procname:$1,prev:framePtr,filename:$2,line:0};$n" &
"framePtr = F;$n", [makeCString(prc.owner.name.s & '.' & prc.name.s),
makeCString(toFilename(prc.info))])
else:
result = nil
if p.beforeRetNeeded:
appf(result, "BeforeRet: do {$n$1} while (false); $n", [mergeStmt(r)])
else:
app(result, mergeStmt(r))
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: var TProc, n: PNode, r: var TCompRes) =
var
p: TProc
resultSym: PSym
name, returnStmt, resultAsgn, header: PRope
a: TCompRes
var prc = n.sons[namePos].sym
initProc(p, oldProc.globals, oldProc.module, n, prc.options)
returnStmt = nil
resultAsgn = nil
name = mangleName(prc)
header = generateHeader(p, prc.typ)
if (prc.typ.sons[0] != nil) and not (sfPure in prc.flags):
resultSym = n.sons[resultPos].sym
resultAsgn = ropef("var $1 = $2;$n", [mangleName(resultSym),
createVar(p, resultSym.typ, isIndirect(resultSym))])
gen(p, n.sons[resultPos], a)
if a.com != nil: appf(returnStmt, "$1;$n", [a.com])
returnStmt = ropef("return $1;$n", [a.res])
genStmt(p, n.sons[codePos], r)
r.com = ropef("function $1($2) {$n$3$4$5}$n",
[name, header, resultAsgn, genProcBody(p, prc, r), returnStmt])
r.res = nil
proc genStmtListExpr(p: var TProc, n: PNode, r: var TCompRes) =
var a: TCompRes
# watch out this trick: ``function () { stmtList; return expr; }()``
r.res = toRope("function () {")
for i in countup(0, sonsLen(n) - 2):
genStmt(p, n.sons[i], a)
app(r.res, mergeStmt(a))
gen(p, lastSon(n), a)
if a.com != nil: appf(r.res, "$1;$n", [a.com])
appf(r.res, "return $1; }()", [a.res])
proc genStmt(p: var TProc, n: PNode, r: var TCompRes) =
var a: TCompRes
r.kind = etyNone
r.com = nil
r.res = nil
case n.kind
of nkNilLit, nkEmpty: nil
of nkStmtList:
for i in countup(0, sonsLen(n) - 1):
genStmt(p, n.sons[i], a)
app(r.com, mergeStmt(a))
of nkBlockStmt: genBlock(p, n, r)
of nkIfStmt: genIfStmt(p, n, r)
of nkWhileStmt: genWhileStmt(p, n, r)
of nkVarSection: genVarStmt(p, n, r)
of nkConstSection: genConstStmt(p, n, r)
of nkForStmt: internalError(n.info, "for statement not eliminated")
of nkCaseStmt: genCaseStmt(p, n, r)
of nkReturnStmt: genReturnStmt(p, n, r)
of nkBreakStmt: genBreakStmt(p, n, r)
of nkAsgn: genAsgn(p, n, r)
of nkFastAsgn: genFastAsgn(p, n, r)
of nkDiscardStmt:
genLineDir(p, n, r)
gen(p, n.sons[0], r)
app(r.res, ';' & tnl)
of nkAsmStmt: genAsmStmt(p, n, r)
of nkTryStmt: genTryStmt(p, n, r)
of nkRaiseStmt: genRaiseStmt(p, n, r)
of nkTypeSection, nkCommentStmt, nkIteratorDef, nkIncludeStmt, nkImportStmt,
nkFromStmt, nkTemplateDef, nkMacroDef, nkPragma:
nil
of nkProcDef, nkMethodDef, nkConverterDef:
if (n.sons[genericParamsPos].kind == nkEmpty):
var prc = n.sons[namePos].sym
if (n.sons[codePos].kind != nkEmpty) and not (lfNoDecl in prc.loc.flags):
genProc(p, n, r)
else:
discard mangleName(prc)
else:
genLineDir(p, n, r)
gen(p, n, r)
app(r.res, ';' & tnl)
proc gen(p: var TProc, n: PNode, r: var TCompRes) =
var f: BiggestFloat
r.kind = etyNone
r.com = nil
r.res = nil
case n.kind
of nkSym:
genSym(p, n, r)
of nkCharLit..nkInt64Lit:
r.res = toRope(n.intVal)
of nkNilLit:
if mapType(n.typ) == etyBaseIndex:
r.kind = etyBaseIndex
r.com = 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)", [makeCString(n.strVal)])
else:
r.res = makeCString(n.strVal)
of nkFloatLit..nkFloat64Lit:
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 nkBlockExpr: genBlock(p, n, r)
of nkIfExpr: genIfExpr(p, n, r)
of nkCall, nkHiddenCallConv, nkCommand, nkCallStrLit:
if (n.sons[0].kind == nkSym) and (n.sons[0].sym.magic != mNone):
genMagic(p, n, r)
else:
genCall(p, n, r)
of nkCurly: genSetConstr(p, n, r)
of nkBracket: genArrayConstr(p, n, r)
of nkPar: genRecordConstr(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 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 nkPassAsOpenArray: gen(p, n.sons[0], r)
of nkStmtListExpr: genStmtListExpr(p, n, r)
of nkEmpty: nil
else: InternalError(n.info, "gen: unknown node type: " & $n.kind)
var globals: PGlobals
proc newModule(module: PSym, filename: string): BModule =
new(result)
result.filename = filename
result.module = module
if globals == nil: globals = newGlobals()
proc genHeader(): PRope =
result = ropef("/* Generated by the Nimrod Compiler v$1 */$n" &
"/* (c) 2010 Andreas Rumpf */$n$n" & "$nvar Globals = this;$n" &
"var framePtr = null;$n" & "var excHandler = null;$n",
[toRope(versionAsString)])
proc genModule(p: var TProc, n: PNode, r: var TCompRes) =
genStmt(p, n, r)
if optStackTrace in p.options:
r.com = ropef("var F = {procname:$1,prev:framePtr,filename:$2,line:0};$n" &
"framePtr = F;$n" & "$3" & "framePtr = framePtr.prev;$n", [
makeCString("module " & p.module.module.name.s),
makeCString(toFilename(p.module.module.info)), r.com])
proc myProcess(b: PPassContext, n: PNode): PNode =
if passes.skipCodegen(n): return n
var
p: TProc
r: TCompRes
result = n
var m = BModule(b)
if m.module == nil: InternalError(n.info, "myProcess")
initProc(p, globals, m, nil, m.module.options)
genModule(p, n, r)
app(p.globals.code, p.data)
app(p.globals.code, mergeStmt(r))
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:
# write the file:
var code = con(globals.typeInfo, globals.code)
var outfile = changeFileExt(completeCFilePath(m.filename), "js")
discard writeRopeIfNotEqual(con(genHeader(), code), outfile)
proc myOpenCached(s: PSym, filename: string, rd: PRodReader): PPassContext =
InternalError("symbol files are not possible with the Ecmas code generator")
result = nil
proc myOpen(s: PSym, filename: string): PPassContext =
result = newModule(s, filename)
proc ecmasgenPass(): TPass =
InitPass(result)
result.open = myOpen
result.close = myClose
result.openCached = myOpenCached
result.process = myProcess