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 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 ()) { while (! iq.at_end ()) {
++iq; ++iq;
} }

View File

@ -468,8 +468,10 @@ public:
* as "delete" or "with ... do". * as "delete" or "with ... do".
* It is basically equivalent to iterating over the query until it is * It is basically equivalent to iterating over the query until it is
* done. * 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) * @brief A dump method (for debugging)

View File

@ -75,8 +75,8 @@ struct LayoutQueryIteratorWrapper
typedef void difference_type; typedef void difference_type;
typedef void pointer; typedef void pointer;
LayoutQueryIteratorWrapper (const db::LayoutQuery &q, const db::Layout *layout) LayoutQueryIteratorWrapper (const db::LayoutQuery &q, const db::Layout *layout, tl::Eval *eval)
: mp_iter (new db::LayoutQueryIterator (q, layout)) : mp_iter (new db::LayoutQueryIterator (q, layout, eval))
{ {
// .. nothing yet .. // .. nothing yet ..
} }
@ -100,9 +100,9 @@ private:
tl::shared_ptr<db::LayoutQueryIterator> mp_iter; 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) 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 " "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" "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" "@brief Executes the query\n"
"\n" "\n"
"This method can be used to execute \"active\" queries such\n" "This method can be used to execute \"active\" queries such\n"
"as \"delete\" or \"with ... do\".\n" "as \"delete\" or \"with ... do\".\n"
"It is basically equivalent to iterating over the query until it is\n" "It is basically equivalent to iterating over the query until it is\n"
"done.\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" "@brief Executes the query and delivered the results iteratively.\n"
"The argument to the block is a \\LayoutQueryIterator object which can be " "The argument to the block is a \\LayoutQueryIterator object which can be "
"asked for specific results.\n" "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" "@brief A layout query\n"
"Layout queries are the backbone of the \"Search & replace\" feature. Layout queries allow retrieval of " "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 * @brief A convenience wrapper for the expression parser
*/ */
class ExpressionWrapper class ExpressionWrapper
: public tl::Object : public tl::Eval, public gsi::ObjectBase
{ {
public: public:
ExpressionWrapper () ExpressionWrapper ()
: mp_eval (new tl::Eval ()) : tl::Eval ()
{ {
// .. nothing yet ..
} }
void parse (const std::string &e) void parse (const std::string &e)
@ -457,20 +458,10 @@ public:
mp_expr.reset (0); mp_expr.reset (0);
std::auto_ptr<tl::Expression> ex (new tl::Expression ()); std::auto_ptr<tl::Expression> ex (new tl::Expression ());
mp_eval->parse (*ex, e); tl::Eval::parse (*ex, e);
mp_expr.reset (ex.release ()); 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 () tl::Variant eval ()
{ {
if (mp_expr.get ()) { if (mp_expr.get ()) {
@ -482,24 +473,23 @@ public:
private: private:
std::auto_ptr<tl::Expression> mp_expr; 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; ExpressionWrapper expr;
expr.parse (e); expr.parse (e);
return expr.eval (); 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 ()); std::auto_ptr<ExpressionWrapper> expr (new ExpressionWrapper ());
expr->parse (e); expr->parse (e);
return expr.release (); 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 ()); std::auto_ptr<ExpressionWrapper> expr (new ExpressionWrapper ());
for (std::map<std::string, tl::Variant>::const_iterator v = variables.begin (); v != variables.end (); ++v) { for (std::map<std::string, tl::Variant>::const_iterator v = variables.begin (); v != variables.end (); ++v) {
@ -523,7 +513,25 @@ namespace tl {
namespace gsi 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"), gsi::constructor ("new", &new_expr1, gsi::arg ("expr"),
"@brief Creates an expression evaluator\n" "@brief Creates an expression evaluator\n"
) + ) +
@ -534,12 +542,6 @@ Class<ExpressionWrapper> decl_ExpressionWrapper ("tl", "Expression",
gsi::method ("text=", &ExpressionWrapper::parse, gsi::arg ("expr"), gsi::method ("text=", &ExpressionWrapper::parse, gsi::arg ("expr"),
"@brief Sets the given text as the expression." "@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, gsi::method ("eval", &ExpressionWrapper::eval,
"@brief Evaluates the current expression and returns the result\n" "@brief Evaluates the current expression and returns the result\n"
) + ) +
@ -555,10 +557,9 @@ Class<ExpressionWrapper> decl_ExpressionWrapper ("tl", "Expression",
"\n" "\n"
"An expression is 'compiled' into an Expression object and can be evaluated multiple times.\n" "An expression is 'compiled' into an Expression object and can be evaluated multiple times.\n"
"\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) static tl::GlobPattern *new_glob_pattern (const std::string &s)
{ {
return new tl::GlobPattern (s); return new tl::GlobPattern (s);

View File

@ -3127,7 +3127,7 @@ Expression::execute (EvalTarget &v) const
Eval Eval::m_global; 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) : mp_parent (parent), m_sloppy (sloppy), mp_ctx_handler (0)
{ {
// .. nothing yet .. // .. nothing yet ..
@ -3867,14 +3867,14 @@ Eval::eval_atomic (ExpressionParserContext &ex, std::auto_ptr<ExpressionNode> &n
const tl::Variant *value = 0; const tl::Variant *value = 0;
tl::Variant *var = 0; tl::Variant *var = 0;
if (am == 2) {
resolve_var_name (t, var); resolve_var_name (t, var);
if (! var) { if (! var) {
if (am == 2) {
set_var (t, tl::Variant ()); set_var (t, tl::Variant ());
resolve_var_name (t, var); resolve_var_name (t, var);
} else {
resolve_name (t, function, value);
} }
} else {
resolve_name (t, function, value, var);
} }
if (function) { if (function) {
@ -3935,31 +3935,31 @@ Eval::resolve_var_name (const std::string &t, tl::Variant *&value)
} }
void 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; function = 0;
value = 0; value = 0;
var = 0;
std::map <std::string, EvalFunction *>::const_iterator f; std::map <std::string, EvalFunction *>::const_iterator f;
f = m_local_functions.find (t); f = m_local_functions.find (t);
if (f != m_local_functions.end ()) { if (f != m_local_functions.end ()) {
function = f->second; function = f->second;
} else if ((function = EvalStaticFunction::function_by_name (t)) == 0) { } else if ((function = EvalStaticFunction::function_by_name (t)) == 0) {
std::map<std::string, tl::Variant>::iterator v;
std::map<std::string, tl::Variant>::const_iterator v;
v = m_local_vars.find (t); v = m_local_vars.find (t);
if (v != m_local_vars.end ()) { if (v != m_local_vars.end ()) {
value = &v->second; var = &v->second;
} else { } else {
value = EvalStaticConstant::constant_by_name (t); value = EvalStaticConstant::constant_by_name (t);
} }
} }
if (! function && ! value) { if (! function && ! value && ! var) {
if (mp_parent) { if (mp_parent) {
mp_parent->resolve_name (t, function, value); mp_parent->resolve_name (t, function, value, var);
} else if (this != &m_global) { } 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 parent The parent evaluation context
* @param sloppy True to enable sloppy evaluation for pure parsing * @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. * @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); 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 * @brief Interpolate the string and return the result
* *
@ -508,14 +513,13 @@ public:
private: private:
friend class Expression; friend class Expression;
const Eval *mp_parent; Eval *mp_parent;
std::map <std::string, tl::Variant> m_local_vars; std::map <std::string, tl::Variant> m_local_vars;
std::map <std::string, EvalFunction *> m_local_functions; std::map <std::string, EvalFunction *> m_local_functions;
bool m_sloppy; bool m_sloppy;
const ContextHandler *mp_ctx_handler; const ContextHandler *mp_ctx_handler;
std::vector<std::string> m_match_substrings; 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_top (ExpressionParserContext &context, std::auto_ptr<ExpressionNode> &v);
void eval_assign (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); 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_unary (ExpressionParserContext &context, std::auto_ptr<ExpressionNode> &v);
void eval_atomic (ExpressionParserContext &context, std::auto_ptr<ExpressionNode> &v, int am); void eval_atomic (ExpressionParserContext &context, std::auto_ptr<ExpressionNode> &v, int am);
void eval_suffix (ExpressionParserContext &context, std::auto_ptr<ExpressionNode> &v); 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); void resolve_var_name (const std::string &name, tl::Variant *&value);
static Eval m_global; static Eval m_global;

View File

@ -24,6 +24,11 @@ class TLTest(unittest.TestCase):
def test_1(self): 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() expr = pya.Expression()
res = expr.eval() res = expr.eval()
self.assertEqual(str(type(res)).replace("class", "type"), "<type 'NoneType'>") self.assertEqual(str(type(res)).replace("class", "type"), "<type 'NoneType'>")

View File

@ -68,6 +68,37 @@ class DBLayoutQuery_TestClass < TestBase
end 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 end
load("test_epilogue.rb") load("test_epilogue.rb")

View File

@ -29,6 +29,11 @@ class Tl_TestClass < TestBase
# Expression basics # Expression basics
def test_1_Expression 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 expr = RBA::Expression::new
res = expr.eval res = expr.eval
assert_equal(res.class.to_s, "NilClass") assert_equal(res.class.to_s, "NilClass")