mkjs-cli
Version:
Make-inspired build tool
268 lines (225 loc) • 6.22 kB
JavaScript
const fs = require('fs');
const path = require('path');
const vm = require('vm');
const T_EVAL_TIMEOUT = 8000; // 8 seconds
const h_eval = {
verbatim(h_verbatim) {
return h_verbatim.value;
},
inline(h_inline, y_context) {
let z_result = this.run(h_inline.code, y_context);
return h_inline.suppress? '': z_result+'';
},
if(h_if) {
if(this.run(h_if.if)) {
return this.eval(h_if.then);
}
else {
let s_output = '';
let b_elseifs = h_if.elseifs.some((h) => {
if(this.run(h.if)) {
s_output = this.eval(h.then);
return true;
}
});
if(!b_elseifs && this.run(h_if.else)) {
s_output = this.eval(h_if.else);
}
return s_output;
}
},
macro(h_macro) {
let f_macro = this.evaluate(`function ${h_macro.def} {
let __JMACS_OUTPUT = '';
${h_macro.body.map(this.codify).join('\n')}
return __JMACS_OUTPUT;
}`);
debugger;
this.macros[f_macro.name] = f_macro;
return '';
},
include(h_include, h_context) {
let s_file = this.evaluate(h_include.file, h_context);
let p_file = path.resolve(this.cwd, s_file);
let s_input = fs.readFileSync(p_file, 'utf8');
let h_result = require('./compiler')({
input: s_input,
cwd: path.dirname(p_file),
});
if(h_result.error) {
throw h_result.error;
}
// merge macros and globals
let h_include_macros = h_result.macros;
for(let s_macro in h_include_macros) {
this.macros[s_macro] = h_include_macros[s_macro];
}
let h_include_global = h_result.global;
for(let s_global in h_include_global) {
this.global[s_global] = h_include_global[s_global];
}
// append output
return h_result.output;
},
};
const h_codify = {
verbatim: h => `__JMACS_OUTPUT += ${JSON.stringify(h.value)};`,
inline: (s_code, b_suppress) => b_suppress
? s_code
: `__JMACS_OUTPUT += (${s_code});`,
if(h_if) {
let s_output = `if(${h_if.if}) {
${this.codify(h_if.then)}
}`;
for(let h_elseif of h_if.elseifs) {
s_output += `
else if(${h_elseif.if}) {
${this.codify(h_elseif.then)}
}`;
}
if(h_if.else.length) {
s_output += `
else {
${this.codify(h_if.else)}
}`;
}
return s_output;
},
};
class evaluator {
constructor() {
Object.assign(this, {
context: vm.createContext({}),
});
}
codify(z_syntax) {
debugger;
if(Array.isArray(z_syntax)) {
return z_syntax.map(h => this.codify(h).join('\n'));
}
else {
return [h_codify[z_syntax.type].apply(this, [z_syntax])];
}
}
run(s_code) {
// prep script
let y_script = new vm.Script(s_code, {});
// eval code
return y_script.runInContext(this.context, {
timeout: T_EVAL_TIMEOUT,
});
}
eval(z_syntax) {
if(Array.isArray(z_syntax)) {
return z_syntax.map(h => this.codify(h).join('\n'));
}
else {
return h_eval[z_syntax.type].apply(this, [z_syntax]);
}
}
}
module.exports = (a_sections) => {
return function(p_cwd, h_global=null, h_macros={}) {
let h_states = {
cwd: p_cwd,
macros: h_macros,
global: h_global,
output: '',
// evaluate_body(a_body, h_context) {
// let s_output = '';
// a_body.forEach((h) => {
// s_output += h_program[h.type].apply(this, [h, h_context]);
// });
// return s_output;
// },
// evaluate(h_expr, h_context, b_no_print=false) {
// if(!h_expr) return null;
// switch(h_expr.type) {
// case 'call': {
// let a_args = h_expr.args.map(h => this.evaluate(h, h_context, true));
// let z_out = this.call_macro(h_expr.macro, a_args);
// if(h_expr.quiet) return '';
// if(!b_no_print && 'object' === typeof z_out) {
// return this.evaluate(z_out, h_context);
// }
// return z_out;
// }
// case 'variable': {
// let s_name = h_expr.name;
// if(s_name in h_context) {
// let z_value = h_context[s_name];
// if(!b_no_print && 'object' === typeof z_value) {
// if(Array.isArray(z_value)) {
// return z_value.map((h_item) => {
// return this.evaluate(h_item, h_context);
// }).join(', ');
// }
// else {
// return this.evaluate(z_value, h_context);
// }
// }
// else {
// return z_value;
// }
// }
// else {
// console.warn(`variable '${s_name}' is undefined`);
// return null;
// // debugger;
// // throw 'undefined variable';
// }
// }
// case 'code': {
// let f_parse = module.parent.parent.exports.parse(h_expr.value);
// let h_eval = f_parse(p_cwd, h_context, this.macros);
// return h_eval.output;
// }
// default:
// debugger;
// break;
// }
// },
// call_macro(s_macro, a_args) {
// let h_macro = this.macros[s_macro];
// let s_output = '';
// let h_context = Object.create(this.global);
// if(!h_macro) {
// // built-in function
// if(H_BUILTIN_FUNCTIONS[s_macro]) {
// return H_BUILTIN_FUNCTIONS[s_macro](...(a_args.map((z_arg) => {
// return ('object' === typeof z_arg && !Array.isArray(z_arg))
// ? this.evaluate(z_arg, h_context, true)
// : z_arg;
// })));
// }
// throw new Error(`no such macro defined: ${s_macro}`);
// }
// let a_params = h_macro.params;
// a_params.forEach((h_param, i_param) => {
// h_context[h_param.name] = (a_args.length > i_param)
// ? a_args[i_param]
// : (h_param.default
// ? this.evaluate(h_param.default)
// : null);
// });
// let a_body = h_macro.body;
// let ne_body = a_body.length - 1;
// a_body.forEach((h_section, i_section) => {
// s_output += h_program[h_section.type].apply(this, [h_section, h_context]);
// });
// return s_output;
// },
};
let k_evaluator = new evaluator();
let a_output = k_evaluator.eval(a_sections);
// // process each section
// a_sections.forEach((h_section) => {
// h_states.output += h_eval[h_section.type].apply(h_states, [h_section, h_states.global]);
// });
let s_eval = `let __JMACS_OUTPUT = '';\n`+a_output.join('\n')+'\n__JMACS_OUTPUT;';
let z_result = k_evaluator.run(s_eval);
return {
output: z_result,
};
};
};