python code
js machine code
extra stuff
####++++
#! /usr/bin/python
# -*- coding: utf-8 -*-
#
# everything above this line gets left out of the browser version
if type(browser) == 'undefined': browser = 1
def tstamp():
return Date.now()/1000
if browser:
cos = Math.cos
floor = Math.floor
time = tstamp
def conlog():
console.log.apply(console, arguments)
if not browser:
tstamp = time
conlog = print
# end of infrastructure
############################################################
###@kwargs
def sum(x=0,y=0,z=0):
return 0+x+y+z
print('should be True:', sum(z=5, y=3) > 0)
print('should be 8: ', sum(z=5, y=3))
####----
// ********************************************
// The javascript driver code.
// Invoke the compiler, then run the compiled code.
// First, some auxiliary functions needed at compile time,
// or otherwise needed by the driver:
Array.prototype['+'] = function(other) {
return this.concat(other)
}
var array_mul = function(arr, num){
if(Number(num) !== num || num % 1 !== 0) {
throw new Error("Can't multiply array by non-integer")
}
if (num < 0) throw new Error("Can't multiply array by negative number")
if (num == 0) return []
if (num == 1) return arr.slice() // slice is important here
var temp = array_mul(arr, Math.floor(num/2))
temp = temp.concat(temp) // concat clones both before joining them
if (num % 2) return temp.concat(arr)
return temp
}
Array.prototype['*'] = function(num) {
return array_mul(this, num)
}
Array.prototype['+'] = function(other) {
return this.concat(other)
}
var get_trace_win = function() {
var trace_win = document.getElementById('trace-win');
if (!trace_win) {
trace_win = document.createElement("pre");
document.documentElement.appendChild(trace_win)
}
return trace_win
}
var add_trace = function(trace_win, msgline, lineno, codewin,
code_line_start, color) {
var txtnode = document.createTextNode(msgline);
var span = document.createElement("span");
span.appendChild(txtnode);
trace_win.appendChild(span);
// This is an example of an HTML closure:
// Function within a parent function, has permanent access to parent's variables:
if (lineno >= 0) {
span.style.color = color
span.ondblclick = (function() {
var LN = lineno
return function() {
if (codewin.style.height == '0px') codewin.style.height = '300px'
codewin.setSelectionRange(code_line_start[LN-1],
code_line_start[LN])
codewin.focus()
}
})()
}
}
var dump_doc = function(printarea){
var all = document.getElementsByTagName("*");
for (var ii=0; ii < all.length; ii++) {
var xx = all[ii];
printarea.textContent += "t: " + xx.type
+ " c: " + xx.getAttribute("class")
+ " id: " + xx.id + "\n";
}
}
// insert A into B:
var insert = function(inner, outer, where) {
return [outer.slice(0, where), inner, outer.slice(where)].join('')
}
// The main driver:
var driver = function(){
var synerr = args['synerr']
var compiled_code = "<-->"
var errmsg = "---"
var source_line_start = {}
var run_line_start = {}
var add_pyline = function(pyline){
if (pyline > 0 && pyline <= comp.__original.text.length) {
var txt = comp.__original.text[pyline-1]
if (1||txt) {
var msgline = "<<< " + (txt) + "\n"
add_trace(trace_win, msgline, pyline, py_win, source_line_start, "red")
}
}
}
// Beware:
// For a wide range of simple syntax errors, the compiler errors out.
// It replaces the *entire* root document with a terse error message
// which is quite nasty.
//
// Also, under firefox (but not chrome), the RS compiler clears the
// javascript console and leaves it in an unusable state,
// which is amazingly nasty.
var wrapper = document.getElementById('wrapper');
// Grab the text of the vpython program to compile:
var py_xmp = document.getElementById('py_xmp');
var py_win = document.getElementById('py_win');
if (synerr) py_win.textContent = '# 1\n====ShouldFail==== # 2\n# 3\n'
else {
var prog = py_xmp.textContent
if (prog.charAt(0) === "\n") py_win.textContent = prog.substr(1)
else py_win.textContent = prog
}
var py_code = py_win.textContent;
if (verbosity) py_win.style.height = '300px'
var support_xmp = document.getElementById('support_xmp');
var support_win = document.getElementById('support_win');
if (support_win) {
support_win.textContent = support_xmp.textContent;
if (verbosity) support_win.style.height = '300px'
}
var print_win = document.getElementById('print');
var extra_win = document.getElementById('extra_win');
var machine_code_win = document.getElementById('machine_code_win');
var comp = {} // common block for compiler shared data
var errmsg // save for later
try {
var compiled_code = glowscript_compile(py_code,
{lang: "vpython", msg_handler: compiler_msg, shared: comp})
}
catch(err) {
errmsg = err.message // will be used later
var emsg = "Compiler bombed out;"
+ " thrown errmsg: (" + err.message + ")"
+ " file: (" + err.fileName + ")"
+ " line: (" + err.lineNumber + ")"
console.log(emsg)
console.log(err.stack)
var pos = 0;
// N+1 entries labeled 0 through N *inclusive*
source_line_start[0] = pos
for (var ii = 0; ii < comp.__original.text.length; ii++){
pos += 1 + comp.__original.text[ii].length
source_line_start[1+ii] = pos
}
var trace_win = get_trace_win()
add_trace(trace_win, err.message + "\n", -1)
// Kludge: thrown lineNumber is often one unit too big:
add_pyline(err.lineNumber - 1)
add_pyline(err.lineNumber)
if (!verbosity) verbosity = 1 // error forces some minimum verbosity
}
if (verbosity > 1) {
// Dump the line-number translation table so we can look at it:
for (var ii in comp.__linenumbers) {
extra_win.textContent += ii + " --> " + comp.__linenumbers[ii] + "\n"
}
}
if (verbosity || len(compiler_msg.str)) extra_win.textContent += ""
+ "Compiler message was: '" + compiler_msg.str + "'\n"
var compiled_split = compiled_code.split("\n")
if (verbosity) extra_win.textContent += "compiled code has "
+ compiled_split.length + " lines\n"
if (compiled_code === '' || compiled_code === '<-->'){
console.log('Evaluation skipped; no code.')
} else {
// Here with some possibly-usable compiled code.
if (typeof comp.__original !== 'undefined'){
extra_win.textContent += "program has "
+ comp.__original.text.length + " lines\n"
var pos = 0;
// N+1 entries labeled 0 through N *inclusive*
source_line_start[0] = pos
for (var ii = 0; ii < comp.__original.text.length; ii++){
pos += 1 + comp.__original.text[ii].length
source_line_start[1+ii] = pos
}
}
// The printarea is the /active/ print area.
// In principle, it could be switched from print_win to some other window.
var printarea = print_win
var machine_code = "";
if (verbosity) machine_code +=
"console.log('top of machine_code; printarea is:', printarea);";
// The glowscript library always requires the following incantation;
// don't ask me why:
machine_code += 'window.__context = '
+ '{ glowscript_container: $("#glowscript") };'
machine_code += support_xmp.textContent
machine_code += compiled_code
if (verbosity) machine_code += "console.log('bottom of machine_code');main"
// Not sure what this is/was supposed to do;
// it looks like it's adding a message to the top of the
// machine_code main() function
var where = machine_code.match("function main.wait. {") // (balancing "}")
if (0 && where && where.length) {
var begin = where.index // not where[0].index (don't ask me why)
var size = where[0].length
machine_code = insert(
"console.log('top of main()'"
+ ", arguments.length"
+ ", typeof arguments[0]"
+ ")",
machine_code, begin+size)
}
// Show the js machine_code, in case the user is curious:
machine_code_win.textContent = machine_code;
if (verbosity) machine_code_win.style.height = '300px'
var machine_split = machine_code.split("\n")
var pos = 0;
// N+1 entries labeled 0 through N *inclusive*
run_line_start[0] = pos
for (var ii = 0; ii < machine_split.length; ii++){
pos += 1 + machine_split[ii].length
run_line_start[1+ii] = pos
}
// This pre-evaluation apparently defines main() but doesn't run it:
var mainprog;
try {
if (verbosity >= 3)
console.log("about to pre-eval main machine_code; length=", machine_split.length)
mainprog = eval(machine_code);
if (verbosity >= 3)
console.log("OK after pre-eval main machine_code")
}
catch(err) {
console.log("Unable to pre-eval compiled code...."
+ " thrown errmsg: (" + err.message + ")"
+ " file: (" + err.fileName + ")"
+ " line: (" + err.lineNumber + ")"
)
console.log(err.stack)
return
}
if (verbosity >= 3)
console.log("machine_code pre-evaluated to obj of type '", typeof mainprog, "'");
// Actually run the program:
// BEWARE: You must call main() with an argument;
// otherwise bizarre and undocumented things will happen.
// This includes error messages getting blackholed.
// This includes parts of the compiled javascript
// getting executed twice.
try {
if (verbosity >= 3) console.log("about to invoke main(...)");
mainprog(_handbasket_);
if (verbosity >= 3) console.log("OK after invoke main(...)");
}
catch(err){
console.log("main(_handbasket_) bombed out in a way that _handbasket_ could not handle;")
console.log("it threw errmsg: «" + err.message + "»"
+ " in file: «" + err.fileName + "»"
+ " on line: «" + err.lineNumber + "»")
console.log(err.stack)
return
}
}
// Dump all the elements in the root document.
// Among other things, this allows us to see what elements
// the runtime libraries have created.
if (0 && verbosity) dump_doc(extra_win);
}
driver
// end of javascript "driver" code.
// ******************************
"use strict";
////// :::::::: Support functions needed at runtime ::::::::: //////////
// To support checking, e.g.
// browser = "Math" in globals()
// Note that globals() gets transmogrified into nonlocals()
if (typeof nonlocals === 'undefined') window.nonlocals = function() {
return window
}
var protectHTML = function(rawtext) {
return rawtext
.replace(/&/g, "&")
.replace(//g, ">")
.replace(/"/g, """)
.replace(/'/g, "'");
}
if (0) { // optional, for testing keyboard events
var fmt_kbd_event = function(evt){
var msg = ""
// war-and-peace mode:
if (0) {
for (var item in evt) {
msg += item + ' --> ' + evt[item] + "; "
}
return msg
}
// relatively concise mode:
return "type: " + evt.type
+ " key: '" + evt.key + "'"
+ " keyCode: " + evt.keyCode
+ " charCode: " + evt.charCode
+ " which: " + evt.which
+ (evt.shiftKey ? " shift" : "")
+ (evt.ctrlKey ? " ctrl" : "")
+ (evt.altKey ? " alt" : "")
}
var test = document.getElementById('char-test');
test.onkeydown = function(evt){
console.log("char_test onkeydown: " + fmt_kbd_event(evt))
}
test.onkeypress = function(evt){
console.log("char_test onkeypress: " + fmt_kbd_event(evt))
}
}
var biteme = function(){
console.log("bite me")
throw new Error("bite me &")
}
var estack_parse = function(line){
var rslt = {}
var stuff = line.split(/>/);
rslt.depth = stuff.length
rslt.func = stuff[0].split('@')[0]
if (rslt.depth > 1) {
var spec = stuff[stuff.length-1].split(":")
rslt.line = Number(spec[1])
rslt.char = Number(spec[2])
var foo = stuff[stuff.length-2].split("line")
rslt.parent = Number(foo[foo.length-1])
} else {
rslt.parent = -1
rslt.line = -1
rslt.char = -1
}
return rslt
}
var trimline = function(txt, where) {
if (where > 35) {
var chomp = where - 30
txt = txt.substr(chomp)
where -= chomp
txt = "…" + txt // compare "⊕"
where++
}
if (txt.length > 70) {
txt = txt.substr(0,70) + "…"
}
return insert("▼", txt, where) // compare "☠"
}
// Object used to decode traceback stack:
var egger = function(){
this.line0 = 0
// The arg is supposed to be an Error object:
this.traceback = function(arg) {
console.log("egger::traceback begins")
var trace_win = get_trace_win()
if (typeof arg.stack !== 'undefined'){
var msg = "Tracing: runtime error was: «" + arg.message + "»\n"
add_trace(trace_win, msg, -1)
msg += " on line: «" + arg.lineNumber + "»"
+ " in file: «" + arg.fileName + "»"
console.log(msg)
// scare quotes: «»
if (!comp.hasOwnProperty('__linenumbers')) {
if (!window.hasOwnProperty('__linenumbers')) {
console.log("no _linenumbers; expect no cmpline or pyline")
} else {
console.log("attempting salvage")
comp = window // salvage situation when "shared: comp" is not implemented
}
}
msg = "" // start a new msg
var stack = arg.stack.split("\n")
console.log("Traceback stack length:", stack.length)
for (var ii = 0; ii < stack.length; ii++){
msg += "[TB " + ii + "] "
var line = stack[ii]
var x = estack_parse(line)
var cmpline = 0
var pyline = -1
var runline = x.line
var fr = ""
fr += "From " + (x.func || '()')
+ " parent: " + x.parent
+ " runline: " + runline
+ " char: " + x.char
fr += "\n"
msg += fr;
add_trace(trace_win, fr, -1)
// Show text of line from python source:
if (pyline > 0) {
var txt = comp.__original.text[pyline-1]
if (1||txt) {
var msgline = "<<< " + (txt) + "\n"
msg += msgline
add_trace(trace_win, msgline, pyline, py_win, source_line_start, "red")
}
}
// Show text of line from compiled code;
// not needed, given that the run-code is a superset,
// but useful for debugging.......
if (0 && cmpline > 0) {
var txt = compiled_split[cmpline-1]
var where = x.char-1
if (1||txt) {
var msgline = ">>> " + (trimline(txt, where)) + "\n"
msg += msgline
add_trace(trace_win, msgline, -1)
}
}
// Show text of line from run code:
if (runline > 0 && x.parent == this.parent0) {
var txt = machine_split[runline-1]
var where = x.char-1
if (1||txt) {
var msgline = ">>> " + (trimline(txt, where)) + "\n"
msg += msgline
add_trace(trace_win, msgline, runline, machine_code_win, run_line_start, "blue")
}
}
} // end loop over traceback stack
console.log(msg)
if (verbosity >= 3) {
console.log("..... and here is the traceback arg.stack, verbatim:\n", arg.stack)
}
throw ''
}
// Arg is not what we were expecting
// Whatever it is, (re)throw it, and hope for the best.
throw arg;
}
// Calibrate the "line0" value:
this.setup = function(arg){
var line = arg.stack.split("\n")
var x = estack_parse(line[0])
//xxx console.log("---- setup:", x.func, x.parent, x.line, x.char)
this.line0 = x.line
this.parent0 = x.parent
}
}
var eggs = new egger()
// This function is passed as the argument to main()
// Implemented as wrapper around egger object.
window._handbasket_ = function() {
if (arguments.length) eggs.traceback(arguments[0])
else console.log("Probably normal exit")
}
// Very rough, approximate, emergency workarounds for chr() and ord()
var ord = function(ch){
if (len(ch) > 1) return -1
return ch.charCodeAt(0)
}
var chr = function(code){
return String.fromCharCode(code)
}
/////// this needs to be the last thing before the compiled code /////////
try{throw new Error("whatever")}catch(e){eggs.setup(e)}
var sanitize_lineno = function(cmpline){
var bad = Number(comp.__linenumbers[cmpline-1])
//console.log("???????", cmpline, bad)
for (var ii = cmpline; ii < compiled_code.length; ii++) {
if (comp.__linenumbers[ii-1]) {
var val = Number(comp.__linenumbers[ii-1])
//console.log("checking ln[" + (ii-1) + "] = " + val)
if (val != bad) return
//console.log("sanitizing ln[" + (ii-1) + "] = " + val)
comp.__linenumbers[ii-1] = -2
}
}
}
// If it was in the compiled code (as opposed to auxilliary code)
// locate the line-number within the python source:
if (comp.hasOwnProperty('__linenumbers') &&
runline > this.line0 && x.parent == this.parent0) {
cmpline = runline - this.line0
if (x.func === 'main' || x.func === '__$main') sanitize_lineno(cmpline)
console.log(comp.__linenumbers.length)
pyline = comp.__linenumbers[cmpline-1]
if (pyline) pyline--
fr += " cmpline: " + cmpline
fr += " pyline: " + pyline
}
Traceback area. To see the problematic code in context,
double-click on any code-line here. Lines starting with "<<<" are
python source code-lines, while lines starting with ">>>" are the
resulting assembly-language (javascript) code-lines.
The "▼"
symbol indicates where on the line the javascript evaluation machine
thinks the problem is.
Bottom of document.