From a5b4ef2149eed994126bd30dc0dd0e840f6001e7 Mon Sep 17 00:00:00 2001 From: mmcwilliams Date: Mon, 25 Sep 2017 23:25:15 -0400 Subject: [PATCH] Import mscript library from mcopy Refactored into es6 style, intention is to extend it to accept delays in looping logic --- lib/mscript/Readme.md | 35 ++++ lib/mscript/index.js | 441 ++++++++++++++++++++++++++++++++++++++++++ tests/mscript.js | 169 ++++++++++++++++ 3 files changed, 645 insertions(+) create mode 100644 lib/mscript/Readme.md create mode 100644 lib/mscript/index.js create mode 100644 tests/mscript.js diff --git a/lib/mscript/Readme.md b/lib/mscript/Readme.md new file mode 100644 index 0000000..611eefa --- /dev/null +++ b/lib/mscript/Readme.md @@ -0,0 +1,35 @@ + + +## mscript +Object representing mscript parser + +**Kind**: global constant + +* [mscript](#mscript) + * [.arg(shrt, lng)](#mscript.arg) + * [.arg_pos(shrt, lng)](#mscript.arg_pos) + + + +### mscript.arg(shrt, lng) +Determine whether or not argument flag has been set + +**Kind**: static method of [mscript](#mscript) + +| Param | Type | Description | +| --- | --- | --- | +| shrt | string | Short flag name (ie `-a`) | +| lng | string | Long flag name (ie `--apple`) | + + + +### mscript.arg_pos(shrt, lng) +Determine position of flag, in argument array + +**Kind**: static method of [mscript](#mscript) + +| Param | Type | Description | +| --- | --- | --- | +| shrt | string | Short flag name (ie `-a`) | +| lng | string | Long flag name (ie `--apple`) | + diff --git a/lib/mscript/index.js b/lib/mscript/index.js new file mode 100644 index 0000000..c12700e --- /dev/null +++ b/lib/mscript/index.js @@ -0,0 +1,441 @@ +'use strict' + +let fs +let input; + +/** Object representing mscript parser */ +const mscript = {} + +/** +* Determine whether or not argument flag has been set +* +* +* @param {string} shrt Short flag name (ie `-a`) +* @param {string} lng Long flag name (ie `--apple`) +* +*/ +mscript.arg = function arg (shrt, lng) { + if (process.argv.indexOf(shrt) !== -1 || + process.argv.indexOf(lng) !== -1) { + return true + } + return false +} +/** +* Determine position of flag, in argument array +* +* +* @param {string} shrt Short flag name (ie `-a`) +* @param {string} lng Long flag name (ie `--apple`) +* +*/ +mscript.arg_pos = function arg_pos (shrt, lng) { + let 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', + 'D' +] +mscript.alts = { + 'CF' : ['CAMERA FORWARD', 'CAM FORWARD', 'C'], + 'PF' : ['PROJECTOR FORWARD', 'PROJ FORWARD', 'P'], + '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 = {} +//TODO: This will memory leak +mscript.state_clear = function state_clear () { + mscript.state = { + cam : 0, + proj : 0, + color : '', + loops : [], + rec : -1 + } +} + +mscript.alts_unique = function alts_unique () { + const ids = Object.keys(mscript.alts) + const all = [] + for (let i = 0; i < ids.length; i++) { + if (all.indexOf(ids[i]) === -1) { + all.push(ids[i]) + } else { + mscript.fail(1, "Can't parse") + } + } +} + +mscript.interpret = function interpret (text, callback) { + mscript.state_clear() + if (typeof text === 'undefined') { + mscript.fail(2, 'No input') + } + const lines = text.split('\n') + const arr = [] + const light = [] + const output = {} + + let two = '' + let target = 0 + let dist = 0 //? + let x + + //loop through all lines + for (let line of lines) { + //preprocess line + line = mscript.preprocess(line) + two = line.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(line, two)) + mscript.state.loops[mscript.state.rec].light.push.apply(mscript.state.loops[mscript.state.rec].light, mscript.light_to_arr(line, two)) + } else { + arr.push.apply(arr, mscript.str_to_arr(line, two)) + light.push.apply(light, mscript.light_to_arr(line, two)) + } + + } else if (line.substring(0, 4) === 'LOOP') { + mscript.state.rec++ + mscript.state.loops[mscript.state.rec] = { + arr : [], + light : [], + cam : 0, + proj : 0, + cmd : line + '' + } + } else if (line.substring(0, 2) === 'L ') { + mscript.light_state(line) + } else if (line.substring(0, 3) === 'END') { + for (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 (line.substring(0, 3) === 'CAM') { //directly go to that frame (black?) + target = parseInt(line.split('CAM ')[1]) + if (mscript.state.loops.length > 0) { + if (target > mscript.state.cam) { + dist = target - mscript.state.cam + for (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 (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 (x = 0; x < dist; x++) { + arr.push('BF') + light.push(mscript.black) + mscript.state_update('BF') + } + } else { + dist = mscript.state.cam - target + for (x = 0; x < dist; x++) { + arr.push('BB') + light.push(mscript.black) + mscript.state_update('BB') + } + } + } + } else if (line.substring(0, 4) === 'PROJ') { //directly go to that frame + target = parseInt(line.split('PROJ ')[1]) + if (mscript.state.loops.length > 0) { + if (target > mscript.state.proj) { + dist = target - mscript.state.proj + for (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 (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 (x = 0; x < dist; x++) { + arr.push('PF') + light.push('') + mscript.state_update('PF') + } + } else { + dist = mscript.state.proj - target + for (x = 0; x < dist; x++) { + arr.push('PB') + light.push('') + mscript.state_update('PB'); + } + } + } + } else if (line.substring(0, 3) === 'SET') { //set that state + if (line.substring(0, 7) === 'SET CAM') { + mscript.state.cam = parseInt(line.split('SET CAM')[1]); + } else if (line.substring(0, 8) === 'SET PROJ') { + mscript.state.proj = parseInt(line.split('SET PROJ')[1]); + } + } else if (line.substring(0, 1) === '#' || line.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.preprocess = function preprocess (line) { + line = line.replace(/\t+/g, '') //strip tabs + line = line.trim() //remove excess whitespace before and after command + line = line.toUpperCase() + return line +} + +mscript.last_loop = function last_loop () { + return mscript.state.loops[mscript.state.loops.length - 1] +} + +mscript.parent_loop = function parent_loop () { + return mscript.state.loops[mscript.state.loops.length - 2] +} + +mscript.state_update = function state_update (cmd, val) { + 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 ') { + //TODO : ???? + } +} + +mscript.str_to_arr = function str_to_arr (str, cmd) { + const cnt = str.split(cmd) + let arr = [] + let c = parseInt(cnt[1]) + + 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) { + //add parsers for other color spaces + const color = str.replace('L ', '').trim() + mscript.state.color = color +} + +mscript.light_to_arr = function light_to_arr (str, cmd) { + const cnt = str.split(cmd) + const arr = [] + let c = parseInt(cnt[1]) + + 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) { + return parseInt(str.split('LOOP ')[1]) +} + +mscript.fail = function fail (code, reason) { + const obj = { success: false, error: true, msg : reason } + console.error(JSON.stringify(obj)) + if (process) process.exit() +} + +mscript.output = function output (data) { + let 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 () { + 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') + } +}; + +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 + +*/ \ No newline at end of file diff --git a/tests/mscript.js b/tests/mscript.js new file mode 100644 index 0000000..8a9f777 --- /dev/null +++ b/tests/mscript.js @@ -0,0 +1,169 @@ +'use strict' +const log = require('../lib/log')('mscript-tests') +const mscript = require('../lib/mscript') +//TODO: rewrite for mocha + +const tests = function tests () { + log.info('Running mscript tests') + console.time('Tests took') + + mscript.alts_unique(); //perform check only during tests + var fail = function (script, obj) { + log.error('...Failed :(') + log.error('script', script) + log.error('err', obj) + process.exit(1) + } + let script = +`CF +PF +CB +PB +BF +BB` + log.info('Basic function test...'); + mscript.interpret(script, function (obj) { + if (obj.success === true + && obj.cam === 0 + && obj.proj === 0 + && obj.arr.length === 6) { + log.info('...Passed!') + } else { + fail(script, obj) + } + }) + + script = +`CF +PF +CB +PB +BF +BB` + log.info('Functions with integers test...') + mscript.interpret(script, function (obj) { + if (obj.success === true + && obj.cam === 0 + && obj.proj === 0 + && obj.arr.length === 6) { + log.info('...Passed!') + } else { + fail(script, obj) + } + }) + + script = +`CF 1000 +CB 1000 +SET PROJ 200 +PB 200` + log.info('Basic state test...') + mscript.interpret(script, function (obj) { + if (obj.success === true + && obj.cam === 0 + && obj.proj === 0) { + log.info('...Passed!') + } else { + fail(script, obj) + } + }) + + script = +`LOOP 10 +CF 3 +PF 1 +END LOOP` + log.info('Basic loop test...') + mscript.interpret(script, function (obj) { + if (obj.success === true + && obj.cam === 30 + && obj.proj === 10 + && obj.arr.length === 40) { + log.info('...Passed!') + } else { + fail(script, obj) + } + }); + + script = `LOOP 4\nLOOP 4\nPF\nBF\nEND LOOP\nEND LOOP` + log.info('Recursive loop test...'); + mscript.interpret(script, function (obj) { + if (obj.success === true + && obj.cam === 16 + && obj.proj === 16 + && obj.arr.length === 32) { + log.info('...Passed!'); + } else { + fail(script, obj); + } + }); + + //Lighting tests + script = `L 255,255,255\nCF\nPF` + log.info('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] === '') { + log.info('...Passed!'); + } else { + fail(script, obj); + } + }); + script = 'L 255,255,255\nCF\nPF\nBF'; + log.info('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) { + log.info('...Passed!'); + } else { + fail(script, obj); + } + }); + script = 'LOOP 2\nL 1,1,1\nCF\nL 2,2,2\nCF\nEND'; + log.info('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') { + log.info('...Passed!'); + } else { + fail(script, obj); + } + }); + + //LOOP W/ CAM and PROJ + script = 'LOOP 2\nCAM 4\nPROJ 4\nEND'; + log.info('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) { + log.info('...Passed!'); + } else { + fail(script, obj); + } + }); + + log.info('All tests completed'); + console.timeEnd('Tests took'); +} + +tests() \ No newline at end of file