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
|
require 'strscan'
class HTemplate
def initialize(source, filename=nil)
@source = source
@filename = filename
compile
end
if defined? Rack::Utils
def escape_html(s)
Rack::Utils.escape_html s
end
else
require 'cgi'
def escape_html(s)
CGI.escapeHTML s
end
end
def expand(data, output="", escape=nil, &block)
escape ||= lambda { |v|
escape_html v.to_s
}
@code.call(output, data, block, escape)
output
end
def compile
code = "lambda { |output, data, block, __escape| data.instance_eval {\n"
scanner = StringScanner.new(@source)
until scanner.eos?
if scanner.bol? and scanner.scan(/\s*\$ (.*\n)/)
# raw line of code, $ if bla
code << scanner[1]
elsif scanner.scan(/\$(:?)(\{(.*?)\}|(@?[\w.!?]+))/)
# expression, ${foo} or $foo.bar
expr = scanner[3] || scanner[4]
if scanner[1] == ":" # disable escaping?
code << %Q{output << (#{expr}).to_s;}
else
code << %Q{output << __escape[#{expr}];}
end
elsif scanner.scan(/\$\$/) # plain $
code << %Q{output << '$';}
elsif scanner.scan(/\$#.*?(?:#\$|$\n?)/) # comment $#...#$ or $#...
# nothing
elsif scanner.scan(/([^\n$]+\n?)|([^\n$]*\n)/) # text
if scanner.matched =~ /\\$/ && scanner.bol?
code << %Q{output << #{scanner.matched.chop.chop.dump};}
else
code << %Q{output << #{scanner.matched.dump};}
end
code << "\n" if scanner.bol?
else
raise "can't parse template: #{scanner.rest[0..20].dump}"
end
end
code << "}}"
@code = eval(code, nil, @filename || '(template)', 0)
end
end
|