Class: Succubus::Grammar

Inherits:
Object
  • Object
show all
Defined in:
lib/succubus/grammar.rb

Overview

Class which contains the rules describing a grammar from which to generate random strings.

Grammar provides the vast majority of the user interface to Succubus.

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize { ... } ⇒ Grammar

Create a new Succubus::Grammar object. Grammar.new should be passed a block defining the rules for the grammar. self in the block will be the newly-created Succubus::Grammar instance

Yields:

Raises:

  • (ParseError)

    if errors were encountered parsing the grammar in the supplied block



24
25
26
27
28
29
30
31
32
33
34
35
36
# File 'lib/succubus/grammar.rb', line 24

def initialize(&block)
  @rules = {}      
  @errors = []
  define_singleton_method(:create, block)
  create
  class << self ; undef_method :create ; end

  unless @errors.empty?
    pe = ParseError.new("Errors found parsing")
    pe.set_errors(@errors)
    raise pe
  end
end

Instance Attribute Details

#last_seedInteger (readonly)

Returns the random seed used by the last call to #execute.

Returns:

  • (Integer)

    the random seed used by the last call to #execute



12
13
14
15
16
17
18
19
20
21
22
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
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
# File 'lib/succubus/grammar.rb', line 12

class Grammar
  attr_reader :last_seed
  attr_reader :rules

  # Create a new {Grammar} object. {#initialize Grammar.new} should be passed
  # a block defining the rules for the grammar. +self+ in the block will be
  # the newly-created {Grammar} instance
  #
  # @yield Block defining the grammar. +self+ is the newly-created {Grammar},
  #   so bare calls to {#add_rule} work as expected.
  # @raise [ParseError] if errors were encountered parsing the grammar in the
  #   supplied block
  def initialize(&block)
    @rules = {}      
    @errors = []
    define_singleton_method(:create, block)
    create
    class << self ; undef_method :create ; end

    unless @errors.empty?
      pe = ParseError.new("Errors found parsing")
      pe.set_errors(@errors)
      raise pe
    end
  end

  # Define a new rule into the {Grammar}.
  #
  # {#add_rule} is usually called from the block parameter of {#initialize Grammar.new};
  # however, it can be called on an existing {Grammar} object. Errors encountered by
  # {#add_rule} will only be reported when called via {#initialize Grammar.new}.
  #
  # @param [#to_sym] name name of the rule
  # @param [Array<String>] choices possible replacements for the rule. When +<name>+ is
  #   encountered during execution of the {Grammar}, +<name>+ will be replaced by one of 
  #   the strings in +choices+ chosen uniformly at random.
  def add_rule(name, *choices)
    name = name.to_sym
    @errors << "Duplicate rule definition: #{name}" if @rules.include? name
    @rules[name] = choices
  end

  # Execute the grammar, producing a random {Result} string.
  #
  # The rule named +start+ is examined, and one of that rule's choices chosen. Then, for each
  # rule reference tag +<name>+ in that choice, the tag is recursively replaced with one of 
  # the choices for the rule named +name+.
  #
  # @param [#to_sym] start name of the rule to start execution at
  # @param [Integer] seed optional random seed. Defaults to +nil+, which will
  #   cause a random seed to be used
  #
  # @raise [ExecuteError] if errors were encountered while executing the grammar
  def execute(start, seed=nil)
    result = Generator.run(self, start.to_sym, seed)

    unless result.errors.empty?
      ee = ExecuteError.new("Errors found executing")
      ee.set_errors(result.errors)
      ee.set_partial(result)
      raise ee
    end

    @last_seed = result.random_seed
    result
  end
end

#rulesHash<Symbol => String> (readonly)

Returns the rules describing this grammar.

Returns:

  • (Hash<Symbol => String>)

    the rules describing this grammar



12
13
14
15
16
17
18
19
20
21
22
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
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
# File 'lib/succubus/grammar.rb', line 12

class Grammar
  attr_reader :last_seed
  attr_reader :rules

  # Create a new {Grammar} object. {#initialize Grammar.new} should be passed
  # a block defining the rules for the grammar. +self+ in the block will be
  # the newly-created {Grammar} instance
  #
  # @yield Block defining the grammar. +self+ is the newly-created {Grammar},
  #   so bare calls to {#add_rule} work as expected.
  # @raise [ParseError] if errors were encountered parsing the grammar in the
  #   supplied block
  def initialize(&block)
    @rules = {}      
    @errors = []
    define_singleton_method(:create, block)
    create
    class << self ; undef_method :create ; end

    unless @errors.empty?
      pe = ParseError.new("Errors found parsing")
      pe.set_errors(@errors)
      raise pe
    end
  end

  # Define a new rule into the {Grammar}.
  #
  # {#add_rule} is usually called from the block parameter of {#initialize Grammar.new};
  # however, it can be called on an existing {Grammar} object. Errors encountered by
  # {#add_rule} will only be reported when called via {#initialize Grammar.new}.
  #
  # @param [#to_sym] name name of the rule
  # @param [Array<String>] choices possible replacements for the rule. When +<name>+ is
  #   encountered during execution of the {Grammar}, +<name>+ will be replaced by one of 
  #   the strings in +choices+ chosen uniformly at random.
  def add_rule(name, *choices)
    name = name.to_sym
    @errors << "Duplicate rule definition: #{name}" if @rules.include? name
    @rules[name] = choices
  end

  # Execute the grammar, producing a random {Result} string.
  #
  # The rule named +start+ is examined, and one of that rule's choices chosen. Then, for each
  # rule reference tag +<name>+ in that choice, the tag is recursively replaced with one of 
  # the choices for the rule named +name+.
  #
  # @param [#to_sym] start name of the rule to start execution at
  # @param [Integer] seed optional random seed. Defaults to +nil+, which will
  #   cause a random seed to be used
  #
  # @raise [ExecuteError] if errors were encountered while executing the grammar
  def execute(start, seed=nil)
    result = Generator.run(self, start.to_sym, seed)

    unless result.errors.empty?
      ee = ExecuteError.new("Errors found executing")
      ee.set_errors(result.errors)
      ee.set_partial(result)
      raise ee
    end

    @last_seed = result.random_seed
    result
  end
end

Instance Method Details

#add_rule(name, *choices) ⇒ Object

Define a new rule into the Succubus::Grammar.

#add_rule is usually called from the block parameter of Grammar.new; however, it can be called on an existing Succubus::Grammar object. Errors encountered by #add_rule will only be reported when called via Grammar.new.

Parameters:

  • name (#to_sym)

    name of the rule

  • choices (Array<String>)

    possible replacements for the rule. When <name> is encountered during execution of the Succubus::Grammar, <name> will be replaced by one of the strings in choices chosen uniformly at random.



48
49
50
51
52
# File 'lib/succubus/grammar.rb', line 48

def add_rule(name, *choices)
  name = name.to_sym
  @errors << "Duplicate rule definition: #{name}" if @rules.include? name
  @rules[name] = choices
end

#execute(start, seed = nil) ⇒ Object

Execute the grammar, producing a random Result string.

The rule named start is examined, and one of that rule’s choices chosen. Then, for each rule reference tag <name> in that choice, the tag is recursively replaced with one of the choices for the rule named name.

Parameters:

  • start (#to_sym)

    name of the rule to start execution at

  • seed (Integer) (defaults to: nil)

    optional random seed. Defaults to nil, which will cause a random seed to be used

Raises:

  • (ExecuteError)

    if errors were encountered while executing the grammar



65
66
67
68
69
70
71
72
73
74
75
76
77
# File 'lib/succubus/grammar.rb', line 65

def execute(start, seed=nil)
  result = Generator.run(self, start.to_sym, seed)

  unless result.errors.empty?
    ee = ExecuteError.new("Errors found executing")
    ee.set_errors(result.errors)
    ee.set_partial(result)
    raise ee
  end

  @last_seed = result.random_seed
  result
end