mirror of
https://github.com/nim-lang/Nim.git
synced 2025-12-29 01:14:41 +00:00
expressions such as Type.field are now recognised by the compiler. This also fixes a bug, preventing the user-defined to check for the presence of regular fields in addition to procs
1713 lines
61 KiB
Nim
1713 lines
61 KiB
Nim
#
|
|
#
|
|
# The Nimrod Compiler
|
|
# (c) Copyright 2014 Andreas Rumpf
|
|
#
|
|
# See the file "copying.txt", included in this
|
|
# distribution, for details about the copyright.
|
|
#
|
|
|
|
# This is the JavaScript code generator.
|
|
# Soon also a Luajit code generator. ;-)
|
|
|
|
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, dirty.} =
|
|
(if p.target == targetJS: a else: b)
|
|
|
|
proc newGlobals(): PGlobals =
|
|
new(result)
|
|
result.forwarded = @[]
|
|
result.generatedSyms = initIntSet()
|
|
result.typeInfoGenerated = initIntSet()
|
|
|
|
proc initCompRes(r: var TCompRes) =
|
|
r.address = nil
|
|
r.res = nil
|
|
r.typ = etyNone
|
|
r.kind = resNone
|
|
|
|
proc rdLoc(a: TCompRes): 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, tyFromExpr, tyForward, tyEmpty, tyFieldAccessor,
|
|
tyExpr, tyStmt, tyStatic, tyTypeDesc, tyTypeClasses:
|
|
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 '_':
|
|
discard
|
|
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]
|
|
TMagicOps = array[mAddI..mStrToStr, TMagicFrmt]
|
|
|
|
const # magic checked op; magic unchecked op; checked op; unchecked op
|
|
jsOps: TMagicOps = [
|
|
["addInt", "", "addInt($1, $2)", "($1 + $2)"], # AddI
|
|
["subInt", "", "subInt($1, $2)", "($1 - $2)"], # SubI
|
|
["mulInt", "", "mulInt($1, $2)", "($1 * $2)"], # MulI
|
|
["divInt", "", "divInt($1, $2)", "Math.floor($1 / $2)"], # DivI
|
|
["modInt", "", "modInt($1, $2)", "Math.floor($1 % $2)"], # ModI
|
|
["addInt64", "", "addInt64($1, $2)", "($1 + $2)"], # AddI64
|
|
["subInt64", "", "subInt64($1, $2)", "($1 - $2)"], # SubI64
|
|
["mulInt64", "", "mulInt64($1, $2)", "($1 * $2)"], # MulI64
|
|
["divInt64", "", "divInt64($1, $2)", "Math.floor($1 / $2)"], # DivI64
|
|
["modInt64", "", "modInt64($1, $2)", "Math.floor($1 % $2)"], # ModI64
|
|
["", "", "($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)"], # EqUntracedRef
|
|
["", "", "($1 <= $2)", "($1 <= $2)"], # LePtr
|
|
["", "", "($1 < $2)", "($1 < $2)"], # LtPtr
|
|
["", "", "($1 == $2)", "($1 == $2)"], # EqCString
|
|
["", "", "($1 != $2)", "($1 != $2)"], # Xor
|
|
["", "", "($1 == $2)", "($1 == $2)"], # EqProc
|
|
["NegInt", "", "NegInt($1)", "-($1)"], # UnaryMinusI
|
|
["NegInt64", "", "NegInt64($1)", "-($1)"], # UnaryMinusI64
|
|
["AbsInt", "", "AbsInt($1)", "Math.abs($1)"], # AbsI
|
|
["AbsInt64", "", "AbsInt64($1)", "Math.abs($1)"], # AbsI64
|
|
["", "", "!($1)", "!($1)"], # Not
|
|
["", "", "+($1)", "+($1)"], # UnaryPlusI
|
|
["", "", "~($1)", "~($1)"], # BitnotI
|
|
["", "", "+($1)", "+($1)"], # 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"]]
|
|
|
|
luaOps: TMagicOps = [
|
|
["addInt", "", "addInt($1, $2)", "($1 + $2)"], # AddI
|
|
["subInt", "", "subInt($1, $2)", "($1 - $2)"], # SubI
|
|
["mulInt", "", "mulInt($1, $2)", "($1 * $2)"], # MulI
|
|
["divInt", "", "divInt($1, $2)", "Math.floor($1 / $2)"], # DivI
|
|
["modInt", "", "modInt($1, $2)", "Math.floor($1 % $2)"], # ModI
|
|
["addInt64", "", "addInt64($1, $2)", "($1 + $2)"], # AddI64
|
|
["subInt64", "", "subInt64($1, $2)", "($1 - $2)"], # SubI64
|
|
["mulInt64", "", "mulInt64($1, $2)", "($1 * $2)"], # MulI64
|
|
["divInt64", "", "divInt64($1, $2)", "Math.floor($1 / $2)"], # DivI64
|
|
["modInt64", "", "modInt64($1, $2)", "Math.floor($1 % $2)"], # ModI64
|
|
["", "", "($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)"], # EqUntracedRef
|
|
["", "", "($1 <= $2)", "($1 <= $2)"], # LePtr
|
|
["", "", "($1 < $2)", "($1 < $2)"], # LtPtr
|
|
["", "", "($1 == $2)", "($1 == $2)"], # EqCString
|
|
["", "", "($1 != $2)", "($1 != $2)"], # Xor
|
|
["", "", "($1 == $2)", "($1 == $2)"], # EqProc
|
|
["NegInt", "", "NegInt($1)", "-($1)"], # UnaryMinusI
|
|
["NegInt64", "", "NegInt64($1)", "-($1)"], # UnaryMinusI64
|
|
["AbsInt", "", "AbsInt($1)", "Math.abs($1)"], # AbsI
|
|
["AbsInt64", "", "AbsInt64($1)", "Math.abs($1)"], # AbsI64
|
|
["", "", "not ($1)", "not ($1)"], # Not
|
|
["", "", "+($1)", "+($1)"], # UnaryPlusI
|
|
["", "", "~($1)", "~($1)"], # BitnotI
|
|
["", "", "+($1)", "+($1)"], # 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 ternaryExpr(p: PProc, n: PNode, r: var TCompRes, magic, frmt: string) =
|
|
var x, y, z: TCompRes
|
|
useMagic(p, magic)
|
|
gen(p, n.sons[1], x)
|
|
gen(p, n.sons[2], y)
|
|
gen(p, n.sons[3], z)
|
|
r.res = ropef(frmt, [x.rdLoc, y.rdLoc, z.rdLoc])
|
|
r.kind = resExpr
|
|
|
|
proc unaryExpr(p: PProc, n: PNode, r: var TCompRes, magic, frmt: string) =
|
|
useMagic(p, magic)
|
|
gen(p, n.sons[1], r)
|
|
r.res = ropef(frmt, [r.rdLoc])
|
|
r.kind = resExpr
|
|
|
|
proc arithAux(p: PProc, n: PNode, r: var TCompRes, op: TMagic, ops: TMagicOps) =
|
|
var
|
|
x, y: TCompRes
|
|
let i = ord(optOverflowCheck notin p.options)
|
|
useMagic(p, ops[op][i])
|
|
if sonsLen(n) > 2:
|
|
gen(p, n.sons[1], x)
|
|
gen(p, n.sons[2], y)
|
|
r.res = 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 arith(p: PProc, n: PNode, r: var TCompRes, op: TMagic) =
|
|
arithAux(p, n, r, op, jsOps | luaOps)
|
|
|
|
proc genLineDir(p: PProc, n: PNode) =
|
|
let line = toLinenumber(n.info)
|
|
if optLineDir in p.options:
|
|
appf(p.body, "// line $2 \"$1\"$n" | "-- 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 ::L$#::$n", [labl])
|
|
setLen(p.blocks, length)
|
|
|
|
proc moveInto(p: PProc, src: var TCompRes, dest: TCompRes) =
|
|
if src.kind != resNone:
|
|
if dest.kind != resNone:
|
|
p.body.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)
|
|
if not isEmptyType(n.typ):
|
|
r.kind = resVal
|
|
r.res = getTemp(p)
|
|
inc(p.unique)
|
|
var safePoint = ropef("Tmp$1", [toRope(p.unique)])
|
|
appf(p.body,
|
|
"var $1 = {prev: excHandler, exc: null};$nexcHandler = $1;$n" |
|
|
"local $1 = pcall(",
|
|
[safePoint])
|
|
if optStackTrace in p.options: app(p.body, "framePtr = F;" & tnl)
|
|
appf(p.body, "try {$n" | "function()$n")
|
|
var length = sonsLen(n)
|
|
var a: TCompRes
|
|
gen(p, n.sons[0], a)
|
|
moveInto(p, a, r)
|
|
var i = 1
|
|
if p.target == targetJS and length > 1 and n.sons[i].kind == nkExceptBranch:
|
|
appf(p.body, "} catch (EXC) {$n")
|
|
elif p.target == targetLua:
|
|
appf(p.body, "end)$n")
|
|
while i < length and n.sons[i].kind == nkExceptBranch:
|
|
let blen = sonsLen(n.sons[i])
|
|
if blen == 1:
|
|
# general except section:
|
|
if i > 1: appf(p.body, "else {$n" | "else$n")
|
|
gen(p, n.sons[i].sons[0], a)
|
|
moveInto(p, a, r)
|
|
if i > 1: appf(p.body, "}$n" | "end$n")
|
|
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, "||" | " or ")
|
|
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" | "if $1.exc and ($2) then$n",
|
|
[safePoint, orExpr])
|
|
gen(p, n.sons[i].sons[blen - 1], a)
|
|
moveInto(p, a, r)
|
|
appf(p.body, "}$n" | "end$n")
|
|
inc(i)
|
|
if p.target == targetJS:
|
|
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)
|
|
if p.target == targetJS:
|
|
app(p.body, "}" & tnl)
|
|
if p.target == targetLua:
|
|
# we need to repeat the finally block for Lua ...
|
|
if i < length and n.sons[i].kind == nkFinally:
|
|
gen(p, n.sons[i].sons[0], a)
|
|
moveInto(p, a, r)
|
|
|
|
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 genCaseJS(p: PProc, n: PNode, r: var TCompRes) =
|
|
var
|
|
cond, stmt: TCompRes
|
|
genLineDir(p, n)
|
|
gen(p, n.sons[0], cond)
|
|
let stringSwitch = skipTypes(n.sons[0].typ, abstractVar).kind == tyString
|
|
if stringSwitch:
|
|
useMagic(p, "toJSStr")
|
|
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 genCaseLua(p: PProc, n: PNode, r: var TCompRes) =
|
|
var
|
|
cond, stmt: TCompRes
|
|
genLineDir(p, n)
|
|
gen(p, n.sons[0], cond)
|
|
let stringSwitch = skipTypes(n.sons[0].typ, abstractVar).kind == tyString
|
|
if stringSwitch:
|
|
useMagic(p, "eqStr")
|
|
let tmp = getTemp(p)
|
|
appf(p.body, "$1 = $2;$n", [tmp, cond.rdLoc])
|
|
if not isEmptyType(n.typ):
|
|
r.kind = resVal
|
|
r.res = getTemp(p)
|
|
for i in countup(1, sonsLen(n) - 1):
|
|
let it = n.sons[i]
|
|
case it.kind
|
|
of nkOfBranch:
|
|
if i != 1: appf(p.body, "$nelsif ")
|
|
else: appf(p.body, "if ")
|
|
for j in countup(0, sonsLen(it) - 2):
|
|
if j != 0: app(p.body, " or ")
|
|
let e = it.sons[j]
|
|
if e.kind == nkRange:
|
|
var ia, ib: TCompRes
|
|
gen(p, e.sons[0], ia)
|
|
gen(p, e.sons[1], ib)
|
|
appf(p.body, "$1 >= $2 and $1 <= $3", [tmp, ia.rdLoc, ib.rdLoc])
|
|
else:
|
|
if stringSwitch:
|
|
case e.kind
|
|
of nkStrLit..nkTripleStrLit: appf(p.body, "eqStr($1, $2)",
|
|
[tmp, makeJSString(e.strVal)])
|
|
else: internalError(e.info, "jsgen.genCaseStmt: 2")
|
|
else:
|
|
gen(p, e, cond)
|
|
appf(p.body, "$1 == $2", [tmp, cond.rdLoc])
|
|
appf(p.body, " then$n")
|
|
gen(p, lastSon(it), stmt)
|
|
moveInto(p, stmt, r)
|
|
of nkElse:
|
|
appf(p.body, "else$n")
|
|
gen(p, it.sons[0], stmt)
|
|
moveInto(p, stmt, r)
|
|
else: internalError(it.info, "jsgen.genCaseStmt")
|
|
appf(p.body, "$nend$n")
|
|
|
|
proc genBlock(p: PProc, n: PNode, r: var TCompRes) =
|
|
inc(p.unique)
|
|
let idx = len(p.blocks)
|
|
if n.sons[0].kind != nkEmpty:
|
|
# named block?
|
|
if (n.sons[0].kind != nkSym): internalError(n.info, "genBlock")
|
|
var sym = n.sons[0].sym
|
|
sym.loc.k = locOther
|
|
sym.loc.a = idx
|
|
setLen(p.blocks, idx + 1)
|
|
p.blocks[idx].id = - p.unique # negative because it isn't used yet
|
|
let labl = p.unique
|
|
appf(p.body, "L$1: do {$n" | "", labl.toRope)
|
|
gen(p, n.sons[1], r)
|
|
appf(p.body, "} while(false);$n" | "$n::L$#::$n", labl.toRope)
|
|
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" | "goto ::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" | "else$n", [])
|
|
inc(toClose)
|
|
gen(p, it.sons[0], cond)
|
|
appf(p.body, "if ($1) {$n" | "if $# then$n", cond.rdLoc)
|
|
gen(p, it.sons[1], stmt)
|
|
else:
|
|
# else part:
|
|
appf(p.body, "else {$n" | "else$n")
|
|
gen(p, it.sons[0], stmt)
|
|
moveInto(p, stmt, r)
|
|
appf(p.body, "}$n" | "end$n")
|
|
if p.target == targetJS:
|
|
app(p.body, repeatChar(toClose, '}') & tnl)
|
|
else:
|
|
for i in 1..toClose: appf(p.body, "end$n")
|
|
|
|
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)
|
|
var tmp = ropef("Tmp$1", [toRope(p.unique)])
|
|
if mapType(skipTypes(n.sons[1].typ, abstractVar)) == 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" |
|
|
"local $1 = $2; $2 = $3; $3 = $1;$n", [
|
|
tmp, a.address, b.address])
|
|
tmp = tmp2
|
|
appf(p.body, "var $1 = $2; $2 = $3; $3 = $1" |
|
|
"local $1 = $2; $2 = $3; $3 = $1", [tmp, a.res, b.res])
|
|
|
|
proc getFieldPosition(f: PNode): int =
|
|
case f.kind
|
|
of nkIntLit..nkUInt64Lit: result = int(f.intVal)
|
|
of nkSym: result = f.sym.position
|
|
else: internalError(f.info, "genFieldPosition")
|
|
|
|
proc genFieldAddr(p: PProc, n: PNode, r: var TCompRes) =
|
|
var a: TCompRes
|
|
r.typ = etyBaseIndex
|
|
let b = if n.kind == nkHiddenAddr: n.sons[0] else: n
|
|
gen(p, b.sons[0], a)
|
|
if skipTypes(b.sons[0].typ, abstractVarRange).kind == tyTuple:
|
|
r.res = makeJSString("Field" & $getFieldPosition(b.sons[1]))
|
|
else:
|
|
if b.sons[1].kind != nkSym: internalError(b.sons[1].info, "genFieldAddr")
|
|
var f = b.sons[1].sym
|
|
if f.loc.r == nil: f.loc.r = mangleName(f)
|
|
r.res = makeJSString(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
|
|
{sfImportc, sfInfixCall} * s.flags != {}:
|
|
discard
|
|
elif s.kind == skMethod and s.getBody.kind == nkEmpty:
|
|
# we cannot produce code for the dispatcher yet:
|
|
discard
|
|
elif sfForward in s.flags:
|
|
p.g.forwarded.add(s)
|
|
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" | "Field$# = $#", 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" | "m_type = $#", [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]" | "{nil, 0}", indirect)
|
|
else:
|
|
result = putToSeq("null" | "nil", indirect)
|
|
of tySequence, tyString, tyCString, tyPointer, tyProc:
|
|
result = putToSeq("null" | "nil", indirect)
|
|
else:
|
|
internalError("createVar: " & $t.kind)
|
|
result = nil
|
|
|
|
proc 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" | "local $1 = $2;$n",
|
|
[mangleName(v), createVar(p, v.typ, isIndirect(v))])
|
|
else:
|
|
discard mangleName(v)
|
|
gen(p, n, a)
|
|
case mapType(v.typ)
|
|
of etyObject:
|
|
if needsNoCopy(n):
|
|
s = a.res
|
|
else:
|
|
useMagic(p, "NimCopy")
|
|
s = 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" | "local $1 = {$2, $3};$n",
|
|
[v.loc.r, a.address, a.res])
|
|
else:
|
|
appf(p.body, "var $1 = $2; var $1_Idx = $3;$n" |
|
|
"local $1 = $2; local $1_Idx = $3;$n", [
|
|
v.loc.r, a.address, a.res])
|
|
return
|
|
else:
|
|
s = a.res
|
|
if isIndirect(v):
|
|
appf(p.body, "var $1 = [$2];$n" | "local $1 = {$2};$n", [v.loc.r, s])
|
|
else:
|
|
appf(p.body, "var $1 = $2;$n" | "local $1 = $2;$n", [v.loc.r, s])
|
|
|
|
proc genVarStmt(p: PProc, n: PNode) =
|
|
for i in countup(0, sonsLen(n) - 1):
|
|
var a = n.sons[i]
|
|
if a.kind == nkCommentStmt: 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)" | "toBool($#)")
|
|
else: internalError(n.info, "genOrd")
|
|
|
|
proc genConStrStr(p: PProc, n: PNode, r: var TCompRes) =
|
|
var a: TCompRes
|
|
|
|
gen(p, n.sons[1], a)
|
|
r.kind = resExpr
|
|
if skipTypes(n.sons[1].typ, abstractVarRange).kind == tyChar:
|
|
r.res.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 optOverflowCheck notin p.options: binaryExpr(p, n, r, "", "$1 += $2")
|
|
else: binaryExpr(p, n, r, "addInt", "$1 = addInt($1, $2)")
|
|
of ast.mDec:
|
|
if optOverflowCheck notin p.options: binaryExpr(p, n, r, "", "$1 -= $2")
|
|
else: binaryExpr(p, n, r, "subInt", "$1 = subInt($1, $2)")
|
|
of mSetLengthStr: binaryExpr(p, n, r, "", "$1.length = ($2)-1")
|
|
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)
|
|
of mCopyStr: binaryExpr(p, n, r, "", "($1.slice($2,-1))")
|
|
of mCopyStrLast: ternaryExpr(p, n, r, "", "($1.slice($2, ($3)+1).concat(0))")
|
|
of mNewString: unaryExpr(p, n, r, "mnewString", "mnewString($1)")
|
|
of mNewStringOfCap: unaryExpr(p, n, r, "mnewString", "mnewString(0)")
|
|
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$#: $#" | "Field$# = $#", [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(1, 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, "$#: $#" | "$# = $#" , [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)" | "toBool($#)", [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)
|
|
appf(p.body, "break BeforeRet;$n" | "goto ::BeforeRet::;$n")
|
|
|
|
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" |
|
|
"local F={procname=$#,prev=framePtr,filename=$#,line=0};$n") &
|
|
"framePtr = F;$n", [
|
|
makeJSString(prc.owner.name.s & '.' & prc.name.s),
|
|
makeJSString(toFilename(prc.info))])
|
|
else:
|
|
result = nil
|
|
if p.beforeRetNeeded:
|
|
appf(result, "BeforeRet: do {$n$1} while (false); $n" |
|
|
"$#;::BeforeRet::$n", [p.body])
|
|
else:
|
|
app(result, p.body)
|
|
if prc.typ.callConv == ccSysCall and p.target == targetJS:
|
|
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" | "local $# = $#;$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" |
|
|
"function $#($#) $n$#$#$#$#$nend$n",
|
|
[name, header, p.locals, resultAsgn,
|
|
genProcBody(p, prc), returnStmt])
|
|
#if gVerbosity >= 3:
|
|
# echo "END generated code for: " & prc.name.s
|
|
|
|
proc genStmt(p: PProc, n: PNode) =
|
|
var r: TCompRes
|
|
gen(p, n, r)
|
|
if r.res != nil: 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):
|
|
discard
|
|
elif mapType(n.typ) == etyBaseIndex:
|
|
r.typ = etyBaseIndex
|
|
r.address = toRope"null" | toRope"nil"
|
|
r.res = toRope"0"
|
|
else:
|
|
r.res = toRope"null" | toRope"nil"
|
|
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: discard
|
|
of nkLambdaKinds:
|
|
let s = n.sons[namePos].sym
|
|
discard mangleName(s)
|
|
r.res = s.loc.r
|
|
if lfNoDecl in s.loc.flags or s.magic != mNone: discard
|
|
elif not p.g.generatedSyms.containsOrIncl(s.id):
|
|
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: discard
|
|
of nkForStmt, nkParForStmt:
|
|
internalError(n.info, "for statement not eliminated")
|
|
of nkCaseStmt:
|
|
if p.target == targetJS: genCaseJS(p, n, r)
|
|
else: genCaseLua(p, n, r)
|
|
of nkReturnStmt: genReturnStmt(p, n)
|
|
of nkBreakStmt: genBreakStmt(p, n)
|
|
of nkAsgn: genAsgn(p, n)
|
|
of nkFastAsgn: genFastAsgn(p, n)
|
|
of nkDiscardStmt:
|
|
if n.sons[0].kind != nkEmpty:
|
|
genLineDir(p, n)
|
|
gen(p, n.sons[0], r)
|
|
of nkAsmStmt: genAsmStmt(p, n)
|
|
of nkTryStmt: genTry(p, n, r)
|
|
of nkRaiseStmt: genRaiseStmt(p, n)
|
|
of nkTypeSection, nkCommentStmt, nkIteratorDef, nkIncludeStmt,
|
|
nkImportStmt, nkImportExceptStmt, nkExportStmt, nkExportExceptStmt,
|
|
nkFromStmt, nkTemplateDef, nkMacroDef, nkPragma: discard
|
|
of nkProcDef, nkMethodDef, nkConverterDef:
|
|
var s = n.sons[namePos].sym
|
|
if {sfExportc, sfCompilerProc} * s.flags == {sfExportc}:
|
|
genSym(p, n.sons[namePos], r)
|
|
r.res = nil
|
|
of nkGotoState, nkState:
|
|
internalError(n.info, "first class iterators not implemented")
|
|
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) 2014 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 wholeCode*(m: BModule): PRope =
|
|
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))
|
|
|
|
result = con(globals.typeInfo, globals.code)
|
|
|
|
proc myClose(b: PPassContext, n: PNode): PNode =
|
|
if passes.skipCodegen(n): return n
|
|
result = myProcess(b, n)
|
|
var m = BModule(b)
|
|
if sfMainModule in m.module.flags:
|
|
let code = wholeCode(m)
|
|
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)
|