mirror of
https://github.com/mistergibson/gxg-web-client.git
synced 2026-08-15 11:34:29 -07:00
347 lines
7 KiB
Ruby
347 lines
7 KiB
Ruby
module Rubinius
|
|
module PEG
|
|
class InvalidPatternError < Exception; end
|
|
|
|
# Class Pattern is the base class for all nodes used to represent the AST
|
|
# for a PEG. Following LPEG, the base is a pattern and not a grammar.
|
|
class Pattern
|
|
def graph
|
|
if defined? Rubinius
|
|
Rubinius::AST::AsciiGrapher.new(self, Pattern).print
|
|
else
|
|
inspect
|
|
end
|
|
|
|
nil
|
|
end
|
|
|
|
def visit(visitor)
|
|
end
|
|
|
|
# Pattern operators
|
|
|
|
def /(other)
|
|
Choice.new self, other
|
|
end
|
|
|
|
# TODO: remove after core transform for :/ is removed
|
|
alias_method :divide, :/
|
|
|
|
def +(other)
|
|
Concatenation.new self, other
|
|
end
|
|
|
|
def -(other)
|
|
Difference.new self, other
|
|
end
|
|
|
|
def *(other)
|
|
Product.new self, other
|
|
end
|
|
|
|
def +@
|
|
If.new self
|
|
end
|
|
|
|
def -@
|
|
Unless.new self
|
|
end
|
|
|
|
def =~(string)
|
|
end
|
|
|
|
def match(string)
|
|
end
|
|
end
|
|
|
|
class UnaryOp < Pattern
|
|
attr_accessor :pattern
|
|
|
|
def initialize(pattern)
|
|
@pattern = PEG.pattern pattern
|
|
end
|
|
|
|
private :initialize
|
|
end
|
|
|
|
class BinaryOp < Pattern
|
|
attr_accessor :first
|
|
attr_accessor :second
|
|
|
|
def initialize(first, second)
|
|
@first = PEG.pattern first
|
|
@second = PEG.pattern second
|
|
end
|
|
|
|
private :initialize
|
|
end
|
|
|
|
class Grammar < Pattern
|
|
attr_accessor :start
|
|
|
|
def initialize(variable=nil)
|
|
if variable
|
|
@start = variable.name
|
|
set_variable variable.name, variable.pattern
|
|
end
|
|
end
|
|
|
|
private :initialize
|
|
|
|
def set_variable(name, pattern)
|
|
variable = get_variable name
|
|
variable.pattern = PEG.pattern pattern
|
|
variable
|
|
end
|
|
|
|
def get_variable(name)
|
|
if singleton_class.method_defined? name
|
|
variable = send name
|
|
else
|
|
vname = name.to_sym
|
|
variable = Variable.new vname
|
|
singleton_class.thunk_method vname, variable
|
|
end
|
|
|
|
variable
|
|
end
|
|
|
|
def method_missing(sym, *args)
|
|
name = sym.to_s
|
|
|
|
if name[-1] == ?= and args.size == 1
|
|
set_variable name[0..-2], args.first
|
|
else
|
|
get_variable name
|
|
end
|
|
end
|
|
|
|
def visit(visitor)
|
|
visitor.grammar self
|
|
end
|
|
|
|
def =~(string)
|
|
end
|
|
|
|
def match(string)
|
|
end
|
|
end
|
|
|
|
class Always < Pattern
|
|
def visit(visitor)
|
|
visitor.always self
|
|
end
|
|
end
|
|
|
|
class AnyRange < Pattern
|
|
attr_accessor :range
|
|
|
|
def initialize(range)
|
|
@range = range
|
|
end
|
|
|
|
private :initialize
|
|
|
|
def visit(visitor)
|
|
visitor.any_range self
|
|
end
|
|
end
|
|
|
|
class Any < Pattern
|
|
attr_accessor :count
|
|
|
|
def initialize(count)
|
|
@count = count
|
|
end
|
|
|
|
private :initialize
|
|
|
|
def visit(visitor)
|
|
visitor.any self
|
|
end
|
|
end
|
|
|
|
class CharacterRange < Pattern
|
|
attr_accessor :range
|
|
|
|
def initialize(range)
|
|
@range = range
|
|
end
|
|
|
|
private :initialize
|
|
|
|
def visit(visitor)
|
|
visitor.character_range self
|
|
end
|
|
end
|
|
|
|
class Character < Pattern
|
|
attr_accessor :string
|
|
|
|
def initialize(string)
|
|
@string = string
|
|
end
|
|
|
|
private :initialize
|
|
|
|
def visit(visitor)
|
|
visitor.character self
|
|
end
|
|
end
|
|
|
|
class Choice < BinaryOp
|
|
def visit(visitor)
|
|
visitor.choice self
|
|
end
|
|
end
|
|
|
|
class Concatenation < BinaryOp
|
|
def visit(visitor)
|
|
visitor.concatenation self
|
|
end
|
|
end
|
|
|
|
class Difference < BinaryOp
|
|
def visit(visitor)
|
|
visitor.difference self
|
|
end
|
|
end
|
|
|
|
# Implements &patt in a PEG. The pattern succeeds if
|
|
# patt matches. Does not consume any input.
|
|
class If < UnaryOp
|
|
def visit(visitor)
|
|
visitor.if self
|
|
end
|
|
end
|
|
|
|
class Never < Pattern
|
|
def visit(visitor)
|
|
visitor.never self
|
|
end
|
|
end
|
|
|
|
class Product < Pattern
|
|
attr_accessor :pattern
|
|
attr_accessor :count
|
|
|
|
def initialize(pattern, count)
|
|
@pattern = PEG.pattern pattern
|
|
@count = Type.coerce_to(count, Integer, :to_int).abs
|
|
end
|
|
|
|
private :initialize
|
|
|
|
def visit(visitor)
|
|
visitor.product self
|
|
end
|
|
end
|
|
|
|
class Set < Pattern
|
|
attr_accessor :set
|
|
|
|
def initialize(set)
|
|
@set = set.uniq
|
|
end
|
|
|
|
private :initialize
|
|
|
|
def visit(visitor)
|
|
visitor.set self
|
|
end
|
|
end
|
|
|
|
# Implements !patt in a PEG. The pattern succeeds if
|
|
# patt does not match. Does not consume any input.
|
|
class Unless < UnaryOp
|
|
def visit(visitor)
|
|
visitor.unless self
|
|
end
|
|
end
|
|
|
|
class Variable < Pattern
|
|
attr_accessor :name
|
|
attr_accessor :pattern
|
|
|
|
def initialize(name, pattern=nil)
|
|
@name = name.to_sym
|
|
@pattern = PEG.pattern pattern
|
|
end
|
|
|
|
private :initialize
|
|
|
|
def visit(visitor)
|
|
visitor.variable self
|
|
end
|
|
end
|
|
|
|
# Accepts a Ruby object representation of simple PEG "atoms" and returns a
|
|
# PEG AST node.
|
|
def pattern(spec)
|
|
case spec
|
|
when Array
|
|
Set.new spec
|
|
when Integer
|
|
node = Any.new spec.abs
|
|
node = If.new node if spec < 0
|
|
node
|
|
when String
|
|
Character.new spec
|
|
when Pattern
|
|
spec
|
|
when true
|
|
Always.new
|
|
when false
|
|
Never.new
|
|
when nil
|
|
nil
|
|
else
|
|
raise InvalidPatternError, "unknown specification type for Pattern: #{spec.inspect}"
|
|
end
|
|
end
|
|
module_function :pattern
|
|
|
|
def grammar(spec=nil, pattern=nil)
|
|
case spec
|
|
when Symbol
|
|
Grammar.new Variable.new(spec, pattern)
|
|
when Variable
|
|
Grammar.new spec
|
|
when Pattern
|
|
Grammar.new Variable.new(:S, spec)
|
|
when nil
|
|
Grammar.new
|
|
else
|
|
Grammar.new Variable.new(:S, pattern)
|
|
end
|
|
end
|
|
module_function :grammar
|
|
|
|
def variable(name, pattern=nil)
|
|
PEG::Variable.new name, pattern
|
|
end
|
|
module_function :variable
|
|
|
|
def self.compile(pattern)
|
|
end
|
|
|
|
def self.peg
|
|
g = grammar = grammar(:grammar)
|
|
g.grammar = (g.nonterminal + "=" + g.sp + g.pattern) * 1
|
|
g.pattern = g.alternative + (pattern("/") + g.sp + g.alternative) * 0
|
|
g.alternative = ((+g.predicate + g.predicate + g.sp + g.suffix) /
|
|
(g.sp + g.suffix)) * 1
|
|
g.predicate = ["!&"]
|
|
g.suffix = g.primary + (pattern(["*+?"]) + g.sp) * 0
|
|
g.primary = (pattern("(") + g.sp + g.pattern + ")" + g.sp) / (pattern(1) + g.sp) /
|
|
g.literal / g.char_class / (g.nonterminal + -pattern("="))
|
|
g.literal = pattern("'") + (-pattern("'") + 1) * 0 + "'" + g.sp
|
|
g.char_class = (pattern("[") + (-pattern("]") +
|
|
((pattern(1) + "-" + 1) / 1)) * 0) + "]" + g.sp
|
|
g.nonterminal = (pattern("_") / ["a".."z"] / ["A".."Z"]) * 1 + g.sp
|
|
g.sp = pattern([" \t\n"]) * 0
|
|
|
|
grammar
|
|
end
|
|
end
|
|
end
|