fop_lang 0.3.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +5 -3
- data/lib/fop/nodes.rb +20 -4
- data/lib/fop/parser.rb +69 -24
- data/lib/fop/tokenizer.rb +39 -18
- data/lib/fop/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8ed95bb4708820a186e6485cc29dbb47286b0f309a1caf91af7778d768b0efb3
|
4
|
+
data.tar.gz: 85d41728ddae13f3667f0a2d55a5c4dcbc26e8217d4f31466e6ed92038859881
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e650bdf66d8d0b5dcb603eae494f38d4969a19647053b4e81e0612705f5b16a5755d007e4ce01fad9b487224d66eff462738f1e37b011ba2a4bf4a45b0203bb3
|
7
|
+
data.tar.gz: 99b31736236785cecc85b9bb23ccc3e366713cd23dd04c992a8fff6676a316d5741bfe5058e17cd0812c43d8bf1979aa7546184386018c1f92ed2462a85eb5fb
|
data/README.md
CHANGED
@@ -29,7 +29,7 @@ The above expression contains the only two parts of Fop (except for the wildcard
|
|
29
29
|
|
30
30
|
**Text Literals**
|
31
31
|
|
32
|
-
A text literal works how it sounds: the input must match it exactly. The only exception is the `*` (wildcard) character, which matches 0 or more of anything. Wildcards can be used anywhere except inside `{...}` (operations).
|
32
|
+
A text literal works how it sounds: the input must match it exactly. If it matches it passes through unchanged. The only exception is the `*` (wildcard) character, which matches 0 or more of anything. Wildcards can be used anywhere except inside `{...}` (operations).
|
33
33
|
|
34
34
|
If `\` (escape) is used before the special characters `*`, `{` or `}`, then that character is treated like a text literal. It's recommended to use single-quoted Ruby strings with Fop expressions that so you don't need to double-escape.
|
35
35
|
|
@@ -42,9 +42,11 @@ Operations are the interesting part of Fop, and are specified between `{` and `}
|
|
42
42
|
* `A` is the alpha class and will match one or more letters (lower or upper case).
|
43
43
|
* `W` is the word class and matches alphanumeric chars and underscores.
|
44
44
|
* `*` is the wildcard class and greedily matches everything after it.
|
45
|
-
* `/.../` matches on the supplied regex between the `/`'s. If you're regex contains a `/`, it must be escaped.
|
45
|
+
* `/.../` matches on the supplied regex between the `/`'s. If you're regex contains a `/`, it must be escaped. Capture groups may be referenced in the operator argument as `$1`, `$2`, etc.
|
46
46
|
3. Operator (optional): What to do to the matching characters.
|
47
|
-
* `=` Replace the matching character(s) with the given argument. If no argument is given, drop the matching chars.
|
47
|
+
* `=` Replace the matching character(s) with the given argument. If no argument is given, drop the matching chars.
|
48
|
+
* `>` Append the following chars to the matching value.
|
49
|
+
* `<` Prepend the following chars to the matching value.
|
48
50
|
* `+` Perform addition on the matching number and the argument (`N` only).
|
49
51
|
* `-` Subtract the argument from the matching number (`N` only).
|
50
52
|
5. Operator argument (required for some operators): meaning varies by operator.
|
data/lib/fop/nodes.rb
CHANGED
@@ -12,11 +12,15 @@ module Fop
|
|
12
12
|
end
|
13
13
|
end
|
14
14
|
|
15
|
-
Op = Struct.new(:wildcard, :match, :regex_match, :regex, :operator, :operator_arg, :expression) do
|
15
|
+
Op = Struct.new(:wildcard, :match, :regex_match, :regex, :operator, :operator_arg, :operator_arg_w_caps, :expression) do
|
16
16
|
def consume!(input)
|
17
|
-
if (
|
18
|
-
|
19
|
-
|
17
|
+
if (match = regex.match(input))
|
18
|
+
val = match.to_s
|
19
|
+
blank = val == Parser::BLANK
|
20
|
+
input.sub!(val, Parser::BLANK) unless blank
|
21
|
+
found_val = regex_match || !blank
|
22
|
+
arg = operator_arg_w_caps ? sub_caps(operator_arg_w_caps, match.captures) : operator_arg
|
23
|
+
expression && found_val ? expression.call(val, operator, arg) : val
|
20
24
|
end
|
21
25
|
end
|
22
26
|
|
@@ -26,6 +30,18 @@ module Fop
|
|
26
30
|
s << " #{operator} #{operator_arg}" if operator
|
27
31
|
s
|
28
32
|
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
def sub_caps(tokens, caps)
|
37
|
+
tokens.map { |t|
|
38
|
+
case t
|
39
|
+
when String then t
|
40
|
+
when Parser::CaptureGroup then caps[t.index].to_s
|
41
|
+
else raise Parser::Error, "Unexpected #{t} in capture group"
|
42
|
+
end
|
43
|
+
}.join("")
|
44
|
+
end
|
29
45
|
end
|
30
46
|
end
|
31
47
|
end
|
data/lib/fop/parser.rb
CHANGED
@@ -3,6 +3,7 @@ require_relative 'nodes'
|
|
3
3
|
module Fop
|
4
4
|
module Parser
|
5
5
|
Error = Class.new(StandardError)
|
6
|
+
CaptureGroup = Struct.new(:index)
|
6
7
|
|
7
8
|
MATCH_NUM = "N".freeze
|
8
9
|
MATCH_WORD = "W".freeze
|
@@ -10,10 +11,19 @@ module Fop
|
|
10
11
|
MATCH_WILD = "*".freeze
|
11
12
|
BLANK = "".freeze
|
12
13
|
OP_REPLACE = "=".freeze
|
14
|
+
OP_APPEND = ">".freeze
|
15
|
+
OP_PREPEND = "<".freeze
|
13
16
|
OP_ADD = "+".freeze
|
14
17
|
OP_SUB = "-".freeze
|
15
18
|
OP_MUL = "*".freeze
|
16
19
|
OP_DIV = "/".freeze
|
20
|
+
VAR = "$".freeze
|
21
|
+
CAP_NUM = /^[1-9]$/
|
22
|
+
|
23
|
+
EXP_REPLACE = ->(_val, _op, arg) { arg || BLANK }
|
24
|
+
EXP_MATH = ->(val, op, arg) { val.to_i.send(op, arg.to_i) }
|
25
|
+
EXP_APPEND = ->(val, _op, arg) { val + arg }
|
26
|
+
EXP_PREPEND = ->(val, _op, arg) { arg + val }
|
17
27
|
|
18
28
|
def self.parse!(tokens)
|
19
29
|
nodes = []
|
@@ -45,7 +55,7 @@ module Fop
|
|
45
55
|
when Nodes::Text, Nodes::Op
|
46
56
|
nodes << curr_node
|
47
57
|
else
|
48
|
-
raise "Unexpected end node #{curr_node}"
|
58
|
+
raise Error, "Unexpected end node #{curr_node}"
|
49
59
|
end
|
50
60
|
|
51
61
|
nodes
|
@@ -59,7 +69,7 @@ module Fop
|
|
59
69
|
Nodes::Text.new(wildcard, token.char.clone)
|
60
70
|
when Tokenizer::Op
|
61
71
|
op = Nodes::Op.new(wildcard)
|
62
|
-
parse_op! op, token
|
72
|
+
parse_op! op, token
|
63
73
|
op
|
64
74
|
when :wildcard
|
65
75
|
:wildcard
|
@@ -85,52 +95,87 @@ module Fop
|
|
85
95
|
end
|
86
96
|
end
|
87
97
|
|
88
|
-
def self.parse_op!(node,
|
89
|
-
t = tokens[0] || raise(Error, "Empty operation")
|
98
|
+
def self.parse_op!(node, token)
|
90
99
|
# parse the matching type
|
91
100
|
node.regex =
|
92
|
-
case
|
101
|
+
case token.match
|
93
102
|
when Tokenizer::Char
|
94
|
-
node.match =
|
103
|
+
node.match = token.match.char
|
95
104
|
node.regex_match = false
|
96
|
-
case
|
105
|
+
case node.match
|
97
106
|
when MATCH_NUM then Regexp.new((node.wildcard ? ".*?" : "^") + "[0-9]+")
|
98
107
|
when MATCH_WORD then Regexp.new((node.wildcard ? ".*?" : "^") + "\\w+")
|
99
108
|
when MATCH_ALPHA then Regexp.new((node.wildcard ? ".*?" : "^") + "[a-zA-Z]+")
|
100
109
|
when MATCH_WILD then /.*/
|
101
|
-
else raise Error, "Unknown match type '#{
|
110
|
+
else raise Error, "Unknown match type '#{node.match}'"
|
102
111
|
end
|
103
112
|
when Tokenizer::Regex
|
104
|
-
node.match = "/#{
|
113
|
+
node.match = "/#{token.match.src}/"
|
105
114
|
node.regex_match = true
|
106
|
-
Regexp.new((node.wildcard ? ".*?" : "^") +
|
115
|
+
Regexp.new((node.wildcard ? ".*?" : "^") + token.match.src)
|
116
|
+
when nil
|
117
|
+
raise Error, "Empty operation"
|
107
118
|
else
|
108
|
-
raise Error, "Unexpected
|
119
|
+
raise Error, "Unexpected #{token.match}"
|
109
120
|
end
|
110
121
|
|
111
122
|
# parse the operator (if any)
|
112
|
-
if
|
113
|
-
raise Error, "Unexpected #{
|
114
|
-
node.operator =
|
115
|
-
|
116
|
-
|
117
|
-
raise Error, "Unexpected #{t}" unless t.is_a? Tokenizer::Char
|
118
|
-
acc + t.char
|
119
|
-
}
|
120
|
-
node.operator_arg = arg == BLANK ? nil : arg
|
121
|
-
|
123
|
+
if token.operator
|
124
|
+
raise Error, "Unexpected #{token.operator} for operator" unless token.operator.is_a? Tokenizer::Char
|
125
|
+
node.operator = token.operator.char
|
126
|
+
node.operator_arg = token.arg if token.arg and token.arg != BLANK
|
127
|
+
node.operator_arg_w_caps = parse_captures! node.operator_arg if node.operator_arg and node.regex_match
|
122
128
|
node.expression =
|
123
129
|
case node.operator
|
124
130
|
when OP_REPLACE
|
125
|
-
|
131
|
+
EXP_REPLACE
|
126
132
|
when OP_ADD, OP_SUB, OP_MUL, OP_DIV
|
127
133
|
raise Error, "Operator #{node.operator} is only available for numeric matches" unless node.match == MATCH_NUM
|
128
134
|
raise Error, "Operator #{node.operator} expects an argument" if node.operator_arg.nil?
|
129
|
-
|
135
|
+
EXP_MATH
|
136
|
+
when OP_APPEND
|
137
|
+
raise Error, "Operator #{node.operator} expects an argument" if node.operator_arg.nil?
|
138
|
+
EXP_APPEND
|
139
|
+
when OP_PREPEND
|
140
|
+
raise Error, "Operator #{node.operator} expects an argument" if node.operator_arg.nil?
|
141
|
+
EXP_PREPEND
|
130
142
|
else
|
131
|
-
raise
|
143
|
+
raise Error, "Unknown operator #{node.operator}"
|
132
144
|
end
|
133
145
|
end
|
134
146
|
end
|
147
|
+
|
148
|
+
def self.parse_captures!(arg)
|
149
|
+
i = 0
|
150
|
+
iend = arg.size - 1
|
151
|
+
escape = false
|
152
|
+
nodes = []
|
153
|
+
|
154
|
+
until i > iend
|
155
|
+
char = arg[i]
|
156
|
+
i += 1
|
157
|
+
|
158
|
+
if escape
|
159
|
+
nodes << char
|
160
|
+
escape = false
|
161
|
+
next
|
162
|
+
end
|
163
|
+
|
164
|
+
case char
|
165
|
+
when Tokenizer::ESCAPE
|
166
|
+
escape = true
|
167
|
+
when VAR
|
168
|
+
num = arg[i].to_s
|
169
|
+
raise Error, "Capture group number must be between 1 and 9; found '#{num}'" unless num =~ CAP_NUM
|
170
|
+
nodes << CaptureGroup.new(num.to_i - 1)
|
171
|
+
i += 1
|
172
|
+
else
|
173
|
+
nodes << char
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
raise Error, "Trailing escape" if escape
|
178
|
+
nodes
|
179
|
+
end
|
135
180
|
end
|
136
181
|
end
|
data/lib/fop/tokenizer.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
module Fop
|
2
2
|
class Tokenizer
|
3
3
|
Char = Struct.new(:char)
|
4
|
-
Op = Struct.new(:
|
4
|
+
Op = Struct.new(:match, :operator, :arg)
|
5
5
|
Regex = Struct.new(:src)
|
6
6
|
Error = Class.new(StandardError)
|
7
7
|
|
@@ -22,28 +22,26 @@ module Fop
|
|
22
22
|
i = 0
|
23
23
|
until i > @end do
|
24
24
|
char = @src[i]
|
25
|
+
i += 1
|
26
|
+
|
25
27
|
if escape
|
26
28
|
tokens << Char.new(char)
|
27
29
|
escape = false
|
28
|
-
i += 1
|
29
30
|
next
|
30
31
|
end
|
31
32
|
|
32
33
|
case char
|
33
34
|
when ESCAPE
|
34
35
|
escape = true
|
35
|
-
i += 1
|
36
36
|
when OP_OPEN
|
37
|
-
i, op = operation! i
|
37
|
+
i, op = operation! i
|
38
38
|
tokens << op
|
39
39
|
when OP_CLOSE
|
40
40
|
raise "Unexpected #{OP_CLOSE}"
|
41
41
|
when WILDCARD
|
42
42
|
tokens << :wildcard
|
43
|
-
i += 1
|
44
43
|
else
|
45
44
|
tokens << Char.new(char)
|
46
|
-
i += 1
|
47
45
|
end
|
48
46
|
end
|
49
47
|
|
@@ -54,40 +52,63 @@ module Fop
|
|
54
52
|
private
|
55
53
|
|
56
54
|
def operation!(i)
|
57
|
-
escape = false
|
58
55
|
found_close = false
|
59
|
-
|
56
|
+
op = Op.new(nil, nil, "")
|
60
57
|
|
58
|
+
# Find matcher
|
59
|
+
until found_close or op.match or i > @end do
|
60
|
+
char = @src[i]
|
61
|
+
i += 1
|
62
|
+
case char
|
63
|
+
when OP_CLOSE
|
64
|
+
found_close = true
|
65
|
+
when REGEX_MARKER
|
66
|
+
i, reg = regex! i
|
67
|
+
op.match = reg
|
68
|
+
else
|
69
|
+
op.match = Char.new(char)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
# Find operator
|
74
|
+
until found_close or op.operator or i > @end do
|
75
|
+
char = @src[i]
|
76
|
+
i += 1
|
77
|
+
case char
|
78
|
+
when OP_CLOSE
|
79
|
+
found_close = true
|
80
|
+
else
|
81
|
+
op.operator = Char.new(char)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
# Find operator arg
|
86
|
+
escape = false
|
61
87
|
until found_close or i > @end do
|
62
88
|
char = @src[i]
|
89
|
+
i += 1
|
90
|
+
|
63
91
|
if escape
|
64
|
-
|
92
|
+
op.arg << char
|
65
93
|
escape = false
|
66
|
-
i += 1
|
67
94
|
next
|
68
95
|
end
|
69
96
|
|
70
97
|
case char
|
71
98
|
when ESCAPE
|
72
99
|
escape = true
|
73
|
-
i += 1
|
74
100
|
when OP_OPEN
|
75
101
|
raise "Unexpected #{OP_OPEN}"
|
76
102
|
when OP_CLOSE
|
77
103
|
found_close = true
|
78
|
-
i += 1
|
79
|
-
when REGEX_MARKER
|
80
|
-
i, reg = regex! i + 1
|
81
|
-
tokens << reg
|
82
104
|
else
|
83
|
-
|
84
|
-
i += 1
|
105
|
+
op.arg << char
|
85
106
|
end
|
86
107
|
end
|
87
108
|
|
88
109
|
raise Error, "Unclosed operation" if !found_close
|
89
110
|
raise Error, "Trailing escape" if escape
|
90
|
-
return i,
|
111
|
+
return i, op
|
91
112
|
end
|
92
113
|
|
93
114
|
def regex!(i)
|
data/lib/fop/version.rb
CHANGED