Login

RPN template math

Author:
durka
Posted:
October 24, 2008
Language:
Python
Version:
1.0
Tags:
template filter math arithmetic rpn reverse-polish-notation
Score:
1 (after 1 ratings)

Django's templates don't provide much in the way of arithmetic: there is an "add" filter and that is about it. Even if sub, mult and div filters are implemented, it is difficult to chain filters while preserving some complicated expression, such as ((x+3)4-(2-y)/12.75). However, this expression can be converted into Reverse Polish Notation: x 3 + 4 * 2 y - 12.75 / - which is just a sequence of operations (push-value or apply-operator) and can be chained.

To use these filters, first create a new stack for the expression with name|stnew (pass it some locally unique value). To push a number (or template variable) onto the stack, call name|stpush:number (note that you have to tell stpush the name of the stack to push onto). To pop, call name|stpop. To perform an operation, call name|st[add,sub,mult,div,mod]:number. All numbers are integers if they look like integers, or floats otherwise (integers are turned into floats upon division if they need to be). All of these functions return the name of the stack so that they can be chained. When the calculation is finished (i.e. the answer is at the bottom of the stack) call name|stget to retrieve it.

Example (this was used to calculate an inline CSS value:

left: {{ forloop.counter|stnew|stpush:res.stwkday|stpush:"9.35"|stmult|stpush:res.get_item_left|stpush:"2.75"|stadd|stadd|stget }}em;

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
from django import template
register = template.Library()

class stack:
    def __init__(self):
        self.stack = []
        
    def push(self, o):
        self.stack.append(o)
        #print 'push', self.stack
        
    def pop(self):
        if len(self.stack) == 0:
            raise KeyError, "Stack is empty"
        o = self.stack[-1]
        #print 'pop', self.stack
        del self.stack[-1]
        return o
    
    def is_empty(self):
        return len(self.stack) == 0
    
    def __len__(self):
        return len(self.stack)

# truncate a floating point number only if it has no decimal part (convert from string if necessary)
def number(num):
    f = float(num)
    i = int(f)
    if i == f: #FIXME: floating point equality?
        return i
    return f

stacks = {}

@register.filter
def stnew(value):
    #print 'stnew'
    stacks[value] = stack()
    return value

@register.filter
def stpush(value, arg):
    #print 'stpush:',
    stacks[value].push(number(arg))
    return value

@register.filter
def stpop(value):
    #print 'stpop:',
    if value in stacks:
        stacks[value].pop()
    return value

@register.filter
def stget(value):
    #print 'stget:',
    if value in stacks:
        return stacks[value].pop()

@register.filter
def stadd(value):
    #print 'stadd:',
    two = stacks[value].pop()
    one = stacks[value].pop()
    stacks[value].push(one + two)
    return value

@register.filter
def stsub(value):
    #print 'stsub:',
    two = stacks[value].pop()
    one = stacks[value].pop()
    stacks[value].push(one - two)
    return value

@register.filter
def stmult(value):
    #print 'stmult:',
    two = stacks[value].pop()
    one = stacks[value].pop()
    stacks[value].push(one * two)
    return value

@register.filter
def stdiv(value):
    #print 'stdiv:',
    two = stacks[value].pop()
    one = stacks[value].pop()
    stacks[value].push(number(float(one) / float(two)))
    return value

@register.filter
def stmod(value):
    two = stacks[value].pop()
    one = stacks[value].pop()
    stacks[value].push(one % two)
    return value

More like this

  1. FloatField with safe expression parsing by joelegner 5 years ago
  2. caching parsed templates by forgems 7 years, 3 months ago
  3. Effective content caching for mass-load site using redirect feature by nnseva 3 years, 8 months ago

Comments

Please login first before commenting.