Module: Musa::GenerativeGrammar

Extended by:
GenerativeGrammar
Included in:
GenerativeGrammar
Defined in:
lib/musa-dsl/generative/generative-grammar.rb

Overview

Generative grammar system for creating formal grammars with combinatorial generation.

GenerativeGrammar provides a DSL for defining formal grammars that can generate all possible combinations of terminal and non-terminal symbols according to production rules. Similar to context-free grammars in formal language theory.

Core Concepts

  • Nodes (N): Terminal or block nodes with content and attributes
  • Proxy Nodes (PN): Placeholder nodes for recursive grammar definitions
  • Operators: Combine nodes to form production rules
    • | (or): Alternative/choice between nodes
    • + (next): Concatenation/sequence of nodes
  • Modifiers:
    • repeat(min:, max:): Repeat node multiple times
    • limit(&block): Filter options by condition
  • Options: Generate all valid combinations matching the grammar

Grammar Definition Process

  1. Define terminal nodes with N(content, **attributes)
  2. Combine nodes using operators (|, +)
  3. Add repetition and limits as needed
  4. Use PN() for recursive grammars
  5. Call .options to generate all valid combinations

Musical Applications

  • Generate melodic patterns with rhythmic constraints
  • Create harmonic progressions with voice leading rules
  • Produce variations of musical motifs
  • Build algorithmic composition structures

Examples:

Simple sequence with alternatives

include Musa::GenerativeGrammar

a = N('a', size: 1)
b = N('b', size: 1)
c = N('c', size: 1)

# Grammar: (a or b) repeated 3 times, then c
grammar = (a | b).repeat(3) + c

# Generate all possibilities
grammar.options(content: :join)
# => ["aaac", "aabc", "abac", "abbc", "baac", "babc", "bbac", "bbbc"]

Grammar with constraints

a = N('a', size: 1)
b = N('b', size: 1)

# Limit: total size must equal 3
grammar = (a | b).repeat.limit { |o| o.collect { |_| _.attributes[:size] }.sum == 3 }

# Filter options where size <= 4
grammar.options(content: :join) { |o| o.collect { |e| e.attributes[:size] }.sum <= 4 }
# => ["aaa", "aab", "aba", "abb", "baa", "bab", "bba", "bbb"]

Recursive grammar using proxy nodes

a = N('a', size: 1)
b = N('b', size: 1)
c = N('c', size: 1)

# Create proxy for recursion
dp = PN()

# Grammar: (c + dp) or (a or b), limit size to 3
d = (c + dp | (a | b)).repeat.limit(:size, :sum, :==, 3)

# Assign recursive reference
dp.node = d

d.options(:size, :sum, :<=, 4, content: :join)
# => ["cca", "ccb", "caa", "cab", "cba", "cbb", "aca", "acb", ...]

Block nodes for dynamic content

a = N(color: :blue) { |parent| 'hola' }
b = N(color: :red) { |parent, attributes|
  OptionElement.new('adios', final: true, **attributes)
}

grammar = (a | b).repeat(2)
grammar.options
# => [["hola", "hola"], ["hola", "adios"], ["adios", "hola"], ["adios", "adios"]]

See Also:

Defined Under Namespace

Classes: OptionElement

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.N(content = nil, **attributes) {|parent, attributes| ... } ⇒ Implementation::FinalNode, Implementation::BlockNode

Creates a terminal or block node.

Nodes are the basic building blocks of grammars. They can be:

  • Terminal nodes: Fixed content with attributes
  • Block nodes: Dynamic content generated by block

Examples:

Terminal node

n = N('a', size: 1)

Block node

n = N(color: :blue) { |parent| "hello" }

Block returning OptionElement

n = N(type: :special) { |parent, attrs|
  OptionElement.new('value', **attrs)
}

Parameters:

  • content (Object, nil) (defaults to: nil)

    terminal content (if no block given)

  • attributes (Hash)

    attributes attached to node

Yield Parameters:

Yield Returns:

Returns:



124
125
126
127
128
129
130
# File 'lib/musa-dsl/generative/generative-grammar.rb', line 124

def N(content = nil, **attributes, &block)
  if block_given? && content.nil?
    Implementation::BlockNode.new(attributes, &block)
  else
    Implementation::FinalNode.new(content, attributes)
  end
end

.PNImplementation::ProxyNode

Creates a proxy node for recursive grammars.

Proxy nodes act as placeholders that can be assigned later, enabling recursive and self-referential grammar definitions.

Examples:

Recursive grammar

a = N('a')
b = N('b')

# Create proxy
proxy = PN()

# Define grammar referencing itself through proxy
grammar = a + proxy | b

# Assign recursive reference
proxy.node = grammar

Returns:



151
152
153
# File 'lib/musa-dsl/generative/generative-grammar.rb', line 151

def PN
  Implementation::ProxyNode.new
end

Instance Method Details

#N(content = nil, **attributes) {|parent, attributes| ... } ⇒ Implementation::FinalNode, Implementation::BlockNode

Creates a terminal or block node.

Nodes are the basic building blocks of grammars. They can be:

  • Terminal nodes: Fixed content with attributes
  • Block nodes: Dynamic content generated by block

Examples:

Terminal node

n = N('a', size: 1)

Block node

n = N(color: :blue) { |parent| "hello" }

Block returning OptionElement

n = N(type: :special) { |parent, attrs|
  OptionElement.new('value', **attrs)
}

Parameters:

  • content (Object, nil) (defaults to: nil)

    terminal content (if no block given)

  • attributes (Hash)

    attributes attached to node

Yield Parameters:

Yield Returns:

Returns:



124
125
126
127
128
129
130
# File 'lib/musa-dsl/generative/generative-grammar.rb', line 124

def N(content = nil, **attributes, &block)
  if block_given? && content.nil?
    Implementation::BlockNode.new(attributes, &block)
  else
    Implementation::FinalNode.new(content, attributes)
  end
end

#PNImplementation::ProxyNode

Creates a proxy node for recursive grammars.

Proxy nodes act as placeholders that can be assigned later, enabling recursive and self-referential grammar definitions.

Examples:

Recursive grammar

a = N('a')
b = N('b')

# Create proxy
proxy = PN()

# Define grammar referencing itself through proxy
grammar = a + proxy | b

# Assign recursive reference
proxy.node = grammar

Returns:



151
152
153
# File 'lib/musa-dsl/generative/generative-grammar.rb', line 151

def PN
  Implementation::ProxyNode.new
end