summaryrefslogtreecommitdiffstats
path: root/utils/regtools/lib/formula.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'utils/regtools/lib/formula.cpp')
-rw-r--r--utils/regtools/lib/formula.cpp214
1 files changed, 214 insertions, 0 deletions
diff --git a/utils/regtools/lib/formula.cpp b/utils/regtools/lib/formula.cpp
new file mode 100644
index 0000000000..1a6b16c773
--- /dev/null
+++ b/utils/regtools/lib/formula.cpp
@@ -0,0 +1,214 @@
+#include "soc_desc.hpp"
+#include <cstdarg>
+#include <cstdio>
+
+using namespace soc_desc;
+
+namespace soc_desc
+{
+
+namespace
+{
+
+struct formula_evaluator
+{
+ std::string formula;
+ size_t pos;
+ error_context_t& ctx;
+ std::string m_loc;
+
+ bool err(const char *fmt, ...)
+ {
+ char buffer[256];
+ va_list args;
+ va_start(args, fmt);
+ vsnprintf(buffer,sizeof(buffer), fmt, args);
+ va_end(args);
+ ctx.add(error_t(error_t::FATAL, m_loc, buffer));
+ return false;
+ }
+
+ formula_evaluator(const std::string& s, error_context_t& ctx):pos(0),ctx(ctx)
+ {
+ for(size_t i = 0; i < s.size(); i++)
+ if(!isspace(s[i]))
+ formula.push_back(s[i]);
+ }
+
+ void set_location(const std::string& loc)
+ {
+ m_loc = loc;
+ }
+
+ void adv()
+ {
+ pos++;
+ }
+
+ char cur()
+ {
+ return end() ? 0 : formula[pos];
+ }
+
+ bool end()
+ {
+ return pos >= formula.size();
+ }
+
+ bool parse_digit(char c, int basis, soc_word_t& res)
+ {
+ c = tolower(c);
+ if(isdigit(c))
+ {
+ res = c - '0';
+ return true;
+ }
+ if(basis == 16 && isxdigit(c))
+ {
+ res = c + 10 - 'a';
+ return true;
+ }
+ return false;
+ }
+
+ bool parse_signed(soc_word_t& res)
+ {
+ char op = cur();
+ if(op == '+' || op == '-')
+ {
+ adv();
+ if(!parse_signed(res))
+ return false;
+ if(op == '-')
+ res *= -1;
+ return true;
+ }
+ else if(op == '(')
+ {
+ adv();
+ if(!parse_expression(res))
+ return false;
+ if(cur() != ')')
+ return err("expected ')', got '%c'", cur());
+ adv();
+ return true;
+ }
+ else if(isdigit(op))
+ {
+ res = op - '0';
+ adv();
+ int basis = 10;
+ if(op == '0' && cur() == 'x')
+ {
+ basis = 16;
+ adv();
+ }
+ soc_word_t digit = 0;
+ while(parse_digit(cur(), basis, digit))
+ {
+ res = res * basis + digit;
+ adv();
+ }
+ return true;
+ }
+ else if(isalpha(op) || op == '_')
+ {
+ std::string name;
+ while(isalnum(cur()) || cur() == '_')
+ {
+ name.push_back(cur());
+ adv();
+ }
+ return get_variable(name, res);
+ }
+ else
+ return err("express signed expression, got '%c'", op);
+ }
+
+ bool parse_term(soc_word_t& res)
+ {
+ if(!parse_signed(res))
+ return false;
+ while(cur() == '*' || cur() == '/' || cur() == '%')
+ {
+ char op = cur();
+ adv();
+ soc_word_t tmp;
+ if(!parse_signed(tmp))
+ return false;
+ if(op == '*')
+ res *= tmp;
+ else if(tmp != 0)
+ res = op == '/' ? res / tmp : res % tmp;
+ else
+ return err("division by 0");
+ }
+ return true;
+ }
+
+ bool parse_expression(soc_word_t& res)
+ {
+ if(!parse_term(res))
+ return false;
+ while(!end() && (cur() == '+' || cur() == '-'))
+ {
+ char op = cur();
+ adv();
+ soc_word_t tmp;
+ if(!parse_term(tmp))
+ return false;
+ if(op == '+')
+ res += tmp;
+ else
+ res -= tmp;
+ }
+ return true;
+ }
+
+ bool parse(soc_word_t& res)
+ {
+ bool ok = parse_expression(res);
+ if(ok && !end())
+ err("unexpected character '%c'", cur());
+ return ok && end();
+ }
+
+ virtual bool get_variable(std::string name, soc_word_t& res)
+ {
+ return err("unknown variable '%s'", name.c_str());
+ }
+};
+
+struct my_evaluator : public formula_evaluator
+{
+ const std::map< std::string, soc_word_t>& var;
+
+ my_evaluator(const std::string& formula, const std::map< std::string, soc_word_t>& _var,
+ error_context_t& ctx)
+ :formula_evaluator(formula, ctx), var(_var) {}
+
+ virtual bool get_variable(std::string name, soc_word_t& res)
+ {
+ std::map< std::string, soc_word_t>::const_iterator it = var.find(name);
+ if(it == var.end())
+ return formula_evaluator::get_variable(name, res);
+ else
+ {
+ res = it->second;
+ return true;
+ }
+ }
+};
+
+}
+
+bool evaluate_formula(const std::string& formula,
+ const std::map< std::string, soc_word_t>& var, soc_word_t& result,
+ const std::string& loc, error_context_t& error)
+{
+ my_evaluator e(formula, var, error);
+ e.set_location(loc);
+ return e.parse(result);
+}
+
+} // soc_desc