159 lines
3.6 KiB
JavaScript
159 lines
3.6 KiB
JavaScript
import Logger, { levels } from '../logger.js';
|
|
import { filename } from '../utils.js';
|
|
|
|
const log = new Logger(filename(import.meta.url), global.ctx.log_level);
|
|
|
|
class Target {
|
|
constructor(channel) {
|
|
this.channel = channel;
|
|
}
|
|
channel;
|
|
value;
|
|
}
|
|
|
|
const available_targets = {
|
|
r: new Target('r'),
|
|
g: new Target('g'),
|
|
b: new Target('b'),
|
|
stack: new Target('stack'),
|
|
stack2: new Target('stack2'),
|
|
stack3: new Target('stack3'),
|
|
stack4: new Target('stack4'),
|
|
stack5: new Target('stack5'),
|
|
stack6: new Target('stack6'),
|
|
stack6: new Target('stackr'),
|
|
stack6: new Target('stackg'),
|
|
stack6: new Target('stackb'),
|
|
tick: new Target('tick'),
|
|
index: new Target('index'),
|
|
};
|
|
|
|
class Instruction {
|
|
constructor(name, targets, args) {
|
|
let valid_targets = [];
|
|
targets.forEach((t) => {
|
|
if (t instanceof Target) {
|
|
let match = Object.keys(available_targets).find((key) => available_targets[key].channel === t.channel);
|
|
if (match) {
|
|
valid_targets.push(t);
|
|
}
|
|
}
|
|
});
|
|
this.name = name;
|
|
this.targets = valid_targets;
|
|
this.args = args;
|
|
}
|
|
name;
|
|
targets;
|
|
args;
|
|
}
|
|
|
|
export const instructions = [
|
|
{ n: 'CONSTANT', a: true },
|
|
{ n: 'ADD', a: true },
|
|
{ n: 'SUBTRACT', a: true },
|
|
{ n: 'MULTIPLY', a: true },
|
|
{ n: 'DIVIDE', a: true },
|
|
{ n: 'MODULO', a: true },
|
|
{ n: 'FADE', a: true },
|
|
{ n: 'RANDOM', a: false },
|
|
{ n: 'JMP', a: false },
|
|
{ n: 'JNZ', a: true },
|
|
{ n: 'JEZ', a: true },
|
|
];
|
|
|
|
export function initialize(ctx) {
|
|
log.s(ctx.log_level);
|
|
}
|
|
|
|
function parseSingleInstruction(str = '') {
|
|
let item = {
|
|
valid: false,
|
|
instruction: undefined,
|
|
errors: undefined,
|
|
};
|
|
let split = str.split(' ');
|
|
let cmd = split[0];
|
|
log.debug(cmd);
|
|
let match = instructions.filter((i) => i.n == cmd.toUpperCase());
|
|
log.debug(match);
|
|
split = split.slice(1);
|
|
if (match.length) {
|
|
let inst_targets = [];
|
|
let inst_args = [];
|
|
let parsed_args = [];
|
|
let join_index = -1;
|
|
let add = true;
|
|
for (let index in split) {
|
|
if (split[index].startsWith('[')) {
|
|
join_index = index;
|
|
add = false;
|
|
}
|
|
if (add) {
|
|
parsed_args.push(split[index]);
|
|
}
|
|
if (split[index].endsWith(']') && join_index != -1) {
|
|
let joined = split
|
|
.slice(join_index, index + 1)
|
|
.join(' ')
|
|
.replace(/[\[\]]/g, '');
|
|
parsed_args.push(joined);
|
|
add = true;
|
|
join_index = -1;
|
|
}
|
|
}
|
|
inst_targets = parsed_args[0]
|
|
.split(',')
|
|
.map((s) => s.trim())
|
|
.map((t) => new Target(t));
|
|
|
|
if (!inst_targets.length) {
|
|
item.errors = 'No valid targets.';
|
|
}
|
|
inst_targets.forEach((t) => {
|
|
let match = Object.keys(available_targets).find((key) => available_targets[key].channel === t.channel);
|
|
if (!match) {
|
|
item.errors = `Invalid target: ${t.channel}`;
|
|
}
|
|
});
|
|
if (match[0].a) {
|
|
if (parsed_args.length < 2) {
|
|
item.errors = 'Not enough arguments.';
|
|
return item;
|
|
}
|
|
inst_args = parsed_args[1]
|
|
.split(',')
|
|
.map((s) => s.trim())
|
|
.map((s) => (available_targets[s] ? s : parseInt(s)));
|
|
if (!inst_args.length) {
|
|
item.errors = 'No valid args.';
|
|
}
|
|
inst_args.forEach((arg) => {
|
|
if (arg === NaN) {
|
|
item.errors = 'An argument is null or undefined.';
|
|
}
|
|
});
|
|
}
|
|
if (item.errors) {
|
|
return item;
|
|
}
|
|
|
|
item.instruction = new Instruction(match[0].n, inst_targets, inst_args || []);
|
|
} else {
|
|
item.errors = 'No command matched.';
|
|
return item;
|
|
}
|
|
item.valid = true;
|
|
return item;
|
|
}
|
|
|
|
export default function parse(str) {
|
|
let parsed = [];
|
|
let split = str.split('\n').filter((s) => s.length > 0);
|
|
split.forEach((item) => {
|
|
let parsedItem = parseSingleInstruction(item);
|
|
parsed.push(parsedItem);
|
|
log.debug(item);
|
|
});
|
|
return parsed;
|
|
}
|