Commit b2c6315

mokha <mokha@cisco.com>
2019-05-14 23:54:22
start to describe API for parsing
1 parent 9ad6199
lib/mpeg/parser.rb
@@ -0,0 +1,165 @@
+module Mpeg
+  class Base
+    def parse(string)
+      call(Input.new(string))
+    end
+
+    def repeat(min = 0, max = nil)
+      Repitition.new(self, min, max)
+    end
+
+    def |(parser)
+      Alternative.new(self, parser)
+    end
+
+    def >>(parser)
+      Sequence.new(self, parser)
+    end
+
+    def absent?
+      Lookahead.new(self, false)
+    end
+  end
+
+  class Slice
+    def initialize(position, string)
+    end
+  end
+
+  class Input
+    def initialize(string)
+      @scanner = StringScanner.new(string)
+    end
+
+    def matches?(pattern)
+      @scanner.match?(pattern)
+    end
+
+    def consume(characters)
+      position = @scanner.pos
+      slice = @scanner.scan(/(.|$){#{characters}}/m)
+
+      Slice.new(position, slice)
+    end
+  end
+
+  class Lookahead < Base
+    def initialize(parser, positive)
+      @parser = parser
+      @positive = positive
+    end
+  end
+
+  class Sequence < Base
+    def initialize(left, right)
+      @left = left
+      @right = right
+    end
+
+    def call(input)
+      @left.call(input) && @right.call(input)
+    end
+  end
+
+  class Alternative < Base
+    def initialize(left, right)
+      @left = left
+      @right = right
+    end
+
+    def call(input)
+      @left.call(input) || @right.call(input)
+    end
+  end
+
+  class Repitition < Base
+    def initialize(parser, min, max)
+      @parser = parser
+      @min = min
+      @max = max
+    end
+
+    def call(input)
+      occurrences = 0
+      loop do
+        break unless @parser.call(input)
+        occurrences += 1
+        return false if @max && occurrences > @max
+      end
+      occurrences >= @min
+    end
+  end
+
+  class Re < Base
+    def initialize(regex)
+      @regex = Regexp.new("[#{regex}]", Regexp::MULTILINE)
+    end
+
+    def call(input)
+      if input.matches?(@regex)
+        input.consume(1)
+        return true
+      end
+      false
+    end
+  end
+
+  class Str < Base
+    def initialize(string)
+      @length = string.size
+      @regex = Regexp.new(Regexp.escape(string))
+    end
+
+    def call(input)
+      if input.matches?(@regex)
+        input.consume(1)
+        return true
+      end
+      false
+    end
+  end
+
+  class Parser
+    def parse(string)
+      root.parse(string)
+    end
+
+    private
+
+    def root
+      alpha.repeat(1, nil) >> comparison_operator >> string
+    end
+
+    def alpha
+      Re.new('a-zA-Z')
+    end
+
+    def comparison_operator
+      equal | not_equal
+    end
+
+    def equal
+      str('eq')
+    end
+
+    def not_equal
+      str('ne')
+    end
+
+    def string
+      quote >> (str('\\') >> any | str('"').absent? >> any).repeat >> quote
+    end
+
+    def quote
+      str('"')
+    end
+
+    def str(string)
+      Str.new(string)
+    end
+
+    def any
+      Re.new('.')
+    end
+  end
+end
lib/mpeg.rb
@@ -1,3 +1,4 @@
+require "mpeg/parser"
 require "mpeg/version"
 
 module Mpeg
spec/parser_spec.rb
@@ -0,0 +1,9 @@
+RSpec.describe Mpeg::Parser do
+  subject { described_class.new }
+
+  describe "#parse" do
+    specify do
+      expect(subject.parse('name eq "mo"')).to be_truthy
+    end
+  end
+end
spec/spec_helper.rb
@@ -1,5 +1,6 @@
 require "bundler/setup"
 require "mpeg"
+require 'byebug'
 
 RSpec.configure do |config|
   # Enable flags like --only-failures and --next-failure
.gitignore
@@ -9,3 +9,4 @@
 
 # rspec failure tracking
 .rspec_status
+.byebug_history
Gemfile.lock
@@ -6,6 +6,7 @@ PATH
 GEM
   remote: https://rubygems.org/
   specs:
+    byebug (11.0.1)
     diff-lcs (1.3)
     rake (10.5.0)
     rspec (3.8.0)
@@ -27,6 +28,7 @@ PLATFORMS
 
 DEPENDENCIES
   bundler (~> 2.0)
+  byebug (~> 11.0)
   mpeg!
   rake (~> 10.0)
   rspec (~> 3.0)
mpeg.gemspec
@@ -24,6 +24,7 @@ Gem::Specification.new do |spec|
   spec.require_paths = ["lib"]
 
   spec.add_development_dependency "bundler", "~> 2.0"
+  spec.add_development_dependency "byebug", "~> 11.0"
   spec.add_development_dependency "rake", "~> 10.0"
   spec.add_development_dependency "rspec", "~> 3.0"
 end