Commits

Leonardo Santagada committed 623b06f

first version

  • Participants

Comments (0)

Files changed (71)

+All benchmarks are from The Computer Language Benchmarks Game, licensed under the revised BSD License
+
+Maybe we should also take a look at those benchmarks:
+ http://trac.webkit.org/projects/webkit/wiki/JavaScript%20and%20DOM%20Benchmarks
+
+Parameters for the benchmarks:
+
+ackermann 9,10,11
+ary 3000,5000,7000,9000
+binarytrees 12,14,16
+fannkuch 9,10,11
+fasta (substring) 250000,2500000,25000000
+fibo 12,24,32
+harmonic 6000000,8000000,10000000
+hash 40000,60000,80000,100000
+hash2 50,100,150,200
+heapsort 20000,40000,60000,80000,100000
+hello ?
+knucleotide (without one simple regex)
+lists (concat) @0
+matrix @0
+methcall @0
+nbody
+nestedloop @0
+nsieve
+nsievebits
+objinst @0
+partialsums (sin, cos, pow)
+random @0
+recursive
+sieve @0
+spectralnorm (sqrt)
+strcat @0
+sumcol (readline!)
+takfp @0

bench/ackermann.javascript

+// The Great Computer Language Shootout
+// http://shootout.alioth.debian.org/
+//
+// contributed by Sjoerd Visscher
+
+function ack(m, n) {
+  return (m == 0
+    ? n + 1
+    : (n == 0
+      ? ack(m - 1, 1) 
+      : ack(m - 1, ack(m, n - 1))));
+}
+
+var n = arguments[0];
+print("ack(3, " + n + "): " + ack(3, n));

bench/ary.javascript

+// The Great Computer Language Shootout
+// http://shootout.alioth.debian.org/
+//
+// contributed by David Hedbor
+// modified by Isaac Gouy
+
+var i, k;
+
+var n = arguments[0];
+var x = Array(n);
+var y = Array(n);
+
+for (i = 0; i < n; i++) {
+  x[i] = i + 1;
+  y[i] = 0; // Need to set all entries in i to zero or the result will be NaN 
+}
+for (k = 0 ; k < 1000; k++) {
+  for (i = n-1; i >= 0; i--) {
+    y[i] += x[i];
+  }
+}
+print(y[0], y[n-1]);
+

bench/binarytrees.javascript

+/* The Great Computer Language Shootout
+   http://shootout.alioth.debian.org/
+   contributed by Isaac Gouy */
+
+function TreeNode(left,right,item){
+   this.left = left;
+   this.right = right;
+   this.item = item;
+}
+
+TreeNode.prototype.itemCheck = function(){
+   if (this.left==null) return this.item;
+   else return this.item + this.left.itemCheck() - this.right.itemCheck();
+}
+
+function bottomUpTree(item,depth){
+   if (depth>0){
+      return new TreeNode(
+          bottomUpTree(2*item-1, depth-1)
+         ,bottomUpTree(2*item, depth-1)
+         ,item
+      );
+   }
+   else {
+      return new TreeNode(null,null,item);
+   }
+}
+
+
+var minDepth = 4;
+var n = arguments[0];
+var maxDepth = Math.max(minDepth + 2, n);
+var stretchDepth = maxDepth + 1;
+
+var check = bottomUpTree(0,stretchDepth).itemCheck();
+print("stretch tree of depth " + stretchDepth + "\t check: " + check);
+
+var longLivedTree = bottomUpTree(0,maxDepth);
+for (var depth=minDepth; depth<=maxDepth; depth+=2){
+   var iterations = 1 << (maxDepth - depth + minDepth);
+
+   check = 0;
+   for (var i=1; i<=iterations; i++){
+      check += bottomUpTree(i,depth).itemCheck();
+      check += bottomUpTree(-i,depth).itemCheck();
+   }
+   print(iterations*2 + "\t trees of depth " + depth + "\t check: " + check);
+}
+
+print("long lived tree of depth " + maxDepth + "\t check: " 
+   + longLivedTree.itemCheck());

bench/fannkuch.javascript

+/* The Great Computer Language Shootout
+   http://shootout.alioth.debian.org/
+   contributed by Isaac Gouy */
+
+function fannkuch(n) {
+   var check = 0;
+   var perm = Array(n);
+   var perm1 = Array(n);
+   var count = Array(n);
+   var maxPerm = Array(n);
+   var maxFlipsCount = 0;
+   var m = n - 1;
+
+   for (var i = 0; i < n; i++) perm1[i] = i;
+   var r = n;
+
+   while (true) {
+      // write-out the first 30 permutations
+      if (check < 30){
+         var s = "";
+         for(var i=0; i<n; i++) s += (perm1[i]+1).toString();
+         print(s);
+         check++;
+      }
+
+      while (r != 1) { count[r - 1] = r; r--; }
+      if (!(perm1[0] == 0 || perm1[m] == m)) {
+         for (var i = 0; i < n; i++) perm[i] = perm1[i];
+
+         var flipsCount = 0;
+         var k;
+
+         while (!((k = perm[0]) == 0)) {
+            var k2 = (k + 1) >> 1;
+            for (var i = 0; i < k2; i++) {
+               var temp = perm[i]; perm[i] = perm[k - i]; perm[k - i] = temp;
+            }
+            flipsCount++;
+         }
+
+         if (flipsCount > maxFlipsCount) {
+            maxFlipsCount = flipsCount;
+            for (var i = 0; i < n; i++) maxPerm[i] = perm1[i];
+         }
+      }
+
+      while (true) {
+         if (r == n) return maxFlipsCount;
+         var perm0 = perm1[0];
+         var i = 0;
+         while (i < r) {
+            var j = i + 1;
+            perm1[i] = perm1[j];
+            i = j;
+         }
+         perm1[r] = perm0;
+
+         count[r] = count[r] - 1;
+         if (count[r] > 0) break;
+         r++;
+      }
+   }
+}
+
+var n = arguments[0];
+print("Pfannkuchen(" + n + ") = " + fannkuch(n));

bench/fasta.javascript

+// The Great Computer Language Shootout
+//  http://shootout.alioth.debian.org
+//
+//  Contributed by Ian Osgood
+
+var last = 42, A = 3877, C = 29573, M = 139968;
+
+function rand(max) {
+  last = (last * A + C) % M;
+  return max * last / M;
+}
+
+var ALU =
+  "GGCCGGGCGCGGTGGCTCACGCCTGTAATCCCAGCACTTTGG" +
+  "GAGGCCGAGGCGGGCGGATCACCTGAGGTCAGGAGTTCGAGA" +
+  "CCAGCCTGGCCAACATGGTGAAACCCCGTCTCTACTAAAAAT" +
+  "ACAAAAATTAGCCGGGCGTGGTGGCGCGCGCCTGTAATCCCA" +
+  "GCTACTCGGGAGGCTGAGGCAGGAGAATCGCTTGAACCCGGG" +
+  "AGGCGGAGGTTGCAGTGAGCCGAGATCGCGCCACTGCACTCC" +
+  "AGCCTGGGCGACAGAGCGAGACTCCGTCTCAAAAA";
+
+var IUB = {
+  a:0.27, c:0.12, g:0.12, t:0.27,
+  B:0.02, D:0.02, H:0.02, K:0.02,
+  M:0.02, N:0.02, R:0.02, S:0.02,
+  V:0.02, W:0.02, Y:0.02
+}
+
+var HomoSap = {
+  a: 0.3029549426680,
+  c: 0.1979883004921,
+  g: 0.1975473066391,
+  t: 0.3015094502008
+}
+
+function makeCumulative(table) {
+  var last = null;
+  for (var c in table) {
+    if (last) table[c] += table[last];
+    last = c;
+  }
+}
+
+function fastaRepeat(n, seq) {
+  var seqi = 0, lenOut = 60;
+  while (n>0) {
+    if (n<lenOut) lenOut = n;
+    if (seqi + lenOut < seq.length) {
+      print( seq.substring(seqi, seqi+lenOut) );
+      seqi += lenOut;
+    } else {
+      var s = seq.substring(seqi);
+      seqi = lenOut - s.length;
+      print( s + seq.substring(0, seqi) );
+    }
+    n -= lenOut;
+  }
+}
+
+function fastaRandom(n, table) {
+  var line = new Array(60);
+  makeCumulative(table);
+  while (n>0) {
+    if (n<line.length) line = new Array(n);
+    for (var i=0; i<line.length; i++) {
+      var r = rand(1);
+      for (var c in table) {
+        if (r < table[c]) {
+          line[i] = c;
+          break;
+        }
+      }
+    }
+    print( line.join('') );
+    n -= line.length;
+  }
+}
+
+var n = arguments[0]
+
+print(">ONE Homo sapiens alu")
+fastaRepeat(2*n, ALU)
+
+print(">TWO IUB ambiguity codes")
+fastaRandom(3*n, IUB)
+
+print(">THREE Homo sapiens frequency")
+fastaRandom(5*n, HomoSap)

bench/fibo.javascript

+// The Great Computer Language Shootout
+// http://shootout.alioth.debian.org/
+//
+// contributed by David Hedbor
+// modified by Isaac Gouy
+
+function fib(n) {
+    if (n < 2) return 1;
+    return fib(n-2) + fib(n-1);
+}
+
+var n = arguments[0];
+print(fib(n));
+

bench/harmonic.javascript

+// The Great Computer Language Shootout
+// http://shootout.alioth.debian.org/
+//
+// contributed by Isaac Gouy 
+
+var n = arguments[0], partialSum = 0.0;
+for (var d = 1; d <= n; d++) partialSum += 1.0/d;
+print(partialSum.toFixed(9));
+

bench/hash.javascript

+// The Great Computer Language Shootout
+// http://shootout.alioth.debian.org/
+//
+// contributed by David Hedbor
+// modified by Isaac Gouy
+
+var i, c = 0;
+var n = arguments[0];
+
+var X = new Object();
+for (i=1; i<=n; i++) {
+   X[i.toString(16)] = i;
+}
+for (i=n; i>0; i--) {
+  if (X[i.toString()]) c++;
+}
+print(c);
+

bench/hash2.javascript

+// The Great Computer Language Shootout
+// http://shootout.alioth.debian.org/
+//
+// contributed by David Hedbor
+// modified by Isaac Gouy
+
+var n = arguments[0];
+var hash1 = Object();
+var hash2 = Object();
+var arr = Array(10000);
+var idx;
+
+for (i=0; i<10000; i++) {
+  idx = "foo_"+i;
+  hash1[idx] = i;
+  // Do this here and run loop below one less since += on an undefined
+  // entry == NaN.
+  hash2[idx] = hash1[idx];
+}
+
+for (i = 1; i < n; i++) {
+  for(a in hash1) {
+    hash2[a] += hash1[a];
+  }
+}
+
+print(hash1["foo_1"], hash1["foo_9999"],
+      hash2["foo_1"], hash2["foo_9999"]);

bench/heapsort.javascript

+// The Great Computer Language Shootout
+// http://shootout.alioth.debian.org/
+//
+// contributed by David Hedbor
+// modified by Isaac Gouy
+
+var IM = 139968;
+var IA = 3877;
+var IC = 29573;
+
+var last = 42;
+
+function gen_random(max) { return(max * (last = (last * IA + IC) % IM) / IM); }
+
+function heapsort(n, ra) {
+    var l, j, ir, i;
+    var rra;
+
+    l = (n >> 1) + 1;
+    ir = n;
+    for (;;) {
+        if (l > 1) {
+            rra = ra[--l];
+        } else {
+            rra = ra[ir];
+            ra[ir] = ra[1];
+            if (--ir == 1) {
+                ra[1] = rra;
+                return;
+            }
+        }
+        i = l;
+        j = l << 1;
+        while (j <= ir) {
+            if (j < ir && ra[j] < ra[j+1]) { ++j; }
+            if (rra < ra[j]) {
+                ra[i] = ra[j];
+                j += (i = j);
+            } else {
+                j = ir + 1;
+            }
+        }
+        ra[i] = rra;
+    }
+}
+
+
+var n = arguments[0];
+var ary, i;
+    
+// create an array of N random floats
+ary = Array(n+1);
+for (i=1; i<=n; i++) {
+  ary[i] = gen_random(1.0);
+}
+heapsort(n, ary);
+print(ary[n].toFixed(10));
+Bytecode specification for the pypy javascript interpreter.
+
+We implement stack-based machine. We'll likely extend the bytecode for
+performance.
+
+LOAD_INTCONSTANT <constant>
+LOAD_FLOATCONSTANT <constant>
+LOAD_STRINGCONSTANT <constant>
+...
+
+LOAD_VARIABLE <identifier>
+
+simple identifier dereferencing
+
+LOAD_UNDEFINED, LOAD_NULL
+
+STORE <identifier>
+
+stores the last value on stack into identifierx
+
+STORE_MEMBER
+
+take from stack: right side, element and where to store and store
+where[element] = right. XXX can be optimized further for direct member
+assignement
+
+LOAD_ARRAY <num>
+
+create array out of num elements on the stack
+
+object creation:
+
+LOAD_OBJECT <num>
+
+Takes one element per one parameter from the stack and initializes
+object this way.
+
+POP
+
+pops one element from the stack.
+
+LOAD_FUNCTION <code object>
+
+loads function object (declared earlier) to a stack. used for
+function expressions.
+
+LOAD_MEMBER <name>
+
+Load a member name from the last element on a stack.
+
+LOAD_ELEMENT
+
+Take element and left side from the stack and load element
+from the left side onto the stack
+
+Arithmetic binary operations:
+(all pops two values and pushes on stack the result)
+
+ADD, SUB, MUL, DIV, MOD
+BITXOR, BITOR, BITAND
+EQ, NE, IS, ISNOT, GT, GE, LT, LE,
+RSHIT, URSHIFT, LSHIFT
+
+Unary arithmetic operations:
+(pops one value and pushes result to the stack)
+
+BITNOT,
+NOT, UPLUS, UMINUS
+
+PREDECR, POSTDECR, PREINCR, POSTINCR
+decrement and increment (++, --) prefix and postfix
+
+control flow:
+
+JUMP, LABEL, JUMP_IF_TRUE, JUMP_IF_FALSE
+
+function control flow:
+
+DECLARE_FUNCTION <code object>
+
+scope control opcodes:
+
+DECLARE_VAR <name>

jaspyon/__init__.py

+#

jaspyon/astbuilder.py

+try:
+    from pypy.rlib.rarithmetic import intmask, ovfcheck, ovfcheck_float_to_int
+except ImportError:
+    from jaspyon.rlib.rarithmetic import intmask, ovfcheck, ovfcheck_float_to_int
+
+from jaspyon.rlib.parsing.tree import RPythonVisitor, Symbol, Nonterminal
+from jaspyon.rlib.parsing.parsing import ParseError
+
+from jaspyon import operations
+
+class FakeParseError(Exception):
+    def __init__(self, pos, msg):
+        self.pos = pos
+        self.msg = msg
+
+class ASTBuilder(RPythonVisitor):
+    BINOP_TO_CLS = {
+        '+': operations.Plus,
+        '-': operations.Sub,
+        '*': operations.Mult,
+        '/': operations.Division,
+        '%': operations.Mod,
+        '^': operations.BitwiseXor,
+        '|': operations.BitwiseOr,
+        '&': operations.BitwiseAnd,
+        '&&': operations.And,
+        '||': operations.Or,
+        '==': operations.Eq,
+        '!=': operations.Ne,
+        '!==': operations.StrictNe,
+        '===': operations.StrictEq,
+        '>': operations.Gt,
+        '>=': operations.Ge,
+        '<': operations.Lt,
+        '<=': operations.Le,
+        '>>': operations.Rsh,
+        '>>>': operations.Ursh,
+        '<<': operations.Lsh,
+        '.': operations.MemberDot,
+        '[': operations.Member,
+        ',': operations.Comma,
+        'in': operations.In,
+    }
+    UNOP_TO_CLS = {
+        '~': operations.BitwiseNot,
+        '!': operations.Not,
+        '+': operations.UPlus,
+        '-': operations.UMinus,
+        'typeof': operations.Typeof,
+        'void': operations.Void,
+        'delete': operations.Delete,
+    }
+    LISTOP_TO_CLS = {
+        '[': operations.Array,
+        '{': operations.ObjectInit,
+    }
+    
+    def __init__(self):
+        self.varlists = []
+        self.funclists = []
+        self.sourcename = ""
+        RPythonVisitor.__init__(self)
+    
+    def set_sourcename(self, sourcename):
+        self.sourcename = sourcename #XXX I should call this
+    
+    def get_pos(self, node):
+        value = ''
+        source_pos = None
+        if isinstance(node, Symbol):
+            value = node.additional_info
+            source_pos = node.token.source_pos
+        elif len(node.children) > 0:
+            curr = node.children[0]
+            while not isinstance(curr, Symbol):
+                if len(curr.children):
+                    curr = curr.children[0]
+                else:
+                    break
+            else:
+                value = curr.additional_info
+                source_pos = curr.token.source_pos
+
+        # XXX some of the source positions are not perfect
+        if source_pos is None:
+            return operations.Position()
+        return operations.Position(
+                   source_pos.lineno,
+                   source_pos.columnno,
+                   source_pos.columnno + len(value))
+
+    def visit_DECIMALLITERAL(self, node):
+        pos = self.get_pos(node)
+        try:
+            
+            f = float(node.additional_info)
+            i = ovfcheck_float_to_int(f)
+            if i != f:
+                return operations.FloatNumber(pos, f)
+            else:
+                return operations.IntNumber(pos, i)
+        except (ValueError, OverflowError):
+            return operations.FloatNumber(pos, float(node.additional_info))
+    
+    def visit_HEXINTEGERLITERAL(self, node):
+        pos = self.get_pos(node)
+        return operations.IntNumber(pos, int(node.additional_info, 16))
+
+    def visit_OCTALLITERAL(self, node):
+        pos = self.get_pos(node)
+        return operations.IntNumber(pos, int(node.additional_info, 8))
+
+    def string(self,node):
+        pos = self.get_pos(node)
+        return operations.String(pos, node.additional_info)
+    visit_DOUBLESTRING = string
+    visit_SINGLESTRING = string
+
+    def binaryop(self, node):
+        left = self.dispatch(node.children[0])
+        for i in range((len(node.children) - 1) // 2):
+            op = node.children[i * 2 + 1]
+            pos = self.get_pos(op)
+            right = self.dispatch(node.children[i * 2 + 2])
+            result = self.BINOP_TO_CLS[op.additional_info](pos, left, right)
+            left = result
+        return left
+    visit_additiveexpression = binaryop
+    visit_multiplicativeexpression = binaryop
+    visit_bitwisexorexpression = binaryop
+    visit_bitwiseandexpression = binaryop
+    visit_bitwiseorexpression = binaryop
+    visit_equalityexpression = binaryop
+    visit_logicalorexpression = binaryop
+    visit_logicalandexpression = binaryop
+    visit_relationalexpression = binaryop
+    visit_shiftexpression = binaryop
+    visit_expression = binaryop
+    visit_expressionnoin = binaryop
+    
+    def visit_memberexpression(self, node):
+        if isinstance(node.children[0], Symbol) and \
+           node.children[0].additional_info == 'new': # XXX could be a identifier?
+            pos = self.get_pos(node)
+            left = self.dispatch(node.children[1])
+            right = self.dispatch(node.children[2])
+            return operations.NewWithArgs(pos, left, right)
+        else:
+            return self.binaryop(node)
+
+
+    def literalop(self, node):
+        pos = self.get_pos(node);
+        value = node.children[0].additional_info
+        if value == "true":
+            return operations.Boolean(pos, True)
+        elif value == "false":
+            return operations.Boolean(pos, False)
+        else:
+            return operations.Null(pos)
+    visit_nullliteral = literalop
+    visit_booleanliteral = literalop
+
+    def visit_unaryexpression(self, node):
+        op = node.children[0]
+        pos = self.get_pos(op)
+        child = self.dispatch(node.children[1])
+        if op.additional_info in ['++', '--']:
+            return self._dispatch_assignment(pos, child, op.additional_info,
+                                             'pre')
+        return self.UNOP_TO_CLS[op.additional_info](pos, child)
+
+    def _dispatch_assignment(self, pos, left, atype, prepost):
+        from jaspyon.operations import Identifier, Member, MemberDot,\
+             VariableIdentifier
+
+        if isinstance(left, Identifier):
+            return operations.SimpleAssignment(pos, left, None, atype, prepost)
+        elif isinstance(left, VariableIdentifier):
+            # XXX exchange to VariableAssignment
+            return operations.SimpleAssignment(pos, left, None, atype,
+                                                 prepost)
+        elif isinstance(left, Member):
+            return operations.MemberAssignment(pos, left.left, left.expr,
+                                               None, atype, prepost)
+        elif isinstance(left, MemberDot):
+            return operations.MemberDotAssignment(pos, left.left, left.name,
+                                                  None, atype, prepost)
+        else:
+            return operations.SimpleIncrement(pos, left, atype)
+
+    def visit_postfixexpression(self, node):
+        op = node.children[1]
+        pos = self.get_pos(op)
+        child = self.dispatch(node.children[0])
+        # all postfix expressions are assignments
+        return self._dispatch_assignment(pos, child, op.additional_info, 'post')
+    
+    def listop(self, node):
+        op = node.children[0]
+        pos = self.get_pos(op)
+        l = [self.dispatch(child) for child in node.children[1:]]
+        return self.LISTOP_TO_CLS[op.additional_info](pos, l)
+    visit_arrayliteral = listop # elision
+    visit_objectliteral = listop
+
+    def visit_block(self, node):
+        op = node.children[0]
+        pos = self.get_pos(op)
+        l = [self.dispatch(child) for child in node.children[1:]]
+        return operations.Block(pos, l)
+
+    def visit_arguments(self, node):
+        pos = self.get_pos(node)
+        nodes = [self.dispatch(child) for child in node.children[1:]]
+        return operations.ArgumentList(pos, nodes)
+    
+    def visit_formalparameterlist(self, node):
+        pos = self.get_pos(node)
+        nodes = [self.dispatch(child) for child in node.children]
+        return operations.ArgumentList(pos, nodes)
+    
+    def visit_variabledeclarationlist(self, node):
+        pos = self.get_pos(node)
+        nodes = [self.dispatch(child) for child in node.children]
+        return operations.VariableDeclList(pos, nodes)
+    visit_variabledeclarationlistnoin = visit_variabledeclarationlist
+    
+    def visit_propertynameandvalue(self, node):
+        pos = self.get_pos(node)
+        l = node.children[0]
+        if l.symbol == "IDENTIFIERNAME":
+            lpos = self.get_pos(l)
+            left = operations.Identifier(lpos, l.additional_info)
+        else:
+            left = self.dispatch(l)
+        right = self.dispatch(node.children[1])
+        return operations.PropertyInit(pos,left,right)
+
+    def _search_identifier(self, name):
+        lenall = len(self.varlists)
+        for i in range(lenall):
+            num = lenall - i - 1
+            vardecl = self.varlists[num]
+            if name in vardecl:
+                return i, vardecl
+        raise ValueError("xxx")
+    
+    def visit_IDENTIFIERNAME(self, node):
+        pos = self.get_pos(node)
+        name = node.additional_info
+        try:
+            t = self._search_identifier(name)
+        except ValueError:
+            pass
+        else:
+            i, vardecl = t
+            return operations.VariableIdentifier(pos, i, name)
+        return operations.Identifier(pos, name)
+    
+    def visit_program(self, node):
+        pos = self.get_pos(node)
+        body = self.dispatch(node.children[0])
+        return operations.Program(pos, body)
+    
+    def visit_variablestatement(self, node):
+        pos = self.get_pos(node)
+        body = self.dispatch(node.children[0])
+        return operations.Variable(pos, body)
+
+    def visit_throwstatement(self, node):
+        pos = self.get_pos(node)
+        exp = self.dispatch(node.children[0])
+        return operations.Throw(pos, exp)
+        
+    def visit_sourceelements(self, node):
+        pos = self.get_pos(node)
+        self.varlists.append({})
+        self.funclists.append({})
+        nodes=[]
+        for child in node.children:
+            node = self.dispatch(child)
+            if node is not None:
+                nodes.append(node)
+        var_decl = self.varlists.pop().keys()
+        func_decl = self.funclists.pop()
+        return operations.SourceElements(pos, var_decl, func_decl, nodes, self.sourcename)
+    
+    def functioncommon(self, node, declaration=True):
+        pos = self.get_pos(node)
+        i=0
+        identifier, i = self.get_next_expr(node, i)
+        parameters, i = self.get_next_expr(node, i)
+        functionbody, i = self.get_next_expr(node, i)
+        if parameters is None:
+            p = []
+        else:
+            p = [pident.get_literal() for pident in parameters.nodes]
+        funcobj = operations.FunctionStatement(pos, identifier, p, functionbody)
+        if declaration:
+            self.funclists[-1][identifier.get_literal()] = funcobj
+        return funcobj
+    
+    def visit_functiondeclaration(self, node):
+        self.functioncommon(node)
+        return None
+    
+    def visit_functionexpression(self, node):
+        return self.functioncommon(node, declaration=False)
+    
+    def visit_variabledeclaration(self, node):
+        pos = self.get_pos(node)
+        identifier = self.dispatch(node.children[0])
+        self.varlists[-1][identifier.get_literal()] = None
+        if len(node.children) > 1:
+            expr = self.dispatch(node.children[1])
+        else:
+            expr = None
+        return operations.VariableDeclaration(pos, identifier, expr)
+    visit_variabledeclarationnoin = visit_variabledeclaration
+    
+    def visit_expressionstatement(self, node):
+        pos = self.get_pos(node)
+        return operations.ExprStatement(pos, self.dispatch(node.children[0]))
+    
+    def visit_callexpression(self, node):
+        pos = self.get_pos(node)
+        left = self.dispatch(node.children[0])
+        nodelist = node.children[1:]
+        while nodelist:
+            currnode = nodelist.pop(0)
+            if isinstance(currnode, Symbol):
+                op = currnode
+                right = self.dispatch(nodelist.pop(0))
+                left = self.BINOP_TO_CLS[op.additional_info](pos, left, right)
+            else:
+                right = self.dispatch(currnode)
+                left = operations.Call(pos, left, right)
+        
+        return left
+        
+    def visit_assignmentexpression(self, node):
+        from jaspyon.operations import Identifier, Member, MemberDot,\
+             VariableIdentifier
+        pos = self.get_pos(node)
+        left = self.dispatch(node.children[0])
+        atype = node.children[1].additional_info
+        right = self.dispatch(node.children[2])
+        if isinstance(left, Identifier):
+            return operations.SimpleAssignment(pos, left, right, atype)
+        elif isinstance(left, VariableIdentifier):
+            # XXX exchange to VariableAssignment
+            return operations.SimpleAssignment(pos, left, right, atype)
+        elif isinstance(left, Member):
+            return operations.MemberAssignment(pos, left.left, left.expr,
+                                               right, atype)
+        elif isinstance(left, MemberDot):
+            return operations.MemberDotAssignment(pos, left.left, left.name,
+                                                  right, atype)
+        else:
+            raise FakeParseError(pos, "invalid lefthand expression")
+    visit_assignmentexpressionnoin = visit_assignmentexpression
+        
+    def visit_emptystatement(self, node):
+        pos = self.get_pos(node)
+        return operations.Empty(pos)
+    
+    def visit_newexpression(self, node):
+        if len(node.children) == 1:
+            return self.dispatch(node.children[0])
+        else:
+            pos = self.get_pos(node)
+            val = self.dispatch(node.children[1])
+            return operations.New(pos, val)
+    
+    def visit_ifstatement(self, node):
+        pos = self.get_pos(node)
+        condition = self.dispatch(node.children[0])
+        ifblock =  self.dispatch(node.children[1])
+        if len(node.children) > 2:
+            elseblock =  self.dispatch(node.children[2])
+        else:
+            elseblock = None
+        return operations.If(pos, condition, ifblock, elseblock)
+    
+    def visit_iterationstatement(self, node):
+        return self.dispatch(node.children[0])
+    
+    def visit_whiles(self, node):
+        pos = self.get_pos(node)
+        itertype = node.children[0].additional_info
+        if itertype == 'while':
+            condition = self.dispatch(node.children[1])
+            block = self.dispatch(node.children[2])
+            return operations.While(pos, condition, block)
+        elif itertype == "do":
+            condition = self.dispatch(node.children[2])
+            block = self.dispatch(node.children[1])
+            return operations.Do(pos, condition, block)
+        else:
+            raise NotImplementedError("Unknown while version %s" % (itertype,))
+    
+    def visit_regularfor(self, node):
+        pos = self.get_pos(node)
+        i = 1
+        setup, i = self.get_next_expr(node, i)
+        condition, i = self.get_next_expr(node, i)
+        if isinstance(condition, operations.Undefined):
+            condition = operations.Boolean(pos, True)
+        update, i = self.get_next_expr(node, i)
+        body, i = self.get_next_expr(node, i)
+        return operations.For(pos, setup, condition, update, body)
+    visit_regularvarfor = visit_regularfor
+    
+    def visit_infor(self, node):
+        from jaspyon.operations import Identifier
+        pos = self.get_pos(node)
+        left = self.dispatch(node.children[1])
+        right = self.dispatch(node.children[2])
+        body= self.dispatch(node.children[3])
+        assert isinstance(left, Identifier)
+        return operations.ForIn(pos, left.name, right, body)
+    
+    def visit_invarfor(self, node):
+        pos = self.get_pos(node)
+        left = self.dispatch(node.children[1])
+        right = self.dispatch(node.children[2])
+        body= self.dispatch(node.children[3])
+        return operations.ForVarIn(pos, left, right, body)    
+    
+    def get_next_expr(self, node, i):
+        if isinstance(node.children[i], Symbol) and \
+           node.children[i].additional_info in [';', ')', '(', '}']:
+            return None, i+1
+        else:
+            return self.dispatch(node.children[i]), i+2
+    
+    def visit_breakstatement(self, node):
+        pos = self.get_pos(node)
+        if len(node.children) > 0:
+            target = self.dispatch(node.children[0])
+        else:
+            target = None
+        return operations.Break(pos, target)
+    
+    def visit_continuestatement(self, node):
+        pos = self.get_pos(node)
+        if len(node.children) > 0:
+            target = self.dispatch(node.children[0])
+        else:
+            target = None
+        return operations.Continue(pos, target)
+    
+    def visit_returnstatement(self, node):
+        pos = self.get_pos(node)
+        if len(node.children) > 0:
+            value = self.dispatch(node.children[0])
+        else:
+            value = None
+        return operations.Return(pos, value)
+
+    def visit_conditionalexpression(self, node):
+        pos = self.get_pos(node)
+        condition = self.dispatch(node.children[0])
+        truepart = self.dispatch(node.children[2])
+        falsepart = self.dispatch(node.children[3])
+        return operations.If(pos, condition, truepart, falsepart)
+    
+    def visit_trystatement(self, node):
+        pos = self.get_pos(node)
+        tryblock = self.dispatch(node.children[0])
+        catchparam = None
+        catchblock = None
+        finallyblock = None
+        if node.children[1].children[0].additional_info == "catch":
+            catchparam = self.dispatch(node.children[1].children[1])
+            catchblock = self.dispatch(node.children[1].children[2])
+            if len(node.children) > 2:
+                finallyblock = self.dispatch(node.children[2].children[1])
+        else:
+            finallyblock = self.dispatch(node.children[1].children[1])
+        return operations.Try(pos, tryblock, catchparam, catchblock, finallyblock)
+    
+    def visit_primaryexpression(self, node):
+        pos = self.get_pos(node)
+        return operations.This(pos, 'this')
+    
+    def visit_withstatement(self, node):
+        pos = self.get_pos(node)
+        identifier = self.dispatch(node.children[0])
+        body = self.dispatch(node.children[1])
+        return operations.With(pos, identifier, body)
+        

jaspyon/baseop.py

+
+""" Base operations implementations
+"""
+
+try:
+    from pypy.rlib.rarithmetic import r_uint, intmask, INFINITY, NAN, ovfcheck,\
+                    isnan, isinf
+except ImportError:
+    from jaspyon.rlib.rarithmetic import r_uint, intmask, INFINITY, NAN, ovfcheck,\
+                    isnan, isinf
+
+from jaspyon.jsobj import W_String, W_IntNumber, W_FloatNumber,\
+     W_PrimitiveObject
+from jaspyon.execution import ThrowException, JsTypeError
+import math
+
+def plus(ctx, nleft, nright):
+    if isinstance(nleft, W_String) or isinstance(nright, W_String):
+        sleft = nleft.ToString(ctx)
+        sright = nright.ToString(ctx)
+        return W_String(sleft + sright)
+    # hot path
+    if isinstance(nleft, W_IntNumber) and isinstance(nright, W_IntNumber):
+        ileft = nleft.intval
+        iright = nright.intval
+        try:
+            return W_IntNumber(ovfcheck(ileft + iright))
+        except OverflowError:
+            return W_FloatNumber(float(ileft) + float(iright))
+    else:
+        fleft = nleft.ToNumber(ctx)
+        fright = nright.ToNumber(ctx)
+        return W_FloatNumber(fleft + fright)
+
+def increment(ctx, nleft, constval=1):
+    if isinstance(nleft, W_IntNumber):
+        return W_IntNumber(nleft.intval + constval)
+    else:
+        return plus(ctx, nleft, W_IntNumber(constval))
+
+def sub(ctx, nleft, nright):
+    if isinstance(nleft, W_IntNumber) and isinstance(nright, W_IntNumber):
+        ileft = nleft.ToInt32(ctx)
+        iright = nright.ToInt32(ctx)
+        try:
+            return W_IntNumber(ovfcheck(ileft - iright))
+        except OverflowError:
+            return W_FloatNumber(float(ileft) - float(iright))
+    fleft = nleft.ToNumber(ctx)
+    fright = nright.ToNumber(ctx)
+    return W_FloatNumber(fleft - fright)
+
+def mult(ctx, nleft, nright):
+    if isinstance(nleft, W_IntNumber) and isinstance(nright, W_IntNumber):
+        ileft = nleft.ToInt32(ctx)
+        iright = nright.ToInt32(ctx)
+        try:
+            return W_IntNumber(ovfcheck(ileft * iright))
+        except OverflowError:
+            return W_FloatNumber(float(ileft) * float(iright))
+    fleft = nleft.ToNumber(ctx)
+    fright = nright.ToNumber(ctx)
+    return W_FloatNumber(fleft * fright)
+
+def mod(ctx, nleft, nright): # XXX this one is really not following spec
+    fleft = nleft.ToNumber(ctx)
+    fright = nright.ToNumber(ctx)
+    return W_FloatNumber(math.fmod(fleft, fright))
+
+def division(ctx, nleft, nright):
+    # XXX optimise for ints and floats
+    fleft = nleft.ToNumber(ctx)
+    fright = nright.ToNumber(ctx)
+    if fright == 0:
+        if fleft < 0:
+            val = -INFINITY
+        elif fleft == 0:
+            val = NAN
+        else:
+            val = INFINITY
+    else:
+        val = fleft / fright
+    return W_FloatNumber(val)
+
+def compare(ctx, x, y):
+    if isinstance(x, W_IntNumber) and isinstance(y, W_IntNumber):
+        return x.intval > y.intval
+    if isinstance(x, W_FloatNumber) and isinstance(y, W_FloatNumber):
+        if isnan(x.floatval) or isnan(y.floatval):
+            return -1
+        return x.floatval > y.floatval
+    s1 = x.ToPrimitive(ctx, 'Number')
+    s2 = y.ToPrimitive(ctx, 'Number')
+    if not (isinstance(s1, W_String) and isinstance(s2, W_String)):
+        s4 = s1.ToNumber(ctx)
+        s5 = s2.ToNumber(ctx)
+        if isnan(s4) or isnan(s5):
+            return False
+        return s4 > s5
+    else:
+        s4 = s1.ToString(ctx)
+        s5 = s2.ToString(ctx)
+        return s4 > s5
+
+def compare_e(ctx, x, y):
+    if isinstance(x, W_IntNumber) and isinstance(y, W_IntNumber):
+        return x.intval >= y.intval
+    if isinstance(x, W_FloatNumber) and isinstance(y, W_FloatNumber):
+        if isnan(x.floatval) or isnan(y.floatval):
+            return -1
+        return x.floatval >= y.floatval
+    s1 = x.ToPrimitive(ctx, 'Number')
+    s2 = y.ToPrimitive(ctx, 'Number')
+    if not (isinstance(s1, W_String) and isinstance(s2, W_String)):
+        s4 = s1.ToNumber(ctx)
+        s5 = s2.ToNumber(ctx)
+        if isnan(s4) or isnan(s5):
+            return False
+        return s4 >= s5
+    else:
+        s4 = s1.ToString(ctx)
+        s5 = s2.ToString(ctx)
+        return s4 >= s5
+
+def AbstractEC(ctx, x, y):
+    """
+    Implements the Abstract Equality Comparison x == y
+    trying to be fully to the spec
+    """
+    if isinstance(x, W_IntNumber) and isinstance(y, W_IntNumber):
+        return x.intval == y.intval
+    if isinstance(x, W_FloatNumber) and isinstance(y, W_FloatNumber):
+        if isnan(x.floatval) or isnan(y.floatval):
+            return False
+        return x.floatval == y.floatval
+    type1 = x.type()
+    type2 = y.type()
+    if type1 == type2:
+        if type1 == "undefined" or type1 == "null":
+            return True
+        if type1 == "number":
+            n1 = x.ToNumber(ctx)
+            n2 = y.ToNumber(ctx)
+            if isnan(n1) or isnan(n2):
+                return False
+            if n1 == n2:
+                return True
+            return False
+        elif type1 == "string":
+            return x.ToString(ctx) == y.ToString(ctx)
+        elif type1 == "boolean":
+            return x.ToBoolean() == x.ToBoolean()
+        # XXX rethink it here
+        return x.ToString(ctx) == y.ToString(ctx)
+    else:
+        #step 14
+        if (type1 == "undefined" and type2 == "null") or \
+           (type1 == "null" and type2 == "undefined"):
+            return True
+        if type1 == "number" and type2 == "string":
+            return AbstractEC(ctx, x, W_FloatNumber(y.ToNumber(ctx)))
+        if type1 == "string" and type2 == "number":
+            return AbstractEC(ctx, W_FloatNumber(x.ToNumber(ctx)), y)
+        if type1 == "boolean":
+            return AbstractEC(ctx, W_FloatNumber(x.ToNumber(ctx)), y)
+        if type2 == "boolean":
+            return AbstractEC(ctx, x, W_FloatNumber(y.ToNumber(ctx)))
+        if (type1 == "string" or type1 == "number") and \
+            type2 == "object":
+            return AbstractEC(ctx, x, y.ToPrimitive(ctx))
+        if (type2 == "string" or type2 == "number") and \
+            type1 == "object":
+            return AbstractEC(ctx, x.ToPrimitive(ctx), y)
+        return False
+            
+        
+    objtype = x.GetValue().type()
+    if objtype == y.GetValue().type():
+        if objtype == "undefined" or objtype == "null":
+            return True
+        
+    if isinstance(x, W_String) and isinstance(y, W_String):
+        r = x.ToString(ctx) == y.ToString(ctx)
+    else:
+        r = x.ToNumber(ctx) == y.ToNumber(ctx)
+    return r
+
+def StrictEC(ctx, x, y):
+    """
+    Implements the Strict Equality Comparison x === y
+    trying to be fully to the spec
+    """
+    type1 = x.type()
+    type2 = y.type()
+    if type1 != type2:
+        return False
+    if type1 == "undefined" or type1 == "null":
+        return True
+    if type1 == "number":
+        n1 = x.ToNumber(ctx)
+        n2 = y.ToNumber(ctx)
+        if isnan(n1) or isnan(n2):
+            return False
+        if n1 == n2:
+            return True
+        return False
+    if type1 == "string":
+        return x.ToString(ctx) == y.ToString(ctx)
+    if type1 == "boolean":
+        return x.ToBoolean() == x.ToBoolean()
+    return x == y
+
+
+def commonnew(ctx, obj, args):
+    if not isinstance(obj, W_PrimitiveObject):
+        raise ThrowException(W_String('it is not a constructor'))
+    try:
+        res = obj.Construct(ctx=ctx, args=args)
+    except JsTypeError:
+        raise ThrowException(W_String('it is not a constructor'))
+    return res
+
+def uminus(obj, ctx):
+    if isinstance(obj, W_IntNumber):
+        return W_IntNumber(-obj.intval)
+    return W_FloatNumber(-obj.ToNumber(ctx))

jaspyon/console.py

+#!/usr/bin/env python
+
+import os, sys
+from jaspyon.interpreter import load_source, Interpreter, load_file
+from jaspyon.jsparser import parse, ParseError
+from jaspyon.jsobj import W_NewBuiltin, W_String, ThrowException, w_Undefined
+from jaspyon.rlib.streamio import open_file_as_stream
+
+def printmessage(msg):
+    if msg is not None:
+        os.write(1, msg)
+
+def readline():
+    result = []
+    while 1:
+        s = os.read(0, 1)
+        if s == '\n':
+            break
+        else:
+            result.append(s)
+        if s == '':
+            if len(result) > 1:
+                break
+            raise SystemExit
+    return ''.join(result)
+
+class W_Quit(W_NewBuiltin):
+    def Call(self, ctx, args=[], this=None):
+        raise SystemExit, 0
+
+class W_Load(W_NewBuiltin):
+    def __init__(self, ctx, interpreter):
+        W_NewBuiltin.__init__(self, ctx)
+        self.interpreter = interpreter
+    
+    def Call(self, ctx, args=[], this=None):
+        if len(args) >= 1:
+            filename = args[0].ToString(self.interpreter.global_context)
+            try:
+                assert filename is not None
+                program = load_file(filename)
+            except EnvironmentError, e:
+                msg = W_String("Can't open %s: %s" % (filename, e))
+                raise ThrowException(msg)
+            self.interpreter.run(program)
+        return w_Undefined
+
+class W_ReadLine(W_NewBuiltin):
+    def Call(self, ctx, args=[], this=None):
+        return W_String(readline())
+
+class JSConsole(object):
+    prompt_ok = 'js> '
+    prompt_more = '... '
+
+    def __init__(self):
+        interp = Interpreter()
+        ctx = interp.global_context
+        
+        interp.w_Global.Put(ctx, 'quit', W_Quit(ctx))
+        interp.w_Global.Put(ctx, 'load', W_Load(ctx, interp))
+        interp.w_Global.Put(ctx, 'readline', W_ReadLine(ctx))
+        
+        self.interpreter = interp
+    
+    def runsource(self, source, filename='<input>'):
+        try:
+            ast = load_source(source, filename)
+        except ParseError, exc:
+            if exc.source_pos.i == len(source):
+                # more input needed
+                return True
+            else:
+                # syntax error
+                self.showsyntaxerror(filename, exc)
+                return False
+        
+        # execute it
+        self.runcode(ast)
+        return False
+    
+    def runcode(self, ast):
+        """Run the javascript code in the AST. All exceptions raised
+        by javascript code must be caught and handled here. When an
+        exception occurs, self.showtraceback() is called to display a
+        traceback.
+        """
+        try:
+            res = self.interpreter.run(ast, interactive=True)
+            if res is not None and res != w_Undefined:
+                try:
+                    printmessage(res.ToString(self.interpreter.global_context))
+                except ThrowException, exc:
+                    printmessage(exc.exception.ToString(self.interpreter.global_context))
+                printmessage('\n')
+        except SystemExit:
+            raise
+        except ThrowException, exc:
+            self.showtraceback(exc)
+    
+    def showtraceback(self, exc):
+        printmessage(exc.exception.ToString(self.interpreter.global_context))
+        printmessage('\n')
+    
+    def showsyntaxerror(self, filename, exc):
+        printmessage(' ' * 4 + \
+                  ' ' * exc.source_pos.columnno + \
+                  '^\n')
+        printmessage('Syntax Error\n')
+    
+    def interact(self):
+        printmessage('PyPy JavaScript Interpreter\n')
+        printmessage(self.prompt_ok)
+        
+        # input for the current statement
+        lines = []
+        
+        while True:
+            try:
+                line = readline()
+            except SystemExit, e:
+                printmessage('\n')
+                return
+            
+            lines.append(line)
+            
+            source = ''.join(lines)
+            need_more = self.runsource(source)
+            
+            if need_more:
+                printmessage(self.prompt_more)
+            else:
+                printmessage(self.prompt_ok)
+                lines = []
+
+if __name__ == '__main__':
+    console = JSConsole()
+    console.interact()

jaspyon/constants.py

+escapes = [
+    r'\n',
+    r'\r',
+    r'\f',
+    r'\v',
+    r'\ ',
+    r'\t',
+    r"\'",
+    r'\b',
+    r'\"',
+    r'\\',
+    r'\u'] #don't know what to do with these
+
+codes = [
+    '\n',
+    '\r',
+    '\f',
+    '\v',
+    '\ ',
+    '\t',
+    "'",
+    "\b",
+    '"',
+    '\\',
+    'u']
+
+escapedict = dict(zip(codes, escapes))
+unescapedict = dict(zip(escapes, codes))
+

jaspyon/execution.py

+
+class JsBaseExcept(Exception):
+    pass    
+
+#XXX Just an idea for now
+class JsRuntimeExcept(Exception):
+    def __init__(self, pos, message, exception_object):
+        self.pos = pos
+        self.message = message
+        self.exception_object = exception_object # JS Exception Object
+
+class ReturnException(Exception):
+    def __init__(self, value):
+        self.value = value
+
+#class ExecutionReturned(JsBaseExcept):
+#    def __init__(self, type='normal', value=None, identifier=None):
+#        self.type = type
+#        self.value = value
+#        self.identifier = identifier
+
+class ThrowException(JsBaseExcept):
+    def __init__(self, exception):
+        self.exception = exception
+        self.args = [exception]
+
+class JsTypeError(JsBaseExcept):
+    pass
+
+class RangeError(JsBaseExcept): pass

jaspyon/interactive.py

+#!/usr/bin/env python
+# encoding: utf-8
+"""
+Ja(va)s(cript)py(th)on interactive interpreter
+"""
+
+import sys
+from os.path import exists, join, dirname, basename, abspath
+
+# insert jaspyon package on the sys.path
+if basename(dirname(abspath(__file__))) == 'jaspyon' and exists(join(dirname(__file__), '__init__.py')):
+    sys.path.insert(0, '..')
+
+import getopt
+from jaspyon.interpreter import load_source, Interpreter, load_file
+from jaspyon.jsparser import parse, ParseError
+from jaspyon.jsobj import W_Builtin, W_String, ThrowException, \
+                               w_Undefined, W_Boolean
+from jaspyon.rlib.streamio import open_file_as_stream
+
+import code
+sys.ps1 = 'js> '
+sys.ps2 = '... '
+
+try:
+    # Setup Readline
+    import readline
+    import os
+    histfile = join(os.environ["HOME"], ".jaspyonhist")
+    try:
+        getattr(readline, "clear_history", lambda : None)()
+        readline.read_history_file(histfile)
+    except IOError:
+        pass
+    import atexit
+    atexit.register(readline.write_history_file, histfile)
+except ImportError:
+    pass
+
+DEBUG = False
+
+def debugjs(ctx, args, this):
+    global DEBUG
+    DEBUG = not DEBUG
+    return W_Boolean(DEBUG)
+
+def loadjs(ctx, args, this):
+    filename = args[0].ToString()
+    t = load_file(filename)
+    return t.execute(ctx)
+
+def tracejs(ctx, args, this):
+    arguments = args
+    import pdb
+    pdb.set_trace()
+    return w_Undefined
+
+def quitjs(ctx, args, this):
+    sys.exit(0)
+    
+class JSInterpreter(code.InteractiveConsole):
+    def __init__(self, locals=None, filename="<console>"):
+        code.InteractiveConsole.__init__(self, locals, filename)
+        self.interpreter = Interpreter()
+        ctx = self.interpreter.global_context
+        self.interpreter.w_Global.Put(ctx, 'quit', W_Builtin(quitjs))
+        self.interpreter.w_Global.Put(ctx, 'load', W_Builtin(loadjs))
+        self.interpreter.w_Global.Put(ctx, 'trace', W_Builtin(tracejs))
+        self.interpreter.w_Global.Put(ctx, 'debug', W_Builtin(debugjs))
+
+    def runcodefromfile(self, filename):
+        f = open_file_as_stream(filename)
+        self.runsource(f.readall(), filename)
+        f.close()
+
+    def runcode(self, ast):
+        """Run the javascript code in the AST. All exceptions raised
+        by javascript code must be caught and handled here. When an
+        exception occurs, self.showtraceback() is called to display a
+        traceback.
+        """
+        try:
+            res = self.interpreter.run(ast, interactive=True)
+            if DEBUG:
+                print self.interpreter._code
+            if res not in (None, w_Undefined):
+                try:
+                    if DEBUG:
+                        print repr(res)
+                    print res.ToString(self.interpreter.w_Global)
+                except ThrowException, exc:
+                    print exc.exception.ToString(self.interpreter.w_Global)
+        except SystemExit:
+            raise
+        except ThrowException, exc:
+            self.showtraceback(exc)
+        else:
+            if code.softspace(sys.stdout, 0):
+                print
+
+    def runsource(self, source, filename="<input>"):
+        """Parse and run source in the interpreter.
+
+        One of these cases can happen:
+        1) The input is incorrect. Prints a nice syntax error message.
+        2) The input in incomplete. More input is required. Returns None.
+        3) The input is complete. Executes the source code.
+        """
+        try:
+            ast = load_source(source, filename)
+        except ParseError, exc:
+            if exc.source_pos.i == len(source):
+                # Case 2
+                return True # True means that more input is needed
+            else:
+                # Case 1
+                self.showsyntaxerror(filename, exc)
+                return False
+
+        # Case 3
+        self.runcode(ast)
+        return False
+
+    def showtraceback(self, exc):
+        # XXX format exceptions nicier
+        print exc.exception.ToString()
+
+    def showsyntaxerror(self, filename, exc):
+        # XXX format syntax errors nicier
+        print ' '*4 + \
+              ' '*exc.source_pos.columnno + \
+              '^'
+        print 'Syntax Error:', exc.errorinformation.failure_reasons
+
+    def interact(self, banner=None):
+        if banner is None:
+            banner = __doc__
+        code.InteractiveConsole.interact(self, banner)
+
+def main(inspect=False, files=[]):
+    jsi = JSInterpreter()
+    for filename in files:
+        jsi.runcodefromfile(filename)
+    if (not files) or inspect:
+        jsi.interact()
+
+def script_main():
+    from optparse import OptionParser
+    parser = OptionParser(usage='%prog [options] [files] ...',
+                          description=__doc__)
+    parser.add_option('-i', dest='inspect',
+                    action='store_true', default=False,
+                    help='inspect interactively after running script')
+
+    # ... (add other options)
+    opts, args = parser.parse_args()
+
+    if args:
+        main(inspect=opts.inspect, files=args)
+    else:
+        main(inspect=opts.inspect)
+    sys.exit(0)
+    
+if __name__ == '__main__':
+    script_main()

jaspyon/interpreter.py

+
+import math
+from jaspyon.jsparser import parse, ParseError
+from jaspyon.astbuilder import ASTBuilder
+from jaspyon.jsobj import global_context, W_Object,\
+     w_Undefined, W_NewBuiltin, W_IntNumber, w_Null, create_object, W_Boolean,\
+     W_FloatNumber, W_String, W_Builtin, W_Array, w_Null, newbool,\
+     isnull_or_undefined, W_PrimitiveObject, W_ListObject, W_BaseNumber,\
+     DE, DD, RO, IT
+from jaspyon.execution import ThrowException, JsTypeError
+from jaspyon.jscode import JsCode
+
+try:
+    from pypy.rlib.objectmodel import we_are_translated
+    from pypy.rlib.streamio import open_file_as_stream
+    from pypy.rlib.rarithmetic import NAN, INFINITY, isnan, isinf, r_uint
+    from pypy.rlib.objectmodel import specialize
+    from pypy.rlib.listsort import TimSort
+except ImportError:
+    from jaspyon.rlib.objectmodel import we_are_translated
+    from jaspyon.rlib.streamio import open_file_as_stream
+    from jaspyon.rlib.rarithmetic import NAN, INFINITY, isnan, isinf, r_uint
+    from jaspyon.rlib.objectmodel import specialize
+    from jaspyon.rlib.listsort import TimSort
+    
+
+ASTBUILDER = ASTBuilder()
+
+def writer(x):
+    print x
+
+def load_source(script_source, sourcename):
+    temp_tree = parse(script_source)
+    ASTBUILDER.sourcename = sourcename
+    return ASTBUILDER.dispatch(temp_tree)
+
+def load_file(filename):
+    f = open_file_as_stream(filename)
+    t = load_source(f.readall(), filename)
+    f.close()
+    return t
+
+class W_NativeObject(W_Object):
+    def __init__(self, Class, Prototype, ctx=None,
+                 Value=w_Undefined, callfunc=None):
+        W_Object.__init__(self, ctx, Prototype,
+                          Class, Value, callfunc)
+    
+class W_ObjectObject(W_NativeObject):
+    def Call(self, ctx, args=[], this=None):
+        if len(args) >= 1 and not isnull_or_undefined(args[0]):
+            return args[0].ToObject(ctx)
+        else:
+            return self.Construct(ctx)
+
+    def Construct(self, ctx, args=[]):
+        if (len(args) >= 1 and not args[0] is w_Undefined and not
+            args[0] is w_Null):
+            # XXX later we could separate builtins and normal objects
+            return args[0].ToObject(ctx)
+        return create_object(ctx, 'Object')
+
+class W_BooleanObject(W_NativeObject):
+    def Call(self, ctx, args=[], this=None):
+        if len(args) >= 1 and not isnull_or_undefined(args[0]):
+            return newbool(args[0].ToBoolean())
+        else:
+            return newbool(False)
+
+    def Construct(self, ctx, args=[]):
+        if len(args) >= 1 and not isnull_or_undefined(args[0]):
+            Value = newbool(args[0].ToBoolean())
+            return create_object(ctx, 'Boolean', Value = Value)
+        return create_object(ctx, 'Boolean', Value = newbool(False))
+
+class W_NumberObject(W_NativeObject):
+    def Call(self, ctx, args=[], this=None):
+        if len(args) >= 1 and not isnull_or_undefined(args[0]):
+            return W_FloatNumber(args[0].ToNumber(ctx))
+        elif len(args) >= 1 and args[0] is w_Undefined:
+            return W_FloatNumber(NAN)
+        else:
+            return W_FloatNumber(0.0)
+
+    def ToNumber(self, ctx):
+        return 0.0
+
+    def Construct(self, ctx, args=[]):
+        if len(args) >= 1 and not isnull_or_undefined(args[0]):
+            Value = W_FloatNumber(args[0].ToNumber(ctx))
+            return create_object(ctx, 'Number', Value = Value)
+        return create_object(ctx, 'Number', Value = W_FloatNumber(0.0))
+
+class W_StringObject(W_NativeObject):
+    length = 1
+    def Call(self, ctx, args=[], this=None):
+        if len(args) >= 1:
+            return W_String(args[0].ToString(ctx))
+        else:
+            return W_String('')
+
+    def Construct(self, ctx, args=[]):
+        if len(args) >= 1:
+            Value = W_String(args[0].ToString(ctx))
+        else:
+            Value = W_String('')
+        return Value.ToObject(ctx)
+
+def create_array(ctx, elements=[]):
+    proto = ctx.get_global().Get(ctx, 'Array').Get(ctx, 'prototype')
+    array = W_Array(ctx, Prototype=proto, Class = proto.Class)
+    i = 0
+    while i < len(elements):
+        array.Put(ctx, str(i), elements[i])
+        i += 1
+    
+    return array
+
+class W_ArrayObject(W_NativeObject):
+    def Call(self, ctx, args=[], this=None):
+        if len(args) == 1 and isinstance(args[0], W_BaseNumber):
+            array = create_array(ctx)
+            array.Put(ctx, 'length', args[0])
+        else:
+            array = create_array(ctx, args)
+        return array
+
+    def Construct(self, ctx, args=[]):
+        return self.Call(ctx, args)
+
+TEST = False
+
+class W_Eval(W_NewBuiltin):
+    length = 1
+    def Call(self, ctx, args=[], this=None):
+        if len(args) >= 1:
+            if  isinstance(args[0], W_String):
+                src = args[0].strval
+            else:
+                return args[0]
+        else:
+            return w_Undefined
+        
+        try:
+            node = load_source(src, 'evalcode')
+        except ParseError, e:
+            raise ThrowException(W_String('SyntaxError: '+str(e)))
+
+        bytecode = JsCode()
+        node.emit(bytecode)
+        return bytecode.run(ctx, retlast=True)
+
+class W_ParseInt(W_NewBuiltin):
+    length = 1
+    def Call(self, ctx, args=[], this=None):
+        if len(args) < 1:
+            return W_FloatNumber(NAN)
+        s = args[0].ToString(ctx).strip(" ")
+        if len(args) > 1:
+            radix = args[1].ToInt32(ctx)
+        else:
+            radix = 10
+        if len(s) >= 2 and (s.startswith('0x') or s.startswith('0X')) :
+            radix = 16
+            s = s[2:]
+        if s == '' or radix < 2 or radix > 36:
+            return W_FloatNumber(NAN)
+        try:
+            n = int(s, radix)
+        except ValueError:
+            return W_FloatNumber(NAN)
+        return W_IntNumber(n)
+
+class W_ParseFloat(W_NewBuiltin):
+    length = 1
+    def Call(self, ctx, args=[], this=None):
+        if len(args) < 1:
+            return W_FloatNumber(NAN)
+        s = args[0].ToString(ctx).strip(" ")
+        try:
+            n = float(s)
+        except ValueError:
+            n = NAN
+        return W_FloatNumber(n)
+
+def printjs(ctx, args, this):
+    writer(",".join([i.ToString(ctx) for i in args]))
+    return w_Undefined
+
+def isnanjs(ctx, args, this):
+    if len(args) < 1:
+        return newbool(True)
+    return newbool(isnan(args[0].ToNumber(ctx)))
+
+def isfinitejs(ctx, args, this):
+    if len(args) < 1:
+        return newbool(True)
+    n = args[0].ToNumber(ctx)
+    if  isinf(n) or isnan(n):
+        return newbool(False)
+    else:
+        return newbool(True)
+        
+def absjs(ctx, args, this):
+    val = args[0]
+    if isinstance(val, W_IntNumber):
+        if val.intval > 0:
+            return val # fast path
+        return W_IntNumber(-val.intval)
+    return W_FloatNumber(abs(args[0].ToNumber(ctx)))
+
+def floorjs(ctx, args, this):
+    return W_IntNumber(int(math.floor(args[0].ToNumber(ctx))))
+
+def powjs(ctx, args, this):
+    return W_FloatNumber(math.pow(args[0].ToNumber(ctx), args[1].ToNumber(ctx)))
+
+def sqrtjs(ctx, args, this):
+    return W_FloatNumber(math.sqrt(args[0].ToNumber(ctx)))
+
+def versionjs(ctx, args, this):
+    return w_Undefined
+
+def _ishex(ch):
+    return ((ch >= 'a' and ch <= 'f') or (ch >= '0' and ch <= '9') or
+            (ch >= 'A' and ch <= 'F'))
+
+def unescapejs(ctx, args, this):
+    # XXX consider using StringBuilder here
+    res = []
+    if not isinstance(args[0], W_String):
+        raise JsTypeError(W_String("Expected string"))
+    strval = args[0].strval
+    lgt = len(strval)
+    i = 0
+    while i < lgt:
+        ch = strval[i]
+        if ch == '%':
+            if (i + 2 < lgt and _ishex(strval[i+1]) and _ishex(strval[i+2])):
+                ch = chr(int(strval[i + 1] + strval[i + 2], 16))
+                i += 2
+            elif (i + 5 < lgt and strval[i + 1] == 'u' and
+                  _ishex(strval[i + 2]) and _ishex(strval[i + 3]) and
+                  _ishex(strval[i + 4]) and _ishex(strval[i + 5])):
+                ch = chr(int(strval[i+2:i+6], 16))
+                i += 5
+        i += 1
+        res.append(ch)
+    return W_String(''.join(res))
+
+class W_ToString(W_NewBuiltin):
+    def Call(self, ctx, args=[], this=None):
+        return W_String("[object %s]"%this.Class)
+
+class W_ValueOf(W_NewBuiltin):
+    length = 0
+    def Call(self, ctx, args=[], this=None):
+        return this
+
+class W_HasOwnProperty(W_NewBuiltin):
+    def Call(self, ctx, args=[], this=None):
+        if len(args) >= 1:
+            propname = args[0].ToString(ctx)
+            if propname in this.propdict:
+                return newbool(True)
+        return newbool(False)
+
+class W_IsPrototypeOf(W_NewBuiltin):
+    def Call(self, ctx, args=[], this=None):
+        if len(args) >= 1 and isinstance(args[0], W_PrimitiveObject):
+            O = this
+            V = args[0].Prototype
+            while V is not None:
+                if O == V:
+                    return newbool(True)
+                V = V.Prototype
+        return newbool(False)
+
+class W_PropertyIsEnumerable(W_NewBuiltin):
+    def Call(self, ctx, args=[], this=None):
+        if len(args) >= 1:
+            propname = args[0].ToString(ctx)
+            if propname in this.propdict and not this.propdict[propname].flags & DE:
+                return newbool(True)
+        return newbool(False)
+
+class W_Function(W_NewBuiltin):
+    def Call(self, ctx, args=[], this=None):
+        tam = len(args)
+        if tam >= 1:
+            fbody  = args[tam-1].ToString(ctx)
+            argslist = []
+            for i in range(tam-1):
+                argslist.append(args[i].ToString(ctx))
+            fargs = ','.join(argslist)
+            functioncode = "function (%s) {%s}"%(fargs, fbody)
+        else:
+            functioncode = "function () {}"
+        #remove program and sourcelements node
+        funcnode = parse(functioncode).children[0].children[0]
+        ast = ASTBUILDER.dispatch(funcnode)
+        bytecode = JsCode()
+        ast.emit(bytecode)
+        return bytecode.run(ctx, retlast=True)
+    
+    def Construct(self, ctx, args=[]):
+        return self.Call(ctx, args, this=None)
+
+functionstring= 'function (arguments go here!) {\n'+ \
+                '    [lots of stuff :)]\n'+ \
+                '}'
+class W_FToString(W_NewBuiltin):
+    def Call(self, ctx, args=[], this=None):
+        if this.Class == 'Function':
+            return W_String(functionstring)
+        else:
+            raise JsTypeError('this is not a function object')
+
+class W_Apply(W_NewBuiltin):
+    def Call(self, ctx, args=[], this=None):
+        try:
+            if isnull_or_undefined(args[0]):
+                thisArg = ctx.get_global()
+            else:
+                thisArg = args[0].ToObject(ctx)
+        except IndexError:
+            thisArg = ctx.get_global()
+        
+        try:
+            arrayArgs = args[1]
+            if isinstance(arrayArgs, W_ListObject):
+                callargs = arrayArgs.tolist()
+            elif isnull_or_undefined(arrayArgs):
+                callargs = []
+            else:
+                raise JsTypeError('arrayArgs is not an Array or Arguments object')
+        except IndexError:
+            callargs = []
+        return this.Call(ctx, callargs, this=thisArg)
+
+class W_Call(W_NewBuiltin):
+    def Call(self, ctx, args=[], this=None):
+        if len(args) >= 1:
+            if isnull_or_undefined(args[0]):
+                thisArg = ctx.get_global()
+            else:
+                thisArg = args[0]
+            callargs = args[1:]
+        else:
+            thisArg = ctx.get_global()
+            callargs = []
+        return this.Call(ctx, callargs, this = thisArg)
+
+class W_ValueToString(W_NewBuiltin):
+    "this is the toString function for objects with Value"
+    mytype = ''
+    def Call(self, ctx, args=[], this=None):
+        if this.Value.type() != self.mytype:
+            raise JsTypeError('Wrong type')
+        return W_String(this.Value.ToString(ctx))
+
+
+class W_NumberValueToString(W_ValueToString):
+    mytype = 'number'
+
+class W_BooleanValueToString(W_ValueToString):
+    mytype = 'boolean'
+
+class W_StringValueToString(W_ValueToString):
+    mytype = 'string'
+
+
+@specialize.memo()
+def get_value_of(type):
+    class W_ValueValueOf(W_NewBuiltin):
+        "this is the valueOf function for objects with Value"
+        def Call(self, ctx, args=[], this=None):
+            if type != this.Class:
+                raise JsTypeError('%s.prototype.valueOf called with incompatible type' % self.type())
+            return this.Value
+    return W_ValueValueOf
+
+class W_FromCharCode(W_NewBuiltin):
+    length = 1
+    def Call(self, ctx, args=[], this=None):
+        temp = []
+        for arg in args:
+            i = arg.ToInt32(ctx) % 65536 # XXX should be uint16
+            temp.append(chr(i))
+        return W_String(''.join(temp))
+
+class W_CharAt(W_NewBuiltin):
+    length = 1
+    def Call(self, ctx, args=[], this=None):
+        string = this.ToString(ctx)
+        if len(args)>=1:
+            pos = args[0].ToInt32(ctx)
+            if (not pos >=0) or (pos > len(string) - 1):
+                return W_String('')
+        else:
+            return W_String('')
+        return W_String(string[pos])
+
+class W_CharCodeAt(W_NewBuiltin):
+    def Call(self, ctx, args=[], this=None):
+        string = this.ToString(ctx)
+        if len(args)>=1:
+            pos = args[0].ToInt32(ctx)
+            if pos < 0 or pos > len(string) - 1:
+                return W_FloatNumber(NAN)
+        else:
+            return W_FloatNumber(NAN)
+        char = string[pos]
+        return W_IntNumber(ord(char))
+
+class W_Concat(W_NewBuiltin):
+    def Call(self, ctx, args=[], this=None):
+        string = this.ToString(ctx)
+        others = [obj.ToString(ctx) for obj in args]
+        string += ''.join(others)
+        return W_String(string)
+
+class W_IndexOf(W_NewBuiltin):
+    length = 1
+    def Call(self, ctx, args=[], this=None):
+        string = this.ToString(ctx)
+        if len(args) < 1:
+            return W_IntNumber(-1)
+        substr = args[0].ToString(ctx)
+        size = len(string)
+        subsize = len(substr)
+        if len(args) < 2:
+            pos = 0
+        else:
+            pos = args[1].ToInteger(ctx)
+        pos = int(min(max(pos, 0), size))
+        assert pos >= 0
+        return W_IntNumber(string.find(substr, pos))
+
+class W_LastIndexOf(W_NewBuiltin):
+    length = 1
+    def Call(self, ctx, args=[], this=None):
+        string = this.ToString(ctx)
+        if len(args) <