├── .gitignore ├── LICENCE.txt ├── README.md ├── bin ├── all-in-one.js ├── main.js └── prelude.js ├── docs ├── Development cycle ├── classSyntax.md ├── memo_peg.md ├── optimization.md └── versions.md ├── examples ├── AutoTyper.st ├── server.st └── titaniumApp.st ├── lib └── beautify.js ├── package.json ├── src ├── .gitignore ├── js │ ├── development │ │ ├── block.js │ │ ├── class.js │ │ ├── expression.js │ │ ├── littleparser.js │ │ ├── littlesmallscript.js │ │ ├── optimization.js │ │ ├── packrat.js │ │ └── statement.js │ └── production │ │ ├── block.js │ │ ├── class.js │ │ ├── expression.js │ │ ├── littleparser.js │ │ ├── littlesmallscript.js │ │ ├── optimization.js │ │ ├── packrat.js │ │ └── statement.js ├── main-dev.js ├── st │ ├── block.st │ ├── class.st │ ├── expression.st │ ├── littleparser.st │ ├── littlesmallscript.st │ ├── optimization.st │ ├── packrat.st │ ├── run.sh │ └── statement.st └── test-web.html └── tests ├── tests-dev.js └── tests.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .DS_Store 3 | \#* 4 | \.\#* 5 | -------------------------------------------------------------------------------- /LICENCE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012 Minori Yamashita 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Little Smallscript 2 | ================== 3 | A dialect of Smalltalk that compiles into JavaScript. 4 | 5 | Author: 6 | ------- 7 | Minori Yamashita 8 | 9 | LICENCE: 10 | -------- 11 | MIT 12 | 13 | INSTALLATION: 14 | ------------- 15 | 16 | ```shell 17 | $ git clone https://github.com/ympbyc/LittleSmallscript.git 18 | $ cd LittleSmallscript 19 | $ npm install -g 20 | ``` 21 | 22 | or simply 23 | 24 | ```shell 25 | $ npm install -g littlesmallscript 26 | ``` 27 | 28 | USAGE: 29 | ------ 30 | 31 | Compile and run on node: 32 | 33 | ```shell 34 | $ cat examples/server.st | littlesmallscript | node 35 | ``` 36 | 37 | Saving the compiled file: 38 | 39 | ```shell 40 | $ cat examples/server.st | littlesmallscript > ~/server.js 41 | ``` 42 | 43 | etc 44 | 45 | Direction: 46 | ---------- 47 | * The goal is to write Javascript in Smalltalk's syntax. 48 | * No class browser and stuff. 49 | * Some expressions are converted to javascripts syntax, for efficiency and readability. 50 | * `(1 === 1) ifTrue: ['yay']` -> `(1 === 1).ifTrue(function () {return 'yay';})` -> `(1 === 1) ? (function () { 'yay'; })() : void 0;` 51 | * `#(1 2 3) at: 0` -> `[1,2,3].at(0)` -> `[1,2,3][0]` 52 | * Other message expressions are translated into method calling expression. 53 | * `obj unary` becomes `obj.unary()`. `array inject:1 into:[]` becomes `array.injectinto(1, function () {})`. 54 | * binary messages take js operators as selectors: `x % 2 === 0` 55 | 56 | Example: 57 | -------- 58 | https://github.com/ympbyc/LittleSmallscript/tree/master/examples 59 | https://github.com/ympbyc/expensivecamera/blob/master/src/expensivecamera.st 60 | 61 | The language is changing every second so do example codes. Here's what works at least for now. 62 | 63 | ```smalltalk 64 | +Object subclass:#Animal variables:#(#name). 65 | 66 | !Animal setName: aName 67 | name := aName !. 68 | 69 | !Animal move: metre 70 | window alert: name + ' moved ' + metre + 'm.' !. 71 | 72 | +Animal subclass:#Snake variables:#(). 73 | 74 | !Snake move 75 | window alert:'Slithering...'. 76 | Snake super:#move arguments:#(5) !. 77 | 78 | Snake new 79 | ; setName:'Sammy the Python' 80 | ; move 81 | ``` 82 | 83 | Versions: 84 | ---------- 85 | 86 | 12:47 29 Jan 2013 87 | v1.0.4 88 | Inputs can now be **piped-in** to `littlesmallscript`. e.g: `echo '(1 to: 5) map: [ :n | n * n]' | littlesmallscript`. 89 | 90 | 12:35 2 Nov 2012 91 | v1.0.3 92 | String literals compile into single-quoted JS string. They were double quotes in previous versions. 93 | String literals support backslash escaping. 94 | 95 | 18:40 17 Oct 2012 96 | v1.0.2 97 | A temporary syntax for calling super methods is introduced. See the example above. 98 | 99 | 23:00 30 Sep 2012 100 | v1.0.0 101 | The first major version! 102 | Every parser is now written in LittleSmallscript itself. 103 | v1 is not backward compatible with v0. 104 | Syntax for accessing instance variables has changed. 105 | Class definition syntax has been added. 106 | 107 | 23:00 20 Sep 2012 108 | v0.0.4 109 | Fixed many bugs. 110 | Binary messages now take bare operators instead of primitives. 111 | method:at: and method:dot: fixes the scope of 'self'. 112 | 113 | 1am 20 Sep 2012 114 | v0.0.3 115 | Ready to ship! Known bugs are to be fixed. 116 | Added optimization. 117 | 118 | 12pm 12 Sep 2012 119 | v0.0.2 120 | Statement parser is coplete. 121 | Consequently, temporary variable declaration is now supported. | foo bar | compiles to var foo, bar;. 122 | Inline javascript as primary values. obj method: 123 | Binary messages with javascript operators. 1 <+> 1; 124 | Much of Little Smalltalk's built-in methods are provided via the library: prelude.js. 125 | Prettyprint using beautify.js. 126 | 127 | 5am 10 Sep 2012 128 | v0.0.1 129 | Implemented a PEG parser in javascript. 130 | Can now parse and generate expressions and literals. 131 | Messages are compiled to method calling, blocks are compiled to function literal. 132 | Temporary variable declaration is yet to be supported. 133 | -------------------------------------------------------------------------------- /bin/main.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | (function () { 4 | 'use strict'; 5 | 6 | var LittleSmallscript, fs, optimist, argv, readline, rl, help, VERSION; 7 | 8 | VERSION = 'littlesmallscript 1.0.4'; 9 | 10 | LittleSmallscript = require("../src/js/production/littlesmallscript"); 11 | 12 | fs = require('fs'); 13 | 14 | optimist = require('optimist'); 15 | argv = optimist 16 | .usage('node littlesmallscript') 17 | .alias('h', 'help') 18 | .describe('h', 'show help message') 19 | .alias('c', 'compile') 20 | .describe('c', 'compile to JavaScript and save as .js files') 21 | .alias('i', 'interactive') 22 | .describe('i', 'run an interactive LittleSmallscript REPL') 23 | .alias('p', 'print') 24 | .describe('p', 'print out the compiled JavaScript') 25 | .describe('packed', 'output without prettyprint') 26 | .alias('v', 'version') 27 | .describe('v', 'print the version') 28 | .alias('w', 'watch') 29 | .describe('watch files in current directory for changes and automaticaly compile them.') 30 | .argv; 31 | 32 | function interactiveShell () { 33 | require('./prelude'); //prototype extension 34 | 35 | readline = require('readline'), 36 | rl = readline.createInterface(process.stdin, process.stdout); 37 | 38 | rl.setPrompt("LittleSmallscript> "); 39 | rl.prompt(); 40 | 41 | rl.on("line", function(input) { 42 | try { 43 | var js = new LittleSmallscript().initWithInputandOptions(input, {prettyprint:true}).toJS(); 44 | console.log(js+'\n'); 45 | console.log(eval(js)+'\n'); 46 | } catch (err) { 47 | console.log(err.message || err.type || err+'\n'); 48 | console.log(err.partialjs); 49 | } 50 | rl.prompt(); 51 | }); 52 | } 53 | 54 | function compile (fileName) { 55 | if (! fileName) return console.log(help); 56 | return fs.readFile(fileName, 'utf8', function (err, lssString) { 57 | if (err) throw err; 58 | try { 59 | var js = new LittleSmallscript().initWithInputandOptions(lssString, {prettyprint: true}).toJS(); 60 | if (argv.p) return console.log(js); 61 | fs.writeFile(fileName.replace(/\.([^\.])+$/, '.js'), js, function (err) { 62 | if (err) throw err; 63 | }); 64 | } catch (err) { 65 | console.log(err.message||err.type||JSON.stringify(err)); 66 | throw err; 67 | } 68 | }); 69 | } 70 | 71 | help = 72 | "\n \ 73 | Usage: littlesmallscript [options] path/to/script.st\n\n \ 74 | -c, --compile compile to JavaScript and save as .js files\n \ 75 | -h, --help display this help message\n \ 76 | -i, --interactive run an interactive LittleSmallscript REPL\n \ 77 | -p, --print print out the compiled JavaScript\n \ 78 | -v, --version print out the version\n \ 79 | -w, --watch watch *.st files in current directory for changes and automaticaly compile them\n \ 80 | "; 81 | 82 | if (argv.h) return console.log(help); 83 | 84 | if (argv.v) return console.log(VERSION); 85 | 86 | if (argv.i) return interactiveShell(); 87 | 88 | if (argv.w) return fs.readdir('.', function (err, files) { 89 | //--watch 90 | files.forEach(function (fileName) { 91 | if (fileName.slice(fileName.length - 3) !== '.st') return; 92 | fs.watchFile(fileName, {interval:5000}, function (curr, prev) { 93 | if (curr.mtime !== prev.mtime) { 94 | console.log("compiling "+fileName+"...\n"); 95 | return compile(fileName); 96 | } 97 | }); 98 | }); 99 | }); 100 | 101 | if (argv.c || argv.p) return compile(argv.c || argv.p); 102 | 103 | //If no command-line option is given, go UNIXy! 104 | (function () { 105 | var inputText = ''; 106 | 107 | process.stdin.resume(); 108 | process.stdin.setEncoding('utf8'); 109 | 110 | process.stdin.on('data', function (chunk) { 111 | inputText += chunk; 112 | }); 113 | 114 | process.stdin.on('end', function () { 115 | console.log( 116 | new LittleSmallscript().initWithInputandOptions(inputText, {prettyprint: true}).toJS()); 117 | inputText = ''; 118 | }); 119 | }()); 120 | 121 | }).call(this); 122 | -------------------------------------------------------------------------------- /bin/prelude.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012 Minori Yamashita 3 | * See LICENCE.txt 4 | */ 5 | /* 6 | * Little Smallmethods 7 | * Define Little Smalltalk's built-in methods for JS primitive objects. 8 | * This library adds methods to basic objects' prototype if you like it or not. 9 | */ 10 | (function () { 11 | 'use strict'; 12 | var __hasProp = {}.hasOwnProperty; 13 | 14 | Object.prototype.asString = Object.prototype.toString; 15 | Object.prototype.class_ = function () { return this.constructor; }; 16 | Object.prototype.copy = Object.prototype.shallowCopy = function () { return this; }; 17 | Object.prototype.deepCopy = function () { 18 | var a = new (this.constructor || Object); 19 | for (var key in this) { 20 | if (__hasProp.call(this, key)) a[key] = this[key]; 21 | } 22 | return a; 23 | }; 24 | Object.prototype.do_ = Object.prototype.binaryDo = function (fn) { 25 | for (var key in this) { 26 | if (__hasProp.call(this, key) && String(key).search(/__/) !== 0) fn(this[key], key); 27 | } 28 | return null; 29 | }; 30 | Object.prototype.error = function (str) { throw str; }; 31 | Object.prototype.isKindOf = function (Klass) { return this instanceof Klass; }; 32 | Object.prototype.isMemberOf = function (Klass) { return this.class_() === Klass; }; 33 | Object.prototype.print = Object.printString = function () { return JSON ? JSON.stringify(this) : this.toString(); }; 34 | Object.prototype.respondsTo = function (name) { return this[name] !== undefined && this[name] !== null; }; 35 | 36 | Number.prototype.to = function (tonum) { 37 | var i = this-1, 38 | res = []; 39 | while (++i < tonum) 40 | res.push(i); 41 | return res; 42 | }; 43 | // to:by: 44 | Number.prototype.toby = function (tonum, bynum) { 45 | var i = this-1, 46 | res = []; 47 | while (i += bynum <= tonum) 48 | res.push(i); 49 | return res; 50 | }; 51 | Number.prototype.timesRepeat = function (fn) { 52 | var _this = this; 53 | return (0).to(this).do_(function (it) { return fn.call(_this, it); }); 54 | }; 55 | 56 | Object.prototype.asArray = function () { 57 | return this.collect(function (it) {return it}); 58 | }; 59 | Object.prototype.asString = function () { 60 | return this.asArray().injectinto('', function (it, lastres) { 61 | return lastres + String(it); 62 | }); 63 | }; 64 | Object.prototype.collect = function (fn) { 65 | var ret = {}, 66 | _this = this; 67 | this.do_(function (it, key) { 68 | ret[key] = fn.call(_this, it); 69 | }); 70 | return ret; 71 | }; 72 | Object.prototype.detect = function (fn) { 73 | this.do_(function (it, key) { 74 | if (fn.call(this, it)) return it; 75 | }); 76 | throw "Object.detect could not find an item that satisfies "+fn.toString()+"."; 77 | }; 78 | // detect:ifAbsent: 79 | Object.prototype.detectifAbsent = function (fn1, fn2) { 80 | try { 81 | return this.detect(fn1); 82 | } catch (err) { 83 | return fn2.call(this); 84 | } 85 | }; 86 | Object.prototype.includes = function (it) { 87 | try{ 88 | this.detect(function (it2) { return it === it2; }); 89 | return true; 90 | } catch (err) { 91 | return false; 92 | } 93 | }; 94 | // inject:into: 95 | Object.prototype.injectinto = function (initialValue,fn) { 96 | var lastres = initialValue, 97 | _this = this; 98 | this.do_(function (it, key) { 99 | lastres = fn.call(_this, it, lastres); 100 | }); 101 | return lastres; 102 | }; 103 | Object.prototype.isEmpty = function () { 104 | return this.size() === 0; 105 | }; 106 | Object.prototype.occuranceOf = function (item) { 107 | return this.injectinto(0, function (it, lst) { return (item === it) ? ++lst : lst; }); 108 | }; 109 | Object.prototype.remove = function (item) { 110 | var found = false, 111 | _this = this; 112 | this.do_(function (it, key) { 113 | if (it === item) { found = true; delete _this[key]; } 114 | }); 115 | return null; 116 | }; 117 | // remove:ifAbsent: 118 | Object.prototype.removeifAbsent = function (item, fn) { 119 | try { 120 | return this.remove(item); 121 | } catch (err) { 122 | return fn.call(this); 123 | } 124 | }; 125 | Object.prototype.select = function (fn) { 126 | var ret = {}, 127 | _this = this; 128 | this.do_(function (it, key) { 129 | if (fn.call(_this, it)) ret[key] = it; 130 | }); 131 | return ret; 132 | }; 133 | Object.prototype.reject = function (fn) { 134 | var ret = {}, 135 | _this = this; 136 | this.do_(function (it, key) { 137 | if ( ! fn.call(_this, it)) ret[key] = it; 138 | }); 139 | return ret; 140 | }; 141 | Object.prototype.size = function () { 142 | return this.injectinto(0,function (a,b) {return b+1}); 143 | }; 144 | Object.prototype.asDictionary = function () { 145 | var ret = {}, 146 | _this = this; 147 | this.do_(function (it, key) { 148 | ret[key] = it; 149 | }); 150 | return ret; 151 | }; 152 | Object.prototype.at = function (key) { 153 | if ((! this[key]) || this[key].isNil()) throw "Object.at: slot "+key+" is nil"; 154 | return this[key]; 155 | }; 156 | // at:ifAbsent: 157 | Object.prototype.atifAbsent = function (key, fn) { 158 | try { 159 | return this.at(key); 160 | } catch (err) { 161 | return fn.call(this); 162 | } 163 | }; 164 | // at:put: 165 | Object.prototype.atput = function (key, item) { 166 | this[key] = item; 167 | return this; 168 | }; 169 | Object.prototype.includesKey = function (key) { 170 | return this[key] !== undefined; 171 | }; 172 | Object.prototype.indexOf = function (item) { 173 | for (var key in this) { 174 | if (this[key] === item) return key; 175 | } 176 | throw "Object.indexOf: not found"; 177 | }; 178 | // indexOf:ifAbsent: 179 | Object.prototype.indexOfifAbsent = function (item, fn) { 180 | try { 181 | return this.indexOf(item); 182 | } catch (err) { 183 | return fn.call(this); 184 | } 185 | }; 186 | Object.prototype.keys = function () { 187 | if (Object.keys) return Object.keys(this); 188 | this.collect(function (it, key) {return key}); 189 | }; 190 | Object.prototype.keysDo = function (fn) { 191 | return this.keys().do_(fn); 192 | }; 193 | Object.prototype.keySelect = function (fn) { 194 | return this.keys().select(fn); 195 | }; 196 | 197 | Array.prototype.addLast = function (item) { this.push(item); return this; }; 198 | Array.prototype.do_ = Array.prototype.binaryDo = Array.prototype.forEach || Object.prototype.do_; 199 | Array.prototype.collect = Array.prototype.map || function (fn) { 200 | var ret = [], 201 | _this = this; 202 | this.do_(function (it, key) { 203 | ret.push(fn.call(_this, it, key)); 204 | }); 205 | return ret; 206 | }; 207 | Array.prototype.select = Array.prototype.filter || function (fn) { 208 | var ret = [], 209 | _this = this; 210 | this.do_(function (it, key) { 211 | if (fn.call(_this, it)) ret.push(it); 212 | }); 213 | return ret; 214 | }; 215 | Array.prototype.reject = function (fn) { 216 | var ret = [], 217 | _this = this; 218 | this.do_(function (it, key) { 219 | if ( ! fn.call(_this, it)) ret.push(it); 220 | }); 221 | return ret; 222 | }; 223 | // copyFrom:to: 224 | Array.prototype.copyFromto = function (from, to) { 225 | return this.slice(from, to); 226 | }; 227 | Array.prototype.copyWith = function (fn) { 228 | return this.concat([]).concat(fn.call(this)); 229 | }; 230 | Array.prototype.copyWithout = function (val) { 231 | return this.reject(function (it) { return it===val; }); 232 | }; 233 | // equals:startingAt: 234 | Array.prototype.equalsstartingAt = function (arr, idx) { 235 | if (this.length !== arr.slice(idx).length) return false; 236 | var tgt = arr.slice(idx), 237 | _this = this; 238 | this.do_(function (it, key) { 239 | if (it !== tgt[key]) return false; 240 | }); 241 | return true; 242 | }; 243 | Array.prototype.findFirst = function (fn) { 244 | var _this = this; 245 | this.do_(function (it, key) { 246 | if (fn.call(_this, it)) return key; 247 | }); 248 | throw "Array.findFirst: not found"; 249 | }; 250 | // findFirst:ifAbsent: 251 | Array.prototype.findFirstifAbsent = function (fn1, fn2) { 252 | try { 253 | return this.findFirst(fn1); 254 | } catch (err) { 255 | return fn2.call(this); 256 | } 257 | }; 258 | Array.prototype.findLast = function (fn) { 259 | var ret, 260 | _this = this; 261 | this.do_(function (it, key) { 262 | if (fn.call(_this, it)) ret = key; 263 | }); 264 | if (ret) return ret; 265 | throw "Array.findLast: not found"; 266 | }; 267 | // findLast:ifAbsent: 268 | Array.prototype.findLastifAbsent = function (fn1, fn2) { 269 | try { 270 | return this.findLast(fn1); 271 | } catch (err) { 272 | return fn2.call(this); 273 | } 274 | }; 275 | Array.prototype.firstKey = function () { return 0; }; 276 | Array.prototype.last = function () { return this[this.length-1]; }; 277 | Array.prototype.lastKey = function () { return this.length - 1; }; 278 | // replaceFrom:to:with: 279 | Array.prototype.replaceFromtowith = function (from, to, replacement) { 280 | for (var i = from, j = 0; i < to; ++i) { 281 | this[i] = replacement[j]; 282 | ++j; 283 | } 284 | return this; 285 | }; 286 | Array.prototype.startingAt = function (idx) { return this.slice(idx); }; 287 | Array.prototype.reversed = function () { 288 | return this.reverse(); 289 | }; 290 | Array.prototype.reverseDo = function (fn) { 291 | return this.reverse().do_(fn); 292 | }; 293 | Array.prototype.sort = Array.prototype.sort; 294 | // with:do: 295 | Array.prototype.withdo = function (col, fn) { 296 | if (this.length !== col.length) throw "Array.withDo: first argument has to be an array that have the same length as the receiver"; 297 | }; 298 | Array.prototype.size = function () { return this.length }; 299 | 300 | String.prototype.at = function (idx) { return this[idx]; }; 301 | 302 | // copyFrom:length: 303 | String.prototype.copyFromlength = function (from, length) { return this.substring(from, from + length); }; 304 | // copyFrom:to: 305 | String.prototype.copyFromto = String.prototype.substring; 306 | String.prototype.print = function () { try { return console.log(this); } catch (err) { throw "String.print: no console found"; } }; 307 | String.prototype.size = function () { return this.length; }; 308 | String.prototype.sameAs = function (str) { return this.toLowerCase() === str.toLowerCase(); }; 309 | 310 | Function.prototype.value = function () { return this(); }; 311 | // value:value:... 312 | Function.prototype.valuevalue 313 | = Function.prototype.valuevaluevalue 314 | = Function.prototype.valuevaluevaluevalue 315 | = Function.prototype.valuevaluevaluevaluevalue 316 | = function (/* &rest arguments */) { 317 | return this.apply(this, arguments); 318 | }; 319 | Function.prototype.whileTrue = function (fn) { 320 | while (this()) if (fn) fn.call(this); 321 | return null; 322 | }; 323 | Function.prototype.whileFalse = function (fn) { 324 | while ( ! this()) if (fn) fn.call(this); 325 | return null; 326 | }; 327 | Function.prototype.tryCatch = function (fn) { 328 | try { 329 | return this(); 330 | } catch (err) { 331 | return fn.call(this, err); 332 | } 333 | }; 334 | Function.prototype.tryCatchfinally = function (fn1, fn2) { 335 | try { 336 | this(); 337 | } catch (err) { 338 | fn1.call(this, err); 339 | } finally { 340 | return fn2.call(this); 341 | } 342 | }; 343 | 344 | }).call(this); 345 | -------------------------------------------------------------------------------- /docs/Development cycle: -------------------------------------------------------------------------------- 1 | Development cycle 2 | ----------------- 3 | 4 | ```zsh 5 | cd src/st/ 6 | edit one of the *.st files 7 | ./run.sh 8 | node ../../tests/tests-dev.js 9 | node ../main-dev.js -i 10 | ``` -------------------------------------------------------------------------------- /docs/classSyntax.md: -------------------------------------------------------------------------------- 1 | class definition syntax 2 | ======================= 3 | 4 | Lets imitate the Little Smalltalk v5's syntax for class definition. 5 | 6 | ```smalltalk 7 | 8 | Animal subclass: #Snake variables: #(#name #colour #awake) 9 | . 10 | !Snake 11 | initWithName: itsName color: itsColour 12 | name := itsName. 13 | colour := itsColour. 14 | awake := true 15 | ! 16 | . 17 | !Snake 18 | getName 19 | ^ name 20 | ! 21 | . 22 | !Snake 23 | move: metre 24 | awake ifFalse: [^ null]. 25 | ^ name , ' the python moved ' , metre , 'm.' 26 | ! 27 | . 28 | (Snake new ; initWithName: 'Sammy' color: 'green') move: 5 29 | ``` 30 | 31 | Indentations and newlines are not required, so you could do something like 32 | 33 | ```smalltalk 34 | !Snake move: metre awake ifFalse: [^ null]. ^ name , '...'! 35 | ``` 36 | -------------------------------------------------------------------------------- /docs/memo_peg.md: -------------------------------------------------------------------------------- 1 | PEG 2 | ===== 3 | 4 | 参照 5 | ----- 6 | http://www.konokida.com/orengo/01.ParsingExpressionGrammar/index.html 7 | http://d.hatena.ne.jp/kmizushima/20100203/1265183754 8 | http://d.hatena.ne.jp/ku-ma-me/20070906/p1 9 | http://code.google.com/p/dupsrem/source/browse/trunk/dupes/chrome/content/removedupes/kouprey.js?r=15 10 | http://practical-scheme.net/wiliki/wiliki.cgi?Rui%3AParsingExpressionGrammar 11 | 12 | parsing expression 13 | ----- 14 | 入力に対してパーサを次々に試していき、成功したらマッチした部分を消費し、失敗したらバックトラックを行う(失敗したパーサ試行前まで戻る)。 15 | 16 | 連接 a b 17 | 全て読み込むかまったく読み込まず死。 18 | 19 | 選択 a / b 20 | aに失敗したらbを検査。どちらかが読み込めればOK 21 | 22 | 繰り返し a* 23 | aの0回以上の繰り返し. 配列が返る。 24 | 25 | 1回以上の繰り返し a+ 26 | aa*のシンタックスシュガー 27 | 28 | 先読みAnd &a 29 | 入力がaにマッチするか検査して、マッチしたら成功。ただし入力を消費しない。 30 | 失敗したら死 31 | 32 | 先読みNot !a 33 | Andの否定 34 | -------------------------------------------------------------------------------- /docs/optimization.md: -------------------------------------------------------------------------------- 1 | Optimization 2 | ============ 3 | 4 | Thought 5 | ------- 6 | 7 | Usualy with Little Smallscript every message expression is translated to method calling expression like this: 8 | from: 9 | 10 | ```smalltalk 11 | Constructor method: [1] at: #one. 12 | ``` 13 | 14 | to: 15 | 16 | ```javascript 17 | Constructor.methodat(function () { return 1; }, 'one') 18 | ``` 19 | 20 | While it works fine when these methods (in this case Function.prototype.methodat) are defined in javascript side, it might be more desirable for users to get the following code instead: 21 | 22 | ```javascript 23 | Constructor.prototype.one = function () { return 1; }; 24 | ``` 25 | 26 | This looks more familiar to javascript coders than the above one. It also is a fraction of a millisecond faster because it has no overhead for creating a call frame for function call; 27 | 28 | Generating code that users are familiar with is certainly a good thing. However if you strictly follow this rule, the resulting code becomes as messy as hell. 29 | Here is a code generated by CoffeeScript: 30 | 31 | ```javascript 32 | var i, res; 33 | res = (function() { 34 | var _i, _results; 35 | _results = []; 36 | for (i = _i = 0; _i < 9; i = ++_i) { 37 | alert(i); 38 | _results.push(i); 39 | } 40 | return _results; 41 | })(); 42 | ``` 43 | 44 | and Here is the code that does the same thing as above; generated by Little Smallscript: 45 | 46 | ```javascript 47 | var res; 48 | res = (0).to(9).collect(function (i) { 49 | alert(i); 50 | return i; 51 | }); 52 | ``` 53 | 54 | Clearly Little Smallscript wins the readability in this case. 55 | 56 | We have to balance between familiarity, efficiancy and readability and carefully choose what to optimize and what to leave as is. 57 | 58 | Direction 59 | ---------- 60 | 61 | Little Smallscript promotes functional programming style over imperative style. This often means that the use of statements should be kept minimim. 62 | fors and whiles should only be used for library functions, and are not for users to write. 63 | 64 | Therefore I decided to optimize only the methods that does not require statements. 65 | 66 | The following is a list of expressions that should be compiled to something other than a simple method invocation expression, and what the resulting code should look like: 67 | 68 | ### Object::at: ### 69 | 70 | ```smalltalk 71 | x at: #a. 72 | x at: someExpression. 73 | x at: #a put: y. 74 | ``` 75 | 76 | ```javascript 77 | x.a; 78 | x[someExpression]; 79 | x.a = y; 80 | ``` 81 | 82 | ### Boolean::ifTrue: ### 83 | 84 | ```smalltalk 85 | x ifTrue: someBlock. 86 | x ifTrue: [exp1. exp2]. 87 | x ifTrue: someBlockA ifFalse: someBlockB. 88 | ``` 89 | 90 | ```javascript 91 | x ? someBlock() : void 0; 92 | x ? (exp1, exp2) : void 0; 93 | x ? someBlock1() : someBlock2(); 94 | ``` 95 | 96 | ### Boolean::and,or,not ### 97 | 98 | ```smalltalk 99 | bool and: [exp, bool2] 100 | ``` 101 | 102 | ```javascript 103 | bool && exp, bool2 104 | ``` 105 | 106 | ### Class definition ### 107 | 108 | ```smalltalk 109 | | Human | 110 | 111 | Human <- Animal subclass. 112 | 113 | Human method: [:name | 114 | this at: #myname put: name 115 | ] at: #constructor. 116 | 117 | Human method: [:name | 118 | window alert: ('Hello ' <+> name) 119 | ] at: #talkTo 120 | ``` 121 | 122 | ```javascript 123 | var Human; 124 | Human = (function (_super) { 125 | var Human; 126 | 127 | Human = function (name) { 128 | this.myname = name; 129 | }; 130 | 131 | Human.prototype = new _super(); 132 | 133 | return Human; 134 | })(Animal) 135 | 136 | Human.prototype.talkTo = function (name) { 137 | window.alert('Hello ' + name) 138 | }; 139 | ``` 140 | 141 | This notation is derived from CoffeeScript 142 | 143 | ### Unary operator "new" ### 144 | 145 | ```smalltalk 146 | Human new: 'Yendor' 147 | ``` 148 | 149 | ```javascript 150 | new Human('Yendor') 151 | ``` 152 | -------------------------------------------------------------------------------- /docs/versions.md: -------------------------------------------------------------------------------- 1 | Difference between versions 2 | ====================== 3 | 4 | Little Smalltalk version 1 5 | -------------------------- 6 | 7 | ```smalltalk 8 | Class Person :Animal 9 | | myname answerString | 10 | [ 11 | new: name 12 | myname <- name. 13 | answerString <- 'hello' , name 14 | | 15 | answer 16 | answerString print. 17 | ^ answerString 18 | | 19 | answer: str | prefix | 20 | prefix <- '.'. 21 | answerString <- str , prefix. 22 | ^ self answer 23 | ] 24 | 25 | ``` 26 | 27 | Little Smalltalk version 3 28 | -------------------------- 29 | 30 | ```smalltalk 31 | Class Person Animal myname answerString 32 | 33 | Methods Person 'all' 34 | new: name 35 | "same as version 1" 36 | | 37 | answer 38 | "same as version 1" 39 | | 40 | answer: str | prefix | 41 | "same as version 1" 42 | ] 43 | ``` 44 | 45 | Little Smalltalk version 4 and 5 46 | -------------------------- 47 | 48 | ```smalltalk 49 | +Animal subclass: #Person variables: #(name answerString) 50 | 51 | !Person 52 | new: name 53 | "same as version 1" 54 | ! 55 | !Person 56 | answer 57 | "same as version 1" 58 | ! 59 | !Person 60 | answer: str | prefix | 61 | "same as version 1" 62 | ! 63 | ``` 64 | -------------------------------------------------------------------------------- /examples/AutoTyper.st: -------------------------------------------------------------------------------- 1 | " 2 | examples/AutoTyper.st 3 | 4 | AutoTyper automates your live coding. 5 | AutoTyper 6 | author: 'Minori Yamashita' 7 | Email: 'ympbyc@gmail.com' 8 | requires: 'jQuery' 9 | " 10 | Object subclass: #AutoTyper variables: #(#input #index #interval #console). 11 | 12 | !AutoTyper init: text 13 | input <- text. 14 | index <- 0. 15 | interval <- 0. 16 | console <- $ value: 'body'!. 17 | 18 | !AutoTyper setConsole: selector 19 | console <- $ value: selector !. 20 | 21 | !AutoTyper setInterval: num 22 | interval <- num !. 23 | 24 | !AutoTyper first 25 | console text: '' !. 26 | 27 | !AutoTyper next 28 | index > (input size - 1) ifFalse: [ 29 | console text: (console text + (input at: index)). 30 | index += 1 ] ifTrue: [null]!. 31 | 32 | !AutoTyper start | interval | 33 | self first. 34 | interval <- [window set:[ 35 | (self next === null) ifTrue: [window clearInterval: interval] 36 | ] Interval: interval] value !. 37 | 38 | AutoTyper new 39 | ; init: 'Hello, world!' 40 | ; setConsole: '#js-source' 41 | ; setInterval: 50 42 | ; start 43 | -------------------------------------------------------------------------------- /examples/server.st: -------------------------------------------------------------------------------- 1 | " 2 | examples/server.st 3 | 4 | Node http server 5 | Compile this file and give it to node 6 | " 7 | | http Server | 8 | http := require value: 'http'. 9 | 10 | Server := http at:#Server. 11 | 12 | !Server listenPort: port host: host 13 | (self at:#listen) value: port value: host. 14 | self!. 15 | 16 | (http createServer: [:req :res | 17 | res write: 200 Head: #{'Content-Type': 'text/plain'}. 18 | res end: 'Hello, LittleSmallscript!' 19 | ]) listenPort: 1337 host: '127.0.0.1'. 20 | 21 | console log: 'Server running at http://127.0.0.1:1337/' -------------------------------------------------------------------------------- /examples/titaniumApp.st: -------------------------------------------------------------------------------- 1 | " 2 | examples/titaniumApp 3 | 4 | Simple titanium mobile application. 5 | Compile this file and save as app.js in your titanium mobile project 6 | " 7 | | UI win label button | 8 | UI := Ti at:#UI. 9 | 10 | win := UI createWindow: ( 11 | Object new 12 | ; at:#backgroundColor put:'#fff' 13 | ; at:#navBarHidden put:false 14 | ; at:#title put:'Hello LittleSmallscript!' 15 | ; at:#layout put:#vartical ). 16 | 17 | label := UI createLabel: ( 18 | Object new 19 | ; at:#text put:'Hello, LittleSmallscript!' 20 | ; at:#top put:'45%' ). 21 | 22 | button := UI createButton: ( 23 | Object new 24 | ; at:#title put:'touch me!' 25 | ; at:#top put:'50%' ). 26 | 27 | win add: label. 28 | win add: button. 29 | label hide. 30 | 31 | button addEvent: 'click' Listener: [ 32 | label show ]. 33 | 34 | win open. -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "littlesmallscript", 3 | "preferGlobal": "true", 4 | "version": "1.0.4", 5 | "author": "Minori Yamashita ", 6 | "description": "A simple Smalltalk to js translator.", 7 | "bin" : { 8 | "littlesmallscript" : "./bin/main.js" 9 | }, 10 | "repository": { 11 | "type": "git", 12 | "url": "https://github.com/ympbyc/LittleSmallscript" 13 | }, 14 | "keywords": [ 15 | "smalltalk", 16 | "compiler", 17 | "littlesmallscript", 18 | "little" 19 | ], 20 | "dependencies": { 21 | "optimist": "0.3.x" 22 | }, 23 | "license": "MIT", 24 | "engines": { 25 | "node": ">=0.6" 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | -------------------------------------------------------------------------------- /src/js/development/block.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | "use strict"; 3 | var Expression; 4 | Expression = require('./expression'); 5 | var Block; 6 | Block = function () { 7 | if (this.init) { 8 | this.init.apply(this, arguments); 9 | } 10 | }; 11 | Block.__super = Expression.prototype; 12 | Block.prototype = new Expression(); 13 | Block.prototype.block = function () { 14 | var _this = this; 15 | var dst_tmpl; 16 | dst_tmpl = 'function (%parameters%) { %body% }'; 17 | return _this.cacheaParser('block', function () { 18 | var parameters, body; 19 | _this.blockStart(); 20 | parameters = _this.blockHead(); 21 | body = _this.optional(function () { 22 | return _this.statement(); 23 | }); 24 | _this.blockEnd(); 25 | return _this.templateapply(dst_tmpl, { 26 | 'parameters': parameters, 27 | 'body': body 28 | }); 29 | }); 30 | }; 31 | Block.prototype.blockParameters = function () { 32 | var _this = this; 33 | return _this.cacheaParser('blockParameters', function () { 34 | var vars; 35 | vars = ''; 36 | _this.skipSpace(); 37 | _this.many(function () { 38 | _this.colon(); 39 | (vars += (_this.variable() + ', ')); 40 | return _this.skipSpace(); 41 | }); 42 | return vars.slice((0), - 2); 43 | }); 44 | }; 45 | Block.prototype.blockHead = function () { 46 | var _this = this; 47 | return _this.cacheaParser('blockHead', function () { 48 | return _this.optional(function () { 49 | var params; 50 | _this.skipSpace(); 51 | params = _this.blockParameters(); 52 | (params.size() > (0)) ? (function () { 53 | return _this.verticalBar(); 54 | })() : void 0; 55 | _this.skipSpace(); 56 | return params; 57 | }); 58 | }); 59 | }; 60 | module.exports = Block; 61 | return Block; 62 | }).call(this); -------------------------------------------------------------------------------- /src/js/development/class.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | "use strict"; 3 | var Block; 4 | Block = require('./block'); 5 | var Class; 6 | Class = function () { 7 | this.instanceVariables = null; 8 | this.currentClass = null; 9 | if (this.init) { 10 | this.init.apply(this, arguments); 11 | } 12 | }; 13 | Class.__super = Block.prototype; 14 | Class.prototype = new Block(); 15 | Class.prototype.init = function () { 16 | var _this = this; 17 | _this.instanceVariables = {}; 18 | return _this.currentClass = null; 19 | }; 20 | Class.prototype.classHeader = function () { 21 | var _this = this; 22 | var dst_tmpl; 23 | dst_tmpl = 'var %className%;\n%className% = function () { %variableInitialization%if (this.init) { this.init.apply(this, arguments); } };\n%className%.__super = %superClass%.prototype;\n%className%.prototype = new %superClass%()'; 24 | return _this.cacheaParser('classHeader', function () { 25 | var className, superClass, variables, v_init; 26 | _this.optional(function () { 27 | return _this.chr('+'); 28 | }); 29 | superClass = _this.variable(); 30 | _this.skipSpace(); 31 | _this.string('subclass:'); 32 | _this.skipSpace(); 33 | className = _this.variablableStringContent(); 34 | _this.skipSpace(); 35 | _this.string('variables:'); 36 | _this.skipSpace(); 37 | variables = _this.instanceVariableArray(); 38 | _this.instanceVariables[className] = []; 39 | v_init = variables.injectinto('', function (a, b) { 40 | _this.instanceVariables[className].push(a); 41 | return (((b + 'this.') + a) + ' = null; '); 42 | }); 43 | return _this.templateapply(dst_tmpl, { 44 | 'className': className, 45 | 'superClass': superClass, 46 | 'variableInitialization': v_init 47 | }); 48 | }); 49 | }; 50 | Class.prototype.instanceVariableArray = function () { 51 | var _this = this; 52 | return _this.cacheaParser('instanceVariableArray', function () { 53 | var variables; 54 | variables = []; 55 | _this.arrayStart(); 56 | _this.many(function () { 57 | var v; 58 | _this.skipSpace(); 59 | v = _this.variablableStringContent(); 60 | variables.push(v); 61 | _this.skipSpace(); 62 | _this.optional(function () { 63 | return _this.chr(','); 64 | }); 65 | _this.skipSpace(); 66 | return v; 67 | }); 68 | _this.closeParen(); 69 | return variables; 70 | }); 71 | }; 72 | Class.prototype.variablableStringContent = function () { 73 | var _this = this; 74 | return _this.cacheaParser('variablableStringContent', function () { 75 | return _this.try_([function () { 76 | _this.chr('#'); 77 | return _this.variable(); 78 | }, function () { 79 | return _this.betweenandaccept((function () { 80 | return _this.apostrophe(); 81 | }), (function () { 82 | return _this.apostrophe(); 83 | }), function () { 84 | return _this.variable(); 85 | }); 86 | }]); 87 | }); 88 | }; 89 | Class.prototype.instanceMethod = function () { 90 | var _this = this; 91 | var method_tmpl; 92 | method_tmpl = '%className%.prototype.%methodName% = function (%args%) { var _this = this; %methodBody% }'; 93 | return _this.cacheaParser('instanceMethod', function () { 94 | var className, methodHead, methodBody; 95 | _this.exclamation(); 96 | _this.skipSpace(); 97 | className = _this.variable(); 98 | _this.skipSpace(); 99 | methodHead = _this.methodHead(); 100 | _this.skipSpace(); 101 | _this.setCurrentClass(className); 102 | methodBody = _this.statement(); 103 | _this.setCurrentClass(null); 104 | _this.skipSpace(); 105 | _this.exclamation(); 106 | return _this.templateapply(method_tmpl, { 107 | 'className': className, 108 | 'methodName': methodHead.name, 109 | 'args': methodHead.args, 110 | 'methodBody': methodBody 111 | }); 112 | }); 113 | }; 114 | Class.prototype.methodHead = function () { 115 | var _this = this; 116 | return _this.cacheaParser('methodHead', function () { 117 | var methodName, args; 118 | methodName = ''; 119 | args = []; 120 | _this.try_([function () { 121 | return _this.many1(function () { 122 | (methodName += _this.keywordSelector().slice((0), - 1)); 123 | _this.skipSpace(); 124 | args.push(_this.variable()); 125 | return _this.skipSpace(); 126 | }); 127 | }, function () { 128 | return methodName = _this.unarySelector(); 129 | }]); 130 | return { 131 | 'name': methodName, 132 | 'args': args.join(', ') 133 | }; 134 | }); 135 | }; 136 | Class.prototype.setCurrentClass = function (className) { 137 | var _this = this; 138 | _this.currentClass = className; 139 | return className; 140 | }; 141 | Class.prototype.instanceVariableP = function (variableName) { 142 | var _this = this; 143 | var v; 144 | return (((_this.currentClass !== null) && (_this.instanceVariables[_this.currentClass] !== undefined)) && (_this.instanceVariables[_this.currentClass].indexOf(variableName) > -1)); 145 | }; 146 | module.exports = Class; 147 | return Class; 148 | }).call(this); -------------------------------------------------------------------------------- /src/js/development/expression.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | "use strict"; 3 | var LittleParser, optimization; 4 | LittleParser = require('./littleparser'); 5 | optimization = require('./optimization'); 6 | var Expression; 7 | Expression = function () { 8 | this.bundledMethods = null; 9 | if (this.init) { 10 | this.init.apply(this, arguments); 11 | } 12 | }; 13 | Expression.__super = LittleParser.prototype; 14 | Expression.prototype = new LittleParser(); 15 | Expression.prototype.init = function () { 16 | var _this = this; 17 | return _this.bundledMethods = []; 18 | }; 19 | Expression.prototype.bundleAMethodIfAvailable = function (methodName) { 20 | var _this = this; 21 | return ((_this.bundledMethods.indexOf(methodName) > -1) && bundlableMethods.bundlable(methodName)) ? (function () { 22 | return _this.bundledMethods.push(bundlableMethods.bundle(methodName)); 23 | })() : void 0; 24 | }; 25 | Expression.prototype.expression = function () { 26 | var _this = this; 27 | var tmpl; 28 | tmpl = '%assignments%%cascade%'; 29 | return _this.cacheaParser('expression', function () { 30 | var assignments, cascade; 31 | assignments = _this.optional(function () { 32 | return _this.assignments(); 33 | }); 34 | cascade = _this.cascade(); 35 | return _this.templateapply(tmpl, { 36 | 'assignments': assignments, 37 | 'cascade': cascade 38 | }); 39 | }); 40 | }; 41 | Expression.prototype.assignments = function () { 42 | var _this = this; 43 | return _this.cacheaParser('assignments', function () { 44 | return _this.many(function () { 45 | var variable; 46 | variable = _this.extendedVariable(); 47 | _this.skipSpace(); 48 | _this.assignmentArrow(); 49 | _this.skipSpace(); 50 | return (variable + ' = '); 51 | }); 52 | }); 53 | }; 54 | Expression.prototype.cascade = function () { 55 | var _this = this; 56 | var tmpl; 57 | tmpl = '(function () { var _receiver = %simpleExpression%; %body% return _receiver; })()'; 58 | return _this.cacheaParser('cascade', function () { 59 | var se; 60 | se = _this.simpleExpression(); 61 | return _this.try_([function () { 62 | _this.skipSpace(); 63 | _this.notFollowedBy(function () { 64 | return _this.semicolon(); 65 | }); 66 | return se; 67 | }, function () { 68 | var conti; 69 | conti = _this.many(function () { 70 | var mes; 71 | _this.skipSpace(); 72 | _this.semicolon(); 73 | _this.skipSpace(); 74 | mes = _this.continuation(); 75 | return optimization.optimizationAvailable(mes.methodName) ? ((function () { 76 | return (optimization.optimize('_receiver', mes.methodName, mes.args) + ';'); 77 | }))() : (function () { 78 | return (('_receiver' + mes.js) + ';'); 79 | })(); 80 | }); 81 | return _this.templateapply(tmpl, { 82 | 'simpleExpression': se, 83 | 'body': conti 84 | }); 85 | }]); 86 | }); 87 | }; 88 | Expression.prototype.simpleExpression = function (allowedParsers) { 89 | var _this = this; 90 | return _this.cacheaParser('simpleExpression', function () { 91 | var receiver, injection; 92 | receiver = injection = _this.primaryReceiver(); 93 | _this.many(function () { 94 | var mes, ret; 95 | mes = _this.continuation(allowedParsers); 96 | return optimization.optimizationAvailable(mes.methodName) ? ((function () { 97 | return injection = optimization.optimize(injection, mes.methodName, mes.args); 98 | }))() : (function () { 99 | return mes.wrapMe ? ((function () { 100 | return injection = ((('(' + injection) + mes.js) + ')'); 101 | }))() : (function () { 102 | return (injection += mes.js); 103 | })(); 104 | })(); 105 | }); 106 | return injection; 107 | }); 108 | }; 109 | Expression.prototype.continuation = function (allowedParsers) { 110 | var _this = this; 111 | return _this.cacheaParser('continuation', function () { 112 | (allowedParsers === undefined) ? (function () { 113 | return allowedParsers = [function () { 114 | return _this.keywordMessage(); 115 | }, function () { 116 | return _this.binaryMessage(); 117 | }, function () { 118 | return _this.unaryMessage(); 119 | }]; 120 | })() : void 0; 121 | return _this.try_(allowedParsers); 122 | }); 123 | }; 124 | Expression.prototype.keywordMessage = function () { 125 | var _this = this; 126 | return _this.cacheaParser('keywordMessage', function () { 127 | var methodName, args; 128 | methodName = ''; 129 | args = []; 130 | _this.many1(function () { 131 | _this.skipSpace(); 132 | (methodName += _this.keywordSelector().replace(':', '')); 133 | _this.skipSpace(); 134 | args.push(_this.simpleExpression([function () { 135 | return _this.binaryMessage(); 136 | }, function () { 137 | return _this.unaryMessage(); 138 | }])); 139 | return _this.skipSpace(); 140 | }); 141 | return { 142 | 'js': (((('.' + methodName) + '(') + args.join(', ')) + ')'), 143 | 'wrapMe': false, 144 | 'methodName': methodName, 145 | 'args': args 146 | }; 147 | }); 148 | }; 149 | Expression.prototype.binaryMessage = function () { 150 | var _this = this; 151 | return _this.cacheaParser('binaryMessage', function () { 152 | var operator, argument; 153 | _this.skipSpace(); 154 | operator = _this.operator(); 155 | _this.skipSpace(); 156 | argument = _this.simpleExpression([function () { 157 | return _this.unaryMessage(); 158 | }]); 159 | return { 160 | 'js': (((' ' + operator) + ' ') + argument), 161 | 'wrapMe': true, 162 | 'methodName': operator, 163 | 'args': [argument] 164 | }; 165 | }); 166 | }; 167 | Expression.prototype.unaryMessage = function () { 168 | var _this = this; 169 | return _this.cacheaParser('unaryMessage', function () { 170 | var unarySelector; 171 | _this.skipSpace(); 172 | unarySelector = _this.unarySelector(); 173 | return { 174 | 'js': (('.' + unarySelector) + '()'), 175 | 'wrapMe': false, 176 | 'methodName': unarySelector, 177 | 'args': [] 178 | }; 179 | }); 180 | }; 181 | Expression.prototype.primary = function () { 182 | var _this = this; 183 | return _this.cacheaParser('primary', function () { 184 | return _this.try_([function () { 185 | return _this.extendedVariable(); 186 | }, function () { 187 | return _this.literal(); 188 | }, function () { 189 | return _this.block(); 190 | }, function () { 191 | return _this.primitive(); 192 | }, function () { 193 | return _this.betweenandaccept((function () { 194 | _this.chr('('); 195 | return _this.skipSpace(); 196 | }), (function () { 197 | _this.skipSpace(); 198 | return _this.chr(')'); 199 | }), function () { 200 | return _this.cascade(); 201 | }); 202 | }]); 203 | }); 204 | }; 205 | Expression.prototype.primaryReceiver = function () { 206 | var _this = this; 207 | return _this.cacheaParser('primaryReceiver', function () { 208 | return _this.try_([function () { 209 | var num; 210 | num = _this.numberLiteral(); 211 | _this.followedBy(function () { 212 | return _this.try_([function () { 213 | return _this.keywordMessage(); 214 | }, function () { 215 | return _this.unaryMessage(); 216 | }]); 217 | }); 218 | return (('(' + num) + ')'); 219 | }, function () { 220 | _this.followedBy(function () { 221 | _this.block(); 222 | _this.skipSpace(); 223 | return _this.try_([function () { 224 | return _this.keywordMessage(); 225 | }, function () { 226 | return _this.unaryMessage(); 227 | }]); 228 | }); 229 | return (('(' + _this.block()) + ')'); 230 | }, function () { 231 | return _this.primary(); 232 | }]); 233 | }); 234 | }; 235 | Expression.prototype.primitive = function () { 236 | var _this = this; 237 | return _this.cacheaParser('primitive', function () { 238 | _this.skipSpace(); 239 | return _this.betweenandaccept((function () { 240 | _this.chr('<'); 241 | _this.notFollowedBy(function () { 242 | return _this.chr('-'); 243 | }); 244 | return '<'; 245 | }), (function () { 246 | return _this.chr('>'); 247 | }), function () { 248 | return _this.anyChar(); 249 | }); 250 | }); 251 | }; 252 | Expression.prototype.operator = function () { 253 | var _this = this; 254 | var p; 255 | p = function (str) { 256 | return function () { 257 | return _this.string(str); 258 | }; 259 | }; 260 | return _this.cacheaParser('operator', function () { 261 | var op; 262 | _this.skipSpace(); 263 | return op = _this.try_([p('+='), p('-='), p('*='), p('/='), p('+'), p('-'), p('*'), p('/'), p('%'), p('==='), p('!=='), p('<='), p('>='), p('<'), p('>'), p('^'), p('&&'), p('||')]); 264 | }); 265 | }; 266 | module.exports = Expression; 267 | return Expression; 268 | }).call(this); -------------------------------------------------------------------------------- /src/js/development/littleparser.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | "use strict"; 3 | var Packrat; 4 | Packrat = require('./packrat'); 5 | var LittleParser; 6 | LittleParser = function () { 7 | if (this.init) { 8 | this.init.apply(this, arguments); 9 | } 10 | }; 11 | LittleParser.__super = Packrat.prototype; 12 | LittleParser.prototype = new Packrat(); 13 | LittleParser.prototype.space = function () { 14 | var _this = this; 15 | return _this.cacheaParser('space', function () { 16 | return _this.regex(new RegExp('^[\\s\\n\\t]+')); 17 | }); 18 | }; 19 | LittleParser.prototype.blockStart = function () { 20 | var _this = this; 21 | return _this.cacheaParser('blockStart', function () { 22 | return _this.chr('['); 23 | }); 24 | }; 25 | LittleParser.prototype.blockEnd = function () { 26 | var _this = this; 27 | return _this.cacheaParser('blockEnd', function () { 28 | return _this.chr(']'); 29 | }); 30 | }; 31 | LittleParser.prototype.verticalBar = function () { 32 | var _this = this; 33 | return _this.cacheaParser('verticalBar', function () { 34 | return _this.chr('|'); 35 | }); 36 | }; 37 | LittleParser.prototype.colon = function () { 38 | var _this = this; 39 | return _this.cacheaParser('colon', function () { 40 | return _this.chr(':'); 41 | }); 42 | }; 43 | LittleParser.prototype.semicolon = function () { 44 | var _this = this; 45 | return _this.cacheaParser('semicolon', function () { 46 | return _this.chr(';'); 47 | }); 48 | }; 49 | LittleParser.prototype.assignmentArrow = function () { 50 | var _this = this; 51 | return _this.cacheaParser('assignmentArrow', function () { 52 | return _this.try_([function () { 53 | return _this.string(':='); 54 | }, function () { 55 | return _this.string('<-'); 56 | }]); 57 | }); 58 | }; 59 | LittleParser.prototype.apostrophe = function () { 60 | var _this = this; 61 | return _this.cacheaParser('apostrophe', function () { 62 | return _this.chr("'"); 63 | }); 64 | }; 65 | LittleParser.prototype.arrayStart = function () { 66 | var _this = this; 67 | return _this.cacheaParser('arrayStart', function () { 68 | _this.string('#('); 69 | return _this.skipSpace(); 70 | }); 71 | }; 72 | LittleParser.prototype.closeParen = function () { 73 | var _this = this; 74 | return _this.cacheaParser('closeParen', function () { 75 | return _this.chr(')'); 76 | }); 77 | }; 78 | LittleParser.prototype.hashStart = function () { 79 | var _this = this; 80 | return _this.cacheaParser('hashStart', function () { 81 | return _this.string('#{'); 82 | }); 83 | }; 84 | LittleParser.prototype.hashEnd = function () { 85 | var _this = this; 86 | return _this.cacheaParser('hashEnd', function () { 87 | return _this.chr('}'); 88 | }); 89 | }; 90 | LittleParser.prototype.exclamation = function () { 91 | var _this = this; 92 | return _this.cacheaParser('exclamation', function () { 93 | return _this.chr('!'); 94 | }); 95 | }; 96 | LittleParser.prototype.variable = function () { 97 | var _this = this; 98 | return _this.cacheaParser('variable', function () { 99 | return _this.regex(new RegExp('^[a-zA-Z_$][a-zA-Z0-9_$]*')); 100 | }); 101 | }; 102 | LittleParser.prototype.extendedVariable = function () { 103 | var _this = this; 104 | return _this.cacheaParser('extendedVariable', function () { 105 | var v; 106 | v = _this.regex(new RegExp('^[a-zA-Z_$][a-zA-Z0-9_$]*')); 107 | return (v === 'self') ? ((function () { 108 | return '_this'; 109 | }))() : (function () { 110 | _this.instanceVariableP(v) ? (function () { 111 | return v = ('_this.' + v); 112 | })() : void 0; 113 | return v; 114 | })(); 115 | }); 116 | }; 117 | LittleParser.prototype.keywordSelector = function () { 118 | var _this = this; 119 | return _this.cacheaParser('keywordSelector', function () { 120 | return _this.sequence([function () { 121 | return _this.variable(); 122 | }, function () { 123 | return _this.colon(); 124 | }]); 125 | }); 126 | }; 127 | LittleParser.prototype.unarySelector = function () { 128 | var _this = this; 129 | return _this.cacheaParser('unarySelector', function () { 130 | var sel; 131 | sel = _this.variable(); 132 | _this.notFollowedBy(function () { 133 | return _this.colon(); 134 | }); 135 | return sel; 136 | }); 137 | }; 138 | LittleParser.prototype.explicitReturn = function () { 139 | var _this = this; 140 | return _this.cacheaParser('explicitReturn', function () { 141 | return _this.chr('^'); 142 | }); 143 | }; 144 | LittleParser.prototype.commentQuote = function () { 145 | var _this = this; 146 | return _this.cacheaParser('commentQuote', function () { 147 | return _this.chr('\"'); 148 | }); 149 | }; 150 | LittleParser.prototype.comment = function () { 151 | var _this = this; 152 | return _this.cacheaParser('comment', function () { 153 | var comment; 154 | comment = _this.betweenandaccept((function () { 155 | return _this.commentQuote(); 156 | }), (function () { 157 | return _this.commentQuote(); 158 | }), function () { 159 | return _this.anyChar(); 160 | }); 161 | _this.optional(function () { 162 | return _this.space(); 163 | }); 164 | return comment; 165 | }); 166 | }; 167 | LittleParser.prototype.skipSpace = function () { 168 | var _this = this; 169 | return _this.cacheaParser('skipSpace', function () { 170 | _this.optional(function () { 171 | return _this.space(); 172 | }); 173 | return _this.many(function () { 174 | return _this.comment(); 175 | }); 176 | }); 177 | }; 178 | LittleParser.prototype.literal = function () { 179 | var _this = this; 180 | return _this.cacheaParser('literal', function () { 181 | return _this.try_([function () { 182 | return _this.numberLiteral(); 183 | }, function () { 184 | return _this.stringLiteral(); 185 | }, function () { 186 | return _this.symbolLiteral(); 187 | }, function () { 188 | return _this.arrayLiteral(); 189 | }, function () { 190 | return _this.hashLiteral(); 191 | }, function () { 192 | return _this.block(); 193 | }]); 194 | }); 195 | }; 196 | LittleParser.prototype.numberLiteral = function () { 197 | var _this = this; 198 | return _this.cacheaParser('numberLiteral', function () { 199 | return _this.regex(new RegExp('^-?[0-9]+(\\.?[0-9]+)?')); 200 | }); 201 | }; 202 | LittleParser.prototype.stringLiteral = function () { 203 | var _this = this; 204 | return _this.cacheaParser('stringLiteral', function () { 205 | return (('\'' + _this.betweenandaccept((function () { 206 | return _this.apostrophe(); 207 | }), (function () { 208 | return _this.apostrophe(); 209 | }), function () { 210 | var c; 211 | c = _this.anyChar(); 212 | return (c === '\\') ? ((function () { 213 | return (c + _this.anyChar()); 214 | }))() : (function () { 215 | return c; 216 | })(); 217 | }).replace(/\n/g, '\\n')) + '\''); 218 | }); 219 | }; 220 | LittleParser.prototype.symbolLiteral = function () { 221 | var _this = this; 222 | return _this.cacheaParser('symbolLiteral', function () { 223 | _this.chr('#'); 224 | return (('\'' + _this.variable()) + '\''); 225 | }); 226 | }; 227 | LittleParser.prototype.arrayLiteral = function () { 228 | var _this = this; 229 | var args; 230 | return _this.cacheaParser('arrayLiteral', function () { 231 | args = []; 232 | _this.arrayStart(); 233 | _this.skipSpace(); 234 | _this.many(function () { 235 | args.push(_this.expression()); 236 | _this.skipSpace(); 237 | _this.optional(function () { 238 | return _this.chr(','); 239 | }); 240 | return _this.skipSpace(); 241 | }); 242 | _this.closeParen(); 243 | return (('[' + args.join(', ')) + ']'); 244 | }); 245 | }; 246 | LittleParser.prototype.hashLiteral = function () { 247 | var _this = this; 248 | return _this.cacheaParser('hashLiteral', function () { 249 | var ret; 250 | ret = ''; 251 | _this.hashStart(); 252 | (ret += '{'); 253 | (ret += _this.many(function () { 254 | var key, val; 255 | _this.skipSpace(); 256 | key = _this.try_([function () { 257 | return _this.stringLiteral(); 258 | }, function () { 259 | return _this.numberLiteral(); 260 | }, function () { 261 | return _this.symbolLiteral(); 262 | }]); 263 | _this.skipSpace(); 264 | _this.colon(); 265 | _this.skipSpace(); 266 | val = _this.expression(); 267 | _this.skipSpace(); 268 | _this.optional(function () { 269 | return _this.chr(','); 270 | }); 271 | return (((key + ': ') + val) + ','); 272 | }).slice((0), - 1)); 273 | _this.skipSpace(); 274 | _this.hashEnd(); 275 | (ret += '}'); 276 | return ret; 277 | }); 278 | }; 279 | LittleParser.prototype.templateapply = function (template, hashmap) { 280 | var _this = this; 281 | var dest_str; 282 | dest_str = template; 283 | hashmap.do_(function (it, key) { 284 | ((it === null) || (it === undefined)) ? (function () { 285 | return it = ''; 286 | })() : void 0; 287 | return dest_str = dest_str.replace(new RegExp((('%' + key) + '%'), 'g'), it); 288 | }); 289 | return dest_str; 290 | }; 291 | module.exports = LittleParser; 292 | return LittleParser; 293 | }).call(this); -------------------------------------------------------------------------------- /src/js/development/littlesmallscript.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | "use strict"; 3 | var Statement; 4 | Statement = require('./statement'); 5 | var LittleSmallscript; 6 | LittleSmallscript = function () { 7 | this.input = null; 8 | this.options = null; 9 | this.beautifyOption = null; 10 | this.cache = null; 11 | if (this.init) { 12 | this.init.apply(this, arguments); 13 | } 14 | }; 15 | LittleSmallscript.__super = Statement.prototype; 16 | LittleSmallscript.prototype = new Statement(); 17 | LittleSmallscript.prototype.initWithInputandOptions = function (text, opt) { 18 | var _this = this; 19 | _this.input = text; 20 | _this.options = opt; 21 | _this.cache = {}; 22 | _this.beautifyOption = { 23 | 'indent_size': 2, 24 | 'indent_char': ' ', 25 | 'jslint_happy': true 26 | }; 27 | return _this; 28 | }; 29 | LittleSmallscript.prototype.onError = function (err) { 30 | var _this = this; 31 | var line, rest, token; 32 | line = (function () { 33 | var _ret; 34 | try { 35 | _ret = (function () { 36 | return (_this.input.substring((0), _this.getMaxIndex()).match(/\n/g).size() + 1); 37 | })(); 38 | } catch (err) { 39 | _ret = function () { 40 | return 0; 41 | }(err); 42 | } 43 | return _ret; 44 | })(); 45 | rest = _this.input.substring(_this.getMaxIndex()); 46 | token = rest.substring((0), rest.search(/[\.\s\t\n]|$/)); 47 | console.log((((('Parse error on line ' + line) + '. Unexpected ') + token) + '.')); 48 | return console.log('===================================================='); 49 | }; 50 | LittleSmallscript.prototype.toJS = function () { 51 | var _this = this; 52 | var wrapTmpl, js, beautifyOption, err; 53 | err = false; 54 | wrapTmpl = '(function () { \"use strict\"; %statement% }).call(this);'; 55 | (function () { 56 | var _ret; 57 | try { 58 | _ret = (function () { 59 | return js = _this.templateapply(wrapTmpl, { 60 | 'statement': _this.statement() 61 | }); 62 | })(); 63 | } catch (err) { 64 | _ret = function () { 65 | err = true; 66 | return _this.onError(); 67 | }(err); 68 | } 69 | return _ret; 70 | })(); 71 | err ? void 0 : (function () { 72 | return (_this.getIndex() < _this.input.size()) ? (function () { 73 | err = true; 74 | return _this.onError(null); 75 | })() : void 0; 76 | })(); 77 | return err ? void 0 : (function () { 78 | return (_this.options && _this.options.prettyprint) ? ((function () { 79 | return require('../../../lib/beautify.js').js_beautify(js, _this.beautifyOption); 80 | }))() : (function () { 81 | return js; 82 | })(); 83 | })(); 84 | }; 85 | module.exports = LittleSmallscript; 86 | return LittleSmallscript; 87 | }).call(this); -------------------------------------------------------------------------------- /src/js/development/optimization.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | "use strict"; 3 | var LP, template, optimTmpl, optimize, canUseDotNotation, optimizationAvailable; 4 | LP = require('./littleparser'); 5 | template = function (template, hashmap) { 6 | var dest_str; 7 | dest_str = template; 8 | hashmap.do_(function (it, key) { 9 | ((it === null) || (it === undefined)) ? (function () { 10 | return it = ''; 11 | })() : void 0; 12 | return dest_str = dest_str.replace(new RegExp((('%' + key) + '%'), 'g'), it); 13 | }); 14 | return dest_str; 15 | }; 16 | optimTmpl = { 17 | 'at': '%receiver%[%arg1%]', 18 | 'atput': '%receiver%[%arg1%] = %arg2%', 19 | 'dot': '%receiver%.%arg1%', 20 | 'dotput': '%receiver%.%arg1% = %arg2%', 21 | 'do': '%receiver%.do_(%arg1%)', 22 | 'value': '%receiver%(%arg1%)', 23 | 'valuevalue': '%receiver%(%args%)', 24 | 'valuevaluevalue': '%receiver%(%args%)', 25 | 'valuevaluevaluevalue': '%receiver%(%args%)', 26 | 'valuevaluevaluevaluevalue': '%receiver%(%args%)', 27 | 'ifTrue': '%receiver% ? (%arg1%)() : void 0', 28 | 'ifFalse': '%receiver% ? void 0 : (%arg1%)()', 29 | 'ifTrueifFalse': '%receiver% ? (%arg1%)() : (%arg2%)()', 30 | 'ifFalseifTrue': '%receiver% ? (%arg2%)() : (%arg1%)()', 31 | 'tryCatch': '(function () { var _ret; try { _ret = %receiver%(); } catch (err) { _ret = %arg1%(err); } return _ret; })()', 32 | 'tryCatchFinally': '(function () { var _ret; try { _ret = %receiver%(); } catch (err) { _ret = %arg1%(err); } finally { _ret = %arg2%(); } return _ret; })()', 33 | 'new': 'new %receiver%(%args%)', 34 | 'super': '%receiver%.__super.%arg1%.call(_this)', 35 | 'superarguments': '%receiver%.__super.%arg1%.apply(_this, %arg2%)' 36 | }; 37 | canUseDotNotation = function (str) { 38 | var v, identifier; 39 | v = new LP(str); 40 | (function () { 41 | var _ret; 42 | try { 43 | _ret = (function () { 44 | return identifier = v.betweenandaccept((function () { 45 | return v.chr("'"); 46 | }), (function () { 47 | return v.chr("'"); 48 | }), v.variable); 49 | })(); 50 | } catch (err) { 51 | _ret = function () { 52 | return null; 53 | }(err); 54 | } 55 | return _ret; 56 | })(); 57 | return (v.getIndex() === v.getInputLength()) ? ((function () { 58 | return identifier; 59 | }))() : (function () { 60 | return null; 61 | })(); 62 | }; 63 | optimize = function (receiver, methodName, args) { 64 | ((methodName === 'at') || (methodName === 'dot')) ? (function () { 65 | var identifier; 66 | identifier = canUseDotNotation(args[0]); 67 | return (identifier !== null) ? (function () { 68 | args[(0)] = identifier; 69 | return methodName = 'dot'; 70 | })() : void 0; 71 | })() : void 0; 72 | ((methodName === 'atput') || (methodName === 'dotput')) ? (function () { 73 | var identifier; 74 | identifier = canUseDotNotation(args[0]); 75 | return (identifier !== null) ? (function () { 76 | args[(0)] = identifier; 77 | return methodName = 'dotput'; 78 | })() : void 0; 79 | })() : void 0; 80 | ((methodName === 'super') || (methodName === 'superarguments')) ? (function () { 81 | return args[(0)] = canUseDotNotation(args[0]); 82 | })() : void 0; 83 | return template(optimTmpl[methodName], { 84 | 'receiver': receiver, 85 | 'args': args.join(', '), 86 | 'arg1': args[0], 87 | 'arg2': args[1], 88 | 'arg3': args[2] 89 | }); 90 | }; 91 | optimizationAvailable = function (methodName) { 92 | return optimTmpl.hasOwnProperty(methodName); 93 | }; 94 | exports.optimize = optimize; 95 | return exports.optimizationAvailable = optimizationAvailable; 96 | }).call(this); -------------------------------------------------------------------------------- /src/js/development/packrat.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | "use strict"; 3 | require('../../../bin/prelude'); 4 | Number.prototype.timesString = function (str) { 5 | var _this = this; 6 | var ret; 7 | ret = ''; 8 | _this.timesRepeat(function (i) { 9 | return (ret += str); 10 | }); 11 | return ret; 12 | }; 13 | var Packrat; 14 | Packrat = function () { 15 | this.input = null; 16 | this.index = null; 17 | this.cache = null; 18 | this.maxIndex = null; 19 | this.logNest = null; 20 | this.stackTrace = null; 21 | if (this.init) { 22 | this.init.apply(this, arguments); 23 | } 24 | }; 25 | Packrat.__super = Object.prototype; 26 | Packrat.prototype = new Object(); 27 | Packrat.prototype.init = function (text) { 28 | var _this = this; 29 | _this.input = text; 30 | _this.index = 0; 31 | _this.cache = {}; 32 | _this.maxIndex = 0; 33 | _this.logNest = -1; 34 | return _this.stackTrace = ''; 35 | }; 36 | Packrat.prototype.getIndex = function () { 37 | var _this = this; 38 | return _this.index; 39 | }; 40 | Packrat.prototype.getMaxIndex = function () { 41 | var _this = this; 42 | return _this.maxIndex; 43 | }; 44 | Packrat.prototype.getInputLength = function () { 45 | var _this = this; 46 | return _this.input.size(); 47 | }; 48 | Packrat.prototype.getStackTrace = function () { 49 | var _this = this; 50 | return _this.stackTrace; 51 | }; 52 | Packrat.prototype.cacheaParser = function (s, fn) { 53 | var _this = this; 54 | var c, slot, logIndent; 55 | fn = (fn !== undefined) ? ((function () { 56 | return fn; 57 | }))() : (function () { 58 | return function () { 59 | return null; 60 | }; 61 | })(); 62 | c = {}; 63 | (_this.logNest += 1); 64 | logIndent = _this.logNest.timesString(' '); 65 | (_this.stackTrace += (((((logIndent + 'ENTER : ') + s) + ' : ') + _this.input.substring(_this.index)) + '\n')); 66 | (function () { 67 | var _ret; 68 | try { 69 | _ret = (function () { 70 | return (_this.cache[s] === undefined) ? (_this.cache[s] = {})() : void 0; 71 | })(); 72 | } catch (err) { 73 | _ret = function () { 74 | return _this.cache[s] = {}; 75 | }(err); 76 | } 77 | return _ret; 78 | })(); 79 | slot = _this.cache[s][_this.index]; 80 | return ((slot !== undefined) && (slot !== null)) ? ((function () { 81 | c = slot; 82 | _this.index = c.idx; 83 | (_this.index > _this.maxIndex) ? (function () { 84 | return _this.maxIndex = _this.index; 85 | })() : void 0; 86 | (_this.stackTrace += (((((logIndent + 'CACHED: ') + s) + ' : ') + c.fn) + '\n')); 87 | (_this.logNest -= 1); 88 | return c.fn; 89 | }))() : (function () { 90 | return (function () { 91 | var _ret; 92 | try { 93 | _ret = (function () { 94 | c.idx = _this.index; 95 | c.fn = fn.call(_this); 96 | _this.cache[s][c.idx] = { 97 | 'fn': c.fn, 98 | 'idx': _this.index 99 | }; 100 | (_this.index > _this.maxIndex) ? (function () { 101 | return _this.maxIndex = _this.index; 102 | })() : void 0; 103 | (_this.stackTrace += (((((logIndent + 'PASS : ') + s) + ' : ') + c.fn) + '\n')); 104 | (_this.logNest -= 1); 105 | return c.fn; 106 | })(); 107 | } catch (err) { 108 | _ret = function (err) { 109 | _this.cache[s][c.idx] = null; 110 | (_this.stackTrace += (((logIndent + 'FAIL : ') + s) + '\n')); 111 | (_this.logNest -= 1); 112 | return _this.noParse(); 113 | }(err); 114 | } 115 | return _ret; 116 | })(); 117 | })(); 118 | }; 119 | Packrat.prototype.noParse = function () { 120 | var _this = this; 121 | return _this.error(('Parse error at:' + _this.index)); 122 | }; 123 | Packrat.prototype.try_ = function (parsers) { 124 | var _this = this; 125 | var ret, i; 126 | i = _this.index; 127 | parsers.do_(function (parser) { 128 | return (ret === undefined) ? (function () { 129 | return (function () { 130 | var _ret; 131 | try { 132 | _ret = (function () { 133 | return ret = parser.call(_this); 134 | })(); 135 | } catch (err) { 136 | _ret = function () { 137 | return _this.index = i; 138 | }(err); 139 | } 140 | return _ret; 141 | })(); 142 | })() : void 0; 143 | }); 144 | return (ret !== undefined) ? ((function () { 145 | return ret; 146 | }))() : (function () { 147 | return _this.noParse(); 148 | })(); 149 | }; 150 | Packrat.prototype.sequence = function (parsers) { 151 | var _this = this; 152 | var ret, i, fail; 153 | i = _this.index; 154 | ret = ''; 155 | fail = false; 156 | parsers.do_(function (parser) { 157 | return fail ? void 0 : (function () { 158 | return (function () { 159 | var _ret; 160 | try { 161 | _ret = (function () { 162 | return (ret += parser.call(_this)); 163 | })(); 164 | } catch (err) { 165 | _ret = function (err) { 166 | _this.index = i; 167 | fail = true; 168 | return _this.noParse(); 169 | }(err); 170 | } 171 | return _ret; 172 | })(); 173 | })(); 174 | }); 175 | return fail ? (function () { 176 | return _this.noParse(); 177 | })() : ((function () { 178 | return ret; 179 | }))(); 180 | }; 181 | Packrat.prototype.optional = function (parser) { 182 | var _this = this; 183 | var ret, i; 184 | i = _this.index; 185 | return (function () { 186 | var _ret; 187 | try { 188 | _ret = (function () { 189 | return parser.call(_this); 190 | })(); 191 | } catch (err) { 192 | _ret = function () { 193 | _this.index = i; 194 | return null; 195 | }(err); 196 | } 197 | return _ret; 198 | })(); 199 | }; 200 | Packrat.prototype.followedBy = function (parser) { 201 | var _this = this; 202 | var f, i; 203 | f = true; 204 | i = _this.index; 205 | (function () { 206 | var _ret; 207 | try { 208 | _ret = (function () { 209 | parser.call(_this); 210 | return f = false; 211 | })(); 212 | } catch (err) { 213 | _ret = function () { 214 | return null; 215 | }(err); 216 | } 217 | return _ret; 218 | })(); 219 | _this.index = i; 220 | return f ? ((function () { 221 | return _this.noParse(); 222 | }))() : (function () { 223 | return null; 224 | })(); 225 | }; 226 | Packrat.prototype.notFollowedBy = function (parser) { 227 | var _this = this; 228 | var f, i; 229 | f = false; 230 | i = _this.index; 231 | (function () { 232 | var _ret; 233 | try { 234 | _ret = (function () { 235 | parser.call(_this); 236 | return f = true; 237 | })(); 238 | } catch (err) { 239 | _ret = function () { 240 | return null; 241 | }(err); 242 | } 243 | return _ret; 244 | })(); 245 | _this.index = i; 246 | return f ? ((function () { 247 | return _this.noParse(); 248 | }))() : (function () { 249 | return null; 250 | })(); 251 | }; 252 | Packrat.prototype.many = function (parser) { 253 | var _this = this; 254 | var a; 255 | return _this.try_([function () { 256 | return _this.many1(function () { 257 | return parser.call(_this); 258 | }); 259 | }, function () { 260 | return ''; 261 | }]); 262 | }; 263 | Packrat.prototype.many1 = function (parser) { 264 | var _this = this; 265 | var v, vs; 266 | v = parser.call(_this); 267 | vs = _this.many(function () { 268 | return parser.call(_this); 269 | }); 270 | return (v + vs); 271 | }; 272 | Packrat.prototype.betweenandaccept = function (start, end, inbetween) { 273 | var _this = this; 274 | var ret; 275 | _this.sequence([start, function () { 276 | return ret = _this.many(function () { 277 | _this.notFollowedBy(end); 278 | return inbetween.call(_this); 279 | }); 280 | }, 281 | end]); 282 | return ret; 283 | }; 284 | Packrat.prototype.anyChar = function () { 285 | var _this = this; 286 | var c; 287 | c = _this.input[_this.index]; 288 | (_this.index += 1); 289 | return (c !== undefined) ? ((function () { 290 | return c; 291 | }))() : (function () { 292 | return _this.noParse(); 293 | })(); 294 | }; 295 | Packrat.prototype.satisfyChar = function (fn) { 296 | var _this = this; 297 | var c; 298 | c = _this.anyChar(); 299 | return (fn(c) !== undefined) ? ((function () { 300 | return c; 301 | }))() : (function () { 302 | return _this.noParse(); 303 | })(); 304 | }; 305 | Packrat.prototype.chr = function (ch) { 306 | var _this = this; 307 | var c; 308 | c = _this.anyChar(); 309 | return (c === ch) ? ((function () { 310 | return c; 311 | }))() : (function () { 312 | return _this.noParse(); 313 | })(); 314 | }; 315 | Packrat.prototype.string = function (str) { 316 | var _this = this; 317 | return (_this.input.substring(_this.index, (_this.index + str.size())) === str) ? ((function () { 318 | (_this.index += str.size()); 319 | return str; 320 | }))() : (function () { 321 | return _this.noParse(); 322 | })(); 323 | }; 324 | Packrat.prototype.regex = function (regex) { 325 | var _this = this; 326 | var rc, match; 327 | rc = regex.exec(_this.input.substring(_this.index)); 328 | return rc.isKindOf(Array) ? ((function () { 329 | match = rc[0]; 330 | (_this.index += match.size()); 331 | return match; 332 | }))() : (function () { 333 | console.log('regexFalse'); 334 | return _this.noParse('regex'); 335 | })(); 336 | }; 337 | Packrat.prototype.toParser = function (str) { 338 | var _this = this; 339 | return function () { 340 | return _this.string(str); 341 | }; 342 | }; 343 | Packrat.prototype.p = function (s) { 344 | var _this = this; 345 | console.log(s); 346 | return s; 347 | }; 348 | module.exports = Packrat; 349 | return Packrat; 350 | }).call(this); -------------------------------------------------------------------------------- /src/js/development/statement.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | "use strict"; 3 | var Class; 4 | Class = require('./class'); 5 | var Statement; 6 | Statement = function () { 7 | if (this.init) { 8 | this.init.apply(this, arguments); 9 | } 10 | }; 11 | Statement.__super = Class.prototype; 12 | Statement.prototype = new Class(); 13 | Statement.prototype.statement = function () { 14 | var _this = this; 15 | return _this.cacheaParser('statement', function () { 16 | var ret, vd; 17 | ret = ''; 18 | _this.skipSpace(); 19 | vd = _this.optional(function () { 20 | return _this.variableDeclaration(); 21 | }); 22 | (vd !== null) ? (function () { 23 | return (ret += vd); 24 | })() : void 0; 25 | _this.skipSpace(); 26 | (ret += _this.many(function () { 27 | var a; 28 | a = _this.statementable(); 29 | _this.skipSpace(); 30 | _this.chr('.'); 31 | _this.skipSpace(); 32 | _this.followedBy(function () { 33 | return _this.statementable(); 34 | }); 35 | return (a + '; '); 36 | })); 37 | (ret += (function () { 38 | var _ret; 39 | try { 40 | _ret = (function () { 41 | return (('return ' + _this.expression()) + ';'); 42 | })(); 43 | } catch (err) { 44 | _ret = function () { 45 | var st; 46 | st = (function () { 47 | var _ret; 48 | try { 49 | _ret = (function () { 50 | return (_this.statementable() + ';'); 51 | })(); 52 | } catch (err) { 53 | _ret = function () { 54 | return ''; 55 | }(err); 56 | } 57 | return _ret; 58 | })(); 59 | return (st + 'return null;'); 60 | }(err); 61 | } 62 | return _ret; 63 | })()); 64 | _this.skipSpace(); 65 | _this.optional(function () { 66 | return _this.chr('.'); 67 | }); 68 | return ret; 69 | }); 70 | }; 71 | Statement.prototype.statementable = function () { 72 | var _this = this; 73 | return _this.cacheaParser('statementable', function () { 74 | return _this.try_([function () { 75 | return _this.classHeader(); 76 | }, function () { 77 | return _this.instanceMethod(); 78 | }, function () { 79 | return _this.expression(); 80 | }]); 81 | }); 82 | }; 83 | Statement.prototype.variableDeclaration = function () { 84 | var _this = this; 85 | return _this.cacheaParser('variableDeclaration', function () { 86 | var ret; 87 | ret = 'var '; 88 | _this.skipSpace(); 89 | _this.verticalBar(); 90 | (ret += _this.many1(function () { 91 | _this.skipSpace(); 92 | return (_this.variable() + ', '); 93 | }).replace(/,\s$/, '; ')); 94 | _this.skipSpace(); 95 | _this.verticalBar(); 96 | return ret; 97 | }); 98 | }; 99 | module.exports = Statement; 100 | return Statement; 101 | }).call(this); -------------------------------------------------------------------------------- /src/js/production/block.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | "use strict"; 3 | var Expression; 4 | Expression = require('./expression'); 5 | var Block; 6 | Block = function () { 7 | if (this.init) { 8 | this.init.apply(this, arguments); 9 | } 10 | }; 11 | Block.__super = Expression.prototype; 12 | Block.prototype = new Expression(); 13 | Block.prototype.block = function () { 14 | var _this = this; 15 | var dst_tmpl; 16 | dst_tmpl = 'function (%parameters%) { %body% }'; 17 | return _this.cacheaParser('block', function () { 18 | var parameters, body; 19 | _this.blockStart(); 20 | parameters = _this.blockHead(); 21 | body = _this.optional(function () { 22 | return _this.statement(); 23 | }); 24 | _this.blockEnd(); 25 | return _this.templateapply(dst_tmpl, { 26 | 'parameters': parameters, 27 | 'body': body 28 | }); 29 | }); 30 | }; 31 | Block.prototype.blockParameters = function () { 32 | var _this = this; 33 | return _this.cacheaParser('blockParameters', function () { 34 | var vars; 35 | vars = ''; 36 | _this.skipSpace(); 37 | _this.many(function () { 38 | _this.colon(); 39 | (vars += (_this.variable() + ', ')); 40 | return _this.skipSpace(); 41 | }); 42 | return vars.slice((0), - 2); 43 | }); 44 | }; 45 | Block.prototype.blockHead = function () { 46 | var _this = this; 47 | return _this.cacheaParser('blockHead', function () { 48 | return _this.optional(function () { 49 | var params; 50 | _this.skipSpace(); 51 | params = _this.blockParameters(); 52 | (params.size() > (0)) ? (function () { 53 | return _this.verticalBar(); 54 | })() : void 0; 55 | _this.skipSpace(); 56 | return params; 57 | }); 58 | }); 59 | }; 60 | module.exports = Block; 61 | return Block; 62 | }).call(this); -------------------------------------------------------------------------------- /src/js/production/class.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | "use strict"; 3 | var Block; 4 | Block = require('./block'); 5 | var Class; 6 | Class = function () { 7 | this.instanceVariables = null; 8 | this.currentClass = null; 9 | if (this.init) { 10 | this.init.apply(this, arguments); 11 | } 12 | }; 13 | Class.__super = Block.prototype; 14 | Class.prototype = new Block(); 15 | Class.prototype.init = function () { 16 | var _this = this; 17 | _this.instanceVariables = {}; 18 | return _this.currentClass = null; 19 | }; 20 | Class.prototype.classHeader = function () { 21 | var _this = this; 22 | var dst_tmpl; 23 | dst_tmpl = 'var %className%;\n%className% = function () { %variableInitialization%if (this.init) { this.init.apply(this, arguments); } };\n%className%.__super = %superClass%.prototype;\n%className%.prototype = new %superClass%()'; 24 | return _this.cacheaParser('classHeader', function () { 25 | var className, superClass, variables, v_init; 26 | _this.optional(function () { 27 | return _this.chr('+'); 28 | }); 29 | superClass = _this.variable(); 30 | _this.skipSpace(); 31 | _this.string('subclass:'); 32 | _this.skipSpace(); 33 | className = _this.variablableStringContent(); 34 | _this.skipSpace(); 35 | _this.string('variables:'); 36 | _this.skipSpace(); 37 | variables = _this.instanceVariableArray(); 38 | _this.instanceVariables[className] = []; 39 | v_init = variables.injectinto('', function (a, b) { 40 | _this.instanceVariables[className].push(a); 41 | return (((b + 'this.') + a) + ' = null; '); 42 | }); 43 | return _this.templateapply(dst_tmpl, { 44 | 'className': className, 45 | 'superClass': superClass, 46 | 'variableInitialization': v_init 47 | }); 48 | }); 49 | }; 50 | Class.prototype.instanceVariableArray = function () { 51 | var _this = this; 52 | return _this.cacheaParser('instanceVariableArray', function () { 53 | var variables; 54 | variables = []; 55 | _this.arrayStart(); 56 | _this.many(function () { 57 | var v; 58 | _this.skipSpace(); 59 | v = _this.variablableStringContent(); 60 | variables.push(v); 61 | _this.skipSpace(); 62 | _this.optional(function () { 63 | return _this.chr(','); 64 | }); 65 | _this.skipSpace(); 66 | return v; 67 | }); 68 | _this.closeParen(); 69 | return variables; 70 | }); 71 | }; 72 | Class.prototype.variablableStringContent = function () { 73 | var _this = this; 74 | return _this.cacheaParser('variablableStringContent', function () { 75 | return _this.try_([function () { 76 | _this.chr('#'); 77 | return _this.variable(); 78 | }, function () { 79 | return _this.betweenandaccept((function () { 80 | return _this.apostrophe(); 81 | }), (function () { 82 | return _this.apostrophe(); 83 | }), function () { 84 | return _this.variable(); 85 | }); 86 | }]); 87 | }); 88 | }; 89 | Class.prototype.instanceMethod = function () { 90 | var _this = this; 91 | var method_tmpl; 92 | method_tmpl = '%className%.prototype.%methodName% = function (%args%) { var _this = this; %methodBody% }'; 93 | return _this.cacheaParser('instanceMethod', function () { 94 | var className, methodHead, methodBody; 95 | _this.exclamation(); 96 | _this.skipSpace(); 97 | className = _this.variable(); 98 | _this.skipSpace(); 99 | methodHead = _this.methodHead(); 100 | _this.skipSpace(); 101 | _this.setCurrentClass(className); 102 | methodBody = _this.statement(); 103 | _this.setCurrentClass(null); 104 | _this.skipSpace(); 105 | _this.exclamation(); 106 | return _this.templateapply(method_tmpl, { 107 | 'className': className, 108 | 'methodName': methodHead.name, 109 | 'args': methodHead.args, 110 | 'methodBody': methodBody 111 | }); 112 | }); 113 | }; 114 | Class.prototype.methodHead = function () { 115 | var _this = this; 116 | return _this.cacheaParser('methodHead', function () { 117 | var methodName, args; 118 | methodName = ''; 119 | args = []; 120 | _this.try_([function () { 121 | return _this.many1(function () { 122 | (methodName += _this.keywordSelector().slice((0), - 1)); 123 | _this.skipSpace(); 124 | args.push(_this.variable()); 125 | return _this.skipSpace(); 126 | }); 127 | }, function () { 128 | return methodName = _this.unarySelector(); 129 | }]); 130 | return { 131 | 'name': methodName, 132 | 'args': args.join(', ') 133 | }; 134 | }); 135 | }; 136 | Class.prototype.setCurrentClass = function (className) { 137 | var _this = this; 138 | _this.currentClass = className; 139 | return className; 140 | }; 141 | Class.prototype.instanceVariableP = function (variableName) { 142 | var _this = this; 143 | var v; 144 | return (((_this.currentClass !== null) && (_this.instanceVariables[_this.currentClass] !== undefined)) && (_this.instanceVariables[_this.currentClass].indexOf(variableName) > -1)); 145 | }; 146 | module.exports = Class; 147 | return Class; 148 | }).call(this); -------------------------------------------------------------------------------- /src/js/production/expression.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | "use strict"; 3 | var LittleParser, optimization; 4 | LittleParser = require('./littleparser'); 5 | optimization = require('./optimization'); 6 | var Expression; 7 | Expression = function () { 8 | this.bundledMethods = null; 9 | if (this.init) { 10 | this.init.apply(this, arguments); 11 | } 12 | }; 13 | Expression.__super = LittleParser.prototype; 14 | Expression.prototype = new LittleParser(); 15 | Expression.prototype.init = function () { 16 | var _this = this; 17 | return _this.bundledMethods = []; 18 | }; 19 | Expression.prototype.bundleAMethodIfAvailable = function (methodName) { 20 | var _this = this; 21 | return ((_this.bundledMethods.indexOf(methodName) > -1) && bundlableMethods.bundlable(methodName)) ? (function () { 22 | return _this.bundledMethods.push(bundlableMethods.bundle(methodName)); 23 | })() : void 0; 24 | }; 25 | Expression.prototype.expression = function () { 26 | var _this = this; 27 | var tmpl; 28 | tmpl = '%assignments%%cascade%'; 29 | return _this.cacheaParser('expression', function () { 30 | var assignments, cascade; 31 | assignments = _this.optional(function () { 32 | return _this.assignments(); 33 | }); 34 | cascade = _this.cascade(); 35 | return _this.templateapply(tmpl, { 36 | 'assignments': assignments, 37 | 'cascade': cascade 38 | }); 39 | }); 40 | }; 41 | Expression.prototype.assignments = function () { 42 | var _this = this; 43 | return _this.cacheaParser('assignments', function () { 44 | return _this.many(function () { 45 | var variable; 46 | variable = _this.extendedVariable(); 47 | _this.skipSpace(); 48 | _this.assignmentArrow(); 49 | _this.skipSpace(); 50 | return (variable + ' = '); 51 | }); 52 | }); 53 | }; 54 | Expression.prototype.cascade = function () { 55 | var _this = this; 56 | var tmpl; 57 | tmpl = '(function () { var _receiver = %simpleExpression%; %body% return _receiver; })()'; 58 | return _this.cacheaParser('cascade', function () { 59 | var se; 60 | se = _this.simpleExpression(); 61 | return _this.try_([function () { 62 | _this.skipSpace(); 63 | _this.notFollowedBy(function () { 64 | return _this.semicolon(); 65 | }); 66 | return se; 67 | }, function () { 68 | var conti; 69 | conti = _this.many(function () { 70 | var mes; 71 | _this.skipSpace(); 72 | _this.semicolon(); 73 | _this.skipSpace(); 74 | mes = _this.continuation(); 75 | return optimization.optimizationAvailable(mes.methodName) ? ((function () { 76 | return (optimization.optimize('_receiver', mes.methodName, mes.args) + ';'); 77 | }))() : (function () { 78 | return (('_receiver' + mes.js) + ';'); 79 | })(); 80 | }); 81 | return _this.templateapply(tmpl, { 82 | 'simpleExpression': se, 83 | 'body': conti 84 | }); 85 | }]); 86 | }); 87 | }; 88 | Expression.prototype.simpleExpression = function (allowedParsers) { 89 | var _this = this; 90 | return _this.cacheaParser('simpleExpression', function () { 91 | var receiver, injection; 92 | receiver = injection = _this.primaryReceiver(); 93 | _this.many(function () { 94 | var mes, ret; 95 | mes = _this.continuation(allowedParsers); 96 | return optimization.optimizationAvailable(mes.methodName) ? ((function () { 97 | return injection = optimization.optimize(injection, mes.methodName, mes.args); 98 | }))() : (function () { 99 | return mes.wrapMe ? ((function () { 100 | return injection = ((('(' + injection) + mes.js) + ')'); 101 | }))() : (function () { 102 | return (injection += mes.js); 103 | })(); 104 | })(); 105 | }); 106 | return injection; 107 | }); 108 | }; 109 | Expression.prototype.continuation = function (allowedParsers) { 110 | var _this = this; 111 | return _this.cacheaParser('continuation', function () { 112 | (allowedParsers === undefined) ? (function () { 113 | return allowedParsers = [function () { 114 | return _this.keywordMessage(); 115 | }, function () { 116 | return _this.binaryMessage(); 117 | }, function () { 118 | return _this.unaryMessage(); 119 | }]; 120 | })() : void 0; 121 | return _this.try_(allowedParsers); 122 | }); 123 | }; 124 | Expression.prototype.keywordMessage = function () { 125 | var _this = this; 126 | return _this.cacheaParser('keywordMessage', function () { 127 | var methodName, args; 128 | methodName = ''; 129 | args = []; 130 | _this.many1(function () { 131 | _this.skipSpace(); 132 | (methodName += _this.keywordSelector().replace(':', '')); 133 | _this.skipSpace(); 134 | args.push(_this.simpleExpression([function () { 135 | return _this.binaryMessage(); 136 | }, function () { 137 | return _this.unaryMessage(); 138 | }])); 139 | return _this.skipSpace(); 140 | }); 141 | return { 142 | 'js': (((('.' + methodName) + '(') + args.join(', ')) + ')'), 143 | 'wrapMe': false, 144 | 'methodName': methodName, 145 | 'args': args 146 | }; 147 | }); 148 | }; 149 | Expression.prototype.binaryMessage = function () { 150 | var _this = this; 151 | return _this.cacheaParser('binaryMessage', function () { 152 | var operator, argument; 153 | _this.skipSpace(); 154 | operator = _this.operator(); 155 | _this.skipSpace(); 156 | argument = _this.simpleExpression([function () { 157 | return _this.unaryMessage(); 158 | }]); 159 | return { 160 | 'js': (((' ' + operator) + ' ') + argument), 161 | 'wrapMe': true, 162 | 'methodName': operator, 163 | 'args': [argument] 164 | }; 165 | }); 166 | }; 167 | Expression.prototype.unaryMessage = function () { 168 | var _this = this; 169 | return _this.cacheaParser('unaryMessage', function () { 170 | var unarySelector; 171 | _this.skipSpace(); 172 | unarySelector = _this.unarySelector(); 173 | return { 174 | 'js': (('.' + unarySelector) + '()'), 175 | 'wrapMe': false, 176 | 'methodName': unarySelector, 177 | 'args': [] 178 | }; 179 | }); 180 | }; 181 | Expression.prototype.primary = function () { 182 | var _this = this; 183 | return _this.cacheaParser('primary', function () { 184 | return _this.try_([function () { 185 | return _this.extendedVariable(); 186 | }, function () { 187 | return _this.literal(); 188 | }, function () { 189 | return _this.block(); 190 | }, function () { 191 | return _this.primitive(); 192 | }, function () { 193 | return _this.betweenandaccept((function () { 194 | _this.chr('('); 195 | return _this.skipSpace(); 196 | }), (function () { 197 | _this.skipSpace(); 198 | return _this.chr(')'); 199 | }), function () { 200 | return _this.cascade(); 201 | }); 202 | }]); 203 | }); 204 | }; 205 | Expression.prototype.primaryReceiver = function () { 206 | var _this = this; 207 | return _this.cacheaParser('primaryReceiver', function () { 208 | return _this.try_([function () { 209 | var num; 210 | num = _this.numberLiteral(); 211 | _this.followedBy(function () { 212 | return _this.try_([function () { 213 | return _this.keywordMessage(); 214 | }, function () { 215 | return _this.unaryMessage(); 216 | }]); 217 | }); 218 | return (('(' + num) + ')'); 219 | }, function () { 220 | _this.followedBy(function () { 221 | _this.block(); 222 | _this.skipSpace(); 223 | return _this.try_([function () { 224 | return _this.keywordMessage(); 225 | }, function () { 226 | return _this.unaryMessage(); 227 | }]); 228 | }); 229 | return (('(' + _this.block()) + ')'); 230 | }, function () { 231 | return _this.primary(); 232 | }]); 233 | }); 234 | }; 235 | Expression.prototype.primitive = function () { 236 | var _this = this; 237 | return _this.cacheaParser('primitive', function () { 238 | _this.skipSpace(); 239 | return _this.betweenandaccept((function () { 240 | _this.chr('<'); 241 | _this.notFollowedBy(function () { 242 | return _this.chr('-'); 243 | }); 244 | return '<'; 245 | }), (function () { 246 | return _this.chr('>'); 247 | }), function () { 248 | return _this.anyChar(); 249 | }); 250 | }); 251 | }; 252 | Expression.prototype.operator = function () { 253 | var _this = this; 254 | var p; 255 | p = function (str) { 256 | return function () { 257 | return _this.string(str); 258 | }; 259 | }; 260 | return _this.cacheaParser('operator', function () { 261 | var op; 262 | _this.skipSpace(); 263 | return op = _this.try_([p('+='), p('-='), p('*='), p('/='), p('+'), p('-'), p('*'), p('/'), p('%'), p('==='), p('!=='), p('<='), p('>='), p('<'), p('>'), p('^'), p('&&'), p('||')]); 264 | }); 265 | }; 266 | module.exports = Expression; 267 | return Expression; 268 | }).call(this); -------------------------------------------------------------------------------- /src/js/production/littleparser.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | "use strict"; 3 | var Packrat; 4 | Packrat = require('./packrat'); 5 | var LittleParser; 6 | LittleParser = function () { 7 | if (this.init) { 8 | this.init.apply(this, arguments); 9 | } 10 | }; 11 | LittleParser.__super = Packrat.prototype; 12 | LittleParser.prototype = new Packrat(); 13 | LittleParser.prototype.space = function () { 14 | var _this = this; 15 | return _this.cacheaParser('space', function () { 16 | return _this.regex(new RegExp('^[\\s\\n\\t]+')); 17 | }); 18 | }; 19 | LittleParser.prototype.blockStart = function () { 20 | var _this = this; 21 | return _this.cacheaParser('blockStart', function () { 22 | return _this.chr('['); 23 | }); 24 | }; 25 | LittleParser.prototype.blockEnd = function () { 26 | var _this = this; 27 | return _this.cacheaParser('blockEnd', function () { 28 | return _this.chr(']'); 29 | }); 30 | }; 31 | LittleParser.prototype.verticalBar = function () { 32 | var _this = this; 33 | return _this.cacheaParser('verticalBar', function () { 34 | return _this.chr('|'); 35 | }); 36 | }; 37 | LittleParser.prototype.colon = function () { 38 | var _this = this; 39 | return _this.cacheaParser('colon', function () { 40 | return _this.chr(':'); 41 | }); 42 | }; 43 | LittleParser.prototype.semicolon = function () { 44 | var _this = this; 45 | return _this.cacheaParser('semicolon', function () { 46 | return _this.chr(';'); 47 | }); 48 | }; 49 | LittleParser.prototype.assignmentArrow = function () { 50 | var _this = this; 51 | return _this.cacheaParser('assignmentArrow', function () { 52 | return _this.try_([function () { 53 | return _this.string(':='); 54 | }, function () { 55 | return _this.string('<-'); 56 | }]); 57 | }); 58 | }; 59 | LittleParser.prototype.apostrophe = function () { 60 | var _this = this; 61 | return _this.cacheaParser('apostrophe', function () { 62 | return _this.chr("'"); 63 | }); 64 | }; 65 | LittleParser.prototype.arrayStart = function () { 66 | var _this = this; 67 | return _this.cacheaParser('arrayStart', function () { 68 | _this.string('#('); 69 | return _this.skipSpace(); 70 | }); 71 | }; 72 | LittleParser.prototype.closeParen = function () { 73 | var _this = this; 74 | return _this.cacheaParser('closeParen', function () { 75 | return _this.chr(')'); 76 | }); 77 | }; 78 | LittleParser.prototype.hashStart = function () { 79 | var _this = this; 80 | return _this.cacheaParser('hashStart', function () { 81 | return _this.string('#{'); 82 | }); 83 | }; 84 | LittleParser.prototype.hashEnd = function () { 85 | var _this = this; 86 | return _this.cacheaParser('hashEnd', function () { 87 | return _this.chr('}'); 88 | }); 89 | }; 90 | LittleParser.prototype.exclamation = function () { 91 | var _this = this; 92 | return _this.cacheaParser('exclamation', function () { 93 | return _this.chr('!'); 94 | }); 95 | }; 96 | LittleParser.prototype.variable = function () { 97 | var _this = this; 98 | return _this.cacheaParser('variable', function () { 99 | return _this.regex(new RegExp('^[a-zA-Z_$][a-zA-Z0-9_$]*')); 100 | }); 101 | }; 102 | LittleParser.prototype.extendedVariable = function () { 103 | var _this = this; 104 | return _this.cacheaParser('extendedVariable', function () { 105 | var v; 106 | v = _this.regex(new RegExp('^[a-zA-Z_$][a-zA-Z0-9_$]*')); 107 | return (v === 'self') ? ((function () { 108 | return '_this'; 109 | }))() : (function () { 110 | _this.instanceVariableP(v) ? (function () { 111 | return v = ('_this.' + v); 112 | })() : void 0; 113 | return v; 114 | })(); 115 | }); 116 | }; 117 | LittleParser.prototype.keywordSelector = function () { 118 | var _this = this; 119 | return _this.cacheaParser('keywordSelector', function () { 120 | return _this.sequence([function () { 121 | return _this.variable(); 122 | }, function () { 123 | return _this.colon(); 124 | }]); 125 | }); 126 | }; 127 | LittleParser.prototype.unarySelector = function () { 128 | var _this = this; 129 | return _this.cacheaParser('unarySelector', function () { 130 | var sel; 131 | sel = _this.variable(); 132 | _this.notFollowedBy(function () { 133 | return _this.colon(); 134 | }); 135 | return sel; 136 | }); 137 | }; 138 | LittleParser.prototype.explicitReturn = function () { 139 | var _this = this; 140 | return _this.cacheaParser('explicitReturn', function () { 141 | return _this.chr('^'); 142 | }); 143 | }; 144 | LittleParser.prototype.commentQuote = function () { 145 | var _this = this; 146 | return _this.cacheaParser('commentQuote', function () { 147 | return _this.chr('\"'); 148 | }); 149 | }; 150 | LittleParser.prototype.comment = function () { 151 | var _this = this; 152 | return _this.cacheaParser('comment', function () { 153 | var comment; 154 | comment = _this.betweenandaccept((function () { 155 | return _this.commentQuote(); 156 | }), (function () { 157 | return _this.commentQuote(); 158 | }), function () { 159 | return _this.anyChar(); 160 | }); 161 | _this.optional(function () { 162 | return _this.space(); 163 | }); 164 | return comment; 165 | }); 166 | }; 167 | LittleParser.prototype.skipSpace = function () { 168 | var _this = this; 169 | return _this.cacheaParser('skipSpace', function () { 170 | _this.optional(function () { 171 | return _this.space(); 172 | }); 173 | return _this.many(function () { 174 | return _this.comment(); 175 | }); 176 | }); 177 | }; 178 | LittleParser.prototype.literal = function () { 179 | var _this = this; 180 | return _this.cacheaParser('literal', function () { 181 | return _this.try_([function () { 182 | return _this.numberLiteral(); 183 | }, function () { 184 | return _this.stringLiteral(); 185 | }, function () { 186 | return _this.symbolLiteral(); 187 | }, function () { 188 | return _this.arrayLiteral(); 189 | }, function () { 190 | return _this.hashLiteral(); 191 | }, function () { 192 | return _this.block(); 193 | }]); 194 | }); 195 | }; 196 | LittleParser.prototype.numberLiteral = function () { 197 | var _this = this; 198 | return _this.cacheaParser('numberLiteral', function () { 199 | return _this.regex(new RegExp('^-?[0-9]+(\\.?[0-9]+)?')); 200 | }); 201 | }; 202 | LittleParser.prototype.stringLiteral = function () { 203 | var _this = this; 204 | return _this.cacheaParser('stringLiteral', function () { 205 | return (('\'' + _this.betweenandaccept((function () { 206 | return _this.apostrophe(); 207 | }), (function () { 208 | return _this.apostrophe(); 209 | }), function () { 210 | var c; 211 | c = _this.anyChar(); 212 | return (c === '\\') ? ((function () { 213 | return (c + _this.anyChar()); 214 | }))() : (function () { 215 | return c; 216 | })(); 217 | }).replace(/\n/g, '\\n')) + '\''); 218 | }); 219 | }; 220 | LittleParser.prototype.symbolLiteral = function () { 221 | var _this = this; 222 | return _this.cacheaParser('symbolLiteral', function () { 223 | _this.chr('#'); 224 | return (('\'' + _this.variable()) + '\''); 225 | }); 226 | }; 227 | LittleParser.prototype.arrayLiteral = function () { 228 | var _this = this; 229 | var args; 230 | return _this.cacheaParser('arrayLiteral', function () { 231 | args = []; 232 | _this.arrayStart(); 233 | _this.skipSpace(); 234 | _this.many(function () { 235 | args.push(_this.expression()); 236 | _this.skipSpace(); 237 | _this.optional(function () { 238 | return _this.chr(','); 239 | }); 240 | return _this.skipSpace(); 241 | }); 242 | _this.closeParen(); 243 | return (('[' + args.join(', ')) + ']'); 244 | }); 245 | }; 246 | LittleParser.prototype.hashLiteral = function () { 247 | var _this = this; 248 | return _this.cacheaParser('hashLiteral', function () { 249 | var ret; 250 | ret = ''; 251 | _this.hashStart(); 252 | (ret += '{'); 253 | (ret += _this.many(function () { 254 | var key, val; 255 | _this.skipSpace(); 256 | key = _this.try_([function () { 257 | return _this.stringLiteral(); 258 | }, function () { 259 | return _this.numberLiteral(); 260 | }, function () { 261 | return _this.symbolLiteral(); 262 | }]); 263 | _this.skipSpace(); 264 | _this.colon(); 265 | _this.skipSpace(); 266 | val = _this.expression(); 267 | _this.skipSpace(); 268 | _this.optional(function () { 269 | return _this.chr(','); 270 | }); 271 | return (((key + ': ') + val) + ','); 272 | }).slice((0), - 1)); 273 | _this.skipSpace(); 274 | _this.hashEnd(); 275 | (ret += '}'); 276 | return ret; 277 | }); 278 | }; 279 | LittleParser.prototype.templateapply = function (template, hashmap) { 280 | var _this = this; 281 | var dest_str; 282 | dest_str = template; 283 | hashmap.do_(function (it, key) { 284 | ((it === null) || (it === undefined)) ? (function () { 285 | return it = ''; 286 | })() : void 0; 287 | return dest_str = dest_str.replace(new RegExp((('%' + key) + '%'), 'g'), it); 288 | }); 289 | return dest_str; 290 | }; 291 | module.exports = LittleParser; 292 | return LittleParser; 293 | }).call(this); -------------------------------------------------------------------------------- /src/js/production/littlesmallscript.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | "use strict"; 3 | var Statement; 4 | Statement = require('./statement'); 5 | var LittleSmallscript; 6 | LittleSmallscript = function () { 7 | this.input = null; 8 | this.options = null; 9 | this.beautifyOption = null; 10 | this.cache = null; 11 | if (this.init) { 12 | this.init.apply(this, arguments); 13 | } 14 | }; 15 | LittleSmallscript.__super = Statement.prototype; 16 | LittleSmallscript.prototype = new Statement(); 17 | LittleSmallscript.prototype.initWithInputandOptions = function (text, opt) { 18 | var _this = this; 19 | _this.input = text; 20 | _this.options = opt; 21 | _this.cache = {}; 22 | _this.beautifyOption = { 23 | 'indent_size': 2, 24 | 'indent_char': ' ', 25 | 'jslint_happy': true 26 | }; 27 | return _this; 28 | }; 29 | LittleSmallscript.prototype.onError = function (err) { 30 | var _this = this; 31 | var line, rest, token; 32 | line = (function () { 33 | var _ret; 34 | try { 35 | _ret = (function () { 36 | return (_this.input.substring((0), _this.getMaxIndex()).match(/\n/g).size() + 1); 37 | })(); 38 | } catch (err) { 39 | _ret = function () { 40 | return 0; 41 | }(err); 42 | } 43 | return _ret; 44 | })(); 45 | rest = _this.input.substring(_this.getMaxIndex()); 46 | token = rest.substring((0), rest.search(/[\.\s\t\n]|$/)); 47 | console.log((((('Parse error on line ' + line) + '. Unexpected ') + token) + '.')); 48 | return console.log('===================================================='); 49 | }; 50 | LittleSmallscript.prototype.toJS = function () { 51 | var _this = this; 52 | var wrapTmpl, js, beautifyOption, err; 53 | err = false; 54 | wrapTmpl = '(function () { \"use strict\"; %statement% }).call(this);'; 55 | (function () { 56 | var _ret; 57 | try { 58 | _ret = (function () { 59 | return js = _this.templateapply(wrapTmpl, { 60 | 'statement': _this.statement() 61 | }); 62 | })(); 63 | } catch (err) { 64 | _ret = function () { 65 | err = true; 66 | return _this.onError(); 67 | }(err); 68 | } 69 | return _ret; 70 | })(); 71 | err ? void 0 : (function () { 72 | return (_this.getIndex() < _this.input.size()) ? (function () { 73 | err = true; 74 | return _this.onError(null); 75 | })() : void 0; 76 | })(); 77 | return err ? void 0 : (function () { 78 | return (_this.options && _this.options.prettyprint) ? ((function () { 79 | return require('../../../lib/beautify.js').js_beautify(js, _this.beautifyOption); 80 | }))() : (function () { 81 | return js; 82 | })(); 83 | })(); 84 | }; 85 | module.exports = LittleSmallscript; 86 | return LittleSmallscript; 87 | }).call(this); -------------------------------------------------------------------------------- /src/js/production/optimization.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | "use strict"; 3 | var LP, template, optimTmpl, optimize, canUseDotNotation, optimizationAvailable; 4 | LP = require('./littleparser'); 5 | template = function (template, hashmap) { 6 | var dest_str; 7 | dest_str = template; 8 | hashmap.do_(function (it, key) { 9 | ((it === null) || (it === undefined)) ? (function () { 10 | return it = ''; 11 | })() : void 0; 12 | return dest_str = dest_str.replace(new RegExp((('%' + key) + '%'), 'g'), it); 13 | }); 14 | return dest_str; 15 | }; 16 | optimTmpl = { 17 | 'at': '%receiver%[%arg1%]', 18 | 'atput': '%receiver%[%arg1%] = %arg2%', 19 | 'dot': '%receiver%.%arg1%', 20 | 'dotput': '%receiver%.%arg1% = %arg2%', 21 | 'do': '%receiver%.do_(%arg1%)', 22 | 'value': '%receiver%(%arg1%)', 23 | 'valuevalue': '%receiver%(%args%)', 24 | 'valuevaluevalue': '%receiver%(%args%)', 25 | 'valuevaluevaluevalue': '%receiver%(%args%)', 26 | 'valuevaluevaluevaluevalue': '%receiver%(%args%)', 27 | 'ifTrue': '%receiver% ? (%arg1%)() : void 0', 28 | 'ifFalse': '%receiver% ? void 0 : (%arg1%)()', 29 | 'ifTrueifFalse': '%receiver% ? (%arg1%)() : (%arg2%)()', 30 | 'ifFalseifTrue': '%receiver% ? (%arg2%)() : (%arg1%)()', 31 | 'tryCatch': '(function () { var _ret; try { _ret = %receiver%(); } catch (err) { _ret = %arg1%(err); } return _ret; })()', 32 | 'tryCatchFinally': '(function () { var _ret; try { _ret = %receiver%(); } catch (err) { _ret = %arg1%(err); } finally { _ret = %arg2%(); } return _ret; })()', 33 | 'new': 'new %receiver%(%args%)', 34 | 'super': '%receiver%.__super.%arg1%.call(_this)', 35 | 'superarguments': '%receiver%.__super.%arg1%.apply(_this, %arg2%)' 36 | }; 37 | canUseDotNotation = function (str) { 38 | var v, identifier; 39 | v = new LP(str); 40 | (function () { 41 | var _ret; 42 | try { 43 | _ret = (function () { 44 | return identifier = v.betweenandaccept((function () { 45 | return v.chr("'"); 46 | }), (function () { 47 | return v.chr("'"); 48 | }), v.variable); 49 | })(); 50 | } catch (err) { 51 | _ret = function () { 52 | return null; 53 | }(err); 54 | } 55 | return _ret; 56 | })(); 57 | return (v.getIndex() === v.getInputLength()) ? ((function () { 58 | return identifier; 59 | }))() : (function () { 60 | return null; 61 | })(); 62 | }; 63 | optimize = function (receiver, methodName, args) { 64 | ((methodName === 'at') || (methodName === 'dot')) ? (function () { 65 | var identifier; 66 | identifier = canUseDotNotation(args[0]); 67 | return (identifier !== null) ? (function () { 68 | args[(0)] = identifier; 69 | return methodName = 'dot'; 70 | })() : void 0; 71 | })() : void 0; 72 | ((methodName === 'atput') || (methodName === 'dotput')) ? (function () { 73 | var identifier; 74 | identifier = canUseDotNotation(args[0]); 75 | return (identifier !== null) ? (function () { 76 | args[(0)] = identifier; 77 | return methodName = 'dotput'; 78 | })() : void 0; 79 | })() : void 0; 80 | ((methodName === 'super') || (methodName === 'superarguments')) ? (function () { 81 | return args[(0)] = canUseDotNotation(args[0]); 82 | })() : void 0; 83 | return template(optimTmpl[methodName], { 84 | 'receiver': receiver, 85 | 'args': args.join(', '), 86 | 'arg1': args[0], 87 | 'arg2': args[1], 88 | 'arg3': args[2] 89 | }); 90 | }; 91 | optimizationAvailable = function (methodName) { 92 | return optimTmpl.hasOwnProperty(methodName); 93 | }; 94 | exports.optimize = optimize; 95 | return exports.optimizationAvailable = optimizationAvailable; 96 | }).call(this); -------------------------------------------------------------------------------- /src/js/production/packrat.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | "use strict"; 3 | require('../../../bin/prelude'); 4 | Number.prototype.timesString = function (str) { 5 | var _this = this; 6 | var ret; 7 | ret = ''; 8 | _this.timesRepeat(function (i) { 9 | return (ret += str); 10 | }); 11 | return ret; 12 | }; 13 | var Packrat; 14 | Packrat = function () { 15 | this.input = null; 16 | this.index = null; 17 | this.cache = null; 18 | this.maxIndex = null; 19 | this.logNest = null; 20 | this.stackTrace = null; 21 | if (this.init) { 22 | this.init.apply(this, arguments); 23 | } 24 | }; 25 | Packrat.__super = Object.prototype; 26 | Packrat.prototype = new Object(); 27 | Packrat.prototype.init = function (text) { 28 | var _this = this; 29 | _this.input = text; 30 | _this.index = 0; 31 | _this.cache = {}; 32 | _this.maxIndex = 0; 33 | _this.logNest = -1; 34 | return _this.stackTrace = ''; 35 | }; 36 | Packrat.prototype.getIndex = function () { 37 | var _this = this; 38 | return _this.index; 39 | }; 40 | Packrat.prototype.getMaxIndex = function () { 41 | var _this = this; 42 | return _this.maxIndex; 43 | }; 44 | Packrat.prototype.getInputLength = function () { 45 | var _this = this; 46 | return _this.input.size(); 47 | }; 48 | Packrat.prototype.getStackTrace = function () { 49 | var _this = this; 50 | return _this.stackTrace; 51 | }; 52 | Packrat.prototype.cacheaParser = function (s, fn) { 53 | var _this = this; 54 | var c, slot, logIndent; 55 | fn = (fn !== undefined) ? ((function () { 56 | return fn; 57 | }))() : (function () { 58 | return function () { 59 | return null; 60 | }; 61 | })(); 62 | c = {}; 63 | (_this.logNest += 1); 64 | logIndent = _this.logNest.timesString(' '); 65 | (_this.stackTrace += (((((logIndent + 'ENTER : ') + s) + ' : ') + _this.input.substring(_this.index)) + '\n')); 66 | (function () { 67 | var _ret; 68 | try { 69 | _ret = (function () { 70 | return (_this.cache[s] === undefined) ? (_this.cache[s] = {})() : void 0; 71 | })(); 72 | } catch (err) { 73 | _ret = function () { 74 | return _this.cache[s] = {}; 75 | }(err); 76 | } 77 | return _ret; 78 | })(); 79 | slot = _this.cache[s][_this.index]; 80 | return ((slot !== undefined) && (slot !== null)) ? ((function () { 81 | c = slot; 82 | _this.index = c.idx; 83 | (_this.index > _this.maxIndex) ? (function () { 84 | return _this.maxIndex = _this.index; 85 | })() : void 0; 86 | (_this.stackTrace += (((((logIndent + 'CACHED: ') + s) + ' : ') + c.fn) + '\n')); 87 | (_this.logNest -= 1); 88 | return c.fn; 89 | }))() : (function () { 90 | return (function () { 91 | var _ret; 92 | try { 93 | _ret = (function () { 94 | c.idx = _this.index; 95 | c.fn = fn.call(_this); 96 | _this.cache[s][c.idx] = { 97 | 'fn': c.fn, 98 | 'idx': _this.index 99 | }; 100 | (_this.index > _this.maxIndex) ? (function () { 101 | return _this.maxIndex = _this.index; 102 | })() : void 0; 103 | (_this.stackTrace += (((((logIndent + 'PASS : ') + s) + ' : ') + c.fn) + '\n')); 104 | (_this.logNest -= 1); 105 | return c.fn; 106 | })(); 107 | } catch (err) { 108 | _ret = function (err) { 109 | _this.cache[s][c.idx] = null; 110 | (_this.stackTrace += (((logIndent + 'FAIL : ') + s) + '\n')); 111 | (_this.logNest -= 1); 112 | return _this.noParse(); 113 | }(err); 114 | } 115 | return _ret; 116 | })(); 117 | })(); 118 | }; 119 | Packrat.prototype.noParse = function () { 120 | var _this = this; 121 | return _this.error(('Parse error at:' + _this.index)); 122 | }; 123 | Packrat.prototype.try_ = function (parsers) { 124 | var _this = this; 125 | var ret, i; 126 | i = _this.index; 127 | parsers.do_(function (parser) { 128 | return (ret === undefined) ? (function () { 129 | return (function () { 130 | var _ret; 131 | try { 132 | _ret = (function () { 133 | return ret = parser.call(_this); 134 | })(); 135 | } catch (err) { 136 | _ret = function () { 137 | return _this.index = i; 138 | }(err); 139 | } 140 | return _ret; 141 | })(); 142 | })() : void 0; 143 | }); 144 | return (ret !== undefined) ? ((function () { 145 | return ret; 146 | }))() : (function () { 147 | return _this.noParse(); 148 | })(); 149 | }; 150 | Packrat.prototype.sequence = function (parsers) { 151 | var _this = this; 152 | var ret, i, fail; 153 | i = _this.index; 154 | ret = ''; 155 | fail = false; 156 | parsers.do_(function (parser) { 157 | return fail ? void 0 : (function () { 158 | return (function () { 159 | var _ret; 160 | try { 161 | _ret = (function () { 162 | return (ret += parser.call(_this)); 163 | })(); 164 | } catch (err) { 165 | _ret = function (err) { 166 | _this.index = i; 167 | fail = true; 168 | return _this.noParse(); 169 | }(err); 170 | } 171 | return _ret; 172 | })(); 173 | })(); 174 | }); 175 | return fail ? (function () { 176 | return _this.noParse(); 177 | })() : ((function () { 178 | return ret; 179 | }))(); 180 | }; 181 | Packrat.prototype.optional = function (parser) { 182 | var _this = this; 183 | var ret, i; 184 | i = _this.index; 185 | return (function () { 186 | var _ret; 187 | try { 188 | _ret = (function () { 189 | return parser.call(_this); 190 | })(); 191 | } catch (err) { 192 | _ret = function () { 193 | _this.index = i; 194 | return null; 195 | }(err); 196 | } 197 | return _ret; 198 | })(); 199 | }; 200 | Packrat.prototype.followedBy = function (parser) { 201 | var _this = this; 202 | var f, i; 203 | f = true; 204 | i = _this.index; 205 | (function () { 206 | var _ret; 207 | try { 208 | _ret = (function () { 209 | parser.call(_this); 210 | return f = false; 211 | })(); 212 | } catch (err) { 213 | _ret = function () { 214 | return null; 215 | }(err); 216 | } 217 | return _ret; 218 | })(); 219 | _this.index = i; 220 | return f ? ((function () { 221 | return _this.noParse(); 222 | }))() : (function () { 223 | return null; 224 | })(); 225 | }; 226 | Packrat.prototype.notFollowedBy = function (parser) { 227 | var _this = this; 228 | var f, i; 229 | f = false; 230 | i = _this.index; 231 | (function () { 232 | var _ret; 233 | try { 234 | _ret = (function () { 235 | parser.call(_this); 236 | return f = true; 237 | })(); 238 | } catch (err) { 239 | _ret = function () { 240 | return null; 241 | }(err); 242 | } 243 | return _ret; 244 | })(); 245 | _this.index = i; 246 | return f ? ((function () { 247 | return _this.noParse(); 248 | }))() : (function () { 249 | return null; 250 | })(); 251 | }; 252 | Packrat.prototype.many = function (parser) { 253 | var _this = this; 254 | var a; 255 | return _this.try_([function () { 256 | return _this.many1(function () { 257 | return parser.call(_this); 258 | }); 259 | }, function () { 260 | return ''; 261 | }]); 262 | }; 263 | Packrat.prototype.many1 = function (parser) { 264 | var _this = this; 265 | var v, vs; 266 | v = parser.call(_this); 267 | vs = _this.many(function () { 268 | return parser.call(_this); 269 | }); 270 | return (v + vs); 271 | }; 272 | Packrat.prototype.betweenandaccept = function (start, end, inbetween) { 273 | var _this = this; 274 | var ret; 275 | _this.sequence([start, function () { 276 | return ret = _this.many(function () { 277 | _this.notFollowedBy(end); 278 | return inbetween.call(_this); 279 | }); 280 | }, 281 | end]); 282 | return ret; 283 | }; 284 | Packrat.prototype.anyChar = function () { 285 | var _this = this; 286 | var c; 287 | c = _this.input[_this.index]; 288 | (_this.index += 1); 289 | return (c !== undefined) ? ((function () { 290 | return c; 291 | }))() : (function () { 292 | return _this.noParse(); 293 | })(); 294 | }; 295 | Packrat.prototype.satisfyChar = function (fn) { 296 | var _this = this; 297 | var c; 298 | c = _this.anyChar(); 299 | return (fn(c) !== undefined) ? ((function () { 300 | return c; 301 | }))() : (function () { 302 | return _this.noParse(); 303 | })(); 304 | }; 305 | Packrat.prototype.chr = function (ch) { 306 | var _this = this; 307 | var c; 308 | c = _this.anyChar(); 309 | return (c === ch) ? ((function () { 310 | return c; 311 | }))() : (function () { 312 | return _this.noParse(); 313 | })(); 314 | }; 315 | Packrat.prototype.string = function (str) { 316 | var _this = this; 317 | return (_this.input.substring(_this.index, (_this.index + str.size())) === str) ? ((function () { 318 | (_this.index += str.size()); 319 | return str; 320 | }))() : (function () { 321 | return _this.noParse(); 322 | })(); 323 | }; 324 | Packrat.prototype.regex = function (regex) { 325 | var _this = this; 326 | var rc, match; 327 | rc = regex.exec(_this.input.substring(_this.index)); 328 | return rc.isKindOf(Array) ? ((function () { 329 | match = rc[0]; 330 | (_this.index += match.size()); 331 | return match; 332 | }))() : (function () { 333 | console.log('regexFalse'); 334 | return _this.noParse('regex'); 335 | })(); 336 | }; 337 | Packrat.prototype.toParser = function (str) { 338 | var _this = this; 339 | return function () { 340 | return _this.string(str); 341 | }; 342 | }; 343 | Packrat.prototype.p = function (s) { 344 | var _this = this; 345 | console.log(s); 346 | return s; 347 | }; 348 | module.exports = Packrat; 349 | return Packrat; 350 | }).call(this); -------------------------------------------------------------------------------- /src/js/production/statement.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | "use strict"; 3 | var Class; 4 | Class = require('./class'); 5 | var Statement; 6 | Statement = function () { 7 | if (this.init) { 8 | this.init.apply(this, arguments); 9 | } 10 | }; 11 | Statement.__super = Class.prototype; 12 | Statement.prototype = new Class(); 13 | Statement.prototype.statement = function () { 14 | var _this = this; 15 | return _this.cacheaParser('statement', function () { 16 | var ret, vd; 17 | ret = ''; 18 | _this.skipSpace(); 19 | vd = _this.optional(function () { 20 | return _this.variableDeclaration(); 21 | }); 22 | (vd !== null) ? (function () { 23 | return (ret += vd); 24 | })() : void 0; 25 | _this.skipSpace(); 26 | (ret += _this.many(function () { 27 | var a; 28 | a = _this.statementable(); 29 | _this.skipSpace(); 30 | _this.chr('.'); 31 | _this.skipSpace(); 32 | _this.followedBy(function () { 33 | return _this.statementable(); 34 | }); 35 | return (a + '; '); 36 | })); 37 | (ret += (function () { 38 | var _ret; 39 | try { 40 | _ret = (function () { 41 | return (('return ' + _this.expression()) + ';'); 42 | })(); 43 | } catch (err) { 44 | _ret = function () { 45 | var st; 46 | st = (function () { 47 | var _ret; 48 | try { 49 | _ret = (function () { 50 | return (_this.statementable() + ';'); 51 | })(); 52 | } catch (err) { 53 | _ret = function () { 54 | return ''; 55 | }(err); 56 | } 57 | return _ret; 58 | })(); 59 | return (st + 'return null;'); 60 | }(err); 61 | } 62 | return _ret; 63 | })()); 64 | _this.skipSpace(); 65 | _this.optional(function () { 66 | return _this.chr('.'); 67 | }); 68 | return ret; 69 | }); 70 | }; 71 | Statement.prototype.statementable = function () { 72 | var _this = this; 73 | return _this.cacheaParser('statementable', function () { 74 | return _this.try_([function () { 75 | return _this.classHeader(); 76 | }, function () { 77 | return _this.instanceMethod(); 78 | }, function () { 79 | return _this.expression(); 80 | }]); 81 | }); 82 | }; 83 | Statement.prototype.variableDeclaration = function () { 84 | var _this = this; 85 | return _this.cacheaParser('variableDeclaration', function () { 86 | var ret; 87 | ret = 'var '; 88 | _this.skipSpace(); 89 | _this.verticalBar(); 90 | (ret += _this.many1(function () { 91 | _this.skipSpace(); 92 | return (_this.variable() + ', '); 93 | }).replace(/,\s$/, '; ')); 94 | _this.skipSpace(); 95 | _this.verticalBar(); 96 | return ret; 97 | }); 98 | }; 99 | module.exports = Statement; 100 | return Statement; 101 | }).call(this); -------------------------------------------------------------------------------- /src/main-dev.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | (function () { 4 | 'use strict'; 5 | 6 | var LittleSmallscript, fs, optimist, argv, readline, rl, help; 7 | 8 | LittleSmallscript = require("./js/development/littlesmallscript"); 9 | 10 | fs = require('fs'); 11 | 12 | optimist = require('optimist'); 13 | argv = optimist 14 | .usage('node littlesmallscript') 15 | .alias('h', 'help') 16 | .describe('h', 'show help message') 17 | .alias('c', 'compile') 18 | .describe('c', 'compile to JavaScript and save as .js files') 19 | .alias('i', 'interactive') 20 | .describe('i', 'run an interactive LittleSmallscript REPL') 21 | .alias('p', 'print') 22 | .describe('p', 'print out the compiled JavaScript') 23 | .describe('packed', 'output without prettyprint') 24 | .argv; 25 | 26 | function interactiveShell () { 27 | require('./prelude.js'); //prototype extension 28 | 29 | readline = require('readline'), 30 | rl = readline.createInterface(process.stdin, process.stdout); 31 | 32 | rl.setPrompt("LittleSmallscript> "); 33 | rl.prompt(); 34 | 35 | rl.on("line", function(input) { 36 | try { 37 | var js = new LittleSmallscript().initWithInputandOptions(input, {prettyprint:true}).toJS(); 38 | console.log(js+'\n'); 39 | console.log(eval(js)+'\n'); 40 | } catch (err) { 41 | console.log(err.message || err.type || err+'\n'); 42 | console.log(err.partialjs); 43 | } 44 | rl.prompt(); 45 | }); 46 | } 47 | 48 | help = 49 | "\n \ 50 | Usage: littlesmallscript [options] path/to/script.st\n\n \ 51 | -c, --compile compile to JavaScript and save as .js files\n \ 52 | -i, --interactive run an interactive LittleSmallscript REPL\n \ 53 | -h, --help display this help message\n \ 54 | -p, --print print out the compiled JavaScript\n \ 55 | "; 56 | 57 | if (argv.h) return console.log(help); 58 | 59 | if (argv.i) return interactiveShell(); 60 | 61 | return (function () { 62 | var fileName = argv.c || argv.p; 63 | if (! fileName) return console.log(help); 64 | return fs.readFile(fileName, 'utf8', function (err, lssString) { 65 | if (err) throw err; 66 | try { 67 | var js = new LittleSmallscript().initWithInputandOptions(lssString, {prettyprint: true}).toJS(); 68 | if (argv.p) return console.log(js); 69 | fs.writeFile(argv.c.replace(/\.([^\.])+$/, '.js'), js, function (err) { 70 | if (err) throw err; 71 | }); 72 | } catch (err) { 73 | console.log(err.message||err.type||JSON.stringify(err)); 74 | if (err.partialjs) console.log('###partial js###\n'+err.partialjs+'\n#########'); 75 | throw err; 76 | } 77 | }); 78 | })(); 79 | 80 | }).call(this); 81 | -------------------------------------------------------------------------------- /src/st/block.st: -------------------------------------------------------------------------------- 1 | " 2 | parse and compile lexical block 3 | " 4 | | Expression | 5 | Expression := require value:'./expression' 6 | . 7 | Expression 8 | subclass:#Block 9 | variables:#() 10 | . 11 | !Block 12 | block | dst_tmpl | 13 | dst_tmpl := 'function (%parameters%) { %body% }'. 14 | self cache: #block aParser: [| parameters body | 15 | self blockStart. 16 | parameters := self blockHead. 17 | body := self optional: [self statement]. 18 | self blockEnd. 19 | self template: dst_tmpl apply: #{#parameters:parameters #body:body} 20 | ] 21 | !. 22 | !Block 23 | blockParameters 24 | self cache: #blockParameters aParser: [| vars | 25 | vars := ''. 26 | self skipSpace. 27 | self many: [ 28 | self colon. 29 | vars += (self variable + ', '). 30 | self skipSpace 31 | ]. 32 | vars sli: 0 ce: -2 33 | ] 34 | !. 35 | !Block 36 | blockHead 37 | self cache: #blockHead aParser: [ 38 | self optional: [|params| 39 | self skipSpace. 40 | params := self blockParameters. 41 | params size > 0 ifTrue: [ self verticalBar ]. 42 | self skipSpace. 43 | params 44 | ] 45 | ] 46 | !. 47 | module at: #exports put: Block 48 | . 49 | Block -------------------------------------------------------------------------------- /src/st/class.st: -------------------------------------------------------------------------------- 1 | "class definition syntax" 2 | | Block | 3 | Block := require value:'./block' 4 | . 5 | Block 6 | subclass: #Class 7 | variables: #( 8 | #instanceVariables 9 | #currentClass 10 | ) 11 | . 12 | !Class 13 | init 14 | instanceVariables := #{}. 15 | currentClass := null 16 | !. 17 | "Animal subclass: #Snake variables: #(#name #colour #awake)" 18 | !Class 19 | classHeader | dst_tmpl | 20 | dst_tmpl := 21 | 'var %className%; 22 | %className% = function () { %variableInitialization%if (this.init) { this.init.apply(this, arguments); } }; 23 | %className%.__super = %superClass%.prototype; 24 | %className%.prototype = new %superClass%()'. 25 | self cache: #classHeader aParser: [| className superClass variables v_init | 26 | self optional: [self chr: '+']. "LSt compatibility" 27 | superClass := self variable. 28 | self skipSpace. 29 | self string: 'subclass:'. 30 | self skipSpace. 31 | className := self variablableStringContent. 32 | self skipSpace. 33 | self string: 'variables:'. 34 | self skipSpace. 35 | variables := self instanceVariableArray. 36 | instanceVariables at: className put: #(). "save variables for this class" 37 | v_init := variables inject: '' into: [:a :b | 38 | (instanceVariables at: className) push: a. 39 | b + 'this.' + a + ' = null; ' 40 | ]. 41 | self template: dst_tmpl apply: #{ 42 | #className: className 43 | #superClass: superClass 44 | #variableInitialization: v_init 45 | } 46 | ] 47 | !. 48 | "#(#a #b) -> ['a', 'b']" 49 | !Class 50 | instanceVariableArray 51 | self cache:#instanceVariableArray aParser:[| variables | 52 | variables := #(). 53 | self arrayStart. 54 | self many: [| v | 55 | self skipSpace. 56 | v := self variablableStringContent. 57 | variables push: v. 58 | self skipSpace. 59 | self optional: [self chr: ',']. 60 | self skipSpace. 61 | v 62 | ]. 63 | self closeParen. 64 | variables 65 | ] 66 | !. 67 | !Class 68 | variablableStringContent 69 | self cache:#variablableStringContent aParser:[ 70 | self try_: #( 71 | [self chr: '#'. self variable], 72 | [self between: [self apostrophe] and: [self apostrophe] accept: [self variable]] 73 | ) 74 | ] 75 | !. 76 | " 77 | !Snake 78 | setName: aname 79 | name := aname. 80 | name 81 | ! 82 | " 83 | !Class 84 | instanceMethod | method_tmpl | 85 | method_tmpl := '%className%.prototype.%methodName% = function (%args%) { var _this = this; %methodBody% }'. 86 | self cache:#instanceMethod aParser:[| className methodHead methodBody | 87 | self exclamation. 88 | self skipSpace. 89 | className := self variable. 90 | self skipSpace. 91 | methodHead := self methodHead. "#{}" 92 | self skipSpace. 93 | self setCurrentClass: className. "set the current class" 94 | methodBody := self statement. "change me" 95 | self setCurrentClass: null. "empty the current class" 96 | self skipSpace. 97 | self exclamation. 98 | self template: method_tmpl apply: #{ 99 | #className: className 100 | #methodName: methodHead at:#name 101 | #args: methodHead at:#args 102 | #methodBody: methodBody 103 | } 104 | ] 105 | !. 106 | !Class 107 | methodHead 108 | self cache:#methodHead aParser:[| methodName args | 109 | methodName := ''. 110 | args := #(). 111 | self try_: #([ 112 | self many1: [ 113 | methodName += (self keywordSelector sli: 0 ce: -1). "remove colon" 114 | self skipSpace. 115 | args push: self variable. 116 | self skipSpace 117 | ] 118 | ],[ 119 | methodName := self unarySelector 120 | ]). 121 | #{ 122 | #name: methodName 123 | #args: args join: ', ' 124 | } 125 | ] 126 | !. 127 | !Class 128 | setCurrentClass: className 129 | currentClass := className. 130 | className 131 | !. 132 | !Class 133 | instanceVariableP: variableName |v| 134 | (currentClass !== null) 135 | && ((instanceVariables at: currentClass) !== undefined) 136 | && (((instanceVariables at: currentClass) indexOf: variableName) > -1) 137 | !. 138 | module at:#exports put:Class 139 | . 140 | Class -------------------------------------------------------------------------------- /src/st/expression.st: -------------------------------------------------------------------------------- 1 | | LittleParser optimization | 2 | LittleParser := require value:'./littleparser' 3 | . 4 | optimization := require value:'./optimization' 5 | . 6 | LittleParser subclass:#Expression 7 | variables:#(#bundledMethods) 8 | . 9 | !Expression 10 | init 11 | bundledMethods := #() 12 | !. 13 | !Expression 14 | bundleAMethodIfAvailable: methodName 15 | (bundledMethods indexOf: methodName) > -1 && (bundlableMethods bundlable:methodName) 16 | ifTrue: [bundledMethods push: (bundlableMethods bundle:methodName)] 17 | !. 18 | !Expression 19 | expression | tmpl | 20 | tmpl := '%assignments%%cascade%'. 21 | self cache:#expression aParser:[ 22 | | assignments cascade | 23 | assignments := self optional: [self assignments]. 24 | cascade := self cascade. 25 | self template: tmpl apply: #{ 26 | #assignments:assignments #cascade:cascade 27 | } 28 | ] 29 | !. 30 | !Expression 31 | assignments 32 | self cache:#assignments aParser:[ 33 | self many: [| variable | 34 | variable := self extendedVariable. 35 | self skipSpace. 36 | self assignmentArrow. 37 | self skipSpace. 38 | variable + ' = ' 39 | ] 40 | ] 41 | !. 42 | !Expression 43 | cascade | tmpl | 44 | tmpl := 45 | '(function () { var _receiver = %simpleExpression%; %body% return _receiver; })()'. 46 | self cache:#cascade aParser:[| se | 47 | se := self simpleExpression. 48 | self try_: #([ 49 | self skipSpace. 50 | self notFollowedBy: [self semicolon]. 51 | se 52 | ], [| conti | 53 | conti := self many: [| mes | 54 | self skipSpace. 55 | self semicolon. 56 | self skipSpace. 57 | mes := self continuation. 58 | (optimization optimizationAvailable: (mes at:#methodName)) 59 | ifTrue:[ 60 | (optimization op: '_receiver' ti: (mes at:#methodName) mize: (mes at:#args)) + ';' 61 | ] 62 | ifFalse: ['_receiver' + (mes at: #js) + ';'] 63 | ]. 64 | self template: tmpl apply: #{ 65 | #simpleExpression:se 66 | #body:conti 67 | } 68 | ]) 69 | ] 70 | !. 71 | !Expression 72 | simpleExpression: allowedParsers 73 | self cache:#simpleExpression aParser:[| receiver injection | 74 | receiver := injection := self primaryReceiver. 75 | self many:[| mes ret | 76 | mes := self continuation: allowedParsers. 77 | 78 | "optimize if optimization is available" 79 | (optimization optimizationAvailable: (mes at:#methodName)) 80 | ifTrue: [ 81 | injection := optimization op: injection ti: (mes at:#methodName) mize: (mes at:#args) 82 | ] 83 | ifFalse:[ 84 | (mes at:#wrapMe) 85 | ifTrue: [injection := '(' + injection + (mes at:#js) + ')'] 86 | ifFalse: [injection += (mes at:#js)] 87 | ] 88 | ]. 89 | injection 90 | ] 91 | !. 92 | !Expression 93 | continuation: allowedParsers 94 | self cache:#continuation aParser:[ 95 | allowedParsers === undefined ifTrue: [ 96 | allowedParsers := #([self keywordMessage], 97 | [self binaryMessage], 98 | [self unaryMessage])]. 99 | self try_: allowedParsers 100 | ] 101 | !. 102 | !Expression 103 | keywordMessage 104 | self cache:#keywordMessage aParser:[| methodName args | 105 | methodName := ''. 106 | args := #(). 107 | self many1: [ 108 | self skipSpace. 109 | methodName += (self keywordSelector rep:':' lace:''). 110 | self skipSpace. 111 | "unary and binary messages are ranked higher" 112 | args push: (self simpleExpression: #([self binaryMessage], [self unaryMessage])). 113 | self skipSpace 114 | ]. 115 | #{ 116 | #js: '.' + methodName + '(' + (args join: ', ') + ')', 117 | #wrapMe: false, 118 | #methodName: methodName, 119 | #args: args 120 | } 121 | ] 122 | !. 123 | !Expression 124 | binaryMessage 125 | self cache:#binaryMessage aParser:[| operator argument | 126 | self skipSpace. 127 | operator := self operator. 128 | self skipSpace. 129 | "unary messages are ranked higher" 130 | argument := self simpleExpression: #([self unaryMessage]). 131 | #{ 132 | #js: ' ' + operator + ' ' + argument, 133 | #wrapMe: true, 134 | #methodName: operator, 135 | #args: #(argument) 136 | } 137 | ] 138 | !. 139 | !Expression 140 | unaryMessage 141 | self cache:#unaryMessage aParser:[| unarySelector | 142 | self skipSpace. 143 | unarySelector := self unarySelector. 144 | #{ 145 | #js: '.' + unarySelector + '()', 146 | #wrapMe: false, 147 | #methodName: unarySelector, 148 | #args: #() 149 | } 150 | ] 151 | !. 152 | !Expression 153 | primary 154 | self cache:#primary aParser:[ 155 | self try_: #( 156 | [self extendedVariable], 157 | [self literal], 158 | [self block], 159 | [self primitive], 160 | [self between: [self chr: '('. self skipSpace] 161 | and: [self skipSpace. self chr: ')'] 162 | accept: [self cascade] 163 | ] 164 | ) 165 | ] 166 | !. 167 | !Expression 168 | primaryReceiver 169 | self cache:#primaryReceiver aParser:[ 170 | self try_: #([| num | 171 | num := self numberLiteral. 172 | self followedBy: [ 173 | self try_: #([self keywordMessage], [self unaryMessage]) 174 | ]. 175 | '(' + num + ')' 176 | ], [ 177 | self followedBy: [ 178 | self block. 179 | self skipSpace. 180 | self try_: #([self keywordMessage], [self unaryMessage]) 181 | ]. 182 | '(' + self block + ')' 183 | ], 184 | [self primary]) 185 | ] 186 | !. 187 | !Expression 188 | primitive 189 | self cache:#primitive aParser:[ 190 | self skipSpace. 191 | self between: [ 192 | self chr: '<'. 193 | self notFollowedBy: [self chr: '-']. 194 | '<' 195 | ] and: [ 196 | self chr: '>' 197 | ] accept: [self anyChar] 198 | ] 199 | !. 200 | !Expression 201 | operator | p | 202 | p := [:str | [self string: str]]. 203 | self cache:#operator aParser:[| op | 204 | self skipSpace. 205 | op := self try_: #( 206 | p value: '+=', 207 | p value: '-=', 208 | p value: '*=', 209 | p value: '/=', 210 | p value: '+', 211 | p value: '-', 212 | p value: '*', 213 | p value: '/', 214 | p value: '%', 215 | p value: '===', 216 | p value: '!==', 217 | p value: '<=', 218 | p value: '>=', 219 | p value: '<', 220 | p value: '>', 221 | p value: '^', 222 | p value: '&&', 223 | p value: '||' 224 | ) 225 | ] 226 | !. 227 | module at: #exports put: Expression 228 | . 229 | Expression 230 | -------------------------------------------------------------------------------- /src/st/littleparser.st: -------------------------------------------------------------------------------- 1 | " 2 | Small parsers that are used to implement the greater parsers 3 | " 4 | | Packrat | 5 | Packrat := require value:'./packrat' 6 | . 7 | Packrat 8 | subclass:#LittleParser 9 | variables:#() 10 | . 11 | !LittleParser 12 | space 13 | self cache: #space aParser: [ 14 | self regex: (RegExp new: '^[\\s\\n\\t]+') 15 | ] 16 | !. 17 | !LittleParser 18 | blockStart 19 | self cache: #blockStart aParser: [self chr: '['] 20 | !. 21 | !LittleParser 22 | blockEnd 23 | self cache: #blockEnd aParser: [self chr: ']'] 24 | !. 25 | !LittleParser 26 | verticalBar 27 | self cache: #verticalBar aParser: [self chr: '|'] 28 | !. 29 | !LittleParser 30 | colon 31 | self cache: #colon aParser: [self chr: ':'] 32 | !. 33 | !LittleParser 34 | semicolon 35 | self cache: #semicolon aParser: [self chr: ';'] 36 | !. 37 | !LittleParser 38 | assignmentArrow 39 | self cache: #assignmentArrow aParser: [ 40 | self try_: #( 41 | [self string: ':='], 42 | [self string: '<-'] 43 | ) 44 | ] 45 | !. 46 | !LittleParser 47 | apostrophe 48 | self cache: #apostrophe aParser: [self chr: <"'">] 49 | !. 50 | !LittleParser 51 | arrayStart 52 | self cache: #arrayStart aParser: [ 53 | self string: '#('. 54 | self skipSpace 55 | ] 56 | !. 57 | !LittleParser 58 | closeParen 59 | self cache: #closeParen aParser: [self chr: ')'] 60 | !. 61 | !LittleParser 62 | hashStart 63 | self cache: #hashStart aParser:[self string: '#{'] 64 | !. 65 | !LittleParser 66 | hashEnd 67 | self cache: #hashEnd aParser: [self chr: '}'] 68 | !. 69 | !LittleParser 70 | exclamation 71 | self cache:#exclamation aParser:[self chr: '!'] 72 | !. 73 | !LittleParser 74 | variable 75 | self cache: #variable aParser: [ 76 | self regex: (RegExp new: '^[a-zA-Z_$][a-zA-Z0-9_$]*') 77 | ] 78 | ! 79 | . 80 | !LittleParser 81 | extendedVariable 82 | self cache: #extendedVariable aParser:[| v | 83 | v := self regex: (RegExp new: '^[a-zA-Z_$][a-zA-Z0-9_$]*'). 84 | v === 'self' ifTrue: ['_this'] 85 | ifFalse: [ 86 | "if it is instance variable, prefix it with 'this'" 87 | (self instanceVariableP: v) ifTrue: [v := '_this.'+v]. 88 | v 89 | ] 90 | ] 91 | !. 92 | !LittleParser 93 | keywordSelector 94 | self cache:#keywordSelector aParser:[ 95 | self sequence: #([self variable], [self colon]) 96 | ] 97 | !. 98 | !LittleParser 99 | unarySelector 100 | self cache:#unarySelector aParser:[| sel | 101 | sel := self variable. 102 | self notFollowedBy: [self colon]. 103 | sel 104 | ] 105 | !. 106 | !LittleParser 107 | explicitReturn 108 | self cache:#explicitReturn aParser:[ 109 | self chr: '^' 110 | ] 111 | !. 112 | !LittleParser 113 | commentQuote 114 | self cache: #commentQuote aParser:[ 115 | self chr: '\"' 116 | ] 117 | !. 118 | !LittleParser 119 | comment 120 | self cache:#comment aParser:[| comment | 121 | comment := self between: [self commentQuote] and: [self commentQuote] accept: [self anyChar]. 122 | self optional: [self space]. 123 | comment 124 | ] 125 | !. 126 | !LittleParser 127 | skipSpace 128 | self cache:#skipSpace aParser:[ 129 | self optional: [self space]. 130 | self many: [self comment] 131 | ] 132 | !. 133 | !LittleParser 134 | literal 135 | self cache: #literal aParser: [ 136 | self try_: #( 137 | [self numberLiteral], 138 | [self stringLiteral], 139 | [self symbolLiteral], 140 | [self arrayLiteral], 141 | [self hashLiteral], 142 | [self block] 143 | ) 144 | ] 145 | !. 146 | !LittleParser 147 | numberLiteral 148 | self cache: #numberLiteral aParser: [ 149 | self regex: (RegExp new: '^-?[0-9]+(\\.?[0-9]+)?') 150 | ] 151 | !. 152 | !LittleParser 153 | stringLiteral 154 | self cache: #stringLiteral aParser: [ 155 | '\'' + ((self 156 | between: [self apostrophe] 157 | and: [self apostrophe] 158 | accept: [|c| 159 | c := self anyChar. 160 | c === '\\' ifTrue:[c + self anyChar] 161 | ifFalse:[c] 162 | ]) rep: lace:'\\n') + '\'' 163 | ] 164 | !. 165 | !LittleParser 166 | symbolLiteral 167 | self cache: #symbolLiteral aParser:[ 168 | self chr: '#'. 169 | '\'' + self variable + '\'' 170 | ] 171 | !. 172 | !LittleParser 173 | arrayLiteral |args| 174 | self cache:#arrayLiteral aParser:[ 175 | args := #(). 176 | self arrayStart. 177 | self skipSpace. 178 | self many: [ 179 | args push: self expression. 180 | self skipSpace. 181 | self optional: [self chr: ',']. 182 | self skipSpace 183 | ]. 184 | self closeParen. 185 | '[' + (args join: ', ') + ']' 186 | ] 187 | !. 188 | !LittleParser 189 | hashLiteral 190 | self cache:#hashLiteral aParser:[| ret | 191 | ret := ''. 192 | self hashStart. 193 | ret += '{'. 194 | ret += ((self many: [| key val | 195 | self skipSpace. 196 | key := self try_: #([self stringLiteral], [self numberLiteral], [self symbolLiteral]). 197 | self skipSpace. 198 | self colon. 199 | self skipSpace. 200 | val := self expression. 201 | self skipSpace. 202 | self optional: [self chr: ',']. 203 | key + ': ' + val + ',' 204 | ]) sli: 0 ce:-1). 205 | self skipSpace. 206 | self hashEnd. 207 | ret += '}'. 208 | ret 209 | ] 210 | !. 211 | !LittleParser 212 | template: template apply: hashmap | dest_str | 213 | dest_str := template. 214 | hashmap do: [:it :key | 215 | (it === null) || (it === undefined) ifTrue: [it := '']. 216 | dest_str := dest_str rep: (RegExp ne: '%'+key+'%' w: 'g') lace: it 217 | ]. 218 | dest_str 219 | !. 220 | module at: #exports put: LittleParser 221 | . 222 | LittleParser -------------------------------------------------------------------------------- /src/st/littlesmallscript.st: -------------------------------------------------------------------------------- 1 | | Statement | 2 | Statement := require value:'./statement' 3 | . 4 | Statement 5 | subclass:#LittleSmallscript 6 | variables:#( 7 | #input 8 | #options 9 | #beautifyOption 10 | #cache 11 | ) 12 | . 13 | !LittleSmallscript 14 | initWithInput:text andOptions:opt 15 | input := text. 16 | options := opt. 17 | cache := #{}. 18 | beautifyOption := #{ 19 | #indent_size: 2 20 | #indent_char: ' ' 21 | #jslint_happy: true 22 | }. 23 | self 24 | !. 25 | !LittleSmallscript 26 | onError:err | line rest token | 27 | line := [((input sub:0 string:self getMaxIndex) match:) size + 1] 28 | tryCatch: [0]. 29 | rest := input substring: self getMaxIndex. 30 | token := rest sub:0 string:(rest search:). 31 | console log: 'Parse error on line '+line+'. Unexpected '+token+'.'. 32 | console log: '===================================================='. 33 | "console log: self getStackTrace" 34 | !. 35 | !LittleSmallscript 36 | toJS | wrapTmpl js beautifyOption err | 37 | err := false. 38 | wrapTmpl := '(function () { \"use strict\"; %statement% }).call(this);'. 39 | [ 40 | js := self template: wrapTmpl apply: #{#statement: self statement} 41 | ] tryCatch: [err := true. self onError]. 42 | err ifFalse: [ 43 | self getIndex < input size ifTrue: [err := true. self onError: null] 44 | ]. 45 | err ifFalse:[ 46 | options && (options at:#prettyprint) ifTrue: [ 47 | (require value:'../../../lib/beautify.js') js_: js beautify: beautifyOption 48 | ] ifFalse: [js] 49 | ] 50 | !. 51 | module at:#exports put:LittleSmallscript 52 | . 53 | LittleSmallscript -------------------------------------------------------------------------------- /src/st/optimization.st: -------------------------------------------------------------------------------- 1 | | LP template optimTmpl optimize canUseDotNotation optimizationAvailable | 2 | LP := require value:'./littleparser' 3 | . 4 | template := [:template :hashmap | | dest_str | 5 | dest_str := template. 6 | hashmap do: [:it :key | 7 | (it === null) || (it === undefined) ifTrue: [it := '']. 8 | dest_str := dest_str rep: (RegExp ne: '%'+key+'%' w: 'g') lace: it 9 | ]. 10 | dest_str 11 | ] 12 | . 13 | optimTmpl := #{ 14 | 'at' : '%receiver%[%arg1%]', 15 | 'atput' : '%receiver%[%arg1%] = %arg2%', 16 | 'dot' : '%receiver%.%arg1%', 17 | 'dotput' : '%receiver%.%arg1% = %arg2%', 18 | 'do' : '%receiver%.do_(%arg1%)', 19 | 'value' : '%receiver%(%arg1%)', 20 | 'valuevalue' : '%receiver%(%args%)', 21 | 'valuevaluevalue' : '%receiver%(%args%)', 22 | 'valuevaluevaluevalue' : '%receiver%(%args%)', 23 | 'valuevaluevaluevaluevalue' : '%receiver%(%args%)', 24 | 'ifTrue' : '%receiver% ? (%arg1%)() : void 0', 25 | 'ifFalse' : '%receiver% ? void 0 : (%arg1%)()', 26 | 'ifTrueifFalse' : '%receiver% ? (%arg1%)() : (%arg2%)()', 27 | 'ifFalseifTrue' : '%receiver% ? (%arg2%)() : (%arg1%)()', 28 | 'tryCatch' : 29 | '(function () { var _ret; try { _ret = %receiver%(); } catch (err) { _ret = %arg1%(err); } return _ret; })()', 30 | 'tryCatchFinally' : 31 | '(function () { var _ret; try { _ret = %receiver%(); } catch (err) { _ret = %arg1%(err); } finally { _ret = %arg2%(); } return _ret; })()', 32 | 'new' : 'new %receiver%(%args%)', 33 | 'super' : '%receiver%.__super.%arg1%.call(_this)', 34 | 'superarguments' : '%receiver%.__super.%arg1%.apply(_this, %arg2%)', 35 | } 36 | . 37 | canUseDotNotation := [:str| | v identifier | 38 | v := LP new: str. 39 | [ 40 | identifier := v between: [v chr: <"'">] and: [v chr: <"'">] accept: (v at:#variable) 41 | ] tryCatch: []. 42 | v getIndex === v getInputLength ifTrue: [identifier] ifFalse: [null] 43 | ] 44 | . 45 | optimize := [:receiver :methodName :args | 46 | "special cases" 47 | (methodName === 'at') || (methodName === 'dot') ifTrue: [| identifier | 48 | identifier := (canUseDotNotation value: (args at: 0)). 49 | identifier !== null ifTrue: [ 50 | args at: 0 put: identifier. 51 | methodName := 'dot' 52 | ] 53 | ]. 54 | (methodName === 'atput') || (methodName === 'dotput') ifTrue: [| identifier | 55 | identifier := (canUseDotNotation value: (args at: 0)). 56 | identifier !== null ifTrue: [ 57 | args at: 0 put: identifier. 58 | methodName := 'dotput' 59 | ] 60 | ]. 61 | (methodName === 'super') || (methodName === 'superarguments') ifTrue: [ 62 | args at:0 put: (canUseDotNotation value: (args at: 0)) 63 | ]. 64 | "end" 65 | 66 | template value: (optimTmpl at: methodName) value: #{ 67 | 'receiver' : receiver, 68 | 'args' : args join: ', ', 69 | 'arg1' : args at: 0, 70 | 'arg2' : args at: 1, 71 | 'arg3' : args at: 2 72 | } 73 | ] 74 | . 75 | optimizationAvailable := [:methodName | 76 | optimTmpl hasOwnProperty: methodName 77 | ] 78 | . 79 | exports at:#optimize put:optimize. 80 | exports at:#optimizationAvailable put:optimizationAvailable -------------------------------------------------------------------------------- /src/st/packrat.st: -------------------------------------------------------------------------------- 1 | require value:'../../../bin/prelude' 2 | . 3 | !Number 4 | timesString: str | ret | 5 | ret := ''. 6 | self timesRepeat:[:i| ret += str]. 7 | ret 8 | !. 9 | Object 10 | subclass: #Packrat 11 | variables: #( 12 | #input 13 | #index 14 | #cache 15 | #maxIndex 16 | #logNest 17 | #stackTrace 18 | ) 19 | . 20 | !Packrat 21 | init: text 22 | input := text. 23 | index := 0. 24 | cache := #{}. 25 | maxIndex := 0. 26 | logNest := -1. 27 | stackTrace := '' 28 | !. 29 | !Packrat 30 | getIndex 31 | index 32 | !. 33 | !Packrat 34 | getMaxIndex 35 | maxIndex 36 | !. 37 | !Packrat 38 | getInputLength 39 | input size 40 | !. 41 | !Packrat 42 | getStackTrace 43 | stackTrace 44 | !. 45 | !Packrat 46 | cache: s aParser: fn | c slot logIndent | 47 | "Save results of parsing at the index for efficiency" 48 | fn := (fn !== undefined) ifTrue:[fn] ifFalse:[[]]. 49 | c := #{}. 50 | 51 | "debug feature" 52 | logNest += 1. 53 | logIndent := logNest timesString: ' '. 54 | stackTrace += (logIndent + 'ENTER : ' + s + ' : ' + (input substring: index) + '\n'). 55 | 56 | [(cache at: s) === undefined ifTrue: (cache at: s put: #{})]tryCatch:[ 57 | cache at: s put: #{} 58 | ]. 59 | slot := (cache at: s) at: index. 60 | (slot !== undefined) && (slot !== null) ifTrue: [ 61 | c := slot. 62 | index := c at: #idx. 63 | 64 | "debug feature" 65 | index > maxIndex ifTrue: [maxIndex := index]. "save max index for error message" 66 | stackTrace += (logIndent + 'CACHED: ' + s + ' : ' + (c at: #fn) + '\n'). 67 | logNest -= 1. 68 | 69 | c at: #fn 70 | ] ifFalse: [ 71 | [ 72 | c at:#idx put:index. 73 | c at:#fn put:(fn call: self). 74 | (cache at: s) at: (c at:#idx) put: #{#fn: c at:#fn, #idx: index}. 75 | 76 | "debug feature" 77 | index > maxIndex ifTrue: [maxIndex := index]. "save max index for error message" 78 | stackTrace += (logIndent + 'PASS : ' + s + ' : ' + (c at:#fn) + '\n'). 79 | logNest -= 1. 80 | 81 | c at:#fn 82 | ] tryCatch: [:err| 83 | (cache at: s) at: (c at:#idx) put: null. 84 | 85 | "debug feature" 86 | stackTrace += (logIndent + 'FAIL : ' + s + '\n'). 87 | logNest -= 1. 88 | 89 | self noParse 90 | ] 91 | ] 92 | !. 93 | !Packrat 94 | noParse 95 | "throw an exception" 96 | self error: ('Parse error at:' + index) 97 | !. 98 | !Packrat 99 | try_: parsers | ret i | 100 | "OK if one of the parser matches. Returns the first match." 101 | i := index. 102 | parsers do: [:parser | 103 | ret === undefined ifTrue: [ 104 | [ret := parser call: self] tryCatch: [ index := i ]]]. 105 | ret !== undefined ifTrue: [ret] ifFalse: [self noParse] 106 | !. 107 | !Packrat 108 | sequence: parsers | ret i fail | 109 | "OK if all the parsers match in order." 110 | i := index. 111 | ret := ''. 112 | fail := false. 113 | parsers do: [:parser | 114 | fail ifFalse: [ 115 | [ 116 | ret += (parser call: self) 117 | ] tryCatch: [:err | 118 | index := i. "backtrack" 119 | fail := true. 120 | self noParse 121 | ]]]. 122 | fail ifFalse: [ret] ifTrue: [self noParse] 123 | !. 124 | !Packrat 125 | optional: parser | ret i | 126 | "Never fails. When not matched return an empty string." 127 | i := index. 128 | [ parser call: self ] tryCatch: [ index := i. null ] 129 | !. 130 | !Packrat 131 | followedBy: parser | f i | 132 | "OK if the parser matches." 133 | f := true. i := index. 134 | [ parser call: self. f := false ] tryCatch: []. 135 | index := i. "this method does not consume the input" 136 | f ifTrue: [self noParse] ifFalse: [null] 137 | !. 138 | !Packrat 139 | notFollowedBy: parser | f i | 140 | "OK if the parser does not match" 141 | f := false. i := index. 142 | [ parser call: self. f := true ] tryCatch: []. 143 | index := i. "this method does not consume the input" 144 | f ifTrue: [self noParse] ifFalse: [null] 145 | !. 146 | !Packrat 147 | many: parser |a| 148 | "Repeat until the parser fails. Return an empty string if no-match" 149 | self try_: #( 150 | [ self many1: [parser call: self] ], 151 | [''] 152 | ) 153 | !. 154 | !Packrat 155 | many1: parser | v vs | 156 | "Same as many butrequires one or more matches" 157 | v := parser call: self. 158 | vs := self many: [ parser call: self ]. 159 | v + vs 160 | !. 161 | !Packrat 162 | between: start and: end accept: inbetween | ret | 163 | "Match 'inbetween' wrapped with 'start' and 'end'" 164 | self sequence: #( 165 | start, 166 | [ret := self many: [ 167 | self notFollowedBy: end. 168 | inbetween call: self 169 | ]], 170 | end 171 | ). 172 | ret 173 | !. 174 | !Packrat 175 | anyChar | c | 176 | "Any character" 177 | c := input at: index. 178 | index += 1. 179 | c !== undefined ifTrue: [c] ifFalse: [self noParse] 180 | !. 181 | !Packrat 182 | satisfyChar: fn | c | 183 | "Character that satisfies the parser given" 184 | c := self anyChar. 185 | ((fn value: c) !== undefined) ifTrue: [c] ifFalse: [self noParse] 186 | !. 187 | !Packrat 188 | chr: ch | c | 189 | "Matches the given character" 190 | c := self anyChar. 191 | c === ch ifTrue: [c] ifFalse: [self noParse] 192 | !. 193 | !Packrat 194 | string: str 195 | "Matches the given string" 196 | (input sub: index string: (index + str size)) 197 | === str ifTrue: [index += str size. str] ifFalse: [self noParse] 198 | !. 199 | !Packrat 200 | regex: regex | rc match | 201 | "Matches the given regex" 202 | rc := regex exec: (input substring: index). 203 | (rc isKindOf: Array) ifTrue: [ 204 | match := rc at: 0 . 205 | index += match size. 206 | match 207 | ] ifFalse: [ 208 | console log: #regexFalse. 209 | self noParse: #regex 210 | ] 211 | !. 212 | !Packrat 213 | toParser: str 214 | "Turn the string into parser" 215 | [self string: str] 216 | ! 217 | . 218 | !Packrat 219 | p: s 220 | "p" 221 | console log: s. 222 | s 223 | !. 224 | module at: #exports put: Packrat 225 | . 226 | Packrat 227 | -------------------------------------------------------------------------------- /src/st/run.sh: -------------------------------------------------------------------------------- 1 | echo packrat && 2 | node ../../bin/main.js -c packrat.st && 3 | 4 | echo littleparser && 5 | node ../../bin/main.js -c littleparser.st && 6 | 7 | echo expression && 8 | node ../../bin/main.js -c expression.st && 9 | 10 | echo blockparser && 11 | node ../../bin/main.js -c block.st && 12 | 13 | echo class && 14 | node ../../bin/main.js -c class.st && 15 | 16 | echo statement && 17 | node ../../bin/main.js -c statement.st && 18 | 19 | echo optimization && 20 | node ../../bin/main.js -c optimization.st && 21 | 22 | echo littlesmallscript && 23 | node ../../bin/main.js -c littlesmallscript.st 24 | 25 | mv *.js ../js/development/ 26 | -------------------------------------------------------------------------------- /src/st/statement.st: -------------------------------------------------------------------------------- 1 | | Class | 2 | Class := require value:'./class' 3 | . 4 | Class subclass: #Statement variables: #() 5 | . 6 | !Statement 7 | statement 8 | self cache:#statement aParser:[| ret vd | 9 | ret := ''. 10 | self skipSpace. 11 | vd := self optional: [self variableDeclaration]. 12 | vd !== null ifTrue:[ ret += vd ]. 13 | 14 | self skipSpace. 15 | ret += (self many: [| a | 16 | a := self statementable. 17 | self skipSpace. 18 | self chr: '.'. 19 | self skipSpace. 20 | self followedBy: [self statementable]. 21 | 22 | a + '; ' 23 | ]). 24 | ret += ([ 25 | 'return ' + self expression + ';' 26 | ] tryCatch: [| st | 27 | st := [self statementable + ';'] tryCatch: ['']. 28 | st + 'return null;' 29 | ]). 30 | self skipSpace. 31 | self optional: [ self chr: '.' ]. "allow tail period" 32 | 33 | ret 34 | ] 35 | !. 36 | !Statement 37 | statementable 38 | self cache:#statementable aParser:[ 39 | self try_: #( 40 | [self classHeader], 41 | [self instanceMethod], 42 | [self expression] 43 | ) 44 | ] 45 | !. 46 | !Statement 47 | variableDeclaration 48 | self cache:#variableDeclaration aParser:[| ret | 49 | ret := 'var '. 50 | self skipSpace. 51 | self verticalBar. 52 | ret += ((self many1: [ 53 | self skipSpace. 54 | self variable + ', ' 55 | ]) rep: lace: '; '). 56 | self skipSpace. 57 | self verticalBar. 58 | ret 59 | ] 60 | !. 61 | module at:#exports put: Statement 62 | . 63 | Statement -------------------------------------------------------------------------------- /src/test-web.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | lss web 5 | 6 | 7 | Open the console and test out. 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /tests/tests-dev.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | 'use strict'; 3 | 4 | var LittleSmallscript, errors, test, log, lss, p; 5 | LittleSmallscript = require("../src/js/development/littlesmallscript"); 6 | 7 | errors = []; 8 | test = function (result, expected, mes) { 9 | if (result !== expected) { 10 | return log(mes + '\n\tEXPECTED:' + expected + '\n\tPASSED :' + result) 11 | } 12 | console.log('\u001b[32m' + 'GREEN:\t' + mes + '\u001b[0m'); 13 | return true; 14 | }; 15 | log = function (a) { 16 | console.log('\u001b[31m' + 'RED :\t' + a + '\u001b[0m'); 17 | errors.push(a); 18 | }; 19 | lss = function (input) { 20 | return new LittleSmallscript().initWithInputandOptions(input, {}); 21 | }; 22 | p = function (a) { 23 | console.log(a); 24 | return a; 25 | }; 26 | 27 | console.log("=========Packrat========="); 28 | 29 | /* Packrat */ 30 | (function () { 31 | var a, example1 = "| foo | foo := 1 + 2 to: #(1 2 3) size ; do: [:a | a]. foo"; 32 | 33 | //cacheDo 34 | a = lss("111"); 35 | test(a.anyChar(), a.anyChar(), "cacheDo01"); 36 | 37 | //try_ 38 | a = lss(example1); 39 | test(a.try_([ 40 | a.toParser("foo"), 41 | a.toParser("bar"), 42 | a.toParser("|"), 43 | a.toParser("baz") 44 | ]), "|", "try_01") 45 | 46 | a.skipSpace(); 47 | 48 | try { 49 | a.try_([ 50 | a.toParser("1"), 51 | a.toParser("2") 52 | ]); 53 | log("try_02") 54 | } catch (err) {} 55 | 56 | //sequence 57 | a = lss(example1); 58 | test(a.sequence([ 59 | a.verticalBar, 60 | a.space, 61 | a.toParser("foo"), 62 | a.space, 63 | a.verticalBar 64 | ]), "| foo |", "sequence01"); 65 | 66 | //optional 67 | a = lss(example1); 68 | test(a.optional(a.toParser("| foo |")), "| foo |", "optional01") 69 | 70 | test(a.optional(a.toParser("fooobaaaaa")), null, "optional02"); 71 | 72 | //followedBy / notFollowedBy 73 | a = lss(example1); 74 | try { 75 | test(a.followedBy(a.verticalBar), null); 76 | test(a.followedBy(a.verticalBar), null); 77 | } catch (e) { 78 | log("followedBy01"); 79 | } 80 | try { 81 | a.FollowedBy(a.toParser("fooobaaaaa")); 82 | log("followedBy02"); 83 | } catch (e) {} 84 | try { 85 | test(a.notFollowedBy(a.toParser("fooobaaaaa")), null); 86 | test(a.notFollowedBy(a.toParser("fooobaaaaa")), null); 87 | } catch (e) { 88 | log("notFollowedBy01"); 89 | } 90 | try { 91 | a.notFollowedBy(a.verticalBar); 92 | log("notFollowedBy02"); 93 | } catch (e) {} 94 | 95 | //many 96 | a = lss(example1); 97 | test(a.many(a.anyChar), example1, "many01"); 98 | 99 | test(a.many(a.many(a.colon)), "", "many02"); 100 | 101 | //many1 102 | a = lss(example1); 103 | try { 104 | test(a.many1("colon")); 105 | log("many01"); 106 | } catch (e) {} 107 | 108 | //between 109 | a = lss(example1); 110 | test(a.betweenandaccept(a.verticalBar, a.verticalBar, a.anyChar), " foo ", "between01"); 111 | 112 | //anyChar 113 | a = lss(example1); 114 | test(a.anyChar(), "|", "anyChar01"); 115 | 116 | //satisfyChar 117 | a = lss(example1); 118 | test(a.satisfyChar(function (c) { return c, "|"; }), "|", "satisfyChar"); 119 | 120 | //chr 121 | a = lss(example1); 122 | test(a.chr("|"), "|", "chr01"); 123 | 124 | //string 125 | a = lss(example1); 126 | test(a.string("| foo |"), "| foo |", "string01"); 127 | 128 | //regex 129 | a = lss(example1); 130 | test(a.regex(/^\|\s[a-z]+\s\|/), "| foo |", "regex01"); 131 | 132 | //toParser 133 | a = lss(example1); 134 | test(a.toParser("| foo |")(), "| foo |", "toParser01"); 135 | })(); 136 | 137 | console.log("=========LittleParser========="); 138 | 139 | //LittleParser 140 | (function () { 141 | var a; 142 | 143 | //whiteSpace 144 | a = lss(" \t\n "); 145 | test(a.space(), " \t\n ", "space01"); 146 | 147 | //assignmentArrow 148 | a = lss(":= <-"); 149 | test(a.assignmentArrow(), ":=", "assignmentArrow01"); 150 | a.space(); 151 | test(a.assignmentArrow(), "<-", "assignmentArrow02") 152 | 153 | //literal 154 | a = lss("-12.15'foo'#symbol123#(1 2 #(3))#{#a: 1,#b: #{#a: #()}}"); 155 | test((function () { 156 | return a.literal() + a.literal() + a.literal() + a.literal() + a.literal(); 157 | })(), "-12.15'foo''symbol123'[1, 2, [3]]{'a': 1,'b': {'a': []}}", "literal01"); 158 | 159 | a = lss("-12.15'foo'#symbol123#(1 2 #(3))#{#a: 1,#b: #{#a: #()}}"); 160 | test((function () { 161 | return a.numberLiteral() + 162 | a.stringLiteral() + 163 | a.symbolLiteral() + 164 | a.arrayLiteral() + 165 | a.hashLiteral(); 166 | })(), "-12.15'foo''symbol123'[1, 2, [3]]{'a': 1,'b': {'a': []}}", "literal02"); 167 | 168 | //newlines inside strings 169 | test( 170 | lss("'1\n2\n3'").stringLiteral(), 171 | "'1\\n2\\n3'", 172 | 'newlines in strings' 173 | ); 174 | 175 | //backslash escaping 176 | test( 177 | lss("'Foo\\'s name:\\nMike'").stringLiteral(), 178 | "'Foo\\'s name:\\nMike'", 179 | "backslash escaping" 180 | ); 181 | 182 | //variable 183 | a = lss("ab123scdh$_jags#ahj[]"); 184 | test(a.variable(), "ab123scdh$_jags", "variable01"); 185 | 186 | //keywordSelector 187 | a = lss("ah_$akd12:foooo"); 188 | test(a.keywordSelector(), "ah_$akd12:", "keywordSelector01"); 189 | 190 | //unarySelector 191 | a = lss("fooo fooo:"); 192 | test(a.unarySelector(), "fooo", "unarySelector01"); 193 | a.space(); 194 | try { 195 | a.unarySelector(); 196 | log(false, "unarySelector02"); 197 | } catch(e) {} 198 | 199 | //skipSpace 200 | a = lss(' "comment " '); 201 | a.skipSpace() 202 | test(a.index, ' "comment " '.length, "skipSpace01"); 203 | })(); 204 | 205 | console.log("=========BlockParser========="); 206 | 207 | /* BlockParser */ 208 | (function () { 209 | var a; 210 | 211 | //block 212 | a = lss("[] [1] [:foo| foo] [:foo||bar|foo.bar]"); 213 | test((function () { 214 | return a.many(function () { 215 | a.skipSpace(); 216 | return a.block() + "; "; 217 | }); 218 | })(), "function () { return null; }; function () { return 1; }; function (foo) { return foo; }; function (foo) { var bar; foo; return bar; }; ", "block01"); 219 | 220 | //blockParameters 221 | a = lss(" :foo :bar :baz"); 222 | test(a.blockParameters(), "foo, bar, baz", "blockParameters01"); 223 | 224 | //blockHead 225 | a = lss(":foo :bar | 123"); 226 | test(a.blockHead(), "foo, bar", "blockHead01"); 227 | })(); 228 | 229 | console.log("=========Expression========="); 230 | 231 | //Expression 232 | (function () { 233 | var a; 234 | 235 | //expression 236 | test(lss("foo := a kw: b + c sel").expression(), "foo = a.kw((b + c.sel()))", "expression01"); 237 | test(lss("b + a kw: c sel").expression(), "(b + a).kw(c.sel())", "expression02"); 238 | test(lss("c sel + b kw: a").expression(), "(c.sel() + b).kw(a)", "expression03"); 239 | 240 | //assignments 241 | test(lss("foo := bar := 1").assignments(), "foo = bar = ", "assignments01"); 242 | 243 | //cascade 244 | test( 245 | lss("Object new ; foo ; bar: 1").cascade(), 246 | "(function () { var _receiver = new Object(); _receiver.foo();_receiver.bar(1); return _receiver; })()", 247 | "cascade01" 248 | ); 249 | 250 | //simpleExpression 251 | test(lss("b + a kw: c sel").simpleExpression(), "(b + a).kw(c.sel())", "simpleExpression01"); 252 | 253 | //primaryReceiver 254 | test(lss("1").primaryReceiver(), "1", "primaryReceiver01"); 255 | test(lss("1 to: 5").primaryReceiver(), "(1)", "primaryReceiver02"); 256 | test(lss("[]").primaryReceiver(), "function () { return null; }", "primaryReceiver03"); 257 | test(lss("[] tryCatch: []").primaryReceiver(), "(function () { return null; })", "primaryReceiver04"); 258 | //primitive 259 | test(lss("").primaryReceiver(), "alert(1)", "primitive01"); 260 | 261 | //optimization in cascade 262 | test( 263 | lss("Object new ; at:#a put:1 ; at:#b put: 2").cascade(), 264 | "(function () { var _receiver = new Object(); _receiver.a = 1;_receiver.b = 2; return _receiver; })()", 265 | "cascade optimization" 266 | ); 267 | 268 | })(); 269 | 270 | console.log("=========Statement========="); 271 | 272 | //Statement 273 | (function () { 274 | var a; 275 | 276 | //statement 277 | test(lss("1").statement(), "return 1;", "statement01"); 278 | test(lss("1.").statement(), "return 1;", "statement02"); 279 | test(lss("a foo. b bar: 1 . c * 2").statement(), "a.foo(); b.bar(1); return (c * 2);", "statement03"); 280 | test(lss("| foo | foo := 1 . foo").statement(), "var foo; foo = 1; return foo;", "statement04"); 281 | 282 | //variableDeclaration 283 | test(lss("| foo bar baz |").variableDeclaration(), "var foo, bar, baz; ", "variableDeclaration01"); 284 | 285 | })(); 286 | 287 | console.log("=========Class========="); 288 | 289 | //Class 290 | (function () { 291 | //classHeader 292 | test( 293 | lss('Animal subclass: #Snake variables: #(#name #color)').classHeader(), 294 | 'var Snake;\nSnake = function () { this.name = null; this.color = null; if (this.init) { this.init.apply(this, arguments); } };\nSnake.__super = Animal.prototype;\nSnake.prototype = new Animal()', 295 | 'classHeader01' 296 | ); 297 | 298 | //instanceMethod 299 | test( 300 | lss('!Snake setName: name myName := name. name!').instanceMethod(), 301 | 'Snake.prototype.setName = function (name) { var _this = this; myName = name; return name; }', 302 | 'instanceMethod01' 303 | ); 304 | 305 | })(); 306 | 307 | console.log("=========LittleSmallscript========="); 308 | 309 | //LittleSmallscript 310 | (function () { 311 | test(lss("1").toJS({prettyprint:false}), '(function () { "use strict"; return 1; }).call(this);', "toJS01"); 312 | 313 | })(); 314 | 315 | if (errors.length === 0) console.log("ALL GREEN"); 316 | }).call(this); 317 | -------------------------------------------------------------------------------- /tests/tests.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | 'use strict'; 3 | 4 | var LittleSmallscript, errors, test, log, lss, p; 5 | LittleSmallscript = require("../src/js/production/littlesmallscript"); 6 | 7 | errors = []; 8 | test = function (result, expected, mes) { 9 | if (result !== expected) { 10 | return log(mes + '\n\tEXPECTED:' + expected + '\n\tPASSED :' + result) 11 | } 12 | console.log('\u001b[32m' + 'GREEN:\t' + mes + '\u001b[0m'); 13 | return true; 14 | }; 15 | log = function (a) { 16 | console.log('\u001b[31m' + 'RED :\t' + a + '\u001b[0m'); 17 | errors.push(a); 18 | }; 19 | lss = function (input) { 20 | return new LittleSmallscript().initWithInputandOptions(input, {}); 21 | }; 22 | p = function (a) { 23 | console.log(a); 24 | return a; 25 | }; 26 | 27 | console.log("=========Packrat========="); 28 | 29 | /* Packrat */ 30 | (function () { 31 | var a, example1 = "| foo | foo := 1 + 2 to: #(1 2 3) size ; do: [:a | a]. foo"; 32 | 33 | //cacheDo 34 | a = lss("111"); 35 | test(a.anyChar(), a.anyChar(), "cacheDo01"); 36 | 37 | //try_ 38 | a = lss(example1); 39 | test(a.try_([ 40 | a.toParser("foo"), 41 | a.toParser("bar"), 42 | a.toParser("|"), 43 | a.toParser("baz") 44 | ]), "|", "try_01") 45 | 46 | a.skipSpace(); 47 | 48 | try { 49 | a.try_([ 50 | a.toParser("1"), 51 | a.toParser("2") 52 | ]); 53 | log("try_02") 54 | } catch (err) {} 55 | 56 | //sequence 57 | a = lss(example1); 58 | test(a.sequence([ 59 | a.verticalBar, 60 | a.space, 61 | a.toParser("foo"), 62 | a.space, 63 | a.verticalBar 64 | ]), "| foo |", "sequence01"); 65 | 66 | //optional 67 | a = lss(example1); 68 | test(a.optional(a.toParser("| foo |")), "| foo |", "optional01") 69 | 70 | test(a.optional(a.toParser("fooobaaaaa")), null, "optional02"); 71 | 72 | //followedBy / notFollowedBy 73 | a = lss(example1); 74 | try { 75 | test(a.followedBy(a.verticalBar), null); 76 | test(a.followedBy(a.verticalBar), null); 77 | } catch (e) { 78 | log("followedBy01"); 79 | } 80 | try { 81 | a.FollowedBy(a.toParser("fooobaaaaa")); 82 | log("followedBy02"); 83 | } catch (e) {} 84 | try { 85 | test(a.notFollowedBy(a.toParser("fooobaaaaa")), null); 86 | test(a.notFollowedBy(a.toParser("fooobaaaaa")), null); 87 | } catch (e) { 88 | log("notFollowedBy01"); 89 | } 90 | try { 91 | a.notFollowedBy(a.verticalBar); 92 | log("notFollowedBy02"); 93 | } catch (e) {} 94 | 95 | //many 96 | a = lss(example1); 97 | test(a.many(a.anyChar), example1, "many01"); 98 | 99 | test(a.many(a.many(a.colon)), "", "many02"); 100 | 101 | //many1 102 | a = lss(example1); 103 | try { 104 | test(a.many1("colon")); 105 | log("many01"); 106 | } catch (e) {} 107 | 108 | //between 109 | a = lss(example1); 110 | test(a.betweenandaccept(a.verticalBar, a.verticalBar, a.anyChar), " foo ", "between01"); 111 | 112 | //anyChar 113 | a = lss(example1); 114 | test(a.anyChar(), "|", "anyChar01"); 115 | 116 | //satisfyChar 117 | a = lss(example1); 118 | test(a.satisfyChar(function (c) { return c, "|"; }), "|", "satisfyChar"); 119 | 120 | //chr 121 | a = lss(example1); 122 | test(a.chr("|"), "|", "chr01"); 123 | 124 | //string 125 | a = lss(example1); 126 | test(a.string("| foo |"), "| foo |", "string01"); 127 | 128 | //regex 129 | a = lss(example1); 130 | test(a.regex(/^\|\s[a-z]+\s\|/), "| foo |", "regex01"); 131 | 132 | //toParser 133 | a = lss(example1); 134 | test(a.toParser("| foo |")(), "| foo |", "toParser01"); 135 | })(); 136 | 137 | console.log("=========LittleParser========="); 138 | 139 | //LittleParser 140 | (function () { 141 | var a; 142 | 143 | //whiteSpace 144 | a = lss(" \t\n "); 145 | test(a.space(), " \t\n ", "space01"); 146 | 147 | //assignmentArrow 148 | a = lss(":= <-"); 149 | test(a.assignmentArrow(), ":=", "assignmentArrow01"); 150 | a.space(); 151 | test(a.assignmentArrow(), "<-", "assignmentArrow02") 152 | 153 | //literal 154 | a = lss("-12.15'foo'#symbol123#(1 2 #(3))#{#a: 1,#b: #{#a: #()}}"); 155 | test((function () { 156 | return a.literal() + a.literal() + a.literal() + a.literal() + a.literal(); 157 | })(), "-12.15'foo''symbol123'[1, 2, [3]]{'a': 1,'b': {'a': []}}", "literal01"); 158 | 159 | a = lss("-12.15'foo'#symbol123#(1 2 #(3))#{#a: 1,#b: #{#a: #()}}"); 160 | test((function () { 161 | return a.numberLiteral() + 162 | a.stringLiteral() + 163 | a.symbolLiteral() + 164 | a.arrayLiteral() + 165 | a.hashLiteral(); 166 | })(), "-12.15'foo''symbol123'[1, 2, [3]]{'a': 1,'b': {'a': []}}", "literal02"); 167 | 168 | //newlines inside strings 169 | test( 170 | lss("'1\n2\n3'").stringLiteral(), 171 | "'1\\n2\\n3'", 172 | 'newlines in strings' 173 | ); 174 | 175 | //backslash escaping 176 | test( 177 | lss("'Foo\\'s name:\\nMike'").stringLiteral(), 178 | "'Foo\\'s name:\\nMike'", 179 | "backslash escaping" 180 | ); 181 | 182 | //variable 183 | a = lss("ab123scdh$_jags#ahj[]"); 184 | test(a.variable(), "ab123scdh$_jags", "variable01"); 185 | 186 | //keywordSelector 187 | a = lss("ah_$akd12:foooo"); 188 | test(a.keywordSelector(), "ah_$akd12:", "keywordSelector01"); 189 | 190 | //unarySelector 191 | a = lss("fooo fooo:"); 192 | test(a.unarySelector(), "fooo", "unarySelector01"); 193 | a.space(); 194 | try { 195 | a.unarySelector(); 196 | log(false, "unarySelector02"); 197 | } catch(e) {} 198 | 199 | //skipSpace 200 | a = lss(' "comment " '); 201 | a.skipSpace() 202 | test(a.index, ' "comment " '.length, "skipSpace01"); 203 | })(); 204 | 205 | console.log("=========BlockParser========="); 206 | 207 | /* BlockParser */ 208 | (function () { 209 | var a; 210 | 211 | //block 212 | a = lss("[] [1] [:foo| foo] [:foo||bar|foo.bar]"); 213 | test((function () { 214 | return a.many(function () { 215 | a.skipSpace(); 216 | return a.block() + "; "; 217 | }); 218 | })(), "function () { return null; }; function () { return 1; }; function (foo) { return foo; }; function (foo) { var bar; foo; return bar; }; ", "block01"); 219 | 220 | //blockParameters 221 | a = lss(" :foo :bar :baz"); 222 | test(a.blockParameters(), "foo, bar, baz", "blockParameters01"); 223 | 224 | //blockHead 225 | a = lss(":foo :bar | 123"); 226 | test(a.blockHead(), "foo, bar", "blockHead01"); 227 | })(); 228 | 229 | console.log("=========Expression========="); 230 | 231 | //Expression 232 | (function () { 233 | var a; 234 | 235 | //expression 236 | test(lss("foo := a kw: b + c sel").expression(), "foo = a.kw((b + c.sel()))", "expression01"); 237 | test(lss("b + a kw: c sel").expression(), "(b + a).kw(c.sel())", "expression02"); 238 | test(lss("c sel + b kw: a").expression(), "(c.sel() + b).kw(a)", "expression03"); 239 | 240 | //assignments 241 | test(lss("foo := bar := 1").assignments(), "foo = bar = ", "assignments01"); 242 | 243 | //cascade 244 | test( 245 | lss("Object new ; foo ; bar: 1").cascade(), 246 | "(function () { var _receiver = new Object(); _receiver.foo();_receiver.bar(1); return _receiver; })()", 247 | "cascade01" 248 | ); 249 | 250 | //simpleExpression 251 | test(lss("b + a kw: c sel").simpleExpression(), "(b + a).kw(c.sel())", "simpleExpression01"); 252 | 253 | //primaryReceiver 254 | test(lss("1").primaryReceiver(), "1", "primaryReceiver01"); 255 | test(lss("1 to: 5").primaryReceiver(), "(1)", "primaryReceiver02"); 256 | test(lss("[]").primaryReceiver(), "function () { return null; }", "primaryReceiver03"); 257 | test(lss("[] tryCatch: []").primaryReceiver(), "(function () { return null; })", "primaryReceiver04"); 258 | //primitive 259 | test(lss("").primaryReceiver(), "alert(1)", "primitive01"); 260 | 261 | //optimization in cascade 262 | test( 263 | lss("Object new ; at:#a put:1 ; at:#b put: 2").cascade(), 264 | "(function () { var _receiver = new Object(); _receiver.a = 1;_receiver.b = 2; return _receiver; })()", 265 | "cascade optimization" 266 | ); 267 | 268 | })(); 269 | 270 | console.log("=========Statement========="); 271 | 272 | //Statement 273 | (function () { 274 | var a; 275 | 276 | //statement 277 | test(lss("1").statement(), "return 1;", "statement01"); 278 | test(lss("1.").statement(), "return 1;", "statement02"); 279 | test(lss("a foo. b bar: 1 . c * 2").statement(), "a.foo(); b.bar(1); return (c * 2);", "statement03"); 280 | test(lss("| foo | foo := 1 . foo").statement(), "var foo; foo = 1; return foo;", "statement04"); 281 | 282 | //variableDeclaration 283 | test(lss("| foo bar baz |").variableDeclaration(), "var foo, bar, baz; ", "variableDeclaration01"); 284 | 285 | })(); 286 | 287 | console.log("=========Class========="); 288 | 289 | //Class 290 | (function () { 291 | //classHeader 292 | test( 293 | lss('Animal subclass: #Snake variables: #(#name #color)').classHeader(), 294 | 'var Snake;\nSnake = function () { this.name = null; this.color = null; if (this.init) { this.init.apply(this, arguments); } };\nSnake.__super = Animal.prototype;\nSnake.prototype = new Animal()', 295 | 'classHeader01' 296 | ); 297 | 298 | //instanceMethod 299 | test( 300 | lss('!Snake setName: name myName := name. name!').instanceMethod(), 301 | 'Snake.prototype.setName = function (name) { var _this = this; myName = name; return name; }', 302 | 'instanceMethod01' 303 | ); 304 | 305 | })(); 306 | 307 | console.log("=========LittleSmallscript========="); 308 | 309 | //LittleSmallscript 310 | (function () { 311 | test(lss("1").toJS({prettyprint:false}), '(function () { "use strict"; return 1; }).call(this);', "toJS01"); 312 | 313 | })(); 314 | 315 | if (errors.length === 0) console.log("ALL GREEN"); 316 | }).call(this); 317 | --------------------------------------------------------------------------------