Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save gabehollombe/600494 to your computer and use it in GitHub Desktop.
Save gabehollombe/600494 to your computer and use it in GitHub Desktop.
Parsing a text file for Pivotal Tracker stories with Treetop
LabelOne LabelTwo
- feature one (description one)
- feature two (description two)
- feature three with multiline desc following
desc line 1
desc line 2
desc line 3
[] chore one (chore desc one)
[] chore two with multiline desc
chore desc line 1
chore desc line 2
LabelThree
- feature four (desc for feature four)
describe 'the pivotxt treetop grammar' do
before do
grammar = File.join(File.dirname(__FILE__), '..', 'lib', 'pivtxt.treetop')
Treetop.load grammar
@parser = PivtxtParser.new
@parser.consume_all_input = false
end
it 'parses an example stories file' do
stories_file = File.join(File.dirname(__FILE__), 'fixtures', 'pivotxt_stories.txt')
tree = @parser.parse(File.read(stories_file).to_s)
print "FAILURE REASON: " + @parser.failure_reason.to_s if ! tree
label_groups = tree.value
labels = label_groups.first[:labels]
labels.should include('LabelOne', 'LabelTwo')
stories = label_groups.first[:stories]
stories.length.should == 5
stories[0][:title].value.should == "feature one "
stories[0][:description].value.should == "(description one)"
stories[0][:type].should == :feature
multi_line_desc = stories[2][:description].value
multi_line_desc.should include('desc line 1', 'desc line 2', 'desc line 3')
stories[3][:type].should == :chore
stories[3][:title].value.should == "chore one "
stories[3][:description].value.should == "(chore desc one)"
#next label group
stories = label_groups[1][:stories]
stories.length.should == 1
label_groups[1][:labels].should include('LabelThree')
end
end
grammar Pivtxt
rule label_groups
labeled_stories*
{
def value
elements.collect{|e| e.value}
end
}
end
rule labeled_stories
blank_line*
labels:(one_line "\n")
stories
{
def value
{ :labels => labels.text_value.split(' '),
:stories => stories.stories_array
}
end
}
end
rule stories
story*
{
def stories_array
elements.collect {|e| e.value}
end
}
end
rule story
blank_line*
item:(feature / chore)
{
def value
item.value
end
}
end
rule feature
indent '- '
story_body
{
def value
{:type => :feature, :title => story_body.title, :description => story_body.description}
end
}
end
rule chore
indent '[] '
story_body
{
def value
{:type => :chore, :title => story_body.title, :description => story_body.description}
end
}
end
rule story_body
title
description:(
"\n" multiline_description { def value; multiline_description.value; end }
/
inline_description "\n" { def value; inline_description.value; end }
)
end
rule title
plain_text {
def value
text_value
end
}
end
rule inline_description
(
'(' inline_description ')'
/
plain_text
)
{
def value
text_value
end
}
end
rule multiline_description
(indent indent one_line "\n")*
{
def value
text_value
end
}
end
#==========================================
# Utility rules
#==========================================
rule plain_text
(!non_plain .)*
end
# Characters not allowed in a one-line text fragment
rule non_plain
([()] / "\n")
end
# Any text to end of line:
rule one_line
(!"\n" .)*
end
rule blank_line
[ \t]* "\n"
end
rule indent
' '
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment