Login

RPN template math

Author:
durka
Posted:
October 24, 2008
Language:
Python
Version:
1.0
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. Template tag - list punctuation for a list of items by shapiromatron 11 months, 2 weeks ago
  2. JSONRequestMiddleware adds a .json() method to your HttpRequests by cdcarter 11 months, 3 weeks ago
  3. Serializer factory with Django Rest Framework by julio 1 year, 6 months ago
  4. Image compression before saving the new model / work with JPG, PNG by Schleidens 1 year, 7 months ago
  5. Help text hyperlinks by sa2812 1 year, 7 months ago

Comments

Please login first before commenting.