├── .gitignore ├── test ├── package.json ├── index.js ├── ald.peg ├── engine.js └── stdlib.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules -------------------------------------------------------------------------------- /test: -------------------------------------------------------------------------------- 1 | (fn x 2 | (if (lte x 2) 3 | 1 4 | (plus 5 | (self (minus x 1)) 6 | (self (minus x 2)) 7 | ) 8 | ) 9 | ) 5 -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ald2", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "directories": { 7 | "test": "test" 8 | }, 9 | "dependencies": { 10 | "pegjs": "^0.9.0", 11 | "read": "^1.0.7" 12 | }, 13 | "devDependencies": {}, 14 | "scripts": { 15 | "test": "echo \"Error: no test specified\" && exit 1" 16 | }, 17 | "author": "", 18 | "license": "ISC" 19 | } 20 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 2 | var read = require('read'); 3 | 4 | const util = require('util'); 5 | 6 | var stdlib = require('./stdlib.js') 7 | var Engine = require('./engine.js') 8 | 9 | 10 | var e = new Engine('ald.peg'); 11 | var sl = new stdlib(e); 12 | 13 | e.sl = sl; 14 | 15 | 16 | 17 | 18 | 19 | 20 | if (process.argv.length >= 3) { 21 | var contents = require('fs').readFileSync(process.argv[2], "utf8"); 22 | 23 | repl = false; 24 | 25 | var context = {}; 26 | 27 | var tree = parser.parse(contents); 28 | parse(tree); 29 | } else { 30 | prompt(); 31 | } 32 | 33 | function prompt() { 34 | read({ prompt: ' >'}, function(err, str) { 35 | e.parse(str).then(function(result) { 36 | parse(result); 37 | }).catch(function(err) { 38 | var reason = err.found === null ? 'end of input' : '"' + err.found + '"'; 39 | console.log('syntax error: unexpected ' + reason); 40 | prompt(); 41 | }); 42 | }); 43 | } 44 | 45 | function parse(tree) { 46 | var context = {}; 47 | 48 | 49 | e.promise(tree, context).then(function(result) { 50 | console.log(typeof result === "function" ? '(fn ' + result.name + ')' : result); 51 | prompt(); 52 | }).catch(function(err) { 53 | console.log(err); 54 | prompt(); 55 | }); 56 | 57 | // console.log(util.inspect(tree, { depth: null })); 58 | } 59 | 60 | 61 | 62 | 63 | 64 | function a(t) { 65 | 66 | } -------------------------------------------------------------------------------- /ald.peg: -------------------------------------------------------------------------------- 1 | { 2 | function r(t, v, a) { 3 | return [t, v, a]; 4 | } 5 | } 6 | 7 | 8 | Command 9 | = _? cmd:Argument _? args:Arguments? _? { 10 | return r("command", cmd, args || []); 11 | } 12 | 13 | Arguments 14 | = arg:Argument _ args:Arguments { return [arg].concat(args); } 15 | / arg:Argument { return [arg]; } 16 | 17 | Argument 18 | = "(" _? cmd:Command _? ")" { return cmd; } 19 | / "\"" str:[^"]* "\"" { return r("string", str.join("")); } 20 | / "'" str:[^']* "'" { return r("string", str.join("")); } 21 | / int:DecimalLiteral { return r("number", int); } 22 | / arg:Identifier { return r("symbol", arg); } 23 | 24 | Identifier = head:[a-zA-Z] tail:[a-zA-Z_0-9]* { 25 | return head + tail.join(""); 26 | } 27 | 28 | DecimalLiteral 29 | = DecimalIntegerLiteral "." DecimalDigit* ExponentPart? { 30 | return parseFloat(text()); 31 | } 32 | / "." DecimalDigit+ ExponentPart? { 33 | return parseFloat(text()); 34 | } 35 | / DecimalIntegerLiteral ExponentPart? { 36 | return parseFloat(text()); 37 | } 38 | 39 | DecimalIntegerLiteral 40 | = "0" 41 | / NonZeroDigit DecimalDigit* 42 | 43 | DecimalDigit 44 | = [0-9] 45 | 46 | NonZeroDigit 47 | = [1-9] 48 | 49 | ExponentPart 50 | = ExponentIndicator SignedInteger 51 | 52 | ExponentIndicator 53 | = "e"i 54 | 55 | SignedInteger 56 | = [+-]? DecimalDigit+ 57 | 58 | _ = [ \t\r\n]+ { 59 | return undefined; 60 | } -------------------------------------------------------------------------------- /engine.js: -------------------------------------------------------------------------------- 1 | var PEG = require('pegjs'); 2 | var fs = require('fs'); 3 | 4 | function Engine(fn) { 5 | this.file = fs.readFileSync(fn); 6 | this.parser = PEG.buildParser(this.file.toString()); 7 | } 8 | 9 | var v = function(node) { 10 | return { 11 | type: function(ch) { 12 | if (!this.valid(node)) return false; 13 | if (ch) return node[0] === ch; 14 | else return node[0]; 15 | }, 16 | value: function(d) { 17 | return d ? node[1] : v(node[1]); 18 | }, 19 | children: function() { 20 | return node[2]; 21 | }, 22 | valid: function() { 23 | return node && node.length === 3; 24 | } 25 | } 26 | } 27 | 28 | Engine.prototype.args = function(args, context) { 29 | return args.map(function(arg, i) { 30 | if (v(arg).type('symbol')) { 31 | if (v(arg).value(true) in context) { 32 | return context[v(arg).value(true)]; 33 | } else return arg; 34 | } else return arg; 35 | }); 36 | } 37 | 38 | 39 | Engine.prototype.parse = function(str) { 40 | return new Promise((res, rej) => { 41 | try { 42 | var r = this.parser.parse(str); 43 | res(r); 44 | } catch (e) { 45 | rej(e); 46 | } 47 | }); 48 | } 49 | 50 | Engine.prototype.execute = function(node, resolve, reject, context) { 51 | // console.log('execute', node); 52 | // console.log('name', v(node).value()) 53 | this.promise(v(node).value(true), context).then(val => { 54 | // console.log('value', val); 55 | if (typeof val === "string") { 56 | var args = v(node).children(); 57 | args = this.args(args, context); 58 | if (val in this.sl) this.sl[val](args, resolve, reject, context); 59 | else reject('not found: ' + val); 60 | } else if (typeof val === "function") { 61 | val(v(node).children(), resolve, reject, context); 62 | } 63 | }).catch(function(err) { 64 | reject(err); 65 | }); 66 | } 67 | 68 | Engine.prototype.promise = function(node, context) { 69 | // console.log('promise', node); 70 | if (v(node).type('command')) { 71 | return new Promise((resolve, reject) => { 72 | this.execute(node, resolve, reject, context); 73 | }); 74 | } else { 75 | return new Promise(function(resolve, reject) { 76 | resolve(v(node).value(true)); 77 | }); 78 | } 79 | /* } else { 80 | return new Promise(function(resolve) { 81 | resolve(node); 82 | }); 83 | }*/ 84 | } 85 | 86 | module.exports = Engine; -------------------------------------------------------------------------------- /stdlib.js: -------------------------------------------------------------------------------- 1 | function sl(engine) { 2 | this.engine = engine; 3 | } 4 | 5 | 6 | var isPrimitive = function(node) { 7 | if (v(node).type('command')) return false; 8 | else return true; 9 | } 10 | 11 | sl.prototype.fn = function fn(args, resolve, reject, context) { 12 | if (!args || args.length === 0) resolve(function(a, res, rej, c) { 13 | res(); 14 | }); 15 | 16 | if (args.length > 0) { 17 | var fn = args.slice(args.length - 1); 18 | var fnargs = args.slice(0, args.length - 1); 19 | 20 | if (isPrimitive(fn)) reject('fn: last argument should be a node'); 21 | 22 | var fnargsx = fnargs.map(el => this.engine.promise(el)); 23 | 24 | Promise.all(fnargsx).then(function(fnargsr) { 25 | if (fnargsr.every(el => typeof el === "string")) { 26 | 27 | } else reject('fn: argument names should resolve to string'); 28 | }); 29 | } 30 | } 31 | 32 | sl.prototype.wait = function wait(args, resolve, reject, context) { 33 | Promise.all(args.map(a => this.engine.promise(a, context))).then(function(argsx) { 34 | var startTime = process.hrtime(); 35 | startTime = startTime[0] * 1000000 + startTime[1] / 1000; 36 | setTimeout(function() { 37 | var endTime = process.hrtime(); 38 | endTime = endTime[0] * 1000000 + endTime[1] / 1000; 39 | 40 | if (argsx[1]) return resolve(argsx[1]); 41 | else return resolve((endTime - startTime) / 1000); 42 | }, argsx[0]); 43 | }); 44 | } 45 | 46 | sl.prototype["add"] = function add(args, resolve, reject, context) { 47 | Promise.all(args.map(arg => this.engine.promise(arg, context))).then(function(args) { 48 | resolve(args.reduce((p, n) => p + n, 0)); 49 | }).catch(function(e) { 50 | reject(e); 51 | }); 52 | } 53 | 54 | sl.prototype["var"] = function variable(args, resolve, reject, context) { 55 | Promise.all(args.slice(0, 2).map(e => this.engine.promise(e, context))).then((argsx) => { 56 | context[argsx[0]] = [ "number", argsx[1], undefined ]; 57 | return this.engine.promise(args[2], context); 58 | }).then(function(v) { 59 | resolve(v); 60 | }).catch(function(err) { 61 | reject(err); 62 | }); 63 | } 64 | 65 | 66 | 67 | sl.prototype.resolve = function resolve(args, resolve, reject, context) { 68 | if (!args || args.length === 0) reject('resolve: no arguments'); 69 | if (args.length !== 1) reject('resolve: too much arguments (expected 1, got ' + args.length + ')'); 70 | 71 | this.engine.promise(args[0], context).then(function(value) { 72 | resolve(value); 73 | }).catch(function(err) { 74 | reject('resolve: ' + err); 75 | }); 76 | } 77 | 78 | sl.prototype.exit = function() { 79 | process.exit(1); 80 | } 81 | 82 | 83 | module.exports = sl; --------------------------------------------------------------------------------