Skip to content

Commit db9f1b3

Browse files
committed
Merge refactoring work of the TemplateParser.
This patch also includes a few fixes along the way.
1 parent 2dd7e3b commit db9f1b3

File tree

3 files changed

+103
-69
lines changed

3 files changed

+103
-69
lines changed

mustang/Escape.ooc

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ String: cover from Char* {
1212
escaped := Buffer new(this length() + 128)
1313

1414
for(c: Char in this) {
15-
e := match c {
15+
e: String = match c {
1616
case '<' => "&lt;"
1717
case '>' => "&gt;"
1818
case '&' => "&amp;"
@@ -21,7 +21,7 @@ String: cover from Char* {
2121
case =>
2222
escaped append(c)
2323
continue
24-
null
24+
"null"
2525
}
2626
escaped append(e)
2727
}

mustang/TemplateParser.ooc

Lines changed: 96 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ import mustang/[Node, TagParser]
22

33
import io/[File, FileReader]
44
import structs/[Stack, List, LinkedList]
5+
import text/Regexp
6+
import text/Buffer
57

68

79
/**
@@ -19,14 +21,19 @@ import structs/[Stack, List, LinkedList]
1921
*/
2022
TemplateParser: class {
2123
templateText: String
22-
index: Int
24+
25+
state: Int
26+
index, currentLine, currentColumn: Int
27+
buffer: Buffer
28+
nextChar: Char
29+
skipChar: Bool
2330

2431
startTag, endTag: String
32+
tagRegex: Regexp
2533
parsers: LinkedList<TagParser>
2634

2735
firstNode, currentNode: TNode
2836
nodeStack: Stack<TNode>
29-
running: Bool
3037

3138
getParserFromFile: static func(file: File) -> This {
3239
size := file size()
@@ -37,9 +44,10 @@ TemplateParser: class {
3744
}
3845

3946
init: func(=templateText, =startTag, =endTag) {
40-
index = 0
47+
buffer = Buffer new(1024)
4148
parsers = LinkedList<TagParser> new()
4249
nodeStack = Stack<TNode> new()
50+
tagRegex = Regexp compile("^%s.+%s$" format(startTag, endTag), RegexpOption DOTALL)
4351

4452
// load builtin parsers
4553
loadBuiltinParsers(parsers)
@@ -68,83 +76,109 @@ TemplateParser: class {
6876
}
6977

7078
parse: func -> TNode {
71-
running = true
79+
state = ParserState PARSE_TEXT
80+
index = 0; currentLine = 1; currentColumn = 0
81+
buffer size = 0
82+
currentNode = null
83+
skipChar = false
7284

73-
while(true) {
74-
if(!parseText()) break
75-
if(!parseTag()) break
76-
}
85+
// Parser state machine
86+
while(state != ParserState STOP) {
87+
nextChar = templateText charAt(index)
7788

78-
return firstNode
79-
}
89+
if(state == ParserState PARSE_TEXT) state = parseText()
90+
else if(state == ParserState PARSE_TAG) state = parseTag()
91+
else Exception new("Invalid parser state: %d" format(state)) throw()
8092

81-
parseText: func -> Bool {
82-
if(index >= templateText length()) {
83-
// End of template, stop parsing
84-
return false
85-
}
86-
87-
// Find the start of the next tag
88-
indexOfNextTag := templateText indexOf(startTag, index)
93+
if(nextChar == '\0') {
94+
// End of template, stop parsing!
95+
break
96+
}
97+
else if(nextChar == '\n') {
98+
// Increment line count and reset column
99+
currentLine += 1
100+
currentColumn = 0
101+
}
102+
else {
103+
currentColumn += 1
104+
}
89105

90-
if(indexOfNextTag == -1) {
91-
// No more tags to parse, rest of context is plaintext.
92-
text := templateText substring(index)
93-
appendNode(TextNode new(text))
94-
return false
95-
}
96-
else if(indexOfNextTag != index) {
97-
text := templateText substring(index, indexOfNextTag)
98-
appendNode(TextNode new(text))
106+
if(!skipChar) buffer append(nextChar)
107+
else skipChar = false
108+
index += 1
99109
}
100110

101-
index = indexOfNextTag
102-
103-
return true
111+
return firstNode
104112
}
105113

106-
parseTag: func -> Bool {
107-
// Read the tag from the template
108-
//TODO: probably best we do this in a regex.
109-
// as of now triple mustaches don't parse right.
110-
index += startTag length() // skip open tag
111-
indexOfEndTag := templateText indexOf(endTag, index) // get index of end tag
112-
tag := templateText substring(index, indexOfEndTag) trim() // substring out the tag content
113-
index = indexOfEndTag + endTag length() // advance past end tag
114-
115-
// End of tag block
116-
if(tag first() == '/') {
117-
endBlock()
118-
if(templateText charAt(index) == '\n') {
119-
// Skip any newline after the ending block tag
120-
index += 1
114+
parseText: func -> Int {
115+
// If at the start of a tag or end of template,
116+
// create a new TextNode to store the buffered text.
117+
if(nextChar == '\0' || templateText compare(startTag, index)) {
118+
if(buffer size) {
119+
appendNode(TextNode new(buffer toString() clone()))
120+
buffer size = 0
121121
}
122-
return true
122+
return ParserState PARSE_TAG
123123
}
124124

125-
// Iterate through parsers and try to find a match
126-
for(p: TagParser in parsers) {
127-
if(p matches(tag)) {
128-
node := p parse(tag)
129-
if(node) {
130-
appendNode(node)
125+
return ParserState PARSE_TEXT
126+
}
127+
128+
parseTag: func -> Int {
129+
if(tagRegex matches(buffer toString())) {
130+
tag := buffer toString()
131+
132+
// Strip off start and end mustaches plus whitespace
133+
tag = tag substring(startTag length(), tag length() - endTag length()) trim()
134+
buffer size = 0
135+
136+
if(tag first() == '/') {
137+
// Block end tag
138+
endBlock()
139+
if(nextChar == '\n') {
140+
// Skip any newline after the block tag.
141+
skipChar = true
131142
}
132143

133-
if(p isBlock()) {
134-
startBlock()
135-
if(templateText charAt(index) == '\n') {
136-
// Skip any newline that comes after a block start tag
137-
index += 1
144+
return ParserState PARSE_TEXT
145+
}
146+
else {
147+
// Find a parser for the tag and generate node
148+
for(p: TagParser in parsers) {
149+
if(p matches(tag)) {
150+
node := p parse(tag)
151+
if(node) {
152+
appendNode(node)
153+
}
154+
155+
if(p isBlock()) {
156+
startBlock()
157+
if(nextChar == '\n') {
158+
// Skip any newlines after a block tag.
159+
skipChar = true
160+
}
161+
}
162+
163+
return ParserState PARSE_TEXT
138164
}
139165
}
140166

141-
return true
167+
Exception new("Invald tag: %s" format(tag)) throw()
142168
}
143169
}
144170

145-
// If control reaches here, no parser could be found for this tag.
146-
// Alert the users of the error.
147-
"ERROR: invalid tag --> {{%s}}" format(tag) println()
148-
return false
171+
else if(nextChar == '\0') {
172+
Exception new("Opps, looks like you have an incomplete tag at the end of the file!") throw()
173+
}
174+
175+
return ParserState PARSE_TAG
149176
}
150177
}
178+
179+
ParserState: class {
180+
STOP: static Int = 1
181+
PARSE_TEXT: static Int = 2
182+
START_PARSE_TAG: static Int = 3
183+
PARSE_TAG: static Int = 4
184+
}

mustang/Value.ooc

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -53,18 +53,18 @@ ListValue: class extends Value {
5353
list add(ListValue new(list))
5454
}
5555

56-
appendHashMap: func(hash: HashMap<Value>) {
56+
appendHashMap: func(hash: HashMap<String, Value>) {
5757
list add(HashValue new(hash))
5858
}
5959

6060
list: func -> List<Value> { list }
6161
}
6262

6363
HashValue: class extends Value {
64-
hash: HashMap<Value>
64+
hash: HashMap<String, Value>
6565

6666
init: func ~fromHashMap(=hash) {}
67-
init: func ~empty { this(HashMap<Value> new()) }
67+
init: func ~empty { this(HashMap<String, Value> new()) }
6868

6969
emit: func -> String { "Hash" }
7070

@@ -84,13 +84,13 @@ HashValue: class extends Value {
8484
setValue(name, ListValue new(list))
8585
}
8686

87-
setHashMap: func(name: String, hash: HashMap<Value>) {
87+
setHashMap: func(name: String, hash: HashMap<String, Value>) {
8888
setValue(name, HashValue new(hash))
8989
}
9090

9191
getValue: func(name: String) -> Value {
9292
hash get(name)
9393
}
9494

95-
hash: func -> HashMap<Value> { hash }
95+
hash: func -> HashMap<String, Value> { hash }
9696
}

0 commit comments

Comments
 (0)