Expression evaluator implemented and compiles... probably doesn't work

Originally committed to SVN as r1506.
This commit is contained in:
Niels Martin Hansen 2007-08-17 23:26:36 +00:00
parent 6cc27087dc
commit 3b5e953c70
3 changed files with 686 additions and 4 deletions

View file

@ -137,10 +137,6 @@ ceil
Takes one argument, produces one result. Takes one argument, produces one result.
Round towards positive infinity. Round towards positive infinity.
trunc
Takes one argument, produces one result.
Round towards zero.
log log
Takes one argument, produces one result. Takes one argument, produces one result.

View file

@ -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> &registers, 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() */
};

113
OverLua/expression_engine.h Normal file
View file

@ -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