Nojotalk Byte Code

[ Home ]

Nojotalk source code is converted into byte code at compile-time. Every function body (as well as main, or top level) corresponds to a string of 16-bit characters. The most significant bit is the operator bit, which is zero for operators and keywords. The next most significant bit is zero for local variables and parameters, and one for global variables. The low order 14 bits for local variables and parameters encode the stack offset. The low order 14 bits for global variables/functions encode the offset value. The following JavaScript code is a sneak preview of the byte code processor, which will eventually be written in Python.

var stack;
var stkbase;
var funcarray;
var glbarray;
var currfunc;
var currpos;
var currbody;
var parmcount;
var varcount;

function mainloop() {
  stack = [];
  stkbase = 0;
  callfunc(0, 0);
  initfuncs();
  initops();
  do {
    token = currbody.charCodeAt(currpos);
    if (token < 32768) {
      operator = operators[token];
      operator();
    }
    else {
      offset = token - 32768;
      push(offset);
    }
    currpos += 1;
  }
  while (stack.length > 0);
}

function push(val) {
  stack.push(val);
}
function pop() {
  return stack.pop();
}
function jump(addr) {
  currpos = addr;
}
function callfunc(funcidx, posidx) {
  currfunc = funcidx;
  currpos = posidx;
  funcobj = funcarray[currfunc];
  currbody = funcobj.body;
  parmcount = funcobj.parmcount;
  varcount = funcobj.varcount;
}
function assign(addr, val) {
  addr -= 32768;
  if (addr < 16384) {
    stack[stkbase + addr] = val;
  }
  else {
    addr -= 16384;
    glbarray[addr] = val;
  }
}
function getval(addr) {
  addr -= 32768;
  if (addr < 16384) {
    val = stack[stkbase + addr];
  }
  else {
    addr -= 16384;
    val = glbarray[addr];
  }
  return val;
}
function initfuncs() {
  typeof_fn = function() {
    push(typeof(pop()));
  };
  to_number = function() {
    push(+pop());
  };
  negate = function() {
    push(-pop());
  };
  not = function() {
    push(!pop());
  };
  multiply = function() {
    push(pop() * pop());
  };
  divide = function() {
    temp = pop();
    push(pop() / temp);
  };
  remainder = function() { 
    temp = pop();
    push(pop() % temp);
  };
  add = function() { 
    temp = pop();
    push(pop() + pop());
  };
  subtract = function() {
    temp = pop();
    push(pop() - pop());
  };
  greater_or_equal = function() {
    temp = pop();
    push(pop() >= pop());
  };
  less_or_equal = function() { 
    temp = pop();
    push(pop() <= pop());
  };
  greater = function() { 
    temp = pop();
    push(pop() > pop());
  };
  less = function() { 
    temp = pop();
    push(pop() < pop());
  };
  equal = function() { 
    push(pop() === pop());
  };
  not_equal = function() { 
    push(pop() !== pop());
  };
  refinement = function() { 
    expr = pop();
    obj = pop();
    push(obj[expr]);
  };
  logical_or = function() { 
    addr = pop();
    flag = pop();
    if (flag) {
      push(true);
      jump(addr);
    }
  };
  logical_and = function() { 
    addr = pop();
    flag = pop();
    if (!flag) {
      push(false);
      jump(addr);
    }
  };
  asst = function() { 
    addr = pop();
    expr = pop();
    assign(addr, expr);
  };
  plus_asst = function() {
    addr = pop();
    expr = pop();
    val = getval(addr);
    assign(addr, val + expr);
  };
  minus_asst = function() { 
    addr = pop();
    expr = pop();
    val = getval(addr);
    assign(addr, val - expr);
  };
  jump_local = function() { 
    jump(pop());
  };
  branch_true = function() {
    addr = pop();
    if (pop()) {
      jump(addr);
    }
  };
  branch_false = function() {
    // (same as ternary)
    addr = pop();
    if (!pop()) {
      jump(addr);
    }
  };
  call = function() {
    addr = pop();
    push(catchaddr);
    push(stkbase);
    push(currfunc);
    push(currpos);
    callfunc(addr, 0);
  };
  post_call = function() { 
    rtnpos = pop();
    rtnfunc = pop();
    stkbase = stack.length - parmcount
    push(varcount);
    push(rtnfunc);
    push(rtnpos);
  };
  return_no_val = function() {
    rtnpos = pop();
    rtnfunc = pop();
    varcount = pop();
    for (i = 1; i <= varcount; i += 1) {
      pop();
    }
    stkbase = pop();
    callfunc(rtnfunc, rtnpos);
    pop();
  };
  return_val = function() { 
    rtnval = pop();
    rtnpos = pop();
    rtnfunc = pop();
    varcount = pop();
    for (i = 1; i <= varcount; i += 1) {
      pop();
    }
    stkbase = pop();
    push(rtnval);
    callfunc(rtnfunc, rtnpos);
    pop();
  };
  array_literal = function() { 
    count = pop();
    array = [];
    for (i = 1; i <= count; i += 1) {
      array.push(pop());
    };
    array.reverse();
    push(array);
  };
  object_literal = function() { 
    count = pop();
    object = {};
    for (i = 1; i <= count; i += 1) {
      expr = pop();
      key = pop();
      object[key] = expr;
    };
    push(object);
  };
  throw_in_try = function() {
    jump(pop());
  };
  catch_clause = function() { 
    expr = pop();
    assign(offset, expr);
  };
  throw_not_try = function() {
    expr = pop();
    do {
      rtnpos = pop();
      rtnfunc = pop();
      varcount = pop();
      for (i = 1; i <= varcount; i += 1) {
        pop();
      }
      stkbase = pop();
      callfunc(rtnfunc, rtnpos);
      catchaddr = pop();
    } 
    while (!catchaddr);
    jump(catchaddr);
    push(expr);
  };
  call_self = function() {
    push(catchaddr);
    push(stkbase);
    push(currfunc);
    push(currpos);
    callfunc(currfunc, 0);
  };
}
function initops() {
  operators = [
    defun,
    begin,
    end,
    typeof_fn,
    to_number,
    negate,
    not,
    multiply,
    divide,
    remainder, 
    add,
    subtract,
    greater_or_equal,
    less_or_equal,
    greater,
    less,
    equal,
    not_equal,
    refinement,
    logical_or,
    logical_and,
    asst,
    plus_asst,
    minus_asst,
    jump_local,
    branch_true,
    branch_false,
    call,
    post_call,
    return_no_val,
    return_val,
    array_literal,
    object_literal,
    throw_in_try,
    catch_clause,
    throw_not_try,
    call_self
  ];
}

Sample Byte Code

Token Name Examples:

  1. Operator:  opadd
  2. Definition:  opdef myvar
  3. Variable reference:  opvar myvar
  4. Global integer constant:  opdef -123
  5. Global float constant:  opdef 3.1416E-12
  6. Global string literal:  opdef "xyz&ABC DEF_123"

Nojotalk Code:

// set label to 'Hello, world!'

(= myFunction (func () (
  (= (: ($ (: document getElementById) 'myLabel') innerHTML) 'Hello, world!')
)))

Equivalent Byte Code:

Token / Overhead

  1. op_post_call  / 6
  2. loc_func1
  3. loc_label1
  4. loc_func2
  5. str_getElementById
  6. str_myLabel
  7. str_Hello_world
  8. str_innerHTML
  9. glb_document
  10. str_getElementById
  11. op_refinement  / 3
  12. loc_func1
  13. op_asst  / 3 + 3
  14. str_myLabel
  15. loc_func1
  16. op_call  / 6 + 6
  17. loc_label1
  18. op_asst  / 3 + 3
  19. str_Hello_world
  20. loc_label1
  21. str_innerHTML
  22. op_refinement  / 3
  23. op_asst  / 3 + 4
  24. op_return_no_val  / 22

Total:   65

Main Loop:   144 = 24 tokens x 6

Grand Total:   209 lines of JavaScript code executed

[ Back to Top ]