summary refs log blame commit diff stats
path: root/lib/system/ecmasys.nim
blob: 7f91feb6b8fbaaf898a4384103c02f9c4485bd7a (plain) (tree)
1
2
3
4
5
6
7
8
9


                                     
                                         




                                                   












































































































                                                                                
























































































































































































                                                                           









                                                                               
 



                                                    
                                                                       





                                                    
                                                                       





                                                    
                                                                       




                                                    

                                                          




                                                    

                                                          


                                 



                                                      
                                                        






                                                      
                                                        






                                                      
                                                        




                                                      

                                                                   




                                                      

                                                                   












                                                                               
                    

                                       
                                                      
                                      
                                                  
                                            




                                                                  
                             























                                                                   
           











































































                                                                               
#
#
#            Nimrod's Runtime Library
#        (c) Copyright 2010 Andreas Rumpf
#
#    See the file "copying.txt", included in this
#    distribution, for details about the copyright.
#

proc alert(s: cstring) {.importc, nodecl.}

type
  PSafePoint = ptr TSafePoint
  TSafePoint {.compilerproc, final.} = object
    prev: PSafePoint # points to next safe point
    exc: ref E_Base

  PCallFrame = ptr TCallFrame
  TCallFrame {.importc, nodecl, final.} = object
    prev: PCallFrame
    procname: CString
    line: int # current line number
    filename: CString

var
  framePtr {.importc, nodecl, volatile.}: PCallFrame
  excHandler {.importc, nodecl, volatile.}: PSafePoint = nil
    # list of exception handlers
    # a global variable for the root of all try blocks

{.push stacktrace: off.}
proc nimBoolToStr(x: bool): string {.compilerproc.} =
  if x: result = "true"
  else: result = "false"

proc nimCharToStr(x: char): string {.compilerproc.} =
  result = newString(1)
  result[0] = x

proc getCurrentExceptionMsg(): string =
  if excHandler != nil: return $excHandler.exc.msg
  return ""

proc auxWriteStackTrace(f: PCallFrame): string =
  type
    TTempFrame = tuple[procname: CString, line: int]
  var
    it = f
    i = 0
    total = 0
    tempFrames: array [0..63, TTempFrame]
  while it != nil and i <= high(tempFrames):
    tempFrames[i].procname = it.procname
    tempFrames[i].line = it.line
    inc(i)
    inc(total)
    it = it.prev
  while it != nil:
    inc(total)
    it = it.prev
  result = ""
  # if the buffer overflowed print '...':
  if total != i:
    add(result, "(")
    add(result, $(total-i))
    add(result, " calls omitted) ...\n")
  for j in countdown(i-1, 0):
    add(result, tempFrames[j].procname)
    if tempFrames[j].line > 0:
      add(result, ", line: ")
      add(result, $tempFrames[j].line)
    add(result, "\n")

proc rawWriteStackTrace(): string =
  if framePtr == nil:
    result = "No stack traceback available\n"
  else:
    result = "Traceback (most recent call last)\n"& auxWriteStackTrace(framePtr)
    framePtr = nil

proc raiseException(e: ref E_Base, ename: cstring) {.compilerproc, pure.} =
  e.name = ename
  if excHandler != nil:
    excHandler.exc = e
  else:
    var buf = rawWriteStackTrace()
    if e.msg != nil and e.msg[0] != '\0':
      add(buf, "Error: unhandled exception: ")
      add(buf, e.msg)
    else:
      add(buf, "Error: unhandled exception")
    add(buf, " [")
    add(buf, ename)
    add(buf, "]\n")
    alert(buf)
  asm """throw `e`;"""

proc reraiseException() =
  if excHandler == nil:
    raise newException(ENoExceptionToReraise, "no exception to reraise")
  else:
    asm """throw excHandler.exc;"""

proc raiseOverflow {.exportc: "raiseOverflow", noreturn.} =
  raise newException(EOverflow, "over- or underflow")

proc raiseDivByZero {.exportc: "raiseDivByZero", noreturn.} =
  raise newException(EDivByZero, "divison by zero")

proc raiseRangeError() {.compilerproc, noreturn.} =
  raise newException(EOutOfRange, "value out of range")

proc raiseIndexError() {.compilerproc, noreturn.} =
  raise newException(EInvalidIndex, "index out of bounds")

proc raiseFieldError(f: string) {.compilerproc, noreturn.} =
  raise newException(EInvalidField, f & " is not accessible")

proc SetConstr() {.varargs, pure, compilerproc.} =
  asm """
    var result = {};
    for (var i = 0; i < arguments.length; ++i) {
      var x = arguments[i];
      if (typeof(x) == "object") {
        for (var j = x[0]; j <= x[1]; ++j) {
          result[j] = true;
        }
      } else {
        result[x] = true;
      }
    }
    return result;
  """

proc cstrToNimstr(c: cstring): string {.pure, compilerproc.} =
  asm """
    var result = [];
    for (var i = 0; i < `c`.length; ++i) {
      result[i] = `c`.charCodeAt(i);
    }
    result[result.length] = 0; // terminating zero
    return result;
  """

proc toEcmaStr(s: string): cstring {.pure, compilerproc.} =
  asm """
    var len = `s`.length-1;
    var result = new Array(len);
    var fcc = String.fromCharCode;
    for (var i = 0; i < len; ++i) {
      result[i] = fcc(`s`[i]);
    }
    return result.join("");
  """

proc mnewString(len: int): string {.pure, compilerproc.} =
  asm """
    var result = new Array(`len`+1);
    result[0] = 0;
    result[`len`] = 0;
    return result;
  """

proc SetCard(a: int): int {.compilerproc, pure.} =
  # argument type is a fake
  asm """
    var result = 0;
    for (var elem in `a`) { ++result; }
    return result;
  """

proc SetEq(a, b: int): bool {.compilerproc, pure.} =
  asm """
    for (var elem in `a`) { if (!`b`[elem]) return false; }
    for (var elem in `b`) { if (!`a`[elem]) return false; }
    return true;
  """

proc SetLe(a, b: int): bool {.compilerproc, pure.} =
  asm """
    for (var elem in `a`) { if (!`b`[elem]) return false; }
    return true;
  """

proc SetLt(a, b: int): bool {.compilerproc.} =
  result = SetLe(a, b) and not SetEq(a, b)

proc SetMul(a, b: int): int {.compilerproc, pure.} =
  asm """
    var result = {};
    for (var elem in `a`) {
      if (`b`[elem]) { result[elem] = true; }
    }
    return result;
  """

proc SetPlus(a, b: int): int {.compilerproc, pure.} =
  asm """
    var result = {};
    for (var elem in `a`) { result[elem] = true; }
    for (var elem in `b`) { result[elem] = true; }
    return result;
  """

proc SetMinus(a, b: int): int {.compilerproc, pure.} =
  asm """
    var result = {};
    for (var elem in `a`) {
      if (!`b`[elem]) { result[elem] = true; }
    }
    return result;
  """

proc cmpStrings(a, b: string): int {.pure, compilerProc.} =
  asm """
    if (`a` == `b`) return 0;
    if (!`a`) return -1;
    if (!`b`) return 1;
    for (var i = 0; i < `a`.length-1; ++i) {
      var result = `a`[i] - `b`[i];
      if (result != 0) return result;
    }
    return 0;
  """

proc cmp(x, y: string): int = return cmpStrings(x, y)

proc eqStrings(a, b: string): bool {.pure, compilerProc.} =
  asm """
    if (`a == `b`) return true;
    if ((!`a`) || (!`b`)) return false;
    var alen = `a`.length;
    if (alen != `b`.length) return false;
    for (var i = 0; i < alen; ++i)
      if (`a`[i] != `b`[i]) return false;
    return true;
  """

type
  TDocument {.importc.} = object of TObject
    write: proc (text: cstring)
    writeln: proc (text: cstring)
    createAttribute: proc (identifier: cstring): ref TNode
    createElement: proc (identifier: cstring): ref TNode
    createTextNode: proc (identifier: cstring): ref TNode
    getElementById: proc (id: cstring): ref TNode
    getElementsByName: proc (name: cstring): seq[ref TNode]
    getElementsByTagName: proc (name: cstring): seq[ref TNode]

  TNodeType* = enum
    ElementNode = 1,
    AttributeNode,
    TextNode,
    CDATANode,
    EntityRefNode,
    EntityNode,
    ProcessingInstructionNode,
    CommentNode,
    DocumentNode,
    DocumentTypeNode,
    DocumentFragmentNode,
    NotationNode
  TNode* {.importc.} = object of TObject
    attributes*: seq[ref TNode]
    childNodes*: seq[ref TNode]
    data*: cstring
    firstChild*: ref TNode
    lastChild*: ref TNode
    nextSibling*: ref TNode
    nodeName*: cstring
    nodeType*: TNodeType
    nodeValue*: cstring
    parentNode*: ref TNode
    previousSibling*: ref TNode
    appendChild*: proc (child: ref TNode)
    appendData*: proc (data: cstring)
    cloneNode*: proc (copyContent: bool)
    deleteData*: proc (start, len: int)
    getAttribute*: proc (attr: cstring): cstring
    getAttributeNode*: proc (attr: cstring): ref TNode
    getElementsByTagName*: proc (): seq[ref TNode]
    hasChildNodes*: proc (): bool
    insertBefore*: proc (newNode, before: ref TNode)
    insertData*: proc (position: int, data: cstring)
    removeAttribute*: proc (attr: cstring)
    removeAttributeNode*: proc (attr: ref TNode)
    removeChild*: proc (child: ref TNode)
    replaceChild*: proc (newNode, oldNode: ref TNode)
    replaceData*: proc (start, len: int, text: cstring)
    setAttribute*: proc (name, value: cstring)
    setAttributeNode*: proc (attr: ref TNode)
    
var
  document {.importc, nodecl.}: ref TDocument

proc ewriteln(x: cstring) = 
  var node = document.getElementsByTagName("body")[0]
  if node != nil: 
    node.appendChild(document.createTextNode(x))
    node.appendChild(document.createElement("br"))
  else: 
    raise newException(EInvalidValue, "<body> element does not exist yet!")

proc rawEcho {.compilerproc.} =
  var node = document.getElementsByTagName("body")[0]
  if node == nil: raise newException(EIO, "<body> element does not exist yet!")
  asm """
    for (var i = 0; i < arguments.length; ++i) {
      var x = `toEcmaStr`(arguments[i]);
      `node`.appendChild(document.createTextNode(x))
    }
  """
  node.appendChild(document.createElement("br"))

# Arithmetic:
proc addInt(a, b: int): int {.pure, compilerproc.} =
  asm """
    var result = `a` + `b`;
    if (result > 2147483647 || result < -2147483648) `raiseOverflow`();
    return result;
  """

proc subInt(a, b: int): int {.pure, compilerproc.} =
  asm """
    var result = `a` - `b`;
    if (result > 2147483647 || result < -2147483648) `raiseOverflow`();
    return result;
  """

proc mulInt(a, b: int): int {.pure, compilerproc.} =
  asm """
    var result = `a` * `b`;
    if (result > 2147483647 || result < -2147483648) `raiseOverflow`();
    return result;
  """

proc divInt(a, b: int): int {.pure, compilerproc.} =
  asm """
    if (`b` == 0) `raiseDivByZero`();
    if (`b` == -1 && `a` == 2147483647) `raiseOverflow`();
    return Math.floor(`a` / `b`);
  """

proc modInt(a, b: int): int {.pure, compilerproc.} =
  asm """
    if (`b` == 0) `raiseDivByZero`();
    if (`b` == -1 && `a` == 2147483647) `raiseOverflow`();
    return Math.floor(`a` % `b`);
  """

proc addInt64(a, b: int): int {.pure, compilerproc.} =
  asm """
    var result = `a` + `b`;
    if (result > 9223372036854775807
    || result < -9223372036854775808) `raiseOverflow`();
    return result;
  """

proc subInt64(a, b: int): int {.pure, compilerproc.} =
  asm """
    var result = `a` - `b`;
    if (result > 9223372036854775807
    || result < -9223372036854775808) `raiseOverflow`();
    return result;
  """

proc mulInt64(a, b: int): int {.pure, compilerproc.} =
  asm """
    var result = `a` * `b`;
    if (result > 9223372036854775807
    || result < -9223372036854775808) `raiseOverflow`();
    return result;
  """

proc divInt64(a, b: int): int {.pure, compilerproc.} =
  asm """
    if (`b` == 0) `raiseDivByZero`();
    if (`b` == -1 && `a` == 9223372036854775807) `raiseOverflow`();
    return Math.floor(`a` / `b`);
  """

proc modInt64(a, b: int): int {.pure, compilerproc.} =
  asm """
    if (`b` == 0) `raiseDivByZero`();
    if (`b` == -1 && `a` == 9223372036854775807) `raiseOverflow`();
    return Math.floor(`a` % `b`);
  """

proc nimMin(a, b: int): int {.compilerproc.} = return if a <= b: a else: b
proc nimMax(a, b: int): int {.compilerproc.} = return if a >= b: a else: b

proc internalAssert(file: cstring, line: int) {.pure, compilerproc.} =
  var
    e: ref EAssertionFailed
  new(e)
  asm """`e`.message = "[Assertion failure] file: "+`file`+", line: "+`line`"""
  raise e

include "system/hti"

proc isFatPointer(ti: PNimType): bool =
  # This has to be consistent with the code generator!
  return ti.base.kind notin {tyObject,
    tyArray, tyArrayConstr, tyPureObject, tyTuple,
    tyOpenArray, tySet, tyVar, tyRef, tyPtr}

proc NimCopy(x: pointer, ti: PNimType): pointer {.compilerproc.}

proc NimCopyAux(dest, src: Pointer, n: ptr TNimNode) {.exportc.} =
  case n.kind
  of nkNone: sysAssert(false)
  of nkSlot:
    asm "`dest`[`n`.offset] = NimCopy(`src`[`n`.offset], `n`.typ);"
  of nkList:
    for i in 0..n.len-1:
      NimCopyAux(dest, src, n.sons[i])
  of nkCase:
    asm """
      `dest`[`n`.offset] = NimCopy(`src`[`n`.offset], `n`.typ);
      for (var i = 0; i < `n`.sons.length; ++i) {
        NimCopyAux(`dest`, `src`, `n`.sons[i][1]);
      }
    """

proc NimCopy(x: pointer, ti: PNimType): pointer =
  case ti.kind
  of tyPtr, tyRef, tyVar, tyNil:
    if not isFatPointer(ti):
      result = x
    else:
      asm """
        `result` = [null, 0];
        `result`[0] = `x`[0];
        `result`[1] = `x`[1];
      """
  of tySet:
    asm """
      `result` = {};
      for (var key in `x`) { `result`[key] = `x`[key]; }
    """
  of tyPureObject, tyTuple, tyObject:
    if ti.base != nil: result = NimCopy(x, ti.base)
    elif ti.kind == tyObject:
      asm "`result` = {m_type: `ti`};"
    else:
      asm "`result` = {};"
    NimCopyAux(result, x, ti.node)
  of tySequence, tyArrayConstr, tyOpenArray, tyArray:
    asm """
      `result` = new Array(`x`.length);
      for (var i = 0; i < `x`.length; ++i) {
        `result`[i] = NimCopy(`x`[i], `ti`.base);
      }
    """
  of tyString:
    asm "`result` = `x`.slice(0);"
  else:
    result = x


proc ArrayConstr(len: int, value: pointer, typ: PNimType): pointer {.
                 pure, compilerproc.} =
  # types are fake
  asm """
    var result = new Array(`len`);
    for (var i = 0; i < `len`; ++i) result[i] = NimCopy(`value`, `typ`);
    return result;
  """

proc chckIndx(i, a, b: int): int {.compilerproc.} =
  if i >= a and i <= b: return i
  else: raiseIndexError()

proc chckRange(i, a, b: int): int {.compilerproc.} =
  if i >= a and i <= b: return i
  else: raiseRangeError()

proc chckObj(obj, subclass: PNimType) {.compilerproc.} =
  # checks if obj is of type subclass:
  var x = obj
  if x == subclass: return # optimized fast path
  while x != subclass:
    if x == nil:
      raise newException(EInvalidObjectConversion, "invalid object conversion")
    x = x.base

{.pop.}

#proc AddU($1, $2)
#SubU($1, $2)
#MulU($1, $2)
#DivU($1, $2)
#ModU($1, $2)
#AddU64($1, $2)
#SubU64($1, $2)
#MulU64($1, $2)
#DivU64($1, $2)
#ModU64($1, $2)
#LeU($1, $2)
#LtU($1, $2)
#LeU64($1, $2)
#LtU64($1, $2)
#Ze($1)
#Ze64($1)
#ToU8($1)
#ToU16($1)
#ToU32($1)

#NegInt($1)
#NegInt64($1)
#AbsInt($1)
#AbsInt64($1)