├── examples ├── throw.iode ├── return.iode ├── array.iode ├── include.iode ├── modulus.iode ├── negative.iode ├── hello.iode ├── massvar.iode ├── regex.iode ├── range.iode ├── typed_vars.iode ├── mass_setting.iode ├── new.iode ├── if.iode ├── numbers.iode ├── single.iode ├── index.iode ├── for.iode ├── modulus.js ├── throw.js ├── comment.iode ├── negative.js ├── regex.js ├── return.js ├── array.js ├── comment.js ├── percentage.iode ├── hello.js ├── include.js ├── repeat.iode ├── try.iode ├── json.iode ├── new.js ├── embedded.iode ├── if.js ├── index.js ├── massvar.js ├── numbers.js ├── std.iode ├── mass_setting.js ├── percentage.js ├── range.js ├── for.js ├── single.js ├── types_func.iode ├── json.js ├── try.js ├── jquery.iode ├── std.js ├── ternary.iode ├── embedded.js ├── repeat.js ├── function.iode ├── jquery.js ├── ternary.js ├── namespace.iode ├── typed_vars.js ├── types_func.js ├── function.js ├── package.js ├── class.iode ├── namespace.js ├── package.iode └── class.js ├── src ├── iode.js ├── browser.js ├── token.js ├── developer.js ├── command.js ├── lexer.js ├── ast.js └── parser.js ├── gulpfile.js ├── package.json ├── .eslintrc ├── .gitignore ├── CONTRIBUTORS.md ├── LICENSE └── README.md /examples/throw.iode: -------------------------------------------------------------------------------- 1 | throw "Error!" -------------------------------------------------------------------------------- /examples/return.iode: -------------------------------------------------------------------------------- 1 | return 123 / 52352 -------------------------------------------------------------------------------- /examples/array.iode: -------------------------------------------------------------------------------- 1 | var arr = [1, 2, 3] 2 | -------------------------------------------------------------------------------- /examples/include.iode: -------------------------------------------------------------------------------- 1 | include "hello.fea" -------------------------------------------------------------------------------- /examples/modulus.iode: -------------------------------------------------------------------------------- 1 | var a = 2 %% 5 2 | -------------------------------------------------------------------------------- /examples/negative.iode: -------------------------------------------------------------------------------- 1 | var num = -1 - 5 2 | -------------------------------------------------------------------------------- /examples/hello.iode: -------------------------------------------------------------------------------- 1 | console.log("Hello world!") -------------------------------------------------------------------------------- /examples/massvar.iode: -------------------------------------------------------------------------------- 1 | var [x, y, z] = [1, 2, 3] -------------------------------------------------------------------------------- /examples/regex.iode: -------------------------------------------------------------------------------- 1 | var reg = ///[0-9]+///g 2 | -------------------------------------------------------------------------------- /examples/range.iode: -------------------------------------------------------------------------------- 1 | var a = [1..7] 2 | console.log(a) -------------------------------------------------------------------------------- /examples/typed_vars.iode: -------------------------------------------------------------------------------- 1 | var name:string = "John" 2 | -------------------------------------------------------------------------------- /examples/mass_setting.iode: -------------------------------------------------------------------------------- 1 | var [a, b, c] = [1, 2, 3] 2 | -------------------------------------------------------------------------------- /examples/new.iode: -------------------------------------------------------------------------------- 1 | var d = new Date 2 | 3 | console.log(d) -------------------------------------------------------------------------------- /examples/if.iode: -------------------------------------------------------------------------------- 1 | if true { 2 | console.log("Nice!") 3 | } 4 | -------------------------------------------------------------------------------- /examples/numbers.iode: -------------------------------------------------------------------------------- 1 | var age = 1_000_000 2 | var otherAge = 1.2 -------------------------------------------------------------------------------- /examples/single.iode: -------------------------------------------------------------------------------- 1 | var coordinate = fn -> (x, y) x + y 2 | -------------------------------------------------------------------------------- /examples/index.iode: -------------------------------------------------------------------------------- 1 | var arr = [1, 2, 3] 2 | console.log(arr[1]) 3 | -------------------------------------------------------------------------------- /src/iode.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | require('./command'); 4 | -------------------------------------------------------------------------------- /examples/for.iode: -------------------------------------------------------------------------------- 1 | for (a = 0, 5 >= a, a++) { 2 | console.log(a) 3 | } -------------------------------------------------------------------------------- /examples/modulus.js: -------------------------------------------------------------------------------- 1 | /* Generated by Iode v0.0.1 */ 2 | 3 | var a = 2 % 5; -------------------------------------------------------------------------------- /examples/throw.js: -------------------------------------------------------------------------------- 1 | /* Generated by Iode v0.0.1 */ 2 | 3 | throw "Error!"; -------------------------------------------------------------------------------- /examples/comment.iode: -------------------------------------------------------------------------------- 1 | # comment! # 2 | 3 | var # anywhere # name = "Bill" 4 | -------------------------------------------------------------------------------- /examples/negative.js: -------------------------------------------------------------------------------- 1 | /* Generated by Iode v0.0.1 */ 2 | 3 | var num = -1 - 5; -------------------------------------------------------------------------------- /examples/regex.js: -------------------------------------------------------------------------------- 1 | /* Generated by Iode v0.0.1 */ 2 | 3 | var reg = /[0-9]+/g; -------------------------------------------------------------------------------- /examples/return.js: -------------------------------------------------------------------------------- 1 | /* Generated by Iode v0.0.1 */ 2 | 3 | return 123 / 52352; -------------------------------------------------------------------------------- /examples/array.js: -------------------------------------------------------------------------------- 1 | /* Generated by Iode v0.0.1 */ 2 | 3 | var arr = [1, 2, 3]; 4 | -------------------------------------------------------------------------------- /examples/comment.js: -------------------------------------------------------------------------------- 1 | /* Generated by Iode v0.0.1 */ 2 | 3 | 4 | var name = "Bill"; -------------------------------------------------------------------------------- /examples/percentage.iode: -------------------------------------------------------------------------------- 1 | var percent = 10% # swag # 2 | console.log(percent) 3 | -------------------------------------------------------------------------------- /examples/hello.js: -------------------------------------------------------------------------------- 1 | /* Generated by Iode v0.0.1 */ 2 | 3 | console.log("Hello world!"); -------------------------------------------------------------------------------- /examples/include.js: -------------------------------------------------------------------------------- 1 | /* Generated by Iode v0.0.1 */ 2 | 3 | 4 | console.log("Hello world!"); -------------------------------------------------------------------------------- /examples/repeat.iode: -------------------------------------------------------------------------------- 1 | var times = 5 2 | 3 | repeat 5 { 4 | console.log("Cool!") 5 | } 6 | -------------------------------------------------------------------------------- /examples/try.iode: -------------------------------------------------------------------------------- 1 | try { 2 | console.log("Cool!") 3 | } catch e { 4 | throw e 5 | } 6 | -------------------------------------------------------------------------------- /examples/json.iode: -------------------------------------------------------------------------------- 1 | var json = { 2 | name: "Bill", 3 | age: 23, 4 | job: "Builder" 5 | } 6 | -------------------------------------------------------------------------------- /examples/new.js: -------------------------------------------------------------------------------- 1 | /* Generated by Iode v0.0.1 */ 2 | 3 | var d = new Date(); 4 | console.log(d); -------------------------------------------------------------------------------- /examples/embedded.iode: -------------------------------------------------------------------------------- 1 | var grade = 96% 2 | 3 | `function showReportCard() { console.log(grade); }` 4 | -------------------------------------------------------------------------------- /examples/if.js: -------------------------------------------------------------------------------- 1 | /* Generated by Iode v0.0.1 */ 2 | 3 | if (true) { 4 | console.log("Nice!"); 5 | } -------------------------------------------------------------------------------- /examples/index.js: -------------------------------------------------------------------------------- 1 | /* Generated by Iode v0.0.1 */ 2 | 3 | var arr = [1, 2, 3]; 4 | console.log(arr[1]); -------------------------------------------------------------------------------- /examples/massvar.js: -------------------------------------------------------------------------------- 1 | /* Generated by Iode v0.0.1 */ 2 | 3 | var x = 1; 4 | var y = 2; 5 | var z = 3; -------------------------------------------------------------------------------- /examples/numbers.js: -------------------------------------------------------------------------------- 1 | /* Generated by Iode v0.0.1 */ 2 | 3 | var age = 1000000; 4 | var otherAge = 1.2; -------------------------------------------------------------------------------- /examples/std.iode: -------------------------------------------------------------------------------- 1 | require("iode-lib") 2 | 3 | var array = [1..3] 4 | console.log(array.empty()) 5 | -------------------------------------------------------------------------------- /examples/mass_setting.js: -------------------------------------------------------------------------------- 1 | /* Generated by Iode v0.0.1 */ 2 | 3 | var a = 1; 4 | var b = 2; 5 | var c = 3; -------------------------------------------------------------------------------- /examples/percentage.js: -------------------------------------------------------------------------------- 1 | /* Generated by Iode v0.0.1 */ 2 | 3 | var percent = 0.10; 4 | console.log(percent); -------------------------------------------------------------------------------- /examples/range.js: -------------------------------------------------------------------------------- 1 | /* Generated by Iode v0.0.1 */ 2 | 3 | var a = [1, 2, 3, 4, 5, 6, 7]; 4 | console.log(a); -------------------------------------------------------------------------------- /examples/for.js: -------------------------------------------------------------------------------- 1 | /* Generated by Iode v0.0.1 */ 2 | 3 | for (var a = 0; 5 >= a; a++) { 4 | console.log(a); 5 | } -------------------------------------------------------------------------------- /examples/single.js: -------------------------------------------------------------------------------- 1 | /* Generated by Iode v0.0.1 */ 2 | 3 | var coordinate = function(x, y) { 4 | return x + y; 5 | }; -------------------------------------------------------------------------------- /examples/types_func.iode: -------------------------------------------------------------------------------- 1 | fn greet (name:string) { 2 | console.log("Hey #{name}!") 3 | } 4 | 5 | greet("John") 6 | -------------------------------------------------------------------------------- /examples/json.js: -------------------------------------------------------------------------------- 1 | /* Generated by Iode v0.0.1 */ 2 | 3 | var json = { 4 | name: "Bill", 5 | age: 23, 6 | job: "Builder" 7 | }; -------------------------------------------------------------------------------- /examples/try.js: -------------------------------------------------------------------------------- 1 | /* Generated by Iode v0.0.1 */ 2 | 3 | try { 4 | console.log("Cool!"); 5 | } catch (e) { 6 | throw e; 7 | } 8 | -------------------------------------------------------------------------------- /examples/jquery.iode: -------------------------------------------------------------------------------- 1 | $(document).ready(fn { 2 | $("a").click(fn (event) { 3 | alert("Thanks for visiting!") 4 | }) 5 | }) -------------------------------------------------------------------------------- /examples/std.js: -------------------------------------------------------------------------------- 1 | /* Generated by Iode v0.0.1 */ 2 | 3 | require("iode-lib"); 4 | 5 | var array = [1, 2, 3]; 6 | console.log(array.empty()); -------------------------------------------------------------------------------- /examples/ternary.iode: -------------------------------------------------------------------------------- 1 | fn alive bool { 2 | return bool 3 | } 4 | 5 | var msg = alive(true) ? "awesome!" : "aww.." 6 | 7 | console.log(msg) -------------------------------------------------------------------------------- /examples/embedded.js: -------------------------------------------------------------------------------- 1 | /* Generated by Iode v0.0.1 */ 2 | 3 | var grade = 0.96; 4 | 5 | function showReportCard() { 6 | console.log(grade); 7 | } -------------------------------------------------------------------------------- /examples/repeat.js: -------------------------------------------------------------------------------- 1 | /* Generated by Iode v0.0.1 */ 2 | 3 | var times = 5; 4 | for (var _i = 1; _i <= 5; _i++) { 5 | console.log("Cool!"); 6 | } -------------------------------------------------------------------------------- /examples/function.iode: -------------------------------------------------------------------------------- 1 | fn hello(name, age = 99) { 2 | console.log("My name is #{name}! I'm #{age} years old!") 3 | } 4 | 5 | hello("John", nil) 6 | -------------------------------------------------------------------------------- /examples/jquery.js: -------------------------------------------------------------------------------- 1 | /* Generated by Iode v0.0.1 */ 2 | 3 | $(document).ready(function() { 4 | $("a").click(function(event) { 5 | alert("Thanks for visiting!"); 6 | }); 7 | }); -------------------------------------------------------------------------------- /examples/ternary.js: -------------------------------------------------------------------------------- 1 | /* Generated by Iode v0.0.1 */ 2 | 3 | function alive(bool) { 4 | return bool; 5 | }; 6 | 7 | var msg = alive(true) ? "awesome!" : "aww.."; 8 | console.log(msg); -------------------------------------------------------------------------------- /examples/namespace.iode: -------------------------------------------------------------------------------- 1 | namespace Main { 2 | class Greeter name, age { 3 | this.name = "John" 4 | 5 | fn hello { 6 | console.log("Cool! Hey #{name}! You're #{age}!") 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /examples/typed_vars.js: -------------------------------------------------------------------------------- 1 | /* Generated by Iode v0.0.1 */ 2 | 3 | var name; 4 | 5 | if (typeof("John") === "string") { 6 | name = "John"; 7 | } else { 8 | throw "Expected type of string for var name"; 9 | } 10 | -------------------------------------------------------------------------------- /examples/types_func.js: -------------------------------------------------------------------------------- 1 | /* Generated by Iode v0.0.1 */ 2 | 3 | function greet(name) { 4 | if (!(typeof(name) === "string")) { 5 | throw 'Expected type of "string" for var "name"'; 6 | } 7 | console.log("Hey " + name + "!"); 8 | }; 9 | 10 | greet("John"); -------------------------------------------------------------------------------- /examples/function.js: -------------------------------------------------------------------------------- 1 | /* Generated by Iode v0.0.1 */ 2 | 3 | function hello(name, age) { 4 | if (age === null || age === undefined) { 5 | age = age = 99; 6 | } 7 | console.log("My name is " + name + "! I'm " + age + " years old!"); 8 | }; 9 | 10 | hello("John", null); -------------------------------------------------------------------------------- /examples/package.js: -------------------------------------------------------------------------------- 1 | /* Generated by Iode v0.0.1 */ 2 | 3 | { 4 | "name": "iode", 5 | "description": "A fast, empirical, useful programming language.", 6 | "version": "0.0.1", 7 | "author": "Danilo Lekovic ", 8 | "license": "MIT", 9 | "preferGlobal": true, 10 | "dependencies": 11 | } 12 | } -------------------------------------------------------------------------------- /gulpfile.js: -------------------------------------------------------------------------------- 1 | var gulp = require('gulp'); 2 | var eslint = require('gulp-eslint'); 3 | 4 | gulp.task('static', function() { 5 | 'use strict'; 6 | return gulp.src(['*.js', 'src/**/*.js']) 7 | .pipe(eslint()) 8 | .pipe(eslint.format()) 9 | .pipe(eslint.failOnError()); 10 | }); 11 | 12 | gulp.task('default', ['static']); 13 | -------------------------------------------------------------------------------- /examples/class.iode: -------------------------------------------------------------------------------- 1 | class Greeter name, age { 2 | this.name = "John" 3 | 4 | fn hello { 5 | console.log("Cool! Hey #{this.name}! You're #{this.age}!") 6 | } 7 | } 8 | 9 | class Tweeter name, age extends Greeter { 10 | fn tweet { 11 | console.log("This is a tweet!") 12 | } 13 | } 14 | 15 | var twt = new Tweeter("Bill", 5) 16 | twt.hello() 17 | -------------------------------------------------------------------------------- /examples/namespace.js: -------------------------------------------------------------------------------- 1 | /* Generated by Iode v0.0.1 */ 2 | 3 | var Main; 4 | (function(Main) { 5 | var Greeter = (function() { 6 | function Greeter(name, age) { 7 | this.name = name; 8 | this.age = age; 9 | this.name = "John"; 10 | } 11 | Greeter.prototype.hello = function() { 12 | console.log("Cool! Hey " + name + "! You're " + age + "!"); 13 | }; 14 | return Greeter; 15 | })(); 16 | Main.Greeter = Greeter; 17 | })(Main || (Main = {})); -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "iode", 3 | "description": "A fast, empirical, useful programming language.", 4 | "version": "0.0.1", 5 | "author": "Danilo Lekovic ", 6 | "license": "MIT", 7 | "preferGlobal": true, 8 | "dependencies": { 9 | "commander": "~2.8.1", 10 | "js-beautify": "~1.5.10" 11 | }, 12 | "bin": { 13 | "iode": "./src/iode.js" 14 | }, 15 | "devDependencies": { 16 | "gulp": "^3.9.0", 17 | "gulp-eslint": "^1.0.0" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /examples/package.iode: -------------------------------------------------------------------------------- 1 | { 2 | "name": "iode", 3 | "description": "A fast, empirical, useful programming language.", 4 | "version": "0.0.1", 5 | "author": "Danilo Lekovic ", 6 | "license": "MIT", 7 | "preferGlobal": true, 8 | "dependencies": { 9 | "commander": "~2.8.1", 10 | "js-beautify": "~1.5.10" 11 | }, 12 | "bin": { 13 | "iode": "./src/iode.js" 14 | }, 15 | "devDependencies": { 16 | "gulp": "^3.9.0", 17 | "gulp-eslint": "^1.0.0" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "rules": { 3 | "indent": [ 4 | 2, 5 | "tab", 6 | { 7 | "SwitchCase": 1 8 | } 9 | ], 10 | "quotes": [ 11 | 2, 12 | "single" 13 | ], 14 | "linebreak-style": [ 15 | 2, 16 | "unix" 17 | ], 18 | "semi": [ 19 | 2, 20 | "always" 21 | ], 22 | "no-console": 0 23 | }, 24 | "env": { 25 | "es6": true, 26 | "node": true 27 | }, 28 | "extends": "eslint:recommended" 29 | } 30 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | 5 | # Runtime data 6 | pids 7 | *.pid 8 | *.seed 9 | 10 | # Directory for instrumented libs generated by jscoverage/JSCover 11 | lib-cov 12 | 13 | # Coverage directory used by tools like istanbul 14 | coverage 15 | 16 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 17 | .grunt 18 | 19 | # node-waf configuration 20 | .lock-wscript 21 | 22 | # Compiled binary addons (http://nodejs.org/api/addons.html) 23 | build/Release 24 | 25 | # Dependency directory 26 | # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git 27 | node_modules 28 | -------------------------------------------------------------------------------- /CONTRIBUTORS.md: -------------------------------------------------------------------------------- 1 | # Main Contributors 2 | 3 | [![danilolekovic](https://avatars0.githubusercontent.com/u/8854152?v=3&s=30)](https://github.com/danilolekovic) 4 |
5 | 6 | Iode is originally developed and created by [@danilolekovic](https://github.com/danilolekovic). 7 | 8 | ## Other Contributors (Contribution by Pull Request) 9 | 10 | [The latest list of all contributors can be viewed here.](https://github.com/iode-lang/Iode/graphs/contributors) 11 | 12 | [![dar5hak](https://avatars0.githubusercontent.com/u/8205055?v=3&s=30)](https://github.com/dar5hak) 13 | 14 | ## All Contributors, Donators (Everyone who has ever been involved in this project!) 15 | 16 | [![danilolekovic](https://avatars0.githubusercontent.com/u/8854152?v=3&s=30)](https://github.com/danilolekovic) 17 | [![dar5hak](https://avatars0.githubusercontent.com/u/8205055?v=3&s=30)](https://github.com/dar5hak) 18 | -------------------------------------------------------------------------------- /examples/class.js: -------------------------------------------------------------------------------- 1 | /* Generated by Iode v0.0.1 */ 2 | 3 | var Greeter = (function() { 4 | function Greeter(name, age) { 5 | this.name = name; 6 | this.age = age; 7 | this.name = "John"; 8 | } 9 | Greeter.prototype.hello = function() { 10 | console.log("Cool! Hey " + this.name + "! You're " + this.age + "!"); 11 | }; 12 | return Greeter; 13 | })(); 14 | 15 | function extend(target, source) { 16 | Object.getOwnPropertyNames(source).forEach(function(propName) { 17 | Object.defineProperty(target, propName, Object.getOwnPropertyDescriptor(source, propName)); 18 | }); 19 | return target; 20 | } 21 | 22 | var Tweeter = (function() { 23 | function Tweeter(name, age) { 24 | this.name = name; 25 | this.age = age; 26 | } 27 | Tweeter.prototype.tweet = function() { 28 | console.log("This is a tweet!"); 29 | }; 30 | return Tweeter; 31 | })(); 32 | 33 | extend(Tweeter.prototype, Greeter.prototype); 34 | 35 | var twt = new Tweeter("Bill", 5); 36 | twt.hello(); -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Danilo Lekovic 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/browser.js: -------------------------------------------------------------------------------- 1 | // For use with HTML files 2 | var elements = document.getElementsByTagName("script"); 3 | 4 | function httpGet(address) { 5 | var xhr = new XMLHttpRequest(); 6 | xhr.open('GET', address, false); 7 | xhr.send(null); 8 | if (xhr.status === 200) { 9 | return xhr.responseText; 10 | } 11 | } 12 | 13 | function generate(code) { 14 | var parser = new Parser(code.toString(), null); 15 | var ast = parser.parse(); 16 | var outputCode = '/* Generated by Iode v0.0.1 */\n'; 17 | 18 | try { 19 | for (var expr in ast) { 20 | outputCode += '\n' + ast[expr].val; 21 | } 22 | } catch (e) { 23 | console.log('[x] Could not generate code. ' + e); 24 | } 25 | } 26 | 27 | for (var i = 0; i < elements.length; i++) { 28 | if (elements[i].type == "text/iode") { 29 | var previousCode = elements[i].innerHTML; 30 | var src = ""; 31 | if (elements[i].hasAttribute("src")) { 32 | src += httpGet(elements[i].src); 33 | eval(generate(src + previousCode)); 34 | elements[i].innerHTML = "/* Compiled by Iode v0.0.1 */\n" + generate(src + previousCode); 35 | elements[i].type = "text/javascript"; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/token.js: -------------------------------------------------------------------------------- 1 | var Token = function(type, value) { 2 | this.type = type; 3 | this.value = value; 4 | }; 5 | 6 | var TokenType = { 7 | IDENTIFIER: 'IDENTIFIER', 8 | NUMBER: 'NUMBER', 9 | EXCLAMATION: 'EXCLAMATION', 10 | PERCENT: 'PERCENT', 11 | AND: 'AND', 12 | LPAREN: 'LPAREN', 13 | RPAREN: 'RPAREN', 14 | MUL: 'MUL', 15 | ADD: 'ADD', 16 | COMMA: 'COMMA', 17 | SUB: 'SUB', 18 | DOT: 'DOT', 19 | DIV: 'DIV', 20 | COLON: 'COLON', 21 | LT: 'LT', 22 | EQUALS: 'EQUALS', 23 | GT: 'GT', 24 | OR: 'OR', 25 | NEWLINE: 'NEWLINE', 26 | BOOLEAN: 'BOOLEAN', 27 | STRING: 'STRING', 28 | VAR: 'VAR', 29 | FUNCTION: 'FUNCTION', 30 | COMMENT: 'COMMENT', 31 | NEQUALS: 'NEQUALS', 32 | IS: 'IS', 33 | SIS: 'SIS', 34 | LTEQUALS: 'LTEQUALS', 35 | GTEQUALS: 'GTEQUALS', 36 | WHILE: 'WHILE', 37 | IF: 'IF', 38 | ELSE: 'ELSE', 39 | ELSIF: 'ELSIF', 40 | NEW: 'NEW', 41 | FOREACH: 'FOREACH', 42 | IN: 'IN', 43 | CONST: 'CONST', 44 | RETURN: 'RETURN', 45 | CONTINUE: 'CONTINUE', 46 | THROW: 'THROW', 47 | INCLUDE: 'INCLUDE', 48 | QUESTION: 'QUESTION', 49 | LBRACK: 'LBRACK', 50 | RBRACK: 'RBRACK', 51 | TWODOTS: 'TWODOTS', 52 | FOR: 'FOR', 53 | PLUSEQ: 'PLUSEQ', 54 | PLUSPLUS: 'PLUSPLUS', 55 | SUBEQ: 'SUBEQ', 56 | SUBSUB: 'SUBSUB', 57 | CLASS: 'CLASS', 58 | ARROW: 'ARROW', 59 | REPEAT: 'REPEAT', 60 | PATTERN: 'PATTERN', 61 | NAMESPACE: 'NAMESPACE', 62 | TRY: 'TRY', 63 | CATCH: 'CATCH', 64 | EMBEDDED: 'EMBEDDED', 65 | MODULUS: 'MODULUS', 66 | EXP: 'EXP', 67 | EXTENDS: 'EXTENDS' 68 | }; 69 | 70 | exports.Token = Token; 71 | exports.TokenType = TokenType; 72 | -------------------------------------------------------------------------------- /src/developer.js: -------------------------------------------------------------------------------- 1 | var iode = require('../package.json'), 2 | Parser = require('./parser.js').Parser, 3 | beauty = require('js-beautify').js_beautify, 4 | readline = require('readline'); 5 | 6 | var rl = readline.createInterface({ 7 | input: process.stdin, 8 | output: process.stdout 9 | }); 10 | 11 | var line = 1; 12 | var currentCode = ""; 13 | 14 | rl.setPrompt('[' + line + '] iode> '); 15 | rl.prompt(); 16 | 17 | rl.on('line', function(code) { 18 | runCode(code); 19 | }).on('close', function() { 20 | console.log('~ Iode Closed ~'); 21 | process.exit(0); 22 | });; 23 | 24 | var runCode = function(code) { 25 | if (code == "R") { 26 | eval(genCode(currentCode + "\n")); 27 | currentCode = ""; 28 | line = 1; 29 | rl.setPrompt('[' + line + '] iode>'); 30 | rl.prompt(); 31 | } else if (code == "C") { 32 | console.log("\n" + genCode(currentCode + "\n") + "\n"); 33 | currentCode = ""; 34 | line = 1; 35 | rl.setPrompt('[' + line + '] iode>'); 36 | rl.prompt(); 37 | } else if (code == "CR" || code == "RC") { 38 | eval(genCode(currentCode + "\n")); 39 | console.log("\n" + genCode(currentCode + "\n") + "\n"); 40 | currentCode = ""; 41 | line = 1; 42 | rl.setPrompt('[' + line + '] iode>'); 43 | rl.prompt(); 44 | } else { 45 | currentCode += "\n" + code; 46 | line++; 47 | rl.setPrompt('[' + line + '] iode>'); 48 | rl.prompt(); 49 | } 50 | }; 51 | 52 | var genCode = function(code) { 53 | var parser = new Parser(code.toString() + '\n', null); 54 | var ast = parser.parse(); 55 | var outputCode = ''; 56 | 57 | try { 58 | for (var expr in ast) { 59 | outputCode += '\n' + ast[expr].val; 60 | } 61 | } catch (e) { 62 | console.error('[x] Could not generate code. ' + e); 63 | } 64 | 65 | outputCode = beauty(outputCode, { indent_size: 2 }); 66 | return outputCode; 67 | }; 68 | -------------------------------------------------------------------------------- /src/command.js: -------------------------------------------------------------------------------- 1 | var program = require('commander'), 2 | iode = require('../package.json'), 3 | beauty = require("js-beautify").js_beautify, 4 | Parser = require('./parser').Parser, 5 | fs = require('fs'), 6 | path = require('path'); 7 | 8 | program 9 | .version(iode.version) 10 | .usage('[options] ') 11 | .option('-r, --run', 'runs program without outputting generated code') 12 | .option('-o, --output', 'outputs the generated code') 13 | .option('-d, --dev', 'runs developer testing mode') 14 | .option('-t, --tokens', 'outputs AST tokens, not lexer tokens') 15 | .parse(process.argv); 16 | 17 | var run_cmd = function(cmd, args, callBack) { 18 | var spawn = require('child_process').spawn; 19 | var child = spawn(cmd, args); 20 | var resp = ""; 21 | 22 | child.stdout.on('data', function(buffer) { 23 | resp += buffer.toString() 24 | }); 25 | 26 | child.stdout.on('end', function() { 27 | callBack(resp) 28 | }); 29 | }; 30 | 31 | if (program.args.length >= 0) { 32 | var fileNames = []; 33 | 34 | program.args.forEach(function(file) { 35 | var filename = path.join(process.cwd(), file); 36 | var code; 37 | 38 | try { 39 | code = fs.readFileSync(filename) + '\n'; 40 | } catch (e) { 41 | if (e.code === 'ENOENT') { 42 | console.error('[x] File not found: ' + filename); 43 | // TODO: Break out of the iteration and move on 44 | } else { 45 | throw e; 46 | } 47 | } 48 | 49 | var parser = new Parser(code.toString(), path.dirname(filename)); 50 | var ast = parser.parse(); 51 | var outputCode = '/* Generated by Iode v' + iode.version + ' */\n'; 52 | 53 | try { 54 | for (var expr in ast) { 55 | outputCode += '\n' + ast[expr].val; 56 | } 57 | } catch (e) { 58 | console.error('[x] Could not generate code. ' + e); 59 | } 60 | 61 | outputCode = beauty(outputCode, { indent_size: 2 }); 62 | 63 | if (program.tokens) { 64 | parser.treeTokens(ast); 65 | } 66 | 67 | if (program.output) { 68 | fs.writeFileSync(filename.replace('.iode', '.js'), outputCode); 69 | fileNames.push(filename.replace('.iode', '.js')); 70 | } 71 | 72 | if (program.run) { 73 | for (file in fileNames) { 74 | run_cmd("node", [fileNames[file]], function(text) { 75 | console.log("\n[" + file + "] " + text); 76 | }); 77 | } 78 | } 79 | 80 | if (program.dev) { 81 | require("./developer.js"); 82 | } 83 | }); 84 | 85 | if (program.output) { 86 | console.log(); 87 | console.log(); 88 | 89 | for (name in fileNames) { 90 | console.log('> ' + fileNames[name]); 91 | } 92 | } 93 | } else { 94 | program.help(); 95 | } 96 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # [![Iode](https://raw.githubusercontent.com/iode-lang/Graphics/master/Logos/Clean%2C%20Thin.png)](http://iode-lang.github.io/) 2 | 3 | Iode is a fast and empirical programming language, inspired by Swift. Contributions are welcome! 4 | 5 | More in depth, Iode is a multi-paradigm, imperative, procedural, object-oriented, and beginner-friendly programming language that compiles to JavaScript. Iode is influenced by languages such as Rust, Ruby, Go, and Swift. 6 | 7 | # Installing the Beta 8 | 9 | Download/clone this repository, cd to the cloned directory, and type `npm install -g`. Installation will require [NodeJS](https://nodejs.org/en/). 10 | 11 | Quick getting-started script: 12 | 13 | ```bash 14 | $ git clone https://github.com/iode-lang/Iode.git 15 | $ cd Iode 16 | $ npm install -g 17 | ``` 18 | 19 | ## Using the Beta 20 | 21 | ```bash 22 | $ iode -h 23 | 24 | Usage: iode [options] 25 | 26 | Options: 27 | 28 | -h, --help output usage information 29 | -V, --version output the version number 30 | -r, --run runs program without outputting generated code 31 | -o, --output outputs the generated code 32 | 33 | ``` 34 | 35 | Example: 36 | 37 | ```bash 38 | $ iode -o -r some/iode/file.iode 39 | ``` 40 | 41 | ## Hand-Built.. for developers 42 | 43 | Under the hood, Iode uses **no** additional libraries for parsing or lexical analysis. This means that the download will be quite smaller (than other alt-js languages..) and that the core will be powerful. 44 | 45 | ## Designed.. for simplicity 46 | 47 | Every aspect of Iode has been designed in a logical, empirical way to ensure serene programming for anyone, anywhere, anytime. Iode trans-compiles into JavaScript, and therefore, will run anywhere. 48 | 49 | ## Created.. for everybody 50 | 51 | The main goal of this programming language is to have a small learning curve so that any beginner can start coding. The second half of this goal is to appeal to experienced programmers as well. Syntax suggestions are being taken into consideration, if you have an idea, [we'd be glad to hear about it!](https://github.com/iode-lang/Iode/issues/new) 52 | 53 | ## Interested in Contributing? 54 | 55 | Awesome! If you want to help with this project but don't know what to do, there's a to-do list available [here](https://trello.com/b/uJ79uDf4/iode). All contributors are added to the `CONTRIBUTORS.md` file. 56 | 57 | ## Testing 58 | 59 | Iode is developed on Windows 10 but should work on most operating systems such as OS X, Linux, FreeBSD, NonStop, IBM AIX, IBM System z, IBM i and other versions of MS Windows. 60 | 61 | ## License 62 | 63 | Iode is licensed under the [permission MIT license](https://raw.githubusercontent.com/iode-lang/Iode/master/LICENSE). 64 | -------------------------------------------------------------------------------- /src/lexer.js: -------------------------------------------------------------------------------- 1 | var Token = require('./token').Token, 2 | TokenType = require('./token').TokenType; 3 | 4 | var Lexer = function(code) { 5 | this.code = code; 6 | this.index = -1; 7 | this.tokens = []; 8 | this.line = 1; 9 | 10 | this.peekToken = function() { 11 | return this.tokens[this.index + 1]; 12 | }; 13 | 14 | this.peekSpecificToken = function(i) { 15 | return this.tokens[this.index + i]; 16 | }; 17 | 18 | this.nextToken = function() { 19 | this.index++; 20 | return this.tokens[this.index]; 21 | }; 22 | 23 | this.isLetter = function(char) { 24 | return ((char >= 65) && (char <= 90)) || ((char >= 97) && (char <= 122)) || 25 | char == 36 || char == 95; 26 | }; 27 | 28 | this.isNumber = function(char) { 29 | return char >= 48 && char <= 57; 30 | }; 31 | 32 | this.isNumberUnder = function(char) { 33 | return char >= 48 && char <= 57 || char == 95; 34 | }; 35 | 36 | this.isLetterOrNumber = function(char) { 37 | return this.isLetter(char) || this.isNumber(char); 38 | }; 39 | 40 | this.isSymbol = function(char) { 41 | return char == 33 || (char >= 37 && char <= 38) || (char >= 40 && char <= 42 | 47) || char == 58 || (char >= 60 && char <= 62) || (char >= 123 && char <= 43 | 125) || char == 35 || char == 63 || char == 91 || char == 93 || char == 96; 44 | }; 45 | 46 | this.isQuote = function(char) { 47 | return char == 34; 48 | }; 49 | 50 | this.isNewLine = function(char) { 51 | return char == 10; 52 | }; 53 | 54 | this.isWhiteSpace = function(char) { 55 | return char == 32 || char == 9 || char == 11; 56 | }; 57 | 58 | this.error = function(msg) { 59 | console.log(); 60 | console.log('[x] ' + msg + ' on line #' + this.line + '.'); 61 | console.log(); 62 | process.exit(1); 63 | }; 64 | 65 | this.tokenize = function() { 66 | var pos = 0; 67 | var code = this.code; 68 | var output = []; 69 | 70 | while (pos < code.length) { 71 | var str = ''; 72 | 73 | if (this.isLetter(code.charCodeAt(pos))) { 74 | str = code[pos]; 75 | pos++; 76 | 77 | while (this.isLetterOrNumber(code.charCodeAt(pos))) { 78 | str += code[pos]; 79 | pos++; 80 | } 81 | 82 | if (str == 'true' || str == 'false' || str == 'nil' || str == 'undefined') { 83 | output.push(new Token(TokenType.BOOLEAN, str)); 84 | } else if (str == 'var') { 85 | output.push(new Token(TokenType.VAR, str)); 86 | } else if (str == 'fn') { 87 | output.push(new Token(TokenType.FUNCTION, str)); 88 | } else if (str == 'while') { 89 | output.push(new Token(TokenType.WHILE, str)); 90 | } else if (str == 'until') { 91 | output.push(new Token(TokenType.UNTIL, str)); 92 | } else if (str == 'else') { 93 | output.push(new Token(TokenType.ELSE, str)); 94 | } else if (str == 'elsif') { 95 | output.push(new Token(TokenType.ELSIF, str)); 96 | } else if (str == 'if') { 97 | output.push(new Token(TokenType.IF, str)); 98 | } else if (str == 'new') { 99 | output.push(new Token(TokenType.NEW, str)); 100 | } else if (str == 'foreach') { 101 | output.push(new Token(TokenType.FOREACH, str)); 102 | } else if (str == 'in') { 103 | output.push(new Token(TokenType.IN, str)); 104 | } else if (str == 'const') { 105 | output.push(new Token(TokenType.CONST, str)); 106 | } else if (str == 'return') { 107 | output.push(new Token(TokenType.RETURN, str)); 108 | } else if (str == 'continue') { 109 | output.push(new Token(TokenType.CONTINUE, str)); 110 | } else if (str == 'throw') { 111 | output.push(new Token(TokenType.THROW, str)); 112 | } else if (str == 'include') { 113 | output.push(new Token(TokenType.INCLUDE, str)); 114 | } else if (str == 'for') { 115 | output.push(new Token(TokenType.FOR, str)); 116 | } else if (str == 'class') { 117 | output.push(new Token(TokenType.CLASS, str)); 118 | } else if (str == 'repeat') { 119 | output.push(new Token(TokenType.REPEAT, str)); 120 | } else if (str == 'namespace') { 121 | output.push(new Token(TokenType.NAMESPACE, str)); 122 | } else if (str == 'try') { 123 | output.push(new Token(TokenType.TRY, str)); 124 | } else if (str == 'catch') { 125 | output.push(new Token(TokenType.CATCH, str)); 126 | } else if (str == 'extends') { 127 | output.push(new Token(TokenType.EXTENDS, str)); 128 | } else { 129 | output.push(new Token(TokenType.IDENTIFIER, str)); 130 | } 131 | 132 | str = ''; 133 | } else if (this.isQuote(code.charCodeAt(pos))) { 134 | pos++; 135 | str = ''; 136 | 137 | while (!this.isQuote(code.charCodeAt(pos))) { 138 | str += code[pos]; 139 | pos++; 140 | } 141 | 142 | output.push(new Token(TokenType.STRING, str)); 143 | pos++; 144 | str = ''; 145 | } else if (this.isNumber(code.charCodeAt(pos))) { 146 | str = code[pos]; 147 | pos++; 148 | 149 | while (this.isNumberUnder(code.charCodeAt(pos)) || code[pos] == '.') { 150 | if (code[pos] == '.' && code[pos + 1] == '.') { 151 | break; 152 | } else { 153 | str += code[pos]; 154 | pos++; 155 | } 156 | } 157 | 158 | if (str.charAt(str.length - 1) == '.') { 159 | this.error('Float/decimal cannot end with a decimal'); 160 | } 161 | 162 | output.push(new Token(TokenType.NUMBER, str)); 163 | 164 | str = ''; 165 | } else if (this.isSymbol(code.charCodeAt(pos))) { 166 | switch (code[pos]) { 167 | case '!': 168 | pos++; 169 | 170 | if (code[pos] == '=') { 171 | output.push(new Token(TokenType.NEQUALS, '!=')); 172 | pos++; 173 | } else { 174 | output.push(new Token(TokenType.EXCLAMATION, '!')); 175 | } 176 | break; 177 | case '%': 178 | pos++; 179 | 180 | if (code[pos] == '%') { 181 | pos++; 182 | output.push(new Token(TokenType.MODULUS, '%%')); 183 | } else { 184 | output.push(new Token(TokenType.PERCENT, '%')); 185 | } 186 | break; 187 | case '&': 188 | pos++; 189 | 190 | if (code[pos] == '&') { 191 | output.push(new Token(TokenType.AND, '&&')); 192 | pos++; 193 | } else { 194 | this.error('Unknown symbol: \'' + code[pos] + '\''); 195 | } 196 | break; 197 | case '|': 198 | pos++; 199 | 200 | if (code[pos] == '|') { 201 | output.push(new Token(TokenType.OR, '||')); 202 | pos++; 203 | } else { 204 | this.error('Unknown symbol: \'' + code[pos] + '\''); 205 | } 206 | break; 207 | case '(': 208 | output.push(new Token(TokenType.LPAREN, '(')); 209 | pos++; 210 | break; 211 | case ')': 212 | output.push(new Token(TokenType.RPAREN, ')')); 213 | pos++; 214 | break; 215 | case '*': 216 | pos++; 217 | 218 | if (code[pos] == '*') { 219 | pos++; 220 | output.push(new Token(TokenType.EXP, '**')); 221 | } else { 222 | output.push(new Token(TokenType.MUL, '*')); 223 | } 224 | break; 225 | case '+': 226 | pos++; 227 | 228 | if (code[pos] == '+') { 229 | pos++; 230 | output.push(new Token(TokenType.PLUSPLUS, '++')); 231 | } else if (code[pos] == '=') { 232 | pos++; 233 | output.push(new Token(TokenType.PLUSEQ, '+=')); 234 | } else { 235 | pos++; 236 | output.push(new Token(TokenType.ADD, '+')); 237 | } 238 | break; 239 | case ',': 240 | output.push(new Token(TokenType.COMMA, ',')); 241 | pos++; 242 | break; 243 | case '-': 244 | pos++; 245 | 246 | if (this.isNumber(code.charCodeAt(pos))) { 247 | str = code[pos]; 248 | pos++; 249 | 250 | while (this.isNumberUnder(code.charCodeAt(pos)) || code[pos] == '.') { 251 | if (code[pos] == '.' && code[pos + 1] == '.') { 252 | break; 253 | } else { 254 | str += code[pos]; 255 | pos++; 256 | } 257 | } 258 | 259 | if (str.charAt(str.length - 1) == '.') { 260 | this.error('Float/decimal cannot end with a decimal'); 261 | } 262 | 263 | output.push(new Token(TokenType.NUMBER, '-' + str)); 264 | 265 | str = ''; 266 | } else if (code[pos] == '-') { 267 | pos++; 268 | output.push(new Token(TokenType.SUBSUB, '--')); 269 | } else if (code[pos] == '=') { 270 | pos++; 271 | output.push(new Token(TokenType.SUBEQ, '-=')); 272 | } else if (code[pos] == '>') { 273 | pos++; 274 | output.push(new Token(TokenType.ARROW, '->')); 275 | } else { 276 | pos++; 277 | output.push(new Token(TokenType.SUB, '-')); 278 | } 279 | break; 280 | case '.': 281 | pos++; 282 | 283 | if (code[pos] == '.') { 284 | pos++; 285 | output.push(new Token(TokenType.TWODOTS, '..')); 286 | } else { 287 | output.push(new Token(TokenType.DOT, '.')); 288 | } 289 | break; 290 | case '/': 291 | if (code[pos + 1] == '/' && code[pos + 2] == '/') { 292 | pos += 3; 293 | var regex = '/'; 294 | 295 | while (code[pos] != '\n') { 296 | if (code[pos] == '/' && code[pos + 1] == '/' && code[pos + 2] == '/') { 297 | break; 298 | } 299 | 300 | regex += code[pos]; 301 | pos++; 302 | } 303 | 304 | regex += '/'; 305 | 306 | if (code[pos] == '/' && code[pos + 1] == '/' && code[pos + 2] == '/') { 307 | pos += 3; 308 | 309 | if (this.isLetter(code.charCodeAt(pos))) { 310 | regex += code[pos]; 311 | pos++; 312 | } 313 | } 314 | 315 | output.push(new Token(TokenType.PATTERN, regex)); 316 | output.push(new Token(TokenType.NEWLINE, '\n')); 317 | } else { 318 | output.push(new Token(TokenType.DIV, '/')); 319 | pos++; 320 | } 321 | break; 322 | case ':': 323 | output.push(new Token(TokenType.COLON, ':')); 324 | pos++; 325 | break; 326 | case '?': 327 | output.push(new Token(TokenType.QUESTION, '?')); 328 | pos++; 329 | break; 330 | case '[': 331 | output.push(new Token(TokenType.LBRACK, '[')); 332 | pos++; 333 | break; 334 | case ']': 335 | output.push(new Token(TokenType.RBRACK, ']')); 336 | pos++; 337 | break; 338 | case '<': 339 | pos++; 340 | 341 | if (code[pos] == '=') { 342 | output.push(new Token(TokenType.LTEQUALS, '<=')); 343 | pos++; 344 | } else { 345 | output.push(new Token(TokenType.LT, '<')); 346 | } 347 | break; 348 | case '=': 349 | pos++; 350 | 351 | if (code[pos] == '=') { 352 | pos++; 353 | 354 | output.push(new Token(TokenType.IS, '==')); 355 | } else { 356 | output.push(new Token(TokenType.EQUALS, '=')); 357 | } 358 | break; 359 | case '>': 360 | pos++; 361 | 362 | if (code[pos] == '=') { 363 | output.push(new Token(TokenType.GTEQUALS, '>=')); 364 | pos++; 365 | } else { 366 | output.push(new Token(TokenType.GT, '>')); 367 | } 368 | break; 369 | case '{': 370 | output.push(new Token(TokenType.LBRACE, '{')); 371 | pos++; 372 | break; 373 | case '}': 374 | output.push(new Token(TokenType.RBRACE, '}')); 375 | pos++; 376 | break; 377 | case '#': 378 | pos++; 379 | str = ''; 380 | 381 | while (code[pos] != '#') { 382 | if (code[pos] == '\n') { 383 | this.error('Expected a \'#\' to end the comment'); 384 | } 385 | 386 | str += code[pos]; 387 | pos++; 388 | } 389 | 390 | pos++; 391 | str = ''; 392 | break; 393 | case '`': 394 | pos++; 395 | var str = ""; 396 | 397 | while (code[pos] != '`') { 398 | if (code[pos] == '\n') { 399 | this.error('Expected a \'`\' to end the embedded JavaScript'); 400 | } 401 | 402 | str += code[pos]; 403 | pos++; 404 | } 405 | 406 | pos++; 407 | 408 | output.push(new Token(TokenType.EMBEDDED, str)); 409 | break; 410 | default: 411 | this.error('Unknown symbol: \'' + code[pos] + '\''); 412 | return; 413 | } 414 | } else if (this.isNewLine(code.charCodeAt(pos))) { 415 | output.push(new Token(TokenType.NEWLINE, '\n')); 416 | this.line++; 417 | pos++; 418 | } else if (this.isWhiteSpace(code.charCodeAt(pos)) || code.charCodeAt(pos) == 13) { 419 | pos++; 420 | } else { 421 | this.error('Unknown symbol: \'' + code[pos] + '\''); 422 | } 423 | } 424 | 425 | return output; 426 | }; 427 | 428 | this.tokens = this.tokenize(); 429 | }; 430 | 431 | exports.Lexer = Lexer; 432 | -------------------------------------------------------------------------------- /src/ast.js: -------------------------------------------------------------------------------- 1 | var IodeIdentifier = function(val) { 2 | this.type = 'Identifer'; 3 | this.val = val; 4 | }; 5 | 6 | var IodeNumber = function(val) { 7 | this.type = 'Number'; 8 | this.val = val.replace(/_/g, ''); 9 | }; 10 | 11 | var IodePercentage = function(val) { 12 | this.type = 'Number with Percent'; 13 | this.val = '(' + val.replace(/_/g, '') + ' / 100)'; 14 | }; 15 | 16 | var IodeString = function(val) { 17 | this.type = 'String'; 18 | this.val = getISValue(val); 19 | }; 20 | 21 | var getISValue = function(val) { 22 | var string = val; 23 | var reg = /(.*)#{(.*)}(.*)/g; 24 | 25 | var matches = [], 26 | found; 27 | 28 | while (found = reg.exec(string)) { 29 | string = string.replace(reg, function($0, $1, $2, $3) { 30 | return $1 + '\" + ' + $2 + ' + \"' + $3; 31 | }); 32 | } 33 | 34 | return '\"' + string + '\"'; 35 | }; 36 | 37 | var IodeBoolean = function(value) { 38 | this.type = 'Boolean'; 39 | this.value = value; 40 | this.val = getIBValue(value); 41 | }; 42 | 43 | var getIBValue = function(value) { 44 | switch (value) { 45 | case 'true': 46 | return 'true'; 47 | case 'false': 48 | return 'false'; 49 | case 'nil': 50 | return 'null'; 51 | case 'undefined': 52 | return 'undefined'; 53 | default: 54 | return 'null'; 55 | } 56 | }; 57 | 58 | var IodeBinaryOp = function(left, op, right) { 59 | this.type = 'Binary Op'; 60 | this.left = left; 61 | this.op = op; 62 | this.right = right; 63 | this.val = getIBOValue(left, op, right); 64 | }; 65 | 66 | var getIBOValue = function(left, op, right) { 67 | var lhs = left.val; 68 | 69 | if (right == undefined) { 70 | return lhs; 71 | } 72 | 73 | var rhs = right.val; 74 | 75 | if (op == '!=') { 76 | op = '!=='; 77 | } else if (op == '==') { 78 | op = '==='; 79 | } 80 | 81 | 82 | if (lhs.charAt(lhs.length - 1) == ';') { 83 | lhs = lhs.substring(0, lhs.length - 1); 84 | } 85 | 86 | if (rhs.charAt(rhs.length - 1) == ';') { 87 | rhs = rhs.substring(0, rhs.length - 1); 88 | } 89 | 90 | if (op == '**') { 91 | return 'Math.pow(' + lhs + ', ' + rhs + ')'; 92 | } 93 | 94 | if (op == '%%') { 95 | op = '%'; 96 | } 97 | 98 | return lhs + ' ' + op + ' ' + rhs; 99 | }; 100 | 101 | var IodeParenthesis = function(value) { 102 | this.type = 'Parenthesis'; 103 | this.value = value; 104 | this.val = '(' + this.value.val + ')'; 105 | }; 106 | 107 | var IodeVariableDeclaration = function(name, value, expectedType) { 108 | this.type = 'Variable Declaration'; 109 | this.name = name; 110 | this.value = value; 111 | this.expectedType = expectedType; 112 | this.val = getIVDValue(name, value.val, expectedType); 113 | }; 114 | 115 | var getIVDValue = function(name, value, expectedType) { 116 | if (expectedType == null) { 117 | if (value == null) { 118 | return 'var ' + name + ';'; 119 | } else { 120 | if (value.charAt(value.length - 1) == ';') { 121 | return 'var ' + name + ' = ' + value; 122 | } else { 123 | return 'var ' + name + ' = ' + value + ';'; 124 | } 125 | } 126 | } else { 127 | if (value == null) { 128 | return 'var ' + name + ';'; 129 | } else { 130 | if (value.charAt(value.length - 1) == ';') { 131 | value = value.substring(0, value.length - 1); 132 | 133 | return 'var ' + name + ';\n\nif (typeof(' + value + ') === \"' + expectedType + '\") { ' + name + ' = ' + value + '; } else {' + 134 | 'throw "Expected type of ' + expectedType + ' for var ' + name + '."; }'; 135 | } else { 136 | return 'var ' + name + ';\n\nif (typeof(' + value + ') === \"' + expectedType + '\") { ' + name + ' = ' + value + '; } else {' + 137 | 'throw "Expected type of ' + expectedType + ' for var ' + name + '."; }'; 138 | } 139 | } 140 | } 141 | }; 142 | 143 | var IodeVariableSetting = function(name, value) { 144 | this.type = 'Variable Setting'; 145 | this.name = name; 146 | this.value = value; 147 | this.val = getIVSValue(name, value); 148 | }; 149 | 150 | var getIVSValue = function(name, value) { 151 | return name + ' = ' + value.val + ';'; 152 | }; 153 | 154 | var generateBody = function(body) { 155 | if (body.length != 0) { 156 | body = body.map(function(stmt) { 157 | if (stmt.val.charAt(stmt.val.length - 1) == ';') { 158 | if (stmt.type == 'While' || stmt.type == 'Until' || stmt.type == 'If' || stmt.type == 'Foreach' || stmt.type == 'For' 159 | || stmt.type == 'Repeat' || stmt.type == 'Foreach') { 160 | return stmt.val.substring(0, stmt.val.length - 1); 161 | } else { 162 | return stmt.val; 163 | } 164 | } else { 165 | if (stmt.type == 'While' || stmt.type == 'Until' || stmt.type == 'If' || stmt.type == 'Foreach' || stmt.type == 'For' 166 | || stmt.type == 'Repeat' || stmt.type == 'Foreach') { 167 | return stmt.val + '\n'; 168 | } else { 169 | if (stmt.type == 'Newline') { 170 | return stmt.val; 171 | } else { 172 | return stmt.val + ';'; 173 | } 174 | } 175 | } 176 | }); 177 | 178 | return body.join('\n'); 179 | } else { 180 | return ''; 181 | } 182 | }; 183 | 184 | var IodePrototype = function(name, proto, args, body) { 185 | this.type = 'Prototype'; 186 | this.name = name; 187 | this.proto = proto; 188 | this.args = args; 189 | this.body = body; 190 | this.val = getIProtoValue(name, proto, args, body); 191 | }; 192 | 193 | var getIProtoValue = function(name, proto, args, body) { 194 | var a = ''; 195 | var formatted = []; 196 | var defaults = []; 197 | 198 | if (args.length != 0) { 199 | for (arg in args) { 200 | if (args[arg].val.charAt(args[arg].val.length - 1) == ';') { 201 | args[arg].val = args[arg].val.substring(0, args[arg].val.length - 1); 202 | } 203 | 204 | if (args[arg].type == 'Variable Setting') { 205 | formatted.push(args[arg].name); 206 | defaults.push({name: args[arg].name, value: args[arg].val}); 207 | continue; 208 | } 209 | 210 | formatted.push(args[arg].val); 211 | } 212 | 213 | a = formatted.join(', '); 214 | } 215 | 216 | var builder = ''; 217 | 218 | if (defaults.length != 0) { 219 | for (item in defaults) { 220 | builder += 'if (' + defaults[item].name + ' == null || ' + defaults[item].name + ' == undefined) { ' + defaults[item].value + '; }\n'; 221 | } 222 | } 223 | 224 | if (generateBody(body).trim() == ';') { 225 | return proto + '.prototype.' + name + ' = function (' + a + ') {' + builder + '};'; 226 | } else { 227 | return proto + '.prototype.' + name + ' = function (' + a + ') {\n' + builder + generateBody(body) + '};'; 228 | } 229 | }; 230 | 231 | var IodeFunction = function(name, args, body) { 232 | this.type = 'Function'; 233 | this.name = name; 234 | this.args = args; 235 | this.body = body; 236 | this.val = getIFValue(name, args, body); 237 | this.valAlt = getIFValueAlt(name, args, body); 238 | }; 239 | 240 | var getIFValue = function(name, args, body) { 241 | var a = ''; 242 | var formatted = []; 243 | var defaults = []; 244 | var checks = []; 245 | 246 | if (args.length != 0) { 247 | for (arg in args) { 248 | if (args[arg].it.val.charAt(args[arg].it.val.length - 1) == ';') { 249 | args[arg].it.val = args[arg].it.val.substring(0, args[arg].it.val.length - 1); 250 | } 251 | 252 | if (args[arg].it.type == 'Variable Setting') { 253 | formatted.push(args[arg].it.name); 254 | defaults.push({name: args[arg].it.name, value: args[arg].it.val}); 255 | continue; 256 | } 257 | 258 | if (args[arg].expecting != null) { 259 | checks.push({name: args[arg].it.val, expecting: args[arg].expecting}); 260 | } 261 | 262 | formatted.push(args[arg].it.val); 263 | } 264 | 265 | a = formatted.join(', '); 266 | } 267 | 268 | var builder = ''; 269 | 270 | if (defaults.length != 0) { 271 | for (item in defaults) { 272 | builder += 'if (' + defaults[item].name + ' === null || ' + defaults[item].name + ' === undefined) { ' + defaults[item].name + ' = ' + defaults[item].value + '; }\n'; 273 | } 274 | } 275 | 276 | if (checks.length != 0) { 277 | for (item in checks) { 278 | builder += 'if (!(typeof(' + checks[item].name + ') === \"' + checks[item].expecting + '\")) { throw \'Expected type of \"' + checks[item].expecting + '\" for var \"' + checks[item].name + '\"\'; }\n'; 279 | } 280 | } 281 | 282 | if (generateBody(body).trim() == ';') { 283 | return 'function ' + name + '(' + a + ') {\ns' + builder + '};'; 284 | } else { 285 | return 'function ' + name + '(' + a + ') {\n' + builder + generateBody(body) + '};'; 286 | } 287 | }; 288 | 289 | var getIFValueAlt = function(name, args, body) { 290 | var a = ''; 291 | 292 | if (args.length != 0) { 293 | a = args.join(', '); 294 | } 295 | 296 | if (generateBody(body).trim() == ';') { 297 | return '.prototype.' + name + ' = function(' + a + ') { };'; 298 | } else { 299 | return '.prototype.' + name + ' = function(' + a + ') {\n' + generateBody(body) + '};'; 300 | } 301 | }; 302 | 303 | var IodeArray = function(args) { 304 | this.type = 'Array'; 305 | this.args = args; 306 | this.val = getIAValue(args); 307 | }; 308 | 309 | var getIAValue = function(args) { 310 | var a = ''; 311 | 312 | if (args.length != 0) { 313 | a = args.join(', '); 314 | } 315 | 316 | return '[' + a + ']'; 317 | }; 318 | 319 | var IodeRange = function(from, to) { 320 | this.type = 'Array Range'; 321 | this.from = from; 322 | this.to = to; 323 | this.val = getIRangeValue(from, to); 324 | }; 325 | 326 | var getIRangeValue = function(from, to) { 327 | return '[' + getNumbers(from.val + '..' + to.val) + ']'; 328 | }; 329 | 330 | var getNumbers = function(stringNumbers) { 331 | var nums = []; 332 | var entries = stringNumbers.split(','); 333 | var length = entries.length; 334 | var i, entry, low, high, range; 335 | 336 | for (i = 0; i < length; i++) { 337 | entry = entries[i]; 338 | 339 | if (!~entry.indexOf('..')) { 340 | nums.push(+entry); 341 | } else { 342 | range = entry.split('..'); 343 | 344 | low = +range[0]; 345 | high = +range[1]; 346 | 347 | if(high < low){ 348 | low = low ^ high; 349 | high = low ^ high; 350 | low = low ^ high; 351 | } 352 | 353 | while (low <= high) { 354 | nums.push(low++); 355 | } 356 | } 357 | } 358 | 359 | return nums.sort(function (a, b) { 360 | return a - b; 361 | }); 362 | }; 363 | 364 | var IodeCall = function(name, args) { 365 | this.type = 'Call'; 366 | this.name = name; 367 | this.args = args; 368 | this.val = getICValue(name, args); 369 | }; 370 | 371 | var getICValue = function(name, args) { 372 | var a = ''; 373 | var b = ''; 374 | 375 | if (args.length != 0) { 376 | a = args.join(', '); 377 | } 378 | 379 | return name + '(' + a + ');'; 380 | }; 381 | 382 | var IodeClass = function(name, constructor, body, extended) { 383 | this.type = 'Class'; 384 | this.name = name; 385 | this.constructor = constructor; 386 | this.body = body; 387 | this.extended = extended; 388 | this.val = getIClassValue(name, constructor, body, extended); 389 | }; 390 | 391 | var getIClassValue = function(name, constructor, body, extended) { 392 | var compile = ''; 393 | var a = ''; 394 | 395 | if (constructor.length != 0) { 396 | a = constructor.join(', '); 397 | } 398 | 399 | if (extended != null) { 400 | compile += 'function extend(target, source) {Object.getOwnPropertyNames(source).forEach(function(propName) {Object.defineProperty(target, propName,Object.getOwnPropertyDescriptor(source, propName));});return target;}'; 401 | compile += '\n\n'; 402 | } 403 | 404 | if (constructor == null || constructor.length == 0) { 405 | compile += 'var ' + name + ' = (function() {\n'; 406 | 407 | for (f in body) { 408 | compile += f.valAlt; 409 | } 410 | } else { 411 | compile += 'var ' + name + ' = (function() {\n'; 412 | compile += 'function ' + name + '(' 413 | 414 | for (a in constructor) { 415 | compile += constructor[a] + ', '; 416 | } 417 | 418 | compile = compile.substring(0, compile.length - 2) + ') {'; 419 | for (a in constructor) { 420 | compile += 'this.' + constructor[a] + ' = ' + constructor[a] + ';'; 421 | } 422 | 423 | for (f in body) { 424 | if (body[f].valAlt == undefined) { 425 | compile += body[f].val; 426 | } 427 | } 428 | 429 | compile += '\n}'; 430 | 431 | for (f in body) { 432 | if (body[f].valAlt != undefined) { 433 | compile += (name + body[f].valAlt); 434 | } 435 | } 436 | } 437 | 438 | compile += '\nreturn ' + name + ';\n})();'; 439 | 440 | if (extended != null) { 441 | compile += '\n\nextend(' + name + '.prototype, ' + extended + '.prototype);'; 442 | } 443 | 444 | return compile; 445 | }; 446 | 447 | var IodeNamespace = function(name, body) { 448 | this.type = 'IodeNamespace'; 449 | this.name = name; 450 | this.body = body; 451 | this.val = getINamespaceValue(name, body); 452 | }; 453 | 454 | var getINamespaceValue = function(name, body) { 455 | var compile = 'var ' + name + ';\n(function(' + name + ') {\n'; 456 | var names = []; 457 | 458 | for (f in body) { 459 | names.push(body[f].name); 460 | 461 | if (body[f].valAlt != undefined) { 462 | compile += body[f].valAlt; 463 | } else { 464 | compile += body[f].val; 465 | } 466 | } 467 | 468 | for (n in names) { 469 | compile += '\n' + name + '.' + names[n] + ' = ' + names[n] + ';'; 470 | } 471 | 472 | compile += '\n})(' + name + ' || (' + name + ' = {}));'; 473 | 474 | return compile; 475 | }; 476 | 477 | var IodeCallList = function(calls) { 478 | this.type = 'Call List'; 479 | this.calls = calls; 480 | this.val = getICLValue(calls); 481 | }; 482 | 483 | var getICLValue = function(calls) { 484 | var out = ''; 485 | 486 | calls = calls.map(function(call) { 487 | if (call.charAt(call.length - 1) == ';') { 488 | return call.substring(0, call.length - 1); 489 | } else { 490 | return call; 491 | } 492 | }); 493 | 494 | if (calls.length != 0) { 495 | out = calls.join('.'); 496 | } 497 | 498 | if (out.charAt(out.length - 1) == ';') { 499 | return out; 500 | } else { 501 | return out + ';'; 502 | } 503 | 504 | return out; 505 | }; 506 | 507 | var IodeIdentNotary = function(ident, expr1, expr2) { 508 | this.type = 'Ternary Notation'; 509 | this.ident = ident; 510 | this.expr1 = expr1; 511 | this.expr2 = expr2; 512 | this.val = getIINValue(ident, expr1, expr2); 513 | } 514 | 515 | var getIINValue = function(ident, expr1, expr2) { 516 | if (ident.val.charAt(ident.val.length - 1) == ';') { 517 | ident.val = ident.val.substring(0, ident.val.length - 1); 518 | } 519 | 520 | return ident.val + ' ? ' + expr1.val + ' : ' + expr2.val; 521 | }; 522 | 523 | var IodeArrayIndex = function(ident, index) { 524 | this.type = 'Array Index'; 525 | this.ident = ident; 526 | this.index = index; 527 | this.val = getIAIValue(ident, index); 528 | } 529 | 530 | var getIAIValue = function(ident, index) { 531 | if (ident.val.charAt(ident.val.length - 1) == ';') { 532 | ident.val = ident.val.substring(0, ident.val.length - 1); 533 | } 534 | 535 | return ident.val + '[' + index.val + ']'; 536 | }; 537 | 538 | var IodeTry = function(body, catchArgs, catchBody) { 539 | this.type = 'Try'; 540 | this.body = body; 541 | this.catchArgs = catchArgs; 542 | this.catchBody = catchBody; 543 | this.val = getITryValue(body, catchArgs, catchBody); 544 | }; 545 | 546 | var getITryValue = function(body, catchArgs, catchBody) { 547 | return 'try {\n' + generateBody(body) + '} catch (' + catchArgs + ') {\n ' + generateBody(catchBody) + '}'; 548 | }; 549 | 550 | var IodeWhile = function(args, body) { 551 | this.type = 'While'; 552 | this.args = args; 553 | this.body = body; 554 | this.val = getIWValue(args, body); 555 | }; 556 | 557 | var getIWValue = function(args, body) { 558 | return 'while (' + args.val + ') {\n' + generateBody(body) + '}'; 559 | }; 560 | 561 | var IodeRepeat = function(args, body) { 562 | this.type = 'Repeat'; 563 | this.args = args; 564 | this.body = body; 565 | this.val = getIRepeatValue(args, body); 566 | }; 567 | 568 | var getIRepeatValue = function(args, body) { 569 | return 'for (var _i = 1; _i <= ' + args.val + '; _i++) {\n' + generateBody(body) + '}'; 570 | }; 571 | 572 | var IodeIf = function(args, body) { 573 | this.type = 'If'; 574 | this.args = args; 575 | this.body = body; 576 | this.val = getIIFValue(args, body); 577 | }; 578 | 579 | var getIIFValue = function(args, body) { 580 | return 'if (' + args.val + ') {\n' + generateBody(body) + '}'; 581 | }; 582 | 583 | var IodeElsIf = function(args, body) { 584 | this.type = 'ElsIf'; 585 | this.args = args; 586 | this.body = body; 587 | this.val = getIEIFValue(args, body); 588 | }; 589 | 590 | var getIEIFValue = function(args, body) { 591 | return 'else if (' + args.val + ') {\n' + generateBody(body) + '}'; 592 | }; 593 | 594 | var IodeElse = function(body) { 595 | this.type = 'Else'; 596 | this.body = body; 597 | this.val = getIELValue(body); 598 | }; 599 | 600 | var getIELValue = function(body) { 601 | return 'else {\n' + generateBody(body) + '}'; 602 | }; 603 | 604 | var IodeIfChain = function(chains) { 605 | this.type = 'If'; 606 | this.chains = chains; 607 | this.val = getIIFCValue(chains); 608 | }; 609 | 610 | var getIIFCValue = function(chains) { 611 | chains = chains.map(function(chain) { 612 | return chain.val; 613 | }); 614 | 615 | return chains.join(' '); 616 | }; 617 | 618 | var IodeUntil = function(args, body) { 619 | this.type = 'Until'; 620 | this.args = args; 621 | this.body = body; 622 | this.val = getIUValue(args, body); 623 | }; 624 | 625 | var getIUValue = function(args, body) { 626 | return 'while (!(' + args.val + ')) {\n' + generateBody(body) + '}'; 627 | }; 628 | 629 | var IodeForeach = function(val, arr, body) { 630 | this.type = 'Foreach'; 631 | this.val = val; 632 | this.arr = arr; 633 | this.val = getIFOREValue(val, arr, body); 634 | }; 635 | 636 | var getIFOREValue = function(val, arr, body) { 637 | return 'for (' + val.val + ' in ' + arr.val + ') {\n' + generateBody(body) + 638 | '}'; 639 | }; 640 | 641 | var IodeConstant = function(name, value) { 642 | this.type = 'Constant Variable Declaration'; 643 | this.name = name; 644 | this.value = value; 645 | this.val = getIConstValue(name, value.val); 646 | }; 647 | 648 | var getIConstValue = function(name, value) { 649 | var output = 650 | 'Object.defineProperty(typeof global === \'object\' ? global : window, \'' + 651 | name + '\',{value:' + value + 652 | ',enumerable:true,writable:false,configurable: false})'; 653 | 654 | if (value.charAt(value.length - 1) == ';') { 655 | return output; 656 | } else { 657 | return output + ';'; 658 | } 659 | }; 660 | 661 | var IodeReturn = function(value) { 662 | this.value = value; 663 | this.val = getIRValue(value); 664 | }; 665 | 666 | var getIRValue = function(value) { 667 | if (value == null) { 668 | return 'return;'; 669 | } else { 670 | if (value.val.charAt(value.val.length - 1) == ';') { 671 | return 'return ' + value.val; 672 | } else { 673 | return 'return ' + value.val + ';'; 674 | } 675 | } 676 | }; 677 | 678 | var IodeContinue = function(value) { 679 | this.value = value; 680 | this.val = getIContValue(value); 681 | }; 682 | 683 | var getIContValue = function(value) { 684 | if (value == null) { 685 | return 'continue;'; 686 | } else { 687 | if (value.val.charAt(value.val.length - 1) == ';') { 688 | return 'continue ' + value.val; 689 | } else { 690 | return 'continue ' + value.val + ';'; 691 | } 692 | } 693 | }; 694 | 695 | var IodeThrow = function(value) { 696 | this.value = value; 697 | this.val = getIThrowValue(value); 698 | }; 699 | 700 | var getIThrowValue = function(value) { 701 | if (value.val.charAt(value.val.length - 1) == ';') { 702 | return 'throw ' + value.val; 703 | } else { 704 | return 'throw ' + value.val + ';'; 705 | } 706 | }; 707 | 708 | var IodeNew = function(obj) { 709 | this.type = 'New'; 710 | this.obj = obj; 711 | this.val = getINewValue(obj); 712 | }; 713 | 714 | var getINewValue = function(obj) { 715 | return 'new ' + obj.val; 716 | }; 717 | 718 | var IodeInclude = function(value) { 719 | this.type = 'Include'; 720 | this.value = value; 721 | this.val = value; 722 | }; 723 | 724 | var IodeNot = function(value) { 725 | this.type = 'Not'; 726 | this.value = value; 727 | this.val = '!' + value.val; 728 | }; 729 | 730 | var IodeMassVariableDeclaration = function(names, values) { 731 | this.type = 'Mass Variable Declaration'; 732 | this.names = names; 733 | this.values = values; 734 | this.val = getIMVDValue(names, values); 735 | }; 736 | 737 | var getIMVDValue = function(names, values) { 738 | var str = ''; 739 | 740 | for (name in names.args) { 741 | str += 'var ' + names.args[name] + ' = ' + values.args[name] + ';\n'; 742 | } 743 | 744 | return str; 745 | }; 746 | 747 | var IodeEmptyMassVariable = function(names) { 748 | this.type = 'Mass Variable Declaration'; 749 | this.names = names; 750 | this.val = getIEMValue(names); 751 | }; 752 | 753 | var getIEMValue = function(names) { 754 | var str = ''; 755 | 756 | for (name in names.args) { 757 | str += 'var ' + names.args[name] + ';\n'; 758 | } 759 | 760 | return str; 761 | }; 762 | 763 | var IodeMassVariableSetting = function(names, values) { 764 | this.type = 'Mass Variable Setting'; 765 | this.names = names; 766 | this.values = values; 767 | this.val = getIMVSValue(names, values); 768 | }; 769 | 770 | var getIMVSValue = function(names, values) { 771 | var str = ''; 772 | 773 | for (name in names.args) { 774 | str += names.args[name] + ' = ' + values.args[name] + ';\n'; 775 | } 776 | 777 | return str; 778 | }; 779 | 780 | var IodeFor = function(name, val, cond, iter, body) { 781 | this.type = 'For'; 782 | this.name = name; 783 | this.val = val; 784 | this.cond = cond; 785 | this.iter = iter; 786 | this.body = body; 787 | this.val = getIForValue(name, val, cond, iter, body); 788 | }; 789 | 790 | var getIForValue = function(name, val, cond, iter, body) { 791 | return 'for (var ' + name + ' = ' + val.val + '; ' 792 | + cond.val + '; ' + iter.val + ') {\n' + generateBody(body) + '\n}'; 793 | }; 794 | 795 | var IodeNumberPlusMinus = function(num, op) { 796 | this.type = 'Number Append/Depend'; 797 | this.num = num; 798 | this.op = op; 799 | this.val = num.val + op; 800 | }; 801 | 802 | var IodeEmptyVariable = function(name) { 803 | this.type = 'Variable Declaration'; 804 | this.name = name; 805 | this.val = 'var ' + name + ';'; 806 | }; 807 | 808 | var IodePattern = function(pattern) { 809 | this.type = 'Pattern'; 810 | this.pattern = pattern; 811 | this.val = getIPatternValue(pattern); 812 | }; 813 | 814 | var getIPatternValue = function(pattern) { 815 | return pattern; 816 | }; 817 | 818 | var IodeJSON = function(elements) { 819 | this.type = 'JSON'; 820 | this.elements = elements; 821 | this.val = getIJSONValue(elements); 822 | }; 823 | 824 | var getIJSONValue = function(elements) { 825 | var builder = '{'; 826 | 827 | for (element in elements) { 828 | builder += '\n' + elements[element].left + ':' + elements[element].right + ','; 829 | } 830 | 831 | builder = builder.substring(0, builder.length - 1) + '}'; 832 | 833 | return builder; 834 | }; 835 | 836 | var IodeEmbedded = function(js) { 837 | this.type = 'Embedded JavaScript'; 838 | this.js = js; 839 | this.val = js; 840 | }; 841 | 842 | var IodeNewline = function() { 843 | this.type = 'Newline'; 844 | this.val = ''; 845 | }; 846 | 847 | exports.IodeIdentifier = IodeIdentifier; 848 | exports.IodeNumber = IodeNumber; 849 | exports.IodeString = IodeString; 850 | exports.IodeBoolean = IodeBoolean; 851 | exports.IodeBinaryOp = IodeBinaryOp; 852 | exports.IodeVariableDeclaration = IodeVariableDeclaration; 853 | exports.IodeVariableSetting = IodeVariableSetting; 854 | exports.IodeFunction = IodeFunction; 855 | exports.IodeParenthesis = IodeParenthesis; 856 | exports.IodeCall = IodeCall; 857 | exports.IodeCallList = IodeCallList; 858 | exports.IodeNewline = IodeNewline; 859 | exports.IodeNew = IodeNew; 860 | exports.IodeWhile = IodeWhile; 861 | exports.IodeUntil = IodeUntil; 862 | exports.IodeIf = IodeIf; 863 | exports.IodeElsIf = IodeElsIf; 864 | exports.IodeElse = IodeElse; 865 | exports.IodeIfChain = IodeIfChain; 866 | exports.IodeNot = IodeNot; 867 | exports.IodeForeach = IodeForeach; 868 | exports.IodeConstant = IodeConstant; 869 | exports.IodeReturn = IodeReturn; 870 | exports.IodeContinue = IodeContinue; 871 | exports.IodeThrow = IodeThrow; 872 | exports.IodeInclude = IodeInclude; 873 | exports.IodeIdentNotary = IodeIdentNotary; 874 | exports.IodeArray = IodeArray; 875 | exports.IodeArrayIndex = IodeArrayIndex; 876 | exports.IodeRange = IodeRange; 877 | exports.IodeMassVariableDeclaration = IodeMassVariableDeclaration; 878 | exports.IodeMassVariableSetting = IodeMassVariableSetting; 879 | exports.IodeFor = IodeFor; 880 | exports.IodeNumberPlusMinus = IodeNumberPlusMinus; 881 | exports.IodeEmptyVariable = IodeEmptyVariable; 882 | exports.IodeEmptyMassVariable = IodeEmptyMassVariable; 883 | exports.IodePercentage = IodePercentage; 884 | exports.IodeClass = IodeClass; 885 | exports.IodeRepeat = IodeRepeat; 886 | exports.IodePattern = IodePattern; 887 | exports.IodeJSON = IodeJSON; 888 | exports.IodeNamespace = IodeNamespace; 889 | exports.IodeTry = IodeTry; 890 | exports.IodeEmbedded = IodeEmbedded; 891 | exports.IodePrototype = IodePrototype; 892 | -------------------------------------------------------------------------------- /src/parser.js: -------------------------------------------------------------------------------- 1 | var Lexer = require("./lexer").Lexer, 2 | fs = require("fs"), 3 | path = require("path"), 4 | TokenType = require("./token").TokenType, 5 | IodeNumber = require("./ast").IodeNumber, 6 | IodeBinaryOp = require("./ast").IodeBinaryOp, 7 | IodeIdentifier = require("./ast").IodeIdentifier, 8 | IodeVariableDeclaration = require("./ast").IodeVariableDeclaration, 9 | IodeBoolean = require("./ast").IodeBoolean, 10 | IodeString = require("./ast").IodeString, 11 | IodeFunction = require("./ast").IodeFunction, 12 | IodePrototype = require("./ast").IodePrototype, 13 | IodeParenthesis = require("./ast").IodeParenthesis, 14 | IodeCall = require("./ast").IodeCall, 15 | IodeCallList = require("./ast").IodeCallList, 16 | IodeNewline = require("./ast").IodeNewline, 17 | IodeWhile = require("./ast").IodeWhile, 18 | IodeUntil = require("./ast").IodeUntil, 19 | IodeIf = require("./ast").IodeIf, 20 | IodeElsIf = require("./ast").IodeElsIf, 21 | IodeElse = require("./ast").IodeElse, 22 | IodeIfChain = require("./ast").IodeIfChain, 23 | IodeNot = require("./ast").IodeNot, 24 | IodeNew = require("./ast").IodeNew, 25 | IodeForeach = require("./ast").IodeForeach, 26 | IodeConstant = require("./ast").IodeConstant, 27 | IodeReturn = require("./ast").IodeReturn, 28 | IodeContinue = require("./ast").IodeContinue, 29 | IodeThrow = require("./ast").IodeThrow, 30 | IodeInclude = require("./ast").IodeInclude, 31 | IodeIdentNotary = require("./ast").IodeIdentNotary, 32 | IodeArray = require("./ast").IodeArray, 33 | IodeArrayIndex = require("./ast").IodeArrayIndex, 34 | IodeRange = require("./ast").IodeRange, 35 | IodeMassVariableDeclaration = require("./ast").IodeMassVariableDeclaration, 36 | IodeMassVariableSetting = require("./ast").IodeMassVariableSetting, 37 | IodeFor = require("./ast").IodeFor, 38 | IodeNumberPlusMinus = require("./ast").IodeNumberPlusMinus, 39 | IodeEmptyVariable = require("./ast").IodeEmptyVariable, 40 | IodeEmptyMassVariable = require("./ast").IodeEmptyMassVariable, 41 | IodeClass = require("./ast").IodeClass, 42 | IodePercentage = require("./ast").IodePercentage, 43 | IodeRepeat = require("./ast").IodeRepeat, 44 | IodePattern = require("./ast").IodePattern, 45 | IodeJSON = require("./ast").IodeJSON, 46 | IodeNamespace = require("./ast").IodeNamespace, 47 | IodeTry = require("./ast").IodeTry, 48 | IodeEmbedded = require("./ast").IodeEmbedded, 49 | IodeVariableSetting = require("./ast").IodeVariableSetting; 50 | 51 | var Parser = function(code, cdir) { 52 | this.lexer = new Lexer(code); 53 | this.code = code; 54 | this.pos = 0; 55 | this.line = 1; 56 | this.cdir = cdir; 57 | this.totalTokens = this.lexer.tokens.length; 58 | this.constants = []; 59 | 60 | this.peekToken = function() { 61 | return this.lexer.peekToken(); 62 | }; 63 | 64 | this.peekCheck = function(t) { 65 | if (this.peekToken() == undefined) { 66 | return null; 67 | } 68 | return this.peekToken().type == t; 69 | }; 70 | 71 | this.peekSpecificCheck = function(t, i) { 72 | if (this.peekSpecificToken(i) == undefined) { 73 | return null; 74 | } 75 | return this.peekSpecificToken(i).type == t; 76 | }; 77 | 78 | this.peekSpecificToken = function(i) { 79 | return this.lexer.peekSpecificToken(i); 80 | }; 81 | 82 | this.skipNewline = function() { 83 | if (this.peekCheck(TokenType.NEWLINE)) { 84 | this.nextToken(); 85 | } 86 | }; 87 | 88 | this.nextToken = function() { 89 | this.pos++; 90 | 91 | if (this.peekToken().type == TokenType.NEWLINE) { 92 | this.line++; 93 | } 94 | 95 | return this.lexer.nextToken(); 96 | }; 97 | 98 | this.nextTokenNewline = function() { 99 | this.pos++; 100 | 101 | if (this.peekToken().type == TokenType.NEWLINE) { 102 | this.line++; 103 | } 104 | 105 | var tok = this.lexer.nextToken(); 106 | 107 | if (this.peekCheck(TokenType.NEWLINE)) { 108 | this.nextToken(); 109 | } 110 | 111 | return tok; 112 | }; 113 | 114 | this.isValidType = function(type) { 115 | return type == "number" || type == "string" || 116 | type == "boolean" || type == "undefined" || 117 | type == "object" || type == "function"; 118 | }; 119 | 120 | this.error = function(msg) { 121 | console.log(); 122 | console.log("[x] " + msg + " on line #" + this.line + "."); 123 | console.log(); 124 | process.exit(1); 125 | }; 126 | 127 | this.warning = function(msg) { 128 | console.log(); 129 | console.log("[!] " + msg + " on line #" + this.line + "."); 130 | console.log(); 131 | }; 132 | 133 | this.isNumberOperator = function() { 134 | return ( 135 | this.peekCheck(TokenType.ADD) || 136 | this.peekCheck(TokenType.SUB) || 137 | this.peekCheck(TokenType.MUL) || 138 | this.peekCheck(TokenType.DIV) || 139 | this.peekCheck(TokenType.EXP) || 140 | this.peekCheck(TokenType.GT) || 141 | this.peekCheck(TokenType.LT) || 142 | this.peekCheck(TokenType.GTEQUALS) || 143 | this.peekCheck(TokenType.LTEQUALS) || 144 | this.peekCheck(TokenType.IS) || 145 | this.peekCheck(TokenType.NEQUALS) || 146 | this.peekCheck(TokenType.MODULUS) || 147 | this.peekCheck(TokenType.SUBSUB) || 148 | this.peekCheck(TokenType.PLUSPLUS) 149 | ); 150 | }; 151 | 152 | this.isStringOperator = function() { 153 | return ( 154 | this.peekCheck(TokenType.ADD) || 155 | this.peekCheck(TokenType.GT) || 156 | this.peekCheck(TokenType.LT) || 157 | this.peekCheck(TokenType.GTEQUALS) || 158 | this.peekCheck(TokenType.LTEQUALS) || 159 | this.peekCheck(TokenType.IS) || 160 | this.peekCheck(TokenType.NEQUALS) 161 | ); 162 | }; 163 | 164 | this.isIdentOperator = function() { 165 | return ( 166 | this.peekSpecificCheck(TokenType.ADD, 2) || 167 | this.peekSpecificCheck(TokenType.SUB, 2) || 168 | this.peekSpecificCheck(TokenType.MUL, 2) || 169 | this.peekSpecificCheck(TokenType.DIV, 2) || 170 | this.peekSpecificCheck(TokenType.GT, 2) || 171 | this.peekSpecificCheck(TokenType.LT, 2) || 172 | this.peekSpecificCheck(TokenType.GTEQUALS, 2) || 173 | this.peekSpecificCheck(TokenType.LTEQUALS, 2) || 174 | this.peekSpecificCheck(TokenType.IS, 2) || 175 | this.peekSpecificCheck(TokenType.NEQUALS, 2) || 176 | this.peekSpecificCheck(TokenType.MODULUS, 2) 177 | ); 178 | }; 179 | 180 | // [0-9]+ 181 | this.parseNumber = function() { 182 | var num = new IodeNumber(this.nextToken().value); 183 | this.skipNewline(); 184 | 185 | if (this.peekCheck(TokenType.PERCENT)) { 186 | this.nextTokenNewline(); 187 | 188 | num = new IodePercentage(num.val) 189 | } 190 | 191 | var op; 192 | var right; 193 | 194 | if (this.peekCheck(TokenType.SUBSUB) || this.peekCheck(TokenType.PLUSPLUS)) { 195 | var op = this.nextToken().value; 196 | this.skipNewline(); 197 | return new IodeNumberPlusMinus(num, op); 198 | } 199 | 200 | if (!this.isNumberOperator()) { 201 | return num; 202 | } 203 | 204 | while (this.isNumberOperator()) { 205 | op = this.nextToken().value; 206 | this.skipNewline(); 207 | right = this.parseNextLiteral(); 208 | this.skipNewline(); 209 | } 210 | 211 | num = new IodeBinaryOp(num, op, right); 212 | 213 | if (this.peekCheck(TokenType.AND) || this.peekCheck(TokenType.OR)) { 214 | while (this.peekCheck(TokenType.AND) || this.peekCheck(TokenType.OR)) { 215 | op = this.nextToken().value; 216 | this.skipNewline(); 217 | right = this.parseNextLiteral(); 218 | this.skipNewline(); 219 | num = new IodeBinaryOp(num, op, right); 220 | } 221 | } 222 | 223 | return num; 224 | }; 225 | 226 | // """ .* """ 227 | this.parseString = function() { 228 | var str = new IodeString(this.nextToken().value); 229 | this.skipNewline(); 230 | var op; 231 | var right; 232 | 233 | if (!this.isStringOperator()) { 234 | 235 | return str; 236 | } 237 | 238 | while (this.isStringOperator()) { 239 | op = this.nextToken().value; 240 | 241 | this.skipNewline(); 242 | right = this.parseNextLiteral(); 243 | this.skipNewline(); 244 | } 245 | 246 | var num = new IodeBinaryOp(str, op, right); 247 | 248 | if (this.peekCheck(TokenType.AND) || this.peekCheck(TokenType.OR)) { 249 | while (this.peekCheck(TokenType.AND) || this.peekCheck(TokenType.OR)) { 250 | op = this.nextToken().value; 251 | this.skipNewline(); 252 | right = this.parseNextLiteral(); 253 | this.skipNewline(); 254 | num = new IodeBinaryOp(num, op, right); 255 | } 256 | } 257 | 258 | return num; 259 | }; 260 | 261 | // true / false / nil / undefined 262 | this.parseBoolean = function() { 263 | var bool = new IodeBoolean(this.nextToken().value); 264 | this.skipNewline(); 265 | var op; 266 | var right; 267 | 268 | if (!(this.peekCheck(TokenType.IS) || this.peekCheck(TokenType.NEQUALS) || this.peekCheck(TokenType.MODULUS))) { 269 | return bool; 270 | } 271 | 272 | while (this.peekCheck(TokenType.IS) || this.peekCheck(TokenType.NEQUALS) || this.peekCheck(TokenType.MODULUS)) { 273 | op = this.nextToken().value; 274 | this.skipNewline(); 275 | right = this.parseNextLiteral(); 276 | this.skipNewline(); 277 | } 278 | 279 | var num = new IodeBinaryOp(bool, op, right); 280 | 281 | if (this.peekCheck(TokenType.AND) || this.peekCheck(TokenType.OR)) { 282 | while (this.peekCheck(TokenType.AND) || this.peekCheck(TokenType.OR)) { 283 | op = this.nextToken().value; 284 | this.skipNewline(); 285 | right = this.parseNextLiteral(); 286 | this.skipNewline(); 287 | num = new IodeBinaryOp(num, op, right); 288 | } 289 | } 290 | 291 | return num; 292 | }; 293 | 294 | // [a-zA-Z][a-zA-Z0-9]* 295 | this.parseIdentifier = function() { 296 | if (this.isIdentOperator()) { 297 | 298 | var ident = new IodeIdentifier(this.nextToken().value); 299 | this.skipNewline(); 300 | var op; 301 | var right; 302 | 303 | while (this.isIdentOperator()) { 304 | 305 | op = this.nextToken().value; 306 | this.skipNewline(); 307 | right = this.parseNextLiteral(); 308 | this.skipNewline(); 309 | } 310 | 311 | var num = new IodeBinaryOp(ident, op, right); 312 | 313 | if (this.peekCheck(TokenType.AND) || this.peekCheck(TokenType.OR)) { 314 | while (this.peekCheck(TokenType.AND) || this.peekCheck(TokenType.OR)) { 315 | op = this.nextToken().value; 316 | this.skipNewline(); 317 | right = this.parseNextLiteral(); 318 | this.skipNewline(); 319 | num = new IodeBinaryOp(num, op, right); 320 | } 321 | } 322 | 323 | return num; 324 | } else if (this.peekSpecificCheck(TokenType.EQUALS, 2)) { 325 | return this.parseVariableSetting(); 326 | } else if (this.peekSpecificCheck(TokenType.SUBSUB, 2) 327 | || this.peekSpecificCheck(TokenType.PLUSPLUS, 2)) { 328 | var ident = new IodeIdentifier(this.nextToken().value); 329 | this.skipNewline(); 330 | var op = this.nextToken().value; 331 | this.skipNewline(); 332 | return new IodeNumberPlusMinus(ident, op) 333 | } else if (this.peekSpecificCheck(TokenType.QUESTION, 2)) { 334 | var ident = new IodeIdentifier(this.nextToken().value); 335 | this.nextTokenNewline(); 336 | var expr1 = this.parseNextLiteral(); 337 | 338 | if (this.peekCheck(TokenType.COLON)) { 339 | this.skipNewline(); 340 | this.nextToken(); 341 | var expr2 = this.parseNextLiteral(); 342 | return new IodeIdentNotary(ident, expr1, expr2); 343 | } else { 344 | this.error("Expected \":\" after first expression"); 345 | } 346 | } else if (this.peekSpecificCheck(TokenType.LBRACK, 2)) { 347 | var ident = new IodeIdentifier(this.nextToken().value); 348 | this.nextTokenNewline(); 349 | var expr = this.parseNextLiteral(); 350 | this.skipNewline(); 351 | 352 | if (this.peekCheck(TokenType.RBRACK)) { 353 | this.nextToken(); 354 | return new IodeArrayIndex(ident, expr); 355 | } else { 356 | this.error("Expected \"]\" at the end of array index"); 357 | } 358 | } else if (this.peekSpecificCheck(TokenType.LPAREN, 2)) { 359 | var name = this.nextToken().value; 360 | this.nextTokenNewline(); 361 | var args = []; 362 | 363 | while (!(this.peekCheck(TokenType.RPAREN))) { 364 | var arg = this.parseNext().val; 365 | this.skipNewline(); 366 | 367 | if (arg.charAt(arg.length - 1) == ";") { 368 | arg = arg.substring(0, arg.length - 1); 369 | } 370 | 371 | args.push(arg); 372 | 373 | if (this.peekCheck(TokenType.COMMA)) { 374 | this.nextTokenNewline(); 375 | } else if (this.peekCheck(TokenType.RPAREN)) { 376 | break; 377 | } else { 378 | this.error("Expected a \",\" or \")\", got \"" + this.peekToken().value + 379 | "\""); 380 | } 381 | } 382 | 383 | if (this.peekCheck(TokenType.RPAREN)) { 384 | this.nextToken(); 385 | } else { 386 | this.error("Expected a \")\", got \"" + this.peekToken().value + "\""); 387 | } 388 | 389 | var calls = [new IodeCall(name, args).val]; 390 | 391 | if (this.peekCheck(TokenType.DOT)) { 392 | this.skipNewline(); 393 | 394 | while (this.peekCheck(TokenType.DOT)) { 395 | this.nextTokenNewline(); 396 | calls.push(this.parseNextLiteral().val); 397 | } 398 | 399 | var call = new IodeCallList(calls); 400 | 401 | if (this.isNumberOperator()) { 402 | 403 | this.skipNewline(); 404 | var op; 405 | var right; 406 | 407 | while (this.isNumberOperator()) { 408 | 409 | op = this.nextToken().value; 410 | this.skipNewline(); 411 | right = this.parseNextLiteral(); 412 | this.skipNewline(); 413 | } 414 | 415 | call = new IodeBinaryOp(call, op, right); 416 | 417 | if (this.peekCheck(TokenType.AND) || this.peekCheck(TokenType.OR)) { 418 | while (this.peekCheck(TokenType.AND) || this.peekCheck(TokenType.OR)) { 419 | op = this.nextToken().value; 420 | this.skipNewline(); 421 | right = this.parseNextLiteral(); 422 | this.skipNewline(); 423 | call = new IodeBinaryOp(call, op, right); 424 | } 425 | } 426 | } else { 427 | if (this.peekToken() != undefined) { 428 | if (this.peekCheck(TokenType.NEWLINE)) { 429 | this.nextToken(); 430 | this.skipNewline(); 431 | } else if (this.peekCheck(TokenType.QUESTION)) { 432 | this.nextToken(); 433 | 434 | var expr1 = this.parseNextLiteral(); 435 | 436 | if (this.peekCheck(TokenType.COLON)) { 437 | this.skipNewline(); 438 | this.nextToken(); 439 | var expr2 = this.parseNextLiteral(); 440 | call = new IodeIdentNotary(call, expr1, expr2); 441 | } else { 442 | this.error("Expected \":\" after first expression"); 443 | } 444 | } else if (this.peekCheck(TokenType.LBRACK)) { 445 | var ident = new IodeIdentifier(this.nextToken().value); 446 | this.skipNewline(); 447 | this.nextToken(); 448 | var expr = this.parseNextLiteral(); 449 | this.skipNewline(); 450 | 451 | if (this.peekCheck(TokenType.RBRACK)) { 452 | this.nextToken(); 453 | call = new IodeArrayIndex(ident, expr); 454 | } else { 455 | this.error("Expected \"]\" at the end of array index"); 456 | } 457 | } else if (this.peekCheck(TokenType.AND) || this.peekCheck(TokenType.OR)) { 458 | call = new IodeBinaryOp(call, op, right); 459 | 460 | while (this.peekCheck(TokenType.AND) || this.peekCheck(TokenType.OR)) { 461 | op = this.nextToken().value; 462 | this.skipNewline(); 463 | right = this.parseNextLiteral(); 464 | this.skipNewline(); 465 | } 466 | 467 | return call; 468 | } else { 469 | return call; 470 | } 471 | } else { 472 | this.error("Expected newline"); 473 | } 474 | } 475 | 476 | return call; 477 | } else { 478 | if (this.peekToken() == undefined) { 479 | this.error("Expected newline"); 480 | } 481 | 482 | var call_ = new IodeCall(name, args); 483 | 484 | if (this.isNumberOperator()) { 485 | 486 | this.skipNewline(); 487 | var op; 488 | var right; 489 | 490 | while (this.isNumberOperator()) { 491 | 492 | op = this.nextToken().value; 493 | this.skipNewline(); 494 | right = this.parseNextLiteral(); 495 | this.skipNewline(); 496 | } 497 | 498 | call_ = new IodeBinaryOp(call_, op, right); 499 | 500 | if (this.peekCheck(TokenType.AND) || this.peekCheck(TokenType.OR)) { 501 | while (this.peekCheck(TokenType.AND) || this.peekCheck(TokenType.OR)) { 502 | op = this.nextToken().value; 503 | this.skipNewline(); 504 | right = this.parseNextLiteral(); 505 | this.skipNewline(); 506 | call_ = new IodeBinaryOp(call_, op, right); 507 | } 508 | } 509 | } else if (this.peekCheck(TokenType.QUESTION)) { 510 | this.nextToken(); 511 | 512 | var expr1 = this.parseNextLiteral(); 513 | 514 | if (this.peekCheck(TokenType.COLON)) { 515 | this.skipNewline(); 516 | this.nextToken(); 517 | var expr2 = this.parseNextLiteral(); 518 | call_ = new IodeIdentNotary(call_, expr1, expr2); 519 | } else { 520 | this.error("Expected \":\" after first expression"); 521 | } 522 | } else if (this.peekCheck(TokenType.LBRACK)) { 523 | var ident = new IodeIdentifier(this.nextToken().value); 524 | this.skipNewline(); 525 | this.nextToken(); 526 | var expr = this.parseNextLiteral(); 527 | this.skipNewline(); 528 | 529 | if (this.peekCheck(TokenType.RBRACK)) { 530 | this.nextToken(); 531 | call_ = new IodeArrayIndex(ident, expr); 532 | } else { 533 | this.error("Expected \"]\" at the end of array index"); 534 | } 535 | } 536 | 537 | return call_; 538 | } 539 | } else { 540 | var ident = new IodeIdentifier(this.nextToken().value); 541 | var calls = [ident.val]; 542 | 543 | if (this.peekCheck(TokenType.DOT)) { 544 | while (this.peekCheck(TokenType.DOT)) { 545 | this.nextTokenNewline(); 546 | calls.push(this.parseNextLiteral().val); 547 | } 548 | 549 | ident = new IodeCallList(calls); 550 | } 551 | 552 | var num = new IodeBinaryOp(ident, op, right); 553 | 554 | if (this.peekCheck(TokenType.AND) || this.peekCheck(TokenType.OR)) { 555 | while (this.peekCheck(TokenType.AND) || this.peekCheck(TokenType.OR)) { 556 | op = this.nextToken().value; 557 | this.skipNewline(); 558 | right = this.parseNextLiteral(); 559 | this.skipNewline(); 560 | num = new IodeBinaryOp(num, op, right); 561 | } 562 | } else { 563 | return ident; 564 | } 565 | 566 | return num; 567 | } 568 | }; 569 | 570 | // var ident "=" expr 571 | this.parseVariableDeclaration = function() { 572 | this.nextTokenNewline(); 573 | var expectedType = null; 574 | 575 | if (this.peekCheck(TokenType.IDENTIFIER)) { 576 | var name = this.nextToken().value; 577 | 578 | if (this.constants.indexOf(name) > -1) { 579 | this.warning("\"" + name + 580 | "\" is already defined as a constant and the value won\"t be changed"); 581 | } 582 | 583 | if (this.peekCheck(TokenType.COLON)) { 584 | this.nextTokenNewline(); 585 | 586 | if (this.peekCheck(TokenType.IDENTIFIER)) { 587 | var type = this.nextToken().value; 588 | 589 | if (this.isValidType(type.toLowerCase())) { 590 | expectedType = type.toLowerCase(); 591 | } else { 592 | this.error("Unknown type: \"" + type.value + "\""); 593 | } 594 | } 595 | } 596 | 597 | if (this.peekCheck(TokenType.EQUALS)) { 598 | this.skipNewline(); 599 | this.nextTokenNewline(); 600 | var oldLine = this.line; 601 | 602 | var val = this.parseNextLiteral(); 603 | 604 | if (this.peekCheck(TokenType.NEWLINE)) { 605 | this.nextTokenNewline(); 606 | } else if (this.line > oldLine) { 607 | return new IodeVariableDeclaration(name, val, expectedType); 608 | } else { 609 | this.error("Expected a newline"); 610 | } 611 | 612 | return new IodeVariableDeclaration(name, val, expectedType); 613 | } else if (this.peekCheck(TokenType.NEWLINE)) { 614 | this.nextTokenNewline(); 615 | return new IodeEmptyVariable(name); 616 | } else { 617 | this.error("Expected \"=\" or a newline, got \"" + this.peekToken().value + "\""); 618 | } 619 | } else if (this.peekCheck(TokenType.LBRACK)) { 620 | return this.parseArray(true); 621 | } else { 622 | this.error("Expected a variable name, got \"" + this.peekToken().value + 623 | "\""); 624 | } 625 | }; 626 | 627 | // "const" ident "=" expr 628 | this.parseConst = function() { 629 | this.nextTokenNewline(); 630 | 631 | if (this.peekCheck(TokenType.IDENTIFIER)) { 632 | var name = this.nextToken().value; 633 | 634 | if (this.constants.indexOf(name) > -1) { 635 | this.warning("\"" + name + 636 | "\" is already defined and the value won\"t be changed"); 637 | } 638 | 639 | this.skipNewline(); 640 | 641 | if (this.peekCheck(TokenType.EQUALS)) { 642 | this.nextTokenNewline(); 643 | var oldLine = this.line; 644 | 645 | var val = this.parseNextLiteral(); 646 | 647 | if (this.peekCheck(TokenType.NEWLINE)) { 648 | this.nextTokenNewline(); 649 | } else if (this.line > oldLine) { 650 | this.constants.push(name); 651 | return new IodeConstant(name, val); 652 | } else { 653 | this.error("Expected a newline"); 654 | } 655 | 656 | this.constants.push(name); 657 | return new IodeConstant(name, val); 658 | } else { 659 | this.error("Expected \"=\", got \"" + this.peekToken().value + "\""); 660 | } 661 | } else { 662 | this.error("Expected a variable name, got \"" + this.peekToken().value + 663 | "\""); 664 | } 665 | }; 666 | 667 | // ident "=" expr 668 | this.parseVariableSetting = function() { 669 | var name = this.nextToken().value; 670 | 671 | if (this.constants.indexOf(name) > -1) { 672 | this.warning("\"" + name + 673 | "\" is a constant variable and the value won\"t be changed"); 674 | } 675 | 676 | this.skipNewline(); 677 | 678 | if (this.peekCheck(TokenType.EQUALS)) { 679 | this.nextTokenNewline(); 680 | var oldLine = this.line; 681 | 682 | var val = this.parseNextLiteral(); 683 | 684 | if (this.peekCheck(TokenType.NEWLINE)) { 685 | this.nextTokenNewline(); 686 | } else if (this.line > oldLine) { 687 | return new IodeVariableSetting(name, val); 688 | } else if (this.peekCheck(TokenType.RPAREN) || this.peekCheck(TokenType.COMMA)) { 689 | return new IodeVariableSetting(name, val); 690 | } else { 691 | this.error("Expected a newline"); 692 | } 693 | 694 | return new IodeVariableSetting(name, val); 695 | } else { 696 | this.error("Expected \"=\", got \"" + this.peekToken().value + "\""); 697 | } 698 | }; 699 | 700 | // fn name (expr?)* { ... } 701 | this.parseFunction = function() { 702 | this.nextTokenNewline(); 703 | var lbrace = false; 704 | 705 | if (this.peekCheck(TokenType.IDENTIFIER)) { 706 | var name = this.nextToken().value; 707 | this.skipNewline(); 708 | var args = []; 709 | var originalArgs = []; 710 | var body = []; 711 | 712 | if (this.peekCheck(TokenType.LPAREN)) { 713 | this.nextTokenNewline(); 714 | 715 | while (!(this.peekCheck(TokenType.RPAREN))) { 716 | var arg = this.parseNext(); 717 | var expectedType = null; 718 | this.skipNewline(); 719 | 720 | if (this.peekCheck(TokenType.COLON)) { 721 | this.nextTokenNewline(); 722 | 723 | if (this.peekCheck(TokenType.IDENTIFIER)) { 724 | var type = this.nextToken().value; 725 | this.skipNewline(); 726 | 727 | if (this.isValidType(type.toLowerCase())) { 728 | expectedType = type.toLowerCase(); 729 | } else { 730 | this.error("Unknown type: \"" + type.value + "\""); 731 | } 732 | } 733 | } 734 | 735 | originalArgs.push(arg); 736 | args.push({ it: arg, expecting: expectedType }); 737 | 738 | if (!(this.peekCheck(TokenType.COMMA) || this.peekCheck(TokenType.RPAREN))) { 739 | this.error("Expected a \",\" or \"{\", got \"" + this.peekToken().value + 740 | "\""); 741 | } else if (this.peekCheck(TokenType.RPAREN)) { 742 | break; 743 | } else { 744 | this.nextTokenNewline(); 745 | } 746 | } 747 | } else if (this.peekCheck(TokenType.LBRACE)) { 748 | lbrace = true; 749 | this.nextTokenNewline(); 750 | } else { 751 | this.error("Expected a \"(\" or a \"{\", got \"" + this.peekToken().value + "\""); 752 | } 753 | 754 | if (!lbrace) { 755 | if (this.peekCheck(TokenType.RPAREN) || lbrace) { 756 | this.nextTokenNewline(); 757 | } else if (this.peekCheck(TokenType.LBRACE) || lbrace) { 758 | this.nextTokenNewline(); 759 | } else { 760 | this.error("Expected a \")\" or a \"{\", got \"" + this.peekToken().value + "\""); 761 | } 762 | } 763 | 764 | if (this.peekCheck(TokenType.COLON)) { 765 | this.nextToken(); 766 | this.skipNewline(); 767 | 768 | if (this.peekCheck(TokenType.IDENTIFIER)) { 769 | var proto = this.nextToken().value; 770 | this.skipNewline(); 771 | 772 | if (this.peekCheck(TokenType.LBRACE)) { 773 | this.nextTokenNewline(); 774 | } else { 775 | this.error("Expected a \"{\""); 776 | } 777 | 778 | while (!(this.peekCheck(TokenType.RBRACE))) { 779 | var stmt = this.parseNext(); 780 | this.skipNewline(); 781 | 782 | body.push(stmt); 783 | 784 | if (this.peekCheck(TokenType.NEWLINE)) { 785 | this.nextTokenNewline(); 786 | } else if (this.peekCheck(TokenType.RBRACE)) { 787 | break; 788 | } 789 | } 790 | 791 | if (this.peekCheck(TokenType.RBRACE)) { 792 | this.nextTokenNewline(); 793 | } else { 794 | this.error("Expected a \"}\", got \"" + this.peekToken().value + "\""); 795 | } 796 | 797 | return new IodePrototype(name, proto, originalArgs, body); 798 | } else { 799 | this.error("Expected an identifier, got \"" + this.peekToken().value + "\""); 800 | } 801 | } else if (this.peekCheck(TokenType.LBRACE) || lbrace) { 802 | if (!lbrace) { 803 | this.nextTokenNewline(); 804 | } 805 | 806 | while (!(this.peekCheck(TokenType.RBRACE))) { 807 | var stmt = this.parseNext(); 808 | this.skipNewline(); 809 | 810 | body.push(stmt); 811 | 812 | if (this.peekCheck(TokenType.NEWLINE)) { 813 | this.nextTokenNewline(); 814 | this.error("Expected a newline, got \"" + this.peekToken().value + 815 | "\""); 816 | } else if (this.peekCheck(TokenType.RBRACE)) { 817 | break; 818 | } 819 | } 820 | 821 | if (this.peekCheck(TokenType.RBRACE)) { 822 | this.nextTokenNewline(); 823 | } else { 824 | this.error("Expected a \"}\", got \"" + this.peekToken().value + "\""); 825 | } 826 | 827 | return new IodeFunction(name, args, body); 828 | } else { 829 | this.error("Expected a \"{\" or a \"::\", got \"" + this.peekToken().value + "\""); 830 | } 831 | } else if (this.peekCheck(TokenType.ARROW)) { 832 | this.nextTokenNewline(); 833 | var args = []; 834 | 835 | if (this.peekCheck(TokenType.LPAREN)) { 836 | this.nextTokenNewline(); 837 | } else { 838 | this.error("Expected a \"(\", got a \"" + this.peekToken().value + "\""); 839 | } 840 | 841 | while (!(this.peekCheck(TokenType.RPAREN))) { 842 | var arg = this.parseNextLiteral().val; 843 | this.skipNewline(); 844 | 845 | if (arg.charAt(arg.length - 1) == ";") { 846 | arg = arg.substring(0, arg.length - 1); 847 | } 848 | 849 | args.push(arg); 850 | 851 | if (!(this.peekCheck(TokenType.COMMA) || this.peekCheck(TokenType.RPAREN))) { 852 | this.error("Expected a \",\" or \")\", got \"" + this.peekToken().value + 853 | "\""); 854 | } else if (this.peekCheck(TokenType.RPAREN)) { 855 | break; 856 | } else { 857 | this.nextTokenNewline(); 858 | } 859 | } 860 | 861 | if (this.peekCheck(TokenType.RPAREN)) { 862 | this.nextTokenNewline(); 863 | } 864 | 865 | var body = [new IodeReturn(this.parseNextLiteral())]; 866 | 867 | if (this.peekCheck(TokenType.NEWLINE)) { 868 | this.nextTokenNewline(); 869 | } else { 870 | this.error("Expected a newline, got \"" + this.peekToken().value + "\""); 871 | } 872 | 873 | return new IodeFunction("", args, body); 874 | } else if (this.peekCheck(TokenType.LBRACE)) { 875 | var body = []; 876 | 877 | this.nextTokenNewline(); 878 | 879 | while (!(this.peekCheck(TokenType.RBRACE))) { 880 | var stmt = this.parseNext(); 881 | this.skipNewline(); 882 | 883 | body.push(stmt); 884 | 885 | if (this.peekCheck(TokenType.NEWLINE)) { 886 | this.nextTokenNewline(); 887 | this.error("Expected a newline, got \"" + this.peekToken().value + 888 | "\""); 889 | } else if (this.peekCheck(TokenType.RBRACE)) { 890 | break; 891 | } 892 | } 893 | 894 | if (this.peekCheck(TokenType.RBRACE)) { 895 | this.nextTokenNewline(); 896 | } else { 897 | this.error("Expected a \"}\", got \"" + this.peekToken().value + "\""); 898 | } 899 | 900 | return new IodeFunction("", [], body); 901 | } else if (this.peekCheck(TokenType.LPAREN)) { 902 | this.nextTokenNewline(); 903 | var args = []; 904 | var body = []; 905 | 906 | while (!(this.peekCheck(TokenType.LBRACE))) { 907 | var arg = this.parseNextLiteral().val; 908 | this.skipNewline(); 909 | 910 | if (arg.charAt(arg.length - 1) == ";") { 911 | arg = arg.substring(0, arg.length - 1); 912 | } 913 | 914 | args.push(arg); 915 | 916 | if (!(this.peekCheck(TokenType.COMMA) || this.peekCheck(TokenType.RPAREN))) { 917 | this.error("Expected a \",\" or \")\", got \"" + this.peekToken().value + 918 | "\""); 919 | } else if (this.peekCheck(TokenType.RPAREN)) { 920 | break; 921 | } else { 922 | this.nextTokenNewline(); 923 | } 924 | } 925 | 926 | if (this.peekCheck(TokenType.RPAREN)) { 927 | this.nextTokenNewline(); 928 | } else { 929 | this.error("Expected a \")\", got \"" + this.peekToken().value + "\""); 930 | } 931 | 932 | if (this.peekCheck(TokenType.LBRACE)) { 933 | this.nextTokenNewline(); 934 | } else { 935 | this.error("Expected a \"{\", got \"" + this.peekToken().value + "\""); 936 | } 937 | 938 | while (!(this.peekCheck(TokenType.RBRACE))) { 939 | var stmt = this.parseNext(); 940 | this.skipNewline(); 941 | 942 | body.push(stmt); 943 | 944 | if (this.peekCheck(TokenType.NEWLINE)) { 945 | this.nextTokenNewline(); 946 | this.error("Expected a newline, got \"" + this.peekToken().value + 947 | "\""); 948 | } else if (this.peekCheck(TokenType.RBRACE)) { 949 | break; 950 | } 951 | } 952 | 953 | if (this.peekCheck(TokenType.RBRACE)) { 954 | this.nextTokenNewline(); 955 | } else { 956 | this.error("Expected a \"}\", got \"" + this.peekToken().value + "\""); 957 | } 958 | 959 | return new IodeFunction("", args, body); 960 | } else { 961 | this.error("Expected a function name, got \"" + this.peekToken().value + 962 | "\""); 963 | } 964 | }; 965 | 966 | this.parseParenthesis = function() { 967 | this.nextTokenNewline(); 968 | var val = this.parseNextLiteral(); 969 | this.skipNewline(); 970 | 971 | if (!(this.peekCheck(TokenType.RPAREN))) { 972 | this.error("Expected a \")\", got \"" + this.peekToken().value + "\""); 973 | } 974 | 975 | this.nextTokenNewline(); 976 | var paren = new IodeParenthesis(val); 977 | var op; 978 | var right; 979 | 980 | if (!this.isNumberOperator()) { 981 | return paren; 982 | } 983 | 984 | while (this.isNumberOperator()) { 985 | op = this.nextToken().value; 986 | this.skipNewline(); 987 | right = this.parseNextLiteral(); 988 | this.skipNewline(); 989 | } 990 | 991 | if (this.peekCheck(TokenType.AND) || this.peekCheck(TokenType.OR)) { 992 | while (this.peekCheck(TokenType.AND) || this.peekCheck(TokenType.OR)) { 993 | op = this.nextToken().value; 994 | this.skipNewline(); 995 | var right = this.parseNextLiteral(); 996 | this.skipNewline(); 997 | } 998 | 999 | return new IodeBinaryOp(paren, op, right); 1000 | } else { 1001 | return new IodeBinaryOp(paren, op, right); 1002 | } 1003 | }; 1004 | 1005 | this.parseIf = function() { 1006 | this.nextTokenNewline(); 1007 | var body = []; 1008 | var args = this.parseNextLiteral(); 1009 | this.skipNewline(); 1010 | 1011 | if (this.peekCheck(TokenType.LBRACE)) { 1012 | this.nextTokenNewline(); 1013 | } else { 1014 | this.error("Expected a \"{\", got \"" + this.peekToken().value + "\""); 1015 | } 1016 | 1017 | while (!(this.peekCheck(TokenType.RBRACE))) { 1018 | var stmt = this.parseNext(); 1019 | this.skipNewline(); 1020 | 1021 | body.push(stmt); 1022 | 1023 | if (this.peekCheck(TokenType.NEWLINE)) { 1024 | this.nextTokenNewline(); 1025 | this.error("Expected a newline, got \"" + this.peekToken().value + 1026 | "\""); 1027 | } else if (this.peekCheck(TokenType.RBRACE)) { 1028 | break; 1029 | } 1030 | } 1031 | 1032 | if (this.peekCheck(TokenType.RBRACE)) { 1033 | this.nextTokenNewline(); 1034 | } else { 1035 | this.error("Expected a \"}\", got \"" + this.peekToken().value + "\""); 1036 | } 1037 | 1038 | if (this.peekCheck(TokenType.ELSIF) || this.peekCheck(TokenType.ELSE)) { 1039 | var chained = [new IodeIf(args, body)]; 1040 | this.skipNewline(); 1041 | 1042 | while (this.peekCheck(TokenType.ELSIF) || this.peekCheck(TokenType.ELSE)) { 1043 | var Else; 1044 | 1045 | if (this.peekCheck(TokenType.ELSE)) { 1046 | Else = true; 1047 | } else { 1048 | Else = false; 1049 | 1050 | for (var chain in chained) { 1051 | if (chained[chain].type == "Else") { 1052 | this.error("Unexpected ElsIF, If chain already has an Else block"); 1053 | } 1054 | } 1055 | } 1056 | 1057 | this.nextTokenNewline(); 1058 | var body_ = []; 1059 | var args_ = null; 1060 | 1061 | if (!Else) { 1062 | args_ = this.parseNextLiteral(); 1063 | } 1064 | 1065 | this.skipNewline(); 1066 | 1067 | if (this.peekCheck(TokenType.LBRACE)) { 1068 | this.nextTokenNewline(); 1069 | } else { 1070 | this.error("Expected a \"{\", got \"" + this.peekToken().value + "\""); 1071 | } 1072 | 1073 | while (!(this.peekCheck(TokenType.RBRACE))) { 1074 | var stmt = this.parseNext(); 1075 | this.skipNewline(); 1076 | 1077 | body_.push(stmt); 1078 | 1079 | if (this.peekCheck(TokenType.NEWLINE)) { 1080 | this.nextTokenNewline(); 1081 | this.error("Expected a newline, got \"" + this.peekToken().value + 1082 | "\""); 1083 | } else if (this.peekCheck(TokenType.RBRACE)) { 1084 | break; 1085 | } 1086 | } 1087 | 1088 | if (this.peekCheck(TokenType.RBRACE)) { 1089 | this.nextTokenNewline(); 1090 | } else { 1091 | this.error("Expected a \"}\", got \"" + this.peekToken().value + "\""); 1092 | } 1093 | 1094 | if (Else) { 1095 | chained.push(new IodeElse(body_)); 1096 | } else { 1097 | chained.push(new IodeElsIf(args_, body_)); 1098 | } 1099 | } 1100 | 1101 | return new IodeIfChain(chained); 1102 | } else { 1103 | return new IodeIf(args, body); 1104 | } 1105 | }; 1106 | 1107 | this.parseWhile = function() { 1108 | this.nextTokenNewline(); 1109 | var body = []; 1110 | var args = this.parseNextLiteral(); 1111 | this.skipNewline(); 1112 | 1113 | if (this.peekCheck(TokenType.LBRACE)) { 1114 | this.nextTokenNewline(); 1115 | } else { 1116 | this.error("Expected a \"{\", got \"" + this.peekToken().value + "\""); 1117 | } 1118 | 1119 | while (!(this.peekCheck(TokenType.RBRACE))) { 1120 | var stmt = this.parseNext(); 1121 | this.skipNewline(); 1122 | 1123 | body.push(stmt); 1124 | 1125 | if (this.peekCheck(TokenType.NEWLINE)) { 1126 | this.nextTokenNewline(); 1127 | this.error("Expected a newline, got \"" + this.peekToken().value + 1128 | "\""); 1129 | } else if (this.peekCheck(TokenType.RBRACE)) { 1130 | break; 1131 | } 1132 | } 1133 | 1134 | if (this.peekCheck(TokenType.RBRACE)) { 1135 | this.nextTokenNewline(); 1136 | } else { 1137 | this.error("Expected a \"}\", got \"" + this.peekToken().value + "\""); 1138 | } 1139 | 1140 | return new IodeWhile(args, body); 1141 | }; 1142 | 1143 | this.parseRepeat = function() { 1144 | this.nextTokenNewline(); 1145 | var body = []; 1146 | var args = this.parseNextLiteral(); 1147 | this.skipNewline(); 1148 | 1149 | if (this.peekCheck(TokenType.LBRACE)) { 1150 | this.nextTokenNewline(); 1151 | } else { 1152 | this.error("Expected a \"{\", got \"" + this.peekToken().value + "\""); 1153 | } 1154 | 1155 | while (!(this.peekCheck(TokenType.RBRACE))) { 1156 | var stmt = this.parseNext(); 1157 | this.skipNewline(); 1158 | 1159 | body.push(stmt); 1160 | 1161 | if (this.peekCheck(TokenType.NEWLINE)) { 1162 | this.nextTokenNewline(); 1163 | this.error("Expected a newline, got \"" + this.peekToken().value + 1164 | "\""); 1165 | } else if (this.peekCheck(TokenType.RBRACE)) { 1166 | break; 1167 | } 1168 | } 1169 | 1170 | if (this.peekCheck(TokenType.RBRACE)) { 1171 | this.nextTokenNewline(); 1172 | } else { 1173 | this.error("Expected a \"}\", got \"" + this.peekToken().value + "\""); 1174 | } 1175 | 1176 | return new IodeRepeat(args, body); 1177 | }; 1178 | 1179 | this.parseUntil = function() { 1180 | this.nextTokenNewline(); 1181 | var body = []; 1182 | var args = this.parseNextLiteral(); 1183 | this.skipNewline(); 1184 | 1185 | if (this.peekCheck(TokenType.LBRACE)) { 1186 | this.nextTokenNewline(); 1187 | } else { 1188 | this.error("Expected a \"{\", got \"" + this.peekToken().value + "\""); 1189 | } 1190 | 1191 | while (!(this.peekCheck(TokenType.RBRACE))) { 1192 | var stmt = this.parseNext(); 1193 | this.skipNewline(); 1194 | 1195 | body.push(stmt); 1196 | 1197 | if (this.peekCheck(TokenType.NEWLINE)) { 1198 | this.nextTokenNewline(); 1199 | this.error("Expected a newline, got \"" + this.peekToken().value + 1200 | "\""); 1201 | } else if (this.peekCheck(TokenType.RBRACE)) { 1202 | break; 1203 | } 1204 | } 1205 | 1206 | if (this.peekCheck(TokenType.RBRACE)) { 1207 | this.nextTokenNewline(); 1208 | } else { 1209 | this.error("Expected a \"}\", got \"" + this.peekToken().value + "\""); 1210 | } 1211 | 1212 | return new IodeUntil(args, body); 1213 | }; 1214 | 1215 | this.parseForeach = function() { 1216 | this.nextTokenNewline(); 1217 | var body = []; 1218 | var val, arr; 1219 | 1220 | if (this.peekSpecificCheck(TokenType.IN, 2)) { 1221 | val = this.parseNextLiteral(); 1222 | this.nextToken(); 1223 | arr = this.parseNextLiteral(); 1224 | } 1225 | 1226 | this.skipNewline(); 1227 | 1228 | if (this.peekCheck(TokenType.LBRACE)) { 1229 | this.nextTokenNewline(); 1230 | } else { 1231 | this.error("Expected a \"{\", got \"" + this.peekToken().value + "\""); 1232 | } 1233 | 1234 | while (!(this.peekCheck(TokenType.RBRACE))) { 1235 | var stmt = this.parseNext(); 1236 | this.skipNewline(); 1237 | 1238 | body.push(stmt); 1239 | 1240 | if (this.peekCheck(TokenType.NEWLINE)) { 1241 | this.nextTokenNewline(); 1242 | this.error("Expected a newline, got \"" + this.peekToken().value + 1243 | "\""); 1244 | } else if (this.peekCheck(TokenType.RBRACE)) { 1245 | break; 1246 | } 1247 | } 1248 | 1249 | if (this.peekCheck(TokenType.RBRACE)) { 1250 | this.nextTokenNewline(); 1251 | } else { 1252 | this.error("Expected a \"}\", got \"" + this.peekToken().value + "\""); 1253 | } 1254 | 1255 | return new IodeForeach(val, arr, body); 1256 | }; 1257 | 1258 | this.parseReturn = function() { 1259 | this.nextToken(); 1260 | 1261 | if (this.peekCheck(TokenType.NEWLINE)) { 1262 | return new IodeReturn(null); 1263 | } else { 1264 | this.skipNewline(); 1265 | return new IodeReturn(this.parseNextLiteral()); 1266 | } 1267 | }; 1268 | 1269 | this.parseContinue = function() { 1270 | this.nextToken(); 1271 | 1272 | if (this.peekCheck(TokenType.NEWLINE)) { 1273 | return new IodeContinue(null); 1274 | } else { 1275 | this.skipNewline(); 1276 | return new IodeContinue(this.parseNextLiteral()); 1277 | } 1278 | }; 1279 | 1280 | this.parseThrow = function() { 1281 | this.nextToken(); 1282 | 1283 | if (this.peekCheck(TokenType.NEWLINE)) { 1284 | this.error("Expecting an error message"); 1285 | return null; 1286 | } else { 1287 | this.skipNewline(); 1288 | return new IodeThrow(this.parseNextLiteral()); 1289 | } 1290 | }; 1291 | 1292 | this.parseNot = function() { 1293 | this.nextTokenNewline(); 1294 | var ret = new IodeNot(this.parseNextLiteral()); 1295 | this.skipNewline(); 1296 | return ret; 1297 | }; 1298 | 1299 | this.parseNew = function() { 1300 | this.nextTokenNewline(); 1301 | var ret = new IodeNew(this.parseNextLiteral()); 1302 | this.skipNewline(); 1303 | return ret; 1304 | }; 1305 | 1306 | this.parseInclude = function() { 1307 | this.nextTokenNewline(); 1308 | 1309 | if (this.peekCheck(TokenType.STRING)) { 1310 | var str = this.nextToken().value; 1311 | str.substring(1, str.length - 1); 1312 | 1313 | var filename = path.join(this.cdir, str); 1314 | var code = "/* imported: " + str + " */\n"; 1315 | 1316 | try { 1317 | code = fs.readFileSync(filename) + "\n"; 1318 | } catch (e) { 1319 | if (e.code === "ENOENT") { 1320 | this.error("File not found"); 1321 | } else { 1322 | this.error("An error occured importing a file"); 1323 | } 1324 | } 1325 | 1326 | var parser = new Parser(code.toString()); 1327 | var ast = parser.parse(); 1328 | var outputCode = ""; 1329 | 1330 | try { 1331 | for (var expr in ast) { 1332 | outputCode += "\n" + ast[expr].val; 1333 | } 1334 | } catch (e) { 1335 | this.error("Could not read file"); 1336 | } 1337 | 1338 | var ret = new IodeInclude(outputCode); 1339 | 1340 | if (this.peekCheck(TokenType.NEWLINE)) { 1341 | this.nextTokenNewline(); 1342 | return ret; 1343 | } else { 1344 | this.error("Expected a newline"); 1345 | } 1346 | } else { 1347 | this.error("Expected a string in import, got a \"" + this.peekToken().type + "\""); 1348 | } 1349 | }; 1350 | 1351 | this.parseArray = function(isVar) { 1352 | this.nextTokenNewline(); 1353 | 1354 | if (this.peekSpecificCheck(TokenType.TWODOTS, 2)) { 1355 | var num1 = this.parseNextLiteral(); 1356 | this.skipNewline(); 1357 | this.nextTokenNewline(); 1358 | var num2 = this.parseNextLiteral(); 1359 | this.skipNewline(); 1360 | 1361 | if (this.peekCheck(TokenType.RBRACK)) { 1362 | this.nextTokenNewline(); 1363 | } else { 1364 | this.error("Expected \"]\", got \"" + this.peekToken().type + "\""); 1365 | } 1366 | 1367 | return new IodeRange(num1, num2); 1368 | } else { 1369 | var args = []; 1370 | 1371 | while (!(this.peekCheck(TokenType.RBRACK))) { 1372 | var arg = this.parseNextLiteral().val; 1373 | this.skipNewline(); 1374 | 1375 | if (arg.charAt(arg.length - 1) == ";") { 1376 | arg = arg.substring(0, arg.length - 1); 1377 | } 1378 | 1379 | args.push(arg); 1380 | 1381 | if (!(this.peekCheck(TokenType.COMMA) || this.peekCheck(TokenType.RBRACK))) { 1382 | this.error("Expected a \",\" or \"]\", got \"" + this.peekToken().value + 1383 | "\""); 1384 | } else if (this.peekCheck(TokenType.RBRACK)) { 1385 | break; 1386 | } else { 1387 | this.nextTokenNewline(); 1388 | } 1389 | } 1390 | 1391 | if (this.peekCheck(TokenType.RBRACK)) { 1392 | this.nextTokenNewline(); 1393 | } else { 1394 | this.error("Expected a \"]\", got \"" + this.peekToken().value + "\""); 1395 | } 1396 | 1397 | if (this.peekCheck(TokenType.EQUALS) && isVar) { 1398 | var arr1 = new IodeArray(args); 1399 | this.nextTokenNewline(); 1400 | var oldLine = this.line; 1401 | var arr2 = []; 1402 | 1403 | if (this.peekCheck(TokenType.LBRACK)) { 1404 | arr2 = this.parseArray(false); 1405 | } else { 1406 | this.error("Expected an \"[\" in mass variable declaration"); 1407 | } 1408 | 1409 | if (this.peekCheck(TokenType.NEWLINE)) { 1410 | this.nextTokenNewline(); 1411 | } else if (this.line > oldLine) { 1412 | this.skipNewline(); 1413 | return new IodeMassVariableDeclaration(arr1, arr2); 1414 | } else { 1415 | this.error("Expected a newline"); 1416 | } 1417 | 1418 | this.skipNewline(); 1419 | return new IodeMassVariableDeclaration(arr1, arr2); 1420 | } else if (this.peekCheck(TokenType.EQUALS)) { 1421 | if (this.peekCheck(TokenType.EQUALS)) { 1422 | var arr1 = new IodeArray(args); 1423 | this.nextTokenNewline(); 1424 | var oldLine = this.line; 1425 | var arr2 = []; 1426 | 1427 | if (this.peekCheck(TokenType.LBRACK)) { 1428 | arr2 = this.parseArray(false); 1429 | } else { 1430 | this.error("Expected an \"[\" in mass variable setting"); 1431 | } 1432 | 1433 | if (this.peekCheck(TokenType.NEWLINE)) { 1434 | this.nextTokenNewline(); 1435 | } else if (this.line > oldLine) { 1436 | this.skipNewline(); 1437 | return new IodeMassVariableSetting(arr1, arr2); 1438 | } else { 1439 | this.error("Expected a newline"); 1440 | } 1441 | 1442 | this.skipNewline(); 1443 | return new IodeMassVariableSetting(arr1, arr2); 1444 | } else { 1445 | return new IodeArray(args); 1446 | } 1447 | } else { 1448 | return new IodeArray(args); 1449 | } 1450 | } 1451 | }; 1452 | 1453 | this.parseClass = function() { 1454 | this.nextTokenNewline(); 1455 | var args = []; 1456 | 1457 | if (this.peekCheck(TokenType.IDENTIFIER)) { 1458 | var name = this.parseNextLiteral().val; 1459 | this.skipNewline(); 1460 | 1461 | while (!(this.peekCheck(TokenType.LBRACE))) { 1462 | var arg = this.parseNextLiteral().val; 1463 | this.skipNewline(); 1464 | 1465 | if (arg.charAt(arg.length - 1) == ";") { 1466 | arg = arg.substring(0, arg.length - 1); 1467 | } 1468 | 1469 | args.push(arg); 1470 | 1471 | if (!(this.peekCheck(TokenType.COMMA) || this.peekCheck(TokenType.LBRACE) || this.peekCheck(TokenType.EXTENDS))) { 1472 | this.error("Expected a \",\" or \")\", got \"" + this.peekToken().value + 1473 | "\""); 1474 | } else if (this.peekCheck(TokenType.LBRACE) || this.peekCheck(TokenType.EXTENDS)) { 1475 | break; 1476 | } else { 1477 | this.nextTokenNewline(); 1478 | } 1479 | } 1480 | 1481 | var extended = null; 1482 | 1483 | if (this.peekCheck(TokenType.EXTENDS)) { 1484 | this.nextTokenNewline(); 1485 | extended = this.nextToken().value; 1486 | this.skipNewline(); 1487 | } 1488 | 1489 | if (this.peekCheck(TokenType.LBRACE)) { 1490 | this.nextTokenNewline(); 1491 | var body = []; 1492 | 1493 | while (!this.peekCheck(TokenType.RBRACE)) { 1494 | var block = this.parseNextClass(); 1495 | this.skipNewline(); 1496 | 1497 | if (block === null) { 1498 | this.error("Class bodies may consist of functions and a constructor"); 1499 | } 1500 | 1501 | this.skipNewline(); 1502 | body.push(block); 1503 | } 1504 | 1505 | if (this.peekCheck(TokenType.RBRACE)) { 1506 | this.nextTokenNewline(); 1507 | } else { 1508 | this.error("Expected a \"}\""); 1509 | } 1510 | 1511 | return new IodeClass(name, args, body, extended); 1512 | } else { 1513 | this.error("Expected \"{\""); 1514 | } 1515 | } else { 1516 | this.error("Expected a class name"); 1517 | } 1518 | }; 1519 | 1520 | this.parseFor = function() { 1521 | this.nextTokenNewline(); 1522 | 1523 | if (this.peekCheck(TokenType.LPAREN)) { 1524 | this.nextTokenNewline(); 1525 | 1526 | if (this.peekCheck(TokenType.IDENTIFIER)) { 1527 | var name = this.nextToken().value; 1528 | this.skipNewline(); 1529 | 1530 | if (this.peekCheck(TokenType.EQUALS)) { 1531 | this.nextToken(); 1532 | var val = this.parseNextLiteral(); 1533 | 1534 | if (this.peekCheck(TokenType.COMMA)) { 1535 | this.skipNewline(); 1536 | this.nextTokenNewline(); 1537 | var cond = this.parseNextLiteral(); 1538 | this.skipNewline(); 1539 | 1540 | if (this.peekCheck(TokenType.COMMA)) { 1541 | this.nextToken(); 1542 | this.skipNewline(); 1543 | var iter = this.parseNextLiteral(); 1544 | this.skipNewline(); 1545 | 1546 | if (this.peekCheck(TokenType.RPAREN)) { 1547 | this.nextToken(); 1548 | this.skipNewline(); 1549 | 1550 | if (this.peekCheck(TokenType.LBRACE)) { 1551 | this.nextToken(); 1552 | this.skipNewline(); 1553 | var body = []; 1554 | 1555 | while (!(this.peekCheck(TokenType.RBRACE))) { 1556 | var stmt = this.parseNext(); 1557 | this.skipNewline(); 1558 | 1559 | body.push(stmt); 1560 | 1561 | if (this.peekCheck(TokenType.NEWLINE)) { 1562 | this.nextToken(); 1563 | this.skipNewline(); 1564 | this.error("Expected a newline, got \"" + this.peekToken().value + 1565 | "\""); 1566 | } else if (this.peekCheck(TokenType.RBRACE)) { 1567 | break; 1568 | } 1569 | } 1570 | 1571 | if (this.peekCheck(TokenType.RBRACE)) { 1572 | this.nextToken(); 1573 | this.skipNewline(); 1574 | } else { 1575 | this.error("Expected a \"}\", got \"" + this.peekToken().value + "\""); 1576 | } 1577 | 1578 | return new IodeFor(name, val, cond, iter, body); 1579 | } else { 1580 | this.error("Expected a \"{\", got \"" + this.peekToken().type + "\""); 1581 | } 1582 | } else { 1583 | this.error("Expected a \")\", got \"" + this.peekToken().type + "\""); 1584 | } 1585 | } else { 1586 | this.error("Expected a \",\", got \"" + this.peekToken().type + "\""); 1587 | } 1588 | } else { 1589 | this.error("Expected a \",\", got \"" + this.peekToken().type + "\""); 1590 | } 1591 | } else { 1592 | this.error("Expected \"=\", got \"" + this.peekToken().type + "\""); 1593 | } 1594 | } else { 1595 | this.error("Expected a variable name"); 1596 | } 1597 | } else { 1598 | this.error("Expected a \"(\", got \"" + this.peekToken().type + "\""); 1599 | } 1600 | }; 1601 | 1602 | this.parseNewline = function() { 1603 | this.nextTokenNewline(); 1604 | return new IodeNewline(); 1605 | }; 1606 | 1607 | this.parsePattern = function() { 1608 | var pattern = this.nextToken().value; 1609 | this.skipNewline(); 1610 | return new IodePattern(pattern); 1611 | }; 1612 | 1613 | this.parseJSON = function() { 1614 | this.nextTokenNewline(); 1615 | 1616 | var elements = []; 1617 | 1618 | while (!this.peekCheck(TokenType.RBRACE)) { 1619 | if (this.peekCheck(TokenType.STRING) || this.peekCheck(TokenType.IDENTIFIER)) { 1620 | var a = this.parseNextLiteral().val; 1621 | this.skipNewline(); 1622 | 1623 | if (this.peekCheck(TokenType.COLON)) { 1624 | this.nextTokenNewline(); 1625 | 1626 | var b = this.parseNextLiteral().val; 1627 | 1628 | if (this.peekCheck(TokenType.COMMA)) { 1629 | this.nextTokenNewline(); 1630 | } else if (this.peekCheck(TokenType.NEWLINE)) { 1631 | this.skipNewline(); 1632 | } else if (this.peekCheck(TokenType.RBRACE)) { 1633 | elements.push({left: a, right: b}); 1634 | this.nextTokenNewline(); 1635 | break; 1636 | } else { 1637 | this.error("Expected a comma or a newline"); 1638 | } 1639 | 1640 | elements.push({left: a, right: b}); 1641 | } else { 1642 | this.error("Expected \":\""); 1643 | } 1644 | } else { 1645 | this.error("Expected string or identifier before a \":\" in a JSON element"); 1646 | } 1647 | } 1648 | 1649 | if (this.peekCheck(TokenType.RBRACE)) { 1650 | this.nextTokenNewline(); 1651 | } 1652 | 1653 | return new IodeJSON(elements); 1654 | }; 1655 | 1656 | this.parseTry = function() { 1657 | this.nextTokenNewline(); 1658 | var body = []; 1659 | 1660 | if (this.peekCheck(TokenType.LBRACE)) { 1661 | this.nextTokenNewline(); 1662 | } else { 1663 | this.error("Expected a \"{\", got \"" + this.peekToken().value + "\""); 1664 | } 1665 | 1666 | while (!(this.peekCheck(TokenType.RBRACE))) { 1667 | var stmt = this.parseNext(); 1668 | this.skipNewline(); 1669 | 1670 | body.push(stmt); 1671 | 1672 | if (this.peekCheck(TokenType.NEWLINE)) { 1673 | this.nextTokenNewline(); 1674 | this.error("Expected a newline, got \"" + this.peekToken().value + 1675 | "\""); 1676 | } else if (this.peekCheck(TokenType.RBRACE)) { 1677 | break; 1678 | } 1679 | } 1680 | 1681 | if (this.peekCheck(TokenType.RBRACE)) { 1682 | this.nextTokenNewline(); 1683 | } else { 1684 | this.error("Expected a \"}\", got \"" + this.peekToken().value + "\""); 1685 | } 1686 | 1687 | if (this.peekCheck(TokenType.CATCH)) { 1688 | this.nextTokenNewline(); 1689 | } else { 1690 | this.error("Expected a \"catch\" statement"); 1691 | } 1692 | 1693 | var catchBody = []; 1694 | var catchArgs = this.nextToken().value; 1695 | this.skipNewline(); 1696 | 1697 | if (this.peekCheck(TokenType.LBRACE)) { 1698 | this.nextTokenNewline(); 1699 | } else { 1700 | this.error("Expected a \"{\", got \"" + this.peekToken().value + "\""); 1701 | } 1702 | 1703 | while (!(this.peekCheck(TokenType.RBRACE))) { 1704 | var stmt = this.parseNext(); 1705 | this.skipNewline(); 1706 | 1707 | catchBody.push(stmt); 1708 | 1709 | if (this.peekCheck(TokenType.NEWLINE)) { 1710 | this.nextTokenNewline(); 1711 | this.error("Expected a newline, got \"" + this.peekToken().value + 1712 | "\""); 1713 | } else if (this.peekCheck(TokenType.RBRACE)) { 1714 | break; 1715 | } 1716 | } 1717 | 1718 | if (this.peekCheck(TokenType.RBRACE)) { 1719 | this.nextTokenNewline(); 1720 | } else { 1721 | this.error("Expected a \"}\", got \"" + this.peekToken().value + "\""); 1722 | } 1723 | 1724 | return new IodeTry(body, catchArgs, catchBody); 1725 | }; 1726 | 1727 | this.parseJavaScript = function() { 1728 | return new IodeEmbedded(this.nextToken().value); 1729 | }; 1730 | 1731 | this.parseNamespace = function() { 1732 | this.skipNewline(); 1733 | this.nextTokenNewline(); 1734 | var name = ""; 1735 | 1736 | if (this.peekCheck(TokenType.IDENTIFIER)) { 1737 | name = this.nextToken().value; 1738 | this.skipNewline(); 1739 | } else { 1740 | this.error("Expected a name"); 1741 | } 1742 | 1743 | if (this.peekCheck(TokenType.LBRACE)) { 1744 | this.nextTokenNewline(); 1745 | } else { 1746 | this.error("Expected a \"{\""); 1747 | } 1748 | 1749 | var body = []; 1750 | 1751 | while (!this.peekCheck(TokenType.RBRACE)) { 1752 | var block = this.parseNextNamespace(); 1753 | this.skipNewline(); 1754 | 1755 | if (block === null) { 1756 | this.error("Namespaces may consist of functions, classes and global variables"); 1757 | } 1758 | 1759 | this.skipNewline(); 1760 | body.push(block); 1761 | } 1762 | 1763 | if (this.peekCheck(TokenType.RBRACE)) { 1764 | this.nextTokenNewline(); 1765 | } else { 1766 | this.error("Expected a \"}\""); 1767 | } 1768 | 1769 | return new IodeNamespace(name, body); 1770 | }; 1771 | 1772 | this.parseNextClass = function() { 1773 | try { 1774 | var tok = this.peekToken(); 1775 | 1776 | switch (tok.type) { 1777 | case TokenType.FUNCTION: 1778 | return this.parseFunction(); 1779 | case TokenType.IDENTIFIER: 1780 | return this.parseIdentifier(); 1781 | case TokenType.NEWLINE: 1782 | return this.parseNewline(); 1783 | default: 1784 | return null; 1785 | } 1786 | } catch (e) { 1787 | this.error(e); 1788 | return null; 1789 | } 1790 | }; 1791 | 1792 | this.parseNextNamespace = function() { 1793 | try { 1794 | var tok = this.peekToken(); 1795 | 1796 | switch (tok.type) { 1797 | case TokenType.FUNCTION: 1798 | return this.parseFunction(); 1799 | case TokenType.IDENTIFIER: 1800 | return this.parseIdentifier(); 1801 | case TokenType.CLASS: 1802 | return this.parseClass(); 1803 | case TokenType.NEWLINE: 1804 | return this.parseNewline(); 1805 | default: 1806 | return null; 1807 | } 1808 | } catch (e) { 1809 | this.error(e); 1810 | return null; 1811 | } 1812 | }; 1813 | 1814 | this.parseNextLiteral = function() { 1815 | try { 1816 | var tok = this.peekToken(); 1817 | 1818 | switch (tok.type) { 1819 | case TokenType.LBRACE: 1820 | return this.parseJSON(); 1821 | case TokenType.IDENTIFIER: 1822 | return this.parseIdentifier(); 1823 | case TokenType.NUMBER: 1824 | return this.parseNumber(); 1825 | case TokenType.LBRACK: 1826 | return this.parseArray(false); 1827 | case TokenType.BOOLEAN: 1828 | return this.parseBoolean(); 1829 | case TokenType.STRING: 1830 | return this.parseString(); 1831 | case TokenType.FUNCTION: 1832 | return this.parseFunction(); 1833 | case TokenType.LPAREN: 1834 | return this.parseParenthesis(); 1835 | case TokenType.EXCLAMATION: 1836 | return this.parseNot(); 1837 | case TokenType.PATTERN: 1838 | return this.parsePattern(); 1839 | case TokenType.NEW: 1840 | return this.parseNew(); 1841 | case TokenType.EMBEDDED: 1842 | return this.parseJavaScript(); 1843 | default: 1844 | this.error("Could not parse expression \"" + tok.type.toLowerCase() + "\""); 1845 | return null; 1846 | } 1847 | } catch (e) { 1848 | throw e; 1849 | this.error("Could not parse next expression. " + e); 1850 | return null; 1851 | } 1852 | }; 1853 | 1854 | this.parseNext = function() { 1855 | try { 1856 | var tok = this.peekToken(); 1857 | 1858 | switch (tok.type) { 1859 | case TokenType.NAMESPACE: 1860 | return this.parseNamespace(); 1861 | case TokenType.IDENTIFIER: 1862 | return this.parseIdentifier(); 1863 | case TokenType.CLASS: 1864 | return this.parseClass(); 1865 | case TokenType.NUMBER: 1866 | return this.parseNumber(); 1867 | case TokenType.LBRACK: 1868 | return this.parseArray(false); 1869 | case TokenType.BOOLEAN: 1870 | return this.parseBoolean(); 1871 | case TokenType.STRING: 1872 | return this.parseString(); 1873 | case TokenType.VAR: 1874 | return this.parseVariableDeclaration(); 1875 | case TokenType.INCLUDE: 1876 | return this.parseInclude(); 1877 | case TokenType.FUNCTION: 1878 | return this.parseFunction(); 1879 | case TokenType.REPEAT: 1880 | return this.parseRepeat(); 1881 | case TokenType.LPAREN: 1882 | return this.parseParenthesis(); 1883 | case TokenType.NEWLINE: 1884 | return this.parseNewline(); 1885 | case TokenType.WHILE: 1886 | return this.parseWhile(); 1887 | case TokenType.UNTIL: 1888 | return this.parseUntil(); 1889 | case TokenType.IF: 1890 | return this.parseIf(); 1891 | case TokenType.EXCLAMATION: 1892 | return this.parseNot(); 1893 | case TokenType.FOREACH: 1894 | return this.parseForeach(); 1895 | case TokenType.CONST: 1896 | return this.parseConst(); 1897 | case TokenType.PATTERN: 1898 | return this.parsePattern(); 1899 | case TokenType.RETURN: 1900 | return this.parseReturn(); 1901 | case TokenType.CONTINUE: 1902 | return this.parseContinue(); 1903 | case TokenType.THROW: 1904 | return this.parseThrow(); 1905 | case TokenType.NEW: 1906 | return this.parseNew(); 1907 | case TokenType.FOR: 1908 | return this.parseFor(); 1909 | case TokenType.TRY: 1910 | return this.parseTry(); 1911 | case TokenType.LBRACE: 1912 | return this.parseJSON(); 1913 | case TokenType.EMBEDDED: 1914 | return this.parseJavaScript(); 1915 | default: 1916 | this.error("Could not parse expression \"" + tok.type.toLowerCase() + "\""); 1917 | return null; 1918 | } 1919 | } catch (e) { 1920 | throw e; 1921 | this.error("Could not parse next expression. " + e); 1922 | return null; 1923 | } 1924 | }; 1925 | 1926 | this.parse = function() { 1927 | var exprs = []; 1928 | 1929 | while (this.pos < this.totalTokens) { 1930 | exprs.push(this.parseNext()); 1931 | } 1932 | 1933 | return exprs; 1934 | }; 1935 | 1936 | this.treeTokens = function(exprs) { 1937 | console.log(exprs); 1938 | }; 1939 | }; 1940 | 1941 | exports.Parser = Parser; 1942 | --------------------------------------------------------------------------------