93 lines
2.5 KiB
Ruby
93 lines
2.5 KiB
Ruby
class Parslet::Expression::Treetop
|
|
class Parser < Parslet::Parser
|
|
root(:expression)
|
|
|
|
rule(:expression) { alternatives }
|
|
|
|
# alternative 'a' / 'b'
|
|
rule(:alternatives) {
|
|
(simple >> (spaced('/') >> simple).repeat).as(:alt)
|
|
}
|
|
|
|
# sequence by simple concatenation 'a' 'b'
|
|
rule(:simple) { occurrence.repeat(1).as(:seq) }
|
|
|
|
# occurrence modifiers
|
|
rule(:occurrence) {
|
|
atom.as(:repetition) >> spaced('*').as(:sign) |
|
|
atom.as(:repetition) >> spaced('+').as(:sign) |
|
|
atom.as(:repetition) >> repetition_spec |
|
|
|
|
atom.as(:maybe) >> spaced('?') |
|
|
atom
|
|
}
|
|
|
|
rule(:atom) {
|
|
spaced('(') >> expression.as(:unwrap) >> spaced(')') |
|
|
dot |
|
|
string |
|
|
char_class
|
|
}
|
|
|
|
# a character class
|
|
rule(:char_class) {
|
|
(str('[') >>
|
|
(str('\\') >> any |
|
|
str(']').absent? >> any).repeat(1) >>
|
|
str(']')).as(:match) >> space?
|
|
}
|
|
|
|
# anything at all
|
|
rule(:dot) { spaced('.').as(:any) }
|
|
|
|
# recognizing strings
|
|
rule(:string) {
|
|
str('\'') >>
|
|
(
|
|
(str('\\') >> any) |
|
|
(str("'").absent? >> any)
|
|
).repeat.as(:string) >>
|
|
str('\'') >> space?
|
|
}
|
|
|
|
# repetition specification like {1, 2}
|
|
rule(:repetition_spec) {
|
|
spaced('{') >>
|
|
integer.maybe.as(:min) >> spaced(',') >>
|
|
integer.maybe.as(:max) >> spaced('}')
|
|
}
|
|
rule(:integer) {
|
|
match['0-9'].repeat(1)
|
|
}
|
|
|
|
# whitespace handling
|
|
rule(:space) { match("\s").repeat(1) }
|
|
rule(:space?) { space.maybe }
|
|
|
|
def spaced(str)
|
|
str(str) >> space?
|
|
end
|
|
end
|
|
|
|
class Transform < Parslet::Transform
|
|
|
|
rule(:repetition => simple(:rep), :sign => simple(:sign)) {
|
|
min = sign=='+' ? 1 : 0
|
|
Parslet::Atoms::Repetition.new(rep, min, nil) }
|
|
rule(:repetition => simple(:rep), :min => simple(:min), :max => simple(:max)) {
|
|
Parslet::Atoms::Repetition.new(rep,
|
|
Integer(min || 0),
|
|
max && Integer(max) || nil) }
|
|
|
|
rule(:alt => subtree(:alt)) { Parslet::Atoms::Alternative.new(*alt) }
|
|
rule(:seq => sequence(:s)) { Parslet::Atoms::Sequence.new(*s) }
|
|
rule(:unwrap => simple(:u)) { u }
|
|
rule(:maybe => simple(:m)) { |d| d[:m].maybe }
|
|
rule(:string => simple(:s)) { Parslet::Atoms::Str.new(s) }
|
|
rule(:match => simple(:m)) { Parslet::Atoms::Re.new(m) }
|
|
rule(:any => simple(:a)) { Parslet::Atoms::Re.new('.') }
|
|
end
|
|
|
|
end
|
|
|