182 lines
3.5 KiB
JavaScript
182 lines
3.5 KiB
JavaScript
class Juicescript {
|
|
// TOKEN TYPES //
|
|
static token_type = new Juicescript_helper_enum(
|
|
// keywords
|
|
"DEF", "GLOBAL", "END",
|
|
|
|
// literals
|
|
"IDENTIFIER", "VARIABLE", "FLAG", "STRING", "NUMBER",
|
|
|
|
// language constants
|
|
"TRUE", "FALSE",
|
|
"NULL",
|
|
|
|
// operators
|
|
"NOT",
|
|
"EQUAL_EQUAL", "NOT_EQUAL",
|
|
"GREATER", "GREATER_EQUAL",
|
|
"LESS", "LESS_EQUAL",
|
|
|
|
// brackets
|
|
"BRACKET_SQUARE_OPEN", "BRACKET_SQUARE_CLOSE",
|
|
"BRACKET_CURLY_OPEN", "BRACKET_CURLY_CLOSE",
|
|
|
|
// special chars
|
|
"AMPERSAND", "QUESTION_MARK",
|
|
|
|
// delimiters
|
|
"DELIMITER",
|
|
|
|
// meta stuff
|
|
"EOF"
|
|
);
|
|
|
|
|
|
// ARGUMENT TYPES //
|
|
static argument_type = new Juicescript_helper_enum(
|
|
// parsable types
|
|
"VARIABLE", "LITERAL", "OPERATOR", "IDENTIFIER",
|
|
|
|
// meta types
|
|
"VALUE"
|
|
);
|
|
|
|
|
|
// VARIABLE TYPES //
|
|
static data_type = new Juicescript_helper_enum(
|
|
// null
|
|
"NULL",
|
|
|
|
// boolean
|
|
"BOOL",
|
|
|
|
// simple values
|
|
"NUM", "STR"
|
|
);
|
|
|
|
|
|
|
|
// COMMAND FUNCTIONS //
|
|
static command = {};
|
|
|
|
|
|
|
|
/*
|
|
CONSTRUCTOR: Return new juicescript parser with OPTIONS
|
|
*/
|
|
constructor(options){
|
|
// STORE CALLBACKS //
|
|
// save
|
|
if(options.callback !== undefined){
|
|
this.callback = options.callback;
|
|
}
|
|
|
|
// check if all have been set
|
|
if(!(this.callback.stdout instanceof Function)){
|
|
throw "invalid stdout callback";
|
|
}
|
|
if(!(this.callback.stderr instanceof Function)){
|
|
throw "invalid stderr callback";
|
|
}
|
|
|
|
|
|
// GET IO ADAPTER //
|
|
this.io = new Juicescript_io(this.callback);
|
|
}
|
|
|
|
/*
|
|
Parse given PROGRAM-STRING and store syntax tree
|
|
*/
|
|
parse(program_string){
|
|
// CLEAR OLD PROGRAM TREE //
|
|
this.program_tree = null;
|
|
|
|
|
|
// DO SCANNING //
|
|
// get lexer
|
|
let lexer = new Juicescript_lexer(program_string, {
|
|
io: this.io
|
|
});
|
|
|
|
// run lexical analysis
|
|
let token_list = lexer.scan();
|
|
|
|
// stop here if unsuccessful
|
|
if(lexer.error_count > 0) return false;
|
|
|
|
|
|
// GET PROGRAM TREE FROM TOKENS //
|
|
// get parser
|
|
let parser = new Juicescript_parser(token_list, {
|
|
io: this.io
|
|
});
|
|
|
|
// do parsing
|
|
let program_tree = parser.parse();
|
|
|
|
// stop here if unsuccessful
|
|
if(parser.error_count > 0) return false;
|
|
|
|
|
|
// FINISH UP //
|
|
// store program tree
|
|
this.program_tree = program_tree;
|
|
|
|
// return success
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
Run previously parsed program
|
|
*/
|
|
run(){
|
|
// RUN //
|
|
// get runner
|
|
let runner = new Juicescript_runner(this.program_tree, {
|
|
io: this.io
|
|
});
|
|
|
|
// run the program
|
|
runner.run();
|
|
}
|
|
|
|
/*
|
|
HELPER: Add command with expanded command aliases to command function list
|
|
*/
|
|
static command_define(command){
|
|
// CHECK IF ALL ATTRIBUTES HAVE BEEN SET //
|
|
// name
|
|
if(!Object.keys(command).includes("name")){
|
|
throw "command definition missing its name";
|
|
}
|
|
|
|
// alias
|
|
if(!Object.keys(command).includes("alias")){
|
|
throw "command definition '" + command.name + "' missing its alias list";
|
|
}
|
|
|
|
// validate function
|
|
if(!(command.validate instanceof Function)){
|
|
throw "command definition '" + command.name + "' missing its validate function";
|
|
}
|
|
|
|
// execute function
|
|
if(!(command.execute instanceof Function)){
|
|
throw "command definition '" + command.name + "' missing its execute function";
|
|
}
|
|
|
|
|
|
for(var name_or_alias of command.alias.concat([command.name])){
|
|
// MAKE SURE WE'RE NOT RE-DEFINING IT //
|
|
if(Object.keys(Juicescript.command).includes(name_or_alias)){
|
|
// throw error
|
|
throw "cannot redefine command '" + name_or_alias + "'";
|
|
}
|
|
|
|
|
|
// ADD //
|
|
Juicescript.command[name_or_alias] = command;
|
|
}
|
|
}
|
|
}
|