diff options
Diffstat (limited to 'nim/ecmasgen.pas')
-rw-r--r-- | nim/ecmasgen.pas | 1869 |
1 files changed, 1869 insertions, 0 deletions
diff --git a/nim/ecmasgen.pas b/nim/ecmasgen.pas new file mode 100644 index 000000000..53ab4f069 --- /dev/null +++ b/nim/ecmasgen.pas @@ -0,0 +1,1869 @@ +// +// +// The Nimrod Compiler +// (c) Copyright 2008 Andreas Rumpf +// +// See the file "copying.txt", included in this +// distribution, for details about the copyright. +// +unit ecmasgen; + +// This is the EMCAScript (also known as JavaScript) code generator. +// **Invariant: each expression only occurs once in the generated +// code!** + +interface + +{$include 'config.inc'} + +uses + nsystem, ast, astalgo, strutils, hashes, trees, platform, magicsys, + extccomp, options, nversion, nimsets, msgs, crc, bitsets, idents, + lists, types, nos, ntime, ropes, nmath, backends, ccgutils, wordrecg, rnimsyn; + +function EcmasBackend(b: PBackend; module: PSym; + const filename: string): PBackend; + +implementation + +type + TEcmasGen = object(TBackend) + end; + BModule = ^TEcmasGen; + + TEcmasTypeKind = ( + 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 = record + kind: TEcmasTypeKind; + com: PRope; // computation part + // address if this is a (address, index)-tuple + res: PRope; // result part; index if this is a (address, index)-tuple + end; + + TBlock = record + 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 + end; + + TGlobals = record + typeInfo, code: PRope; + typeInfoGenerated: TIntSet; + end; + PGlobals = ^TGlobals; + + TProc = record + procDef: PNode; + prc: PSym; + data: PRope; + options: TOptions; + module: BModule; + globals: PGlobals; + BeforeRetNeeded: bool; + nestedTryStmts: int; + unique: int; + blocks: array of TBlock; + end; + +function newGlobals(): PGlobals; +begin + new(result); +{@ignore} fillChar(result^, sizeof(result^), 0); {@emit} + IntSetInit(result.typeInfoGenerated); +end; + +procedure initCompRes(var r: TCompRes); +begin + r.com := nil; r.res := nil; r.kind := etyNone; +end; + +procedure initProc(var p: TProc; globals: PGlobals; module: BModule; + procDef: PNode; options: TOptions); +begin +{@ignore} + fillChar(p, sizeof(p), 0); +{@emit + p.blocks := [];} + p.options := options; + p.module := module; + p.procDef := procDef; + p.globals := globals; + if procDef <> nil then p.prc := procDef.sons[namePos].sym; +end; + +const + MappedToObject = {@set}[tyObject, tyArray, tyArrayConstr, tyTuple, + tyEmptySet, tyOpenArray, tySet, tyVar, + tyRef, tyPtr]; + +function mapType(typ: PType): TEcmasTypeKind; +begin + case skipGeneric(typ).kind of + tyVar, tyRef, tyPtr: begin + if typ.sons[0].kind in mappedToObject then + result := etyObject + else + result := etyBaseIndex + end; + tyPointer: begin + // treat a tyPointer like a typed pointer to an array of bytes + result := etyInt; + end; + tyRange: result := mapType(typ.sons[0]); + tyInt..tyInt64, tyEnum, tyAnyEnum, tyChar: + result := etyInt; + tyBool: result := etyBool; + tyFloat..tyFloat128: result := etyFloat; + tySet: begin + result := etyObject // map a set to a table + end; + tyString, tySequence: + result := etyInt; // little hack to get the right semantics + tyObject, tyArray, tyArrayConstr, tyTuple, tyEmptySet, tyOpenArray: + result := etyObject; + tyNil: result := etyNull; + tyGenericInst, tyGenericParam, tyGeneric, tyNone, tyForward: + result := etyNone; + tyProc: result := etyProc; + tyCString: result := etyString; + end +end; + +function mangle(const name: string): string; +var + i: int; +begin + result := ''; + for i := strStart to length(name) + strStart-1 do begin + case name[i] of + 'A'..'Z': addChar(result, chr(ord(name[i]) - ord('A') + ord('a'))); + '_': begin end; + 'a'..'z', '0'..'9': addChar(result, name[i]); + else result := result +{&} 'X' +{&} toHex(ord(name[i]), 2); + end + end +end; + +function mangleName(s: PSym): PRope; +begin + result := s.loc.r; + if result = nil then begin + result := toRope(mangle(s.name.s)); + app(result, '_'+''); + app(result, toRope(s.id)); + s.loc.r := result; + end +end; + +// ----------------------- type information ---------------------------------- + +function genTypeInfo(var p: TProc; typ: PType): PRope; forward; + +function genObjectFields(var p: TProc; typ: PType; n: PNode): PRope; +var + s, u: PRope; + len, i, j: int; + field: PSym; + b: PNode; +begin + result := nil; + case n.kind of + nkRecList: begin + len := sonsLen(n); + if len = 1 then // generates more compact code! + result := genObjectFields(p, typ, n.sons[0]) + else begin + s := nil; + for i := 0 to len-1 do begin + if i > 0 then app(s, ', ' + tnl); + app(s, genObjectFields(p, typ, n.sons[i])); + end; + result := ropef('{kind: 2, len: $1, offset: 0, ' + + 'typ: null, name: null, sons: [$2]}', [toRope(len), s]); + end + end; + nkSym: begin + 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)]); + end; + nkRecCase: begin + len := sonsLen(n); + if (n.sons[0].kind <> nkSym) then + InternalError(n.info, 'genObjectFields'); + field := n.sons[0].sym; + s := genTypeInfo(p, field.typ); + for i := 1 to len-1 do begin + b := n.sons[i]; // branch + u := nil; + case b.kind of + nkOfBranch: begin + if sonsLen(b) < 2 then + internalError(b.info, 'genObjectFields; nkOfBranch broken'); + for j := 0 to sonsLen(b)-2 do begin + if u <> nil then app(u, ', '); + if b.sons[j].kind = nkRange then begin + appf(u, '[$1, $2]', [toRope(getOrdValue(b.sons[j].sons[0])), + toRope(getOrdValue(b.sons[j].sons[1]))]); + end + else + app(u, toRope(getOrdValue(b.sons[j]))) + end + end; + nkElse: u := toRope(lengthOrd(field.typ)); + else internalError(n.info, 'genObjectFields(nkRecCase)'); + end; + if result <> nil then app(result, ', ' + tnl); + appf(result, '[SetConstr($1), $2]', + [u, genObjectFields(p, typ, lastSon(b))]); + end; + 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]); + end; + else internalError(n.info, 'genObjectFields'); + end +end; + +procedure genObjectInfo(var p: TProc; typ: PType; name: PRope); +var + s: PRope; +begin + 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) then begin + appf(p.globals.typeInfo, '$1.base = $2;$n', + [name, genTypeInfo(p, typ.sons[0])]); + end +end; + +procedure genEnumInfo(var p: TProc; typ: PType; name: PRope); +var + s, n: PRope; + len, i: int; + field: PSym; +begin + len := sonsLen(typ.n); + s := nil; + for i := 0 to len-1 do begin + if (typ.n.sons[i].kind <> nkSym) then + InternalError(typ.n.info, 'genEnumInfo'); + field := typ.n.sons[i].sym; + if i > 0 then 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)]); + end; + n := ropef('var NNI$1 = {kind: 2, offset: 0, typ: null, ' + + 'name: null, len: $2, sons: [$3]};$n', + [toRope(typ.id), toRope(len), 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 then begin + appf(p.globals.typeInfo, '$1.base = $2;$n', + [name, genTypeInfo(p, typ.sons[0])]); + end; +end; + +function genTypeInfo(var p: TProc; typ: PType): PRope; +var + t: PType; + s: PRope; +begin + t := typ; + if t.kind = tyGenericInst then t := lastSon(t); + result := ropef('NTI$1', [toRope(t.id)]); + if IntSetContainsOrIncl(p.globals.TypeInfoGenerated, t.id) then exit; + case t.kind of + tyPointer, tyProc, tyBool, tyChar, tyCString, tyString, + tyInt..tyFloat128: begin + 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); + end; + tyVar, tyRef, tyPtr, tySequence, tyRange, tySet: begin + 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])]); + end; + tyArrayConstr, tyArray: begin + 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])]); + end; + tyEnum: genEnumInfo(p, t, result); + tyObject, tyTuple: genObjectInfo(p, t, result); + else InternalError('genTypeInfo(' + typekindToStr[t.kind] + ')'); + end +end; + +// --------------------------------------------------------------------------- + +procedure gen(var p: TProc; n: PNode; var r: TCompRes); forward; +procedure genStmt(var p: TProc; n: PNode; var r: TCompRes); forward; + +procedure useMagic(var p: TProc; const ident: string); +begin + // to implement +end; + +function mergeExpr(a, b: PRope): PRope; overload; +begin + if (a <> nil) then begin + if b <> nil then result := ropef('($1, $2)', [a, b]) + else result := a + end + else result := b +end; + +function mergeExpr(const r: TCompRes): PRope; overload; +begin + result := mergeExpr(r.com, r.res); +end; + +function mergeStmt(const r: TCompRes): PRope; +begin + if r.res = nil then result := r.com + else if r.com = nil then result := r.res + else result := ropef('$1$2', [r.com, r.res]) +end; + +procedure genAnd(var p: TProc; a, b: PNode; var r: TCompRes); +var + x, y: TCompRes; +begin + gen(p, a, x); + gen(p, b, y); + r.res := ropef('($1 && $2)', [mergeExpr(x), mergeExpr(y)]) +end; + +procedure genOr(var p: TProc; a, b: PNode; var r: TCompRes); +var + x, y: TCompRes; +begin + gen(p, a, x); + gen(p, b, y); + r.res := ropef('($1 || $2)', [mergeExpr(x), mergeExpr(y)]) +end; + +type + TMagicFrmt = array [0..3] of string; + +const + // magic checked op; magic unchecked op; checked op; unchecked op + ops: array [mAddi..mStrToStr] of 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)'), // 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 + ('', '', '($1 + $2)', '($1 + $2)'), // AddF64 + ('', '', '($1 - $2)', '($1 - $2)'), // SubF64 + ('', '', '($1 * $2)', '($1 * $2)'), // MulF64 + ('', '', '($1 / $2)', '($1 / $2)'), // DivF64 + ('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') + ); + +procedure binaryExpr(var p: TProc; n: PNode; var r: TCompRes; + const magic, frmt: string); +var + x, y: TCompRes; +begin + if magic <> '' then 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); +end; + +procedure binaryStmt(var p: TProc; n: PNode; var r: TCompRes; + const magic, frmt: string); +var + x, y: TCompRes; +begin + if magic <> '' then useMagic(p, magic); + gen(p, n.sons[1], x); + gen(p, n.sons[2], y); + if x.com <> nil then appf(r.com, '$1;$n', [x.com]); + if y.com <> nil then appf(r.com, '$1;$n', [y.com]); + appf(r.com, frmt, [x.res, y.res]); +end; + +procedure unaryExpr(var p: TProc; n: PNode; var r: TCompRes; + const magic, frmt: string); +begin + if magic <> '' then useMagic(p, magic); + gen(p, n.sons[1], r); + r.res := ropef(frmt, [r.res]); +end; + +procedure arith(var p: TProc; n: PNode; var r: TCompRes; op: TMagic); +var + x, y: TCompRes; + i: int; +begin + if optOverflowCheck in p.options then i := 0 else i := 1; + useMagic(p, ops[op][i]); + if sonsLen(n) > 2 then begin + 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); + end + else begin + gen(p, n.sons[1], r); + r.res := ropef(ops[op][i+2], [r.res]) + end +end; + +procedure genLineDir(var p: TProc; n: PNode; var r: TCompRes); +var + line: int; +begin + line := toLinenumber(n.info); + if optLineDir in p.Options then // pretty useless, but better than nothing + 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)) then begin + useMagic(p, 'endb'); + appf(r.com, 'endb($1);$n', [toRope(line)]) + end + else if ([optLineTrace, optStackTrace] * p.Options = + [optLineTrace, optStackTrace]) and ((p.prc = nil) or + not (sfPure in p.prc.flags)) then + appf(r.com, 'F.line = $1;$n', [toRope(line)]) +end; + +procedure finishTryStmt(var p: TProc; var r: TCompRes; howMany: int); +var + i: int; +begin + for i := 1 to howMany do + app(r.com, 'excHandler = excHandler.prev;' + tnl); +end; + +procedure genWhileStmt(var p: TProc; n: PNode; var r: TCompRes); +var + cond, stmt: TCompRes; + len, labl: int; +begin + genLineDir(p, n, r); + inc(p.unique); + len := length(p.blocks); + setLength(p.blocks, len+1); + p.blocks[len].id := -p.unique; + p.blocks[len].nestedTryStmts := p.nestedTryStmts; + labl := p.unique; + gen(p, n.sons[0], cond); + genStmt(p, n.sons[1], stmt); + if p.blocks[len].id > 0 then + 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)]); + setLength(p.blocks, len); +end; + +procedure genTryStmt(var p: TProc; n: PNode; var r: 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, j, len, blen: int; + safePoint, orExpr, epart: PRope; + a: TCompRes; +begin + 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 then + app(r.com, 'framePtr = F;' + tnl); + app(r.com, 'try {' + tnl); + len := sonsLen(n); + inc(p.nestedTryStmts); + genStmt(p, n.sons[0], a); + app(r.com, mergeStmt(a)); + i := 1; + epart := nil; + while (i < len) and (n.sons[i].kind = nkExceptBranch) do begin + blen := sonsLen(n.sons[i]); + if blen = 1 then begin + // general except section: + if i > 1 then app(epart, 'else {' + tnl); + genStmt(p, n.sons[i].sons[0], a); + app(epart, mergeStmt(a)); + if i > 1 then app(epart, '}' + tnl); + end + else begin + orExpr := nil; + for j := 0 to blen-2 do begin + if (n.sons[i].sons[j].kind <> nkType) then + InternalError(n.info, 'genTryStmt'); + if orExpr <> nil then app(orExpr, '||'); + appf(orExpr, '($1.exc.m_type == $2)', + [safePoint, genTypeInfo(p, n.sons[i].sons[j].typ)]) + end; + if i > 1 then 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)]); + end; + inc(i) + end; + if epart <> nil then + 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 < len) and (n.sons[i].kind = nkFinally) then begin + genStmt(p, n.sons[i].sons[0], a); + app(r.com, mergeStmt(a)); + end; + app(r.com, '}' + tnl); +end; + +procedure genRaiseStmt(var p: TProc; n: PNode; var r: TCompRes); +var + a: TCompRes; + typ: PType; +begin + genLineDir(p, n, r); + if n.sons[0] <> nil then begin + gen(p, n.sons[0], a); + if a.com <> nil then appf(r.com, '$1;$n', [a.com]); + typ := skipPtrsGeneric(n.sons[0].typ); + useMagic(p, 'raiseException'); + appf(r.com, 'raiseException($1, $2);$n', + [a.res, makeCString(typ.sym.name.s)]); + end + else begin + useMagic(p, 'reraiseException'); + app(r.com, 'reraiseException();' + tnl); + end +end; + +procedure genCaseStmt(var p: TProc; n: PNode; var r: TCompRes); +var + cond, stmt: TCompRes; + i, j: int; + it, e, v: PNode; + stringSwitch: bool; +begin + genLineDir(p, n, r); + gen(p, n.sons[0], cond); + if cond.com <> nil then + appf(r.com, '$1;$n', [cond.com]); + stringSwitch := skipVarGeneric(n.sons[0].typ).kind = tyString; + if stringSwitch then begin + useMagic(p, 'toEcmaStr'); + appf(r.com, 'switch (toEcmaStr($1)) {$n', [cond.res]) + end + else + appf(r.com, 'switch ($1) {$n', [cond.res]); + for i := 1 to sonsLen(n)-1 do begin + it := n.sons[i]; + case it.kind of + nkOfBranch: begin + for j := 0 to sonsLen(it)-2 do begin + e := it.sons[j]; + if e.kind = nkRange then begin + v := copyNode(e.sons[0]); + while (v.intVal <= e.sons[1].intVal) do begin + gen(p, v, cond); + if cond.com <> nil then + internalError(v.info, 'ecmasgen.genCaseStmt'); + appf(r.com, 'case $1: ', [cond.res]); + Inc(v.intVal) + end + end + else begin + gen(p, e, cond); + if cond.com <> nil then + internalError(e.info, 'ecmasgen.genCaseStmt'); + if stringSwitch then begin + case e.kind of + nkStrLit..nkTripleStrLit: + appf(r.com, 'case $1: ', [makeCString(e.strVal)]); + else InternalError(e.info, 'ecmasgen.genCaseStmt: 2'); + end + end + else + appf(r.com, 'case $1: ', [cond.res]); + end + end; + genStmt(p, lastSon(it), stmt); + appf(r.com, '$n$1break;$n', [mergeStmt(stmt)]); + end; + nkElse: begin + genStmt(p, it.sons[0], stmt); + appf(r.com, 'default: $n$1break;$n', [mergeStmt(stmt)]); + end + else internalError(it.info, 'ecmasgen.genCaseStmt') + end + end; + appf(r.com, '}$n', []); +end; + +procedure genStmtListExpr(var p: TProc; n: PNode; var r: TCompRes); forward; + +procedure genBlock(var p: TProc; n: PNode; var r: TCompRes); +var + idx, labl: int; + sym: PSym; +begin + inc(p.unique); + idx := length(p.blocks); + if n.sons[0] <> nil then begin // named block? + if (n.sons[0].kind <> nkSym) then InternalError(n.info, 'genBlock'); + sym := n.sons[0].sym; + sym.loc.k := locOther; + sym.loc.a := idx + end; + setLength(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 then genStmtListExpr(p, n.sons[1], r) + else genStmt(p, n.sons[1], r); + if p.blocks[idx].id > 0 then begin // label has been used: + r.com := ropef('L$1: do {$n$2} while(false);$n', + [toRope(labl), r.com]); + end; + setLength(p.blocks, idx) +end; + +procedure genBreakStmt(var p: TProc; n: PNode; var r: TCompRes); +var + idx: int; + sym: PSym; +begin + genLineDir(p, n, r); + idx := length(p.blocks)-1; + if n.sons[0] <> nil then begin // named break? + assert(n.sons[0].kind = nkSym); + sym := n.sons[0].sym; + assert(sym.loc.k = locOther); + idx := sym.loc.a + end; + 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)]) +end; + +procedure genAsmStmt(var p: TProc; n: PNode; var r: TCompRes); +var + i: int; +begin + genLineDir(p, n, r); + assert(n.kind = nkAsmStmt); + for i := 0 to sonsLen(n)-1 do begin + case n.sons[i].Kind of + nkStrLit..nkTripleStrLit: app(r.com, n.sons[i].strVal); + nkSym: app(r.com, mangleName(n.sons[i].sym)); + else InternalError(n.sons[i].info, 'ecmasgen: genAsmStmt()') + end + end +end; + +procedure genIfStmt(var p: TProc; n: PNode; var r: TCompRes); +var + i, toClose: int; + cond, stmt: TCompRes; + it: PNode; +begin + toClose := 0; + for i := 0 to sonsLen(n)-1 do begin + it := n.sons[i]; + if sonsLen(it) <> 1 then begin + gen(p, it.sons[0], cond); + genStmt(p, it.sons[1], stmt); + if i > 0 then begin appf(r.com, 'else {$n', []); inc(toClose) end; + if cond.com <> nil then appf(r.com, '$1;$n', [cond.com]); + appf(r.com, 'if ($1) {$n$2}', [cond.res, mergeStmt(stmt)]); + end + else begin + // else part: + genStmt(p, it.sons[0], stmt); + appf(r.com, 'else {$n$1}$n', [mergeStmt(stmt)]); + end + end; + app(r.com, repeatChar(toClose, '}')+{&}tnl); +end; + +procedure genIfExpr(var p: TProc; n: PNode; var r: TCompRes); +var + i, toClose: int; + cond, stmt: TCompRes; + it: PNode; +begin + toClose := 0; + for i := 0 to sonsLen(n)-1 do begin + it := n.sons[i]; + if sonsLen(it) <> 1 then begin + gen(p, it.sons[0], cond); + gen(p, it.sons[1], stmt); + if i > 0 then begin app(r.res, ': ('); inc(toClose); end; + r.com := mergeExpr(r.com, cond.com); + r.com := mergeExpr(r.com, stmt.com); + appf(r.res, '($1) ? ($2)', [cond.res, stmt.res]); + end + else begin + // else part: + gen(p, it.sons[0], stmt); + r.com := mergeExpr(r.com, stmt.com); + appf(r.res, ': ($1)', [stmt.res]); + end + end; + app(r.res, repeatChar(toClose, ')')); +end; + +function generateHeader(var p: TProc; typ: PType): PRope; +var + i: int; + param: PSym; + name: PRope; +begin + result := nil; + for i := 1 to sonsLen(typ.n)-1 do begin + if result <> nil then 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 then begin + app(result, ', '); + app(result, name); + app(result, '_Idx'); + end + end +end; + +const + nodeKindsNeedNoCopy = {@set}[nkCharLit..nkInt64Lit, nkStrLit..nkTripleStrLit, + nkFloatLit..nkFloat64Lit, + nkCurly, nkPar, + nkStringToCString, nkCStringToString, + nkCall, nkHiddenCallConv]; + +function needsNoCopy(y: PNode): bool; +begin + result := (y.kind in nodeKindsNeedNoCopy) + or (skipGeneric(y.typ).kind in [tyRef, tyPtr, tyVar]) +end; + +procedure genAsgnAux(var p: TProc; x, y: PNode; var r: TCompRes); +var + a, b: TCompRes; +begin + gen(p, x, a); + gen(p, y, b); + case mapType(x.typ) of + etyObject: begin + if a.com <> nil then appf(r.com, '$1;$n', [a.com]); + if b.com <> nil then appf(r.com, '$1;$n', [b.com]); + if needsNoCopy(y) then + appf(r.com, '$1 = $2;$n', [a.res, b.res]) + else begin + useMagic(p, 'NimCopy'); + appf(r.com, '$1 = NimCopy($2, $3);$n', + [a.res, b.res, genTypeInfo(p, y.typ)]); + end + end; + etyBaseIndex: begin + if (a.kind <> etyBaseIndex) or (b.kind <> etyBaseIndex) then + internalError(x.info, 'genAsgn'); + appf(r.com, '$1 = $2; $3 = $4;$n', [a.com, b.com, a.res, b.res]); + end + else begin + if a.com <> nil then appf(r.com, '$1;$n', [a.com]); + if b.com <> nil then appf(r.com, '$1;$n', [b.com]); + appf(r.com, '$1 = $2;$n', [a.res, b.res]); + end + end +end; + +procedure genAsgn(var p: TProc; n: PNode; var r: TCompRes); +begin + genLineDir(p, n, r); + genAsgnAux(p, n.sons[0], n.sons[1], r); +end; + +procedure genSwap(var p: TProc; n: PNode; var r: TCompRes); +var + a, b: TCompRes; + tmp, tmp2: PRope; +begin + gen(p, n.sons[1], a); + gen(p, n.sons[2], b); + inc(p.unique); + tmp := ropef('Tmp$1', [toRope(p.unique)]); + case mapType(n.sons[1].typ) of + etyBaseIndex: begin + inc(p.unique); + tmp2 := ropef('Tmp$1', [toRope(p.unique)]); + if (a.kind <> etyBaseIndex) or (b.kind <> etyBaseIndex) then + 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]); + end + else begin + if a.com <> nil then appf(r.com, '$1;$n', [a.com]); + if b.com <> nil then appf(r.com, '$1;$n', [b.com]); + appf(r.com, 'var $1 = $2; $2 = $3; $3 = $1', [tmp, a.res, b.res]); + end + end +end; + +procedure genFieldAddr(var p: TProc; n: PNode; var r: TCompRes); +var + a: TCompRes; + f: PSym; +begin + r.kind := etyBaseIndex; + gen(p, n.sons[0], a); + if n.sons[1].kind <> nkSym then + InternalError(n.sons[1].info, 'genFieldAddr'); + f := n.sons[1].sym; + if f.loc.r = nil then f.loc.r := mangleName(f); + r.res := makeCString(ropeToStr(f.loc.r)); + r.com := mergeExpr(a); +end; + +procedure genFieldAccess(var p: TProc; n: PNode; var r: TCompRes); +var + f: PSym; +begin + r.kind := etyNone; + gen(p, n.sons[0], r); + if n.sons[1].kind <> nkSym then + InternalError(n.sons[1].info, 'genFieldAddr'); + f := n.sons[1].sym; + if f.loc.r = nil then f.loc.r := mangleName(f); + r.res := ropef('$1.$2', [r.res, f.loc.r]); +end; + +procedure genCheckedFieldAddr(var p: TProc; n: PNode; var r: TCompRes); +begin + genFieldAddr(p, n.sons[0], r); // XXX +end; + +procedure genCheckedFieldAccess(var p: TProc; n: PNode; var r: TCompRes); +begin + genFieldAccess(p, n.sons[0], r); // XXX +end; + +procedure genArrayAddr(var p: TProc; n: PNode; var r: TCompRes); +var + a, b: TCompRes; + first: biggestInt; + typ: PType; +begin + r.kind := etyBaseIndex; + gen(p, n.sons[0], a); + gen(p, n.sons[1], b); + r.com := mergeExpr(a); + typ := skipPtrsGeneric(n.sons[0].typ); + if typ.kind in [tyArray, tyArrayConstr] then first := FirstOrd(typ.sons[0]) + else first := 0; + if (optBoundsCheck in p.options) and not isConstExpr(n.sons[1]) then begin + useMagic(p, 'chckIndx'); + b.res := ropef('chckIndx($1, $2, $3.length)-$2', + [b.res, toRope(first), a.res]); + // XXX: BUG: a.res evaluated twice! + end + else if first <> 0 then begin + b.res := ropef('($1)-$2', [b.res, toRope(first)]); + end; + r.res := mergeExpr(b); +end; + +procedure genArrayAccess(var p: TProc; n: PNode; var r: TCompRes); +begin + genArrayAddr(p, n, r); + r.kind := etyNone; + r.res := ropef('$1[$2]', [r.com, r.res]); + r.com := nil; +end; + +(* +type + TMyList = record + x: seq[ptr ptr int] + L: int + next: ptr TMyList + +proc myAdd(head: var ptr TMyList, item: ptr TMyList) = + item.next = head + head = item + +proc changeInt(i: var int) = inc(i) + +proc f(p: ptr TMyList, x: ptr ptr int) = + add p.x, x + p.next = nil + changeInt(p.L) + +*) + +procedure genAddr(var p: TProc; n: PNode; var r: TCompRes); +var + s: PSym; +begin + case n.sons[0].kind of + nkSym: begin + s := n.sons[0].sym; + if s.loc.r = nil then InternalError(n.info, 'genAddr: 3'); + case s.kind of + skVar: begin + if mapType(n.typ) = etyObject then begin + // make addr() a no-op: + r.kind := etyNone; + r.res := s.loc.r; + r.com := nil; + end + else if sfGlobal in s.flags then begin + // globals are always indirect accessible + r.kind := etyBaseIndex; + r.com := toRope('Globals'); + r.res := makeCString(ropeToStr(s.loc.r)); + end + else if sfAddrTaken in s.flags then begin + r.kind := etyBaseIndex; + r.com := s.loc.r; + r.res := toRope('0'+''); + end + else InternalError(n.info, 'genAddr: 4'); + end; + else InternalError(n.info, 'genAddr: 2'); + end; + end; + nkCheckedFieldExpr: genCheckedFieldAddr(p, n, r); + nkDotExpr, nkQualified: genFieldAddr(p, n, r); + nkBracketExpr: genArrayAddr(p, n, r); + else InternalError(n.info, 'genAddr'); + end +end; + +procedure genSym(var p: TProc; n: PNode; var r: TCompRes); +var + s: PSym; + k: TEcmasTypeKind; +begin + s := n.sym; + if s.loc.r = nil then + InternalError(n.info, 'symbol has no generated name: ' + s.name.s); + case s.kind of + skVar, skParam, skTemp: begin + k := mapType(s.typ); + if k = etyBaseIndex then begin + r.kind := etyBaseIndex; + if [sfAddrTaken, sfGlobal] * s.flags <> [] then begin + r.com := ropef('$1[0]', [s.loc.r]); + r.res := ropef('$1[1]', [s.loc.r]); + end + else begin + r.com := s.loc.r; + r.res := con(s.loc.r, '_Idx'); + end + end + else if (k <> etyObject) and (sfAddrTaken in s.flags) then + r.res := ropef('$1[0]', [s.loc.r]) + else + r.res := s.loc.r + end + else r.res := s.loc.r; + end +end; + +procedure genDeref(var p: TProc; n: PNode; var r: TCompRes); +var + a: TCompRes; +begin + if mapType(n.sons[0].typ) = etyObject then + gen(p, n.sons[0], r) + else begin + gen(p, n.sons[0], a); + if a.kind <> etyBaseIndex then InternalError(n.info, 'genDeref'); + r.res := ropef('$1[$2]', [a.com, a.res]) + end +end; + +procedure genCall(var p: TProc; n: PNode; var r: TCompRes); +var + a: TCompRes; + i: int; +begin + gen(p, n.sons[0], r); + app(r.res, '('+''); + for i := 1 to sonsLen(n)-1 do begin + if i > 1 then app(r.res, ', '); + gen(p, n.sons[i], a); + if a.kind = etyBaseIndex then begin + app(r.res, a.com); + app(r.res, ', '); + app(r.res, a.res); + end + else + app(r.res, mergeExpr(a)); + end; + app(r.res, ')'+''); +end; + +function putToSeq(const s: string; indirect: bool): PRope; +begin + result := toRope(s); + if indirect then result := ropef('[$1]', [result]) +end; + +function createVar(var p: TProc; typ: PType; + indirect: bool): PRope; forward; + +function createRecordVarAux(var p: TProc; rec: PNode; var c: int): PRope; +var + i: int; +begin + result := nil; + case rec.kind of + nkRecList: begin + for i := 0 to sonsLen(rec)-1 do + app(result, createRecordVarAux(p, rec.sons[i], c)) + end; + nkRecCase: begin + app(result, createRecordVarAux(p, rec.sons[0], c)); + for i := 1 to sonsLen(rec)-1 do + app(result, createRecordVarAux(p, lastSon(rec.sons[i]), c)); + end; + nkSym: begin + if c > 0 then app(result, ', '); + app(result, mangleName(rec.sym)); + app(result, ': '); + app(result, createVar(p, rec.sym.typ, false)); + inc(c); + end; + else InternalError(rec.info, 'createRecordVarAux') + end +end; + +function createVar(var p: TProc; typ: PType; indirect: bool): PRope; +var + i, len, c: int; + t, e: PType; +begin + t := skipGeneric(typ); + case t.kind of + tyInt..tyInt64, tyEnum, tyAnyEnum, tyChar: begin + result := putToSeq('0'+'', indirect) + end; + tyFloat..tyFloat128: result := putToSeq('0.0', indirect); + tyRange: result := createVar(p, typ.sons[0], indirect); + tySet: result := toRope('{}'); + tyBool: result := putToSeq('false', indirect); + tyArray, tyArrayConstr: begin + len := int(lengthOrd(t)); + e := elemType(t); + if len > 32 then begin + useMagic(p, 'ArrayConstr'); + result := ropef('ArrayConstr($1, $2, $3)', + [toRope(len), createVar(p, e, false), + genTypeInfo(p, e)]) + end + else begin + result := toRope('['+''); + i := 0; + while i < len do begin + if i > 0 then app(result, ', '); + app(result, createVar(p, e, false)); + inc(i); + end; + app(result, ']'+''); + end + end; + tyTuple: begin + result := toRope('{'+''); + c := 0; + app(result, createRecordVarAux(p, t.n, c)); + app(result, '}'+''); + end; + tyObject: begin + result := toRope('{'+''); + c := 0; + if not (tfFinal in t.flags) or (t.sons[0] <> nil) then begin + inc(c); + appf(result, 'm_type: $1', [genTypeInfo(p, t)]); + end; + while t <> nil do begin + app(result, createRecordVarAux(p, t.n, c)); + t := t.sons[0]; + end; + app(result, '}'+''); + end; + tyVar, tyPtr, tyRef: begin + if mapType(t) = etyBaseIndex then + result := putToSeq('[null, 0]', indirect) + else + result := putToSeq('null', indirect); + end; + tySequence, tyString, tyCString, tyPointer: begin + result := putToSeq('null', indirect); + end + else begin + internalError('createVar: ' + typekindtoStr[t.kind]); + result := nil; + end + end +end; + +function isIndirect(v: PSym): bool; +begin + result := (sfAddrTaken in v.flags) and (mapType(v.typ) <> etyObject); +end; + +procedure genVarInit(var p: TProc; v: PSym; n: PNode; var r: TCompRes); +var + a: TCompRes; + s: PRope; +begin + if n = nil then begin + appf(r.com, 'var $1 = $2;$n', + [mangleName(v), createVar(p, v.typ, isIndirect(v))]) + end + else begin + {@discard} mangleName(v); + gen(p, n, a); + case mapType(v.typ) of + etyObject: begin + if a.com <> nil then appf(r.com, '$1;$n', [a.com]); + if needsNoCopy(n) then s := a.res + else begin + useMagic(p, 'NimCopy'); + s := ropef('NimCopy($1, $2)', [a.res, genTypeInfo(p, n.typ)]); + end + end; + etyBaseIndex: begin + if (a.kind <> etyBaseIndex) then InternalError(n.info, 'genVarInit'); + if [sfAddrTaken, sfGlobal] * v.flags <> [] then + 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]); + exit + end + else begin + if a.com <> nil then appf(r.com, '$1;$n', [a.com]); + s := a.res; + end + end; + if isIndirect(v) then + appf(r.com, 'var $1 = [$2];$n', [v.loc.r, s]) + else + appf(r.com, 'var $1 = $2;$n', [v.loc.r, s]) + end; +end; + +procedure genVarStmt(var p: TProc; n: PNode; var r: TCompRes); +var + i: int; + v: PSym; + a: PNode; +begin + for i := 0 to sonsLen(n)-1 do begin + a := n.sons[i]; + if a.kind = nkCommentStmt then continue; + assert(a.kind = nkIdentDefs); + assert(a.sons[0].kind = nkSym); + v := a.sons[0].sym; + if lfNoDecl in v.loc.flags then continue; + genLineDir(p, a, r); + genVarInit(p, v, a.sons[2], r); + end +end; + +procedure genConstStmt(var p: TProc; n: PNode; var r: TCompRes); +var + c: PSym; + i: int; +begin + genLineDir(p, n, r); + for i := 0 to sonsLen(n)-1 do begin + if n.sons[i].kind = nkCommentStmt then continue; + assert(n.sons[i].kind = nkConstDef); + c := n.sons[i].sons[0].sym; + if (c.ast <> nil) and (c.typ.kind in ConstantDataTypes) and + not (lfNoDecl in c.loc.flags) then begin + genLineDir(p, n.sons[i], r); + genVarInit(p, c, c.ast, r); + end + end +end; + +procedure genNew(var p: TProc; n: PNode; var r: TCompRes); +var + a: TCompRes; + t: Ptype; +begin + gen(p, n.sons[1], a); + t := skipVarGeneric(n.sons[1].typ).sons[0]; + if a.com <> nil then appf(r.com, '$1;$n', [a.com]); + appf(r.com, '$1 = $2;$n', [a.res, createVar(p, t, true)]); +end; + +procedure genOrd(var p: TProc; n: PNode; var r: TCompRes); +begin + case skipVarGeneric(n.sons[1].typ).kind of + tyEnum, tyAnyEnum, tyInt..tyInt64, tyChar: gen(p, n.sons[1], r); + tyBool: unaryExpr(p, n, r, '', '($1 ? 1:0)'); + else InternalError(n.info, 'genOrd'); + end +end; + +procedure genConStrStr(var p: TProc; n: PNode; var r: TCompRes); +var + a, b: TCompRes; +begin + gen(p, n.sons[1], a); + gen(p, n.sons[2], b); + r.com := mergeExpr(a.com, b.com); + if skipVarGenericRange(n.sons[1].typ).kind = tyChar then + a.res := ropef('[$1, 0]', [a.res]); + if skipVarGenericRange(n.sons[2].typ).kind = tyChar then + b.res := ropef('[$1, 0]', [b.res]); + r.res := ropef('($1.slice(0,-1)).concat($2)', [a.res, b.res]); +end; + +procedure genMagic(var p: TProc; n: PNode; var r: TCompRes); +var + a: TCompRes; + line, filen: PRope; + op: TMagic; +begin + op := n.sons[0].sym.magic; + case op of + mOr: genOr(p, n.sons[1], n.sons[2], r); + mAnd: genAnd(p, n.sons[1], n.sons[2], r); + mAddi..mStrToStr: arith(p, n, r, op); + //mRepr: genRepr(p, n, r); + mSwap: genSwap(p, n, r); + mPred: begin // XXX: range checking? + if not (optOverflowCheck in p.Options) then + binaryExpr(p, n, r, '', '$1 - $2') + else + binaryExpr(p, n, r, 'subInt', 'subInt($1, $2)') + end; + mSucc: begin // XXX: range checking? + if not (optOverflowCheck in p.Options) then + binaryExpr(p, n, r, '', '$1 - $2') + else + binaryExpr(p, n, r, 'addInt', 'addInt($1, $2)') + end; + mAppendStrCh: binaryStmt(p, n, r, 'addChar', '$1 = addChar($1, $2)'); + mAppendStrStr: + binaryStmt(p, n, r, '', '$1 = ($1.slice(0,-1)).concat($2)'); + // XXX: make a copy of $2, because of EMCAScript's sucking semantics + mAppendSeqElem: binaryStmt(p, n, r, '', '$1.push($2)'); + mConStrStr: genConStrStr(p, n, r); + mEqStr: binaryExpr(p, n, r, 'eqStrings', 'eqStrings($1, $2)'); + mLeStr: binaryExpr(p, n, r, 'cmpStrings', '(cmpStrings($1, $2) <= 0)'); + mLtStr: binaryExpr(p, n, r, 'cmpStrings', '(cmpStrings($1, $2) < 0)'); + mIsNil: unaryExpr(p, n, r, '', '$1 == null'); + mAssert: begin + if (optAssert in p.Options) then begin + 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)]) + end + end; + mNew, mNewFinalize: genNew(p, n, r); + mSizeOf: r.res := toRope(getSize(n.sons[1].typ)); + mChr: gen(p, n.sons[1], r); // nothing to do + mOrd: genOrd(p, n, r); + mLengthStr: unaryExpr(p, n, r, '', '($1.length-1)'); + mLengthSeq, mLengthOpenArray, mLengthArray: + unaryExpr(p, n, r, '', '$1.length'); + mHigh: begin + if skipVarGeneric(n.sons[0].typ).kind = tyString then + unaryExpr(p, n, r, '', '($1.length-2)') + else + unaryExpr(p, n, r, '', '($1.length-1)'); + end; + mInc: begin + if not (optOverflowCheck in p.Options) then + binaryStmt(p, n, r, '', '$1 += $2') + else + binaryStmt(p, n, r, 'addInt', '$1 = addInt($1, $2)') + end; + ast.mDec: begin + if not (optOverflowCheck in p.Options) then + binaryStmt(p, n, r, '', '$1 -= $2') + else + binaryStmt(p, n, r, 'subInt', '$1 = subInt($1, $2)') + end; + mSetLengthStr: binaryStmt(p, n, r, '', '$1.length = ($2)-1'); + mSetLengthSeq: binaryStmt(p, n, r, '', '$1.length = $2'); + mCard: unaryExpr(p, n, r, 'SetCard', 'SetCard($1)'); + mLtSet: binaryExpr(p, n, r, 'SetLt', 'SetLt($1, $2)'); + mLeSet: binaryExpr(p, n, r, 'SetLe', 'SetLe($1, $2)'); + mEqSet: binaryExpr(p, n, r, 'SetEq', 'SetEq($1, $2)'); + mMulSet: binaryExpr(p, n, r, 'SetMul', 'SetMul($1, $2)'); + mPlusSet: binaryExpr(p, n, r, 'SetPlus', 'SetPlus($1, $2)'); + mMinusSet: binaryExpr(p, n, r, 'SetMinus', 'SetMinus($1, $2)'); + mIncl: binaryStmt(p, n, r, '', '$1[$2] = true'); + mExcl: binaryStmt(p, n, r, '', 'delete $1[$2]'); + mInSet: binaryExpr(p, n, r, '', '($1[$2] != undefined)'); + mNLen..mNError: + liMessage(n.info, errCannotGenerateCodeForX, n.sons[0].sym.name.s); + else genCall(p, n, r); + //else internalError(e.info, 'genMagic: ' + magicToStr[op]); + end +end; + +procedure genSetConstr(var p: TProc; n: PNode; var r: TCompRes); +var + a, b: TCompRes; + i: int; + it: PNode; +begin + useMagic(p, 'SetConstr'); + r.res := toRope('SetConstr('); + for i := 0 to sonsLen(n)-1 do begin + if i > 0 then app(r.res, ', '); + it := n.sons[i]; + if it.kind = nkRange then begin + 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]); + end + else begin + gen(p, it, a); + r.com := mergeExpr(r.com, a.com); + app(r.res, a.res); + end + end; + app(r.res, ')'+''); +end; + +procedure genArrayConstr(var p: TProc; n: PNode; var r: TCompRes); +var + a: TCompRes; + i: int; +begin + r.res := toRope('['+''); + for i := 0 to sonsLen(n)-1 do begin + if i > 0 then app(r.res, ', '); + gen(p, n.sons[i], a); + r.com := mergeExpr(r.com, a.com); + app(r.res, a.res); + end; + app(r.res, ']'+''); +end; + +procedure genRecordConstr(var p: TProc; n: PNode; var r: TCompRes); +var + a: TCompRes; + i, len: int; +begin + i := 0; + len := sonsLen(n); + r.res := toRope('{'+''); + while i < len do begin + if i > 0 then app(r.res, ', '); + if (n.sons[i].kind <> nkSym) then + 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) + end +end; + +procedure genConv(var p: TProc; n: PNode; var r: TCompRes); +var + src, dest: PType; +begin + dest := skipVarGenericRange(n.typ); + src := skipVarGenericRange(n.sons[1].typ); + gen(p, n.sons[1], r); + if (dest.kind <> src.kind) and (src.kind = tyBool) then + r.res := ropef('(($1)? 1:0)', [r.res]) +end; + +procedure upConv(var p: TProc; n: PNode; var r: TCompRes); +begin + gen(p, n.sons[0], r); // XXX +end; + +procedure genRangeChck(var p: TProc; n: PNode; var r: TCompRes; + const magic: string); +var + a, b: TCompRes; +begin + gen(p, n.sons[0], r); + if optRangeCheck in p.options then begin + 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]); + end +end; + +procedure convStrToCStr(var p: TProc; n: PNode; var r: TCompRes); +begin + // we do an optimization here as this is likely to slow down + // much of the code otherwise: + if n.sons[0].kind = nkCStringToString then + gen(p, n.sons[0].sons[0], r) + else begin + gen(p, n.sons[0], r); + if r.res = nil then InternalError(n.info, 'convStrToCStr'); + useMagic(p, 'toEcmaStr'); + r.res := ropef('toEcmaStr($1)', [r.res]); + end; +end; + +procedure convCStrToStr(var p: TProc; n: PNode; var r: TCompRes); +begin + // we do an optimization here as this is likely to slow down + // much of the code otherwise: + if n.sons[0].kind = nkStringToCString then + gen(p, n.sons[0].sons[0], r) + else begin + gen(p, n.sons[0], r); + if r.res = nil then InternalError(n.info, 'convCStrToStr'); + useMagic(p, 'cstrToNimstr'); + r.res := ropef('cstrToNimstr($1)', [r.res]); + end; +end; + +procedure genReturnStmt(var p: TProc; n: PNode; var r: TCompRes); +var + a: TCompRes; +begin + if p.procDef = nil then InternalError(n.info, 'genReturnStmt'); + p.BeforeRetNeeded := true; + if (n.sons[0] <> nil) then begin + genStmt(p, n.sons[0], a); + if a.com <> nil then appf(r.com, '$1;$n', mergeStmt(a)); + end + else genLineDir(p, n, r); + finishTryStmt(p, r, p.nestedTryStmts); + app(r.com, 'break BeforeRet;' + tnl); +end; + +function genProcBody(var p: TProc; prc: PSym; const r: TCompRes): PRope; +begin + if optStackTrace in prc.options then begin + 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))]); + end + else + result := nil; + if p.beforeRetNeeded then + appf(result, 'BeforeRet: do {$n$1} while (false); $n', [mergeStmt(r)]) + else + app(result, mergeStmt(r)); + if prc.typ.callConv = ccSysCall then begin + result := ropef('try {$n$1} catch (e) {$n'+ + ' alert("Unhandled exception:\n" + e.message + "\n"$n}', + [result]); + end; + if optStackTrace in prc.options then + app(result, 'framePtr = framePtr.prev;' + tnl); +end; + +procedure genProc(var oldProc: TProc; n: PNode; var r: TCompRes); +var + p: TProc; + prc, resultSym: PSym; + name, returnStmt, resultAsgn, header: PRope; + a: TCompRes; +begin + 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) then begin + 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 then appf(returnStmt, '$1;$n', [a.com]); + returnStmt := ropef('return $1;$n', [a.res]); + end; + 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; +end; + +procedure genStmtListExpr(var p: TProc; n: PNode; var r: TCompRes); +var + i: int; + a: TCompRes; +begin + // watch out this trick: ``function () { stmtList; return expr; }()`` + r.res := toRope('function () {'); + for i := 0 to sonsLen(n)-2 do begin + genStmt(p, n.sons[i], a); + app(r.res, mergeStmt(a)); + end; + gen(p, lastSon(n), a); + if a.com <> nil then appf(r.res, '$1;$n', [a.com]); + appf(r.res, 'return $1; }()', [a.res]); +end; + +procedure genStmt(var p: TProc; n: PNode; var r: TCompRes); +var + prc: PSym; + i: int; + a: TCompRes; +begin + r.kind := etyNone; + r.com := nil; + r.res := nil; + case n.kind of + nkNilLit: begin end; + nkStmtList: begin + for i := 0 to sonsLen(n)-1 do begin + genStmt(p, n.sons[i], a); + app(r.com, mergeStmt(a)); + end + end; + nkBlockStmt: genBlock(p, n, r); + nkIfStmt: genIfStmt(p, n, r); + nkWhileStmt: genWhileStmt(p, n, r); + nkVarSection: genVarStmt(p, n, r); + nkConstSection: genConstStmt(p, n, r); + nkForStmt: internalError(n.info, 'for statement not eliminated'); + nkCaseStmt: genCaseStmt(p, n, r); + nkReturnStmt: genReturnStmt(p, n, r); + nkBreakStmt: genBreakStmt(p, n, r); + nkAsgn: genAsgn(p, n, r); + nkDiscardStmt: begin + genLineDir(p, n, r); + gen(p, n.sons[0], r); + app(r.res, ';'+ tnl); + end; + nkAsmStmt: genAsmStmt(p, n, r); + nkTryStmt: genTryStmt(p, n, r); + nkRaiseStmt: genRaiseStmt(p, n, r); + nkTypeSection, nkCommentStmt, nkIteratorDef, + nkIncludeStmt, nkImportStmt, + nkFromStmt, nkTemplateDef, nkMacroDef, nkPragma: begin end; + nkProcDef, nkConverterDef: begin + if (n.sons[genericParamsPos] = nil) then begin + prc := n.sons[namePos].sym; + if (n.sons[codePos] <> nil) and not (lfNoDecl in prc.loc.flags) then + genProc(p, n, r) + else + {@discard} mangleName(prc); + end + end; + else begin + genLineDir(p, n, r); + gen(p, n, r); + app(r.res, ';'+ tnl); + end + end +end; + +procedure gen(var p: TProc; n: PNode; var r: TCompRes); +var + f: BiggestFloat; +begin + r.kind := etyNone; + r.com := nil; + r.res := nil; + case n.kind of + nkSym: genSym(p, n, r); + nkCharLit..nkInt64Lit: begin + r.res := toRope(n.intVal); + end; + nkNilLit: begin + if mapType(n.typ) = etyBaseIndex then begin + r.kind := etyBaseIndex; + r.com := toRope('null'); + r.res := toRope('0'+''); + end + else + r.res := toRope('null'); + end; + nkStrLit..nkTripleStrLit: begin + if skipVarGenericRange(n.typ).kind = tyString then begin + useMagic(p, 'cstrToNimstr'); + r.res := ropef('cstrToNimstr($1)', [makeCString(n.strVal)]) + end + else + r.res := makeCString(n.strVal) + end; + nkFloatLit..nkFloat64Lit: begin + f := n.floatVal; + if f <> f then + r.res := toRope('NaN') + else if f = 0.0 then + r.res := toRopeF(f) + else if f = 0.5 * f then + if f > 0.0 then r.res := toRope('Infinity') + else r.res := toRope('-Infinity') + else + r.res := toRopeF(f); + end; + nkBlockExpr: genBlock(p, n, r); + nkIfExpr: genIfExpr(p, n, r); + nkCall, nkHiddenCallConv: begin + if (n.sons[0].kind = nkSym) and (n.sons[0].sym.magic <> mNone) then + genMagic(p, n, r) + else + genCall(p, n, r) + end; + nkCurly: genSetConstr(p, n, r); + nkBracket: genArrayConstr(p, n, r); + nkPar: genRecordConstr(p, n, r); + nkHiddenStdConv, nkHiddenSubConv, nkConv: genConv(p, n, r); + nkAddr, nkHiddenAddr: genAddr(p, n, r); + nkDerefExpr, nkHiddenDeref: genDeref(p, n, r); + nkBracketExpr: genArrayAccess(p, n, r); + nkDotExpr: genFieldAccess(p, n, r); + nkCheckedFieldExpr: genCheckedFieldAccess(p, n, r); + nkObjDownConv: gen(p, n.sons[0], r); + nkObjUpConv: upConv(p, n, r); + nkChckRangeF: genRangeChck(p, n, r, 'chckRangeF'); + nkChckRange64: genRangeChck(p, n, r, 'chckRange64'); + nkChckRange: genRangeChck(p, n, r, 'chckRange'); + nkStringToCString: convStrToCStr(p, n, r); + nkCStringToString: convCStrToStr(p, n, r); + nkPassAsOpenArray: gen(p, n.sons[0], r); + nkStmtListExpr: genStmtListExpr(p, n, r); + else + InternalError(n.info, 'gen: unknown node type: ' + nodekindToStr[n.kind]) + end +end; + +// ------------------------------------------------------------------------ + +var + globals: PGlobals; + +function newModule(module: PSym; const filename: string): BModule; +begin + new(result); +{@ignore} + fillChar(result^, sizeof(result^), 0); +{@emit} + result.filename := filename; + result.module := module; + if globals = nil then globals := newGlobals(); +end; + +function genHeader(): PRope; +begin + result := ropef( + '/* Generated by the Nimrod Compiler v$1 */$n' + + '/* (c) 2008 Andreas Rumpf */$n$n' + + '$nvar Globals = this;$n' + + 'var framePtr = null;$n' + + 'var excHandler = null;$n', + [toRope(versionAsString)]) +end; + +procedure genModule(var p: TProc; n: PNode; var r: TCompRes); +begin + genStmt(p, n, r); + if optStackTrace in p.options then begin + 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]) + end +end; + +procedure finishModule(b: PBackend; n: PNode); +var + m: BModule; + outfile: string; + p: TProc; + r: TCompRes; + code: PRope; +begin + m := BModule(b); + if m.module = nil then InternalError(n.info, 'finishModule'); + initProc(p, globals, m, nil, m.module.options); + genModule(p, n, r); + app(p.globals.code, p.data); + app(p.globals.code, mergeStmt(r)); + if sfMainModule in m.module.flags then begin + // write the file: + code := con(p.globals.typeInfo, p.globals.code); + outfile := changeFileExt(completeCFilePath(m.filename), 'js'); + {@discard} writeRopeIfNotEqual(con(genHeader(), code), outfile); + end; +end; + +function EcmasBackend(b: PBackend; module: PSym; + const filename: string): PBackend; +var + g: BModule; +begin + g := newModule(module, filename); + g.backendCreator := EcmasBackend; + g.eventMask := {@set}[eAfterModule]; + g.afterModuleEvent := finishModule; + result := g; +end; + +end. |