Class: YABFI::Consumer

Inherits:
Object
  • Object
show all
Defined in:
lib/yabfi/consumer.rb

Overview

This class provides generic methods to declaratively consume an Array of input.

Direct Known Subclasses

Lexer

Constant Summary collapse

Unsatisfied =

Raised when the expected input does not match the given input.

Class.new(BaseError)
EndOfInput =

Raised when the end of input is reached.

Class.new(BaseError)

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(tokens) ⇒ Consumer

Create a new Consumer.

Parameters:

  • tokens (Array<Object>)

    consumer input.



17
18
19
# File 'lib/yabfi/consumer.rb', line 17

def initialize(tokens)
  @tokens = tokens
end

Instance Attribute Details

#tokensObject (readonly)



12
13
14
# File 'lib/yabfi/consumer.rb', line 12

def tokens
  @tokens
end

Instance Method Details

#advanceObject

Look at the next character of input and advance the parse by one element.

Returns:

  • (Object)

    the next token in the parse.

Raises:



55
56
57
# File 'lib/yabfi/consumer.rb', line 55

def advance
  peek.tap { seek(consume_index.succ) }
end

#attemptObject?

Try a block of code, resetting the parse state on failure.

Returns:

  • (Object, nil)

    the result of the block, or nil if the block fails.



99
100
101
102
103
104
105
# File 'lib/yabfi/consumer.rb', line 99

def attempt
  idx = consume_index
  yield
rescue
  seek(idx)
  nil
end

#consume_indexInteger

Lazily evaluated _conumer_idx instnace variable.

Returns:

  • (Integer)

    of the current index of the input consumption.



24
25
26
# File 'lib/yabfi/consumer.rb', line 24

def consume_index
  @consume_index ||= 0
end

#end_of_input?true, false

Test if the parse has completed.

Returns:

  • (true, false)

    whether or not the input has been fully consumed.



38
39
40
# File 'lib/yabfi/consumer.rb', line 38

def end_of_input?
  consume_index >= tokens.length
end

#eq(expected) ⇒ Object

Declare that the next token in the stream should be the given token.

Parameters:

  • expected (Object)

    next expected object in the parse.

Returns:

  • (Object)

    the satisfied token.

Raises:

  • (EndOfInput)

    if the parse has completed.

  • (Unsatisfied)

    if the token does not equal the argument.



82
83
84
# File 'lib/yabfi/consumer.rb', line 82

def eq(expected)
  satisfy("Expected #{expected}, got:") { |tok| tok == expected }
end

#manyObject?

Consume 0 or more occurrences of the given block.

Returns:

  • (Object, nil)

    the result of the block, or nil if the block fails.



110
111
112
113
114
115
116
117
118
119
120
# File 'lib/yabfi/consumer.rb', line 110

def many
  idx = consume_index
  results = []
  loop do
    idx = consume_index
    results << yield
  end
rescue
  seek(idx)
  results
end

#many_one(&block) ⇒ Object?

Consume 1 or more occurrences of the given block.

Returns:

  • (Object, nil)

    the result of the block, or nil if the block fails.



125
126
127
128
129
# File 'lib/yabfi/consumer.rb', line 125

def many_one(&block)
  many(&block).tap do |results|
    fail Unsatisfied, '#many_one: got no results' if results.empty?
  end
end

#one_of(*toks) ⇒ Object

Declare that the next token in the stream should match the given token.

Parameters:

  • toks (Array<Object>)

    list of objects that could match.

Returns:

  • (Object)

    the satisfied token.

Raises:



92
93
94
# File 'lib/yabfi/consumer.rb', line 92

def one_of(*toks)
  satisfy("Expected one of #{toks}, got:") { |tok| toks.include?(tok) }
end

#peekObject

Look at the next character of input without advancing the consumption.

Returns:

  • (Object)

    the next token in the parse.

Raises:



46
47
48
49
# File 'lib/yabfi/consumer.rb', line 46

def peek
  fail EndOfInput, '#peek: end of input' if end_of_input?
  tokens[consume_index]
end

#satisfy(message = nil) {|token| ... } ⇒ Object

Given an optional error message and predicate, test if the next token in the parse satisfies the predicate.

Parameters:

  • message (String) (defaults to: nil)

    error message to throw when the condition is not satisfied.

Yield Parameters:

  • token (Object)

    the token to test.

Returns:

  • (Object)

    the satisfied token.

Raises:



68
69
70
71
72
73
74
# File 'lib/yabfi/consumer.rb', line 68

def satisfy(message = nil)
  message ||= '#satisfy:'
  tok = peek
  fail Unsatisfied, "#{message} '#{tok}'" unless yield(tok)
  seek(consume_index.succ)
  tok
end

#seek(n) ⇒ Object

Seek to the given posision.

Parameters:

  • n (Integer)

    the integer to seek to.



31
32
33
# File 'lib/yabfi/consumer.rb', line 31

def seek(n)
  @consume_index = n
end