Source

par / lib / par.rb

Full commit

module Par
  class Result
    attr_reader :obj

    def initialize obj
      @obj = obj
    end

    def >> other
      # Concatenation with another Result
      if other.respond_to? :obj
        return concat(obj, other.obj)
      end

      # Concatenation with bottom
      return bottom if other == bottom
      return self if other == top

      # Wild concatenation with any other object
      return concat(obj, other)
    end
    def | other
      self
    end
    def maybe
      self
    end

    def new obj
      self.class.new obj
    end
    def bottom
      self.class.bottom
    end
    def top
      self.class.top
    end

    def bottom?
      false
    end
    
    def concat obj1, obj2
      new(obj1 + obj2)
    end

    def to_s
      obj.to_s
    end
    def <=> other
      return obj <=> other.to_str if other.respond_to? :to_str
    end
    include Comparable

    def self.bottom
      @bottom ||= Bottom.new
    end
    def self.top
      @top ||= Top.new
    end

    class Bottom
      def >> other
        self
      end
      def | other
        other
      end
      def maybe
        Par.top
      end
      def bottom?
        true
      end
    end

    class Top
      def >> other
        other
      end
      def | other
        self
      end
      def maybe
        self
      end
      def bottom?
        false
      end
    end
  end
  class Source
    attr_reader :str 

    def initialize str
      @str = str
      @pos = 0
    end

    def string str
      if head(str.size) == str
        return self.n(str.size)
      end

      Par.bottom
    end
    def pattern pat
      idx = head.index(pat)
      return bottom unless idx == 0
      md =head.match(pat)
      n(md[0].size)
    end

    def n n
      Par.result(head(n)).tap { @pos += n }
    end

    def conditional_rewind
      old_pos = @pos
      r = yield
      old_pos = @pos if r != Par.bottom

      return r
    ensure
      @pos = old_pos
    end
    alias :cr :conditional_rewind

    def head n=nil
      n && str[@pos, n] || str[@pos..-1]
    end
  end

module_function
  def result str
    Result.new str
  end
  def source str
    Source.new str
  end
  def bottom
    Result.bottom
  end
  def top
    Result.top
  end
end