Class: Parslet::Atoms::Base

Inherits:
Object
  • Object
show all
Includes:
CanFlatten, DSL, Precedence
Defined in:
lib/parslet/atoms/base.rb,
lib/parslet/convenience.rb,
lib/parslet/atoms/visitor.rb

Overview

Base class for all parslets, handles orchestration of calls and implements a lot of the operator and chaining methods.

Also see Parslet::Atoms::DSL chaining parslet atoms together.

Direct Known Subclasses

Alternative, Entity, Lookahead, Named, Re, Repetition, Sequence, Str, Parser

Constant Summary

Constants included from Precedence

Precedence::ALTERNATE, Precedence::BASE, Precedence::LOOKAHEAD, Precedence::OUTER, Precedence::REPETITION, Precedence::SEQUENCE

Class Method Summary collapse

Instance Method Summary collapse

Methods included from CanFlatten

#flatten, #flatten_repetition, #flatten_sequence, #foldl, #merge_fold, #warn_about_duplicate_keys

Methods included from DSL

#>>, #absent?, #as, #maybe, #present?, #repeat, #|

Class Method Details

.precedence(prec) ⇒ Object

Debug printing - in Treetop syntax.



102
103
104
# File 'lib/parslet/atoms/base.rb', line 102

def self.precedence(prec)
  define_method(:precedence) { prec }
end

Instance Method Details

#accept(visitor) ⇒ Object

Raises:

  • (NotImplementedError)


7
8
9
# File 'lib/parslet/atoms/visitor.rb', line 7

def accept(visitor)
  raise NotImplementedError, "No #accept method on #{self.class.name}."
end

#apply(source, context) ⇒ Object


Calls the #try method of this parslet. In case of a parse error, apply leaves the source in the state it was before the attempt. +++



79
80
81
82
83
84
85
86
87
88
89
90
# File 'lib/parslet/atoms/base.rb', line 79

def apply(source, context)
  old_pos = source.pos

  #success, value = result = context.try_with_cache(self, source)
  success, value = result = try(source, context)

  return result if success

  # We only reach this point if the parse has failed. Rewind the input.
  source.pos = old_pos
  return result
end

#inspectObject



113
114
115
# File 'lib/parslet/atoms/base.rb', line 113

def inspect
  to_s(OUTER)
end

#parse(io, options = {}) ⇒ Hash, ...

Given a string or an IO object, this will attempt a parse of its contents and return a result. If the parse fails, a Parslet::ParseFailed exception will be thrown.

Parameters:

  • io (String, Source)

    input for the parse process

  • options (Hash) (defaults to: {})

    a customizable set of options

Options Hash (options):

  • :reporter (Parslet::ErrorReporter)

    error reporter to use, defaults to Parslet::ErrorReporter::Tree

  • :prefix (Boolean)

    Should a prefix match be accepted? (default: false)

Returns:

  • (Hash, Array, Parslet::Slice)

    PORO (Plain old Ruby object) result tree



23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
# File 'lib/parslet/atoms/base.rb', line 23

def parse(io, options={})
  source = io.respond_to?(:line_and_column) ? 
    io : 
    Parslet::Source.new(io)

  # Try to cheat. Assuming that we'll be able to parse the input, don't 
  # run error reporting code. 
  success, value = setup_and_apply(source, nil)
  
  # If we didn't succeed the parse, raise an exception for the user. 
  # Stack trace will be off, but the error tree should explain the reason
  # it failed.
  unless success
    # Cheating has not paid off. Now pay the cost: Rerun the parse,
    # gathering error information in the process.
    reporter = options[:reporter] || Parslet::ErrorReporter::Tree.new
    success, value = setup_and_apply(source, reporter)
    
    fail "Assertion failed: success was true when parsing with reporter" \
      if success
    
    # Value is a Parslet::Cause, which can be turned into an exception:
    value.raise
    
    fail "NEVER REACHED"
  end
  
  # assert: success is true
  
  # If we haven't consumed the input, then the pattern doesn't match. Try
  # to provide a good error message (even asking down below)
  if !options[:prefix] && source.chars_left > 0
    old_pos = source.pos
    Parslet::Cause.format(
      source, old_pos, 
      "Don't know what to do with #{source.consume(10).to_s.inspect}").
      raise(Parslet::UnconsumedInput)
  end
  
  return flatten(value)
end

#parse_with_debug(str, opts = {}) ⇒ Object

Packages the common idiom

begin
  tree = parser.parse('something')
rescue Parslet::ParseFailed => error
  puts parser.cause.ascii_tree
end

into a convenient method.

Usage:

require 'parslet'
require 'parslet/convenience'

class FooParser < Parslet::Parser
  rule(:foo) { str('foo') }
  root(:foo)
end

FooParser.new.parse_with_debug('bar')

See Also:



27
28
29
30
31
32
33
# File 'lib/parslet/convenience.rb', line 27

def parse_with_debug str, opts={}
  parse str, opts
rescue Parslet::UnconsumedInput => error
  puts error
rescue Parslet::ParseFailed => error
  puts error.cause.ascii_tree
end

#setup_and_apply(source, error_reporter) ⇒ <Boolean, Object>

Creates a context for parsing and applies the current atom to the input. Returns the parse result.

Returns:

  • (<Boolean, Object>)

    Result of the parse. If the first member is true, the parse has succeeded.



70
71
72
73
# File 'lib/parslet/atoms/base.rb', line 70

def setup_and_apply(source, error_reporter)
  context = Parslet::Atoms::Context.new(error_reporter)
  apply(source, context)
end

#to_s(outer_prec = OUTER) ⇒ Object



106
107
108
109
110
111
112
# File 'lib/parslet/atoms/base.rb', line 106

def to_s(outer_prec=OUTER)
  if outer_prec < precedence
    "("+to_s_inner(precedence)+")"
  else
    to_s_inner(precedence)
  end
end

#try(source, context) ⇒ Object

Override this in your Atoms::Base subclasses to implement parsing behaviour.

Raises:

  • (NotImplementedError)


95
96
97
98
# File 'lib/parslet/atoms/base.rb', line 95

def try(source, context)
  raise NotImplementedError, \
    "Atoms::Base doesn't have behaviour, please implement #try(source, context)."
end