[Formed-commits] r313 - in trunk: . formed/formed/model
scm-commit@wald.intevation.org
scm-commit at wald.intevation.org
Fri Mar 13 10:50:08 CET 2009
Author: teichmann
Date: 2009-03-13 10:50:05 +0100 (Fri, 13 Mar 2009)
New Revision: 313
Added:
trunk/formed/formed/model/exprtree.py
Modified:
trunk/ChangeLog
trunk/formed/formed/model/__init__.py
trunk/formed/formed/model/data.py
Log:
Use new tree based expression engine.
Modified: trunk/ChangeLog
===================================================================
--- trunk/ChangeLog 2009-03-12 16:14:26 UTC (rev 312)
+++ trunk/ChangeLog 2009-03-13 09:50:05 UTC (rev 313)
@@ -1,3 +1,10 @@
+2009-03-13 Torsten Irländer <torsten.irlaender at intevation.de>
+
+ * formed/formed/model/exprtree.py: New tree based expression engine.
+
+ * formed/formed/model/__init__.py, formed/formed/model/data.py:
+ Adjusted imports.
+
2009-03-12 Torsten Irländer <torsten.irlaender at intevation.de>
Introduced new order attribute for date fields
Modified: trunk/formed/formed/model/__init__.py
===================================================================
--- trunk/formed/formed/model/__init__.py 2009-03-12 16:14:26 UTC (rev 312)
+++ trunk/formed/formed/model/__init__.py 2009-03-13 09:50:05 UTC (rev 313)
@@ -1 +1,7 @@
#Do not remove me!
+
+from datetime import date
+
+UNKNOWN_STR = 'unbekannt'
+UNKNOWN_INT = -9999999
+UNKNOWN_DATE = date(1, 1, 1)
Modified: trunk/formed/formed/model/data.py
===================================================================
--- trunk/formed/formed/model/data.py 2009-03-12 16:14:26 UTC (rev 312)
+++ trunk/formed/formed/model/data.py 2009-03-13 09:50:05 UTC (rev 313)
@@ -17,7 +17,7 @@
VISIT_IGNORE_CHILDREN, \
VISIT_CONTINUE
-from expr import Expr
+from exprtree import Expr
import formed.io.document
Added: trunk/formed/formed/model/exprtree.py
===================================================================
--- trunk/formed/formed/model/exprtree.py 2009-03-12 16:14:26 UTC (rev 312)
+++ trunk/formed/formed/model/exprtree.py 2009-03-13 09:50:05 UTC (rev 313)
@@ -0,0 +1,319 @@
+#!/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 formed.model import UNKNOWN_DATE, UNKNOWN_INT, UNKNOWN_STR
+
+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 == UNKNOWN_INT \
+ or a == UNKNOWN_DATE \
+ or (type(a) in StringTypes and a.lower() == UNKNOWN_STR))
+
+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:
More information about the Formed-commits
mailing list