<repo_name>ashmoran/language_implementation_patterns<file_sep>/Gemfile
source 'http://rubygems.org'

ruby '1.9.3'

gem 'facets'
gem 'treetop'

group :development do
	gem 'guard'
	gem 'listen'
	gem 'rb-fsevent'
  gem 'growl'
  gem 'terminal-notifier-guard'

	gem 'cucumber'
	gem 'guard-cucumber'
	gem 'aruba'

	gem 'rspec'
	gem 'guard-rspec'
	gem 'fuubar'
	gem 'fakefs'
	gem 'lstrip-on-steroids'

	gem 'pry'

	gem 'awesome_print'
end
<file_sep>/spec/1_getting_started/ch02_basic_parsing/p04_llk_recursive_descent_parser/list_parser_with_assignment_spec.rb
require 'spec_helper'

require '1_getting_started/ch02_basic_parsing/p04_llk_recursive_descent_parser/list_parser_with_assignment'
require '1_getting_started/ch02_basic_parsing/p04_llk_recursive_descent_parser/lexer_lookahead'
require '1_getting_started/ch02_basic_parsing/p02_ll1_recursive_descent_lexer/list_lexer'
require_relative '../p03_ll1_recursive_descent_parser/list_parser_contract'
require_relative 'list_parser_assignment_contract'

module GettingStarted
  module BasicParsing
    module LLkRecursiveDescentParser
      describe "Intergration:", ListParserWithAssignment, "and lexer" do
        let(:input) { "[ a = b ]" }

        let(:lexer) { LL1RecursiveDescentLexer::ListLexer.new(input) }
        let(:lookahead) { LexerLookahead.new(lexer) }
        subject(:parser) { ListParserWithAssignment.new(lookahead) }

        it "parses lists with assignments" do
          expect(parser.list).to be == [ { :a => :b } ]
        end
      end

      describe ListParserWithAssignment do
        let(:tokens) {
          LL1RecursiveDescentLexer::Token.descriptions_to_tokens(token_descriptions)
        }
        let(:lexer) { tokens.each }
        let(:lookahead) { LexerLookahead.new(lexer) }
        subject(:parser) { ListParserWithAssignment.new(lookahead) }

        it_behaves_like "a ListParser"
        it_behaves_like "a ListParser with assignment"
      end
    end
  end
end<file_sep>/lib/2_analyzing_languages/ch06_tracking_symbols/p16_monolithic_scope/variable_symbol.rb
module AnalyzingLanguages
  module TrackingSymbols
    module MonolithicScope
      class VariableSymbol
        attr_reader :name, :type

        def initialize(name, type)
          @name = name.to_sym
          @type = type
        end

        def ==(other)
          other.is_a?(VariableSymbol) &&
            @name == other.name &&
            @type == other.type
        end

        def to_s
          "<#{@name}:#{@type}>"
        end
      end
    end
  end
end<file_sep>/lib/1_getting_started/ch03_enhanced_parsing/p06_memoizing_parser/memoizing_list_parser.rb
require '1_getting_started/errors'

# UNFINISHED!!!
#
# I got bored of this - it's a big refactoring for a relatively minor change,
# and I'm not primarily concerned with how to implement parsers. But I've left
# the unfunished code here in case I decide to return to it some time.
# It passes all the tests :)

module GettingStarted
  module EnhancedParsing
    module MemoizingParser

      class MemoizingListParser
        def initialize(replayable_lexer)
          @lexer = replayable_lexer
        end

        def stat
          if speculate_stat_list
            stat_list
          elsif speculate_stat_parallel_assigment
            stat_parallel_assignment
          else
            raise NoViableAlternativeError.new("Expecting <list> or <parallel assignment>")
          end
        end

        def speculate_stat_list
          @lexer.speculate do
            stat_list
          end
        rescue RecognitionError => e
          false
        end

        def speculate_stat_parallel_assigment
          @lexer.speculate do
            stat_parallel_assignment
          end
        rescue RecognitionError => e
          false
        end

        def stat_list
          matched_list = list
          match(:eof)
          matched_list
        end

        def stat_parallel_assignment
          matched_parallel_assigment = parallel_assignment
          match(:eof)
          matched_parallel_assigment
        end

        def list
          failed = false

          @lexer.if_speculating do
            return if already_parsed?(:list)
          end

          parsed_list = _list

          @lexer.if_speculating do
            memoize(:list, parsed_list)
          end

          parsed_list
        rescue RecognitionError => e
          @lexer.if_speculating do
            memoize_failure(:list)
          end

          raise
        end

        def _list
          [ ].tap do |collected_list|
            match(:lbrack)
            elements(collected_list)
            match(:rbrack)
          end
        end

        def parallel_assignment
          lhs = list
          match(:equals)
          rhs = list
          { lhs => rhs }
        end

        def elements(collected_list)
          first_element(collected_list)
          while @lexer.peek.type == :comma
            match(:comma)
            element(collected_list)
          end
        end

        def first_element(collected_list)
          case @lexer.peek.type
          when :name, :lbrack
            element(collected_list)
          when :rbrack
            return
          else
            raise RecognitionError.new(
              "Expected :lbrack, :name or :rbrack, found #{@lexer.peek.inspect}"
            )
          end
        end

        def element(collected_list)
          if @lexer.peek(1).type == :name && @lexer.peek(2).type == :equals
            lhs, _, rhs = match(:name), match(:equals), match(:name)
            collected_list << { lhs.value.to_sym => rhs.value.to_sym }
          elsif @lexer.peek.type == :name
            collected_list << @lexer.peek.value.to_sym
            match(:name)
          elsif @lexer.peek.type == :lbrack
            collected_list << list
          else
            raise RecognitionError.new(
              "Expected :name or :lbrack, found #{@lexer.peek.inspect}"
            )
          end
        end

        private

        def already_parsed?(expression_type)
          false
        end

        def memoize(expression_type, expression)
          expression
        end

        def memoize_failure(expression_type)
          nil
        end

        def match(expected_type)
          if @lexer.peek.type == expected_type
            consume
          else
            raise RecognitionError.new(
              "Expected #{expected_type.inspect}, found #{@lexer.peek.inspect}"
            )
          end
        end

        def consume
          @lexer.next
        end
      end

    end
  end
end
<file_sep>/spec/2_analyzing_languages/ch06_tracking_symbols/p16_monolithic_scope/language_symbol_spec.rb
require 'spec_helper'

require '2_analyzing_languages/ch06_tracking_symbols/p16_monolithic_scope/language_symbol'

module AnalyzingLanguages
  module TrackingSymbols
    module MonolithicScope
      describe LanguageSymbol do
        subject(:symbol) { LanguageSymbol.new(:foo) }

        its(:to_s) { should be == "foo" }

        describe "#==" do
          example do
            expect(symbol).to be == LanguageSymbol.new(:foo)
          end

          example do
            expect(symbol).to be == LanguageSymbol.new("foo")
          end

          example do
            expect(symbol).to_not be == LanguageSymbol.new(:bar)
          end

          example do
            expect(symbol).to_not be == :foo
          end
        end

        describe "#name" do
          its(:name) { should be == :foo }

          it "is always a symbol" do
            expect(LanguageSymbol.new("foo").name).to be == :foo
          end
        end
      end
    end
  end
end<file_sep>/lib/2_analyzing_languages/ch06_tracking_symbols/p17_nested_scopes/cymbol2.rb
require_relative 'cymbol2_parser'

module AnalyzingLanguages
  module TrackingSymbols
    module NestedScopes

      class Cymbol2
        def initialize
          @parser = CymbolNestedParser.new
        end

        def parse(source)
          @parser.parse(source)
        end
      end

    end
  end
end<file_sep>/lib/2_analyzing_languages/ch06_tracking_symbols/p16_monolithic_scope/language_symbol.rb
module AnalyzingLanguages
  module TrackingSymbols
    module MonolithicScope
      class LanguageSymbol
        attr_reader :name

        def initialize(name)
          @name = name.to_sym
        end

        def ==(other)
          other.is_a?(LanguageSymbol) && @name == other.name
        end

        def to_s
          @name.to_s
        end
      end
    end
  end
end<file_sep>/spec/2_analyzing_languages/ch06_tracking_symbols/p16_monolithic_scope/symbol_table_spec.rb
require 'spec_helper'

require '2_analyzing_languages/ch06_tracking_symbols/p16_monolithic_scope/symbol_table'
require '2_analyzing_languages/ch06_tracking_symbols/p16_monolithic_scope/language_symbol'

module AnalyzingLanguages
  module TrackingSymbols
    module MonolithicScope
      describe SymbolTable do
        subject(:symbol_table) { SymbolTable.new }

        describe "#define" do
          it "ignores metadata" do
            expect {
              symbol_table.define(LanguageSymbol.new(:foo), unused: "stuff")
            }.to_not raise_error(ArgumentError)
          end
        end

        describe "#resolve" do
          context "undefined symbol" do
            specify {
              expect {
                symbol_table.resolve("foo")
              }.to raise_error(RuntimeError, "Unknown symbol: foo")
            }

            it "ignores metadata" do
              expect {
                symbol_table.resolve("foo", unused: "stuff")
              }.to_not raise_error(ArgumentError)
            end
          end

          context "defined symbol" do
            let(:symbol) { LanguageSymbol.new(:foo) }

            before(:each) do
              symbol_table.define(symbol)
            end

            specify {
              expect(symbol_table.resolve("foo")).to equal(symbol)
            }

            specify {
              expect(symbol_table.resolve(:foo)).to equal(symbol)
            }
          end
        end

        describe "#to_s" do
          before(:each) do
            symbol_table.define(LanguageSymbol.new(:foo))
            symbol_table.define(LanguageSymbol.new(:bar))
            symbol_table.define(LanguageSymbol.new(:baz))
          end

          it "outputs in alphabetic order" do
            expect(symbol_table.to_s).to be ==
              "globals: {bar=bar, baz=baz, foo=foo}"
          end
        end
      end
    end
  end
end<file_sep>/spec/1_getting_started/ch02_basic_parsing/p02_ll1_recursive_descent_lexer/list_lexer_contract.rb
require_relative 'enumerator_contract'

module GettingStarted
  module BasicParsing
    module LL1RecursiveDescentLexer
      shared_examples_for "a ListLexer" do
        def tokenize_all_input
          begin
            loop do
              collected_output << lexer.next
            end
          rescue StopIteration => e

          end
        end

        describe "#peek" do
          let(:input) { "[a]" }

          it "lets you see the next character" do
            expect {
              lexer.next
            }.to change {
              lexer.peek
            }.from(Token.new(lbrack: "[")).to(Token.new(name: "a"))
          end
        end

        describe "Enumerator-like properties" do
          let(:input) { "" }

          def advance_to_end
            lexer.next
          end

          it_behaves_like "an Enumerator"
        end

        context "valid input" do
          before(:each) do
            tokenize_all_input
          end

          context "empty string" do
            let(:input) { "" }

            it "marks the end of the tokens explicitly" do
              expect(output).to be == [ { eof: nil } ]
            end
          end

          context "blank string" do
            context "spaces, tabs, newlines" do
              let(:input) { "   " }

              specify {
                expect(output).to be == [ { eof: nil } ]
              }
            end
          end

          context "lbrack" do
            let(:input) { "[" }

            specify {
              expect(output).to be == [ { lbrack: "[" }, { eof: nil } ]
            }
          end

          context "comma" do
            let(:input) { "," }

            specify {
              expect(output).to be == [ { comma: "," }, { eof: nil } ]
            }
          end

          context "equals" do
            let(:input) { "=" }

            specify {
              expect(output).to be == [ { equals: "=" }, { eof: nil } ]
            }
          end

          context "rbrack" do
            let(:input) { "]" }

            specify {
              expect(output).to be == [ { rbrack: "]" }, { eof: nil } ]
            }
          end

          context "names" do
            context "single letter" do
              let(:input) { "a" }

              specify {
                expect(output).to be == [ { name: "a" }, { eof: nil } ]
              }
            end

            context "multi-letter" do
              let(:input) { "abcdefghijklmnopqrstuvwxyz" }

              specify {
                expect(output).to be == [ { name: "abcdefghijklmnopqrstuvwxyz" }, { eof: nil } ]
              }
            end

            context "in a list" do
              let(:input) { "[ a, xyz, bc ]" }

              specify {
                expect(output).to be == [
                  { lbrack: "[" },
                  { name:   "a" },
                  { comma:  "," },
                  { name:   "xyz" },
                  { comma:  "," },
                  { name:   "bc" },
                  { rbrack: "]" },
                  { eof:    nil }
                ]
              }
            end
          end

          context "delimiters, separators, and spaces" do
            let(:input) { " [ \t, \n, ] \n" }

            specify {
              expect(output).to be == [
                { lbrack: "[" }, { comma: "," }, { comma: "," }, { rbrack: "]" }, { eof: nil }
              ]
            }
          end
        end

        context "invalid input" do
          context "invalid characters" do
            context "in normal context" do
              let(:input) { "@" }

              specify {
                expect { tokenize_all_input }.to raise_error(ArgumentError, "Invalid character: @")
              }
            end

            context "in a name" do
              let(:input) { "a$"}

              specify {
                expect { tokenize_all_input }.to raise_error(ArgumentError, "Invalid character: $")
              }
            end
          end
        end
      end

    end
  end
end<file_sep>/lib/2_analyzing_languages/ch06_tracking_symbols/p16_monolithic_scope/cymbol.rb
require 'treetop'

module AnalyzingLanguages; module TrackingSymbols; module MonolithicScope; end; end; end
Treetop.load(File.dirname(__FILE__) + '/cymbol_monolithic')

require_relative 'cymbol_monolithic_node_classes'

module AnalyzingLanguages
  module TrackingSymbols
    module MonolithicScope

      # Implements Pattern 16: Symbol Table for Monolithic Scope
      #
      # Note that this is more of an experimental playground than a translation
      # of the example in the book. Key points:
      #
      # * I'm using Treetop, not ANTLR. Mainly because I wanted to learn an
      #   implementation of Parsing Expression Grammars (mentioned on p56),
      #   and because Treetop works well in all Ruby runtimes, whereas ANTLR
      #   needs JRuby if you want access to the latest version. (The Ruby
      #   target lags behind the Java, C, C++ targets etc.)
      #
      # * The book produces output with code blocks in the ANTLR grammar.
      #   I wanted to avoid hijacking the predicate system in Treetop, so the
      #   way I solve it is closer to Pattern 25: Tree-Based Interpreter. Note
      #   that it's not a very *good* interpreter, but it does the job of
      #   spitting out the output we want (or something close enough to it).
      class Cymbol
        def initialize(symbol_table)
          @symbol_table = symbol_table
          @parser = CymbolMonolithicParser.new
        end

        def parse(source)
          populate_symbols(tree(source))
        end

        private

        def tree(source)
          @parser.parse(source)
        end

        def populate_symbols(tree)
          tree.populate_symbols(@symbol_table)
        end
      end

    end
  end
end<file_sep>/lib/2_analyzing_languages/ch06_tracking_symbols/p16_monolithic_scope/symbol_table.rb
module AnalyzingLanguages
  module TrackingSymbols
    module MonolithicScope
      class SymbolTable
        def initialize
          @symbols = { }
        end

        def define(symbol, metadata = { })
          @symbols[symbol.name] = symbol
        end

        def resolve(name, metadata = { })
          @symbols.fetch(name.to_sym) {
            raise "Unknown symbol: #{name}"
          }
        end

        def to_s
          "globals: {#{key_value_pairs.join(", ")}}"
        end

        private

        def key_value_pairs
          @symbols.keys.sort.map { |key| "#{key}=#{@symbols[key]}" }
        end
      end
    end
  end
end<file_sep>/spec/2_analyzing_languages/ch06_tracking_symbols/p17_nested_scopes/method_symbol_spec.rb
require 'spec_helper'

require '2_analyzing_languages/ch06_tracking_symbols/p17_nested_scopes/method_symbol'
require '2_analyzing_languages/ch06_tracking_symbols/p16_monolithic_scope/language_symbol'

module AnalyzingLanguages
  module TrackingSymbols
    module NestedScopes
      describe MethodSymbol do
        let(:type) { LanguageSymbol.new(:type) }
        subject(:symbol) { MethodSymbol.new(:name, type) }

        its(:to_s) { should be == "method<name:type>" }

        describe "#==" do
          example do
            expect(symbol).to be == MethodSymbol.new(:name, type)
          end

          example do
            expect(symbol).to be == MethodSymbol.new("name", type)
          end

          example do
            expect(symbol).to_not be == MethodSymbol.new(:bar, type)
          end

          example do
            expect(symbol).to_not be == MethodSymbol.new(:name, LanguageSymbol.new(:wrong_type))
          end

          example do
            expect(symbol).to_not be == :name
          end
        end

        describe "#name" do
          its(:name) { should be == :name }

          it "is always a symbol" do
            expect(MethodSymbol.new("name", :ununused_type).name).to be == :name
          end
        end
      end
    end
  end
end<file_sep>/lib/1_getting_started/ch02_basic_parsing/p04_llk_recursive_descent_parser/lexer_lookahead.rb
module GettingStarted
  module BasicParsing
    module LLkRecursiveDescentParser

      # Helps implement Pattern 4: LL(k) Recursive-Descent Parser
      #
      # Major differences from the book example:
      #
      # * This isn't actually LL(k) because there isn't a fixed size _k_ for
      #   the lookahead buffer (the book example uses a small circular buffer).
      #   It's so easy to implement this by shifting a Ruby Array I went with
      #   the idiomatic solution rather than the memory-efficient one. I'm not
      #   too bothered about this from a learning point of view because I only
      #   care about what this does, not how to implement it efficiently.
      #
      # * This solution uses a separate object rather than rolling the lookahead
      #   code into the Parser. The code below could be used to add lookahead to
      #   any lexer, and can feed any parser, even a non-lookahead one (it
      #   conforms to the ListLexer contract).
      class LexerLookahead
        def initialize(lexer)
          @lexer        = lexer
          @lexer_empty  = false
          @buffer       = [ ]
        end

        def peek(lookahead_distance = 1)
          fill_buffer(lookahead_distance)
          token_or_stop_iteration(@buffer[lookahead_distance - 1])
        end

        def next
          fill_buffer(1)
          consume
        end

        private

        def consume
          token_or_stop_iteration(@buffer.shift)
        end

        def fill_buffer(required_size)
          (required_size - @buffer.size).times do
            harvest
          end
        end

        def harvest
          @buffer << @lexer.next
        rescue StopIteration
          def self.harvest
            # NOOP
          end

          def self.token_or_stop_iteration(token)
            raise StopIteration if token.nil?
            token
          end
        end

        def token_or_stop_iteration(token)
          token
        end
      end

    end
  end
end<file_sep>/lib/1_getting_started/ch02_basic_parsing/p04_llk_recursive_descent_parser/list_parser_with_assignment.rb
require '1_getting_started/errors'

module GettingStarted
  module BasicParsing
    module LLkRecursiveDescentParser

      # Implements Pattern 4: LL(k) Recursive-Descent Parser
      # (Needs the help of the LexerLookahead)
      #
      # Major differences from the book example:
      #
      # * I put the code for the lookahead in a separate class (the rest of
      #   the discussion is in the LexerLookahead code)
      class ListParserWithAssignment
        def initialize(lookahead_lexer)
          @lexer = lookahead_lexer
        end

        def list
          [ ].tap do |collected_list|
            match(:lbrack)
            elements(collected_list)
            match(:rbrack)
          end
        end

        def elements(collected_list)
          first_element(collected_list)
          while @lexer.peek.type == :comma
            match(:comma)
            element(collected_list)
          end
        end

        def first_element(collected_list)
          case @lexer.peek.type
          when :name, :lbrack
            element(collected_list)
          when :rbrack
            return
          else
            raise RecognitionError.new(
              "Expected :lbrack, :name or :rbrack, found #{@lexer.peek.inspect}"
            )
          end
        end

        def element(collected_list)
          if @lexer.peek(1).type == :name && @lexer.peek(2).type == :equals
            lhs, _, rhs = match(:name), match(:equals), match(:name)
            collected_list << { lhs.value.to_sym => rhs.value.to_sym }
          elsif @lexer.peek.type == :name
            collected_list << @lexer.peek.value.to_sym
            match(:name)
          elsif @lexer.peek.type == :lbrack
            collected_list << list
          else
            raise RecognitionError.new(
              "Expected :name or :lbrack, found #{@lexer.peek.inspect}"
            )
          end
        end

        private

        def match(expected_type)
          if @lexer.peek.type == expected_type
            consume
          else
            raise RecognitionError.new(
              "Expected #{expected_type.inspect}, found #{@lexer.peek.inspect}"
            )
          end
        end

        def consume
          @lexer.next
        end
      end

    end
  end
end<file_sep>/spec/1_getting_started/ch02_basic_parsing/p04_llk_recursive_descent_parser/lexer_lookahead_spec.rb
require 'spec_helper'

require '1_getting_started/ch02_basic_parsing/p02_ll1_recursive_descent_lexer/list_lexer'
require '1_getting_started/ch02_basic_parsing/p04_llk_recursive_descent_parser/lexer_lookahead'
require_relative '../p02_ll1_recursive_descent_lexer/list_lexer_contract'

module GettingStarted
  module BasicParsing
    module LLkRecursiveDescentParser
      shared_examples_for "LexerLookahead#peek" do
        example do
          expect(lexer.peek.value).to be == "["
        end

        example do
          expect(lexer.peek(1).value).to be == "["
        end

        example do
          expect(lexer.peek(2).value).to be == "a"
        end

        example do
          expect(lexer.peek(5).value).to be == "]"
        end

        example do
          expect(lexer.peek(6).to_hash).to be == { eof: nil }
        end

        example do
          expect { lexer.peek(7) }.to raise_error(StopIteration)
        end
      end

      describe LexerLookahead do
        describe "lookahead" do
          let(:tokens) {
            LL1RecursiveDescentLexer::Token.descriptions_to_tokens(
              [
                { lbrack: "[" },
                { name:   "a" },
                { equals: "=" },
                { name:   "b" },
                { rbrack: "]" },
                { eof:    nil }
              ]
            )
          }

          let(:underlying_lexer) { tokens.each }

          subject(:lexer) { LexerLookahead.new(underlying_lexer) }

          describe "#peek" do
            it_behaves_like "LexerLookahead#peek"
          end
        end

        context "wrapping a ListLexer" do
          let(:input) { "[ a = b ]" }
          let(:underlying_lexer) { LL1RecursiveDescentLexer::ListLexer.new(input) }
          subject(:lexer) { LexerLookahead.new(underlying_lexer) }

          let(:collected_output) { [ ] }
          let(:output) {
            collected_output.map { |token| token.to_hash }
          }

          it_behaves_like "a ListLexer"

          describe "#peek" do
            it_behaves_like "LexerLookahead#peek"
          end
        end
      end
    end
  end
end<file_sep>/lib/2_analyzing_languages/ch06_tracking_symbols/p16_monolithic_scope/symbol_table_logger.rb
module AnalyzingLanguages
  module TrackingSymbols
    module MonolithicScope
      # A Decorator for SymbolTable
      #
      # Note this makes the questionable decision of only logging if location
      # metadata is available. Arguable we should have distinct #resolve_at_location
      # and #define_at_location methods to be used by clients (I haven't thought
      # this through though).
      class SymbolTableLogger
        def initialize(symbol_table, output_io)
          @symbol_table = symbol_table
          @output_io    = output_io
        end

        def resolve(symbol_name, metadata = { })
          @symbol_table.resolve(symbol_name).tap do |symbol|
            log("ref", symbol, metadata)
          end
        rescue RuntimeError => e # Eek! Seems we were using too geneneral an error class...
          log("ref [failed]", symbol_name, metadata)
        end

        def define(symbol, metadata = { })
          log("def", symbol, metadata)
          @symbol_table.define(symbol)
        end

        # Hacked in as part of the NestedScopes code...
        # I'm not taking too much care to refactor now
        def define_as_scope(symbol, metadata = { })
          log("defscope", symbol, metadata)
          @symbol_table.define_as_scope(symbol)
        end

        # Also hacked in
        def push_scope
          @symbol_table.push_scope
        end

        # And this
        def pop_scope
          @symbol_table.pop_scope
        end

        private

        def log(event, item, metadata)
          @output_io.puts("#{metadata[:location]}: #{event} #{item}")
        end
      end
    end
  end
end<file_sep>/spec/2_analyzing_languages/ch06_tracking_symbols/p17_nested_scopes/cymbol2_parser_spec.rb
require 'spec_helper'

require '2_analyzing_languages/ch06_tracking_symbols/p17_nested_scopes/cymbol2_parser'

RSpec::Matchers.define :parse do
  match do |source|
    parser.parse(source)
  end
end

module AnalyzingLanguages
  module TrackingSymbols
    module NestedScopes

      describe CymbolNestedParser do
        subject(:parser) { CymbolNestedParser.new }

        # If something goes wrong in here, we're missing a focused example!
        describe "a complex program" do
          example do
            expect(<<-C).to parse
              // intro comment
              int i = 9;
              float j;
              int k = i+2;

              float f(int x, float y)
              {
                float i;
                { float z = x+y; i = z; }
                return i;
              }

              void g()
              {
                f(i, 2);
              }
            C
          end
        end

        describe "empty program" do
          example do
            expect("").to parse
          end
          example do
            expect("  ").to parse
          end
          example do
            expect(" \n").to parse
          end
          example do
            expect("\t\t\t").to parse
          end
          example do
            expect("\n\n\n").to parse
          end
          example do
            expect("\n \n\t\n").to parse
          end
        end

        describe "comments" do
          example do
            expect("// this is a comment\n").to parse
          end
          example do
            expect("// this is a comment").to parse
          end
          example do
            expect(" // this is a comment").to parse
          end
        end

        describe "variable declaration" do
          context "without initialization" do
            example do
              expect("int foo;").to parse
            end
            example do
              expect("int foo ;").to parse
            end
            example do
              expect("float foo;").to parse
            end
          end

          context "with initialization" do
            context "to a literal" do
              example do
                expect("int foo=99;").to parse
              end
              example do
                expect("int foo = 99 ;").to parse
              end
            end

            context "to a variable" do
              example do
                expect("int foo = x;").to parse
              end
            end

            context "to an arbitrary expression" do
              example do
                expect("int foo = x + 1;").to parse
              end
              example do
                expect("int foo = x+1;").to parse
              end
            end
          end
        end

        describe "assignment" do
          example do
            expect("foo=bar;").to parse
          end
          example do
            expect("foo = bar;").to parse
          end
          example do
            expect("foo = 1;").to parse
          end
        end

        describe "blocks" do
          example do
            expect("{}").to parse
          end
          example do
            expect("{ }").to parse
          end
          example do
            expect("{ int i; }").to parse
          end
          example do
            expect("{ int i; x = y; }").to parse
          end
          example do
            expect("{
              int i;
              x = y;
            }").to parse
          end
          example do
            expect("{ x = a + b; }").to parse
          end

          describe "nested" do
            example do
              expect("{{}}").to parse
            end
            example do
              expect("{ { } }").to parse
            end
            example do
              expect("{
                int i;
                {
                  float k;
                }
              }").to parse
            end
          end
        end

        describe "methods" do
          describe "definitions" do
            example do
              expect("int foo(){}").to parse
            end
            example do
              expect("int foo() { }").to parse
            end
            example do
              expect("int foo()\n{ }").to parse
            end
            example do
              expect("int foo(int x) { }").to parse
            end
            example do
              expect("int foo(int x, float y) { }").to parse
            end
            example do
              expect("int foo( int x ,float y ) { }").to parse
            end
            example do
              expect("float f() { int i; }").to parse
            end
            example do
              expect("float f() { return a + b; }").to parse
            end
          end
          describe "calls" do
            example do
              expect("foo();").to parse
            end
            example do
              expect("foo( ) ;").to parse
            end
            example do
              expect("f(a);").to parse
            end
            example do
              expect("f(a, b);").to parse
            end
            example do
              expect("f(a, b + 1);").to parse
            end
          end
        end
      end

    end
  end
end<file_sep>/lib/1_getting_started/ch03_enhanced_parsing/p05_backtracking_parser/replayable_buffer.rb
require 'forwardable'

module GettingStarted
  module EnhancedParsing
    module BacktrackingParser

      class ReplayableBuffer
        extend Forwardable

        def_delegator :@buffer, :[]
        def_delegator :@buffer, :size

        def_delegator :@buffer, :<<
        def_delegator :@buffer, :shift

        class ArrayProxy
          def initialize(buffer = [ ])
            @buffer = buffer
          end

          # Queries

          def [](index)
            @buffer[index]
          end

          def size
            @buffer.size
          end

          # Commands

          def <<(object)
            @buffer << object
          end

          def shift
            @buffer.shift
          end

          # Support

          def dup
            ArrayProxy.new(@buffer.dup)
          end

          def unwrap
            raise RuntimeError.new("No mark has been set")
          end
        end

        class TimeMachine
          def initialize(buffer, snapshot = nil)
            @buffer   = buffer
            @snapshot = snapshot || buffer.dup
          end

          # Queries

          def [](index)
            @snapshot[index]
          end

          def size
            @snapshot.size
          end

          # Commands

          def <<(object)
            @snapshot << object
            @buffer << object
          end

          def shift
            @snapshot.shift
          end

          # Support

          def dup
            TimeMachine.new(self, @snapshot.dup)
          end

          def unwrap
            @buffer
          end
        end

        def initialize
          @buffer = ArrayProxy.new
        end

        def mark
          @buffer = TimeMachine.new(@buffer)
        end

        def release
          @buffer = @buffer.unwrap
        end
      end

    end
  end
end
<file_sep>/lib/1_getting_started/ch02_basic_parsing/p02_ll1_recursive_descent_lexer/token.rb
module GettingStarted
  module BasicParsing
    module LL1RecursiveDescentLexer
      class Token
        attr_reader :type, :value

        class << self
          def descriptions_to_tokens(descriptions)
            descriptions.map { |description| Token.new(description) }
          end
        end

        def initialize(description)
          @type   = description.keys.first
          @value  = description.values.first
        end

        def to_hash
          { @type => @value }
        end

        def inspect
          @type.inspect
        end

        def ==(other)
          @type == other.type && @value == other.value
        end
      end
    end
  end
end<file_sep>/spec/1_getting_started/ch03_enhanced_parsing/p05_backtracking_parser/replayable_buffer_spec.rb
require 'spec_helper'

require '1_getting_started/ch03_enhanced_parsing/p05_backtracking_parser/replayable_buffer'

module GettingStarted
  module EnhancedParsing
    module BacktrackingParser

      describe ReplayableBuffer do
        subject(:buffer) { ReplayableBuffer.new }

        context "used without marking" do
          describe "#[]" do
            example do
              expect(buffer[0]).to be_nil
              expect(buffer[1]).to be_nil
              expect(buffer[2]).to be_nil
              expect(buffer[-1]).to be_nil
            end

            example do
              buffer << :a
              buffer << :b

              expect(buffer[0]).to be == :a
              expect(buffer[1]).to be == :b
              expect(buffer[2]).to be_nil
              expect(buffer[-1]).to be == :b
            end
          end

          describe "#shift" do
            example do
              expect(buffer.shift).to be_nil
            end

            example do
              buffer << :a
              buffer << :b

              expect(buffer.shift).to be == :a
              expect(buffer.shift).to be == :b
              expect(buffer.shift).to be_nil
            end
          end

          describe "#size" do
            example do
              expect {
                buffer << :a
                buffer << :b
              }.to change { buffer.size }.from(0).to(2)
            end

            example do
              buffer << :a
              buffer << :b
              expect { buffer.shift }.to change { buffer.size }.from(2).to(1)
              expect { buffer.shift }.to change { buffer.size }.from(1).to(0)
            end
          end

          describe "#release" do
            it "raises an error" do
              expect {
                buffer.release
              }.to raise_error(RuntimeError, "No mark has been set")
            end
          end
        end

        context "with a mark" do
          before(:each) do
            buffer << :premark_a
            buffer << :premark_b
            buffer.mark
          end

          describe "#shift" do
            example do
              expect(buffer.shift).to be == :premark_a
              expect(buffer.shift).to be == :premark_b
              expect(buffer.shift).to be_nil

              buffer.release

              expect(buffer.shift).to be == :premark_a
              expect(buffer.shift).to be == :premark_b
              expect(buffer.shift).to be_nil
            end
          end

          describe "#[]" do
            example do
              buffer.shift
              buffer.shift
              buffer.release

              expect(buffer[0]).to be == :premark_a
              expect(buffer[1]).to be == :premark_b
            end
          end

          describe "#size" do
            example do
              expect { buffer.shift }.to change { buffer.size }.from(2).to(1)
              expect { buffer.shift }.to change { buffer.size }.from(1).to(0)
              expect { buffer.release }.to change { buffer.size }.from(0).to(2)
            end
          end
        end

        context "with two marks" do
          describe "releasing both marks" do
            before(:each) do
              buffer << :a
              buffer << :b
              buffer << :c
              buffer.mark
              buffer.mark
            end

            example do
              output_before_release_1 = [ buffer.shift, buffer.shift, buffer.shift ]
              expect(output_before_release_1).to be == [ :a, :b, :c ]

              buffer.release

              output_before_release_2 = [ buffer.shift, buffer.shift, buffer.shift ]
              expect(output_before_release_2).to be == [ :a, :b, :c ]

              buffer.release

              output_before_release_3 = [ buffer.shift, buffer.shift, buffer.shift ]
              expect(output_before_release_3).to be == [ :a, :b, :c ]
            end
          end

          describe "shifting between the marks" do
            example do
              buffer << :a
              buffer << :b
              buffer.mark
              expect(buffer.shift).to be == :a
              buffer.mark
              expect(buffer.shift).to be == :b
              buffer.release
              expect(buffer.shift).to be == :b
              buffer.release
              expect(buffer.shift).to be == :a
              expect(buffer.shift).to be == :b
            end
          end

          describe "buffering across two marks" do
            before(:each) do
              buffer << :a
              buffer.mark
              buffer << :b
              buffer.mark
              buffer << :c
            end

            example do
              output_before_release_1 = [ buffer.shift, buffer.shift, buffer.shift ]
              expect(output_before_release_1).to be == [ :a, :b, :c ]

              buffer.release

              output_before_release_2 = [ buffer.shift, buffer.shift, buffer.shift ]
              expect(output_before_release_2).to be == [ :a, :b, :c ]

              buffer.release

              output_before_release_3 = [ buffer.shift, buffer.shift, buffer.shift ]
              expect(output_before_release_3).to be == [ :a, :b, :c ]
            end
          end
        end
      end

    end
  end
end
<file_sep>/lib/2_analyzing_languages/ch06_tracking_symbols/p17_nested_scopes/symbol_table.rb
module AnalyzingLanguages
  module TrackingSymbols
    module NestedScopes
      module Scope
        def define(symbol)
          @symbols[symbol.name] = symbol
        end

        def resolve(name)
          @symbols.fetch(name.to_sym) {
            @parent_scope.resolve(name)
          }
        end

        def unwrap
          @parent_scope
        end
      end

      class SymbolTable
        class DeadEndScope
          def resolve(name)
            raise "Unknown symbol: #{name}"
          end
        end

        class LocalScope
          include Scope

          def initialize(parent_scope = :remove_me)
            @parent_scope = parent_scope
            @symbols = { }
          end


          def to_s
            "symbols: {#{key_value_pairs.join(", ")}}"
          end

          private

          def key_value_pairs
            @symbols.keys.sort.map { |key| "#{key}=#{@symbols[key]}" }
          end
        end

        def initialize
          @scope = LocalScope.new(DeadEndScope.new)
        end

        def push_scope
          @scope = LocalScope.new(@scope)
        end

        def pop_scope
          @scope = @scope.unwrap
        end

        def define(symbol, metadata = { })
          @scope.define(symbol)
        end

        def define_as_scope(symbol, metadata = { })
          define(symbol)
          symbol.wrap(@scope)
          @scope = symbol
        end

        def resolve(name, metadata = { })
          @scope.resolve(name)
        end

        def to_s
          @scope.to_s
        end
      end

    end
  end
end<file_sep>/lib/1_getting_started/ch03_enhanced_parsing/p05_backtracking_parser/list_parser_with_parallel_assignment.rb
require '1_getting_started/errors'

module GettingStarted
  module EnhancedParsing
    module BacktrackingParser
      class ListParserWithParallelAssignment
        def initialize(replayable_lexer)
          @lexer = replayable_lexer
        end

        def stat
          if speculate_stat_list
            matched_list = list
            match(:eof)
            matched_list
          elsif speculate_stat_parallel_assigment
            matched_parallel_assigment = parallel_assignment
            match(:eof)
            matched_parallel_assigment
          else
            raise NoViableAlternativeError.new("Expecting <list> or <parallel assignment>")
          end
        end

        def speculate_stat_list
          @lexer.speculate do
            list
            match(:eof)
          end
        rescue RecognitionError => e
          false
        end

        def speculate_stat_parallel_assigment
          @lexer.speculate do
            parallel_assignment
            match(:eof)
          end
        rescue RecognitionError => e
          false
        end

        def list
          [ ].tap do |collected_list|
            match(:lbrack)
            elements(collected_list)
            match(:rbrack)
          end
        end

        def parallel_assignment
          lhs = list
          match(:equals)
          rhs = list
          { lhs => rhs }
        end

        def elements(collected_list)
          first_element(collected_list)
          while @lexer.peek.type == :comma
            match(:comma)
            element(collected_list)
          end
        end

        def first_element(collected_list)
          case @lexer.peek.type
          when :name, :lbrack
            element(collected_list)
          when :rbrack
            return
          else
            raise RecognitionError.new(
              "Expected :lbrack, :name or :rbrack, found #{@lexer.peek.inspect}"
            )
          end
        end

        def element(collected_list)
          if @lexer.peek(1).type == :name && @lexer.peek(2).type == :equals
            lhs, _, rhs = match(:name), match(:equals), match(:name)
            collected_list << { lhs.value.to_sym => rhs.value.to_sym }
          elsif @lexer.peek.type == :name
            collected_list << @lexer.peek.value.to_sym
            match(:name)
          elsif @lexer.peek.type == :lbrack
            collected_list << list
          else
            raise RecognitionError.new(
              "Expected :name or :lbrack, found #{@lexer.peek.inspect}"
            )
          end
        end

        private

        def match(expected_type)
          if @lexer.peek.type == expected_type
            consume
          else
            raise RecognitionError.new(
              "Expected #{expected_type.inspect}, found #{@lexer.peek.inspect}"
            )
          end
        end

        def consume
          @lexer.next
        end
      end

    end
  end
end
<file_sep>/lib/1_getting_started/errors.rb
class RecognitionError < RuntimeError; end
class NoViableAlternativeError < RuntimeError; end<file_sep>/spec/1_getting_started/ch02_basic_parsing/p04_llk_recursive_descent_parser/list_parser_assignment_contract.rb
shared_examples_for "a ListParser with assignment" do
  context "list with assignment" do
    let(:token_descriptions) {
      [
        { lbrack: "[" },
        { name:   "a" },
        { equals: "=" },
        { name:   "b" },
        { rbrack: "]" },
        { eof:    nil }
      ]
    }

    specify {
      expect(parser.list).to be == [ { :a => :b } ]
    }
  end
end
<file_sep>/spec/1_getting_started/ch02_basic_parsing/p02_ll1_recursive_descent_lexer/enumerator_contract.rb
shared_examples_for "an Enumerator" do
  context "at the start" do
    specify {
      expect(subject.next).to_not be_nil
    }

    specify {
      expect(subject.peek).to_not be_nil
    }

    specify {
      peeked_value = subject.peek
      expect(subject.next).to be == peeked_value
    }
  end

  context "at the end" do
    before(:each) do
      advance_to_end
    end

    specify {
      expect { subject.next }.to raise_error(StopIteration)
    }

    specify {
      expect { subject.peek }.to raise_error(StopIteration)
    }
  end
end
<file_sep>/spec/2_analyzing_languages/ch06_tracking_symbols/p16_monolithic_scope/variable_symbol_spec.rb
require 'spec_helper'

require '2_analyzing_languages/ch06_tracking_symbols/p16_monolithic_scope/variable_symbol'

module AnalyzingLanguages
  module TrackingSymbols
    module MonolithicScope
      describe VariableSymbol do
        let(:type) { LanguageSymbol.new(:type) }
        subject(:symbol) { VariableSymbol.new(:name, type) }

        its(:to_s) { should be == "<name:type>" }

        describe "#==" do
          example do
            expect(symbol).to be == VariableSymbol.new(:name, type)
          end

          example do
            expect(symbol).to be == VariableSymbol.new("name", type)
          end

          example do
            expect(symbol).to_not be == VariableSymbol.new(:bar, type)
          end

          example do
            expect(symbol).to_not be == VariableSymbol.new(:name, LanguageSymbol.new(:wrong_type))
          end

          example do
            expect(symbol).to_not be == :name
          end
        end

        describe "#name" do
          its(:name) { should be == :name }

          it "is always a symbol" do
            expect(VariableSymbol.new("name", :ununused_type).name).to be == :name
          end
        end
      end
    end
  end
end<file_sep>/spec/1_getting_started/ch02_basic_parsing/p02_ll1_recursive_descent_lexer/enumerator_spec.rb
require 'spec_helper'

require_relative 'enumerator_contract'

# Prove that the Enumerator interface we depend on works how we think it does
describe "a real Enumerator" do
  subject(:array_enumerator) { [ { eof: nil } ].each }

  def advance_to_end
    array_enumerator.next
  end

  it_behaves_like "an Enumerator"
end<file_sep>/lib/2_analyzing_languages/ch06_tracking_symbols/p16_monolithic_scope/cymbol_monolithic_node_classes.rb
require 'treetop'

module AnalyzingLanguages; module TrackingSymbols; module MonolithicScope; end; end; end
Treetop.load(File.dirname(__FILE__) + '/cymbol_monolithic')

require_relative 'variable_symbol'

module AnalyzingLanguages
  module TrackingSymbols
    module MonolithicScope

      # Module for Treetop
      module CymbolMonolithic
        class ::Treetop::Runtime::SyntaxNode
          def populate_symbols(symbol_table)
            return unless elements
            elements.each do |element|
              element.populate_symbols(symbol_table)
            end
          end

          private

          def location
            "line #{input.line_of(interval.first)}"
          end
        end

        class VarDeclaration < Treetop::Runtime::SyntaxNode
          def populate_symbols(symbol_table)
            var_type = symbol_table.resolve(type.name, location: location)
            if assignment
              assignment.populate_symbols(symbol_table)
            end
            symbol_table.define(VariableSymbol.new(var_name.name, var_type), location: location)
          end
        end

        class VarExpression < Treetop::Runtime::SyntaxNode
          def populate_symbols(symbol_table)
            symbol_table.resolve(type.name, location: location)
          end
        end

        class CymbolSymbol < Treetop::Runtime::SyntaxNode
          def name
            text_value
          end

          def populate_symbols(symbol_table)
            symbol_table.resolve(name, location: location)
          end
        end
      end

    end
  end
end<file_sep>/spec/1_getting_started/ch02_basic_parsing/p03_ll1_recursive_descent_parser/list_parser_spec.rb
require 'spec_helper'

require '1_getting_started/ch02_basic_parsing/p03_ll1_recursive_descent_parser/list_parser'
require '1_getting_started/ch02_basic_parsing/p02_ll1_recursive_descent_lexer/list_lexer'
require_relative 'list_parser_contract'

module GettingStarted
  module BasicParsing
    module LL1RecursiveDescentParser
      describe "Intergration:", ListParser, "and lexer" do
        let(:input) { "[ a, [ x, [ i, j ] ], b ]" }

        let(:lexer) { LL1RecursiveDescentLexer::ListLexer.new(input) }

        subject(:parser) { ListParser.new(lexer) }

        it "parses lists!" do
          expect(parser.list).to be == [ :a, [ :x, [ :i, :j ] ], :b ]
        end
      end

      describe ListParser do
        it_behaves_like "a ListParser"

        let(:tokens) {
          LL1RecursiveDescentLexer::Token.descriptions_to_tokens(token_descriptions)
        }
        let(:lexer) { tokens.each }

        subject(:parser) { ListParser.new(lexer) }
      end
    end
  end
end<file_sep>/spec/1_getting_started/ch02_basic_parsing/p02_ll1_recursive_descent_lexer/list_lexer_spec.rb
require 'spec_helper'

require '1_getting_started/ch02_basic_parsing/p02_ll1_recursive_descent_lexer/list_lexer'
require_relative 'list_lexer_contract'

module GettingStarted
  module BasicParsing
    module LL1RecursiveDescentLexer
      describe ListLexer do
        subject(:lexer) { ListLexer.new(input) }

        let(:collected_output) { [ ] }
        let(:output) {
          collected_output.map { |token| token.to_hash }
        }

        it_behaves_like "a ListLexer"
      end
    end
  end
end<file_sep>/spec/1_getting_started/ch02_basic_parsing/p03_ll1_recursive_descent_parser/list_parser_contract.rb
shared_examples_for "a ListParser" do
  # The example in the book (deliberately?) avoids this case, but it's an obvious
  # TDD bootstrapping case, and turned out to be not too difficult to implement
  # compared to the Java example
  context "empty list" do
    let(:token_descriptions) {
      [
        { lbrack: "[" },
        { rbrack: "]" },
        { eof:    nil }
      ]
    }

    specify {
      expect(parser.list).to be == [ ]
    }
  end

  context "list with a name" do
    let(:token_descriptions) {
      [
        { lbrack: "[" },
        { name:   "a" },
        { rbrack: "]" },
        { eof:    nil }
      ]
    }

    specify {
      expect(parser.list).to be == [ :a ]
    }
  end

  context "list with a multiple names" do
    let(:token_descriptions) {
      [
        { lbrack: "[" },
        { name:   "a" },
        { comma:  "," },
        { name:   "b" },
        { comma:  "," },
        { name:   "c" },
        { rbrack: "]" },
        { eof:    nil }
      ]
    }

    specify {
      expect(parser.list).to be == [ :a, :b, :c ]
    }
  end

  context "list of lists" do
    let(:token_descriptions) {
      [
        { lbrack: "[" },
          { name:   "a" },
          { comma:  "," },
            { lbrack: "[" },
              { name:   "x" },
              { comma:  "," },
              { name:   "y" },
            { rbrack: "]" },
          { comma:  "," },
          { name:   "b" },
        { rbrack: "]" },
        { eof:    nil }
      ]
    }

    specify {
      expect(parser.list).to be == [ :a, [ :x, :y ], :b ]
    }
  end

  context "empty list in a list" do
    let(:token_descriptions) {
      [
        { lbrack: "[" },
          { name:   "a" },
          { comma:  "," },
            { lbrack: "[" },
            { rbrack: "]" },
          { comma:  "," },
          { name:   "b" },
        { rbrack: "]" },
        { eof:    nil }
      ]
    }

    specify {
      expect(parser.list).to be == [ :a, [ ], :b ]
    }
  end

  context "list of list of lists to prove to Bobby my code really works" do
    let(:token_descriptions) {
      [
        { lbrack: "[" },
          { name:   "a" },
          { comma:  "," },
            { lbrack: "[" },
              { name:   "x" },
              { comma:  "," },
                { lbrack: "[" },
                  { name:   "i" },
                  { comma:  "," },
                  { name:   "j" },
                { rbrack: "]" },
            { rbrack: "]" },
          { comma:  "," },
          { name:   "b" },
        { rbrack: "]" },
        { eof:    nil }
      ]
    }

    specify {
      expect(parser.list).to be == [ :a, [ :x, [ :i, :j ] ], :b ]
    }
  end

  context "invalid input" do
    context "no list" do
      let(:token_descriptions) {
        [
          { eof: nil }
        ]
      }

      specify {
        expect { parser.list }.to raise_error(RecognitionError, "Expected :lbrack, found :eof")
      }
    end

    context "unclosed list" do
      let(:token_descriptions) {
        [
          { lbrack: "[" },
          { eof:    nil }
        ]
      }

      specify {
        expect { parser.list }.to raise_error(
          RecognitionError, "Expected :lbrack, :name or :rbrack, found :eof"
        )
      }
    end

    context "missing name before a comma" do
      let(:token_descriptions) {
        [
          { lbrack: "[" },
          { comma:  "," },
          { rbrack: "]" },
          { eof:    nil }
        ]
      }

      specify {
        expect { parser.list }.to raise_error(
          RecognitionError, "Expected :lbrack, :name or :rbrack, found :comma"
        )
      }
    end

    context "missing name after a comma" do
      let(:token_descriptions) {
        [
          { lbrack: "[" },
          { name:   "a" },
          { comma:  "," },
          { rbrack: "]" },
          { eof:    nil }
        ]
      }

      specify {
        expect { parser.list }.to raise_error(
          RecognitionError, "Expected :name or :lbrack, found :rbrack"
        )
      }
    end
  end
end<file_sep>/spec/1_getting_started/ch03_enhanced_parsing/p05_backtracking_parser/list_parser_with_parallel_assignment_spec.rb
require 'spec_helper'

require '1_getting_started/ch03_enhanced_parsing/p05_backtracking_parser/list_parser_with_parallel_assignment'
require '1_getting_started/ch03_enhanced_parsing/p05_backtracking_parser/lexer_replayable_lookahead'
require '1_getting_started/ch02_basic_parsing/p02_ll1_recursive_descent_lexer/list_lexer'
require_relative '../../ch02_basic_parsing/p03_ll1_recursive_descent_parser/list_parser_contract'
require_relative '../../ch02_basic_parsing/p04_llk_recursive_descent_parser/list_parser_assignment_contract'
require_relative 'list_parser_with_parallel_assignment_contract'

module GettingStarted
  module EnhancedParsing
    module BacktrackingParser
      describe "Intergration:", ListParserWithParallelAssignment, "and lexer" do
        let(:input) { "[ a ] = [ b ]" }

        let(:lexer) { BasicParsing::LL1RecursiveDescentLexer::ListLexer.new(input) }
        let(:lookahead) { LexerReplayableLookahead.new(lexer) }
        subject(:parser) { ListParserWithParallelAssignment.new(lookahead) }

        describe "statements" do
          context "a list" do
            let(:input) { "[ a = b, c, d ]" }

            example do
              expect(parser.stat).to be == [ { :a => :b }, :c, :d ]
            end
          end

          context "a parallel assignment" do
            let(:input) { "[ a, b ] = [ c, d ]" }

            example do
              expect(parser.stat).to be == { [ :a, :b ] => [ :c, :d ] }
            end
          end
        end
      end

      describe ListParserWithParallelAssignment do
        let(:tokens) {
          BasicParsing::LL1RecursiveDescentLexer::Token.descriptions_to_tokens(token_descriptions)
        }
        let(:lexer) { tokens.each }
        let(:lookahead) { LexerReplayableLookahead.new(lexer) }
        subject(:parser) { ListParserWithParallelAssignment.new(lookahead) }

        it_behaves_like "a ListParser"
        it_behaves_like "a ListParser with assignment"
        it_behaves_like "a ListParser with parallel assignment"
      end
    end
  end
end<file_sep>/spec/1_getting_started/ch03_enhanced_parsing/p05_backtracking_parser/list_parser_with_parallel_assignment_contract.rb
shared_examples_for "a ListParser with parallel assignment" do
  describe "statements" do
    context "a list" do
      let(:token_descriptions) {
        [
          { lbrack: "[" },
            { name:   "a" }, { equals: "=" }, { name:   "b" }, { comma:  "," },
            { name:   "c" }, { comma:  "," },
            { name:   "d" },
          { rbrack: "]" },
          { eof:    nil }
        ]
      }

      specify {
        expect(parser.list).to be == [ { :a => :b }, :c, :d ]
      }
    end

    context "a parallel assignment" do
      let(:token_descriptions) {
        [
          { lbrack: "[" },
            { name:   "a" }, { comma:  "," }, { name:   "b" },
          { rbrack: "]" },
          { equals: "=" },
          { lbrack: "[" },
            { name:   "c" }, { comma:  "," }, { name:   "d" },
          { rbrack: "]" },
          { eof:    nil }
        ]
      }

      specify {
        expect(parser.stat).to be == { [ :a, :b ] => [ :c, :d ] }
      }
    end

    context "invalid statement" do
      # "[ a ] b"
      let(:token_descriptions) {
        [
          { lbrack: "[" },
          { name:   "a" },
          { rbrack: "]" },
          { name:   "b" },
          { eof:    nil }
        ]
      }

      specify {
        expect {
          parser.stat
        }.to raise_error(
          NoViableAlternativeError, "Expecting <list> or <parallel assignment>"
        )
      }
    end
  end
end<file_sep>/lib/2_analyzing_languages/ch06_tracking_symbols/p17_nested_scopes/cymbol2_parser.rb
require 'treetop'

require_relative 'method_symbol'
require_relative '../p16_monolithic_scope/variable_symbol'

module AnalyzingLanguages; module TrackingSymbols; module NestedScopes; end; end; end
Treetop.load(File.dirname(__FILE__) + '/cymbol_nested')

module AnalyzingLanguages
  module TrackingSymbols
    module NestedScopes

      # Module for Treetop
      module CymbolNested
        class ::Treetop::Runtime::SyntaxNode
          def walk(symbol_table)
            return unless elements
            elements.each do |element|
              element.walk(symbol_table)
            end
          end

          private

          def location
            "line #{input.line_of(interval.first)}"
          end
        end

        class MethodDefinition < Treetop::Runtime::SyntaxNode
          def walk(symbol_table)
            method_type = symbol_table.resolve(type.name, location: location)
            symbol_table.define_as_scope(MethodSymbol.new(name.name, method_type), location: location)

            parameters.each do |parameter|
              parameter_type = symbol_table.resolve(parameter.type.name, location: location)
              symbol_table.define(
                MonolithicScope::VariableSymbol.new(parameter.name.name, parameter_type),
                location: location
              )
            end

            body.walk(symbol_table)
          end
        end

        class CymbolBlock < Treetop::Runtime::SyntaxNode
          def walk(symbol_table)
            symbol_table.push_scope
            elements.each do |element|
              element.walk(symbol_table)
            end
          end
        end

        class ParameterList < Treetop::Runtime::SyntaxNode
          def each(&block)
            parameters.each(&block)
          end

          # We can only handle one - as it happens the book example only uses one
          def parameters
            [ first_parameter ]
          end
        end

        class VarDeclaration < Treetop::Runtime::SyntaxNode
          def walk(symbol_table)
            var_type = symbol_table.resolve(type.name, location: location)
            if initialization
              initialization.walk(symbol_table)
            end
            symbol_table.define(
              MonolithicScope::VariableSymbol.new(var_name.name, var_type),
              location: location
            )
          end
        end

        class CymbolSymbol < Treetop::Runtime::SyntaxNode
          def name
            text_value
          end

          def walk(symbol_table)
            symbol_table.resolve(name, location: location)
          end
        end
      end

    end
  end
end<file_sep>/spec/1_getting_started/ch02_basic_parsing/p02_ll1_recursive_descent_lexer/token_spec.rb
require 'spec_helper'

require '1_getting_started/ch02_basic_parsing/p02_ll1_recursive_descent_lexer/token'

module GettingStarted
  module BasicParsing
    module LL1RecursiveDescentLexer
      describe Token do
        subject(:token) { Token.new(token_type: "token_value") }

        its(:type)    { should be == :token_type }
        its(:value)   { should be == "token_value" }
        its(:inspect) { should be == ":token_type" } # Only used for errors so far
        its(:to_hash) { should be == { token_type: "token_value" } }

        describe "#==" do
          it "knows equality" do
            expect(token).to be == Token.new(token_type: "token_value")
          end

          it "compares type" do
            expect(token).to_not be == Token.new(wrong_token_type: "token_value")
          end

          it "compares value" do
            expect(token).to_not be == Token.new(token_type: "wrong_token_value")
          end
        end

        describe ".descriptions_to_tokens" do
          example do
            expect(
              Token.descriptions_to_tokens([{ a: "one" }, { b: "two" }])
            ).to be == [
              Token.new(a: "one"), Token.new(b: "two")
            ]
          end
        end
      end
    end
  end
end<file_sep>/spec/2_analyzing_languages/ch06_tracking_symbols/p17_nested_scopes/symbol_table_spec.rb
require 'spec_helper'

require '2_analyzing_languages/ch06_tracking_symbols/p17_nested_scopes/symbol_table'
require '2_analyzing_languages/ch06_tracking_symbols/p16_monolithic_scope/language_symbol'

module AnalyzingLanguages
  module TrackingSymbols
    module NestedScopes
      # Easy access to the existing LanguageSymbol
      include MonolithicScope

      describe SymbolTable do
        subject(:symbol_table) { SymbolTable.new }

        context "single scope" do
          describe "#define" do
            it "ignores metadata" do
              expect {
                symbol_table.define(LanguageSymbol.new(:foo), unused: "stuff")
              }.to_not raise_error(ArgumentError)
            end
          end

          describe "#resolve" do
            context "undefined symbol" do
              specify {
                expect {
                  symbol_table.resolve("foo")
                }.to raise_error(RuntimeError, "Unknown symbol: foo")
              }

              it "ignores metadata" do
                expect {
                  symbol_table.resolve("foo", unused: "stuff")
                }.to_not raise_error(ArgumentError)
              end
            end

            context "defined symbol" do
              let(:symbol) { LanguageSymbol.new(:foo) }

              before(:each) do
                symbol_table.define(symbol)
              end

              specify {
                expect(symbol_table.resolve("foo")).to equal(symbol)
              }

              specify {
                expect(symbol_table.resolve(:foo)).to equal(symbol)
              }
            end
          end

          describe "#to_s" do
            before(:each) do
              symbol_table.define(LanguageSymbol.new(:foo))
              symbol_table.define(LanguageSymbol.new(:bar))
              symbol_table.define(LanguageSymbol.new(:baz))
            end

            it "outputs in alphabetic order" do
              expect(symbol_table.to_s).to be ==
                "symbols: {bar=bar, baz=baz, foo=foo}"
            end
          end
        end

        describe "nested scopes" do
          let(:symbol) { LanguageSymbol.new(:foo) }

          before(:each) do
            symbol_table.define(symbol)
            symbol_table.push_scope
          end

          it "gives access to the higher scope" do
            expect(symbol_table.resolve(:foo)).to equal(symbol)
          end

          describe "overriding" do
            let(:new_foo) { LanguageSymbol.new(:foo) }

            before(:each) do
              symbol_table.define(new_foo)
            end

            it "lets you override a symbol" do
              expect(symbol_table.resolve(:foo)).to equal(new_foo)
            end

            it "lets you pop the scope tree to get the old symbol back" do
              symbol_table.pop_scope
              expect(symbol_table.resolve(:foo)).to equal(symbol)
            end

            it "updates #to_s" do
              pending
            end
          end
        end

        describe "scope symbols" do
          # Using a mock here bleeds some implementation details of the aggregate,
          # we should probably remove this if we get a suitable integration test
          let(:scope_symbol) {
            mock("ScopeSymbol", name: :bar, wrap: nil, resolve: :trust_passed_through)
          }

          it "defines the symbol" do
            symbol_table.define_as_scope(scope_symbol)
            expect(symbol_table.resolve(:bar)).to equal(:trust_passed_through)
          end

          it "ignores metadata" do
            expect {
              symbol_table.define_as_scope(scope_symbol, unused: "stuff")
            }.to_not raise_error(ArgumentError)
          end

          it "pushes the symbol as a scope" do
            symbol_table.define(LanguageSymbol.new(:moo))

            scope_symbol.should_receive(:wrap) do |parent_scope|
              expect(parent_scope.resolve(:moo))
            end

            symbol_table.define_as_scope(scope_symbol)
          end

          it "uses the pushed scope" do
            symbol_table.define_as_scope(scope_symbol)
            scope_symbol.should_receive(:resolve).with(:baz)
            symbol_table.resolve(:baz)
          end
        end
      end
    end
  end
end<file_sep>/spec/1_getting_started/ch03_enhanced_parsing/p05_backtracking_parser/lexer_replayable_lookahead_spec.rb
require 'spec_helper'

require '1_getting_started/ch02_basic_parsing/p02_ll1_recursive_descent_lexer/list_lexer'
require '1_getting_started/ch03_enhanced_parsing/p05_backtracking_parser/lexer_replayable_lookahead'
require_relative '../../ch02_basic_parsing/p02_ll1_recursive_descent_lexer/list_lexer_contract'

module GettingStarted
  module EnhancedParsing
    module BacktrackingParser
      shared_examples_for "LexerReplayableLookahead#peek" do
        example do
          expect(lexer.peek.value).to be == "["
        end

        example do
          expect(lexer.peek(1).value).to be == "["
        end

        example do
          expect(lexer.peek(2).value).to be == "a"
        end

        example do
          expect(lexer.peek(5).value).to be == "]"
        end

        example do
          expect(lexer.peek(6).to_hash).to be == { eof: nil }
        end

        example do
          expect { lexer.peek(7) }.to raise_error(StopIteration)
        end
      end

      shared_examples_for "LexerReplayableLookahead#speculate" do
        specify {
          lexer.speculate do
            expect(lexer.peek.value).to be == "["
            expect(lexer.next.value).to be == "["
            expect(lexer.next.value).to be == "a"
          end
        }

        specify {
          lexer.next
          expect {
            lexer.speculate do
              lexer.next
            end
          }.to_not change { lexer.peek.value }
        }

        specify {
          expect(lexer.peek.value).to be == "["
          lexer.speculate do
            lexer.next
            expect(lexer.peek.value).to be == "a"
            lexer.speculate do
              lexer.next
              expect(lexer.peek.value).to be == "="
            end
            expect(lexer.peek.value).to be == "a"
          end
          expect(lexer.peek.value).to be == "["
        }

        describe "#if_speculating" do
          specify {
            should_not_receive(:was_speculating)
            lexer.if_speculating do
              was_speculating
            end
          }

          specify {
            should_receive(:was_speculating)
            lexer.speculate do
              lexer.if_speculating do
                was_speculating
              end
            end
          }

          specify {
            should_receive(:was_speculating)
            lexer.speculate do
              lexer.speculate do
                # nothing here
              end
              lexer.if_speculating do
                was_speculating
              end
            end
          }
        end

        describe "error handling" do
          it "(re-)raises errors" do
            expect {
              lexer.speculate do
                raise "re-raised error"
              end
            }.to raise_error(RuntimeError, "re-raised error")
          end

          it "is not disturbed by errors" do
            lexer.speculate do
              lexer.next
              lexer.next
              raise "ignored error"
            end rescue nil

            expect(lexer.next.value).to be == "["
            expect(lexer.next.value).to be == "a"
          end
        end
      end

      describe LexerReplayableLookahead do
        describe "lookahead" do
          let(:tokens) {
            BasicParsing::LL1RecursiveDescentLexer::Token.descriptions_to_tokens(
              [
                { lbrack: "[" },
                { name:   "a" },
                { equals: "=" },
                { name:   "b" },
                { rbrack: "]" },
                { eof:    nil }
              ]
            )
          }

          let(:underlying_lexer) { tokens.each }

          subject(:lexer) { LexerReplayableLookahead.new(underlying_lexer) }

          describe "#peek" do
            it_behaves_like "LexerReplayableLookahead#peek"
          end

          describe "#speculate" do
            it_behaves_like "LexerReplayableLookahead#speculate"
          end
        end

        context "wrapping a ListLexer" do
          let(:input) { "[ a = b ]" }
          let(:underlying_lexer) { BasicParsing::LL1RecursiveDescentLexer::ListLexer.new(input) }
          subject(:lexer) { LexerReplayableLookahead.new(underlying_lexer) }

          let(:collected_output) { [ ] }
          let(:output) {
            collected_output.map { |token| token.to_hash }
          }

          it_behaves_like "a ListLexer"

          describe "#peek" do
            it_behaves_like "LexerReplayableLookahead#peek"
          end

          describe "#speculate" do
            it_behaves_like "LexerReplayableLookahead#speculate"
          end
        end
      end
    end
  end
end<file_sep>/spec/2_analyzing_languages/ch06_tracking_symbols/p16_monolithic_scope/symbol_table_logger_spec.rb
require 'spec_helper'

require '2_analyzing_languages/ch06_tracking_symbols/p16_monolithic_scope/symbol_table'
require '2_analyzing_languages/ch06_tracking_symbols/p16_monolithic_scope/symbol_table_logger'

module AnalyzingLanguages
  module TrackingSymbols
    module MonolithicScope
      describe SymbolTableLogger do
        let(:symbol_table) { mock(SymbolTable, define: nil, resolve: :resolved_symbol) }

        let(:output_io) { StringIO.new }
        def output
          output_io.rewind
          output_io.read.chomp
        end

        subject(:logger) { SymbolTableLogger.new(symbol_table, output_io) }

        describe "#resolve" do
          specify {
            symbol_table.should_receive(:resolve).with(:symbol_name)
            logger.resolve(:symbol_name).should be == :resolved_symbol
          }

          specify {
            logger.resolve(:symbol_name, location: "line 1")
            expect(output).to be == "line 1: ref resolved_symbol"
          }
        end

        describe "#define" do
          specify {
            logger.define(:symbol, location: "line 1")
            expect(output).to be == "line 1: def symbol"
          }

          specify {
            symbol_table.should_receive(:define).with(:symbol)
            logger.define(:symbol)
          }
        end
      end
    end
  end
end<file_sep>/lib/1_getting_started/ch02_basic_parsing/p02_ll1_recursive_descent_lexer/list_lexer.rb
require_relative 'token'

module GettingStarted
  module BasicParsing
    module LL1RecursiveDescentLexer

      # Implements Pattern 2: LL(1) Recursive-Descent Lexer
      #
      # Note that there are two main differences from the example implementation:
      #
      # * I don't use a Token class (which is just a dumb struct in the Java
      #   example), this can be more easily implemented in Ruby with simple hashes.
      #
      # * I set about solving this using standard Ruby iterators with blocks,
      #   which makes some bits more idiomatic and some bits more complex. The
      #   Java example looks ahead to the next character, whereas with blocks
      #   we have to `redo` if we detect we've entered a different token type.
      #   Because of this, the code has ended up with a State implementation,
      #   albeit a slightly wacky one.
      #
      # * There's the minor difference that I forgot to allow parsing capital
      #   letters in names. Oops.
      #
      # ERRATA: I just realised that the whole point of LL(1) is to *look ahead*
      # and therefore this implementation using blocks misses the point. Sight.
      # Oh well, from the outside, it's behaviourally equivalent, ie passes all
      # the same tests it would do if we weren't `redo`ing enumerator blocks.
      # I'm not going to refactor it unless I have to.
      class ListLexer
        def initialize(input)
          @input = input

          # Hack to work around the fact I didn't implement this as a pull system
          @tokens = tokenize_all.each
        end

        def peek
          @tokens.peek
        end

        def next
          @tokens.next
        end

        private

        def tokenize_all
          [ ].tap do |tokens|
            tokenize do |token|
              tokens << token
            end
          end
        end

        def tokenize(&block)
          switch_to_mode(:normal)

          @input.chars.each do |char|
            match_result =
              catch :state_changed do
                match(char, &block)
              end

            redo if match_result == :state_changed
          end

          finish(&block)
        end

        def match_normal(char, &block)
          case char
          when " ", "\t", "\n"
          when "["
            yield(Token.new(lbrack: char))
          when ","
            yield(Token.new(comma: char))
          when "="
            yield(Token.new(equals: char))
          when "]"
            yield(Token.new(rbrack: char))
          when "a".."z"
            switch_to_mode(:name)
            throw(:state_changed, :state_changed)
          else
            raise ArgumentError.new("Invalid character: #{char}")
          end
        end

        def match_name(char, &block)
          case char
          when "a".."z"
            @name << char
          else
            yield(Token.new(name: @name))
            switch_to_mode(:normal)
            throw(:state_changed, :state_changed)
          end
        end

        def finish_normal(&block)
          yield(Token.new(eof: nil))
        end

        def finish_name(&block)
          yield(Token.new(name: @name))
          yield(Token.new(eof: nil))
        end

        # This is an insane way to implement the State pattern, but I've left it in purely
        # because it's so ridiculous, and this is only the first pattern in the book
        def switch_to_mode(mode)
          @name = ""

          singleton_class.send(:alias_method, :match, :"match_#{mode}")
          singleton_class.send(:alias_method, :finish, :"finish_#{mode}")
        end
      end

    end
  end
end<file_sep>/spec/1_getting_started/ch03_enhanced_parsing/p06_memoizing_parser/memoizing_list_parser_spec.rb
require 'spec_helper'

require '1_getting_started/ch03_enhanced_parsing/p06_memoizing_parser/memoizing_list_parser'
require '1_getting_started/ch03_enhanced_parsing/p05_backtracking_parser/lexer_replayable_lookahead'
require '1_getting_started/ch02_basic_parsing/p02_ll1_recursive_descent_lexer/list_lexer'
require_relative '../../ch02_basic_parsing/p03_ll1_recursive_descent_parser/list_parser_contract'
require_relative '../../ch02_basic_parsing/p04_llk_recursive_descent_parser/list_parser_assignment_contract'
require_relative '../p05_backtracking_parser/list_parser_with_parallel_assignment_contract'

module GettingStarted
  module EnhancedParsing
    module MemoizingParser

      # The spec for this is identical to the backtracking parser, only the
      # implementation differs. I've decided not to write any specs to prove
      # eg performance or memory usage, as for my purposes I only want to
      # understand what is going on, not be able to implement these efficiently
      # myself.
      describe MemoizingListParser do
        let(:tokens) {
          BasicParsing::LL1RecursiveDescentLexer::Token.descriptions_to_tokens(token_descriptions)
        }
        let(:lexer) { tokens.each }
        let(:lookahead) { BacktrackingParser::LexerReplayableLookahead.new(lexer) }
        subject(:parser) { MemoizingListParser.new(lookahead) }

        it_behaves_like "a ListParser"
        it_behaves_like "a ListParser with assignment"
        it_behaves_like "a ListParser with parallel assignment"
      end

    end
  end
end
<file_sep>/lib/1_getting_started/ch02_basic_parsing/p03_ll1_recursive_descent_parser/list_parser.rb
require '1_getting_started/errors'

module GettingStarted
  module BasicParsing
    module LL1RecursiveDescentParser

      # Implements Pattern 3: LL(1) Recursive-Descent Parser
      #
      # Two major differences from the book examples:
      #
      # * We turn the tokens into a Ruby array (ie we do something with them
      #   other than detect errors). This is pretty easy as we just need a
      #   Collecting Parameter.
      #
      # * We handle the empty list case. I didn't notice this was excluded
      #   from the grammar, but the discussion on p40 hints at why. Turns out
      #   even in this simple case it's much harder to parse optional elements,
      #   as it means we have to treat the first element of a list differently
      #   (because "[ ]" is valid but "[ a, ]" is not, which is what a naive
      #   parser gives you).
      class ListParser
        def initialize(lexer)
          @lexer = lexer
          @lookahead = @lexer.next
        end

        def list
          [ ].tap do |collected_list|
            match(:lbrack)
            elements(collected_list)
            match(:rbrack)
          end
        end

        def elements(collected_list)
          first_element(collected_list)
          while @lookahead.type == :comma
            match(:comma)
            element(collected_list)
          end
        end

        def first_element(collected_list)
          case @lookahead.type
          when :name, :lbrack
            element(collected_list)
          when :rbrack
            return
          else
            raise RecognitionError.new(
              "Expected :lbrack, :name or :rbrack, found #{@lookahead.type.inspect}"
            )
          end
        end

        def element(collected_list)
          case @lookahead.type
          when :name
            collected_list << @lookahead.value.to_sym
            match(:name)
          when :lbrack
            collected_list << list
          else
            raise RecognitionError.new(
              "Expected :name or :lbrack, found #{@lookahead.type.inspect}"
            )
          end
        end

        private

        def match(expected_type)
          if @lookahead.type == expected_type
            consume
          else
            raise RecognitionError.new(
              "Expected #{expected_type.inspect}, found #{@lookahead.type.inspect}"
            )
          end
        end

        def consume
          @lookahead = @lexer.next
        end
      end

    end
  end
end<file_sep>/spec/spec_helper.rb
require 'ap'

require 'fakefs/spec_helpers'
require 'lstrip-on-steroids'

RSpec.configure do |config|
  config.filter_run(focus: true)
  config.run_all_when_everything_filtered = true
end
<file_sep>/spec/2_analyzing_languages/ch06_tracking_symbols/p16_monolithic_scope/cymbol_spec.rb
require 'spec_helper'

require '2_analyzing_languages/ch06_tracking_symbols/p16_monolithic_scope/cymbol'
require '2_analyzing_languages/ch06_tracking_symbols/p16_monolithic_scope/symbol_table'
require '2_analyzing_languages/ch06_tracking_symbols/p16_monolithic_scope/symbol_table_logger'
require '2_analyzing_languages/ch06_tracking_symbols/p16_monolithic_scope/language_symbol'
require '2_analyzing_languages/ch06_tracking_symbols/p16_monolithic_scope/variable_symbol'

module AnalyzingLanguages
  module TrackingSymbols
    module MonolithicScope
      describe Cymbol do
        let(:symbol_table) { mock(SymbolTable, resolve: nil, define: nil) }

        let(:cymbol) { Cymbol.new(symbol_table) }

        context "an uninitialised variable" do
          let(:source) { "float j;" }

          specify {
            symbol_table.should_receive(:resolve).with("float", hash_including(location: "line 1"))
            cymbol.parse(source)
          }
        end

        context "an initialized variable" do
          let(:source) { "int i = 9;" }

          let(:int) { LanguageSymbol.new(:int) }

          before(:each) do
            symbol_table.stub(resolve: int)
          end

          specify {
            symbol_table.should_receive(:resolve).with(
              "int", hash_including(location: "line 1")
            ).ordered
            symbol_table.should_receive(:define).with(
              VariableSymbol.new(:i, LanguageSymbol.new(:int)),
              hash_including(location: "line 1")
            ).ordered

            cymbol.parse(source)
          }
        end

        context "a variable initialized with an expression" do
          let(:source) { "int k = i + 2;" }

          let(:float) { LanguageSymbol.new(:float) }

          before(:each) do
            symbol_table.stub(resolve: float)
          end

          specify {
            symbol_table.should_receive(:resolve).with(
              "int", hash_including(location: "line 1")
            ).ordered
            symbol_table.should_receive(:resolve).with(
              "i", hash_including(location: "line 1")
            ).ordered
            symbol_table.should_receive(:define).with(
              VariableSymbol.new(:k, float), hash_including(location: "line 1")
            ).ordered

            cymbol.parse(source)
          }
        end
      end

      describe Cymbol, "from the book" do
        let(:symbol_table) { SymbolTable.new }
        subject(:cymbol) { Cymbol.new(symbol_table_for_parser) }

        let(:source) {
          -%{
            int i = 9;
            float j;
            int k = i+2;
          }
        }

        before(:each) do
          # Bypass any logging
          symbol_table.define(LanguageSymbol.new(:int))
          symbol_table.define(LanguageSymbol.new(:float))
        end

        context "but with no logging" do
          let(:symbol_table_for_parser) { symbol_table }

          specify {
            expect { cymbol.parse(source) }.to_not raise_error
          }
        end

        context "with logging as described" do
          let(:output_io) { StringIO.new }
          def output
            output_io.rewind
            output_io.read.chomp
          end

          let(:symbol_table_for_parser) { SymbolTableLogger.new(symbol_table, output_io) }

          # Changed slightly because to produce the same output as the book
          # requires an inordinate increase in complexity in the code
          #
          # Test output from the book:
          #   line 1: ref int
          #   line 1: def i
          #   line 2: ref float
          #   line 2: def j
          #   line 3: ref int
          #   line 3: ref to <i:int>
          #   line 3: def k
          specify {
            cymbol.parse(source)

            expect(output.to_s).to be == -%{
              line 1: ref int
              line 1: def <i:int>
              line 2: ref float
              line 2: def <j:float>
              line 3: ref int
              line 3: ref <i:int>
              line 3: def <k:int>
            }
          }

          # We don't write this to the output just to avoid having to pass an IO in to Cymbol
          specify {
            cymbol.parse(source)

            expect(symbol_table.to_s).to be ==
              "globals: {float=float, i=<i:int>, int=int, j=<j:float>, k=<k:int>}"
          }
        end
      end
    end
  end
end<file_sep>/lib/1_getting_started/ch03_enhanced_parsing/p05_backtracking_parser/lexer_replayable_lookahead.rb
require_relative 'replayable_buffer'

module GettingStarted
  module EnhancedParsing
    module BacktrackingParser

      class LexerReplayableLookahead
        def initialize(lexer)
          @lexer        = lexer
          @lexer_empty  = false
          @buffer       = ReplayableBuffer.new
          @speculating  = [ false ]
        end

        def peek(lookahead_distance = 1)
          fill_buffer(lookahead_distance)
          token_or_stop_iteration(@buffer[lookahead_distance - 1])
        end

        def next
          fill_buffer(1)
          consume
        end

        def speculate(&block)
          @speculating.push(true)
          @buffer.mark
          yield
        ensure
          @speculating.pop
          @buffer.release
        end

        def if_speculating(&block)
          yield if @speculating.last
        end

        private

        def consume
          token_or_stop_iteration(@buffer.shift)
        end

        def fill_buffer(required_size)
          (required_size - @buffer.size).times do
            harvest
          end
        end

        def harvest
          @buffer << @lexer.next
        rescue StopIteration
          def self.harvest
            # NOOP
          end

          def self.token_or_stop_iteration(token)
            raise StopIteration if token.nil?
            token
          end
        end

        def token_or_stop_iteration(token)
          token
        end
      end

    end
  end
end
<file_sep>/spec/2_analyzing_languages/ch06_tracking_symbols/p17_nested_scopes/cymbol2_spec.rb
require 'spec_helper'

require '2_analyzing_languages/ch06_tracking_symbols/p17_nested_scopes/cymbol2'
require '2_analyzing_languages/ch06_tracking_symbols/p17_nested_scopes/symbol_table'
require '2_analyzing_languages/ch06_tracking_symbols/p16_monolithic_scope/symbol_table_logger'
require '2_analyzing_languages/ch06_tracking_symbols/p16_monolithic_scope/language_symbol'
# require '2_analyzing_languages/ch06_tracking_symbols/p16_monolithic_scope/variable_symbol'

module AnalyzingLanguages
  module TrackingSymbols
    module NestedScopes
      include MonolithicScope

      describe Cymbol2, "from the book" do
        let(:symbol_table) { SymbolTable.new }
        let(:symbol_table_logger) { SymbolTableLogger.new(symbol_table, output_io) }

        let(:output_io) { StringIO.new }
        def output
          output_io.rewind
          output_io.read.chomp
        end

        subject(:cymbol) { Cymbol2.new }

        before(:each) do
          # Bypass any logging
          symbol_table.define(LanguageSymbol.new(:int))
          symbol_table.define(LanguageSymbol.new(:float))
        end

        let(:source) {
          -%{
            // global scope
            float f(int x)
            {
              float i;
              { float z = x+y; i = z; }
            }
          }
        }

        specify {
          cymbol.parse(source).walk(symbol_table_logger)

          # Changed a bit: I made it as close as I felt was worth it,
          # but some stuff is quite hard (eg assignment, unless we start)
          # extending the symbol table for this
          expect(output.to_s).to be == -%{
            line 2: ref float
            line 2: defscope method<f:float>
            line 2: ref int
            line 2: def <x:int>
            line 4: ref float
            line 4: def <i:float>
            line 5: ref float
            line 5: ref <x:int>
            line 5: ref [failed] y
            line 5: def <z:float>
            line 5: ref <i:float>
            line 5: ref <z:float>
          }
        }
      end
    end
  end
end<file_sep>/README.markdown
# Language Implementation Patterns

My workthrough of the book [Language Implementation Patterns][lipbook] by [<NAME>][parrt].

[lipbook]: http://pragprog.com/book/tpdsl/language-implementation-patterns
[parrt]: http://www.cs.usfca.edu/~parrt/
<file_sep>/lib/2_analyzing_languages/ch06_tracking_symbols/p17_nested_scopes/method_symbol.rb
require_relative 'symbol_table'

module AnalyzingLanguages
  module TrackingSymbols
    module NestedScopes
      class MethodSymbol
        include Scope

        attr_reader :name, :type

        def initialize(name, type)
          @name = name.to_sym
          @type = type

          @symbols = { } # Hackety hackety hack
        end

        def ==(other)
          other.is_a?(MethodSymbol) &&
            @name == other.name &&
            @type == other.type
        end

        def wrap(parent_scope)
          @parent_scope = parent_scope
        end

        def to_s
          "method<#{@name}:#{@type}>"
        end
      end
    end
  end
end