-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathdoclink_parser.rb
132 lines (111 loc) · 2.67 KB
/
doclink_parser.rb
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
class DocLinkParser
STATE_OPEN_QUOTE = 0
STATE_QUOTE_CONTENTS = 1
STATE_END_OR_COMMA = 2
STATE_PARAM_KEY = 3
STATE_PARAM_VALUE_START = 4
STATE_PARAM_VALUE_CONTENTS = 5
ERROR_MESSAGES = {
STATE_OPEN_QUOTE => "Expected double-quote indicating a page name",
STATE_QUOTE_CONTENTS => "Unexpected end-of-tag, expected double-quote",
STATE_END_OR_COMMA => nil,
STATE_PARAM_KEY => "Expected parameter key after comma",
STATE_PARAM_VALUE_START => "Expected double-quote starting value after colon",
STATE_PARAM_VALUE_CONTENTS => "Unexpected end-of-tag, expected double-quote"
}
attr_reader :idx, :state
def initialize(data)
@data = data
@idx = 0
@max = data.length
@start = 0
@state = STATE_OPEN_QUOTE
@link = nil
@current_key = nil
@opts = {}
end
def inc
@idx += 1
end
def mark(val = nil)
if val.nil?
@start = @idx
else
@start = val
end
end
def marked
@data[@start...idx]
end
def transition(state)
@state = state
end
def parse
catch(:stop_parsing) { parse_one while idx < @max }
err = ERROR_MESSAGES[state]
raise ArgumentError, err unless err.nil?
return {
link: @link,
opts: @opts,
}
end
def stop
throw :stop_parsing
end
def chomp_spaces
inc while @data[idx] == ' '
stop if idx >= @max
end
def ch
@data[idx]
end
def parse_one
case state
when STATE_OPEN_QUOTE
raise ArgumentError, "Unexpected `#{ch}', expected double-quote" if ch != '"'
transition STATE_QUOTE_CONTENTS
inc
mark
when STATE_QUOTE_CONTENTS
if ch == '"'
transition STATE_END_OR_COMMA
@link = marked
end
inc
when STATE_END_OR_COMMA
chomp_spaces
if ch != ','
raise ArgumentError, "Unexpected `#{ch}', expected ',' or end of tag"
end
transition STATE_PARAM_KEY
inc
chomp_spaces
mark
when STATE_PARAM_KEY
if ch >= 'a' && ch <= 'z'
inc
elsif ch == ':'
@current_key = marked
if @current_key.strip.empty?
raise ArgumentError, "Unexpected `:', expected a-z+"
end
inc
chomp_spaces
transition STATE_PARAM_VALUE_START
else
raise ArgumentError, "Unexpected `#{ch}', expected a-z+, or ':'"
end
when STATE_PARAM_VALUE_START
raise ArgumentError, "Unexpected `#{ch}, expected double-quote" if ch != '"'
inc
mark
transition STATE_PARAM_VALUE_CONTENTS
when STATE_PARAM_VALUE_CONTENTS
if ch == '"'
@opts[@current_key] = marked
transition STATE_END_OR_COMMA
end
inc
end
end
end