# # # 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