Skip to content
← Back to Examples

Transform Patterns

RubyconceptualintermediateParslet-compatible

Transform DSL patterns demonstration

How the Code Works

This section explains how the parsing rules map to the format semantics.

Transform Class Structure

class Spring < Parsanol::Transform
  rule(:stem => sequence(:branches)) {
    {:stem => (branches + [{:branch => :leaf}])}
  }
end

Each season is a separate transform class: Different classes encapsulate different transformation behaviors.

Sequence Pattern

rule(:stem => sequence(:branches)) {
  {:stem => (branches + [{:branch => :leaf}])}
}

`sequence(:x)` matches arrays of simple values: `branches` is bound to the array content.

Subtree Pattern

rule(:stem => subtree(:branches)) {
  new_branches = branches.map { |b| {:branch => [:leaf, :flower]} }
  {:stem => new_branches}
}

`subtree(:x)` matches any nested structure: Recursively applies to children.

Branch Pattern

class Fall < Parsanol::Transform
  rule(:branch => sequence(:x)) {
    x.each { |e| puts "Fruit!" if e == :flower }
    x.each { |e| puts "Falling Leaves!" if e == :leaf }
    {:branch => []}
  }
end

Match and transform nested arrays: Iterate over array contents while transforming.

Composition

def do_seasons(tree)
  [Spring, Summer, Fall, Winter].each do |season|
    tree = season.new.apply(tree)
  end
  tree
end

Multiple transforms are applied in sequence: Each transform passes its output to the next.

Composition

def do_seasons(tree)
  [Spring, Summer, Fall, Winter].each do |season|
    tree = season.new.apply(tree)
  end
  tree
end

Multiple transforms are applied in sequence: Each transform passes its output to the next.

Source Code

Ruby
# frozen_string_literal: true

$LOAD_PATH.unshift "#{File.dirname(__FILE__)}/../lib"

require "parsanol/parslet"
require "pp"

tree = { bud: { stem: [] } }

class Spring < Parsanol::Transform
  rule(stem: sequence(:branches)) do
    { stem: (branches + [{ branch: :leaf }]) }
  end
end

class Summer < Parsanol::Transform
  rule(stem: subtree(:branches)) do
    new_branches = branches.map { |_b| { branch: %i[leaf flower] } }
    { stem: new_branches }
  end
end

class Fall < Parsanol::Transform
  rule(branch: sequence(:x)) do
    x.each do |e|
      puts "Fruit!" if e == :flower
      puts "Falling Leaves!" if e == :leaf
    end
    { branch: [] }
  end
end

class Winter < Parsanol::Transform
  rule(stem: subtree(:x)) do
    { stem: [] }
  end
end

def do_seasons(tree)
  [Spring, Summer, Fall, Winter].each do |season|
    p "And when #{season} comes"
    tree = season.new.apply(tree)
    pp tree
    puts
  end
  tree
end

# What marvel of life!
tree = do_seasons(tree)
do_seasons(tree)