Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
path: root/mwlib/expr.py
diff options
context:
space:
mode:
Diffstat (limited to 'mwlib/expr.py')
-rwxr-xr-xmwlib/expr.py222
1 files changed, 222 insertions, 0 deletions
diff --git a/mwlib/expr.py b/mwlib/expr.py
new file mode 100755
index 0000000..fa11ce9
--- /dev/null
+++ b/mwlib/expr.py
@@ -0,0 +1,222 @@
+#! /usr/bin/env python
+
+# Copyright (c) 2007-2008 PediaPress GmbH
+# See README.txt for additional licensing information.
+# based on pyparsing example code (SimpleCalc.py)
+
+"""Implementation of mediawiki's #expr template.
+http://meta.wikimedia.org/wiki/ParserFunctions#.23expr:
+"""
+
+from __future__ import division
+
+import re
+import inspect
+import math
+
+class ExprError(Exception):
+ pass
+
+def _myround(a,b):
+ r=round(a, int(b))
+ if int(r)==r:
+ return int(r)
+ return r
+
+
+pattern = """
+(?:\s+)
+|((?:(?:\d+)(?:\.\d+)?
+ |(?:\.\d+)) (?:e(?:\+|-)?\d+)?)
+|(\+|-|\*|/|>=|<=|<>|!=|[a-zA-Z]+|.)
+"""
+
+rxpattern = re.compile(pattern, re.VERBOSE | re.DOTALL | re.IGNORECASE)
+def tokenize(s):
+ res = []
+ for (v1,v2) in rxpattern.findall(s):
+ if not (v1 or v2):
+ continue
+ v2=v2.lower()
+ if v2 in Expr.constants:
+ res.append((v2,""))
+ else:
+ res.append((v1,v2))
+ return res
+
+ return [(v1,v2.lower()) for (v1,v2) in rxpattern.findall(s) if v1 or v2]
+
+class uminus: pass
+class uplus: pass
+
+precedence = {"(":-1, ")":-1}
+functions = {}
+
+def addop(op, prec, fun, numargs=None):
+ precedence[op] = prec
+ if numargs is None:
+ numargs = len(inspect.getargspec(fun)[0])
+
+
+ def wrap(stack):
+ assert len(stack)>=numargs
+ args = tuple(stack[-numargs:])
+ del stack[-numargs:]
+ stack.append(fun(*args))
+
+ functions[op] = wrap
+
+a=addop
+a(uminus, 10, lambda x: -x)
+a(uplus, 10, lambda x: x)
+a("^", 10, math.pow, 2)
+a("not", 9, lambda x:int(not(bool(x))))
+a("abs", 9, abs, 1)
+a("sin", 9, math.sin, 1)
+a("cos", 9, math.cos, 1)
+a("asin", 9, math.asin, 1)
+a("acos", 9, math.acos, 1)
+a("tan", 9, math.tan, 1)
+a("atan", 9, math.atan, 1)
+a("exp", 9, math.exp, 1)
+a("ln", 9, math.log, 1)
+a("ceil", 9, lambda x: int(math.ceil(x)))
+a("floor", 9, lambda x: int(math.floor(x)))
+a("trunc", 9, long, 1)
+
+a("*", 8, lambda x,y: x*y)
+a("/", 8, lambda x,y: x/y)
+a("div", 8, lambda x,y: x/y)
+a("mod", 8, lambda x,y: int(x)%int(y))
+
+
+a("+", 6, lambda x,y: x+y)
+a("-", 6, lambda x,y: x-y)
+
+a("round", 5, _myround)
+
+a("<", 4, lambda x,y: int(x<y))
+a(">", 4, lambda x,y: int(x>y))
+a("<=", 4, lambda x,y: int(x<=y))
+a(">=", 4, lambda x,y: int(x>=y))
+a("!=", 4, lambda x,y: int(x!=y))
+a("<>", 4, lambda x,y: int(x!=y))
+a("=", 4, lambda x,y: int(x==y))
+
+a("and", 3, lambda x,y: int(bool(x) and bool(y)))
+a("or", 2, lambda x,y: int(bool(x) or bool(y)))
+del a
+
+class Expr(object):
+ constants = dict(
+ e=math.e,
+ pi=math.pi)
+
+ def as_float_or_int(self, s):
+ try:
+ return self.constants[s]
+ except KeyError:
+ pass
+
+ if "." in s or "e" in s.lower():
+ return float(s)
+ return long(s)
+
+ def output_operator(self, op):
+ return functions[op](self.operand_stack)
+
+ def output_operand(self, operand):
+ self.operand_stack.append(operand)
+
+ def parse_expr(self, s):
+ tokens = tokenize(s)
+ if not tokens:
+ return ""
+
+ self.operand_stack = []
+ operator_stack = []
+
+ seen_operand=False
+
+ last_operand, last_operator = False, True
+
+ for operand, operator in tokens:
+ if operand:
+ if last_operand:
+ raise ExprError("expected operator")
+ self.output_operand(self.as_float_or_int(operand))
+ elif operator=="(":
+ operator_stack.append("(")
+ elif operator==")":
+ while 1:
+ if not operator_stack:
+ raise ExprError("unbalanced parenthesis")
+ t = operator_stack.pop()
+ if t=="(":
+ break
+ self.output_operator(t)
+ elif operator in precedence:
+ if last_operator and last_operator!=")":
+ if operator=='-':
+ operator = uminus
+ elif operator=='+':
+ operator = uplus
+
+ is_unary = operator in (uplus, uminus)
+ prec = precedence[operator]
+ while not is_unary and operator_stack and prec<=precedence[operator_stack[-1]]:
+ p = operator_stack.pop()
+ self.output_operator(p)
+ operator_stack.append(operator)
+ else:
+ raise ExprError("unknown operator: %r" % (operator,))
+
+ last_operand, last_operator = operand, operator
+
+
+ while operator_stack:
+ p=operator_stack.pop()
+ if p=="(":
+ raise ExprError("unbalanced parenthesis")
+ self.output_operator(p)
+
+ if len(self.operand_stack)!=1:
+ raise ExprError("bad stack: %s" % (self.operand_stack,))
+
+ return self.operand_stack[-1]
+
+def expr(s):
+ return Expr().parse_expr(s)
+
+def main():
+ ParseException = ExprError
+ import time
+ try:
+ import readline # do not remove. makes raw_input use readline
+ readline
+ except ImportError:
+ pass
+
+ ep = expr
+
+ while 1:
+ input_string = raw_input("> ")
+ if not input_string:
+ continue
+
+ stime = time.time()
+ try:
+ res=expr(input_string)
+ except Exception, err:
+ print "ERROR:", err
+ import traceback
+ traceback.print_exc()
+
+ continue
+ print res
+ print time.time()-stime, "s"
+
+if __name__=='__main__':
+ main()
+
+