From 88260bccbf4eb235aaac78761ab3cecc223c6fe7 Mon Sep 17 00:00:00 2001 From: "chris.double" Date: Mon, 11 Dec 2006 11:14:58 +0000 Subject: [PATCH] fjsc: initial version of factor to javascript compiler --- apps/fjsc-responder/fjsc-responder.factor | 49 + apps/fjsc-responder/load.factor | 18 + apps/fjsc-responder/resources/bootstrap.js | 42 + .../resources/yahoo/connection.js | 586 +++++++++ apps/fjsc-responder/resources/yahoo/event.js | 1096 +++++++++++++++++ apps/fjsc-responder/resources/yahoo/yahoo.js | 61 + libs/fjsc/fjsc.factor | 66 + libs/fjsc/load.factor | 19 + libs/fjsc/tests.factor | 22 + 9 files changed, 1959 insertions(+) create mode 100644 apps/fjsc-responder/fjsc-responder.factor create mode 100644 apps/fjsc-responder/load.factor create mode 100644 apps/fjsc-responder/resources/bootstrap.js create mode 100644 apps/fjsc-responder/resources/yahoo/connection.js create mode 100644 apps/fjsc-responder/resources/yahoo/event.js create mode 100644 apps/fjsc-responder/resources/yahoo/yahoo.js create mode 100644 libs/fjsc/fjsc.factor create mode 100644 libs/fjsc/load.factor create mode 100644 libs/fjsc/tests.factor diff --git a/apps/fjsc-responder/fjsc-responder.factor b/apps/fjsc-responder/fjsc-responder.factor new file mode 100644 index 0000000000..32844b8758 --- /dev/null +++ b/apps/fjsc-responder/fjsc-responder.factor @@ -0,0 +1,49 @@ +! Copyright (C) 2006 Chris Double. All Rights Reserved. +! See http://factorcode.org/license.txt for BSD license. +! +IN: fjsc-responder +USING: kernel lazy-lists parser-combinators fjsc cont-responder html io namespaces file-responder httpd hashtables ; + +USE: prettyprint + +: fjsc-eval-page ( -- ) + [ "response" get . ] string-out log-message + "code" "response" get hash dup log-message + serving-text + 'expression' parse car + parse-result-parsed compile write flush ; + +: fjsc-page ( -- ) + [ + + + + + + + + +
+ + +
+
+
+
+
+ + + ] show ; + + +"fjsc" [ fjsc-page ] install-cont-responder +"fjsceval" [ fjsc-eval-page ] add-simple-responder +"fjsc-resources" [ + [ + "apps/fjsc-responder/resources/" resource-path "doc-root" set + file-responder + ] with-scope +] add-simple-responder + + diff --git a/apps/fjsc-responder/load.factor b/apps/fjsc-responder/load.factor new file mode 100644 index 0000000000..83b2878ea0 --- /dev/null +++ b/apps/fjsc-responder/load.factor @@ -0,0 +1,18 @@ +! Copyright (C) 2006 Chris Double. All Rights Reserved. +! See http://factorcode.org/license.txt for BSD license. +! +REQUIRES: libs/httpd libs/fjsc ; + +PROVIDE: apps/fjsc-responder +{ + +files+ { + "fjsc-responder.factor" + } +} { + +tests+ { + } +} { + +help+ + { + } +} ; diff --git a/apps/fjsc-responder/resources/bootstrap.js b/apps/fjsc-responder/resources/bootstrap.js new file mode 100644 index 0000000000..3885606c37 --- /dev/null +++ b/apps/fjsc-responder/resources/bootstrap.js @@ -0,0 +1,42 @@ +function fjsc_eval(form) { + var callback = { + success: function(o) { + var v = o.responseText; + eval(v) + display_datastack(); + document.getElementById('compiled').innerHTML="
" + v + "
"; + document.getElementById('code').value=""; + + } + }; + YAHOO.util.Connect.setForm(form); + YAHOO.util.Connect.asyncRequest('POST', "/responder/fjsceval/", callback); +} + +var data_stack = [ ] + +function fjsc_dup() { + var v = data_stack.pop(); + data_stack.push(v); + data_stack.push(v); +} + +function fjsc_drop() { + data_stack.pop(); +} + +function fjsc_alert() { + alert(data_stack.pop()) +} + +function display_datastack() { + var html=[]; + html.push("") + for(var i = 0; i < data_stack.length; ++i) { + html.push("") + } + html.push("
") + html.push(data_stack[i]) + html.push("
") + document.getElementById('stack').innerHTML=html.join(""); +} diff --git a/apps/fjsc-responder/resources/yahoo/connection.js b/apps/fjsc-responder/resources/yahoo/connection.js new file mode 100644 index 0000000000..098a3aba98 --- /dev/null +++ b/apps/fjsc-responder/resources/yahoo/connection.js @@ -0,0 +1,586 @@ +/* +Copyright (c) 2006, Yahoo! Inc. All rights reserved. +Code licensed under the BSD License: +http://developer.yahoo.net/yui/license.txt +*/ + +/** + * The Connection Manager provides a simplified interface to the XMLHttpRequest + * object. It handles cross-browser instantiantion of XMLHttpRequest, negotiates the + * interactive states and server response, returning the results to a pre-defined + * callback you create. + * @ class + */ +YAHOO.util.Connect = +{ + /** + * Array of MSFT ActiveX ids for XMLHttpRequest. + * @private + * @type array + */ + _msxml_progid:[ + 'MSXML2.XMLHTTP.5.0', + 'MSXML2.XMLHTTP.4.0', + 'MSXML2.XMLHTTP.3.0', + 'MSXML2.XMLHTTP', + 'Microsoft.XMLHTTP' + ], + + /** + * Array of HTTP header(s) + * @private + * @type array + */ + _http_header:{}, + + /** + * Determines if HTTP headers are set. + * @private + * @type boolean + */ + _has_http_headers:false, + + /** + * Property modified by setForm() to determine if the data + * should be submitted as an HTML form. + * @private + * @type boolean + */ + _isFormSubmit:false, + + /** + * Property modified by setForm() to set the HTML form data + * for each transaction. + * @private + * @type string + */ + _sFormData:null, + + /** + * Collection of polling references to the polling mechanism in handleReadyState. + * @private + * @type string + */ + _poll:[], + + /** + * The polling frequency, in milliseconds, for HandleReadyState. + * when attempting to determine a transaction's XHR readyState. + * The default is 50 milliseconds. + * @private + * @type int + */ + _polling_interval:50, + + /** + * A transaction counter that increments the transaction id for each transaction. + * @private + * @type int + */ + _transaction_id:0, + + /** + * Member to add an ActiveX id to the existing xml_progid array. + * In the event(unlikely) a new ActiveX id is introduced, it can be added + * without internal code modifications. + * @public + * @param string id The ActiveX id to be added to initialize the XHR object. + * @return void + */ + setProgId:function(id) + { + this.msxml_progid.unshift(id); + }, + + /** + * Member to modify the default polling interval. + * @public + * @param {int} i The polling interval in milliseconds. + * @return void + */ + setPollingInterval:function(i) + { + if(typeof i == 'number' && isFinite(i)){ + this._polling_interval = i; + } + }, + + /** + * Instantiates a XMLHttpRequest object and returns an object with two properties: + * the XMLHttpRequest instance and the transaction id. + * @private + * @param {int} transactionId Property containing the transaction id for this transaction. + * @return connection object + */ + createXhrObject:function(transactionId) + { + var obj,http; + try + { + // Instantiates XMLHttpRequest in non-IE browsers and assigns to http. + http = new XMLHttpRequest(); + // Object literal with http and tId properties + obj = { conn:http, tId:transactionId }; + } + catch(e) + { + for(var i=0; i= 200 && httpStatus < 300){ + responseObject = this.createResponseObject(o, callback.argument); + if(callback.success){ + if(!callback.scope){ + callback.success(responseObject); + } + else{ + // If a scope property is defined, the callback will be fired from + // the context of the object. + callback.success.apply(callback.scope, [responseObject]); + } + } + } + else{ + switch(httpStatus){ + // The following case labels are wininet.dll error codes that may be encountered. + // Server timeout + case 12002: + // 12029 to 12031 correspond to dropped connections. + case 12029: + case 12030: + case 12031: + // Connection closed by server. + case 12152: + // See above comments for variable status. + case 13030: + responseObject = this.createExceptionObject(o, callback.argument); + if(callback.failure){ + if(!callback.scope){ + callback.failure(responseObject); + } + else{ + callback.failure.apply(callback.scope,[responseObject]); + } + } + break; + default: + responseObject = this.createResponseObject(o, callback.argument); + if(callback.failure){ + if(!callback.scope){ + callback.failure(responseObject); + } + else{ + callback.failure.apply(callback.scope,[responseObject]); + } + } + } + } + + this.releaseObject(o); + }, + + /** + * This method evaluates the server response, creates and returns the results via + * its properties. Success and failure cases will differ in the response + * object's property values. + * @private + * @param {object} o The connection object + * @param {} callbackArg User-defined argument or arguments to be passed to the callback + * @return object + */ + createResponseObject:function(o, callbackArg) + { + var obj = {}; + var headerObj = {}; + + try + { + var headerStr = o.conn.getAllResponseHeaders(); + var header = headerStr.split("\n"); + for(var i=0; i < header.length; i++){ + var delimitPos = header[i].indexOf(':'); + if(delimitPos != -1){ + headerObj[header[i].substring(0,delimitPos)] = header[i].substring(delimitPos+1); + } + } + + obj.tId = o.tId; + obj.status = o.conn.status; + obj.statusText = o.conn.statusText; + obj.getResponseHeader = headerObj; + obj.getAllResponseHeaders = headerStr; + obj.responseText = o.conn.responseText; + obj.responseXML = o.conn.responseXML; + if(typeof callbackArg !== undefined){ + obj.argument = callbackArg; + } + } + catch(e){} + finally + { + return obj; + } + }, + + /** + * If a transaction cannot be completed due to dropped or closed connections, + * there may be not be enough information to build a full response object. + * The failure callback will be fired and this specific condition can be identified + * by a status property value of 0. + * @private + * @param {int} tId Transaction Id + * @param callbackArg The user-defined arguments + * @return object + */ + createExceptionObject:function(tId, callbackArg) + { + var COMM_CODE = 0; + var COMM_ERROR = 'communication failure'; + + var obj = {}; + + obj.tId = tId; + obj.status = COMM_CODE; + obj.statusText = COMM_ERROR; + if(callbackArg){ + obj.argument = callbackArg; + } + + return obj; + }, + + /** + * Public method that stores the custom HTTP headers for each transaction. + * @public + * @param {string} label The HTTP header label + * @param {string} value The HTTP header value + * @return void + */ + initHeader:function(label,value) + { + if(this._http_header[label] === undefined){ + this._http_header[label] = value; + } + else{ + this._http_header[label] = value + "," + this._http_header[label]; + } + + this._has_http_headers = true; + }, + + /** + * Accessor that sets the HTTP headers for each transaction. + * @private + * @param {object} o The connection object for the transaction. + * @return void + */ + setHeader:function(o) + { + for(var prop in this._http_header){ + o.conn.setRequestHeader(prop, this._http_header[prop]); + } + delete this._http_header; + + this._http_header = {}; + this._has_http_headers = false; + }, + + /** + * This method assembles the form label and value pairs and + * constructs an encoded string. + * asyncRequest() will automatically initialize the + * transaction with a HTTP header Content-Type of + * application/x-www-form-urlencoded. + * @public + * @param {string || object} form id or name attribute, or form object. + * @return void + */ + setForm:function(formId) + { + this._sFormData = ''; + if(typeof formId == 'string'){ + // Determine if the argument is a form id or a form name. + // Note form name usage is deprecated by supported + // here for legacy reasons. + var oForm = (document.getElementById(formId) || document.forms[formId] ); + } + else if(typeof formId == 'object'){ + var oForm = formId; + } + else{ + return; + } + var oElement, oName, oValue, oDisabled; + var hasSubmit = false; + + // Iterate over the form elements collection to construct the + // label-value pairs. + for (var i=0; i + * - The type of event + * - All of the arguments fire() was executed with as an array + * - The custom object (if any) that was passed into the subscribe() method + * + * + * @param {Array} an arbitrary set of parameters to pass to the handler + */ + fire: function() { + for (var i=0, len=this.subscribers.length; i= 0) { + cacheItem = listeners[index]; + } + + if (!el || !cacheItem) { + return false; + } + + + if (el.removeEventListener) { + el.removeEventListener(sType, cacheItem[this.WFN], false); + } else if (el.detachEvent) { + el.detachEvent("on" + sType, cacheItem[this.WFN]); + } + + // removed the wrapped handler + delete listeners[index][this.WFN]; + delete listeners[index][this.FN]; + delete listeners[index]; + + return true; + + }, + + /** + * Returns the event's target element + * @param {Event} ev the event + * @param {boolean} resolveTextNode when set to true the target's + * parent will be returned if the target is a + * text node + * @return {HTMLElement} the event's target + */ + getTarget: function(ev, resolveTextNode) { + var t = ev.target || ev.srcElement; + + if (resolveTextNode && t && "#text" == t.nodeName) { + return t.parentNode; + } else { + return t; + } + }, + + /** + * Returns the event's pageX + * @param {Event} ev the event + * @return {int} the event's pageX + */ + getPageX: function(ev) { + var x = ev.pageX; + if (!x && 0 !== x) { + x = ev.clientX || 0; + + if ( this.isIE ) { + x += this._getScrollLeft(); + } + } + + return x; + }, + + /** + * Returns the event's pageY + * @param {Event} ev the event + * @return {int} the event's pageY + */ + getPageY: function(ev) { + var y = ev.pageY; + if (!y && 0 !== y) { + y = ev.clientY || 0; + + if ( this.isIE ) { + y += this._getScrollTop(); + } + } + + return y; + }, + + /** + * Returns the pageX and pageY properties as an indexed array. + * @type int[] + */ + getXY: function(ev) { + return [this.getPageX(ev), this.getPageY(ev)]; + }, + + /** + * Returns the event's related target + * @param {Event} ev the event + * @return {HTMLElement} the event's relatedTarget + */ + getRelatedTarget: function(ev) { + var t = ev.relatedTarget; + if (!t) { + if (ev.type == "mouseout") { + t = ev.toElement; + } else if (ev.type == "mouseover") { + t = ev.fromElement; + } + } + + return t; + }, + + /** + * Returns the time of the event. If the time is not included, the + * event is modified using the current time. + * @param {Event} ev the event + * @return {Date} the time of the event + */ + getTime: function(ev) { + if (!ev.time) { + var t = new Date().getTime(); + try { + ev.time = t; + } catch(e) { + // can't set the time property + return t; + } + } + + return ev.time; + }, + + /** + * Convenience method for stopPropagation + preventDefault + * @param {Event} ev the event + */ + stopEvent: function(ev) { + this.stopPropagation(ev); + this.preventDefault(ev); + }, + + /** + * Stops event propagation + * @param {Event} ev the event + */ + stopPropagation: function(ev) { + if (ev.stopPropagation) { + ev.stopPropagation(); + } else { + ev.cancelBubble = true; + } + }, + + /** + * Prevents the default behavior of the event + * @param {Event} ev the event + */ + preventDefault: function(ev) { + if (ev.preventDefault) { + ev.preventDefault(); + } else { + ev.returnValue = false; + } + }, + + /** + * Finds the event in the window object, the caller's arguments, or + * in the arguments of another method in the callstack. This is + * executed automatically for events registered through the event + * manager, so the implementer should not normally need to execute + * this function at all. + * @param {Event} the event parameter from the handler + * @return {Event} the event + */ + getEvent: function(e) { + var ev = e || window.event; + + if (!ev) { + var c = this.getEvent.caller; + while (c) { + ev = c.arguments[0]; + if (ev && Event == ev.constructor) { + break; + } + c = c.caller; + } + } + + return ev; + }, + + /** + * Returns the charcode for an event + * @param {Event} ev the event + * @return {int} the event's charCode + */ + getCharCode: function(ev) { + return ev.charCode || ((ev.type == "keypress") ? ev.keyCode : 0); + }, + + /** + * @private + * Locating the saved event handler data by function ref + */ + _getCacheIndex: function(el, sType, fn) { + for (var i=0,len=listeners.length; i 0); + } + + // Delayed listeners + var stillDelayed = []; + + for (var i=0,len=delayedListeners.length; i 0) { + for (i=0,len=listeners.length; i ] <@ ; + +LAZY: 'number' ( -- parser ) + 'digit' <+> [ 0 [ swap 10 * + ] reduce ] <@ ; + +LAZY: 'quote' ( -- parser ) + [ CHAR: " = ] satisfy ; + +LAZY: 'string' ( -- parser ) + 'quote' sp [ + CHAR: " = not + ] satisfy <+> [ >string ] <@ &> 'quote' <& ; + +LAZY: 'identifier' ( -- parser ) + [ + [ blank? not ] keep + [ digit? not ] keep + [ CHAR: : = not ] keep + [ CHAR: " = not ] keep + CHAR: ; = not + and and and and + ] satisfy <+> [ >string ] <@ ; + +LAZY: 'atom' ( -- parser ) + 'number' 'identifier' <|> 'string' <|> ; + +LAZY: 'expression' ( -- parser ) + 'atom' sp <*> [ ] <@ ; + +GENERIC: (compile) ( ast -- ) + +M: ast-number (compile) + "data_stack.push(" , + ast-number-value number>string , + ")" , ; + +M: ast-string (compile) + "data_stack.push('" , + ast-string-value , + "')" , ; + +M: ast-identifier (compile) + "fjsc_" , ast-identifier-value , "()" , ; + +M: ast-expression (compile) + ast-expression-values [ + (compile) "; " , + ] each ; + +: compile ( ast -- string ) + [ + [ (compile) ] { } make [ write ] each + ] string-out ; + diff --git a/libs/fjsc/load.factor b/libs/fjsc/load.factor new file mode 100644 index 0000000000..9f44876fe2 --- /dev/null +++ b/libs/fjsc/load.factor @@ -0,0 +1,19 @@ +! Copyright (C) 2006 Chris Double. All Rights Reserved. +! See http://factorcode.org/license.txt for BSD license. +! +REQUIRES: libs/lazy-lists libs/parser-combinators ; + +PROVIDE: libs/fjsc +{ + +files+ { + "fjsc.factor" + } +} { + +tests+ { + "tests.factor" + } +} { + +help+ + { + } +} ; diff --git a/libs/fjsc/tests.factor b/libs/fjsc/tests.factor new file mode 100644 index 0000000000..e9f7fb983c --- /dev/null +++ b/libs/fjsc/tests.factor @@ -0,0 +1,22 @@ +! Copyright (C) 2006 Chris Double. All Rights Reserved. +! See http://factorcode.org/license.txt for BSD license. +! +USING: kernel test parser-combinators lazy-lists fjsc ; +IN: temporary + +{ "data_stack.push(123)" } [ + "123" 'number' parse car parse-result-parsed compile +] unit-test + +{ "fjsc_alert()" } [ + "alert" 'identifier' parse car parse-result-parsed compile +] unit-test + +{ "data_stack.push(123); fjsc_alert(); " } [ + "123 alert" 'expression' parse car parse-result-parsed compile +] unit-test + +{ "data_stack.push(123); data_stack.push('hello'); fjsc_alert(); " } [ + "123 \"hello\" alert" 'expression' parse car parse-result-parsed compile +] unit-test + \ No newline at end of file