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
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
|
# File 'lib/tache/parser.rb', line 4
def parse(source, tags = nil)
return [] if source == ''
tags ||= ['{{', '}}']
raise ArgumentError, "Invalid tags: '#{tags.join(', ')}'" unless tags.size == 2
state = :text
index = 0
start = 0
finish = 0
line = 0
tokens = [['indent']]
sections = []
left = tags.first
right = tags.last
type = nil
before = nil
indent = ''
standalone = nil
within = nil
while char = source[index]
case state
when :text
case char
when left[0]
if source[index, left.size] == left
if start == line && source[start...index] =~ /\A([\ \t]*)\Z/
indent = $1
standalone = true
else
tokens << ['text', source[start...index]] if index > start
indent = ''
standalone = false
end
before = index
index += left.size - 1
start = index + 1
state = :seek
end
when "\r"
when "\n"
feed = (source[index - 1] == "\r" ? "\r\n" : "\n")
tokens << ['text', source[start..index - feed.size] << feed]
tokens << ['indent'] unless index + 1 == source.size
start = index + 1
line = index + 1
end
when :seek
case char
when '#', '^', '/', '&', '>', '{', '<', '$'
start = index + 1
state = :pre
type = char
when '!', '='
start = index + 1
state = :special
type = char
when ALLOWED
state = :name
type = 'name'
start = index
end
when :pre
case char
when ALLOWED
state = :name
start = index
when ' '
else
error "Invalid character in tag name: #{char.inspect}", source, index
end
when :name, :special, :post
case char
when right[0]
if source[index, right.size] == right
content = source[start...(state == :post ? finish : index)]
if type == '{' && source[index, 1 + right.size] == ('}' + right)
index += 1
type = '&'
end
index += right.size - 1
tail = ''
if standalone
carriage = source[index + 1] == "\r"
index += 1 if carriage
if source[index + 1] == "\n"
index += 1
line = index + 1
tail = carriage ? "\r\n" : "\n"
end
end
within = sections.last && sections.last[0]
case type
when 'name', '&'
if within == '<'
error "Illegal tag inside a partial layout tag", source, before
end
tokens << [type, content, indent, tail]
when '#', '^', '<', '$'
if within == '<' && type != '$'
error "Illegal tag inside a partial layout tag", source, before
end
nested = []
tokens << ['text', indent] if !indent.empty? && tail.empty?
tokens << [type, content, nested]
sections << [type, content, before, index + 1, tokens]
tokens = nested
when '>'
if within == '<'
error "Illegal tag inside a partial layout tag", source, before
end
tokens << ['>', content, indent]
when '!'
when '='
if content[-1] == '='
tags = content[0..-2].strip.split(' ')
error "Invalid tags '#{tags.join(', ')}'", source, before unless tags.size == 2
left, right = *tags
end
when '/'
tokens << ['text', indent] if !indent.empty? && tail.empty? && index + 1 != source.size
opener, name, at, pos, tokens = sections.pop
error "Closing unopened '#{content}'", source, before unless name
error "Unclosed section '#{name}'", source, at if name != content
tokens.last << source[pos...before] + indent << tags if opener == '#'
end
start = index + 1
state = :text
tokens << ['indent'] unless tail.empty? || index + 1 == source.size
end
when ALLOWED
when '}'
if type == '{'
state = :post
type = '&'
finish = index
end
when ' ', "\t"
if state == :name
state = :post
finish = index
end
else
unless state == :special
error "Invalid character in tag name: #{char.inspect}", source, index
end
end
end
index += 1
end
unless sections.empty?
opener, name, at = sections.pop
error "Unclosed section '#{name}'", source, at
end
case state
when :text
tokens << ['text', source[start...index]] if start < index
when :name
error "Unclosed tag", source, before
end
tokens
end
|