diff --git a/example/example-page.css b/example/example-page.css index fcd44e0..bb4917b 100644 --- a/example/example-page.css +++ b/example/example-page.css @@ -10,7 +10,7 @@ body { } .container .col { - width: 50%; + width: 33%; padding: 10px; } @@ -20,4 +20,15 @@ body { .CodeMirror { box-shadow: 3px 4px 21px -4px rgba(0,0,0,0.75); -} \ No newline at end of file +} + + +#fullscreenButton { + position: absolute; + bottom: 0; + left: 50%; + transform: translate(-50%, 0px); +} +#output { position: relative; } +main { text-align: center; } +main .error { background: red; padding: 10px; font-family: courier; line-height: 5ex; } \ No newline at end of file diff --git a/example/example.js b/example/example.js index ed93723..e50fd01 100644 --- a/example/example.js +++ b/example/example.js @@ -5,7 +5,7 @@ import prettyJS from 'pretty-js' import { compile } from '../src/wescheme' -import Evaluator from '../src/runtime/mzscheme-vm/evaluator' +import { Runner } from '../src/runtime/mzscheme-vm/evaluator' import loadProject from '../src/runtime/loadProject' require('./example.css') @@ -22,18 +22,19 @@ var cm2 = CodeMirror.fromTextArea( ) cm.on('change', function() { try { - cm2.setValue(prettyJS(compile("TEST PROGRAM", cm.getValue(), true).bytecode.toString())) + const bytecode = prettyJS(compile(cm.getValue(), true).bytecode.toString()); + cm2.setValue(bytecode) } catch (e) { + cm2.setValue("Compilation Error (see console for details)") if (e instanceof Error) { throw e } - cm2.setValue(e) } }) - cm2.on('change', function() { - try { init(); } + if(cm2.getValue().includes("Compilation Error")) return; + try { runBytecode(); } catch (e) { throw e; } }); @@ -42,130 +43,19 @@ cm.setValue('(triangle 200 "solid" "turquoise")') /////////////////////////////////////////////////////////////////////////////// // imported from WeScheme war-src/js/run.js -function init(publicId) { - - var Runner = function(outputDOMContainer) { - var that = this; - this.outputDOMContainer = outputDOMContainer; - this.evaluator = new Evaluator({ - write: function(thing) { that.addToInteractions(thing); }, - }); - this.evaluator.setImageProxy("/imageProxy"); - this.evaluator.setRootLibraryPath("/js/mzscheme-vm/collects"); - - this.runCompiledCode = function(compiledCode, permStringArray) { - var that = this; - var onSuccessRun = function() { }; - var onFailRun = function(exn) { that.renderErrorAsDomNode(exn); }; - this.evaluator.executeCompiledProgram((0,eval)('(' + compiledCode + ')'), - onSuccessRun, - onFailRun); - }; - - this.runSourceCode = function(title, sourceCode, permStringArray) { - var that = this; - var onSuccessRun = function() { console.log('success')}; - var onFailRun = function(exn) { that.renderErrorAsDomNode(exn); }; - this.evaluator.executeProgram(title, sourceCode, onSuccessRun, onFailRun); - }; - - this.addToInteractions = function(interactionVal) { - - // Returns if x is a dom node. - function isDomNode(x) { - return (x.nodeType != undefined); - } - // Returns if x is a node that should be printed - // Printable Nodes are CANVAS elements, OR non-empty SPANs - function isPrintableNode(x){ - return x.nodeName === "CANVAS" || x.childNodes.length > 0; - } - - if(!isPrintableNode(interactionVal)){ return;} // make sure there are no other topLevelEvaluationNodes in the outputDOMContainer - while(this.outputDOMContainer.firstChild){ - this.outputDOMContainer.removeChild(this.outputDOMContainer.firstChild); - } - if (isDomNode(interactionVal)) { - interactionVal.style.display="inline-block"; - interactionVal.classList.add("replOutput"); // simulate the editor REPL, so CSS spacing will kick in - this.outputDOMContainer.append(interactionVal); - } else { - var newArea = document.createElement("div"); - newArea.style.width='100%'; - newArea.text(interactionVal); - newArea.style.display="inline-block"; - this.outputDOMContainer.append(newArea); - } - this.outputDOMContainer.scrollTop = this.outputDOMContainer.scrollHeight; - }; - - // renderErrorAsDomNode: exception -> element - // Given an exception, produces error dom node to be displayed. - this.renderErrorAsDomNode = function(err) { - var msg = this.evaluator.getMessageFromExn(err); - - var dom = document.createElement('div'); - dom['class'] = 'moby-error'; - - var msgDom = document.createElement('div'); - msgDom['class'] = 'moby-error:message'; - msgDom.appendChild(document.createTextNode(msg)); - dom.appendChild(msgDom); - - var stacktrace = this.evaluator.getTraceFromExn(err); - for (var i = 0; i < stacktrace.length; i++) { - dom.appendChild(document.createTextNode("at: line " + stacktrace[i].line + - ", column " + stacktrace[i].column)); - } - return dom; - }; - }; - +function runBytecode(publicId) { + var inter = document.getElementById('interactions'); var runner = new Runner(document.getElementById('interactions')); - var afterLoad = function(aProgram) { - - var title = "Test Program", // aProgram.getTitle(), - sourceCode = cm.getValue(), // aProgram.getSourceCode(), - programCode = null, // Set it to null, so that the client-side compiler is invoked. - permissions = null, // aProgram.getPermissions(), - notes = null; // aProgram.getNotes(); - - var j = document.getElementById('interactions'), - b = document.getElementsByTagName("body")[0]; - - var toggleFullscreen = function() { - // obtain the element being added - var elem; - if (j.querySelectorAll("canvas").length == 1) { elem = j.querySelectorAll("canvas")[0]; } - else { elem = j[0]; } - - // get fullscreen access - if(!document.fullscreenElement) elem.requestFullscreen( Element.ALLOW_KEYBOARD_INPUT ); - else document.exitFullscreen(); - }; - var input = document.createElement("input"); - input.type = "button"; - input.value = "Run Fullscreen"; - input.style = "margin-top: 20px; display: block; margin-left: auto; margin-right: auto"; - input.onclick = toggleFullscreen; - b.appendChild(input); - - var appendFinishedMsg = function() { - var inter = document.getElementById('interactions'); - var finished = document.createElement('span'); - finished.id = "finished"; - finished.innerHTML = "The program has finished running, but only included definitions (which do not produce any output)."; - if(inter.children.length == 0) { - inter.appendChild(finished); - } - }; - - if (programCode) { - runner.runCompiledCode(programCode, permissions); - } else { - runner.runSourceCode("TEST PROGRAM", sourceCode, permissions); + var reportIfNoOutput = function() { + if(inter.children.length == 0) { + inter.innerHTML = "The program has finished running, but only included definitions (which do not produce any output)."; } - appendFinishedMsg(); }; - afterLoad(); + try { + runner.runSourceCode(null, cm.getValue(), null); // pass null for permissions and title + } catch(e) { + inter.innerHTML = "" + e.toString() + ""; + } finally { + reportIfNoOutput(); + } } \ No newline at end of file diff --git a/example/index.html b/example/index.html index 37c2995..3231740 100644 --- a/example/index.html +++ b/example/index.html @@ -1,19 +1,21 @@ -

wescheme-js

+

WeScheme as a Node Module

-
+

Write scheme code here...

-
-

And get compiled javascript here!

+
+

get compiled bytecode here...

-
-

And see executed output here!

+
+

and see output here!

+
+ diff --git a/src/compiler.js b/src/compiler.js index 36d6456..e03f6c3 100644 --- a/src/compiler.js +++ b/src/compiler.js @@ -29,7 +29,7 @@ import { } from './structures'; var compiler = require('./compiler'); var jsnums = require('./runtime/js-numbers'); -var types = require('./runtime/types'); +import types from './runtime/types'; /* SOMEDAY...but probably never diff --git a/src/lex.js b/src/lex.js index a176361..a605312 100644 --- a/src/lex.js +++ b/src/lex.js @@ -1,5 +1,5 @@ var compiler = require('./structures'); -var types = require('./runtime/types'); +import types from './runtime/types'; var jsnums = require('./runtime/js-numbers'); /* @@ -1026,8 +1026,6 @@ export var lex = function(str, strSource, debug) { var end = new Date().getTime(); if (debug) { console.log("Lexed in " + (Math.floor(end - start)) + "ms"); - console.log(sexp); - console.log(sexpToString(sexp)); } return sexp; }; \ No newline at end of file diff --git a/src/parser.js b/src/parser.js index 072cf34..6d5cac4 100644 --- a/src/parser.js +++ b/src/parser.js @@ -30,7 +30,7 @@ import { throwError, keywords } from './structures'; -var types = require('./runtime/types'); +import types from './runtime/types'; var compiler = require('./compiler'); /* diff --git a/src/runtime/control.js b/src/runtime/control.js index 6434c79..6a2ebb9 100644 --- a/src/runtime/control.js +++ b/src/runtime/control.js @@ -1,9 +1,9 @@ // Control structures var sys = require('sys'); -var types = require('./types'); +var types = require('./types').default import primitive from './primitive' -var types = require('./types'); +import state from './state' var DEBUG_ON = false; diff --git a/src/runtime/helpers.js b/src/runtime/helpers.js index 4cf0b61..837de9f 100644 --- a/src/runtime/helpers.js +++ b/src/runtime/helpers.js @@ -1,3 +1,4 @@ +import types from './types' ////////////////////////////////////////////////////////////// @@ -622,4 +623,4 @@ var helpers = {}; })(); ///////////////////////////////////////////////////////////////// -export default helpers; +export default helpers; \ No newline at end of file diff --git a/src/runtime/interpret.js b/src/runtime/interpret.js index 45b5601..5418323 100644 --- a/src/runtime/interpret.js +++ b/src/runtime/interpret.js @@ -1,12 +1,13 @@ // For node.js. var sys = require('sys'); -var types = require('./types'); +var types = require('./types').default import primitive from './primitive' import loader from './loader'; var assert = require('assert'); import control from './control' -var state = require('./state'); +import helpers from './helpers' +var state = require('./state').default; var DEBUG_ON = false; @@ -143,22 +144,18 @@ var run = function(aState, onSuccessK, onFailK) { if (! onFailK) { onFailK = function(exn) { throw exn; } }; function doRunWork(){ - console.log(1); var gas = MAX_STEPS_BEFORE_BOUNCE; while( (! aState.isStuck()) && (gas > 0)) { - console.log(2); step(aState); gas--; } if (aState.breakRequested) { - console.log('breakRequested'); onFailK(types.schemeError( types.exnBreak("user break", state.captureCurrentContinuationMarks(aState), captureContinuationClosure(aState)))); return; } else if (gas <= 0) { - console.log('pausing for browser thread'); var stateValues = aState.save(); setTimeout(function(){ aState.restore(stateValues); @@ -166,7 +163,6 @@ var run = function(aState, onSuccessK, onFailK) { }, 0); } else { - console.log('done!') onSuccessK(aState.v); return; } @@ -184,7 +180,8 @@ var run = function(aState, onSuccessK, onFailK) { // scheme exception // If the error is incomplete, finish constructing it if ( types.isIncompleteExn(e.val) ) { - var contMarks = state.captureCurrentContinuationMarks(aState); + console.log(state) + var contMarks = state.captureCurrentContinuationMarks(aState); e = types.schemeError(e.val.constructor.apply(null, [e.val.msg, contMarks].concat(e.val.otherArgs) )); } onFailK(e); diff --git a/src/runtime/loader.js b/src/runtime/loader.js index 818ba10..8945605 100644 --- a/src/runtime/loader.js +++ b/src/runtime/loader.js @@ -1,6 +1,8 @@ // Loader: take bytecode and translate to internal format. import control from './control' +import state from './state' + var sys = require('sys'); var loader = {}; diff --git a/src/runtime/mzscheme-vm/evaluator.js b/src/runtime/mzscheme-vm/evaluator.js index 2fad5da..130f0fc 100644 --- a/src/runtime/mzscheme-vm/evaluator.js +++ b/src/runtime/mzscheme-vm/evaluator.js @@ -1,5 +1,5 @@ import { compileAndRun } from '../../wescheme' -var types = require('../types') +var types = require('../types').default import interpret from '../interpret' import helpers from '../helpers' @@ -220,27 +220,24 @@ var Evaluator = (function() { programName, code, function(responseText) { - console.log('Evaluator.executeProgram: got compiled bytecode. about to run') var result = JSON.parse(responseText); that.executeCompiledProgram(eval('(' + result.bytecode + ')'), onDone, onDoneError); }, function(responseErrorText) { - console.log(responseErrorText); that._onCompilationFailure(JSON.parse(responseErrorText || '""'), onDoneError); + throw responseErrorText; }) }; Evaluator.prototype.executeCompiledProgram = function(compiledBytecode, onDoneSuccess, onDoneFail) { this.aState.clearForEval(); try { - console.log('Evaluator.executeCompiledProgram: about to load bytecode into interpreter'); interpret.load(compiledBytecode, this.aState); } catch(e) { console.error(e); onDoneFail(e); return; } - console.log('Evaluator.executeCompiledProgram: about to run interpreter'); interpret.run(this.aState, onDoneSuccess, onDoneFail); }; @@ -530,4 +527,86 @@ var Evaluator = (function() { return Evaluator; })(); + +export var Runner = function(outputDOMContainer) { + var that = this; + this.outputDOMContainer = outputDOMContainer; + this.evaluator = new Evaluator({ + write: function(thing) { that.addToInteractions(thing); }, + }); + this.evaluator.setImageProxy("/imageProxy"); + this.evaluator.setRootLibraryPath("/js/mzscheme-vm/collects"); + + this.runCompiledCode = function(compiledCode, permStringArray) { + while(this.outputDOMContainer.firstChild) { + this.outputDOMContainer.removeChild(this.outputDOMContainer.firstChild); + } + + var that = this; + var onSuccessRun = function() { }; + var onFailRun = function(exn) { that.renderErrorAsDomNode(exn); }; + this.evaluator.executeCompiledProgram(eval('(' + compiledCode + ')'), + onSuccessRun, + onFailRun); + }; + + this.runSourceCode = function(title, sourceCode, permStringArray) { + while(this.outputDOMContainer.firstChild) { + this.outputDOMContainer.removeChild(this.outputDOMContainer.firstChild); + } + var that = this; + var onSuccessRun = function() { /* no-op */ }; + var onFailRun = function(exn) { that.renderErrorAsDomNode(exn); }; + this.evaluator.executeProgram(title, sourceCode, onSuccessRun, onFailRun); + }; + + this.addToInteractions = function(interactionVal) { + + // Returns if x is a dom node. + function isDomNode(x) { + return (x.nodeType != undefined); + } + // Returns if x is a node that should be printed + // Printable Nodes are CANVAS elements, OR non-empty SPANs + function isPrintableNode(x){ + return x.nodeName === "CANVAS" || x.childNodes.length > 0; + } + + if(!isPrintableNode(interactionVal)){ return;} // make sure there are no other topLevelEvaluationNodes in the outputDOMContainer + if (isDomNode(interactionVal)) { + interactionVal.style.display="inline-block"; + interactionVal.classList.add("replOutput"); // simulate the editor REPL, so CSS spacing will kick in + this.outputDOMContainer.append(interactionVal); + } else { + var newArea = document.createElement("div"); + newArea.style.width='100%'; + newArea.text(interactionVal); + newArea.style.display="inline-block"; + this.outputDOMContainer.append(newArea); + } + this.outputDOMContainer.scrollTop = this.outputDOMContainer.scrollHeight; + }; + + // renderErrorAsDomNode: exception -> element + // Given an exception, produces error dom node to be displayed. + this.renderErrorAsDomNode = function(err) { + var msg = this.evaluator.getMessageFromExn(err); + + var dom = document.createElement('div'); + dom['class'] = 'moby-error'; + + var msgDom = document.createElement('div'); + msgDom['class'] = 'moby-error:message'; + msgDom.appendChild(document.createTextNode(msg)); + dom.appendChild(msgDom); + + var stacktrace = this.evaluator.getTraceFromExn(err); + for (var i = 0; i < stacktrace.length; i++) { + dom.appendChild(document.createTextNode("at: line " + stacktrace[i].line + + ", column " + stacktrace[i].column)); + } + return dom; + }; + }; + export default Evaluator; diff --git a/src/runtime/primitive.js b/src/runtime/primitive.js index bf2e0a6..902704f 100644 --- a/src/runtime/primitive.js +++ b/src/runtime/primitive.js @@ -1,4 +1,4 @@ -var types = require('./types') +var types = require('./types').default import helpers, { check } from './helpers' var jsnums = require('./js-numbers') import { world } from './world/world' @@ -15,8 +15,6 @@ var setCALL = function(V) { }; }; - - var PAUSE; var setPAUSE = function(V) { PAUSE = function(onPause) { @@ -24,13 +22,6 @@ var setPAUSE = function(V) { }; }; - - - - - - - var PRIMITIVES = {}; var PrimProc = types.PrimProc; @@ -44,7 +35,6 @@ var CasePrimitive = types.CasePrimitive; var id = function(x) { return x; }; - var callWithValues = function(f, vals) { if (vals instanceof types.ValuesWrapper) { return CALL(f, vals.elts, id); diff --git a/src/runtime/state.js b/src/runtime/state.js index 7e320ad..4d5e74d 100644 --- a/src/runtime/state.js +++ b/src/runtime/state.js @@ -1,10 +1,6 @@ -var types = require('./types'); - - +var types = require('./types').default // Represents the interpreter state. - - var state = {}; (function () { diff --git a/src/runtime/types.js b/src/runtime/types.js index f089788..c54a207 100644 --- a/src/runtime/types.js +++ b/src/runtime/types.js @@ -1,29 +1,22 @@ ////////////////////////////////////////////////////////////////////// // helper functions -var jsnums = require('./js-numbers'); -var _Hashtable = require('./jshashtable'); -var helpers = require('./helpers'); +var jsnums = require('./js-numbers') +var _Hashtable = require('./jshashtable') +import helpers from './helpers' var types = {}; (function () { ////////////////////////////////////////////////////////////////////// - - var appendChild = function(parent, child) { parent.appendChild(child); }; - - var hasOwnProperty = {}.hasOwnProperty; ////////////////////////////////////////////////////////////////////// - - - var _eqHashCodeCounter = 0; var makeEqHashCode = function() { _eqHashCodeCounter++; @@ -48,7 +41,6 @@ var getEqHashCode = function(x) { // Union/find for circular equality testing. - var UnionFind = function() { // this.parenMap holds the arrows from an arbitrary pointer // to its parent. @@ -2327,4 +2319,4 @@ types.Char = Char; })(); -module.exports = types; +export default types \ No newline at end of file diff --git a/src/runtime/world/world.js b/src/runtime/world/world.js index 2a4365f..867aaca 100644 --- a/src/runtime/world/world.js +++ b/src/runtime/world/world.js @@ -1,4 +1,4 @@ -var types = require('../types') +import types from '../types'; export var world = {}; world.Kernel = {}; diff --git a/src/structures.js b/src/structures.js index c165c3f..9ba6cfa 100644 --- a/src/structures.js +++ b/src/structures.js @@ -1,7 +1,7 @@ /*global */ -var types = require('./runtime/types'); -var Vector = types.Vector; +import types from './runtime/types' +var Vector = types.Vector ////////////////////////////////////////////////////////////////////////////// /////////////////// COMMON FUNCTIONS AND STRUCTURES ////////////////////////// diff --git a/src/wescheme.js b/src/wescheme.js index b016eca..fde5fce 100644 --- a/src/wescheme.js +++ b/src/wescheme.js @@ -3,15 +3,21 @@ var parse = require('./parser').parse import {desugar, analyze} from './analyzer' var codegen = require('./compiler').compile -var types = require('./runtime/types') +import types from './runtime/types'; function compile(code, debug=false) { - var lexemes = lex(code, 'fake-src-filename', debug) - var AST = parse(lexemes) - var desugared = desugar(AST)[0] // includes [AST, pinfo] - var pinfo = analyze(desugared) - var local_bytecode = codegen(desugared, pinfo) - return local_bytecode + try { + var lexemes = lex(code, 'fake-src-filename', debug) + var AST = parse(lexemes) + var desugared = desugar(AST)[0] // includes [AST, pinfo] + var pinfo = analyze(desugared) + var local_bytecode = codegen(desugared, pinfo) + return local_bytecode + } catch (e) { + var local_error = getError(e).toString(); + console.log(local_error) + throw local_error; + } } // check to make sure it's JSON parseable before returning it. @@ -28,11 +34,10 @@ function getError(e){ var onCompilationFail = function(onDoneError) { // If all servers are failing, we simulate a // compile time error with the following content: - onDoneError( - JSON.stringify(onDoneError("The local compiler has failed to run properly. " - + "You may want to confirm that you are running " - + "a modern web browser (IE9+, Safari 6+, FF 20+, " - + "Chrome 20+)."))); + onDoneError("The local compiler has failed to run properly. " + + "You may want to confirm that you are running " + + "a modern web browser (IE9+, Safari 6+, FF 20+, " + + "Chrome 20+)."); }; function compileAndRun(programName, code, onDone, onDoneError) { @@ -56,7 +61,7 @@ function compileAndRun(programName, code, onDone, onDoneError) { onDone(JSON.stringify(local_bytecode)); } catch (e) { var local_error = getError(e).toString(); - console.error(local_error); + console.error(e) // if it's a fatal error, log the error and move on if(/FATAL ERROR/.test(local_error.toString())){ //logResults(code, JSON.stringify(local_error), "FATAL ERROR"); @@ -65,6 +70,7 @@ function compileAndRun(programName, code, onDone, onDoneError) { } else{ onDoneError(local_error); } + throw local_error; } var end = new Date().getTime(), localTime = Math.floor(end-start); console.log("Compiled in: " + Math.floor(end-start) +"ms");