template-ruby-parser 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +1 -0
- data/.overcommit.yml +4 -0
- data/Gemfile +5 -0
- data/Gemfile.lock +32 -0
- data/bin/code-parser +27 -0
- data/bin/format +3 -0
- data/bin/template-parser +27 -0
- data/docs/class.code +9 -0
- data/docs/meetup.code +14 -0
- data/docs/rain.code +23 -0
- data/docs/slack.code +17 -0
- data/docs/stripe.code +7 -0
- data/docs/twitter.code +7 -0
- data/lib/code/parser/addition.rb +13 -0
- data/lib/code/parser/and_operator.rb +13 -0
- data/lib/code/parser/bitwise_and.rb +13 -0
- data/lib/code/parser/bitwise_or.rb +13 -0
- data/lib/code/parser/boolean.rb +13 -0
- data/lib/code/parser/call.rb +174 -0
- data/lib/code/parser/chained_call.rb +41 -0
- data/lib/code/parser/class.rb +56 -0
- data/lib/code/parser/code.rb +20 -0
- data/lib/code/parser/comments.rb +46 -0
- data/lib/code/parser/dictionnary.rb +48 -0
- data/lib/code/parser/equal.rb +39 -0
- data/lib/code/parser/equality.rb +20 -0
- data/lib/code/parser/error/syntax_error.rb +36 -0
- data/lib/code/parser/error.rb +6 -0
- data/lib/code/parser/function.rb +109 -0
- data/lib/code/parser/greater_than.rb +13 -0
- data/lib/code/parser/group.rb +15 -0
- data/lib/code/parser/identifier.rb +54 -0
- data/lib/code/parser/if.rb +81 -0
- data/lib/code/parser/if_modifier.rb +39 -0
- data/lib/code/parser/list.rb +20 -0
- data/lib/code/parser/multiplication.rb +13 -0
- data/lib/code/parser/negation.rb +18 -0
- data/lib/code/parser/not_keyword.rb +24 -0
- data/lib/code/parser/nothing.rb +13 -0
- data/lib/code/parser/number.rb +39 -0
- data/lib/code/parser/operation.rb +44 -0
- data/lib/code/parser/or_keyword.rb +13 -0
- data/lib/code/parser/or_operator.rb +13 -0
- data/lib/code/parser/power.rb +33 -0
- data/lib/code/parser/range.rb +13 -0
- data/lib/code/parser/rescue.rb +38 -0
- data/lib/code/parser/shift.rb +13 -0
- data/lib/code/parser/statement.rb +9 -0
- data/lib/code/parser/string.rb +61 -0
- data/lib/code/parser/ternary.rb +73 -0
- data/lib/code/parser/unary_minus.rb +23 -0
- data/lib/code/parser/while.rb +36 -0
- data/lib/code/parser.rb +237 -0
- data/lib/code-ruby-parser.rb +7 -0
- data/lib/code.rb +2 -0
- data/lib/template/parser.rb +32 -0
- data/lib/template-ruby-parser.rb +7 -0
- data/lib/template.rb +2 -0
- data/spec/code/parser/addition_spec.rb +26 -0
- data/spec/code/parser/and_operator_spec.rb +26 -0
- data/spec/code/parser/bitwise_and_spec.rb +26 -0
- data/spec/code/parser/bitwise_or_spec.rb +26 -0
- data/spec/code/parser/boolean_spec.rb +13 -0
- data/spec/code/parser/call_spec.rb +52 -0
- data/spec/code/parser/chained_call_spec.rb +33 -0
- data/spec/code/parser/class_spec.rb +32 -0
- data/spec/code/parser/code_spec.rb +13 -0
- data/spec/code/parser/dictionnary_spec.rb +40 -0
- data/spec/code/parser/equal_spec.rb +42 -0
- data/spec/code/parser/equality_spec.rb +26 -0
- data/spec/code/parser/function_spec.rb +43 -0
- data/spec/code/parser/greater_than_spec.rb +26 -0
- data/spec/code/parser/group_spec.rb +13 -0
- data/spec/code/parser/if_modifier_spec.rb +26 -0
- data/spec/code/parser/if_spec.rb +39 -0
- data/spec/code/parser/list_spec.rb +27 -0
- data/spec/code/parser/multiplication_spec.rb +26 -0
- data/spec/code/parser/negation_spec.rb +13 -0
- data/spec/code/parser/not_keyword_spec.rb +21 -0
- data/spec/code/parser/nothing_spec.rb +20 -0
- data/spec/code/parser/number_spec.rb +24 -0
- data/spec/code/parser/or_keyword_spec.rb +26 -0
- data/spec/code/parser/or_operator_spec.rb +26 -0
- data/spec/code/parser/power_spec.rb +21 -0
- data/spec/code/parser/range_spec.rb +21 -0
- data/spec/code/parser/rescue_spec.rb +26 -0
- data/spec/code/parser/shift_spec.rb +26 -0
- data/spec/code/parser/string_spec.rb +27 -0
- data/spec/code/parser/ternary_spec.rb +26 -0
- data/spec/code/parser/unary_minus_spec.rb +21 -0
- data/spec/code/parser/while_spec.rb +32 -0
- data/spec/spec_helper.rb +6 -0
- data/spec/template/parser_spec.rb +13 -0
- data/template-ruby-parser.gemspec +16 -0
- metadata +171 -0
@@ -0,0 +1,38 @@
|
|
1
|
+
class Code
|
2
|
+
class Parser
|
3
|
+
class Rescue < ::Code::Parser
|
4
|
+
def parse
|
5
|
+
left = parse_subclass(::Code::Parser::Ternary)
|
6
|
+
|
7
|
+
previous_cursor = cursor
|
8
|
+
|
9
|
+
comments_before = parse_comments
|
10
|
+
|
11
|
+
if match(RESCUE_KEYWORD)
|
12
|
+
previous_cursor = cursor
|
13
|
+
comments_after = parse_comments
|
14
|
+
right = parse_subclass(::Code::Parser::Rescue)
|
15
|
+
|
16
|
+
if right
|
17
|
+
{
|
18
|
+
rescue: {
|
19
|
+
left: left,
|
20
|
+
right: right,
|
21
|
+
comments_before: comments_before,
|
22
|
+
comments_after: comments_after
|
23
|
+
}.compact
|
24
|
+
}
|
25
|
+
else
|
26
|
+
@cursor = previous_cursor
|
27
|
+
buffer!
|
28
|
+
{ rescue: { left: left, comments_before: comments_before }.compact }
|
29
|
+
end
|
30
|
+
else
|
31
|
+
@cursor = previous_cursor
|
32
|
+
buffer!
|
33
|
+
left
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
class Code
|
2
|
+
class Parser
|
3
|
+
class String < ::Code::Parser
|
4
|
+
def parse
|
5
|
+
if match(SINGLE_QUOTE)
|
6
|
+
parse_string(SINGLE_QUOTE)
|
7
|
+
elsif match(DOUBLE_QUOTE)
|
8
|
+
parse_string(DOUBLE_QUOTE)
|
9
|
+
elsif match(COLON) && !next?(SPECIAL)
|
10
|
+
buffer!
|
11
|
+
consume while !next?(SPECIAL) && !end_of_input?
|
12
|
+
{ string: buffer }
|
13
|
+
else
|
14
|
+
parse_subclass(::Code::Parser::Number)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def parse_string(quote)
|
21
|
+
buffer!
|
22
|
+
output = []
|
23
|
+
|
24
|
+
while !next?(quote) && !end_of_input?
|
25
|
+
c = consume
|
26
|
+
|
27
|
+
if c == BACKSLASH
|
28
|
+
match(OPENING_CURLY_BRACKET) || match(quote)
|
29
|
+
elsif c == OPENING_CURLY_BRACKET
|
30
|
+
output << { text: escape_string(buffer![..-2]) } if buffer?
|
31
|
+
|
32
|
+
output << { code: parse_code }
|
33
|
+
|
34
|
+
match(CLOSING_CURLY_BRACKET)
|
35
|
+
buffer!
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
output << { text: escape_string(buffer) } if buffer?
|
40
|
+
|
41
|
+
match(quote)
|
42
|
+
|
43
|
+
if output.empty?
|
44
|
+
{ string: EMPTY_STRING }
|
45
|
+
elsif output.one? && output.first.key?(:text)
|
46
|
+
{ string: output.first[:text] }
|
47
|
+
else
|
48
|
+
{ string: output }
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def escape_string(string)
|
53
|
+
string
|
54
|
+
.gsub(SPECIAL_NEWLINE, SPECIAL_NEWLINE_ESCAPED)
|
55
|
+
.gsub(BACKSLASH + OPENING_CURLY_BRACKET, OPENING_CURLY_BRACKET)
|
56
|
+
.gsub(BACKSLASH + SINGLE_QUOTE, SINGLE_QUOTE)
|
57
|
+
.gsub(BACKSLASH + DOUBLE_QUOTE, DOUBLE_QUOTE)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
class Code
|
2
|
+
class Parser
|
3
|
+
class Ternary < ::Code::Parser
|
4
|
+
def parse
|
5
|
+
left = parse_subclass(::Code::Parser::Range)
|
6
|
+
|
7
|
+
previous_cursor = cursor
|
8
|
+
return left if !match(WHITESPACE) && !(first_comments = parse_comments)
|
9
|
+
first_comments ||= parse_comments
|
10
|
+
|
11
|
+
if match(QUESTION_MARK)
|
12
|
+
second_comments = parse_comments
|
13
|
+
|
14
|
+
middle = parse_subclass(::Code::Parser::Ternary)
|
15
|
+
|
16
|
+
if middle
|
17
|
+
previous_cursor = cursor
|
18
|
+
third_comments = parse_comments
|
19
|
+
|
20
|
+
if match(COLON)
|
21
|
+
fourth_comments = parse_comments
|
22
|
+
right = parse_subclass(::Code::Parser::Ternary)
|
23
|
+
|
24
|
+
if right
|
25
|
+
{
|
26
|
+
ternary: {
|
27
|
+
left: left,
|
28
|
+
middle: middle,
|
29
|
+
right: right,
|
30
|
+
first_comments: first_comments,
|
31
|
+
second_comments: second_comments,
|
32
|
+
third_comments: third_comments,
|
33
|
+
fourth_comments: fourth_comments
|
34
|
+
}.compact
|
35
|
+
}
|
36
|
+
else
|
37
|
+
@cursor = previous_cursor
|
38
|
+
buffer!
|
39
|
+
{
|
40
|
+
ternary: {
|
41
|
+
left: left,
|
42
|
+
middle: middle,
|
43
|
+
first_comments: first_comments,
|
44
|
+
second_comments: second_comments
|
45
|
+
}.compact
|
46
|
+
}
|
47
|
+
end
|
48
|
+
else
|
49
|
+
@cursor = previous_cursor
|
50
|
+
buffer!
|
51
|
+
{
|
52
|
+
ternary: {
|
53
|
+
left: left,
|
54
|
+
middle: middle,
|
55
|
+
first_comments: first_comments,
|
56
|
+
second_comments: second_comments
|
57
|
+
}.compact
|
58
|
+
}
|
59
|
+
end
|
60
|
+
else
|
61
|
+
@cursor = previous_cursor
|
62
|
+
buffer!
|
63
|
+
left
|
64
|
+
end
|
65
|
+
else
|
66
|
+
@cursor = previous_cursor
|
67
|
+
buffer!
|
68
|
+
left
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
class Code
|
2
|
+
class Parser
|
3
|
+
class UnaryMinus < ::Code::Parser
|
4
|
+
def parse
|
5
|
+
if match(MINUS)
|
6
|
+
previous_cursor = cursor
|
7
|
+
comments = parse_comments
|
8
|
+
right = parse_subclass(::Code::Parser::UnaryMinus)
|
9
|
+
|
10
|
+
if right
|
11
|
+
{ unary_minus: { right: right, comments: comments }.compact }
|
12
|
+
else
|
13
|
+
@cursor = previous_cursor
|
14
|
+
buffer!
|
15
|
+
parse_subclass(::Code::Parser::Power)
|
16
|
+
end
|
17
|
+
else
|
18
|
+
parse_subclass(::Code::Parser::Power)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
class Code
|
2
|
+
class Parser
|
3
|
+
class While < ::Code::Parser
|
4
|
+
def parse
|
5
|
+
if operator = match([WHILE_KEYWORD, UNTIL_KEYWORD])
|
6
|
+
previous_cursor = cursor
|
7
|
+
|
8
|
+
comments = parse_comments
|
9
|
+
|
10
|
+
condition = parse_subclass(::Code::Parser::While)
|
11
|
+
|
12
|
+
if condition
|
13
|
+
body = parse_code
|
14
|
+
|
15
|
+
match(END_KEYWORD)
|
16
|
+
|
17
|
+
{
|
18
|
+
while: {
|
19
|
+
operator: operator,
|
20
|
+
comments: comments,
|
21
|
+
condition: condition,
|
22
|
+
body: body
|
23
|
+
}.compact
|
24
|
+
}
|
25
|
+
else
|
26
|
+
@cursor = previous_cursor
|
27
|
+
buffer!
|
28
|
+
parse_subclass(::Code::Parser::If)
|
29
|
+
end
|
30
|
+
else
|
31
|
+
parse_subclass(::Code::Parser::If)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
data/lib/code/parser.rb
ADDED
@@ -0,0 +1,237 @@
|
|
1
|
+
class Code
|
2
|
+
class Parser
|
3
|
+
attr_accessor :input, :cursor, :buffer
|
4
|
+
|
5
|
+
EMPTY_STRING = ""
|
6
|
+
|
7
|
+
DIGITS = %w[0 1 2 3 4 5 6 7 8 9]
|
8
|
+
NON_ZERO_DIGITS = %w[1 2 3 4 5 6 7 8 9]
|
9
|
+
|
10
|
+
X = "x"
|
11
|
+
O = "o"
|
12
|
+
B = "b"
|
13
|
+
|
14
|
+
SINGLE_QUOTE = "'"
|
15
|
+
DOUBLE_QUOTE = '"'
|
16
|
+
OPENING_CURLY_BRACKET = "{"
|
17
|
+
CLOSING_CURLY_BRACKET = "}"
|
18
|
+
OPENING_SQUARE_BRACKET = "["
|
19
|
+
CLOSING_SQUARE_BRACKET = "]"
|
20
|
+
OPENING_PARENTHESIS = "("
|
21
|
+
CLOSING_PARENTHESIS = ")"
|
22
|
+
DOT = "."
|
23
|
+
BACKSLASH = "\\"
|
24
|
+
COMMA = ","
|
25
|
+
SPACE = " "
|
26
|
+
SLASH = "/"
|
27
|
+
ASTERISK = "*"
|
28
|
+
NEWLINE = "\n"
|
29
|
+
HASH = "#"
|
30
|
+
COLON = ":"
|
31
|
+
EQUAL = "="
|
32
|
+
GREATER = ">"
|
33
|
+
LESSER = "<"
|
34
|
+
AMPERSAND = "&"
|
35
|
+
PIPE = "|"
|
36
|
+
UNDERSCORE = "_"
|
37
|
+
EXCLAMATION_POINT = "!"
|
38
|
+
QUESTION_MARK = "?"
|
39
|
+
MINUS = "-"
|
40
|
+
PLUS = "+"
|
41
|
+
PERCENT = "%"
|
42
|
+
CARET = "^"
|
43
|
+
TILDE = "~"
|
44
|
+
|
45
|
+
EQUALS = [
|
46
|
+
EQUAL,
|
47
|
+
PLUS + EQUAL,
|
48
|
+
MINUS + EQUAL,
|
49
|
+
ASTERISK + EQUAL,
|
50
|
+
SLASH + EQUAL,
|
51
|
+
PERCENT + EQUAL,
|
52
|
+
GREATER + GREATER + EQUAL,
|
53
|
+
LESSER + LESSER + EQUAL,
|
54
|
+
AMPERSAND + EQUAL,
|
55
|
+
PIPE + EQUAL,
|
56
|
+
CARET + EQUAL,
|
57
|
+
PIPE + PIPE + EQUAL,
|
58
|
+
AMPERSAND + AMPERSAND + EQUAL
|
59
|
+
]
|
60
|
+
|
61
|
+
WHITESPACE = [SPACE, NEWLINE]
|
62
|
+
|
63
|
+
SPECIAL_NEWLINE = "\\n"
|
64
|
+
SPECIAL_NEWLINE_ESCAPED = "\n"
|
65
|
+
|
66
|
+
NOTHING_KEYWORD = "nothing"
|
67
|
+
NULL_KEYWORD = "null"
|
68
|
+
NIL_KEYWORD = "nil"
|
69
|
+
TRUE_KEYWORD = "true"
|
70
|
+
FALSE_KEYWORD = "false"
|
71
|
+
DO_KEYWORD = "do"
|
72
|
+
END_KEYWORD = "end"
|
73
|
+
RESCUE_KEYWORD = "rescue"
|
74
|
+
NOT_KEYWORD = "not"
|
75
|
+
OR_KEYWORD = "or"
|
76
|
+
AND_KEYWORD = "and"
|
77
|
+
IF_KEYWORD = "if"
|
78
|
+
UNLESS_KEYWORD = "unless"
|
79
|
+
ELSE_KEYWORD = "else"
|
80
|
+
ELSIF_KEYWORD = "elsif"
|
81
|
+
ELSUNLESS_KEYWORD = "elsunless"
|
82
|
+
WHILE_KEYWORD = "while"
|
83
|
+
UNTIL_KEYWORD = "until"
|
84
|
+
CLASS_KEYWORD = "class"
|
85
|
+
|
86
|
+
NOTHING_KEYWORDS = [NOTHING_KEYWORD, NULL_KEYWORD, NIL_KEYWORD]
|
87
|
+
BOOLEAN_KEYWORDS = [TRUE_KEYWORD, FALSE_KEYWORD]
|
88
|
+
KEYWORDS =
|
89
|
+
NOTHING_KEYWORDS + BOOLEAN_KEYWORDS +
|
90
|
+
[
|
91
|
+
DO_KEYWORD, END_KEYWORD, RESCUE_KEYWORD, NOT_KEYWORD, OR_KEYWORD,
|
92
|
+
AND_KEYWORD, IF_KEYWORD, UNLESS_KEYWORD, ELSE_KEYWORD, ELSIF_KEYWORD,
|
93
|
+
ELSUNLESS_KEYWORD, WHILE_KEYWORD, UNTIL_KEYWORD, CLASS_KEYWORD
|
94
|
+
]
|
95
|
+
|
96
|
+
SPECIAL = [
|
97
|
+
SINGLE_QUOTE, DOUBLE_QUOTE, OPENING_CURLY_BRACKET, CLOSING_CURLY_BRACKET,
|
98
|
+
OPENING_SQUARE_BRACKET, CLOSING_SQUARE_BRACKET, BACKSLASH, DOT, COMMA,
|
99
|
+
SPACE, SLASH, ASTERISK, NEWLINE, HASH, COLON, EQUAL, GREATER, LESSER,
|
100
|
+
OPENING_PARENTHESIS, CLOSING_PARENTHESIS, AMPERSAND, PIPE,
|
101
|
+
EXCLAMATION_POINT, QUESTION_MARK, MINUS, PLUS, TILDE
|
102
|
+
]
|
103
|
+
|
104
|
+
def initialize(
|
105
|
+
input,
|
106
|
+
cursor: 0,
|
107
|
+
buffer: EMPTY_STRING,
|
108
|
+
check_end_of_input: true
|
109
|
+
)
|
110
|
+
@input = input
|
111
|
+
@cursor = cursor
|
112
|
+
@buffer = buffer
|
113
|
+
@check_end_of_input = check_end_of_input
|
114
|
+
end
|
115
|
+
|
116
|
+
def self.parse(input)
|
117
|
+
new(input).parse
|
118
|
+
end
|
119
|
+
|
120
|
+
def parse
|
121
|
+
output = parse_subclass(::Code::Parser::Code)
|
122
|
+
|
123
|
+
if check_end_of_input && cursor != input.size
|
124
|
+
syntax_error("Unexpected end of input")
|
125
|
+
end
|
126
|
+
|
127
|
+
output
|
128
|
+
end
|
129
|
+
|
130
|
+
private
|
131
|
+
|
132
|
+
attr_reader :check_end_of_input
|
133
|
+
|
134
|
+
def syntax_error(message)
|
135
|
+
raise(
|
136
|
+
::Code::Parser::Error::SyntaxError.new(
|
137
|
+
message,
|
138
|
+
input: input,
|
139
|
+
line: line,
|
140
|
+
column: column,
|
141
|
+
offset_lines: offset_lines,
|
142
|
+
offset_columns: offset_columns
|
143
|
+
)
|
144
|
+
)
|
145
|
+
end
|
146
|
+
|
147
|
+
def line
|
148
|
+
input[0...cursor].count("\n")
|
149
|
+
end
|
150
|
+
|
151
|
+
def column
|
152
|
+
cursor - input.lines[0...line].map(&:size).sum
|
153
|
+
end
|
154
|
+
|
155
|
+
def offset_lines
|
156
|
+
buffer.count("\n")
|
157
|
+
end
|
158
|
+
|
159
|
+
def offset_columns
|
160
|
+
buffer.size + 1
|
161
|
+
end
|
162
|
+
|
163
|
+
def consume(n = 1)
|
164
|
+
if cursor + n <= input.size
|
165
|
+
consumed = input[cursor, n]
|
166
|
+
@buffer += input[cursor, n]
|
167
|
+
@cursor += n
|
168
|
+
consumed
|
169
|
+
else
|
170
|
+
syntax_error("Unexpected end of input")
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
def add(output)
|
175
|
+
@output << output if output
|
176
|
+
output
|
177
|
+
end
|
178
|
+
|
179
|
+
def next?(expected)
|
180
|
+
if expected.is_a?(Array)
|
181
|
+
expected.any? { |e| input[cursor, e.size] == e }
|
182
|
+
else
|
183
|
+
input[cursor, expected.size] == expected
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
def next_next?(expected)
|
188
|
+
if expected.is_a?(Array)
|
189
|
+
expected.any? { |e| next_next?(e) }
|
190
|
+
else
|
191
|
+
input[cursor + 1, expected.size] == expected
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
def match(expected)
|
196
|
+
if expected.is_a?(Array)
|
197
|
+
expected.detect { |e| match(e) }
|
198
|
+
else
|
199
|
+
if input[cursor, expected.size] == expected
|
200
|
+
@buffer += expected
|
201
|
+
@cursor += expected.size
|
202
|
+
expected
|
203
|
+
else
|
204
|
+
false
|
205
|
+
end
|
206
|
+
end
|
207
|
+
end
|
208
|
+
|
209
|
+
def parse_subclass(subclass, **kargs)
|
210
|
+
parser =
|
211
|
+
subclass.new(input, cursor: cursor, check_end_of_input: false, **kargs)
|
212
|
+
output = parser.parse
|
213
|
+
@cursor = parser.cursor
|
214
|
+
output
|
215
|
+
end
|
216
|
+
|
217
|
+
def parse_code
|
218
|
+
parse_subclass(::Code::Parser::Code)
|
219
|
+
end
|
220
|
+
|
221
|
+
def parse_comments(whitespace: WHITESPACE)
|
222
|
+
parse_subclass(::Code::Parser::Comments, whitespace: whitespace)
|
223
|
+
end
|
224
|
+
|
225
|
+
def end_of_input?
|
226
|
+
cursor >= input.size
|
227
|
+
end
|
228
|
+
|
229
|
+
def buffer?
|
230
|
+
buffer != EMPTY_STRING
|
231
|
+
end
|
232
|
+
|
233
|
+
def buffer!
|
234
|
+
buffer.tap { @buffer = EMPTY_STRING }
|
235
|
+
end
|
236
|
+
end
|
237
|
+
end
|
data/lib/code.rb
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
class Template
|
2
|
+
class Parser < ::Code::Parser
|
3
|
+
Version = Gem::Version.new("0.1.0")
|
4
|
+
|
5
|
+
def parse
|
6
|
+
parts = []
|
7
|
+
|
8
|
+
while !end_of_input?
|
9
|
+
c = consume
|
10
|
+
|
11
|
+
if c == OPENING_CURLY_BRACKET
|
12
|
+
parts << { text: escape(buffer!) } if buffer?
|
13
|
+
parts << { code: parse_code }
|
14
|
+
match(CLOSING_CURLY_BRACKET)
|
15
|
+
buffer!
|
16
|
+
elsif c == BACKSLASH
|
17
|
+
match(OPENING_CURLY_BRACKET)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
parts << { text: escape(buffer!) } if buffer?
|
22
|
+
|
23
|
+
parts
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
def escape(text)
|
29
|
+
text.gsub(BACKSLASH + OPENING_CURLY_BRACKET, OPENING_CURLY_BRACKET)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
data/lib/template.rb
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
RSpec.describe ::Code::Parser do
|
4
|
+
subject { ::Code::Parser.parse(input) }
|
5
|
+
|
6
|
+
["a + b", "a - b", "(a - b) + c", "a - (b + c)"].each do |input|
|
7
|
+
context input do
|
8
|
+
let!(:input) { input }
|
9
|
+
|
10
|
+
it { expect { subject }.to_not raise_error }
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
[
|
15
|
+
"a /* cool */ * b",
|
16
|
+
"a + /* cool */ b",
|
17
|
+
"a + b - c /* cool */",
|
18
|
+
"a + b - /* cool */ c"
|
19
|
+
].each do |input|
|
20
|
+
context input do
|
21
|
+
let!(:input) { input }
|
22
|
+
|
23
|
+
it { expect(subject.to_json).to include("cool") }
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
RSpec.describe ::Code::Parser do
|
4
|
+
subject { ::Code::Parser.parse(input) }
|
5
|
+
|
6
|
+
["a && b", "a && b && c"].each do |input|
|
7
|
+
context input do
|
8
|
+
let!(:input) { input }
|
9
|
+
|
10
|
+
it { expect { subject }.to_not raise_error }
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
[
|
15
|
+
"a /* cool */ && b",
|
16
|
+
"a && /* cool */ b",
|
17
|
+
"a && b && c /* cool */",
|
18
|
+
"a && b && /* cool */ c"
|
19
|
+
].each do |input|
|
20
|
+
context input do
|
21
|
+
let!(:input) { input }
|
22
|
+
|
23
|
+
it { expect(subject.to_json).to include("cool") }
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
RSpec.describe ::Code::Parser do
|
4
|
+
subject { ::Code::Parser.parse(input) }
|
5
|
+
|
6
|
+
["a & b", "a & b & c"].each do |input|
|
7
|
+
context input do
|
8
|
+
let!(:input) { input }
|
9
|
+
|
10
|
+
it { expect { subject }.to_not raise_error }
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
[
|
15
|
+
"a /* cool */ & b",
|
16
|
+
"a & /* cool */ b",
|
17
|
+
"a & b & c /* cool */",
|
18
|
+
"a & b & /* cool */ c"
|
19
|
+
].each do |input|
|
20
|
+
context input do
|
21
|
+
let!(:input) { input }
|
22
|
+
|
23
|
+
it { expect(subject.to_json).to include("cool") }
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
RSpec.describe ::Code::Parser do
|
4
|
+
subject { ::Code::Parser.parse(input) }
|
5
|
+
|
6
|
+
["a | b", "a | b | c"].each do |input|
|
7
|
+
context input do
|
8
|
+
let!(:input) { input }
|
9
|
+
|
10
|
+
it { expect { subject }.to_not raise_error }
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
[
|
15
|
+
"a /* cool */ | b",
|
16
|
+
"a | /* cool */ b",
|
17
|
+
"a | b | c /* cool */",
|
18
|
+
"a | b | /* cool */ c"
|
19
|
+
].each do |input|
|
20
|
+
context input do
|
21
|
+
let!(:input) { input }
|
22
|
+
|
23
|
+
it { expect(subject.to_json).to include("cool") }
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|