[Mpuls-commits] r369 - in wasko/trunk: . waskaweb/lib waskaweb/model

scm-commit@wald.intevation.org scm-commit at wald.intevation.org
Thu Mar 12 14:36:02 CET 2009


Author: teichmann
Date: 2009-03-12 14:35:59 +0100 (Thu, 12 Mar 2009)
New Revision: 369

Added:
   wasko/trunk/waskaweb/model/exprtree.py
Modified:
   wasko/trunk/ChangeLog.txt
   wasko/trunk/waskaweb/lib/renderer.py
   wasko/trunk/waskaweb/model/__init__.py
   wasko/trunk/waskaweb/model/data.py
   wasko/trunk/waskaweb/model/semantic.py
Log:
New tree based expression engine


Modified: wasko/trunk/ChangeLog.txt
===================================================================
--- wasko/trunk/ChangeLog.txt	2009-03-12 13:31:11 UTC (rev 368)
+++ wasko/trunk/ChangeLog.txt	2009-03-12 13:35:59 UTC (rev 369)
@@ -1,3 +1,17 @@
+2009-03-12	Sascha L. Teichmann	<teichmann at intevation.de>
+
+	* waskaweb/model/exprtree.py: New tree based implementation
+	  of the expression engine. The old stack based one is deprecated.
+	  Added a 'known' function to figure out if values are different
+	  from None and 'unknown'
+
+	* waskaweb/model/__init__.py: Moved UNKNOWN_INT and UNKNOWN_DATE
+	  to module to prevent circular imports.
+
+	* waskaweb/model/data.py: Use new tree based expression engine.
+
+	* waskaweb/model/semantic.py, waskaweb/lib/renderer.py: Fixed import.
+
 2009-03-12	Torsten Irlaender  <torsten.irlaender at intevation.de> 
 
 	Fixed issue25

Modified: wasko/trunk/waskaweb/lib/renderer.py
===================================================================
--- wasko/trunk/waskaweb/lib/renderer.py	2009-03-12 13:31:11 UTC (rev 368)
+++ wasko/trunk/waskaweb/lib/renderer.py	2009-03-12 13:35:59 UTC (rev 369)
@@ -32,7 +32,7 @@
 
 import waskaweb.model.data as data
 
-from waskaweb.model.semantic import UNKNOWN_DATE, UNKNOWN_INT
+from waskaweb.model import UNKNOWN_DATE, UNKNOWN_INT
 
 from filters import NA
 

Modified: wasko/trunk/waskaweb/model/__init__.py
===================================================================
--- wasko/trunk/waskaweb/model/__init__.py	2009-03-12 13:31:11 UTC (rev 368)
+++ wasko/trunk/waskaweb/model/__init__.py	2009-03-12 13:35:59 UTC (rev 369)
@@ -21,3 +21,14 @@
 # within the programme Kompetenzagenturen (Durchfuehrungsphase) funded by
 # the Bundesministerium fuer Familie, Senioren, Frauen und Jugend and 
 # European Social Fund resources.
+
+from datetime import date
+
+UNKNOWN_INT  = -9999999
+UNKNOWN_DATE = date(1, 1, 1)
+
+
+
+
+
+

Modified: wasko/trunk/waskaweb/model/data.py
===================================================================
--- wasko/trunk/waskaweb/model/data.py	2009-03-12 13:31:11 UTC (rev 368)
+++ wasko/trunk/waskaweb/model/data.py	2009-03-12 13:35:59 UTC (rev 369)
@@ -35,7 +35,7 @@
     VISIT_IGNORE_CHILDREN, \
     VISIT_CONTINUE
 
-from expr import Expr
+from exprtree import Expr
 
 import sys
 import os

Added: wasko/trunk/waskaweb/model/exprtree.py
===================================================================
--- wasko/trunk/waskaweb/model/exprtree.py	2009-03-12 13:31:11 UTC (rev 368)
+++ wasko/trunk/waskaweb/model/exprtree.py	2009-03-12 13:35:59 UTC (rev 369)
@@ -0,0 +1,320 @@
+#!/usr/bin/env python
+# -*- coding: latin1 -*-
+#
+# Copyright 2007, 2008, 2009 Intevation GmbH, Germany, <info at intevation.de>
+# 
+# This file is part of mpuls WASKA (CoMPUter-based case fiLeS - 
+# Web-Anwendungs-Server fuer Kompetenzagenturen).
+# 
+# mpuls WASKA is free software: you can redistribute it and/or modify it under
+# the terms of the GNU Affero General Public License as published by the
+# Free Software Foundation, either version 3 of the License, or (at your
+# option) any later version.
+# 
+# mpuls WASKA 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 Affero General Public
+# License for more details.
+# 
+# You should have received a copy of the GNU Affero General Public
+# License along with mpuls WASKA. If not, see <http://www.gnu.org/licenses/>.
+# 
+# mpuls WASKA has been developed on behalf of the 
+# Projekttraeger im Deutschen Zentrum fuer Luft- und Raumfahrt e.V. (PT-DLR)
+# within the programme Kompetenzagenturen (Durchfuehrungsphase) funded by
+# the Bundesministerium fuer Familie, Senioren, Frauen und Jugend and 
+# European Social Fund resources.
+#
+# Authors:
+# Sascha L. Teichmann <teichmann at intevation.de>
+#
+
+import sys
+import re
+import codecs
+import traceback
+
+from datetime import date
+from types    import StringTypes
+
+from waskaweb.model import UNKNOWN_DATE, UNKNOWN_INT
+
+FLT_RE = re.compile(r'^([-+]?[0-9]*\.?[0-9]+([eE][-+]?[0-9]+)?)$')
+INT_RE = re.compile(r'^([-+]?[0-9]+)$')
+VAR_RE = re.compile(r'^\$(\w+)$')
+
+from shlex import split as lexsplit
+
+class Node(object):
+
+    def __init__(self):
+        pass
+
+    def eval(self, ctx):
+        return None
+
+class Unary(Node):
+
+    def __init__(self, a = None):
+        Node.__init__(self)
+        self.a = a
+
+    def __repr__(self):
+        return "%s(%s)" % (
+            self.__class__.__name__,
+            repr(self.a))
+
+class Binary(Unary):
+
+    def __init__(self, a = None, b = None):
+        Unary.__init__(self, a)
+        self.b = b
+
+    def __repr__(self):
+        return "%s(%s, %s)" % (
+            self.__class__.__name__,
+            repr(self.a),
+            repr(self.b))
+
+class OperatorUnary(Unary):
+
+    def __init__(self, a, operator):
+        Unary.__init__(self, a)
+        self.operator = operator
+
+    def eval(self, ctx):
+        return self.operator(self.a.eval(ctx))
+
+    def __repr__(self):
+        return "%s[%s](%s)" % (
+            self.__class__.__name__,
+            self.operator.__name__,
+            repr(self.a))
+
+class OperatorBinary(Binary):
+
+    def __init__(self, a, b, operator):
+        Binary.__init__(self, a, b)
+        self.operator = operator
+
+    def eval(self, ctx):
+        return self.operator(self.a.eval(ctx), self.b.eval(ctx))
+
+    def __repr__(self):
+        return "%s[%s](%s, %s)" % (
+            self.__class__.__name__,
+            self.operator.__name__,
+            repr(self.a),
+            repr(self.b))
+
+class Not(Unary):
+
+    def eval(self, ctx):
+        return not self.a.eval(ctx)
+
+class And(Binary):
+
+    def eval(self, ctx):
+        ea = self.a.eval(ctx)
+        if not ea: return False
+        return bool(self.b.eval(ctx))
+
+class Or(Binary):
+
+    def eval(self, ctx):
+        return self.a.eval(ctx) and True or bool(self.b.eval(ctx))
+
+class Var(Node):
+
+    def __init__(self, var_name):
+        self.var_name = var_name
+
+    def eval(self, ctx):
+        return ctx.vars[self.var_name]
+
+    def __repr__(self):
+        return "var(%s)" % (self.var_name)
+
+class Const(Node):
+
+    def __init__(self, const):
+        self.const = const
+
+    def eval(self, ctx):
+        return self.const
+
+    def __repr__(self):
+        return "const(%s)" % repr(self.const)
+
+class Today(Node):
+
+    def eval(self):
+        return date.today()
+
+class DateNode(Node):
+
+    def __init__(self, year, month, day):
+        self.day   = day
+        self.month = month
+        self.year  = year
+
+    def eval(self, ctx):
+        day   = self.day.eval(ctx)
+        month = self.mounth.eval(ctx)
+        year  = self.year.eval(ctx)
+        return date(year, month, day)
+
+class Out(Node):
+
+    def __init__(self, parent):
+        self.parent = parent
+
+    def eval(self, ctx):
+        e = self.parent(ctx)
+        print repr(e)
+        return e
+
+class IThenElse(Node):
+
+    def __init__(self, q, a, b):
+        self.q = q
+        self.a = a
+        self.b = b
+
+    def eval(self, ctx):
+        if self.q.eval(ctx): return self.a.eval(ctx)
+        return self.b.eval(ctx)
+
+def ADD(a, b):   return a + b
+def MINUS(a, b): return a - b
+def MUL(a, b):   return a * b
+def DIV(a, b):   return a / b
+def MOD(a, b):   return a % b
+def POW(a, b):   return a ** b
+def EQ(a, b):    return a == b
+def NE(a, b):    return a != b
+def GT(a, b):    return a > b
+def LT(a, b):    return a < b
+def LE(a, b):    return a <= b
+def GE(a, b):    return a >= b
+def MIN(a, b):   return min(a, b)
+def MAX(a, b):   return max(a, b)
+def INT(a):      return int(a)
+def FLOAT(a):    return float(a)
+def STR(a):      return str(a)
+def LEN(a):      return len(a)
+def ABS(a):      return abs(a)
+def NOT(a):      return not a
+def ISSET(a):    return not a is None
+def TYPE(a):     return type(a)
+
+def KNOWN(a):
+    return not (
+           a is None \
+        or a == UNKNOWN_INT \
+        or a == UNKNOWN_DATE \
+        or (type(a) in StringTypes and not a)) 
+
+NODE_FACTORIES = {
+    '+':     lambda s: OperatorBinary(s.pop(),   s.pop(), ADD),
+    '-':     lambda s: OperatorBinary(s.pop(-2), s.pop(), MINUS),
+    '*':     lambda s: OperatorBinary(s.pop(),   s.pop(), MUL),
+    '/':     lambda s: OperatorBinary(s.pop(-2), s.pop(), DIV),
+    '%':     lambda s: OperatorBinary(s.pop(-2), s.pop(), MOD),
+    '**':    lambda s: OperatorBinary(s.pop(-2), s.pop(), POW),
+    '==':    lambda s: OperatorBinary(s.pop(),   s.pop(), EQ),
+    '!=':    lambda s: OperatorBinary(s.pop(),   s.pop(), NE),
+    '>':     lambda s: OperatorBinary(s.pop(-2), s.pop(), GT),
+    '<':     lambda s: OperatorBinary(s.pop(-2), s.pop(), LT),
+    '<=':    lambda s: OperatorBinary(s.pop(-2), s.pop(), LE),
+    '>=':    lambda s: OperatorBinary(s.pop(-2), s.pop(), GE),
+    'min':   lambda s: OperatorBinary(s.pop(), s.pop(),   MIN),
+    'max':   lambda s: OperatorBinary(s.pop(), s.pop(),   MAX),
+    'int':   lambda s: OperatorUnary(s.pop(),             INT),
+    'float': lambda s: OperatorUnary(s.pop(),             FLOAT),
+    'str':   lambda s: OperatorUnary(s.pop(),             STR),
+    'len':   lambda s: OperatorUnary(s.pop(),             LEN),
+    'abs':   lambda s: OperatorUnary(s.pop(),             ABS),
+    'not':   lambda s: OperatorUnary(s.pop(),             NOT),
+    'isset': lambda s: OperatorUnary(s.pop(),             ISSET),
+    'known': lambda s: OperatorUnary(s.pop(),             KNOWN),
+    'type':  lambda s: OperatorUnary(s.pop(),             TYPE),
+    'and':   lambda s: And(s.pop(-2), s.pop()),
+    'or':    lambda s: Or(s.pop(-2), s.pop()),
+    'today': lambda s: Today(),
+    'date':  lambda s: DateNode(s.pop(), s.pop(), s.pop()),
+    '.':     lambda s: Out(s.pop()),
+    '?':     lambda s: IThenElse(s.pop(), s.pop(), s.pop())
+}
+
+class EvalContext(object):
+
+    def __init__(self, vars):
+        self.vars = vars
+
+class Expr(object):
+
+    def __init__(self, expr):
+        self.expr    = expr
+        self.prog    = None
+        self.depends = None
+
+    def evaluate(self, vars=None):
+        if self.prog is None:
+            self.compile()
+
+        ctx = EvalContext(vars)
+        try:
+            return self.prog.eval(ctx)
+        except:
+            traceback.print_exc(file = sys.stderr)
+            print >> sys.stderr, "Expr: ", repr(self.prog)
+            raise
+
+    def compile(self):
+        depends = set()
+
+        stack = []
+
+        # XXX: shlex does not support unicode -> ascii
+        encoder = codecs.getencoder("ascii")
+        for token in lexsplit(encoder(self.expr)[0]):
+            token = unicode(token)
+            m = VAR_RE.match(token)
+            if m:
+                var = m.group(1)
+                stack.append(Var(var))
+                depends.add(var)
+                continue
+            m = INT_RE.match(token)
+            if m:
+                stack.append(Const(int(m.group(1))))
+                continue
+            m = FLT_RE.match(token)
+            if m:
+                stack.append(Const(float(m.group(1))))
+                continue
+            try:
+                stack.append(NODE_FACTORIES[token](stack))
+            except KeyError:
+                stack.append(Const(token))
+
+        self.prog    = stack and stack[0] or None
+        self.depends = depends
+
+    def getDependencies(self):
+        if self.depends is None:
+            self.compile()
+        return self.depends
+
+#def main():
+#    for arg in sys.argv[1:]:
+#        expr = Expr(arg)
+#        vars = { 'a': 3 }
+#        result = expr.evaluate(vars)
+#        print str(result)
+#
+#if __name__ == "__main__":
+#    main()
+        
+# vim:set ts=4 sw=4 si et sta sts=4:

Modified: wasko/trunk/waskaweb/model/semantic.py
===================================================================
--- wasko/trunk/waskaweb/model/semantic.py	2009-03-12 13:31:11 UTC (rev 368)
+++ wasko/trunk/waskaweb/model/semantic.py	2009-03-12 13:35:59 UTC (rev 369)
@@ -38,14 +38,13 @@
 
 #from waskaweb.lib.helpers import safe_unicode
 
+
+from waskaweb.model import UNKNOWN_INT, UNKNOWN_DATE
+
 def safe_unicode(s):
     return s
 
 
-UNKNOWN_INT  = -9999999
-UNKNOWN_DATE = date(1, 1, 1)
-
-
 class ErrorItem:
 
     def __init__(self, page=None, bad=None, name=None):



More information about the Mpuls-commits mailing list