Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

Make lispyscript browser compliant #16

Merged
merged 12 commits into from

2 participants

@Gozala

This is yet another batch of changes on top of other pull request, I've already submitted. This does multiple things:

  • Moves engine specific stuff into separate modules node.ls and browser.ls
  • Adds new special form as things like XMLHttpRequest throw if new is not used.
  • Adds array-ish macros that were missing.
  • Creates try coffee like page for trying out lispyscript and to see live preview of generated JS. Here is my gh-pages branch with this.
  • Adds support for <script type="application/lispyscript"> tags in similar manner as done by coffee script.

All other changes are part of other pull requests I've send earlier.

@santoshrajan santoshrajan merged commit a72f9a2 into from
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
This page is out of date. Refresh to see the latest.
View
169 index.html
@@ -0,0 +1,169 @@
+<html>
+ <head>
+ <meta charset="utf-8">
+ <title>LispyScript</title>
+ <script src="http://codemirror.net/lib/codemirror.js"></script>
+ <script src="http://codemirror.net/mode/scheme/scheme.js"></script>
+ <script src="http://codemirror.net/mode/javascript/javascript.js"></script>
+ <link rel="stylesheet" href="http://codemirror.net/lib/codemirror.css">
+ <link rel="stylesheet" href="http://codemirror.net/theme/ambiance.css">
+ <link rel="stylesheet" href="http://codemirror.net/doc/docs.css">
+
+ <script data-main=./lib/browser type=text/javascript src=http://requirejs.org/docs/release/2.0.2/minified/require.js></script>
+ <script>
+ require.config({
+ paths: {
+ "underscore": "https://raw.github.com/amdjs/underscore/9e944c2dd3e1c64227a260d1974c44a29c38e962/underscore-min"
+ }
+ })
+ </script>
+ <script type=application/lispyscript src=./src/macros.ls></script>
+ <style>
+ html, body {
+ font-size: 12px;
+ margin: 0;
+ padding: 0;
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 100%;
+ min-width: 100%;
+ height: 100%;
+ min-height: 100%;
+ }
+
+ .CodeMirror {
+ float: left;
+ width: 50%;
+ height: 100%;
+ }
+
+ .CodeMirror .CodeMirror-scroll {
+ height: 100%;
+ }
+ </style>
+ </head>
+ <body>
+<textarea id="input" name="input">
+;; Hello World! in LispyScript.
+(console.log "Hello LispyScript!")
+
+;; A more intricate Hello World!
+(if (undefined? window)
+ (console.log "Hello LispyScript!")
+ (alert "Hello LispyScript!"))
+
+; Functions
+;; An anonymous function in LispyScript
+(function (x) (* x x))
+
+;; The first element in an expression can be an anonymous function.
+((function (x) (* x x)) 2)
+
+;; You can set a variable name to a function.
+(var square
+ (function (x)
+ (* x x)))
+(console.log (square 10))
+
+; LispyScript is Javascript!
+
+(Array.prototype.forEach.call [1, 2, 3]
+ (function (elem index list)
+ (console.log elem)))
+
+;; You can access object methods and properties using the "." notation.
+
+(console.log (.greet {greet: "hello"}))
+
+;; You can also use the 'get' expression to access a property of an object.
+
+(console.log (get "greet" {greet: "hello"}))
+(console.log (get 1 [1, 2, 3]))
+
+;; You can 'set' variables too.
+
+(set window.onload (function () (alert "Page Loaded")))
+
+; Node
+;; The node server example in LispyScript.
+
+(var http (require "http"))
+(var server
+ (http.createServer
+ (function (request response)
+ (response.writeHead 200 {'Content-Type': 'text/plain'})
+ (response.end "Hello World\n"))))
+(server.listen 1337 "127.0.0.1")
+(console.log "Server running at http://127.0.0.1:1337/")
+
+; Macros
+
+;; You can define a macro.
+(macro array? (obj)
+ (= (toString.call ~obj) "[object Array]"))
+
+;; Now let us create a Lisp like 'let' macro in LispyScript.
+
+(macro let (names vals rest...)
+ ((function ~names ~rest...) ~@vals))
+
+(let (name email tel) ("John" "john@example.org" "555-555-5555")
+ (console.log name)
+ (console.log email)
+ (console.log tel))
+
+;; Conditions
+
+(if (= document.readyState "complete")
+ (console.log "loaded") ;; true expression
+ (console.log "loading")) ;; optional false expression
+
+;; Do expression
+
+(if (= process.argv.length 2)
+ (do
+ (process.stdin.resume)
+ (process.stdin.setEncoding "utf8")
+ (compile process.stdin process.stdout (process.cwd))))
+
+;; Each macro
+
+(each [1, 2, 3]
+ (function (elem index list)
+ (console.log elem)))
+
+;; Exception handling
+
+(var fs (require 'fs'))
+(var outfile "text.txt")
+(try
+ (fs.writeFileSync outfile "Hello World")
+ (function (e)
+ (console.log (+ "Cannot write file " outfile)
+ (process.exit 1))))
+
+</textarea>
+<textarea id="output" name="output"></textarea>
+ <script>
+ function updatePreview(editor) {
+ output.setValue(require('../lib/ls')._compile(editor.getValue()))
+ }
+ var input = CodeMirror.fromTextArea(document.getElementById("input"), {
+ lineNumbers: true,
+ mode: "scheme",
+ theme: "ambiance",
+ autofocus: true,
+ onChange: updatePreview
+ });
+ var output = CodeMirror.fromTextArea(document.getElementById("output"), {
+ lineNumbers: true,
+ mode: "javascript",
+ theme: "ambiance",
+ readOnly: "nocursor"
+ });
+
+ setTimeout(updatePreview, 1000, input)
+ </script>
+ </body>
+</html>
View
48 lib/browser.js
@@ -0,0 +1,48 @@
+// Generated by LispyScript v0.1.6
+define(function(require,exports,module) {
+ var ls = require("../lib/ls");
+ exports.eval = function(code,url) {
+ return eval(ls._compile(code,url));
+ };
+ exports.run = function(code,url) {
+ return Function(ls._compile(code,url))();
+ };
+ exports.load = function(url,callback) {
+ var request = window.XMLHttpRequest ?
+ new XMLHttpRequest() :
+ new ActiveXObject("Microsoft.XMLHTTP");
+ request.open("GET",url,true);
+ request.overrideMimeType ?
+ request.overrideMimeType("text/plain") :
+ undefined;
+ request.onreadystatechange = function() {
+ return (request.readyState === 4) ?
+ ((request.status === 0) || (request.status === 200)) ?
+ callback(exports.run(request.responseText,url)) :
+ callback("Could not load") :
+ undefined;
+ };
+ return request.send(null);
+ };
+ var runScripts = function() {
+ var scripts = Array.prototype.filter.call(document.getElementsByTagName("script"),function(script) {
+ return (script.type === "application/lispyscript");
+ });
+ var next = function() {
+ return scripts.length ?
+ (function() {
+ var script = scripts.shift();
+ return script.src ?
+ exports.load(script.src,next) :
+ next(exports.run(script.innerHTML));
+ })() :
+ undefined;
+ };
+ return next();
+ };
+ return ((document.readyState === "complete") || (document.readyState === "interactive")) ?
+ runScripts() :
+ window.addEventListener ?
+ addEventListener("DOMContentLoaded",runScripts,false) :
+ attachEvent("onload",runScripts);
+});
View
34 lib/lispy.js
@@ -1,16 +1,19 @@
// Generated by LispyScript v0.1.6
+require("./node");
var fs = require("fs");
-var ls = require("./ls");
+var path = require("path");
+var ls = require("../lib/ls");
var repl = require("./repl");
var exit = function(error) {
return error ?
(function() {
console.log(error);
return process.exit(1);
- })() : process.exit(0);
+ })() :
+ process.exit(0);
};
var compileFiles = function(input,output) {
- return compile(fs.createReadStream(input),fs.createWriteStream(output));
+ return compile(fs.createReadStream(input),fs.createWriteStream(output),path.resolve(input));
};
var compile = function(input,output,uri) {
var source = "";
@@ -18,9 +21,14 @@ var compile = function(input,output,uri) {
return source = (source + chunck.toString());
});
input.on("end",function() {
- var jscode = (function () {try {
- return output.write(ls._compile(source,uri));
- } catch (err) {return (exit())(err);}})();
+ var jscode = (function () {
+ try {
+ return output.write(ls._compile(source,uri));
+
+ } catch (e) {
+ return (exit)(e);
+ }
+ })();
});
input.on("error",exit);
return output.on("error",exit);
@@ -30,19 +38,23 @@ exports.run = function() {
(function() {
process.stdin.resume();
process.stdin.setEncoding("utf8");
- compile(process.stdin,process.stdout);
+ compile(process.stdin,process.stdout,process.cwd());
setTimeout(function() {
return (process.stdin.bytesRead === 0) ?
(function() {
process.stdin.removeAllListeners("data");
return repl.runrepl();
- })() : undefined;
+ })() :
+ undefined;
},20);
- })() : (process.argv.length === 3) ?
+ })() :
+ (process.argv.length === 3) ?
(function() {
var i = process.argv[2];
var o = i.replace(".ls",".js");
return (i === o) ?
- console.log("Input file must have extension '.ls'") : compileFiles(i,o);
- })() : compileFiles(process.argv[2],process.argv[3]);
+ console.log("Input file must have extension '.ls'") :
+ compileFiles(i,o);
+ })() :
+ compileFiles(process.argv[2],process.argv[3]);
};
View
60 lib/ls.js
@@ -1,19 +1,19 @@
/*
- *
+ *
LispyScript - Javascript using tree syntax!
This is the compiler written in javascipt
*
*/
+define(function(require, exports, module) {
-var fs = require('fs'),
- path = require('path'),
- _ = require('underscore');
+var _ = require('underscore');
+var Module = module.constructor;
String.prototype.repeat = function(num) {
return new Array(num + 1).join(this);
};
-this.version = "0.1.6";
+exports.version = "0.1.6";
var _LS = {},
banner = "// Generated by LispyScript v" + this.version + "\n",
isWhitespace = /\s/,
@@ -25,10 +25,11 @@ var _LS = {},
macros = {},
templates = {};
templates["var"] = _.template("var <%= rest %>");
+templates["new"] = _.template("new <%= rest %>");
templates["set"] = _.template("<%= name %> = <%= value %>");
templates["function"] = _.template("function(<%= params %>) {\n<%= expressions %><%= indent %>}");
-templates["try"] = _.template("(function () {try {\n<%= trypart %><%= indent %>} catch (err) {return (<%= catchpart %>)(err);}})()");
-templates["if"] = _.template("<%= condition %> ?\n<%= indent %><%= trueexpr %> : <%= falseexpr %>");
+templates["try"] = _.template("(function () {\n<%= indent %><%= indent %>try {\n<%= indent %><%= trypart %>\n<%= indent %><%= indent %>} catch (e) {\n<%= indent %><%= indent %><%= indent %>return (<%= catchpart %>)(e);\n<%= indent %><%= indent %>}\n<%= indent %>})()");
+templates["if"] = _.template("<%= condition %> ?\n<%= indent %><%= trueexpr %> :\n<%= indent %><%= falseexpr %>");
templates["get"] = _.template("<%= list %>[<%= key %>]");
templates["operator"] = _.template("(<%= loperand %> <%= operator %> <%= roperand %>)");
templates["str"] = _.template("[<%= elems %>].join('')");
@@ -38,7 +39,7 @@ var parse = function(code, filename) {
var length = code.length,
pos = 1,
lineno = 1;
-
+
var parser = function() {
var tree = [],
token = "",
@@ -172,7 +173,7 @@ var handleExpressions = function(exprs) {
_.each(exprs, function(expr, i, exprs) {
var tmp = "", r = "";
if (_.isArray(expr)) {
- if (expr[0] === "include")
+ if (expr[0] === "include")
ret += handleExpression(expr);
else
tmp = handleExpression(expr);
@@ -207,13 +208,12 @@ var handleExpression = function(expr) {
if (!fName) throw handleError(1, expr._line);
if (isFunction.test(fName)) fName = "(" + fName + ")";
return fName + "(" + expr.slice(1).join(",") + ")";
-
};
var handleSubExpressions = function(expr) {
_.each(expr, function(value, i, t) {
if (_.isArray(value)) t[i] = handleExpression(value);
- });
+ });
};
var macroExpand = function(tree) {
@@ -249,7 +249,7 @@ var macroExpand = function(tree) {
} else {
ret.push(repl);
}
- } else {
+ } else {
ret.push(token);
}
}
@@ -267,18 +267,6 @@ var handleOperator = function(arr) {
return templates["operator"]({operator: arr[0], loperand: arr[1], roperand: arr[2]});
};
-var includeFile = (function () {
- var included = [];
- return function(filename) {
- var found = _.find(included, function(f) {return f === filename});
- if (found) return "";
- included.push(filename);
- var code = fs.readFileSync(filename);
- var tree = parse(code, filename);
- return handleExpressions(tree);
- };
-})();
-
var handleError = function(no, line, filename) {
return errors[no] + ((line) ? "\nLine no " + line : "") + ((filename) ? "\nFile " + filename : "");
};
@@ -288,6 +276,12 @@ keywords["var"] = function(arr) {
return templates["var"]({rest: keywords.set(arr)});
};
+keywords["new"] = function(arr) {
+ if (arr.length < 2) throw handleError(0, arr._line, arr._filename);
+ return templates["new"]({ rest: handleExpression(arr.slice(1)) });
+};
+
+
keywords["set"] = function(arr) {
if (arr.length != 3) throw handleError(0, arr._line, arr._filename);
return templates["set"]({
@@ -309,7 +303,7 @@ keywords["try"] = function(arr) {
var c = arr.pop();
return templates["try"]({
trypart: handleExpressions(arr.slice(1)),
- catchpart: handleExpression(c),
+ catchpart: _.isArray(c) ? handleExpression(c) : c,
indent: " ".repeat(indent)});
};
@@ -350,14 +344,19 @@ keywords["include"] = function(arr) {
var filename = arr[1];
if (typeof filename === "string")
filename = filename.replace(/["']/g, "");
+
+ var module = new Module(arr._filename);
+ module.filename = arr._filename;
+
try {
- filename = fs.realpathSync(filename);
+ filename = Module._resolveFilename(filename, module);
+ require(filename);
} catch (err) {
throw handleError(11, arr._line, arr._filename);
}
- var ret = includeFile(filename);
+
indent += 4;
- return ret;
+ return "";
};
keywords["+"] = handleOperator;
@@ -406,8 +405,9 @@ errors[9] = "Invalid character in var name";
errors[10] = "Extra chars at end of file. Maybe an extra ')'.";
errors[11] = "Cannot Open include File";
-this._compile = function(code, filename) {
- includeFile(path.join(__dirname,"../src") + "/macros.ls");
+exports._compile = function(code, filename) {
var tree = parse(code, filename);
return banner + handleExpressions(tree);
};
+
+});
View
13 lib/node.js
@@ -0,0 +1,13 @@
+// Generated by LispyScript v0.1.6
+require("amd-loader");
+var fs = require("fs");
+var ls = require("../lib/ls");
+global.define = function() {
+ var factory = Array.prototype.slice.call(arguments,-1);
+ return factory(require,exports,module);
+};
+require.extensions[".ls"] = function(module,filename) {
+ var code = fs.readFileSync(filename,"utf8");
+ return module._compile(ls._compile(code,filename),filename);
+};
+require("../src/macros");
View
16 lib/repl.js
@@ -1,16 +1,22 @@
// Generated by LispyScript v0.1.6
+require("./node");
var readline = require("readline");
-var ls = require("./ls");
+var ls = require("../lib/ls");
var prefix = "lispy> ";
exports.runrepl = function() {
var rl = readline.createInterface(process.stdin,process.stdout);
rl.on('line',function(line) {
- (function () {try {
- var l = ls._compile(line);
+ (function () {
+ try {
+ var l = ls._compile(line);
return console.log(eval(l));
- } catch (err) {return (function(err) {
+
+ } catch (e) {
+ return (function(err) {
return console.log(err);
- })(err);}})();
+ })(e);
+ }
+ })();
rl.setPrompt(prefix,prefix.length);
return rl.prompt();
});
View
5 package.json
@@ -25,9 +25,10 @@
"url": "git://github.com/santoshrajan/lispyscript.git"
},
"scripts": {
- "prepublish": "lispy src/lispy.ls lib/lispy.js && lispy src/repl.ls lib/repl.js"
+ "prepublish": "lispy src/lispy.ls lib/lispy.js && lispy src/repl.ls lib/repl.js && lispy src/node.ls lib/node.js && lispy src/browser.ls lib/browser.js"
},
"dependencies": {
- "underscore": ">=1.0.0"
+ "underscore": ">=1.0.0",
+ "amd-loader": "~0.0.4"
}
}
View
61 src/browser.ls
@@ -0,0 +1,61 @@
+(define (function (require exports module)
+
+(var ls (require "../lib/ls"))
+
+(set exports.eval
+ (function (code url)
+ (eval (ls._compile code url))))
+
+;; Running code does not provide access to this scope.
+(set exports.run
+ (function (code url)
+ ((Function (ls._compile code url)))))
+
+;; If we're not in a browser environment, we're finished with the public API.
+;; return unless window?
+;;
+;; Load a remote script from the current domain via XHR.
+(set exports.load
+ (function (url callback)
+ (var request
+ (if window.XMLHttpRequest
+ (new XMLHttpRequest)
+ (new ActiveXObject "Microsoft.XMLHTTP")))
+ (request.open "GET" url true)
+ (if request.overrideMimeType (request.overrideMimeType "text/plain"))
+ (set request.onreadystatechange
+ (function ()
+ (if (= request.readyState 4)
+ (if (|| (= request.status 0) (= request.status 200))
+ (callback (exports.run request.responseText url))
+ (callback "Could not load")))))
+ (request.send null)))
+
+;; Activate LispyScript in the browser by having it compile and evaluate
+;; all script tags with a content-type of `application/lispyscript`.
+;; This happens on page load.
+(var runScripts
+ (function ()
+ (var scripts
+ (filter
+ (document.getElementsByTagName "script")
+ (function (script) (= script.type "application/lispyscript"))))
+
+ (var next
+ (function ()
+ (if scripts.length
+ (do
+ (var script (scripts.shift))
+ (if script.src
+ (exports.load script.src next)
+ (next (exports.run script.innerHTML)))))))
+
+ (next)))
+
+;; Listen for window load, both in browsers and in IE.
+(if (|| (= document.readyState "complete")
+ (= document.readyState "interactive"))
+ (runScripts)
+ (if window.addEventListener
+ (addEventListener "DOMContentLoaded" runScripts false)
+ (attachEvent "onload" runScripts)))))
View
11 src/lispy.ls
@@ -1,7 +1,9 @@
;; The lispy command script
+(require "./node")
(var fs (require "fs"))
-(var ls (require "./ls"))
+(var path (require "path"))
+(var ls (require "../lib/ls"))
(var repl (require "./repl"))
(var exit
@@ -16,7 +18,8 @@
(function (input output)
(compile
(fs.createReadStream input)
- (fs.createWriteStream output))))
+ (fs.createWriteStream output)
+ (path.resolve input))))
(var compile
(function (input output uri)
@@ -31,7 +34,7 @@
(var jscode
(try
(output.write (ls._compile source uri))
- (exit)))))
+ exit))))
(input.on "error" exit)
(output.on "error" exit)))
@@ -42,7 +45,7 @@
(do
(process.stdin.resume)
(process.stdin.setEncoding "utf8")
- (compile process.stdin process.stdout)
+ (compile process.stdin process.stdout (process.cwd))
(setTimeout
(function ()
(if (= process.stdin.bytesRead 0)
View
31 src/macros.ls
@@ -3,28 +3,28 @@
(macro object? (obj)
(= (typeof ~obj) "object"))
-
+
(macro array? (obj)
(= (toString.call ~obj) "[object Array]"))
-
+
(macro string? (obj)
(= (toString.call ~obj) "[object String]"))
-
+
(macro number? (obj)
(= (toString.call ~obj) "[object Number]"))
-
+
(macro boolean? (obj)
(= (typeof ~obj) "boolean"))
-
+
(macro function? (obj)
(= (toString.call ~obj) "[object Function]"))
-
+
(macro undefined? (obj)
(= (typeof ~obj) "undefined"))
-
+
(macro null? (obj)
(= ~obj null))
-
+
(macro do (rest...)
((function () ~rest...)))
@@ -33,13 +33,22 @@
(macro unless (cond rest...)
(when (! ~cond) (do ~rest...)))
-
+
(macro each (rest...)
(Array.prototype.forEach.call ~rest...))
-
+
(macro map (rest...)
(Array.prototype.map.call ~rest...))
-
+
+(macro filter (rest...)
+ (Array.prototype.filter.call ~rest...))
+
+(macro some (rest...)
+ (Array.prototype.some.call ~rest...))
+
+(macro every (rest...)
+ (Array.prototype.every.call ~rest...))
+
(macro reduce (rest...)
(Array.prototype.reduce.call ~rest...))
View
18 src/node.ls
@@ -0,0 +1,18 @@
+(require "amd-loader")
+(var fs (require "fs"))
+(var ls (require "../lib/ls"))
+
+(set global.define
+ (function ()
+ (var factory (Array.prototype.slice.call arguments -1))
+ (factory require exports module)))
+
+;; Register `.ls` file extension so that `ls`
+;; modules can be simply required.
+(set require.extensions[".ls"]
+ (function (module filename)
+ (var code (fs.readFileSync filename "utf8"))
+ (module._compile (ls._compile code filename) filename)))
+
+;; Load macros to be included into a compiler.
+(require "../src/macros")
View
3  src/repl.ls
@@ -1,7 +1,8 @@
;; A very simple REPL written in LispyScript
+(require "./node")
(var readline (require "readline"))
-(var ls (require "./ls"))
+(var ls (require "../lib/ls"))
(var prefix "lispy> ")
Something went wrong with that request. Please try again.