Merge pull request #314 from KLayout/vars-for-queries

Vars for queries
This commit is contained in:
Matthias Köfferlein 2019-08-18 17:31:11 +02:00 committed by GitHub
commit 16ae0346b8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 106 additions and 52 deletions

View File

@ -2427,9 +2427,9 @@ LayoutQuery::dump () const
}
void
LayoutQuery::execute (db::Layout &layout)
LayoutQuery::execute (db::Layout &layout, tl::Eval *context)
{
LayoutQueryIterator iq (*this, &layout);
LayoutQueryIterator iq (*this, &layout, context);
while (! iq.at_end ()) {
++iq;
}

View File

@ -468,8 +468,10 @@ public:
* as "delete" or "with ... do".
* It is basically equivalent to iterating over the query until it is
* done.
*
* The context provides a way to define variables and functions.
*/
void execute (db::Layout &layout);
void execute (db::Layout &layout, tl::Eval *context = 0);
/**
* @brief A dump method (for debugging)

View File

@ -75,8 +75,8 @@ struct LayoutQueryIteratorWrapper
typedef void difference_type;
typedef void pointer;
LayoutQueryIteratorWrapper (const db::LayoutQuery &q, const db::Layout *layout)
: mp_iter (new db::LayoutQueryIterator (q, layout))
LayoutQueryIteratorWrapper (const db::LayoutQuery &q, const db::Layout *layout, tl::Eval *eval)
: mp_iter (new db::LayoutQueryIterator (q, layout, eval))
{
// .. nothing yet ..
}
@ -100,9 +100,9 @@ private:
tl::shared_ptr<db::LayoutQueryIterator> mp_iter;
};
static LayoutQueryIteratorWrapper iterate (const db::LayoutQuery *q, const db::Layout *layout)
static LayoutQueryIteratorWrapper iterate (const db::LayoutQuery *q, const db::Layout *layout, tl::Eval *eval)
{
return LayoutQueryIteratorWrapper (*q, layout);
return LayoutQueryIteratorWrapper (*q, layout, eval);
}
static tl::Variant iter_get (db::LayoutQueryIterator *iter, const std::string &name)
@ -187,18 +187,24 @@ Class<db::LayoutQuery> decl_LayoutQuery ("db", "LayoutQuery",
"This method allows detection of the properties available. Within the query, all of these "
"properties can be obtained from the query iterator using \\LayoutQueryIterator#get.\n"
) +
gsi::method ("execute", &db::LayoutQuery::execute, gsi::arg("layout"),
gsi::method ("execute", &db::LayoutQuery::execute, gsi::arg("layout"), gsi::arg ("context", (tl::Eval *) 0, "nil"),
"@brief Executes the query\n"
"\n"
"This method can be used to execute \"active\" queries such\n"
"as \"delete\" or \"with ... do\".\n"
"It is basically equivalent to iterating over the query until it is\n"
"done.\n"
"\n"
"The context argument allows supplying an expression execution context. This context can be used for "
"example to supply variables for the execution. It has been added in version 0.26.\n"
) +
gsi::iterator_ext ("each", &iterate, gsi::arg ("layout"),
gsi::iterator_ext ("each", &iterate, gsi::arg ("layout"), gsi::arg ("context", (tl::Eval *) 0, "nil"),
"@brief Executes the query and delivered the results iteratively.\n"
"The argument to the block is a \\LayoutQueryIterator object which can be "
"asked for specific results.\n"
"\n"
"The context argument allows supplying an expression execution context. This context can be used for "
"example to supply variables for the execution. It has been added in version 0.26.\n"
),
"@brief A layout query\n"
"Layout queries are the backbone of the \"Search & replace\" feature. Layout queries allow retrieval of "

View File

@ -444,12 +444,13 @@ namespace
* @brief A convenience wrapper for the expression parser
*/
class ExpressionWrapper
: public tl::Object
: public tl::Eval, public gsi::ObjectBase
{
public:
ExpressionWrapper ()
: mp_eval (new tl::Eval ())
: tl::Eval ()
{
// .. nothing yet ..
}
void parse (const std::string &e)
@ -457,20 +458,10 @@ public:
mp_expr.reset (0);
std::auto_ptr<tl::Expression> ex (new tl::Expression ());
mp_eval->parse (*ex, e);
tl::Eval::parse (*ex, e);
mp_expr.reset (ex.release ());
}
void set_var (const std::string &name, const tl::Variant &value)
{
mp_eval->set_var (name, value);
}
static void set_global_var (const std::string &name, const tl::Variant &value)
{
tl::Eval::set_global_var (name, value);
}
tl::Variant eval ()
{
if (mp_expr.get ()) {
@ -482,24 +473,23 @@ public:
private:
std::auto_ptr<tl::Expression> mp_expr;
std::auto_ptr<tl::Eval> mp_eval;
};
tl::Variant eval_expr (const std::string &e)
static tl::Variant eval_expr (const std::string &e)
{
ExpressionWrapper expr;
expr.parse (e);
return expr.eval ();
}
ExpressionWrapper *new_expr1 (const std::string &e)
static ExpressionWrapper *new_expr1 (const std::string &e)
{
std::auto_ptr<ExpressionWrapper> expr (new ExpressionWrapper ());
expr->parse (e);
return expr.release ();
}
ExpressionWrapper *new_expr2 (const std::string &e, const std::map<std::string, tl::Variant> &variables)
static ExpressionWrapper *new_expr2 (const std::string &e, const std::map<std::string, tl::Variant> &variables)
{
std::auto_ptr<ExpressionWrapper> expr (new ExpressionWrapper ());
for (std::map<std::string, tl::Variant>::const_iterator v = variables.begin (); v != variables.end (); ++v) {
@ -523,7 +513,25 @@ namespace tl {
namespace gsi
{
Class<ExpressionWrapper> decl_ExpressionWrapper ("tl", "Expression",
Class<tl::Eval> decl_ExpressionContext ("tl", "ExpressionContext",
gsi::method ("var", &tl::Eval::set_var, gsi::arg ("name"), gsi::arg ("value"),
"@brief Defines a variable with the given name and value\n"
) +
gsi::method ("global_var", &tl::Eval::set_global_var, gsi::arg ("name"), gsi::arg ("value"),
"@brief Defines a global variable with the given name and value\n"
) +
gsi::method ("eval", &tl::Eval::eval, gsi::arg ("expr"),
"@brief Compiles and evaluates the given expression in this context\n"
"This method has been introduced in version 0.26."
),
"@brief Represents the context of an expression evaluation\n"
"\n"
"The context provides a variable namespace for the expression evaluation.\n"
"\n"
"This class has been introduced in version 0.26 when \\Expression was separated into the execution and context part.\n"
);
Class<ExpressionWrapper> decl_ExpressionWrapper (decl_ExpressionContext, "tl", "Expression",
gsi::constructor ("new", &new_expr1, gsi::arg ("expr"),
"@brief Creates an expression evaluator\n"
) +
@ -534,12 +542,6 @@ Class<ExpressionWrapper> decl_ExpressionWrapper ("tl", "Expression",
gsi::method ("text=", &ExpressionWrapper::parse, gsi::arg ("expr"),
"@brief Sets the given text as the expression."
) +
gsi::method ("var", &ExpressionWrapper::set_var, gsi::arg ("name"), gsi::arg ("value"),
"@brief Defines a variable with the given name and value\n"
) +
gsi::method ("global_var", &ExpressionWrapper::set_global_var, gsi::arg ("name"), gsi::arg ("value"),
"@brief Defines a global variable with the given name and value\n"
) +
gsi::method ("eval", &ExpressionWrapper::eval,
"@brief Evaluates the current expression and returns the result\n"
) +
@ -555,10 +557,9 @@ Class<ExpressionWrapper> decl_ExpressionWrapper ("tl", "Expression",
"\n"
"An expression is 'compiled' into an Expression object and can be evaluated multiple times.\n"
"\n"
"This class has been introduced in version 0.25.\n"
"This class has been introduced in version 0.25. In version 0.26 it was separated into execution and context.\n"
);
static tl::GlobPattern *new_glob_pattern (const std::string &s)
{
return new tl::GlobPattern (s);

View File

@ -3127,7 +3127,7 @@ Expression::execute (EvalTarget &v) const
Eval Eval::m_global;
Eval::Eval (const Eval *parent, bool sloppy)
Eval::Eval (Eval *parent, bool sloppy)
: mp_parent (parent), m_sloppy (sloppy), mp_ctx_handler (0)
{
// .. nothing yet ..
@ -3867,14 +3867,14 @@ Eval::eval_atomic (ExpressionParserContext &ex, std::auto_ptr<ExpressionNode> &n
const tl::Variant *value = 0;
tl::Variant *var = 0;
resolve_var_name (t, var);
if (! var) {
if (am == 2) {
if (am == 2) {
resolve_var_name (t, var);
if (! var) {
set_var (t, tl::Variant ());
resolve_var_name (t, var);
} else {
resolve_name (t, function, value);
}
} else {
resolve_name (t, function, value, var);
}
if (function) {
@ -3935,31 +3935,31 @@ Eval::resolve_var_name (const std::string &t, tl::Variant *&value)
}
void
Eval::resolve_name (const std::string &t, const EvalFunction *&function, const tl::Variant *&value) const
Eval::resolve_name (const std::string &t, const EvalFunction *&function, const tl::Variant *&value, tl::Variant *&var)
{
function = 0;
value = 0;
var = 0;
std::map <std::string, EvalFunction *>::const_iterator f;
f = m_local_functions.find (t);
if (f != m_local_functions.end ()) {
function = f->second;
} else if ((function = EvalStaticFunction::function_by_name (t)) == 0) {
std::map<std::string, tl::Variant>::const_iterator v;
std::map<std::string, tl::Variant>::iterator v;
v = m_local_vars.find (t);
if (v != m_local_vars.end ()) {
value = &v->second;
var = &v->second;
} else {
value = EvalStaticConstant::constant_by_name (t);
}
}
if (! function && ! value) {
if (! function && ! value && ! var) {
if (mp_parent) {
mp_parent->resolve_name (t, function, value);
mp_parent->resolve_name (t, function, value, var);
} else if (this != &m_global) {
m_global.resolve_name (t, function, value);
m_global.resolve_name (t, function, value, var);
}
}
}

View File

@ -367,7 +367,7 @@ public:
* @param parent The parent evaluation context
* @param sloppy True to enable sloppy evaluation for pure parsing
*/
Eval (const Eval *parent = 0, bool sloppy = false);
Eval (Eval *parent = 0, bool sloppy = false);
/**
* @brief virtual dtor to enable dynamic_cast on derived classes.
@ -481,6 +481,11 @@ public:
*/
static std::string parse_expr (tl::Extractor &ex, bool top = true);
/**
* @brief A convenience method to evaluate an expression (by string) in this context
*/
tl::Variant eval (const std::string &expr);
/**
* @brief Interpolate the string and return the result
*
@ -508,14 +513,13 @@ public:
private:
friend class Expression;
const Eval *mp_parent;
Eval *mp_parent;
std::map <std::string, tl::Variant> m_local_vars;
std::map <std::string, EvalFunction *> m_local_functions;
bool m_sloppy;
const ContextHandler *mp_ctx_handler;
std::vector<std::string> m_match_substrings;
tl::Variant eval (const std::string &expr);
void eval_top (ExpressionParserContext &context, std::auto_ptr<ExpressionNode> &v);
void eval_assign (ExpressionParserContext &context, std::auto_ptr<ExpressionNode> &v);
void eval_if (ExpressionParserContext &context, std::auto_ptr<ExpressionNode> &v);
@ -528,7 +532,7 @@ private:
void eval_unary (ExpressionParserContext &context, std::auto_ptr<ExpressionNode> &v);
void eval_atomic (ExpressionParserContext &context, std::auto_ptr<ExpressionNode> &v, int am);
void eval_suffix (ExpressionParserContext &context, std::auto_ptr<ExpressionNode> &v);
void resolve_name (const std::string &name, const EvalFunction *&function, const tl::Variant *&value) const;
void resolve_name (const std::string &name, const EvalFunction *&function, const tl::Variant *&value, tl::Variant *&var);
void resolve_var_name (const std::string &name, tl::Variant *&value);
static Eval m_global;

View File

@ -24,6 +24,11 @@ class TLTest(unittest.TestCase):
def test_1(self):
ctx = pya.ExpressionContext()
self.assertEqual(ctx.eval("1+2"), 3)
ctx.var("a", 21)
self.assertEqual(ctx.eval("2*a"), 42)
expr = pya.Expression()
res = expr.eval()
self.assertEqual(str(type(res)).replace("class", "type"), "<type 'NoneType'>")

View File

@ -68,6 +68,37 @@ class DBLayoutQuery_TestClass < TestBase
end
# variables
def test_4
ly = RBA::Layout::new
ly.read(ENV["TESTSRC"] + "/testdata/gds/t11.gds")
ctx = RBA::ExpressionContext::new
ctx.var("suffix", "!")
ctx.var("all", [])
ctx.var("nonmod", "")
q = RBA::LayoutQuery::new("select cell.name + suffix from *")
res = []
q.each(ly, ctx) do |iter|
res << iter.data.inspect
end
assert_equal(res.size, 2)
assert_equal(res[0], "[\"TOPTOP!\"]")
assert_equal(res[1], "[\"TOP!\"]")
q = RBA::LayoutQuery::new("with * do var nonmod = cell.name; all.push(nonmod)")
q.execute(ly, ctx)
assert_equal(ctx.eval("all").join(","), "TOPTOP,TOP")
# not modified, because we used "var nonmod" in the query which
# creates a local variable:
assert_equal(ctx.eval("nonmod"), "")
end
end
load("test_epilogue.rb")

View File

@ -29,6 +29,11 @@ class Tl_TestClass < TestBase
# Expression basics
def test_1_Expression
ctx = RBA::ExpressionContext::new
assert_equal(ctx.eval("1+2"), 3)
ctx.var("a", 21)
assert_equal(ctx.eval("2*a"), 42)
expr = RBA::Expression::new
res = expr.eval
assert_equal(res.class.to_s, "NilClass")