-
Notifications
You must be signed in to change notification settings - Fork 2
/
parser.st
186 lines (154 loc) · 5.15 KB
/
parser.st
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
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
179
180
181
182
183
184
185
186
Namespace current: Shampoo [
Object subclass: MessageParserState [
<category: 'Shampoo-Protocol'>
<comment: 'My subclasses represent various states of the message parser
FSM. I contain the most common methods for all states and define the state
interface (see the "fsm" category).'>
| parser buffer |
MessageParserState class >> new: aString [
<category: 'instance creation'>
^self basicNew
initialize: aString;
yourself
]
initialize: aString [
<category: 'initialization'>
buffer := aString
]
invalidateBuffer [
<category: 'private'>
buffer := String new.
]
parser: aParser [
<category: 'accessors'>
parser := aParser.
]
consume: aString [
<category: 'accessors'>
buffer := buffer, aString
]
canProcess [
<category: 'fsm'>
self subclassResponsibility
]
processInto: aCollection [
<category: 'fsm'>
[self tryParse: buffer readStream into: aCollection
] on: Error do: [:e | e inspect. self handleFail]
]
switchToContent: aLength rest: aString [
<category: 'fsm'>
| st |
st := ParseContentState buffer: aString length: aLength.
parser switchTo: st
]
switchToHeader: aString [
<category: 'fsm'>
parser switchTo: (ParseHeaderState buffer: aString)
]
tryParse: aStream into: aCollection [
<category: 'private'>
self subclassResponsibility
]
fail [
<category: 'private'>
self error: 'Error occured during parsing'
]
handleFail [
<category: 'private'>
self subclassResponsibility
]
]
MessageParserState subclass: ParseHeaderState [
<category: 'Shampoo-Protocol'>
<comment: 'My instances parse the header part of the message. The
main purpose is to extract the content length ("Content-Length" header)
and then to switch the FSM to the ParseContentState state and to pass the
rest of the message and the expected content length to it.'>
ParseHeaderState class >> buffer: aString [
<category: 'instance creation'>
^(self new: aString)
yourself
]
canProcess [
<category: 'fsm'>
^buffer lines size >= 2
]
tryParse: aStream into: aCollection [
<category: 'private'>
| sz |
aStream upToAll: 'Content-Length: '.
aStream atEnd ifTrue: [self fail].
sz := Integer readFrom: aStream.
sz = 0 ifTrue: [self fail].
2 timesRepeat:
[(aStream next: 2) = String crlf ifFalse: [self fail]].
self switchToContent: sz rest: aStream upToEnd
]
handleFail [
<category: 'private'>
self invalidateBuffer
]
]
MessageParserState subclass: ParseContentState [
<category: 'Shampoo-Protocol'>
<comment: 'My instances parse the content part of the message. When
there are enough bytes in the message (i.e. more than or equal to expected),
my instance will put the content into a collection and then will switch the
FSM to the ParseHeaderState state and will pass the rest of the buffer to it.'>
| expected |
ParseContentState class >> buffer: aString length: anInteger [
<category: 'instance creation'>
^(self new: aString)
expected: anInteger;
yourself
]
expected: anInteger [
<category: 'accessors'>
expected := anInteger
]
canProcess [
<category: 'fsm'>
^buffer size >= expected
]
tryParse: aStream into: aCollection [
<category: 'fsm'>
aCollection add: (aStream next: expected).
self switchToHeader: aStream upToEnd
]
handleFail [
<category: 'private'>
"Do nothing"
]
]
Object subclass: MessageParser [
<category: 'Shampoo-Protocol'>
<comment: 'I am the main class to handle the incoming messages.
My instances take the received raw data and extract the contents from it.'>
| state |
MessageParser class >> new [
<category: 'instance creation'>
^(self basicNew)
initialize;
yourself
]
initialize [
<category: 'initialization'>
self switchTo: (ParseHeaderState buffer: '')
]
process: aString [
<category: 'parsing'>
| results |
results := OrderedCollection new.
state consume: aString.
[state canProcess] whileTrue:
[state processInto: results].
^results
]
switchTo: aState [
<category: 'fsm'>
state := aState.
state parser: self.
]
]
]