var fs, input; var mscript = {}; mscript.arg = function arg (shrt, lng) { 'use strict'; if (process.argv.indexOf(shrt) !== -1 || process.argv.indexOf(lng) !== -1) { return true; } return false; }; mscript.arg_pos = function arg_pos (shrt, lng) { 'use strict'; var pos = -1; pos = process.argv.indexOf(shrt); if (pos === -1) { pos = process.argv.indexOf(lng); } return pos; }; mscript.black = '0,0,0'; mscript.cmd = [ 'CF', 'PF', 'BF', 'CB', 'PB', 'BB' ]; mscript.alts = { 'CF' : ['CAMERA FORWARD', 'CAM FORWARD'], 'PF' : ['PROJECTOR FORWARD', 'PROJ FORWARD'], 'BF': ['BLACK FORWARD'], 'CB' : ['CAMERA BACKWARD', 'CAM BACKWARD', 'CAMERA BACK', 'CAM BACK'], 'PB' : ['PROJECTOR FORWARD', 'PROJ FORWARD', 'PROJECTOR BACK', 'PROJ BACK'], 'BB' : ['BLACK BACKWARD', 'BLACK BACK'], 'L ' : ['LIGHT', 'COLOR', 'LAMP'] }; mscript.state = {}; mscript.state_clear = function state_clear () { 'use strict'; mscript.state = { cam : 0, proj : 0, color : '', loops : [], rec : -1 }; }; mscript.alts_unique = function alts_unique () { 'use strict'; var ids = Object.keys(mscript.alts), all = []; for (var i = 0; i < ids.length; i++) { if (all.indexOf(ids[i]) === -1) { all.push(ids[i]); } else { mscript.fail("Can't compile"); } } }; mscript.interpret = function interpret (text, callback) { 'use strict'; mscript.state_clear(); if (typeof text === 'undefined') { mscript.fail('No input'); } var lines = text.split('\n'), two = '', arr = [], light = [], target = 0, dist = 0, //? output = {}; for (var i = 0; i < lines.length; i++) { lines[i] = lines[i].replace(/\t+/g, ""); //strip tabs lines[i] = lines[i].trim(); //remove excess whitespace before and after command two = lines[i].substring(0, 2); if (mscript.cmd.indexOf(two) !== -1) { if (mscript.state.loops.length > 0) { //hold generated arr in state loop array mscript.state.loops[mscript.state.rec].arr .push.apply(mscript.state.loops[mscript.state.rec].arr, mscript.str_to_arr(lines[i], two)); mscript.state.loops[mscript.state.rec].light .push.apply(mscript.state.loops[mscript.state.rec].light, mscript.light_to_arr(lines[i], two)); } else { arr.push.apply(arr, mscript.str_to_arr(lines[i], two)); light.push.apply(light, mscript.light_to_arr(lines[i], two)) } } else if (lines[i].substring(0, 4) === 'LOOP') { mscript.state.rec++; mscript.state.loops[mscript.state.rec] = { arr : [], light : [], cam : 0, proj : 0, cmd : lines[i] + '' }; } else if (lines[i].substring(0, 2) === 'L ') { mscript.light_state(lines[i]); } else if (lines[i].substring(0, 3) === 'END') { for (var x = 0; x < mscript.loop_count(mscript.state.loops[mscript.state.rec].cmd); x++) { if (mscript.state.rec === 0) { arr.push.apply(arr, mscript.state.loops[mscript.state.rec].arr); light.push.apply(light, mscript.state.loops[mscript.state.rec].light); } else if (mscript.state.rec >= 1) { mscript.state.loops[mscript.state.rec - 1].arr .push.apply(mscript.state.loops[mscript.state.rec - 1].arr, mscript.state.loops[mscript.state.rec].arr); mscript.state.loops[mscript.state.rec - 1].light .push.apply(mscript.state.loops[mscript.state.rec - 1].light, mscript.state.loops[mscript.state.rec].light); } } mscript.state_update('END', mscript.loop_count(mscript.state.loops[mscript.state.rec].cmd)); delete mscript.state.loops[mscript.state.rec]; mscript.state.rec--; } else if (lines[i].substring(0, 3) === 'CAM') { //directly go to that frame (black?) target = parseInt(lines[i].split('CAM ')[1]); if (mscript.state.loops.length > 0) { if (target > mscript.state.cam) { dist = target - mscript.state.cam; for (var x = 0; x < dist; x++) { mscript.state.loops[mscript.state.rec].arr.push('BF'); mscript.state.loops[mscript.state.rec].light.push(mscript.black); mscript.state_update('BF'); } } else { dist = mscript.state.cam - target; for (var x = 0; x < dist; x++) { mscript.state.loops[mscript.state.rec].arr.push('BB'); mscript.state.loops[mscript.state.rec].light.push(mscript.black); mscript.state_update('BB'); } } } else { if (target > mscript.state.cam) { dist = target - mscript.state.cam; for (var x = 0; x < dist; x++) { arr.push('BF'); light.push(mscript.black); mscript.state_update('BF'); } } else { dist = mscript.state.cam - target; for (var x = 0; x < dist; x++) { arr.push('BB'); light.push(mscript.black); mscript.state_update('BB'); } } } } else if (lines[i].substring(0, 4) === 'PROJ') { //directly go to that frame target = parseInt(lines[i].split('PROJ ')[1]); if (mscript.state.loops.length > 0) { if (target > mscript.state.proj) { dist = target - mscript.state.proj; for (var x = 0; x < dist; x++) { mscript.state.loops[mscript.state.rec].arr.push('PF'); mscript.state.loops[mscript.state.rec].light.push(''); mscript.state_update('PF'); } } else { dist = mscript.state.proj - target; for (var x = 0; x < dist; x++) { mscript.state.loops[mscript.state.rec].arr.push('PB'); mscript.state.loops[mscript.state.rec].light.push(''); mscript.state_update('PB'); } } } else { if (target > mscript.state.proj) { dist = target - mscript.state.proj; for (var x = 0; x < dist; x++) { arr.push('PF'); light.push(''); mscript.state_update('PF'); } } else { dist = mscript.state.proj - target; for (var x = 0; x < dist; x++) { arr.push('PB'); light.push(''); mscript.state_update('PB'); } } } } else if (lines[i].substring(0, 3) === 'SET') { //set that state if (lines[i].substring(0, 7) === 'SET CAM') { mscript.state.cam = parseInt(lines[i].split('SET CAM')[1]); } else if (lines[i].substring(0, 8) === 'SET PROJ') { mscript.state.proj = parseInt(lines[i].split('SET PROJ')[1]); } } else if (lines[i].substring(0, 1) === '#' || lines[i].substring(0, 2) === '//') { //comments //ignore while parsing } } output.success = true; output.arr = arr; output.light = light; output.cam = mscript.state.cam; output.proj = mscript.state.proj; if (typeof callback !== 'undefined') { //should only be invoked by running mscript.tests() callback(output); } else { return mscript.output(output); } }; mscript.last_loop = function last_loop () { 'use strict'; return mscript.state.loops[mscript.state.loops.length - 1]; }; mscript.parent_loop = function parent_loop () { 'use script'; return mscript.state.loops[mscript.state.loops.length - 2]; }; mscript.state_update = function state_update (cmd, val) { 'use strict'; if (cmd === 'END') { for (var i = 0; i < val; i++) { if (mscript.state.rec === 0) { mscript.state.cam += mscript.state.loops[mscript.state.rec].cam; mscript.state.proj += mscript.state.loops[mscript.state.rec].proj; } else if (mscript.state.rec >= 1) { mscript.state.loops[mscript.state.rec - 1].cam += mscript.state.loops[mscript.state.rec].cam; mscript.state.loops[mscript.state.rec - 1].proj += mscript.state.loops[mscript.state.rec].proj; } } } else if (cmd === 'CF') { if (mscript.state.loops.length < 1) { mscript.state.cam++; } else { mscript.state.loops[mscript.state.rec].cam++; } } else if (cmd === 'CB') { if (mscript.state.loops.length < 1) { mscript.state.cam--; } else { mscript.state.loops[mscript.state.rec].cam--; } } else if (cmd === 'PF') { if (mscript.state.loops.length < 1) { mscript.state.proj++; } else { mscript.state.loops[mscript.state.rec].proj++; } } else if (cmd === 'PB') { if (mscript.state.loops.length < 1) { mscript.state.proj--; } else { mscript.state.loops[mscript.state.rec].proj--; } } else if (cmd === 'BF') { if (mscript.state.loops.length < 1) { mscript.state.cam++; } else { mscript.state.loops[mscript.state.rec].cam++; } } else if (cmd === 'BB') { if (mscript.state.loops.length < 1) { mscript.state.cam--; } else { mscript.state.loops[mscript.state.rec].cam++; } } else if (cmd === 'L ') { } }; mscript.str_to_arr = function str_to_arr (str, cmd) { 'use strict'; var cnt = str.split(cmd), c = parseInt(cnt[1]), arr = []; if (cnt[1] === '') { c = 1; } else { c = parseInt(cnt[1]); } for (var i = 0; i < c; i++) { arr.push(cmd); mscript.state_update(cmd); } return arr; }; mscript.light_state = function light_state (str) { 'use strict'; //add parsers for other color spaces var color = str.replace('L ', '').trim(); mscript.state.color = color; }; mscript.light_to_arr = function light_to_arr (str, cmd) { var cnt = str.split(cmd), c = parseInt(cnt[1]), arr = []; if (cnt[1] === '') { c = 1; } else { c = parseInt(cnt[1]); } for (var i = 0; i < c; i++) { if (cmd === 'CF' || cmd === 'CB') { arr.push(mscript.state.color); } else if (cmd === 'BF' || cmd === 'BB') { arr.push(mscript.black); } else { arr.push(''); } } return arr; }; mscript.loop_count = function loop_count (str) { 'use strict'; return parseInt(str.split('LOOP ')[1]); }; mscript.fail = function fail (reason) { 'use strict'; console.error(JSON.stringify({success: false, error: true, msg : reason})); if (process) process.exit(); }; mscript.output = function output (data) { 'use strict'; var json = true; //default if (mscript.arg('-j', '--json')) { json = true; } if (mscript.arg('-t', '--text')) { json = false; } if (json) { console.log(JSON.stringify(data)); } else { var ids = Object.keys(data); for (var i = 0; i < ids.length; i++) { console.log(ids[i] + ': ' + data[ids[i]]); } } }; mscript.init = function init () { 'use strict'; if (mscript.arg('-t', '--tests')) { return mscript.tests(); } if (mscript.arg('-v', '--verbose')) { console.time('mscript'); } if (mscript.arg('-c', '--cam')) { mscript.state.cam = parseInt(process.argv[mscript.arg_pos('-c', '--cam') + 1]); } if (mscript.arg('-p', '--proj')) { mscript.state.proj = parseInt(process.argv[mscript.arg_pos('-p', '--proj') + 1]); } if (mscript.arg('-f', '--file')) { input = process.argv[mscript.arg_pos('-f', '--file') + 1]; mscript.interpret(fs.readFileSync(input, 'utf8')); } else { mscript.interpret(input); } if (mscript.arg('-v', '--verbose')) { console.timeEnd('mscript'); } }; mscript.tests = function tests () { 'use strict'; console.log('Running mscript tests'); console.time('Tests took'); mscript.alts_unique(); //perform check only during tests var fail = function (script, obj) { console.log('...Failed :('); console.log(script); console.log(obj); process.exit(); }; var script = 'CF\nPF\nCB\nPB\nBF\nBB'; console.log('Basic function test...'); mscript.interpret(script, function (obj) { if (obj.success === true && obj.cam === 0 && obj.proj === 0 && obj.arr.length === 6) { console.log('...Passed!'); } else { fail(script, obj); } }); var script = 'CF\nPF\nCB\nPB\nBF\nBB'; console.log('Functions with integers test...'); mscript.interpret(script, function (obj) { if (obj.success === true && obj.cam === 0 && obj.proj === 0 && obj.arr.length === 6) { console.log('...Passed!'); } else { fail(script, obj); } }); script = 'CF 1000\nCB 1000\nSET PROJ 200\nPB 200'; console.log('Basic state test...'); mscript.interpret(script, function (obj) { if (obj.success === true && obj.cam === 0 && obj.proj === 0) { console.log('...Passed!'); } else { fail(script, obj); } }); script = 'LOOP 10\nCF 3\nPF 1\nEND LOOP'; console.log('Basic loop test...'); mscript.interpret(script, function (obj) { if (obj.success === true && obj.cam === 30 && obj.proj === 10 && obj.arr.length === 40) { console.log('...Passed!'); } else { fail(script, obj); } }); script = 'LOOP 4\nLOOP 4\nPF\nBF\nEND LOOP\nEND LOOP'; console.log('Recursive loop test...'); mscript.interpret(script, function (obj) { if (obj.success === true && obj.cam === 16 && obj.proj === 16 && obj.arr.length === 32) { console.log('...Passed!'); } else { fail(script, obj); } }); //Lighting tests script = 'L 255,255,255\nCF\nPF'; console.log('Basic light test...'); mscript.interpret(script, function (obj) { if (obj.success === true && obj.cam === 1 && obj.proj === 1 && obj.arr.length === 2 && obj.light.length === 2 && obj.light[0] === '255,255,255' && obj.light[1] === '') { console.log('...Passed!'); } else { fail(script, obj); } }); script = 'L 255,255,255\nCF\nPF\nBF'; console.log('Basic black test...'); mscript.interpret(script, function (obj) { if (obj.success === true && obj.cam === 2 && obj.proj === 1 && obj.arr.length === 3 && obj.light.length === 3 && obj.light[0] === '255,255,255' && obj.light[1] === '' && obj.light[2] === mscript.black) { console.log('...Passed!'); } else { fail(script, obj); } }); script = 'LOOP 2\nL 1,1,1\nCF\nL 2,2,2\nCF\nEND'; console.log('Basic light loop test...'); mscript.interpret(script, function (obj) { if (obj.success === true && obj.cam === 4 && obj.proj === 0 && obj.arr.length === 4 && obj.light.length === 4 && obj.light[0] === '1,1,1' && obj.light[3] === '2,2,2') { console.log('...Passed!'); } else { fail(script, obj); } }); //LOOP W/ CAM and PROJ script = 'LOOP 2\nCAM 4\nPROJ 4\nEND'; console.log('Basic cam/proj loop test...'); mscript.interpret(script, function (obj) { if (obj.success === true && obj.cam === 8 && obj.proj === 8 && obj.arr.length === 16 && obj.light.length === 16 && obj.light[0] === mscript.black) { console.log('...Passed!'); } else { fail(script, obj); } }); console.log('All tests completed'); console.timeEnd('Tests took'); }; if (typeof document === 'undefined' && typeof module !== 'undefined' && !module.parent) { //node script fs = require('fs'); input = process.argv[2]; mscript.init(); } else if (typeof module !== 'undefined' && module.parent) { //module fs = require('fs'); module.exports = mscript; } else { //web } /* CAM # - go to camera frame # PROJ # - go to projector frame # SET CAM # - sets camera count to # SET PROJ # - sets projector count to # LOOP # - begin loop, can nest recursively, # times END LOOP - (or END) closes loop L #RGB - sets light to rgb value FADE CF - Camera forwards PF - Projector forwards BF - Black forwards CB - Camera backwards PB - Projector backwards BB - Black backwards */