mirror of https://github.com/odrling/Aegisub
Expression evaluator implemented and compiles... probably doesn't work
Originally committed to SVN as r1506.
This commit is contained in:
parent
6cc27087dc
commit
3b5e953c70
|
@ -137,10 +137,6 @@ ceil
|
|||
Takes one argument, produces one result.
|
||||
Round towards positive infinity.
|
||||
|
||||
trunc
|
||||
Takes one argument, produces one result.
|
||||
Round towards zero.
|
||||
|
||||
|
||||
log
|
||||
Takes one argument, produces one result.
|
||||
|
|
|
@ -0,0 +1,573 @@
|
|||
/*
|
||||
* OverLua expression engine
|
||||
*
|
||||
|
||||
Copyright 2007 Niels Martin Hansen
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
Contact:
|
||||
E-mail: <jiifurusu@gmail.com>
|
||||
IRC: jfs in #aegisub on irc.rizon.net
|
||||
|
||||
*/
|
||||
|
||||
|
||||
#include "expression_engine.h"
|
||||
#include <math.h>
|
||||
|
||||
|
||||
namespace ExpressionEngine {
|
||||
|
||||
// Builtin functions
|
||||
|
||||
static bool f_abs(Stack &stack, void *data)
|
||||
{
|
||||
if (stack.size() >= 1) {
|
||||
double v = stack.back();
|
||||
stack.pop_back();
|
||||
stack.push_back(fabs(v));
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
static const Function fs_abs = {"abs", f_abs};
|
||||
|
||||
static bool f_floor(Stack &stack, void *data)
|
||||
{
|
||||
if (stack.size() >= 1) {
|
||||
double v = stack.back();
|
||||
stack.pop_back();
|
||||
stack.push_back(floor(v));
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
static const Function fs_floor = {"floor", f_floor};
|
||||
|
||||
static bool f_ceil(Stack &stack, void *data)
|
||||
{
|
||||
if (stack.size() >= 1) {
|
||||
double v = stack.back();
|
||||
stack.pop_back();
|
||||
stack.push_back(ceil(v));
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
static const Function fs_ceil = {"ceil", f_ceil};
|
||||
|
||||
static bool f_log(Stack &stack, void *data)
|
||||
{
|
||||
if (stack.size() >= 1) {
|
||||
double v = stack.back();
|
||||
stack.pop_back();
|
||||
stack.push_back(log(v));
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
static const Function fs_log = {"log", f_log};
|
||||
|
||||
static bool f_exp(Stack &stack, void *data)
|
||||
{
|
||||
if (stack.size() >= 1) {
|
||||
double v = stack.back();
|
||||
stack.pop_back();
|
||||
stack.push_back(exp(v));
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
static const Function fs_exp = {"exp", f_exp};
|
||||
|
||||
static bool f_sqrt(Stack &stack, void *data)
|
||||
{
|
||||
if (stack.size() >= 1) {
|
||||
double v = stack.back();
|
||||
stack.pop_back();
|
||||
stack.push_back(sqrt(v));
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
static const Function fs_sqrt = {"sqrt", f_sqrt};
|
||||
|
||||
static bool f_e(Stack &stack, void *data)
|
||||
{
|
||||
stack.push_back(2.71828182845904523536);
|
||||
return true;
|
||||
}
|
||||
static const Function fs_e = {"e", f_e};
|
||||
|
||||
static bool f_min(Stack &stack, void *data)
|
||||
{
|
||||
if (stack.size() >= 2) {
|
||||
double v1 = stack.back();
|
||||
stack.pop_back();
|
||||
double v2 = stack.back();
|
||||
stack.pop_back();
|
||||
if (v1 < v2)
|
||||
stack.push_back(v1);
|
||||
else
|
||||
stack.push_back(v2);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
static const Function fs_min = {"min", f_min};
|
||||
|
||||
static bool f_max(Stack &stack, void *data)
|
||||
{
|
||||
if (stack.size() >= 2) {
|
||||
double v1 = stack.back();
|
||||
stack.pop_back();
|
||||
double v2 = stack.back();
|
||||
stack.pop_back();
|
||||
if (v1 > v2)
|
||||
stack.push_back(v1);
|
||||
else
|
||||
stack.push_back(v2);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
static const Function fs_max = {"max", f_max};
|
||||
|
||||
static bool f_pi(Stack &stack, void *data)
|
||||
{
|
||||
stack.push_back(3.14159265358979323846);
|
||||
return true;
|
||||
}
|
||||
static const Function fs_pi = {"pi", f_pi};
|
||||
|
||||
static bool f_sin(Stack &stack, void *data)
|
||||
{
|
||||
if (stack.size() >= 1) {
|
||||
double v = stack.back();
|
||||
stack.pop_back();
|
||||
stack.push_back(sin(v));
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
static const Function fs_sin = {"sin", f_sin};
|
||||
|
||||
static bool f_cos(Stack &stack, void *data)
|
||||
{
|
||||
if (stack.size() >= 1) {
|
||||
double v = stack.back();
|
||||
stack.pop_back();
|
||||
stack.push_back(cos(v));
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
static const Function fs_cos = {"cos", f_cos};
|
||||
|
||||
static bool f_tan(Stack &stack, void *data)
|
||||
{
|
||||
if (stack.size() >= 1) {
|
||||
double v = stack.back();
|
||||
stack.pop_back();
|
||||
stack.push_back(tan(v));
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
static const Function fs_tan = {"tan", f_tan};
|
||||
|
||||
static bool f_asin(Stack &stack, void *data)
|
||||
{
|
||||
if (stack.size() >= 1) {
|
||||
double v = stack.back();
|
||||
stack.pop_back();
|
||||
stack.push_back(asin(v));
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
static const Function fs_asin = {"asin", f_asin};
|
||||
|
||||
static bool f_acos(Stack &stack, void *data)
|
||||
{
|
||||
if (stack.size() >= 1) {
|
||||
double v = stack.back();
|
||||
stack.pop_back();
|
||||
stack.push_back(acos(v));
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
static const Function fs_acos = {"acos", f_acos};
|
||||
|
||||
static bool f_atan(Stack &stack, void *data)
|
||||
{
|
||||
if (stack.size() >= 1) {
|
||||
double v = stack.back();
|
||||
stack.pop_back();
|
||||
stack.push_back(atan(v));
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
static const Function fs_atan = {"atan", f_atan};
|
||||
|
||||
static bool f_mod(Stack &stack, void *data)
|
||||
{
|
||||
if (stack.size() >= 2) {
|
||||
double v1 = stack.back();
|
||||
stack.pop_back();
|
||||
double v2 = stack.back();
|
||||
stack.pop_back();
|
||||
stack.push_back(fmod(v2, v1));
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
static const Function fs_mod = {"mod", f_mod};
|
||||
|
||||
static bool f_rand(Stack &stack, void *data)
|
||||
{
|
||||
stack.push_back((double)rand()/RAND_MAX);
|
||||
return true;
|
||||
}
|
||||
static const Function fs_rand = {"rand", f_rand};
|
||||
|
||||
static bool f_ifgtz(Stack &stack, void *data)
|
||||
{
|
||||
if (stack.size() >= 2) {
|
||||
double v1 = stack.back();
|
||||
stack.pop_back();
|
||||
double v2 = stack.back();
|
||||
stack.pop_back();
|
||||
double v3 = stack.back();
|
||||
stack.pop_back();
|
||||
if (v3 > 0)
|
||||
stack.push_back(v2);
|
||||
else
|
||||
stack.push_back(v1);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
static const Function fs_ifgtz = {"ifgtz", f_ifgtz};
|
||||
|
||||
static bool f_ifeqz(Stack &stack, void *data)
|
||||
{
|
||||
if (stack.size() >= 2) {
|
||||
double v1 = stack.back();
|
||||
stack.pop_back();
|
||||
double v2 = stack.back();
|
||||
stack.pop_back();
|
||||
double v3 = stack.back();
|
||||
stack.pop_back();
|
||||
if (v3 == 0)
|
||||
stack.push_back(v2);
|
||||
else
|
||||
stack.push_back(v1);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
static const Function fs_ifeqz = {"ifeqz", f_ifeqz};
|
||||
|
||||
|
||||
// Machine specification
|
||||
|
||||
Specification::Specification()
|
||||
{
|
||||
// Add standard functions
|
||||
functions.push_back(fs_abs);
|
||||
functions.push_back(fs_floor);
|
||||
functions.push_back(fs_ceil);
|
||||
functions.push_back(fs_log);
|
||||
functions.push_back(fs_exp);
|
||||
functions.push_back(fs_sqrt);
|
||||
functions.push_back(fs_e);
|
||||
functions.push_back(fs_min);
|
||||
functions.push_back(fs_max);
|
||||
functions.push_back(fs_pi);
|
||||
functions.push_back(fs_sin);
|
||||
functions.push_back(fs_cos);
|
||||
functions.push_back(fs_tan);
|
||||
functions.push_back(fs_asin);
|
||||
functions.push_back(fs_acos);
|
||||
functions.push_back(fs_atan);
|
||||
functions.push_back(fs_mod);
|
||||
functions.push_back(fs_rand);
|
||||
functions.push_back(fs_ifgtz);
|
||||
functions.push_back(fs_ifeqz);
|
||||
}
|
||||
|
||||
|
||||
// Machine runner
|
||||
|
||||
bool Machine::Run()
|
||||
{
|
||||
// Prepare the stack
|
||||
std::vector<double> stack;
|
||||
stack.reserve(16);
|
||||
|
||||
// Assume the registers are already initialised like the manager wants
|
||||
|
||||
// Execute the program
|
||||
for (size_t pc = 0; pc < program.size(); pc++) {
|
||||
Instruction &i = program[pc];
|
||||
switch (i.op) {
|
||||
double v1, v2; // values for operators;
|
||||
|
||||
case INST_PUSH_CONST:
|
||||
stack.push_back(i.vd);
|
||||
break;
|
||||
|
||||
case INST_PUSH_REG:
|
||||
stack.push_back(registers[i.vu]);
|
||||
break;
|
||||
|
||||
case INST_ADD:
|
||||
if (stack.size() < 2) return false;
|
||||
v1 = stack.back(); stack.pop_back();
|
||||
v2 = stack.back(); stack.pop_back();
|
||||
stack.push_back(v2+v1);
|
||||
break;
|
||||
|
||||
case INST_SUB:
|
||||
if (stack.size() < 2) return false;
|
||||
v1 = stack.back(); stack.pop_back();
|
||||
v2 = stack.back(); stack.pop_back();
|
||||
stack.push_back(v2-v1);
|
||||
break;
|
||||
|
||||
case INST_MUL:
|
||||
if (stack.size() < 2) return false;
|
||||
v1 = stack.back(); stack.pop_back();
|
||||
v2 = stack.back(); stack.pop_back();
|
||||
stack.push_back(v2*v1);
|
||||
break;
|
||||
|
||||
case INST_DIV:
|
||||
if (stack.size() < 2) return false;
|
||||
v1 = stack.back(); stack.pop_back();
|
||||
v2 = stack.back(); stack.pop_back();
|
||||
stack.push_back(v2/v1);
|
||||
break;
|
||||
|
||||
case INST_POW:
|
||||
if (stack.size() < 2) return false;
|
||||
v1 = stack.back(); stack.pop_back();
|
||||
v2 = stack.back(); stack.pop_back();
|
||||
stack.push_back(pow(v2, v1));
|
||||
break;
|
||||
|
||||
case INST_UNM:
|
||||
if (stack.size() < 1) return false;
|
||||
v1 = stack.back(); stack.pop_back();
|
||||
stack.push_back(-v1);
|
||||
break;
|
||||
|
||||
case INST_CALL:
|
||||
if (!i.vf(stack, i.vfd))
|
||||
return false;
|
||||
break;
|
||||
|
||||
case INST_STORE:
|
||||
if (stack.size() < 1) return false;
|
||||
v1 = stack.back(); stack.pop_back();
|
||||
registers[i.vu] = v1;
|
||||
break;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// The registers should now be in the final state
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static const char *parse_register_name(const char *source, const std::vector<std::string> ®isters, size_t &index)
|
||||
{
|
||||
// Find end of the potential register name
|
||||
// That is, end of string or whitespace
|
||||
const char *end = source;
|
||||
while (*end && *end != ' ' && *end != '\t' && *end != '\n' && *end != '\r') end++;
|
||||
// Now end points to one past last character in name
|
||||
|
||||
std::string regname(source, end-source);
|
||||
|
||||
if (regname.size() == 0) return 0;
|
||||
|
||||
// Check for supplied register name
|
||||
for (size_t i = 0; i < registers.size(); i++) {
|
||||
if (regname == registers[i]) {
|
||||
index = i;
|
||||
return end;
|
||||
}
|
||||
}
|
||||
|
||||
// Check for temp register name
|
||||
if (regname[0] == 't' && regname.size() == 2) {
|
||||
if (regname[1] >= '0' && regname[1] <= '9') {
|
||||
index = registers.size() + regname[1] - '0';
|
||||
return end;
|
||||
}
|
||||
}
|
||||
|
||||
// Nothing matches
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const char *parse_function_name(const char *source, const std::vector<Function> &functions, FunctionPtr &funcptr, void *&funcdata)
|
||||
{
|
||||
// Find end of the potential function name
|
||||
// That is, end of string or whitespace
|
||||
const char *end = source;
|
||||
while (*end && *end != ' ' && *end != '\t' && *end != '\n' && *end != '\r') end++;
|
||||
// Now end points to one past last character in name
|
||||
|
||||
std::string funcname(source, end-source);
|
||||
|
||||
if (funcname.size() == 0) return 0;
|
||||
|
||||
// Check for supplied register name
|
||||
for (size_t i = 0; i < functions.size(); i++) {
|
||||
if (funcname == functions[i].name) {
|
||||
funcptr = functions[i].function;
|
||||
funcdata = functions[i].data;
|
||||
return end;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
Machine::Machine(const ExpressionEngine::Specification &spec, const char *source)
|
||||
{
|
||||
// Set up the registers
|
||||
const size_t temp_register_count = 10;
|
||||
registers.resize(spec.registers.size() + temp_register_count);
|
||||
|
||||
program.reserve(64);
|
||||
|
||||
// Parse the program
|
||||
while (*source) {
|
||||
Instruction i;
|
||||
|
||||
// Skip whitespace
|
||||
while (*source && (*source == ' ' || *source == '\t' || *source == '\n' || *source == '\r')) source++;
|
||||
if (!*source) break;
|
||||
|
||||
// First see if it can be read as a number constant
|
||||
{
|
||||
char *tmp = 0;
|
||||
double v = strtod(source, &tmp);
|
||||
if (tmp && source != tmp) {
|
||||
// Could be read, so we have a constant here
|
||||
source = tmp;
|
||||
i.op = INST_PUSH_CONST;
|
||||
i.vd = v;
|
||||
program.push_back(i);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// Not a number constant
|
||||
// Check for arithmetic operator
|
||||
if (*source == '+') {
|
||||
source++;
|
||||
i.op = INST_ADD;
|
||||
program.push_back(i);
|
||||
}
|
||||
else if (*source == '-') {
|
||||
source++;
|
||||
i.op = INST_SUB;
|
||||
program.push_back(i);
|
||||
}
|
||||
else if (*source == '*') {
|
||||
source++;
|
||||
i.op = INST_MUL;
|
||||
program.push_back(i);
|
||||
}
|
||||
else if (*source == '/') {
|
||||
source++;
|
||||
i.op = INST_DIV;
|
||||
program.push_back(i);
|
||||
}
|
||||
else if (*source == '^') {
|
||||
source++;
|
||||
i.op = INST_POW;
|
||||
program.push_back(i);
|
||||
}
|
||||
else if (*source == '~') {
|
||||
source++;
|
||||
i.op = INST_UNM;
|
||||
program.push_back(i);
|
||||
}
|
||||
// Check for assignment
|
||||
else if (*source == '=') {
|
||||
i.op = INST_STORE;
|
||||
const char *tmp = parse_register_name(source+1, spec.registers, i.vu);
|
||||
if (!tmp) throw source; // No register name found, error
|
||||
source = tmp;
|
||||
program.push_back(i);
|
||||
}
|
||||
// Register push or function call
|
||||
else {
|
||||
const char *tmp = parse_register_name(source, spec.registers, i.vu);
|
||||
if (tmp) {
|
||||
// Register push
|
||||
i.op = INST_PUSH_REG;
|
||||
source = tmp;
|
||||
program.push_back(i);
|
||||
}
|
||||
else {
|
||||
tmp = parse_function_name(source, spec.functions, i.vf, i.vfd);
|
||||
if (tmp) {
|
||||
// Function call
|
||||
i.op = INST_CALL;
|
||||
source = tmp;
|
||||
program.push_back(i);
|
||||
}
|
||||
else {
|
||||
// Nothing, error
|
||||
throw source;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} /* end while */
|
||||
|
||||
} /* end Machine::Machine() */
|
||||
|
||||
};
|
|
@ -0,0 +1,113 @@
|
|||
/*
|
||||
* OverLua expression engine
|
||||
*
|
||||
|
||||
Copyright 2007 Niels Martin Hansen
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
Contact:
|
||||
E-mail: <jiifurusu@gmail.com>
|
||||
IRC: jfs in #aegisub on irc.rizon.net
|
||||
|
||||
*/
|
||||
|
||||
#ifndef EXPRESSION_ENGINE_H
|
||||
#define EXPRESSION_ENGINE_H
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
|
||||
namespace ExpressionEngine {
|
||||
|
||||
// The stack passed around
|
||||
typedef std::vector<double> Stack;
|
||||
|
||||
// Type of callable functions
|
||||
typedef bool (*FunctionPtr)(Stack &stack, void *data);
|
||||
|
||||
// A callable function
|
||||
struct Function {
|
||||
// The name of the function
|
||||
const char *name;
|
||||
// Function pointer; null for builtins
|
||||
FunctionPtr function;
|
||||
// Function data
|
||||
void *data;
|
||||
};
|
||||
|
||||
// A machine specification that can compile programs
|
||||
struct Specification {
|
||||
// The input and output register names
|
||||
// The index of a register name in this vector translates to the register's number in the machine
|
||||
std::vector<std::string> registers;
|
||||
|
||||
// The callable functions
|
||||
std::vector<Function> functions;
|
||||
|
||||
// Just adds standard functions to functions vector
|
||||
Specification();
|
||||
};
|
||||
|
||||
// Operation type
|
||||
enum Opcode {
|
||||
INST_PUSH_CONST = 0,
|
||||
INST_PUSH_REG,
|
||||
INST_ADD,
|
||||
INST_SUB,
|
||||
INST_MUL,
|
||||
INST_DIV,
|
||||
INST_POW,
|
||||
INST_UNM, // unary minus
|
||||
INST_CALL,
|
||||
INST_STORE
|
||||
};
|
||||
|
||||
// A single program instruction
|
||||
struct Instruction {
|
||||
Opcode op;
|
||||
union {
|
||||
double vd; // double value, for const push
|
||||
size_t vu; // uint value, for register indices
|
||||
struct {
|
||||
FunctionPtr vf; // function to call
|
||||
void *vfd; // data for function
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
// A program is a sequence of instructions
|
||||
typedef std::vector<Instruction> Program;
|
||||
|
||||
// A machine running a program
|
||||
struct Machine {
|
||||
// Values in the registers
|
||||
std::vector<double> registers;
|
||||
// The program
|
||||
Program program;
|
||||
|
||||
// Run the machine.
|
||||
// Return true is no errors, false if error
|
||||
bool Run();
|
||||
|
||||
// Create a machine from a specification and a program source
|
||||
Machine(const Specification &spec, const char *source);
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
|
||||
#endif
|
Loading…
Reference in New Issue