# # # The Nimrod Compiler # (c) Copyright 2010 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, nhashes, trees, platform, magicsys, extccomp, options, nversion, nimsets, msgs, crc, bitsets, idents, lists, types, os, times, ropes, math, passes, ccgutils, wordrecg, rnimsyn, rodread, rodutils 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) IntSetInit(result.typeInfoGenerated) 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 IntSetContainsOrIncl(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] != nil: 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] != nil: # 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] != nil: # 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: 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: 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: result = createVar(p, typ.sons[0], 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: 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 == nil: 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 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: liMessage(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] != nil): 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: 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] == nil): var prc = n.sons[namePos].sym if (n.sons[codePos] != nil) 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) 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 = 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 = 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