lucid-tdl 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in lucid-tdl.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2012 Jeff Nyman
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,29 @@
1
+ # Lucid TDL
2
+
3
+ This is a test description language for the Lucid tool, modeled on Gherkin.
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'lucid-tdl'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install lucid-tdl
18
+
19
+ ## Usage
20
+
21
+ Instructions will follow once I have an implementation.
22
+
23
+ ## Contributing
24
+
25
+ 1. Fork it
26
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
27
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
28
+ 4. Push to the branch (`git push origin my-new-feature`)
29
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,21 @@
1
+ require "bundler/gem_tasks"
2
+
3
+ require 'rake/testtask'
4
+
5
+ Rake::TestTask.new do |t|
6
+ t.libs << "test"
7
+ t.test_files = FileList['./test/**/*_test.rb']
8
+ end
9
+
10
+ desc "Generate Lexer"
11
+ task :lexer do
12
+ `rex lib/lucid-tdl/parser/lucid-tdl.rex -o lib/lucid-tdl/parser/lexer.rb`
13
+ end
14
+
15
+ desc "Generate Parser"
16
+ task :parser do
17
+ `racc lib/lucid-tdl/parser/lucid-tdl.y -o lib/lucid-tdl/parser/parser.rb`
18
+ end
19
+
20
+ desc "Generate Lexer and Parser"
21
+ task :generate => [:lexer, :parser]
@@ -0,0 +1,102 @@
1
+ module LucidTDL
2
+ module AST
3
+ class Node
4
+ attr_reader :filename, :line
5
+
6
+ def accept(visitor)
7
+ name = self.class.name.split('::').last
8
+ visitor.send("visit_#{name}".to_sym, self)
9
+ end
10
+
11
+ def pos(filename, line=nil)
12
+ @filename = filename
13
+ @line = line
14
+ end
15
+
16
+ end # class Node
17
+
18
+ class Feature < Node
19
+ include Enumerable
20
+
21
+ attr_reader :name
22
+ attr_accessor :description, :background, :scenarios, :tags
23
+
24
+ def initialize(name, scenarios=[], tags=[], background=nil)
25
+ @name = name
26
+ @background = background
27
+ @scenarios = scenarios
28
+ @tags = tags
29
+ end
30
+
31
+ def each
32
+ @scenarios.each
33
+ end
34
+ end # class Feature
35
+
36
+ class Ability < Feature
37
+ end
38
+
39
+ class Responsibility < Feature
40
+ end
41
+
42
+ class Background < Node
43
+ include Enumerable
44
+
45
+ attr_accessor :steps
46
+
47
+ def initialize(steps=[])
48
+ @steps = steps
49
+ end
50
+
51
+ def line
52
+ @steps.first.line - 1 if @steps.any?
53
+ end
54
+
55
+ def each
56
+ @steps.each
57
+ end
58
+ end # class Background
59
+
60
+ class Scenario < Node
61
+ include Enumerable
62
+
63
+ attr_reader :name, :steps, :tags
64
+
65
+ def initialize(name, steps=[], tags=[])
66
+ @name = name.to_s
67
+ @steps = steps
68
+ @tags = tags
69
+ end
70
+
71
+ def line
72
+ @steps.first.line - 1 if @steps.any?
73
+ end
74
+
75
+ def each
76
+ @steps.each
77
+ end
78
+ end # class Scenario
79
+
80
+ class Test < Scenario
81
+ end
82
+
83
+ class Step < Node
84
+ attr_reader :name
85
+ attr_reader :keyword
86
+
87
+ def initialize(name, keyword)
88
+ @name = name.to_s
89
+ @keyword = keyword.to_s
90
+ end
91
+ end # class Step
92
+
93
+ class Tag < Node
94
+ attr_reader :name
95
+
96
+ def initialize(name)
97
+ @name = name
98
+ end
99
+ end # class Tag
100
+
101
+ end # module AST
102
+ end # module LucidTDL
@@ -0,0 +1,130 @@
1
+ #--
2
+ # DO NOT MODIFY!!!!
3
+ # This file is automatically generated by rex 1.0.5
4
+ # from lexical definition file "lib/lucid-tdl/parser/lucid-tdl.rex".
5
+ #++
6
+
7
+ require 'racc/parser'
8
+ class LucidTDL::Parser < Racc::Parser
9
+ require 'strscan'
10
+
11
+ class ScanError < StandardError ; end
12
+
13
+ attr_reader :lineno
14
+ attr_reader :filename
15
+ attr_accessor :state
16
+
17
+ def scan_setup(str)
18
+ @ss = StringScanner.new(str)
19
+ @lineno = 1
20
+ @state = nil
21
+ end
22
+
23
+ def action
24
+ yield
25
+ end
26
+
27
+ def scan_str(str)
28
+ scan_setup(str)
29
+ do_parse
30
+ end
31
+ alias :scan :scan_str
32
+
33
+ def load_file( filename )
34
+ @filename = filename
35
+ open(filename, "r") do |f|
36
+ scan_setup(f.read)
37
+ end
38
+ end
39
+
40
+ def scan_file( filename )
41
+ load_file(filename)
42
+ do_parse
43
+ end
44
+
45
+
46
+ def next_token
47
+ return if @ss.eos?
48
+
49
+ # skips empty actions
50
+ until token = _next_token or @ss.eos?; end
51
+ token
52
+ end
53
+
54
+ def _next_token
55
+ text = @ss.peek(1)
56
+ @lineno += 1 if text == "\n"
57
+ token = case @state
58
+ when nil
59
+ case
60
+ when (text = @ss.scan(/[ \t]+/))
61
+ ;
62
+
63
+ when (text = @ss.scan(/\n/))
64
+ action { [:NEWLINE, text] }
65
+
66
+ when (text = @ss.scan(/Feature:/))
67
+ action { [:FEATURE, text[0..-2]] }
68
+
69
+ when (text = @ss.scan(/Ability:/))
70
+ action { [:ABILITY, text[0..-2]] }
71
+
72
+ when (text = @ss.scan(/Responsibility:/))
73
+ action { [:RESPONSIBILITY, text[0..-2]] }
74
+
75
+ when (text = @ss.scan(/Background:/))
76
+ action { [:BACKGROUND, text[0..-2]] }
77
+
78
+ when (text = @ss.scan(/Context:/))
79
+ action { [:CONTEXT, text[0..-2]] }
80
+
81
+ when (text = @ss.scan(/Scenario:/))
82
+ action { [:SCENARIO, text[0..-2]] }
83
+
84
+ when (text = @ss.scan(/Test:/))
85
+ action { [:TEST, text[0..-2]] }
86
+
87
+ when (text = @ss.scan(/@(\w|-)+/))
88
+ action { [:TAG, text[1..-1]] }
89
+
90
+ when (text = @ss.scan(/Given/))
91
+ action { [:GIVEN, text] }
92
+
93
+ when (text = @ss.scan(/When/))
94
+ action { [:WHEN, text] }
95
+
96
+ when (text = @ss.scan(/Then/))
97
+ action { [:THEN, text] }
98
+
99
+ when (text = @ss.scan(/And/))
100
+ action { [:AND, text] }
101
+
102
+ when (text = @ss.scan(/But/))
103
+ action { [:BUT, text] }
104
+
105
+ when (text = @ss.scan(/\*/))
106
+ action { [:GENERIC, text] }
107
+
108
+ when (text = @ss.scan(/[^\n]*/))
109
+ action { [:TEXT, text.strip] }
110
+
111
+ else
112
+ text = @ss.string[@ss.pos .. -1]
113
+ raise ScanError, "can not match: '" + text + "'"
114
+ end # if
115
+
116
+ else
117
+ raise ScanError, "undefined state: '" + state.to_s + "'"
118
+ end # case state
119
+ token
120
+ end # def _next_token
121
+
122
+ def tokenize(code)
123
+ scan_setup(code)
124
+ tokens = []
125
+ while token = next_token
126
+ tokens << token
127
+ end
128
+ tokens
129
+ end
130
+ end # class
@@ -0,0 +1,40 @@
1
+ class LucidTDL::Parser
2
+
3
+ macro
4
+ BLANK [\ \t]+
5
+
6
+ rule
7
+ {BLANK} # no action
8
+
9
+ \n { [:NEWLINE, text] }
10
+
11
+ Feature: { [:FEATURE, text[0..-2]] }
12
+ Ability: { [:ABILITY, text[0..-2]] }
13
+ Responsibility: { [:RESPONSIBILITY, text[0..-2]] }
14
+
15
+ Background: { [:BACKGROUND, text[0..-2]] }
16
+ Context: { [:CONTEXT, text[0..-2]] }
17
+ Scenario: { [:SCENARIO, text[0..-2]] }
18
+ Test: { [:TEST, text[0..-2]] }
19
+
20
+ @(\w|-)+ { [:TAG, text[1..-1]] }
21
+
22
+ Given { [:GIVEN, text] }
23
+ When { [:WHEN, text] }
24
+ Then { [:THEN, text] }
25
+ And { [:AND, text] }
26
+ But { [:BUT, text] }
27
+ \* { [:GENERIC, text] }
28
+
29
+ [^\n]* { [:TEXT, text.strip] }
30
+
31
+ inner
32
+ def tokenize(code)
33
+ scan_setup(code)
34
+ tokens = []
35
+ while token = next_token
36
+ tokens << token
37
+ end
38
+ tokens
39
+ end
40
+ end
@@ -0,0 +1,107 @@
1
+ class LucidTDL::Parser
2
+
3
+ rule
4
+ Root:
5
+ Feature { result = val[0] }
6
+ | Feature
7
+ Scenarios { result = val[0]; result.scenarios = val[1] }
8
+ | FeatureTags Feature { result = val[1]; result.tags = val[0] }
9
+ | FeatureTags Feature
10
+ Scenarios { result = val[1]; result.scenarios = val[2]; result.tags = val[0] }
11
+ ;
12
+
13
+ Newline:
14
+ NEWLINE
15
+ | Newline NEWLINE
16
+ ;
17
+
18
+ Description:
19
+ TEXT Newline { result = val[0] }
20
+ | Description TEXT Newline { result = val[0...-1].flatten }
21
+ ;
22
+
23
+ FeatureTags:
24
+ Tags { result = val[0] }
25
+ | Newline Tags { result = val[1] }
26
+ ;
27
+
28
+ Feature:
29
+ FeatureHeader { result = val[0] }
30
+ | FeatureHeader
31
+ Background { result = val[0]; result.background = val[1] }
32
+ ;
33
+
34
+ FeatureHeader:
35
+ FeatureName { result = val[0] }
36
+ | FeatureName Newline { result = val[0] }
37
+ | FeatureName Newline
38
+ Description { result = val[0]; result.description = val[2] }
39
+ ;
40
+
41
+ FeatureName:
42
+ FEATURE TEXT { result = AST::Feature.new(val[1]); result.pos(filename, lineno) }
43
+ | ABILITY TEXT { result = AST::Feature.new(val[1]); result.pos(filename, lineno) }
44
+ | RESPONSIBILITY TEXT { result = AST::Feature.new(val[1]); result.pos(filename, lineno) }
45
+ | Newline FEATURE TEXT { result = AST::Feature.new(val[2]); result.pos(filename, lineno) }
46
+ | Newline ABILITY TEXT { result = AST::Feature.new(val[2]); result.pos(filename, lineno) }
47
+ | Newline RESPONSIBILITY TEXT { result = AST::Feature.new(val[2]); result.pos(filename, lineno) }
48
+ ;
49
+
50
+ Background:
51
+ BackgroundHeader
52
+ Steps { result = val[0]; result.steps = val[1] }
53
+ ;
54
+
55
+ BackgroundHeader:
56
+ BACKGROUND Newline { result = AST::Background.new; result.pos(filename, lineno) }
57
+ | CONTEXT Newline { result = AST::Background.new; result.pos(filename, lineno) }
58
+ ;
59
+
60
+ Steps:
61
+ Step { result = [val[0]] }
62
+ | Step Newline { result = [val[0]] }
63
+ | Step Newline Steps { val[2].unshift(val[0]); result = val[2] }
64
+ ;
65
+
66
+ Step:
67
+ Keyword TEXT { result = AST::Step.new(val[1], val[0]); result.pos(filename, lineno) }
68
+ ;
69
+
70
+ Keyword:
71
+ GIVEN | WHEN | THEN | AND | BUT | GENERIC
72
+ ;
73
+
74
+ Scenarios:
75
+ Scenario { result = [val[0]] }
76
+ | Scenarios Scenario { result = val[0] << val[1] }
77
+ ;
78
+
79
+ Scenario:
80
+ SCENARIO TEXT Newline
81
+ Steps { result = AST::Scenario.new(val[1], val[3]); result.pos(filename, lineno - 1) }
82
+ | TEST TEXT Newline
83
+ Steps { result = AST::Scenario.new(val[1], val[3]); result.pos(filename, lineno - 1) }
84
+ | Tags Newline
85
+ SCENARIO TEXT Newline
86
+ Steps { result = AST::Scenario.new(val[3], val[5], val[0]); result.pos(filename, lineno - 2) }
87
+ | Tags Newline
88
+ TEST TEXT Newline
89
+ Steps { result = AST::Scenario.new(val[3], val[5], val[0]); result.pos(filename, lineno - 2) }
90
+ ;
91
+
92
+ Tags:
93
+ TAG { result = [AST::Tag.new(val[0])] }
94
+ | Tags TAG { result = val[0] << AST::Tag.new(val[1]) }
95
+ ;
96
+
97
+ end
98
+
99
+ ---- header
100
+ require_relative "./lexer"
101
+ require_relative "../ast"
102
+
103
+ ---- inner
104
+
105
+ def parse(input)
106
+ scan_str(input)
107
+ end