Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

Put all development in gh-pages branch to avoid annoyance

  • Loading branch information...
commit 614376ac30bbd19f7995d8dbb4dc2a22215221b5 1 parent 0fa9f8b
authored
16 README.txt
... ... @@ -0,0 +1,16 @@
  1 +To run the index page against the source files instead of the build files, you replace this script tag:
  2 +
  3 + <script type="text/javascript" src="Javathcript.js"></script>
  4 +
  5 +with these script tags:
  6 +
  7 + <script type="text/javascript" src="../source/BPWJs.js"></script>
  8 + <script type="text/javascript" src="../source/Atom.js"></script>
  9 + <script type="text/javascript" src="../source/UnevaluatedObj.js"></script>
  10 + <script type="text/javascript" src="../source/JavathcriptTokenizer.js"></script>
  11 + <script type="text/javascript" src="../source/JavathcriptParser.js"></script>
  12 + <script type="text/javascript" src="../source/Environment.js"></script>
  13 + <script type="text/javascript" src="../source/Javathcript.js"></script>
  14 + <script type="text/javascript" src="../source/DocumentEvaluator.js"></script>
  15 +
  16 +You can build a new Javathcript.js file by running the build.sh script inside the build folder.
5 build/build.sh
... ... @@ -0,0 +1,5 @@
  1 +currdir=`dirname $0`
  2 +currdir=`readlink -f $currdir`
  3 +
  4 +cd ${currdir}/..
  5 +java -jar ${currdir}/compiler.jar --js=source/BPWJs.js --js=source/Atom.js --js=source/UnevaluatedObj.js --js=source/JavathcriptTokenizer.js --js=source/JavathcriptParser.js --js=source/Environment.js --js=source/Javathcript.js --js=source/DocumentEvaluator.js --js_output_file=webcontent/Javathcript.js --compilation_level WHITESPACE_ONLY
BIN  build/compiler.jar
Binary file not shown
286 index.html
... ... @@ -1,285 +1 @@
1   -<html>
2   -<head>
3   - <title>Javathcript - Javascript with a lisp</title>
4   -
5   - <link rel="stylesheet" href="style/main.css" />
6   -
7   - <script type="text/javascript" src="Javathcript.js"></script>
8   - <script type="text/lisp" src="lisp/prelude.lsp"></script>
9   -
10   - <script type="text/javascript">
11   - function evaluateElement(srcElementName, resultElementName) {
12   - var srcElement = document.getElementById(srcElementName);
13   - var code = srcElement.innerHTML.replace(/<span[^>]*>/g, '').replace(/<\/span[^>]*>/g, '');
14   - code = code.replace(/&gt;/g, ">");
15   - code = code.replace(/&lt;/g, "<");
16   - var result = Javathcript.eval(code);
17   - var resultElement = document.getElementById(resultElementName);
18   - resultElement.style.display = 'block';
19   - resultElement.innerHTML = result.toString();
20   - }
21   -
22   - function format(code) {
23   - for (var func in Javathcript.environment) {
24   - func = func.replace(/\*/g, "\\*");
25   - if ("<span class='func'></span>t".indexOf(func) < 0) {
26   - code = code.replace(new RegExp("\\("+func, "g"), "(<span class='func'>"+func+"</span>");
27   - }
28   - code = code.replace(/(\"[^\"]*\")/g, "<span class='string'>$1</span>");
29   - }
30   - return code;
31   - }
32   - var divId = 0;
33   - function insertExamples(examples) {
34   - for (var i = 0; i < examples.length; ++i) {
35   - var id = divId ++;
36   - document.write("<pre class='evaluable' onclick=\"evaluateElement('javathcript_example_"+id+"', 'javathcript_example_result_"+id+"')\" id='javathcript_example_"+id+"'>"+format(examples[i])+"</pre><pre class='result' id='javathcript_example_result_"+id+"'></pre>");
37   - }
38   - }
39   -
40   - </script>
41   -</head>
42   -
43   -<body>
44   -
45   - <a name="top"><h1>Javathcript - Javascript with a lisp</h1></a>
46   -
47   - <p>Javathcript allows you to script your web pages in a simple lisp variant. Once you include <a href="Javathcript.js">Javathcript.js</a>,
48   - any script tags in your document with <code>type="text/lisp"</code> will be evaluated. It will also download lisp files (only from the original server),
49   - if you have a script tag that has a <code>src</code> attribute. Finally, you can also evaluate lisp code from javascript using
50   - <code>Javathcript.eval(lispString)</code>.</p>
51   -
52   - <p>While it is not an exact implementation of any pre-existing variant of lisp, if you know lisp most of it should be familiar.
53   - If you don't you might find it useful to follow a tutorial, e.g. <a href="http://www.n-a-n-o.com/lisp/cmucl-tutorials/LISP-tutorial.html">this one</a>.
54   - There <em>will</em> be differences between this implementation and others, but there is also much that is common.</p>
55   -
56   - <h2>Contents</h2>
57   -
58   - <ol type="1">
59   - <li><a href="#top">Introduction</a></li>
60   - <li><a href="#usage">Example Usage</a></li>
61   - <li><a href="#functions">Javathcript Functions</a>
62   - <ol type="i">
63   - <li><a href="#functions-basic">Functions from <em>A Micro-manual for Lisp - Not the Whole Truth</em></a></li>
64   - <li><a href="#functions-scope">Scope</a></li>
65   - <li><a href="#functions-sugar">Syntactic Sugar, Comments and Aliases</a></li>
66   - <li><a href="#functions-strings">Strings</a></li>
67   - <li><a href="#functions-numbers">Numbers and Maths</a></li>
68   - <li><a href="#functions-objects">Objects</a></li>
69   - <li><a href="#functions-js">Interacting with Javascript and the Browser</a></li>
70   - </ol>
71   - </li>
72   - <li><a href="#have-a-go">Have a Go</a></li>
73   - <li><a href="#getting-source">Getting the Source</a></li>
74   - <li><a href="#limitations">Limitations</a></li>
75   - <li><a href="#acknowledgements">Acknowledgements</a></li>
76   - <li><a href="#contact">Contact</a></li>
77   - </ol>
78   -
79   - <a name="usage"><h2>Example Usage</h2></a>
80   -
81   - <p>Since the browser doesn't natively understand script tags with <code>type="text/lisp"</code>, the <a href="Javathcript.js">Javathcript.js</a> file
82   - is included in the header to provide parsing and evaluation for lisp code. We have also included the <a href="lisp/prelude.lsp">prelude.lsp</a> lisp
83   - source file in the header since it defines a number of functions that this code uses (e.g. <code>getElement</code> and <code>alert</code>).
84   - To include these files, the following script tags are in the head of this page. Note that the <code>prelude.lsp</code> file has to be served from the same
85   - server as any page that uses it, as it is fetched by an XHR.
86   -
87   - <pre>&lt;<span class='tags'>script</span> <span class='attributes'>type</span>=<span class='string'>"text/javascript"</span> <span class='attributes'>src</span>=<span class='string'>"Javathcript.js"</span>&gt;&lt;<span class='tags'>/script</span>&gt;
88   -&lt;<span class='tags'>script</span> <span class='attributes'>type</span>=<span class='string'>"text/lisp"</span> <span class='attributes'>src</span>=<span class='string'>"lisp/prelude.lsp"</span>&gt;&lt;<span class='tags'>/script</span>&gt;</pre>
89   -
90   - <p>The block of code below attaches a <em>javathcript</em> handler to the <code>onclick</code> attribute of the button.
91   - Click the button to see the code execute. <input id='name' type="text" value="Fred"></input><button id='btn'>Click Me</button></p>
92   -
93   - <script type="text/lisp">
94   - (let*
95   - ( (button (getElement "btn"))
96   - (nameField (getElement "name"))
97   - (clickHandler (lambda () (alert (concat "Hello " (get nameField "value"))))) )
98   - (set button "onclick" (export clickHandler))
99   - )
100   - </script>
101   - <pre>&lt;<span class='tags'>script</span> <span class='attributes'>type</span>=<span class='string'>"text/lisp"</span>&gt;
102   - (<span class='func'>let*</span>
103   - ( (button (<span class='func'>getElement</span> <span class='string'>"btn"</span>))
104   - (nameField (<span class='func'>getElement</span> <span class='string'>"name"</span>))
105   - (clickHandler (<span class='func'>lambda</span> () (<span class='func'>alert</span> (<span class='func'>concat</span> <span class='string'>"Hello "</span> (<span class='func'>get</span> nameField <span class='string'>"value"</span>))))) )
106   - (<span class='func'>set</span> button <span class='string'>"onclick"</span> (<span class='func'>export</span> clickHandler))
107   - )
108   -&lt;<span class='tags'>/script</span>&gt;</pre>
109   -
110   - <a name="functions"><h2>Javathcript Functions</h2></a>
111   -
112   - <a name="functions-basic"><h3>Functions from <em>A Micro-manual for Lisp - Not the Whole Truth</em></h3></a>
113   -
114   - <code>quote car cdr cons equal atom cond lambda label</code>
115   - <p>These functions provide the core of Lisp, and are enough to create a self-hosting LISP interpreter. Here are some examples (click to evaluate them):</p>
116   - <script type="text/javascript">
117   - insertExamples([
118   - "(quote a)",
119   - "(car (quote (a b c)))",
120   - "(cdr (quote (a b c)))",
121   - "(cons (quote a) (quote (b c)))",
122   - "(equal (car (quote (a b))) (quote a))",
123   - "(cond ((atom (quote a)) (quote b)) ((quote t) (quote c)))",
124   - "((lambda (X Y) (cons (car X) Y)) (quote (a b)) (cdr (quote (c d))))",
125   - "((label ff (lambda (x) (cond ((atom x) x) ((quote t) (ff (car x)))))) (quote ((a b) c)))"
126   - ]);
127   - </script>
128   - <p>The <em>Micro-manual</em> also suggests <code>t Nil or and not null defun list</code> which I have included, and <code>cadr</code> with arbitrary combinations
129   - of 'a' and 'd', which I have not. I've modified the following example slightly from that given in the paper as the empty list is not regarded as an
130   - atom in <em>javathcript</em>.</p>
131   -
132   - <script type="text/javascript">
133   - insertExamples([
134   - "(defun subst (x y z) (cond ((or (atom z) (null z)) (cond ((equal z y) x) (t z))) (t (cons (subst x y (car z)) (subst x y (cdr z))))))",
135   - "(subst (quote (plus x y)) (quote v) (quote (times x v)))"
136   - ]);
137   - </script>
138   -
139   - <a name="functions-scope"><h3>Scope</h3></a>
140   -
141   - <code>def defun let let*</code>
142   -
143   - <p>As well as <code>defun</code> to set a name for a function in the global scope, there is also <code>def</code>, to allow you to set names for
144   - other kinds of values in the global scope. Functions provide their own scope, but you can also use <code>let</code> and <code>let*</code> to create
145   - temporary scopes. The code example at the top of this page used <code>let*</code> to assign names for the button, the nameField and the clickHandler.</p>
146   -
147   - <a name="functions-sugar"><h3>Syntactic Sugar, Comments and Aliases</h3></a>
148   -
149   - <code>if ' ;</code>
150   -
151   - <p>You can quote something by simply putting the single quote in front of it. This is a bit more terse than having to wrap it in <code>(quote ...)</code>.
152   - Comments are lines that start with the <code>;</code> character. <code>if</code> provides a slighly more intuitive alternative to <code>cond</code>.</p>
153   -
154   - <p>A number of aliases are provided in the <a href="lisp/prelude.lsp">prelude.lsp</a> file, eg. <code>eq eq? head tail first rest</code>, as well as
155   - a number of useful functions, e.g. <code>map</code>.</p>
156   -
157   - <a name="functions-strings"><h3>Strings</h3></a>
158   -
159   - <code>length concat substring</code>
160   -
161   - <p>As well as atoms and lists, <em>Javathcript</em> also provides some support for strings Strings literals can
162   - be entered by surrounding them in double quotes.</p>
163   -
164   - <p>Most functions that work on lists will also work on strings, although unlike in some lisps, <em>Javathcript</em> strings are not lists.
165   - Also note that <code>concat</code> if started with a string will concatenate numbers into the string too.</p>
166   -
167   - <script type="text/javascript">
168   - insertExamples([
169   - "(concat (substring (concat \"hello everyone\" \" in the room\") 6 18) \"number \" 23)",
170   - "(head (tail \"abc\"))",
171   - "(length \"Do not ask for whom the bell tolls\")",
172   - "; Empty string is equal to null\n\t(null \"\")"
173   - ]);
174   - </script>
175   -
176   - <a name="functions-numbers"><h3>Numbers and Maths</h3></a>
177   -
178   - <code>plus minus divide times rem &lt; &gt; &lt;= &gt;= = /=</code>
179   -
180   - <p><em>Javathcript</em> supports numeric literals, and the normal arithmetic functions (which are aliased by <a href="lisp/prelude.lsp">prelude.lsp</a> to be accessible by the symbols too).</p>
181   -
182   - <code>sin cos tan asin acos atan floor max min log abs ceil pow exp atan2 random sqrt round</code>
183   -
184   - <p>The <a href="lisp/prelude.lsp">prelude.lsp</a> also imports all the javascript Math functions.</p>
185   -
186   - <script type="text/javascript">
187   - insertExamples([
188   - "(defun fibonacci (N) (if (< N 2) 1 (+ (fibonacci (- N 2)) (fibonacci (- N 1)))))",
189   - "(map fibonacci '(0 1 2 3 4 5))",
190   - "(defun factorial (N) (if (= N 1) 1 (* N (factorial (- N 1)))))",
191   - "(* (random) (factorial 5))",
192   - "(floor (sqrt (pow 5 5)))",
193   - ]);
194   - </script>
195   -
196   - <a name="functions-objects"><h3>Objects</h3></a>
197   -
198   - <code>set get</code>
199   -
200   - <p>Object literals can be entered surrounded by curly braces, similarly to in javascript. <code>set</code> sets properties on them, and <code>get</code>
201   - returns previously set properties.
202   - </p>
203   -
204   - <script type="text/javascript">
205   - insertExamples([
206   - "(def obj {a: \"hello\", b: '(a b c), number: 0})",
207   - "(tail (get obj \"b\"))",
208   - "(set obj a (concat (get obj a) \" world\"))",
209   - "(get obj a)"
210   - ]);
211   - </script>
212   -
213   - <a name="functions-js"><h3>Interacting with Javascript and the Browser</h3></a>
214   -
215   - <code>js method export</code>
216   -
217   - <p>The <code>js</code> function evaluates its string argument, and returns the result. If the result is a function, a small adjustment is made to it
218   - so that it will evaluate its arguments in the current <em>Javathcript</em> scope before executing. If the result is a function that belongs to an
219   - object, executing separately will unbind the function from the object it was connected to (the <code>this</code> reference will be wrong). In that
220   - case, you should use <code>(method object "methodname")</code> to get a reference to the function that will execute against the provided object.</p>
221   -
222   - <p>While <em>Javathcript</em> functions turn into normal javascript functions, they are bound to the scope that they execute in, and won't work if
223   - disconnected from that scope. If you want to call them from, e.g. event handlers, then you can use the <code>export</code> function to create
224   - a function with the scope boiled in.</p>
225   -
226   - <code>document body window getElement alert message confirm</code>
227   -
228   - <p>These functions are defined in the <a href="lisp/prelude.lsp">prelude.lsp</a> file, and provide you references to the document and the window. <code>getElement</code> is
229   - the same as javascript <code>getElementById</code>, and message is a synonym for <code>alert</code>.</p>
230   -
231   - <script type="text/javascript">
232   - insertExamples([
233   - "(if (confirm \"Self destruct?\")\n\t(message \"Actually, I don't think I will.\")\n\t(message \"Fair enough.\")\n)",
234   - "(get body offsetWidth)",
235   - "; Make sure your window isn't maximised if you want this to work\n\t(let\t((moveBy (method window \"moveBy\")))\n\t\t(moveBy (- (* (random) 10) 5) (- (* (random) 10) 5) ))"
236   - ]);
237   - </script>
238   -
239   - <a name="have-a-go"><h2>Have a go</h2></a>
240   -
241   - <textarea id='code_entry'>(concat "PI is " PI)</textarea>
242   - <pre id='code_result' class='result'></pre>
243   - <button id='eval' onclick="evaluateElement('code_entry', 'code_result')">Evaluate</button>
244   -
245   - <a name="getting-source"><h2>Getting the Source</h2></a>
246   -
247   - <p>The project is hosted on github <a href="https://github.com/kybernetikos/Javathcript">here</a>. Depending on your setup, the following
248   - commands should get you a local copy of the code.
249   -
250   -<pre> git clone git@github.com:kybernetikos/Javathcript.git
251   - cd Javathcript/
252   - git submodule init
253   - git submodule update</pre>
254   -
255   - <a name="limitations"><h2>Limitations</h2></a>
256   -
257   - <p>This implementation does not have any tail recursion optimisations or macros. I haven't spent any time making it fast, so it is unlikely to be
258   - suitable for performance sensitive applications.</p>
259   -
260   - <a name="acknowledgements"><h2>Acknowledgements</h2></a>
261   -
262   - <p>Parsing is done with parsing code that I ported from Java. The original java is the code that accompanies the book
263   - <a href="http://oozinoz.xp123.com/bpwj.htm">Building Parsers With Java</a> and is copyright Steven J. Metsker.</p>
264   -
265   - <p>The lisp engine was created by me, based on
266   - <a href="http://scholar.google.co.uk/scholar?cluster=8012033452170110322&hl=en&as_sdt=2000">A Micro-Manual for Lisp - Not the whole truth</a>
267   - by John McCarthy.</p>
268   -
269   - <p>Although <em>Javathcript</em> doesn't take code from other projects, running lisp variants in the browser is not a new thing. It's been done
270   - before by at least:
271   - <ul>
272   - <li>Douglas Crockford's <a href="http://www.crockford.com/javascript/scheme.html">little scheme</a>.</li>
273   - <li>Steve Lacey's <a href="http://stevela.github.com/js-lisp/">js-lisp</a>.</li>
274   - <li>Joe Ganley's <a href="http://joeganley.com/code/jslisp.html">jslisp</a>.</li>
275   - <li>Brian Morearty's <a href="http://www.ducklet.com/jisp/">jisp</a>.</li>
276   - <li>Marc Belmot's <a href="http://marcbelmont.com/projects/lisp-interpreter-javascript-jquery/">lisp interpreter</a>.</li>
277   - <li>Paul Park's <a href="http://www.parkscomputing.com/lisptest.html">lisp test</a>.</li>
278   - </ul>
279   -
280   - <a name="contact"><h2>Contact</h2></a>
281   -
282   - <p>You can contact me by email at <div id='mail'></div></p>
283   -</body>
284   -
285   -</html>
  1 +<html><body>Hello</body></html>
12 source/Atom.js
... ... @@ -0,0 +1,12 @@
  1 +function Atom(s) {
  2 + this.CLASS = "Atom";
  3 + this.name = s;
  4 +}
  5 +
  6 +Atom.prototype.equals = function equals(a) {
  7 + return (a instanceof Atom && a.name == this.name);
  8 +};
  9 +
  10 +Atom.prototype.toString = function toString() {
  11 + return this.name;
  12 +};
56 source/BPWJs.js
... ... @@ -0,0 +1,56 @@
  1 +/*
  2 + * This is a parsing framework and a few Utility classes.
  3 + *
  4 + * Significant portions of this code were ported by kybernetikos from Java code.
  5 + *
  6 + * The Java code is (c) Steven John Metsker and was companion to his book Building Parsers With Java.
  7 + * http://oozinoz.xp123.com/bpwj.htm In that book, he gave permission for the code to be used as
  8 + * the reader wished, as long as they do not claim that they wrote it.
  9 + *
  10 + */
  11 +var Util=function(){function Util(){}Util.prototype.extend=function(subclass,superclass){var intermediate=function(){};intermediate.prototype=superclass.prototype;subclass.prototype=new intermediate};Util.prototype.bind=function(object,func){if(typeof func=="string")func=object[func];if(typeof func!="function")throw new Error("Invalid function passed to bind");return function(){func.apply(object,arguments)}};Util.prototype.getFile=function getFile(url,successHandler,failureHandler,timeout,timeoutHandler){var req=
  12 +new XMLHttpRequest;req.open("GET",url,true);req.onreadystatechange=function(aEvt){if(req.readyState==4)if(req.status==200||url.substring(0,5)=="file:"&&req.status==0)successHandler(req.responseText,req.getAllResponseHeaders());else if(failureHandler!=null)failureHandler(url,req.status)};req.send(null);if(timeout!=null&&timeout>0)setTimeout(function(){req.abort();if(timeoutHandler!=null)timeoutHandler(url,timeout)},timeout)};Util.prototype.lazy=function(initialise){var value=null;return function(){if(value==
  13 +null)value=initialise();return value}};Util.prototype.startsWith=function startsWith(sPrefix,sString){if(sPrefix==null||sString==null)return false;if(sPrefix=="")return true;if(sString.length<sPrefix.length)return false;return sString.substring(0,sPrefix.length)==sPrefix};Util.prototype.clamp=function clamp(value,min,max){return Math.min(Math.max(value,min),max)};Util.prototype.random=function random(arrayLength){return Math.floor(Math.random()*arrayLength)};Util.prototype.store=function store(name,
  14 +data){try{localStorage[name]=JSON.stringify(data)}catch(e){return false}return true};Util.prototype.retrieve=function retreive(name){var stored=localStorage[name];if(stored==null)return null;return JSON.parse(stored)};Util.prototype.clear=function clear(name){delete localStorage[name]};Util.prototype.toSet=function toSet(string){var items=string.split(" ");var result={};for(var i=0;i<items.length;++i)result[items[i]]=true;return result};return new Util}();var Arrays=function(){function Arrays(){}Arrays.prototype.remove=function(array,value){var index=this.indexOf(array,value);if(index>=0)array.splice(index,1)};Arrays.prototype.indexOf=function(array,value){for(var i=0;i<array.length;++i)if(array[i]==value)return i;return-1};Arrays.prototype.contains=function(array,value){for(var i=0;i<array.length;++i)if(array[i]==value)return true;return false};Arrays.prototype.each=function(array,func){for(var i=0;i<array.length;++i)func(array[i])};Arrays.prototype.map=
  15 +function(array,func){var result=new Array(array.length);for(var i=0;i<array.length;++i)result[i]=func(array[i])};return new Arrays}();function PushbackReader(string){this.string=string;this.index=0}PushbackReader.prototype.read=function read(){if(this.index<this.string.length){var c=this.string.charAt(this.index++);return c}return null};PushbackReader.prototype.unread=function unread(chr){if(chr==null)return;if(this.string.charAt(--this.index)!=chr)throw new Error("Unread char "+chr+" but was actually "+this.string.charAt(this.index));};function Assembly(){this.CLASS="Assembly";this.stack=[];this.target=null;this.index=0}Assembly.prototype.clone=function clone(a){if(a==null)throw new Error("a must be an instance of the class you want to clone");a.stack=this.stack.slice();if(this.target!=null)a.target=this.target.clone();a.index=this.index;return a};Assembly.prototype.consumed=function consumed(delimiter){throw new Error("Not Implemented: Assembly.consumed(String)");};
  16 +Assembly.prototype.defaultDelimiter=function defaultDelimiter(){throw new Error("Not Implemented: Assembly.defaultDelimiter()");};Assembly.prototype.elementsConsumed=function elementsConsumed(){return this.index};Assembly.prototype.elementsRemaining=function elementsRemaining(){return this.length()-this.elementsConsumed()};Assembly.prototype.getStack=function getStack(){return this.stack};Assembly.prototype.getTarget=function getTarget(){return this.target};
  17 +Assembly.prototype.hasMoreElements=function hasMoreElements(){return this.elementsConsumed()<this.length()};Assembly.prototype.nextElement=function nextElement(){throw new Error("Not Implemented: Assembly.nextElement()");};Assembly.prototype.length=function length(){throw new Error("Not Implemented: Assembly.length()");};Assembly.prototype.peek=function peek(){throw new Error("Not Implemented: Assembly.peek()");};Assembly.prototype.pop=function pop(){return this.stack.shift()};
  18 +Assembly.prototype.push=function push(o){this.stack.unshift(o)};Assembly.prototype.remainder=function remainder(delimiter){throw new Error("Not Implemented: Assembly.remainder");};Assembly.prototype.setTarget=function setTarget(target){this.target=target};Assembly.prototype.stackIsEmpty=function stackIsEmpty(){return this.stack.length==0};
  19 +Assembly.prototype.toString=function toString(){var delimiter=this.defaultDelimiter();var stack=this.stack.slice().reverse();return"["+stack.join(",")+"]"+this.consumed(delimiter)+"^"+this.remainder(delimiter)};Assembly.prototype.unget=function unget(n){this.index-=n;if(this.index<0)this.index=0};function Parser(name){this.name=name;this.assembler=null}Parser.prototype.accept=function(pv,visited){throw new Error("Not implemented: Parser.acceptVisited [accept(ParserVisitor, Vector)]");};Parser.add=function add(v1,v2){for(var i=0;i<v2.length;++i)v1.push(v2[i])};Parser.prototype.best=function best(v){var best=null;for(var i=0;i<v.length;++i){var a=v[i];if(!a.hasMoreElements())return a;if(best==null)best=a;else if(a.elementsConsumed()>best.elementsConsumed())best=a}return best};
  20 +Parser.prototype.bestMatch=function bestMatch(a){var inArr=[a];var out=this.matchAndAssemble(inArr);return this.best(out)};Parser.prototype.completeMatch=function completeMatch(a){var best=this.bestMatch(a);if(best!=null&&!best.hasMoreElements())return best;return null};Parser.elementClone=function elementClone(v){var copy=[];for(var i=0;i<v.length;++i){var a=v[i];copy.push(a.clone())}return copy};Parser.prototype.getName=function getName(){return this.name};
  21 +Parser.prototype.match=function match(inArr){throw new Error("Not Implemented: Parser.match(Vector inArr)");};Parser.prototype.matchAndAssemble=function matchAndAssemble(inArr){var out=this.match(inArr);if(this.assembler!=null)for(var i=0;i<out.length;++i)this.assembler.workOn(out[i]);return out};Parser.prototype.randomExpansion=function randomExpansion(maxDepth,depth){throw new Error("Not Implemented: Parser.randomExpansion(int, int)");};
  22 +Parser.prototype.randomInput=function randomInput(maxDepth,separator){var buf="";var e=this.randomExpansion(maxDepth,0);return e.join(separator)};Parser.prototype.setAssembler=function(assembler){this.assembler=assembler;return this};Parser.prototype.toString=function toString(visited){if(visited==null)visited=[];if(this.name!=null)return this.name;else if(Arrays.contains(visited,this))return"...";else{visited.push(this);return this.unvisitedString(visited)}};
  23 +Parser.prototype.unvisitedString=function unvisitedString(visited){throw new Error("Unimplemented: Parser.unvisistedString(Vector)");};Parser.prototype.or=function or(altParser){return new Alternation(this,altParser)};Parser.prototype.repeating=function repeating(){return new Repetition(this)};Parser.prototype.then=function then(nextParser){return new Sequence(this,nextParser)};
  24 +Parser.prototype.matcher=function matcher(assemblyType){var p=this;function match(str){var as=str;if(str instanceof Assembly==false)as=new assemblyType(str);return p.completeMatch(as)!=null}return{match:match,toString:function(){return"Matcher("+p.toString()+")"}}};function Repetition(subparser,name){if(subparser instanceof Parser==false)throw new Error("Subparser for Repetition must be a parser.");Parser.call(this,name);this.CLASS="Repetition";this.subparser=subparser;this.EXPWIDTH=4;this.preAssembler=null}Util.extend(Repetition,Parser);Repetition.prototype.accept=function accept(pv,visited){pv.visitRepetition(this,visited)};Repetition.prototype.getSubparser=function getSubparser(){return this.subparser};
  25 +Repetition.prototype.match=function(inArr){if(this.preAssembler!=null)for(var i=0;i<inArr.length;++i)this.preAssembler.workOn(inArr[i]);var out=Parser.elementClone(inArr);var s=inArr;while(s.length>0){if(this.subparser.matchAndAssemble==null)console.log(">>> "+this.subparser.CLASS);s=this.subparser.matchAndAssemble(s);Parser.add(out,s)}return out};
  26 +Repetition.prototype.randomExpansion=function randomExpansion(maxDepth,depth){v=[];if(depth>=maxDepth)return v;var n=Math.floor(this.EXPWIDTH*Math.random());for(var j=0;j<n;j++){var w=this.subparser.randomExpansion(maxDepth,depth++);for(var i=0;i<w.length;i++)v.push(w[i])}return v};Repetition.prototype.setPreAssembler=function setPreAssembler(preAssembler){this.preAssembler=preAssembler;return this};
  27 +Repetition.prototype.unvisitedString=function unvisitedString(visited){return this.subparser.toString(visited)+"*"};function Terminal(name){Parser.call(this,name);this._discard=false}Util.extend(Terminal,Parser);Terminal.prototype.discard=function discard(){return this.setDiscard(true)};Terminal.prototype.match=function match(inArr){var out=[];for(var i=0;i<inArr.length;++i){var a=inArr[i];var b=this.matchOneAssembly(a);if(b!=null)out.push(b)}return out};
  28 +Terminal.prototype.matchOneAssembly=function matchOneAssembly(inAs){if(!inAs.hasMoreElements())return null;var peek=inAs.peek();if(this.qualifies(peek)){var out=inAs.clone();var o=out.nextElement();if(!this._discard)out.push(o);return out}return null};Terminal.prototype.qualifies=function qualifies(o){return true};Terminal.prototype.randomExpansion=function randomExpansion(maxDepth,depth){var v=[];v.push(this.toString());return v};
  29 +Terminal.prototype.setDiscard=function setDiscard(discard){this._discard=discard;return this};Terminal.prototype.unvisitedString=function unvisitedString(visited){return"any"};function CollectionParser(name){if(typeof name=="string"){Parser.call(this,name);this.subparsers=[]}else{Parser.call(this);this.subparsers=[].slice.call(arguments)}this.CLASS="CollectionParser"}Util.extend(CollectionParser,Parser);CollectionParser.prototype.add=function add(e){this.subparsers.push(e);return this};CollectionParser.prototype.getSubparsers=function getSubparsers(){return this.subparsers};
  30 +CollectionParser.prototype.toStringSeparator=function toStringSeparator(){throw new Error("Not Implemented: CollectionParser.toStringSeparator");};CollectionParser.prototype.unvisitedString=function unvisitedString(visited){var buf=["<"];for(var i=0;i<this.subparsers.length;++i)buf.push(this.subparsers[i].toString(visited));buf.push(">");return buf.join(this.toStringSeparator())};function Alternation(){CollectionParser.apply(this,arguments);this.CLASS="Alternation"}Util.extend(Alternation,CollectionParser);Alternation.prototype.accept=function accept(pv,visited){pv.visitAlternation(this,visited)};Alternation.prototype.match=function match(inArr){var out=[];for(var i=0;i<this.subparsers.length;++i)Parser.add(out,this.subparsers[i].matchAndAssemble(inArr));return out};
  31 +Alternation.prototype.randomExpansion=function randomExpansion(maxDepth,depth){if(depth>=maxDepth)return this.randomSettle(maxDepth,depth);var n=this.subparsers.length;var i=Math.floor(n*Math.random());var j=this.subparsers[i];return j.randomExpansion(maxDepth,depth++)};
  32 +Alternation.prototype.randomSettle=function randomSettle(maxDepth,depth){var terms=[];for(var i=0;i<this.subparsers.length;++i){var j=this.subparsers[i];if(j instanceof Terminal)terms.push(j)}var which=terms;if(terms.length==0)which=this.subparsers;var n=which.length;var i=Math.floor(n*Math.random());var p=which[i];return p.randomExpansion(maxDepth,depth++)};Alternation.prototype.toStringSeparator=function toStringSeparator(){return"|"};Alternation.prototype.or=function or(altParser){return this.add(altParser)};
  33 +Alternation.prototype.either=Alternation.prototype.or;function Sequence(){CollectionParser.apply(this,arguments);this.CLASS="Sequence"}Util.extend(Sequence,CollectionParser);Sequence.prototype.accept=function accept(pv,visited){pv.visitSequence(this,visited)};Sequence.prototype.match=function match(inArr){var out=inArr;for(var i=0;i<this.subparsers.length;++i){var p=this.subparsers[i];if(p==null)console.log(this.subparsers);out=p.matchAndAssemble(out);if(out.length==0)return out}return out};
  34 +Sequence.prototype.randomExpansion=function randomExpansion(maxDepth,depth){var v=[];for(var i=0;i<this.subparsers.length;++i){var p=this.subparsers[i];var w=p.randomExpansion(maxDepth,depth++);Parser.add(v,w)}return v};Sequence.prototype.toStringSeparator=function toStringSeparator(){return""};Sequence.prototype.then=function(childParser){if(childParser instanceof Sequence){for(var i=0;i<childParser.subparsers.length;++i)this.subparsers.push(childParser.subparsers[i]);return this}return this.add(childParser)};
  35 +Sequence.prototype.first=function(child){if(this.subparsers.length==0)return this.add(child);else throw new Error("There is already a parser in this sequence; could nopt add "+child);};function Track(){Sequence.apply(this,arguments)}Util.extend(Track,Sequence);Track.prototype.match=function match(inArr){var inTrack=false;var last=inArr;var out=inArr;for(var i=0;i<this.subparsers.length;++i){var p=this.subparsers[i];out=p.matchAndAssemble(last);if(out.length==0){if(inTrack)this.throwTrackException(last,p);return out}inTrack=true;last=out}return out};
  36 +Track.prototype.throwTrackException=function throwTrackException(previousState,p){var best=this.best(previousState);var after=best.consumed(" ");if(after=="")after="-nothing-";var expected=p.toString();var next=best.peek();var found=next==null?"-nothing-":next.toString();throw new Error("After: "+after+"\n expected: "+expected+"\n found: "+found);};function Empty(){Parser.call(this)}Util.extend(Empty,Parser);Empty.prototype.accept=function accept(pv,visited){pv.visitEmpty(this,visited)};Empty.prototype.match=function match(inArr){return Parser.elementClone(inArr)};Empty.prototype.randomExpansion=function randomExpansion(maxDepth,depth){return[]};Empty.prototype.unvisitedString=function unvisitedString(visited){return" empty "};function Assembler(workFunc){if(workFunc!=null)this.workOn=workFunc}Assembler.unary=function unary(workFunc){return new Assembler(function(a){var val=a.pop();a.push(workFunc(val))})};Assembler.binary=function binary(workFunc){return new Assembler(function(a){var val2=a.pop();var val1=a.pop();a.push(workFunc(val1,val2))})};Assembler.elementsAbove=function elementsAbove(a,fence){var items=[];while(!a.stackIsEmpty()){var top=a.pop();if(top==fence||top.equals&&top.equals(fence))break;items.push(top)}return items};
  37 +Assembler.prototype.workOn=function workOn(a){throw new Error("Not Implemented: Assembler.workOn");};function grammarRule(type,initialise,assembler){var val;if(typeof type=="function")val=new type;else val=type;var initialised=false;return function(){if(initialised==false){initialised=true;initialise(val);if(assembler!=null)val.setAssembler(assembler)}return val}};function Num(){Terminal.call(this)}Util.extend(Num,Terminal);Num.prototype.qualifies=function qualifies(o){var t=o;return t.isNumber()};Num.prototype.randomExpansion=function randomExpansion(maxDepth,depth){var d=Math.floor(1E3*Math.random())/10;var v=[];v.push(new String(d));return v};Num.prototype.unvisitedString=function unvisitedString(visited){return"Num"};function NumberState(){this.CLASS="NumberState";this.c="";this._value=0;this.absorbedLeadingMinus=false;this.absorbedDot=false;this.gotAdigit=false}NumberState.prototype.absorbDigits=function absorbDigits(r,fraction){var divideBy=1;var v=0;while(this.c!=null&&"0"<=this.c&&this.c<="9"){this.gotAdigit=true;v=v*10+parseInt(this.c,10);this.c=r.read();if(fraction)divideBy*=10}if(fraction)v=v/divideBy;return v};
  38 +NumberState.prototype.nextToken=function nextToken(r,cin,t){this.reset(cin);this.parseLeft(r);this.parseRight(r);r.unread(this.c);return this.value(r,t)};NumberState.prototype.parseLeft=function parseLeft(r){if(this.c=="-"){this.c=r.read();this.absorbedLeadingMinus=true}this._value=this.absorbDigits(r,false)};NumberState.prototype.parseRight=function parseRight(r){if(this.c=="."){this.c=r.read();this.absorbedDot=true;this._value+=this.absorbDigits(r,true)}};
  39 +NumberState.prototype.reset=function reset(cin){this.c=cin;this._value=0;this.absorbedLeadingMinus=false;this.absorbedDot=false;this.gotAdigit=false};
  40 +NumberState.prototype.value=function value(r,t){if(!this.gotAdigit){if(this.absorbedLeadingMinus&&this.absorbedDot){r.unread(".");return t.symbolState.nextToken(r,"-",t)}if(this.absorbedLeadingMinus)return t.symbolState.nextToken(r,"-",t);if(this.absorbedDot)return t.symbolState.nextToken(r,".",t)}if(this.absorbedLeadingMinus)this._value=-this._value;return new Token(Token.TT_NUMBER,"",this._value)};NumberState.prototype.toString=function toString(){return"{NumberState}"};function QuoteState(){this.CLASS="QuoteState"}QuoteState.prototype.nextToken=function nextToken(r,cin,t){var c=cin;var sval=c;do{c=r.read();if(c==null)c=cin;sval+=c}while(c!=cin);return new Token(Token.TT_QUOTED,sval,0)};QuoteState.prototype.toString=function toString(){return"{QuoteState}"};function SlashSlashState(){this.CLASS="SlashSlashState"}SlashSlashState.prototype.nextToken=function nextToken(r,theSlash,t){var c="";while((c=r.read())!="\n"&&c!="\r"&&c!=null);return t.nextToken()};SlashSlashState.prototype.toString=function toString(){return"{SlashSlashState}"};function SlashStarState(){this.CLASS="SlashStarState"}SlashStarState.prototype.nextToken=function nextToken(r,theStar,t){var c="";var lastc="";while(c!=null){if(lastc=="*"&&c=="/")break;lastc=c;c=r.read()}return t.nextToken()};SlashStarState.prototype.toString=function toString(){return"{SlashStarState}"};function SlashState(){this.slashStarState=new SlashStarState;this.slashSlashState=new SlashSlashState}SlashState.prototype.nextToken=function nextToken(r,theSlash,t){var c=r.read();if(c=="*")return this.slashStarState.nextToken(r,"*",t);if(c=="/")return this.slashSlashState.nextToken(r,"/",t);if(c!=null)r.unread(c);return new Token(Token.TT_SYMBOL,"/",0)};SlashState.prototype.toString=function toString(){return"{SlashState}"};function SymbolNode(parent,myChar){this.myChar=myChar;this.children=[];this.valid=false;this.parent=parent}SymbolNode.prototype.addDescendantLine=function addDescendantLine(s){if(s.length>0){var c=s.charAt(0);var n=this.ensureChildWithChar(c);n.addDescendantLine(s.substring(1))}};SymbolNode.prototype.ancestry=function ancestry(){return this.parent.ancestry()+this.myChar};SymbolNode.prototype.deepestRead=function(r){var c=r.read();var n=this.findChildWithChar(c);if(n==null){r.unread(c);return this}return n.deepestRead(r)};
  41 +SymbolNode.prototype.ensureChildWithChar=function ensureChildWithChar(c){var n=this.findChildWithChar(c);if(n==null){n=new SymbolNode(this,c);this.children.push(n)}return n};SymbolNode.prototype.findChildWithChar=function findChildWithChar(c){for(var i=0;i<this.children.length;++i){var n=this.children[i];if(n.myChar==c)return n}return null};SymbolNode.prototype.findDescendant=function findDescendant(s){var c=s.substring(0,1);var n=this.findChildWithChar(c);if(s.length==1)return n;return n.findDescendant(s.substring(1))};
  42 +SymbolNode.prototype.setValid=function setValid(b){this.valid=b};SymbolNode.prototype.toString=function toString(){return""+this.myChar+"("+this.valid+")"};SymbolNode.prototype.unreadToValid=function unreadToValid(r){if(this.valid)return this;r.unread(this.myChar);return this.parent.unreadToValid(r)};function SymbolRootNode(){SymbolNode.call(null,"");this.table={};this.init()}Util.extend(SymbolRootNode,SymbolNode);SymbolRootNode.prototype.add=function add(s){var c=s.substring(0,1);var n=this.ensureChildWithChar(c);n.addDescendantLine(s.substring(1));this.findDescendant(s).setValid(true)};SymbolRootNode.prototype.ancestry=function ancestry(){return""};SymbolRootNode.prototype.findChildWithChar=function findChildWithChar(c){return this.table[c]};
  43 +SymbolRootNode.prototype.init=function init(){for(var i=0;i<256;++i){var c=String.fromCharCode(i);this.table[c]=new SymbolNode(this,c);this.table[c].setValid(true)}};SymbolRootNode.prototype.nextSymbol=function nextSymbol(r,first){var n1=this.findChildWithChar(first);var n2=n1.deepestRead(r);var n3=n2.unreadToValid(r);return n3.ancestry()};function SymbolState(){this.CLASS="SymbolState";this.symbols=new SymbolRootNode;this.add("!=");this.add(":-");this.add("<=");this.add(">=")}SymbolState.prototype.add=function add(s){this.symbols.add(s)};SymbolState.prototype.nextToken=function nextToken(r,first,t){var s=this.symbols.nextSymbol(r,first);return new Token(Token.TT_SYMBOL,s,0)};SymbolState.prototype.toString=function toString(){return"{SymbolState}"};function Token(ttype,sval,nval){if(arguments.length==1){var type=typeof ttype;if(type=="number"){this.nval=ttype;this.sval="";this.ttype=Token.TT_NUMBER}else if(type=="string"){this.nval=0;this.sval=ttype;if(ttype.length==1)this.ttype=Token.TT_SYMBOL;else this.ttype=Token.TT_WORD}}else{this.ttype=ttype;this.sval=sval;this.nval=nval}}Token.TT_EOF="eof";Token.EOF=new Token(Token.TT_EOF,"",0);Token.TT_NUMBER="number";Token.TT_WORD="word";Token.TT_SYMBOL="symbol";Token.TT_QUOTED="quoted";
  44 +Token.prototype.equals=function equals(t){if(!(t instanceof Token))return false;if(this.ttype!=t.ttype)return false;if(this.ttype==Token.TT_NUMBER)return this.nval==t.nval;return this.sval==t.sval};Token.prototype.equalsIgnoreCase=function equalsIgnoreCase(t){if(!(t instanceof Token))return false;if(this.ttype!=t.ttype)return false;if(this.ttype==Token.TT_NUMBER)return this.nval==t.nval;if(this.sval==null||t.sval==null)return false;return this.sval.toLowerCase()==t.sval.toLowerCase()};
  45 +Token.prototype.isNumber=function isNumber(){return this.ttype==Token.TT_NUMBER};Token.prototype.isQuotedString=function isQuotedString(){return this.ttype==Token.TT_QUOTED};Token.prototype.isSymbol=function isSymbol(){return this.ttype==Token.TT_SYMBOL};Token.prototype.isWord=function isWord(){return this.ttype==Token.TT_WORD};Token.prototype.toString=function toString(){if(this.ttype==Token.TT_EOF)return"EOF";return this.value().toString()};
  46 +Token.prototype.value=function value(){if(this.ttype==Token.TT_NUMBER)return this.nval;if(this.ttype==Token.TT_EOF)return Token.EOF;if(this.sval!=null)return this.sval;return this.ttype};function TokenAssembly(tokenString){Assembly.call(this);this.CLASSNAME="TokenAssembly";if(tokenString instanceof TokenString==false)tokenString=new TokenString(tokenString);this.tokenString=tokenString;this.start=0}Util.extend(TokenAssembly,Assembly);TokenAssembly.prototype.consumed=function consumed(delimiter){var result=[];for(var i=this.start;i<this.start+this.index;++i)result.push(this.tokenString.tokenAt(i));return result.join(delimiter)};
  47 +TokenAssembly.prototype.commit=function commit(){this.start=this.start+this.index;this.index=0;this.stack=[]};TokenAssembly.prototype.defaultDelimiter=function defaultDelimiter(){return"/"};TokenAssembly.prototype.length=function length(){return this.tokenString.length()};TokenAssembly.prototype.nextElement=function nextElement(){var oldIndex=this.index++;return this.tokenString.tokenAt(this.start+oldIndex)};
  48 +TokenAssembly.prototype.peek=function peek(){if(this.start+this.index<this.length())return this.tokenString.tokenAt(this.start+this.index);else return null};TokenAssembly.prototype.remainder=function remainder(delimiter){var buf=[];for(var i=this.start+this.elementsConsumed();i<this.tokenString.length();i++)buf.push(this.tokenString.tokenAt(i));return buf.join(delimiter)};TokenAssembly.prototype.hasMoreElements=function hasMoreElements(){return this.start+this.index<this.length()};
  49 +TokenAssembly.prototype.clone=function clone(){var tokenAssembly=new TokenAssembly(this.tokenString);tokenAssembly.start=this.start;return Assembly.prototype.clone.call(this,tokenAssembly)};function Tokenizer(s){this.reader=null;this.characterState={};this.numberState=new NumberState;this.quoteState=new QuoteState;this.slashState=new SlashState;this.symbolState=new SymbolState;this.whitespaceState=new WhitespaceState;this.wordState=new WordState;this.initialCharacterState();if(s!=null)this.setString(s)}
  50 +Tokenizer.prototype.initialCharacterState=function(){console.log("Tokenizer initialChar");this.setCharacterState(0,255,this.symbolState);this.setCharacterState(0," ",this.whitespaceState);this.setCharacterState("a","z",this.wordState);this.setCharacterState("A","Z",this.wordState);this.setCharacterState(192,255,this.wordState);this.setCharacterState("0","9",this.numberState);this.setCharacterState("-","-",this.numberState);this.setCharacterState(".",".",this.numberState);this.setCharacterState('"',
  51 +'"',this.quoteState);this.setCharacterState("'","'",this.quoteState);this.setCharacterState("/","/",this.slashState)};Tokenizer.prototype.getReader=function getReader(){return this.reader};Tokenizer.prototype.nextToken=function nextToken(){var c=this.reader.read();if(c!=null){var result=this.characterState[c].nextToken(this.reader,c,this);return result}return Token.EOF};
  52 +Tokenizer.prototype.setCharacterState=function setCharacterState(from,to,state){if(state==null){var str=from;state=to;for(var i=0;i<str.length;i++)this.characterState[str.substring(i,i+1)]=state}else{var fromCode=from;if(fromCode.charCodeAt)fromCode=fromCode.charCodeAt(0);var toCode=to;if(toCode.charCodeAt)toCode=toCode.charCodeAt(0);for(var i=fromCode;i<=toCode;i++)this.characterState[String.fromCharCode(i)]=state}};Tokenizer.prototype.setReader=function setReader(r){this.reader=r};
  53 +Tokenizer.prototype.setString=function setString(s){this.setReader(new PushbackReader(s))};function TokenString(tokens){if(typeof tokens=="string")tokens=new Tokenizer(tokens);if(tokens instanceof Tokenizer){t=tokens;tokens=[];while(true){var tok=t.nextToken();if(tok.ttype==Token.TT_EOF)break;tokens.push(tok)}}this.tokens=tokens}TokenString.prototype.length=function length(){return this.tokens.length};TokenString.prototype.tokenAt=function tokenAt(i){return this.tokens[i]};TokenString.prototype.toString=function toString(){return this.tokens.join(" ")};function WhitespaceState(){this.CLASS="WhitespaceState";this.whitespaceChar={};this.setWhitespaceChars(String.fromCharCode(0)," ",true)}WhitespaceState.prototype.nextToken=function nextToken(r,aWhitespaceChar,t){var c;do c=r.read();while(c!=null&&this.whitespaceChar[c]===true);if(c!=null)r.unread(c);return t.nextToken()};
  54 +WhitespaceState.prototype.setWhitespaceChars=function setWhitespaceChars(from,to,b){for(var i=from.charCodeAt(0);i<=to.charCodeAt(0);i++)this.whitespaceChar[String.fromCharCode(i)]=b};WhitespaceState.prototype.toString=function toString(){return"{WhitespaceState}"};function WordState(){this.CLASS="WordState";this.wordChars={};this.setWordChars("a","z",true);this.setWordChars("A","Z",true);this.setWordChars("0","9",true);this.setWordChars("-","-",true);this.setWordChars("_","_",true);this.setWordChars("'","'",true);this.setWordChars(String.fromCharCode(192),String.fromCharCode(255),true)}
  55 +WordState.prototype.nextToken=function nextToken(r,c,t){var sval="";do{sval+=c;c=r.read()}while(this.wordChars[c]===true);if(c!=null)r.unread(c);return new Token(Token.TT_WORD,sval,0)};
  56 +WordState.prototype.setWordChars=function setWordChars(from,to,b){if(b==null){var str=from;b=to;for(var i=0;i<str.length;i++)this.wordChars[str.substring(i,i+1)]=b}else{var fromCode=from;if(fromCode.charCodeAt)fromCode=fromCode.charCodeAt(0);var toCode=to;if(toCode.charCodeAt)toCode=toCode.charCodeAt(0);for(var i=fromCode;i<=toCode;i++)this.wordChars[String.fromCharCode(i)]=b}};WordState.prototype.toString=function toString(){return"{WordState}"};function Word(){Terminal.call(this);this.CLASSNAME="Word"}Util.extend(Word,Terminal);Word.prototype.qualifies=function qualifies(t){return t.isWord()};Word.prototype.randomExpansion=function randomExpansion(maxDepth,depth){var n=Math.floor(5*Math.random())+3;var letters="";for(var i=0;i<n;i++)letters+=String.fromCharCode(26*Math.random()+"a".charCodeAt(0));return[letters]};Word.prototype.unvisitedString=function unvisitedString(visited){return"Word"};function QuotedString(){Terminal.call(this)}Util.extend(QuotedString,Terminal);QuotedString.prototype.qualifies=function qualifies(t){return t.isQuotedString()};QuotedString.prototype.randomExpansion=function ramdomExpansion(maxDepth,depth){var n=Math.floor(5*Math.random());var result='"';for(var i=0;i<n;i++)result+=String.fromCharCode(26*Math.random()+"a".charCodeAt(0));result+='"';return[result]};QuotedString.prototype.unvisitedString=function unvisitedString(visited){return"QuotedString"};function Symbol(s){Terminal.call(this);this.CLASS="Symbol";this.symbol=new Token(Token.TT_SYMBOL,s,0)}Util.extend(Symbol,Terminal);Symbol.prototype.qualifies=function qualifies(o){return this.symbol.equals(o)};Symbol.prototype.unvisitedString=function unvisitedString(visited){return this.symbol.toString()};
68 source/DocumentEvaluator.js
... ... @@ -0,0 +1,68 @@
  1 +var WorkQueue = (function() {
  2 +
  3 + function WorkQueue() {
  4 + this.ready = true;
  5 + this.queue = [];
  6 + }
  7 +
  8 + WorkQueue.prototype.add = function add(job) {
  9 + this.queue.push(job);
  10 + if (this.ready) {
  11 + this.startWork();
  12 + }
  13 + };
  14 +
  15 + WorkQueue.prototype.startWork = function startWork() {
  16 + if (this.queue.length < 1 || this.ready == false) {
  17 + return;
  18 + }
  19 + this.ready = false;
  20 + var workQueue = this;
  21 + function done() {
  22 + workQueue.ready = true;
  23 + workQueue.startWork();
  24 + }
  25 + setTimeout(function() {
  26 + var job = workQueue.queue.shift();
  27 + job(done);
  28 + }, 0);
  29 + };
  30 +
  31 + return new WorkQueue();
  32 +})();
  33 +
  34 +function evalFileJob(src) {
  35 + return function(done) {
  36 + var req = new XMLHttpRequest();
  37 + req.open('GET', src, true);
  38 + req.onreadystatechange = function (aEvt) {
  39 + if (req.readyState == 4 && req.status == 200) {
  40 + Javathcript.evalMulti(req.responseText);
  41 + done();
  42 + }
  43 + };
  44 + req.send(null);
  45 + };
  46 +}
  47 +
  48 +function evalScriptTagJob(txt) {
  49 + return function(done) {
  50 + Javathcript.evalMulti(txt);
  51 + done();
  52 + };
  53 +};
  54 +
  55 +window.addEventListener("DOMContentLoaded", function() {
  56 + var scripts = document.getElementsByTagName("script");
  57 + for (var i = 0; i < scripts.length; ++i) {
  58 + var script = scripts[i];
  59 + if (script.getAttribute("type") == "text/lisp") {
  60 + var src = script.getAttribute("src");
  61 + if (src != null) {
  62 + WorkQueue.add(evalFileJob(src));
  63 + } else {
  64 + WorkQueue.add(evalScriptTagJob(script.text));
  65 + }
  66 + }
  67 + }
  68 +}, false);
405 source/Environment.js
... ... @@ -0,0 +1,405 @@
  1 +var Environment = (function() {
  2 +
  3 + var Nil = [];
  4 +
  5 + function buildStringRep(exp, result) {
  6 + if (result == null) {
  7 + result = [];
  8 + }
  9 + if (exp instanceof Array) {
  10 + result.push("(");
  11 + for (var i = 0; i < exp.length; ++i) {
  12 + buildStringRep(exp[i], result);
  13 + }
  14 + result.push(")");
  15 + } else {
  16 + result.push(exp);
  17 + }
  18 + return result;
  19 + }
  20 +
  21 + function stringify(exp) {
  22 + return buildStringRep(exp).join(" ");
  23 + }
  24 +
  25 + function bool(b) {
  26 + return b ? 't' : Nil;
  27 + }
  28 +
  29 + function equal(a, b) {
  30 + // three types, list, atom, string, number
  31 + if (a instanceof Atom) {
  32 + return b instanceof Atom && a.name == b.name;
  33 + } else if (b instanceof Atom) {
  34 + return false;
  35 + }
  36 +
  37 + if (a instanceof Array || b instanceof Array) {
  38 + if (a.length != b.length) {
  39 + return false;
  40 + }
  41 + for (var i = 0; i < a.length; ++i) {
  42 + if (equal(a[i], b[i]) == false) {
  43 + return false;
  44 + }
  45 + }
  46 + return true;
  47 + }
  48 +
  49 + return a == b;
  50 + }
  51 +
  52 + function newScope(oldScope) {
  53 + function constructor() {}
  54 + constructor.prototype = oldScope;
  55 + return new constructor();
  56 + }
  57 +
  58 + var globalScope = null;
  59 +
  60 + function Environment() {
  61 + globalScope = this;
  62 + }
  63 +
  64 + Environment.prototype["quote"] = function(a) {
  65 + return a;
  66 + };
  67 +
  68 + Environment.prototype["car"] = function(args) {
  69 + return this["_value"](args)[0];
  70 + };
  71 +
  72 +
  73 + Environment.prototype["cdr"] = function(args) {
  74 + var val = this["_value"](args).slice();
  75 + if (val.substring) {
  76 + return val.substring(1);
  77 + }
  78 + val.shift();
  79 + return val;
  80 + },
  81 +
  82 + Environment.prototype["equal"] = function(a, b) {
  83 + var a = this["_value"](a);
  84 + var b = this["_value"](b);
  85 + return equal(a, b) ? "t" : Nil;
  86 + };
  87 +
  88 + Environment.prototype["cons"] = function(a, b) {
  89 + var a = this["_value"](a);
  90 + var b = this["_value"](b);
  91 +
  92 + if (typeof(a) == 'string' && typeof(b) == 'string') {
  93 + return a+b;
  94 + }
  95 +
  96 + b = b.slice();
  97 + b.unshift(a);
  98 + return b;
  99 + };
  100 +
  101 + Environment.prototype["concat"] = function() {
  102 + var args = this["_valueArray"](arguments);
  103 + var result = args.shift();
  104 + if (result instanceof Array) {
  105 + return Array.prototype.concat.apply(result, args);
  106 + }
  107 + return result + args.join("");
  108 + };
  109 +
  110 + Environment.prototype["atom"] = function(a) {
  111 + return bool( this["_value"](a) instanceof Atom );
  112 + };
  113 +
  114 + Environment.prototype["cond"] = function() {
  115 + for (var i = 0; i < arguments.length; ++i) {
  116 + var condition = this["_value"](arguments[i][0]);
  117 + if (equal(condition, Nil) == false) {
  118 + return this["_value"](arguments[i][1]);
  119 + }
  120 + }
  121 + };
  122 +
  123 + Environment.prototype["lambda"] = function(variables, expression) {
  124 + var func = function() {
  125 + var funcScope = newScope(this);
  126 + for (var i = 0; i < variables.length; ++i) {
  127 + var varval = this["_value"](arguments[i]);
  128 + funcScope[variables[i]] = varval;
  129 + }
  130 + if (arguments.callee["label"] != null) {
  131 + funcScope[arguments.callee["label"]] = arguments.callee;
  132 + }
  133 + return funcScope._value(expression);
  134 + };
  135 + func.toString = function() {
  136 + return "\u03bb"+stringify(variables)+stringify(expression);
  137 + };
  138 + return func;
  139 + };
  140 +
  141 + Environment.prototype["export"] = function(lambda) {
  142 + var scope = this;
  143 + return function() {
  144 + return scope._value(lambda).apply(scope, arguments);
  145 + };
  146 + };
  147 +
  148 + Environment.prototype["label"] = function(l, f) {
  149 + var func = this["_value"](f);
  150 + func["label"] = l;
  151 + return func;
  152 + };
  153 +
  154 + function wrapJsResult(jsResult) {
  155 + if (typeof(jsResult) == 'boolean') {
  156 + if (jsResult) {
  157 + return "t";
  158 + } else {
  159 + return Nil;
  160 + }
  161 + }
  162 + return jsResult;
  163 + }
  164 +
  165 + Environment.prototype["js"] = function() {
  166 + var val = this["_value"](arguments[0]);
  167 + var result = eval(val);
  168 + if (typeof(result) == 'function') {
  169 + return function() {
  170 + var jsResult = result.apply(bind, this["_valueArray"](arguments));
  171 + return wrapJsResult(jsResult);
  172 + };
  173 + }
  174 + return result;
  175 + };
  176 +
  177 + Environment.prototype["method"] = function(obj, property) {
  178 + var val = this["_value"](obj);
  179 + property = this["_value"](property);
  180 + if (property instanceof Atom) {
  181 + property = property.name;
  182 + }
  183 + if (! val[property] || typeof(val[property]) != "function") {
  184 + throw new Error("Method "+property+" not found on object "+val);
  185 + }
  186 + return function() {
  187 + var args = this["_valueArray"](arguments);
  188 + var jsResult = val[property].apply(val, args);
  189 + return wrapJsResult(jsResult);
  190 + };
  191 + };
  192 +
  193 + Environment.prototype["let"] = function(bindings, expression) {
  194 + var oldScope = this;
  195 + var letScope = newScope(this);
  196 + for (var i = 0; i < bindings.length; ++i) {
  197 + var binding = bindings[i];
  198 + var name = binding[0];
  199 + var value = oldScope._value(binding[1]);
  200 + letScope[name] = value;
  201 + }
  202 + return letScope._value(expression);
  203 + };
  204 +
  205 + Environment.prototype["let*"] = function(bindings, expression) {
  206 + var letScope = newScope(this);
  207 + for (var i = 0; i < bindings.length; ++i) {
  208 + var binding = bindings[i];
  209 + var name = binding[0];
  210 + var value = letScope._value(binding[1]);
  211 + letScope[name] = value;
  212 + }
  213 + return letScope._value(expression);
  214 + };
  215 +
  216 + Environment.prototype["defun"] = function(name, variables, expression) {
  217 + var func = this["lambda"](variables, expression);
  218 + globalScope[name] = func;
  219 + return func;
  220 + };
  221 +
  222 + Environment.prototype["def"] = function(name, value) {
  223 + globalScope[name] = this["_value"](value);
  224 + return value;
  225 + };
  226 +
  227 + Environment.prototype["def-dyn"] = function(name, value) {
  228 + return this["def"](this["_value"](name), value);
  229 + };
  230 +
  231 + Environment.prototype["plus"] = function() {
  232 + var result = 0;
  233 + for (var i = 0; i < arguments.length; ++i) {
  234 + result += this["_value"](arguments[i]);
  235 + }
  236 + return result;
  237 + };
  238 +
  239 + Environment.prototype["minus"] = function() {
  240 + if (arguments.length == 1) {
  241 + return - this["_value"](arguments[0]);
  242 + }
  243 + var result = this["_value"](arguments[0]);
  244 + for (var i = 1; i < arguments.length; ++i) {
  245 + result -= this["_value"](arguments[i]);
  246 + }
  247 + return result;
  248 + };
  249 +
  250 + Environment.prototype["divide"] = function() {
  251 + var result = this["_value"](arguments[0]);
  252 + for (var i = 1; i < arguments.length; ++i) {
  253 + result = result / this["_value"](arguments[i]);
  254 + }
  255 + return result;
  256 + };
  257 +
  258 + Environment.prototype["times"] = function() {
  259 + var result = this["_value"](arguments[0]);
  260 + for (var i = 1; i < arguments.length; ++i) {
  261 + result *= this["_value"](arguments[i]);
  262 + }
  263 + return result;
  264 + };
  265 +
  266 + Environment.prototype["rem"] = function(a, b) {
  267 + return this["_value"](a) % this["_value"](b);
  268 + };
  269 +
  270 + Environment.prototype["if"] = function(conditional, truePath, falsePath) {
  271 + if (equal(this["_value"](conditional), Nil) == false) {
  272 + return this["_value"](truePath);
  273 + } else if (falsePath != null) {
  274 + return this["_value"](falsePath);
  275 + }
  276 + return Nil;
  277 + };
  278 +
  279 + Environment.prototype["<"] = function(a, b) {
  280 + return bool( this["_value"](a) < this["_value"](b) );
  281 + };
  282 +
  283 + Environment.prototype[">"] = function(a, b) {
  284 + return bool( this["_value"](a) > this["_value"](b) );
  285 + };
  286 +
  287 + Environment.prototype["/="] = function(a, b) {
  288 + return bool( equal(this["_value"](a), this["_value"](b)) == false );
  289 + };
  290 +
  291 + Environment.prototype[">="] = function(a, b) {
  292 + return bool( this["_value"](a) >= this["_value"](b) );
  293 + };
  294 +
  295 + Environment.prototype["<="] = function(a, b) {
  296 + return bool( this["_value"](a) <= this["_value"](b) );
  297 + };
  298 +
  299 + Environment.prototype["not"] = function(a) {
  300 + return bool( equal(this["_value"](a), Nil) );
  301 + };
  302 +
  303 + Environment.prototype["or"] = function() {
  304 + for (var i = 0; i < arguments.length; ++i) {
  305 + var val = this["_value"](arguments[i]);
  306 + if (equal(val, Nil) == false) {
  307 + return val;
  308 + }
  309 + }
  310 + return Nil;
  311 + };
  312 +
  313 + Environment.prototype["and"] = function() {
  314 + for (var i = 0; i < arguments.length; ++i) {
  315 + var val = this["_value"](arguments[i]);
  316 + if (equal(val, Nil)) {
  317 + return Nil;
  318 + }
  319 + }
  320 + return "t";
  321 + };
  322 +
  323 + Environment.prototype["nth"] = function(n, l) {
  324 + return this["_value"](l)[this["_value"](n)];
  325 + };
  326 +
  327 + Environment.prototype["consp"] = function(a) {
  328 + var val = this["_value"](a);
  329 + return bool( val instanceof Array && val.length > 0 );
  330 + };
  331 +
  332 + Environment.prototype["length"] = function(l) {
  333 + return this["_value"](l).length;
  334 + };
  335 +
  336 + Environment.prototype["list"] = function() {
  337 + return this["_valueArray"](arguments);
  338 + };
  339 +
  340 + Environment.prototype["get"] = function(obj, property) {
  341 + var val = this["_value"](obj);
  342 + if (property instanceof Atom) {
  343 + property = property.name;
  344 + }
  345 + return val[property];
  346 + };
  347 +
  348 + Environment.prototype["substring"] = function(string, start, end) {
  349 + return this["_value"](string).substring(this["_value"](start), this["_value"](end));
  350 + };
  351 +
  352 + Environment.prototype["set"] = function(obj, property, value) {
  353 + var obj = this["_value"](obj);
  354 + var val = this["_value"](value);
  355 + if (property instanceof Atom) {
  356 + property = property.name;
  357 + }
  358 + obj[property] = val;
  359 + return obj;
  360 + };
  361 +
  362 + Environment.prototype["t"] = "t";
  363 + Environment.prototype["Nil"] = Nil;
  364 +
  365 + Environment.prototype["_value"] = function(e) {
  366 + if (e instanceof UnevaluatedObj) {
  367 + var object = new Object();
  368 + for (var key in e) {
  369 + object[key] = this["_value"](e[key]);
  370 + }
  371 + return object;
  372 + } else if (e instanceof Array) {
  373 + var data = e.slice();
  374 + var head = data.shift();
  375 + var headFunc = this["_value"](head);
  376 + if (typeof(headFunc) == 'function') {
  377 + var result = headFunc.apply(this, data);
  378 + if (result == null) {
  379 + result = Nil;
  380 + }
  381 + return result;
  382 + }
  383 + throw new Error(head+" not a function in environment when trying to evaluate "+e);
  384 + } else if (e instanceof Atom) {
  385 + if (this[e.name] != null) {
  386 + return this[e.name];
  387 + }
  388 + return e;
  389 + } else {
  390 + return e;
  391 + }
  392 + };
  393 +
  394 + Environment.prototype["_valueArray"] = function(arr) {
  395 + var result = Array.prototype.slice.call(arr);
  396 + for (var i = 0; i < result.length; ++i) {
  397 + result[i] = this["_value"](result[i]);
  398 + }
  399 + return result;
  400 + };
  401 +
  402 + Environment.stringify = stringify;
  403 +
  404 + return Environment;
  405 +})();
31 source/Javathcript.js
... ... @@ -0,0 +1,31 @@
  1 +var Javathcript = (function() {
  2 +
  3 + var defaultEnvironment = new Environment();
  4 +
  5 + function eval(environment, s) {
  6 + var data = JavathcriptParser.parse(s);
  7 + var value = environment._value(data);
  8 + if (value instanceof Array) {
  9 + value.toString = function() {
  10 + return Environment.stringify(this);
  11 + };
  12 + }
  13 + return value;
  14 + }
  15 +
  16 + return {
  17 + eval: function(s) {
  18 + return eval(defaultEnvironment, s);
  19 + },
  20 + evalMulti: function(s) {
  21 + var tokA = new TokenAssembly(new JavathcriptTokenizer(s));
  22 + while (tokA.hasMoreElements()) {
  23 + tokA = JavathcriptParser.parsePartial(tokA);
  24 + defaultEnvironment._value(tokA.pop());
  25 + tokA.commit();
  26 + }
  27 + },
  28 + environment: defaultEnvironment
  29 + };
  30 +
  31 +})();
92 source/JavathcriptParser.js
... ... @@ -0,0 +1,92 @@
  1 +var JavathcriptParser = (function() {
  2 +
  3 + /*
  4 + * expression = atomOrListOrObject | quotedAtomOrList
  5 + * atomOrList = atom | list | object
  6 + * object = '{' nameValue* '}
  7 + * nameValue = atom ':' expression
  8 + * quotedAtomOrList = ''' atomOrList
  9 + * list = '(' expression* ')'
  10 + * atom = Word | Num | QuotedString
  11 + */
  12 +
  13 + var expression = grammarRule(Alternation, function(exp) {
  14 + exp.either(atomOrListOrObject()).or(quotedAtomOrList());
  15 + });
  16 +
  17 + var atomOrListOrObject = grammarRule(Alternation, function(atomOrListOrObject) {
  18 + atomOrListOrObject.either(atom()).or(list()).or(object());
  19 + });
  20 +
  21 + var objectStart = new Symbol("{").discard();
  22 + objectStart.setAssembler(new Assembler(function(a) {
  23 + a.push(new UnevaluatedObj());
  24 + }));
  25 +
  26 + var object = grammarRule(Track, function(object) {
  27 + object.first(objectStart).then(nameValue().repeating()).then(new Symbol("}").discard());
  28 + });
  29 +
  30 + var nameValue = grammarRule(Track, function(nameValue) {
  31 + nameValue.first(atom()).then(new Symbol(":").discard()).then(expression());
  32 + }, new Assembler(function(a) {
  33 + var exp = a.pop();
  34 + var name = a.pop();
  35 + var obj = a.pop();
  36 + obj[name] = exp;
  37 + a.push(obj);
  38 + }));
  39 +
  40 + var quotedAtomOrList = grammarRule(Sequence, function(quotedAtomOrList) {
  41 + quotedAtomOrList.first(new Symbol("'").discard()).then(atomOrListOrObject());
  42 + }, new Assembler(function(a) {
  43 + var val = a.pop();
  44 + a.push([new Atom("quote"), val]);
  45 + }));
  46 +
  47 + var openBrace = new Symbol('(');
  48 +
  49 + var list = grammarRule(Track, function(list) {
  50 + list.first(openBrace).then(expression().repeating()).then(new Symbol(')').discard());
  51 + }, new Assembler(function(a) {
  52 + var elements = Assembler.elementsAbove(a, "(");
  53 + a.push(elements.reverse());
  54 + }));
  55 +
  56 + var string = new QuotedString();
  57 + string.setAssembler(Assembler.unary(function (token) {
  58 + return token.sval.substring(1, token.sval.length -1);
  59 + }));
  60 +
  61 + var num = new Num();
  62 + num.setAssembler(Assembler.unary(function (token) {return token.nval;}));
  63 +
  64 + var word = new Word();
  65 + word.setAssembler(Assembler.unary(function (token) {
  66 + token.sval.atom = true;
  67 + return new Atom(token.sval);
  68 + }));
  69 +
  70 + var atom = grammarRule(Alternation, function(atom) {
  71 + atom.either(word).or(num).or(string);
  72 + });
  73 +
  74 + return {
  75 + parse: function(s) {
  76 + var a = expression().completeMatch(new TokenAssembly(new JavathcriptTokenizer(s)));
  77 + if (a == null) {
  78 + throw new Error("Failed to parse "+s);
  79 + }
  80 + return a.pop();
  81 + },
  82 + parsePartial: function(tokA) {
  83 + var a = expression().bestMatch(tokA);
  84 + if (a == null) {
  85 + throw new Error("Failed to parse "+tokA);
  86 + }
  87 + return a;
  88 + }
  89 + };
  90 +
  91 +})();
  92 +
19 source/JavathcriptTokenizer.js
... ... @@ -0,0 +1,19 @@
  1 +function JavathcriptTokenizer(s) {
  2 + Tokenizer.call(this, s);
  3 + this.CLASS = "JavathcriptTokenizer";
  4 +}
  5 +
  6 +Util.extend(JavathcriptTokenizer, Tokenizer);
  7 +
  8 +JavathcriptTokenizer.prototype.initialCharacterState = function() {
  9 + this.wordState.setWordChars(".+*-<>=", true);
  10 + this.commentState = new SlashSlashState();
  11 + this.setCharacterState(0, 255, this.wordState);
  12 + this.setCharacterState(0, ' ', this.whitespaceState);
  13 + this.setCharacterState(",", this.whitespaceState);
  14 + this.setCharacterState("'(){}:", this.symbolState);
  15 + this.setCharacterState(";", this.commentState);
  16 + this.setCharacterState('0', '9', this.numberState);
  17 + this.setCharacterState( '"', this.quoteState);
  18 + this.setCharacterState( ".", this.numberState);
  19 +};
1  source/UnevaluatedObj.js
... ... @@ -0,0 +1 @@
  1 +function UnevaluatedObj() {};
0  Javathcript.js → webcontent/Javathcript.js
File renamed without changes
285 webcontent/index.html
... ... @@ -0,0 +1,285 @@
  1 +<html>
  2 +<head>
  3 + <title>Javathcript - Javascript with a lisp</title>
  4 +
  5 + <link rel="stylesheet" href="style/main.css" />
  6 +
  7 + <script type="text/javascript" src="Javathcript.js"></script>
  8 + <script type="text/lisp" src="lisp/prelude.lsp"></script>
  9 +
  10 + <script type="text/javascript">
  11 + function evaluateElement(srcElementName, resultElementName) {
  12 + var srcElement = document.getElementById(srcElementName);
  13 + var code = srcElement.innerHTML.replace(/<span[^>]*>/g, '').replace(/<\/span[^>]*>/g, '');
  14 + code = code.replace(/&gt;/g, ">");
  15 + code = code.replace(/&lt;/g, "<");
  16 + var result = Javathcript.eval(code);
  17 + var resultElement = document.getElementById(resultElementName);
  18 + resultElement.style.display = 'block';
  19 + resultElement.innerHTML = result.toString();
  20 + }
  21 +
  22 + function format(code) {
  23 + for (var func in Javathcript.environment) {
  24 + func = func.replace(/\*/g, "\\*");
  25 + if ("<span class='func'></span>t".indexOf(func) < 0) {
  26 + code = code.replace(new RegExp("\\("+func, "g"), "(<span class='func'>"+func+"</span>");
  27 + }
  28 + code = code.replace(/(\"[^\"]*\")/g, "<span class='string'>$1</span>");
  29 + }
  30 + return code;
  31 + }
  32 + var divId = 0;
  33 + function insertExamples(examples) {
  34 + for (var i = 0; i < examples.length; ++i) {
  35 + var id = divId ++;
  36 + document.write("<pre class='evaluable' onclick=\"evaluateElement('javathcript_example_"+id+"', 'javathcript_example_result_"+id+"')\" id='javathcript_example_"+id+"'>"+format(examples[i])+"</pre><pre class='result' id='javathcript_example_result_"+id+"'></pre>");
  37 + }
  38 + }
  39 +
  40 + </script>
  41 +</head>
  42 +
  43 +<body>
  44 +
  45 + <a name="top"><h1>Javathcript - Javascript with a lisp</h1></a>
  46 +
  47 + <p>Javathcript allows you to script your web pages in a simple lisp variant. Once you include <a href="Javathcript.js">Javathcript.js</a>,
  48 + any script tags in your document with <code>type="text/lisp"</code> will be evaluated. It will also download lisp files (only from the original server),
  49 + if you have a script tag that has a <code>src</code> attribute. Finally, you can also evaluate lisp code from javascript using
  50 + <code>Javathcript.eval(lispString)</code>.</p>
  51 +
  52 + <p>While it is not an exact implementation of any pre-existing variant of lisp, if you know lisp most of it should be familiar.
  53 + If you don't you might find it useful to follow a tutorial, e.g. <a href="http://www.n-a-n-o.com/lisp/cmucl-tutorials/LISP-tutorial.html">this one</a>.
  54 + There <em>will</em> be differences between this implementation and others, but there is also much that is common.</p>
  55 +
  56 + <h2>Contents</h2>
  57 +
  58 + <ol type="1">
  59 + <li><a href="#top">Introduction</a></li>
  60 + <li><a href="#usage">Example Usage</a></li>
  61 + <li><a href="#functions">Javathcript Functions</a>
  62 + <ol type="i">
  63 + <li><a href="#functions-basic">Functions from <em>A Micro-manual for Lisp - Not the Whole Truth</em></a></li>
  64 + <li><a href="#functions-scope">Scope</a></li>
  65 + <li><a href="#functions-sugar">Syntactic Sugar, Comments and Aliases</a></li>
  66 + <li><a href="#functions-strings">Strings</a></li>
  67 + <li><a href="#functions-numbers">Numbers and Maths</a></li>
  68 + <li><a href="#functions-objects">Objects</a></li>
  69 + <li><a href="#functions-js">Interacting with Javascript and the Browser</a></li>
  70 + </ol>
  71 + </li>
  72 + <li><a href="#have-a-go">Have a Go</a></li>
  73 + <li><a href="#getting-source">Getting the Source</a></li>
  74 + <li><a href="#limitations">Limitations</a></li>
  75 + <li><a href="#acknowledgements">Acknowledgements</a></li>
  76 + <li><a href="#contact">Contact</a></li>
  77 + </ol>
  78 +
  79 + <a name="usage"><h2>Example Usage</h2></a>
  80 +
  81 + <p>Since the browser doesn't natively understand script tags with <code>type="text/lisp"</code>, the <a href="Javathcript.js">Javathcript.js</a> file
  82 + is included in the header to provide parsing and evaluation for lisp code. We have also included the <a href="lisp/prelude.lsp">prelude.lsp</a> lisp
  83 + source file in the header since it defines a number of functions that this code uses (e.g. <code>getElement</code> and <code>alert</code>).
  84 + To include these files, the following script tags are in the head of this page. Note that the <code>prelude.lsp</code> file has to be served from the same
  85 + server as any page that uses it, as it is fetched by an XHR.
  86 +
  87 + <pre>&lt;<span class='tags'>script</span> <span class='attributes'>type</span>=<span class='string'>"text/javascript"</span> <span class='attributes'>src</span>=<span class='string'>"Javathcript.js"</span>&gt;&lt;<span class='tags'>/script</span>&gt;
  88 +&lt;<span class='tags'>script</span> <span class='attributes'>type</span>=<span class='string'>"text/lisp"</span> <span class='attributes'>src</span>=<span class='string'>"lisp/prelude.lsp"</span>&gt;&lt;<span class='tags'>/script</span>&gt;</pre>
  89 +
  90 + <p>The block of code below attaches a <em>javathcript</em> handler to the <code>onclick</code> attribute of the button.
  91 + Click the button to see the code execute. <input id='name' type="text" value="Fred"></input><button id='btn'>Click Me</button></p>
  92 +
  93 + <script type="text/lisp">
  94 + (let*
  95 + ( (button (getElement "btn"))
  96 + (nameField (getElement "name"))
  97 + (clickHandler (lambda () (alert (concat "Hello " (get nameField "value"))))) )
  98 + (set button "onclick" (export clickHandler))
  99 + )
  100 + </script>
  101 + <pre>&lt;<span class='tags'>script</span> <span class='attributes'>type</span>=<span class='string'>"text/lisp"</span>&gt;
  102 + (<span class='func'>let*</span>
  103 + ( (button (<span class='func'>getElement</span> <span class='string'>"btn"</span>))
  104 + (nameField (<span class='func'>getElement</span> <span class='string'>"name"</span>))
  105 + (clickHandler (<span class='func'>lambda</span> () (<span class='func'>alert</span> (<span class='func'>concat</span> <span class='string'>"Hello "</span> (<span class='func'>get</span> nameField <span class='string'>"value"</span>))))) )
  106 + (<span class='func'>set</span> button <span class='string'>"onclick"</span> (<span class='func'>export</span> clickHandler))
  107 + )
  108 +&lt;<span class='tags'>/script</span>&gt;</pre>
  109 +
  110 + <a name="functions"><h2>Javathcript Functions</h2></a>
  111 +
  112 + <a name="functions-basic"><h3>Functions from <em>A Micro-manual for Lisp - Not the Whole Truth</em></h3></a>
  113 +
  114 + <code>quote car cdr cons equal atom cond lambda label</code>
  115 + <p>These functions provide the core of Lisp, and are enough to create a self-hosting LISP interpreter. Here are some examples (click to evaluate them):</p>
  116 + <script type="text/javascript">
  117 + insertExamples([
  118 + "(quote a)",
  119 + "(car (quote (a b c)))",
  120 + "(cdr (quote (a b c)))",
  121 + "(cons (quote a) (quote (b c)))",
  122 + "(equal (car (quote (a b))) (quote a))",
  123 + "(cond ((atom (quote a)) (quote b)) ((quote t) (quote c)))",
  124 + "((lambda (X Y) (cons (car X) Y)) (quote (a b)) (cdr (quote (c d))))",
  125 + "((label ff (lambda (x) (cond ((atom x) x) ((quote t) (ff (car x)))))) (quote ((a b) c)))"
  126 + ]);
  127 + </script>
  128 + <p>The <em>Micro-manual</em> also suggests <code>t Nil or and not null defun list</code> which I have included, and <code>cadr</code> with arbitrary combinations
  129 + of 'a' and 'd', which I have not. I've modified the following example slightly from that given in the paper as the empty list is not regarded as an
  130 + atom in <em>javathcript</em>.</p>
  131 +
  132 + <script type="text/javascript">
  133 + insertExamples([
  134 + "(defun subst (x y z) (cond ((or (atom z) (null z)) (cond ((equal z y) x) (t z))) (t (cons (subst x y (car z)) (subst x y (cdr z))))))",
  135 + "(subst (quote (plus x y)) (quote v) (quote (times x v)))"
  136 + ]);
  137 + </script>
  138 +
  139 + <a name="functions-scope"><h3>Scope</h3></a>
  140 +
  141 + <code>def defun let let*</code>
  142 +
  143 + <p>As well as <code>defun</code> to set a name for a function in the global scope, there is also <code>def</code>, to allow you to set names for
  144 + other kinds of values in the global scope. Functions provide their own scope, but you can also use <code>let</code> and <code>let*</code> to create
  145 + temporary scopes. The code example at the to