require 'spec_helper'

module SemanticPredicateSpec
  describe "An &-predicate block" do
    testing_expression '& {|| $ok_to_succeed}'

    it "succeeds if it returns true, returning an epsilon syntax node" do
      $ok_to_succeed = true
      parse('foo', :consume_all_input => false) do |result|
        result.should_not be_nil
        result.interval.should == (0...0)
      end
    end

    it "fails if it returns false" do
      $ok_to_succeed = false
      parse('foo', :consume_all_input => false) do |result|
        result.should be_nil
        terminal_failures = parser.terminal_failures
        terminal_failures.size.should == 1
      end
    end

  end

  describe "A sequence of a terminal and an &-predicate block" do
    testing_expression '"prior " &{|s| $value = s[0].text_value; $ok_to_succeed }'

    it "matches the input terminal and consumes it if the block returns true, seeing the terminal in the sequence" do
      $ok_to_succeed = true
      $value = nil
      parse('prior foo', :consume_all_input => false) do |result|
        result.should_not be_nil
        result.elements[0].text_value.should == "prior "
        result.text_value.should == 'prior '
        $value.should == 'prior '
      end
    end

    it "fails if the block returns false, but sees the terminal in the sequence" do
      $ok_to_succeed = false
      $value = nil
      parse('prior foo', :consume_all_input => false) do |result|
        result.should be_nil
        $value.should == 'prior '
        terminal_failures = parser.terminal_failures
        terminal_failures.size.should == 1
      end
    end

  end

  describe "A sequence of an optional terminal and an &-predicate block" do
    testing_expression '"prior "? &{|s| $value = s[0].text_value; $ok_to_succeed}'

    it "matches the input terminal and consumes it if the block returns true" do
      $ok_to_succeed = true
      parse('prior foo', :consume_all_input => false) do |result|
        result.should_not be_nil
        result.elements[0].text_value.should == "prior "
        result.text_value.should == 'prior '
        $value.should == 'prior '
      end
    end

    it "fails with one terminal_failure if the block returns false" do
      $ok_to_succeed = false
      parse('prior foo', :consume_all_input => false) do |result|
        result.should be_nil
        $value.should == 'prior '
        terminal_failures = parser.terminal_failures
        terminal_failures.size.should == 1
      end
    end

    it "fail and return the expected optional preceeding terminal as expected input if the block returns false" do
      $ok_to_succeed = false
      parse('foo', :consume_all_input => false) do |result|
        result.should be_nil
        terminal_failures = parser.terminal_failures
        # We should get "prior " failed, and also the predicate block
        terminal_failures.size.should == 2
        terminal_failures[0].index.should == 0
        terminal_failures[0].expected_string.should == '"prior "'
        terminal_failures[1].index.should == 0
        terminal_failures[1].expected_string.should == '<semantic predicate>'
      end
    end

  end

  describe "A !-predicate block" do
    testing_expression '! {|| $ok_to_succeed}'

    it "succeeds if it returns false, returning an epsilon syntax node" do
      $ok_to_succeed = false
      parse('foo', :consume_all_input => false) do |result|
        result.should_not be_nil
        result.interval.should == (0...0)
      end
    end

    it "fails if it returns true" do
      $ok_to_succeed = true
      parse('foo', :consume_all_input => false) do |result|
        result.should be_nil
        terminal_failures = parser.terminal_failures
        terminal_failures.size.should == 1
      end
    end

  end

  describe "A sequence of a terminal and an !-predicate block" do
    testing_expression '"prior " !{|s| $value = s[0].text_value; $ok_to_succeed }'

    it "matches the input terminal and consumes it if the block returns false, seeing the terminal in the sequence" do
      $ok_to_succeed = false
      $value = nil
      parse('prior foo', :consume_all_input => false) do |result|
        result.should_not be_nil
        result.elements[0].text_value.should == "prior "
        result.text_value.should == 'prior '
        $value.should == 'prior '
      end
    end

    it "fails if the block returns true, but sees the terminal in the sequence" do
      $ok_to_succeed = true
      $value = nil
      parse('prior foo', :consume_all_input => false) do |result|
        result.should be_nil
        $value.should == 'prior '
        terminal_failures = parser.terminal_failures
        terminal_failures.size.should == 1
      end
    end

  end

  describe "A sequence of an optional terminal and an !-predicate block" do
    testing_expression '"prior "? !{|s| $value = s[0].text_value; $ok_to_succeed}'

    it "matches the input terminal and consumes it if the block returns false" do
      $ok_to_succeed = false
      parse('prior foo', :consume_all_input => false) do |result|
        result.should_not be_nil
        result.elements[0].text_value.should == "prior "
        result.text_value.should == 'prior '
        $value.should == 'prior '
      end
    end

    it "fails with one terminal_failure if the block returns true" do
      $ok_to_succeed = true
      parse('prior foo', :consume_all_input => false) do |result|
        result.should be_nil
        $value.should == 'prior '
        terminal_failures = parser.terminal_failures
        terminal_failures.size.should == 1
      end
    end

    it "fail and return the expected optional preceeding terminal as expected input if the block returns true" do
      $ok_to_succeed = true
      parse('foo', :consume_all_input => false) do |result|
        result.should be_nil
        terminal_failures = parser.terminal_failures
        terminal_failures.size.should == 2
        terminal_failures[0].index.should == 0
        terminal_failures[0].expected_string.should == '"prior "'
      end
    end

  end
end
