lunokhod 0.0.1.pre

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,41 @@
1
+ require 'lunokhod/rule_parser'
2
+ require 'set'
3
+
4
+ module Lunokhod
5
+ # Rules are chains of condition tags joined by ANDs and ORs.
6
+ # Parentheses may be used to modify precedence.
7
+ #
8
+ # Examples:
9
+ #
10
+ # A
11
+ # A or B
12
+ # A and (B or C)
13
+ # A and (B or (C and D))
14
+ #
15
+ # Surveyor actually uses eval (!) to evaluate dependencies. This means that
16
+ # Surveyor's dependency language inherits its precedence rules from Ruby.
17
+ module RuleParsing
18
+ attr_reader :parsed_rule
19
+
20
+ def parse_rule
21
+ parser = RuleParser.new(rule)
22
+ parser.parse
23
+ @parsed_rule = parser.ast
24
+ end
25
+
26
+ def referenced_conditions
27
+ Set.new.tap do |cs|
28
+ visit_rule { |n| cs << n.name if n.respond_to?(:name) }
29
+ end
30
+ end
31
+
32
+ def visit_rule(rule = parsed_rule, &block)
33
+ yield rule
34
+
35
+ if rule.respond_to?(:conj)
36
+ visit_rule(rule.left, &block)
37
+ visit_rule(rule.right, &block)
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,155 @@
1
+ require 'lunokhod/ast'
2
+ require 'lunokhod/visitation'
3
+
4
+ module Lunokhod
5
+ class Verifier
6
+ include Ast
7
+ include Visitation
8
+
9
+ attr_reader :atag_count
10
+ attr_reader :atag_expectations
11
+ attr_reader :by_qref
12
+ attr_reader :ctag_count
13
+ attr_reader :qtag_count
14
+ attr_reader :qtag_expectations
15
+ attr_reader :survey
16
+
17
+ def initialize(survey)
18
+ @atag_count = Hash.new(0)
19
+ @atag_expectations = Hash.new(0)
20
+ @by_qref = {}
21
+ @ctag_count = Hash.new(0)
22
+ @qtag_count = Hash.new(0)
23
+ @qtag_expectations = Hash.new(0)
24
+ @survey = survey
25
+ end
26
+
27
+ def run(&block)
28
+ visit(survey, true) do |n, level, prev, boundary|
29
+ case n
30
+ when Condition; inspect_condition(n)
31
+ when Dependency, Validation; inspect_conditional(n)
32
+ when Question; inspect_question(n)
33
+ end
34
+ end
35
+
36
+ run_checks(&block)
37
+ end
38
+
39
+ # When we see a question:
40
+ #
41
+ # 1. If the question has a non-empty tag, index the question by its tag.
42
+ # 2. Register references to it and all of its answers.
43
+ def inspect_question(q)
44
+ index_question(q) unless q.tag.empty?
45
+ ref_question(q)
46
+ q.answers.each { |a| ref_answer(q, a) }
47
+ end
48
+
49
+ # There are three references in conditions that we have to worry about:
50
+ #
51
+ # - the question ref, if one exists
52
+ # - the answer ref, if one exists
53
+ # - the condition tag
54
+ #
55
+ # Seeing a condition registers a reference to the condition and
56
+ # expectations for the qref and aref if those refs exist.
57
+ def inspect_condition(c)
58
+ pc = c.parsed_condition
59
+
60
+ ctag_count[[c.parent, c.tag]] += 1
61
+
62
+ pending_question(pc.qtag, c) if pc.qtag
63
+ pending_answer(pc.qtag, pc.atag, c) if pc.qtag && pc.atag
64
+ end
65
+
66
+ # When we see a dependency or validation, register references to all of its
67
+ # conditions.
68
+ def inspect_conditional(c)
69
+ c.referenced_conditions.each do |tag|
70
+ key = [c, tag]
71
+
72
+ unless ctag_count.has_key?(key)
73
+ ctag_count[key] = 0
74
+ end
75
+ end
76
+ end
77
+
78
+ # Find all unsatisfied expectations.
79
+ def run_checks
80
+ bad_refs(qtag_count) do |qt|
81
+ yield Error.new(survey, Question, :bad_ref, qt, qtag_expectations[qt])
82
+ end
83
+
84
+ bad_refs(atag_count) do |qt, at|
85
+ yield Error.new(survey, Answer, :bad_ref, at, atag_expectations[[qt, at]])
86
+ end
87
+
88
+ bad_refs(ctag_count) do |c, tag|
89
+ yield Error.new(survey, Condition, :bad_ref, tag, c)
90
+ end
91
+
92
+ by_qref.select { |k, v| v.length > 1 }.each do |k, vs|
93
+ yield Error.new(survey, Question, :duplicate_qref, k, vs)
94
+ end
95
+ end
96
+
97
+ def bad_refs(refs)
98
+ refs.each { |k, c| yield k if c < 1 }
99
+ end
100
+
101
+ def index_question(q)
102
+ key = q.tag
103
+
104
+ by_qref[key] = [] unless by_qref.has_key?(key)
105
+ by_qref[key] << q
106
+ end
107
+
108
+ def ref_question(q)
109
+ qtag_count[q.tag.to_s] += 1
110
+ end
111
+
112
+ def ref_answer(q, a)
113
+ atag_count[[q.tag.to_s, a.tag.to_s]] += 1
114
+ end
115
+
116
+ def pending_question(qtag, from)
117
+ qtag_expectations[qtag] = from
118
+
119
+ unless qtag_count.has_key?(qtag)
120
+ qtag_count[qtag] = 0
121
+ end
122
+ end
123
+
124
+ def pending_answer(qtag, atag, from)
125
+ key = [qtag, atag]
126
+ atag_expectations[key] = from
127
+
128
+ unless atag_count.has_key?(key)
129
+ atag_count[key] = 0
130
+ end
131
+ end
132
+
133
+ class Error < Struct.new(:survey, :node_type, :error_type, :key, :at_fault)
134
+ def bad_question_tag?
135
+ bad_tag? && node_type == Ast::Question
136
+ end
137
+
138
+ def bad_answer_tag?
139
+ bad_tag? && node_type == Ast::Answer
140
+ end
141
+
142
+ def bad_condition?
143
+ bad_tag? && node_type == Ast::Condition
144
+ end
145
+
146
+ def duplicate_question?
147
+ error_type == :duplicate_qref
148
+ end
149
+
150
+ def bad_tag?
151
+ error_type == :bad_ref
152
+ end
153
+ end
154
+ end
155
+ end
@@ -0,0 +1,3 @@
1
+ module Lunokhod
2
+ VERSION = '0.0.1.pre'
3
+ end
@@ -0,0 +1,12 @@
1
+ module Lunokhod
2
+ module Visitation
3
+ def visit(root, only_enter = false, level = 0, prev = nil, &block)
4
+ yield root, level, prev, :enter
5
+ root.children.each { |c| visit(c, only_enter, level + 1, root, &block) }
6
+
7
+ unless only_enter
8
+ yield root, level, prev, :exit
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,28 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'lunokhod/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "lunokhod"
8
+ spec.version = Lunokhod::VERSION
9
+ spec.authors = ["David Yip"]
10
+ spec.email = ["yipdw@northwestern.edu"]
11
+ spec.description = %q{Parsing and compilation tools for Surveyor surveys}
12
+ spec.summary = %q{Parsing and compilation tools for Surveyor surveys}
13
+ spec.homepage = "https://github.com/NUBIC/lunokhod"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_dependency 'case'
22
+ spec.add_dependency 'uuidtools'
23
+
24
+ spec.add_development_dependency "bundler", "~> 1.2"
25
+ spec.add_development_dependency "rake"
26
+ spec.add_development_dependency 'kpeg'
27
+ spec.add_development_dependency 'rspec'
28
+ end
@@ -0,0 +1,62 @@
1
+ require 'spec_helper'
2
+
3
+ module Lunokhod
4
+ describe ErrorReport do
5
+ let(:ep) { ErrorReport.new(surveys) }
6
+ let(:surveys) { p = Parser.new(data); p.parse; p.surveys }
7
+
8
+ describe '#run' do
9
+ let(:data) do
10
+ %q{survey "foo" do
11
+ section "bar" do
12
+ q_1 "a question"
13
+ a_1 "an answer"
14
+ dependency :rule => "A or B"
15
+ condition_A :q_oops, '==', :a_1
16
+ q_1 "duplicate"
17
+ a_1 "yep"
18
+ end
19
+ end
20
+
21
+ survey "bar" do
22
+ section "baz" do
23
+ q_1 "a question"
24
+ a_1 "an answer"
25
+ dependency :rule => "A"
26
+ condition_A :q_1, '==', :a_oops
27
+ end
28
+ end}
29
+ end
30
+
31
+ before do
32
+ ep.run
33
+ end
34
+
35
+ def errors_for(sname)
36
+ ep.errors.select { |e| e.survey.name == sname }
37
+ end
38
+
39
+ describe 'for each survey' do
40
+ it 'finds unknown question references' do
41
+ errors_for('foo').select(&:bad_question_tag?).length.should == 1
42
+ errors_for('bar').select(&:bad_question_tag?).length.should == 0
43
+ end
44
+
45
+ it 'finds unknown answer references' do
46
+ errors_for('foo').select(&:bad_answer_tag?).length.should == 1
47
+ errors_for('bar').select(&:bad_answer_tag?).length.should == 1
48
+ end
49
+
50
+ it 'finds unknown condition references' do
51
+ errors_for('foo').select(&:bad_condition?).length.should == 1
52
+ errors_for('bar').select(&:bad_condition?).length.should == 0
53
+ end
54
+
55
+ it 'finds duplicate question references' do
56
+ errors_for('foo').select(&:duplicate_question?).length.should == 1
57
+ errors_for('bar').select(&:duplicate_question?).length.should == 0
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,87 @@
1
+ require 'spec_helper'
2
+
3
+ module Lunokhod
4
+ describe Parser do
5
+ let(:parser) { Parser.new(data, 'fake') }
6
+ let(:ast) { parser.surveys.first }
7
+
8
+ describe 'for repeaters' do
9
+ let(:data) do
10
+ %q{
11
+ survey "fake" do
12
+ section "one" do
13
+ q_a "Eh?"
14
+ a_y "Yes"
15
+
16
+ repeater "I'm going to ask you a bunch of questions" do
17
+ dependency :rule => "A"
18
+ condition_A :q_a, "==", :a_y
19
+
20
+ q "Who is your daddy, and what does he do?"
21
+ a :string
22
+ end
23
+ end
24
+ end
25
+ }
26
+ end
27
+
28
+ before do
29
+ parser.parse
30
+ end
31
+
32
+ let(:repeater) { ast.sections[0].questions[1] }
33
+
34
+ it 'associates repeater dependencies with the repeater' do
35
+ repeater.dependencies.length.should == 1
36
+ end
37
+ end
38
+
39
+ describe 'for answers' do
40
+ let(:data) do
41
+ %q{
42
+ survey "fake" do
43
+ section "one" do
44
+ q_a "A question"
45
+ a "An answer", :string
46
+ a "An answer with options", :help_text => "Yep"
47
+ a "An answer with everything", :string, :help_text => "Yep"
48
+ a :string
49
+ a :other, :string
50
+ end
51
+ end
52
+ }
53
+ end
54
+
55
+ before do
56
+ parser.parse
57
+ end
58
+
59
+ let(:answers) { ast.sections[0].questions[0].answers }
60
+
61
+ it 'parses answer response class' do
62
+ answers[0].type.should == :string
63
+ answers[0].options.should be_empty
64
+ end
65
+
66
+ it 'parses options on answers' do
67
+ answers[1].options.should == { :help_text => 'Yep' }
68
+ end
69
+
70
+ it 'parses answers with options and response class' do
71
+ answers[2].type.should == :string
72
+ answers[2].options.should == { :help_text => 'Yep' }
73
+ end
74
+
75
+ it 'parses answers with a response class but no text' do
76
+ answers[3].type.should == :string
77
+ answers[3].options.should be_empty
78
+ end
79
+
80
+ it 'parses the "other" answer type with a response class' do
81
+ answers[4].should be_other
82
+ answers[4].type.should == :string
83
+ answers[4].options.should be_empty
84
+ end
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,6 @@
1
+ $LOAD_PATH.unshift(File.expand_path('../../lib', __FILE__))
2
+
3
+ require 'lunokhod'
4
+
5
+ RSpec.configure do |c|
6
+ end
metadata ADDED
@@ -0,0 +1,181 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: lunokhod
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1.pre
5
+ prerelease: 6
6
+ platform: ruby
7
+ authors:
8
+ - David Yip
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-03-08 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: case
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
30
+ - !ruby/object:Gem::Dependency
31
+ name: uuidtools
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ type: :runtime
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ - !ruby/object:Gem::Dependency
47
+ name: bundler
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ~>
52
+ - !ruby/object:Gem::Version
53
+ version: '1.2'
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ~>
60
+ - !ruby/object:Gem::Version
61
+ version: '1.2'
62
+ - !ruby/object:Gem::Dependency
63
+ name: rake
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ! '>='
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
70
+ type: :development
71
+ prerelease: false
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ! '>='
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
78
+ - !ruby/object:Gem::Dependency
79
+ name: kpeg
80
+ requirement: !ruby/object:Gem::Requirement
81
+ none: false
82
+ requirements:
83
+ - - ! '>='
84
+ - !ruby/object:Gem::Version
85
+ version: '0'
86
+ type: :development
87
+ prerelease: false
88
+ version_requirements: !ruby/object:Gem::Requirement
89
+ none: false
90
+ requirements:
91
+ - - ! '>='
92
+ - !ruby/object:Gem::Version
93
+ version: '0'
94
+ - !ruby/object:Gem::Dependency
95
+ name: rspec
96
+ requirement: !ruby/object:Gem::Requirement
97
+ none: false
98
+ requirements:
99
+ - - ! '>='
100
+ - !ruby/object:Gem::Version
101
+ version: '0'
102
+ type: :development
103
+ prerelease: false
104
+ version_requirements: !ruby/object:Gem::Requirement
105
+ none: false
106
+ requirements:
107
+ - - ! '>='
108
+ - !ruby/object:Gem::Version
109
+ version: '0'
110
+ description: Parsing and compilation tools for Surveyor surveys
111
+ email:
112
+ - yipdw@northwestern.edu
113
+ executables:
114
+ - lunokhod-driver
115
+ extensions: []
116
+ extra_rdoc_files: []
117
+ files:
118
+ - .gitignore
119
+ - .gitmodules
120
+ - .travis.yml
121
+ - CHANGELOG
122
+ - Gemfile
123
+ - LICENSE
124
+ - README
125
+ - Rakefile
126
+ - TODO
127
+ - bin/lunokhod-driver
128
+ - kitchen_sink_survey.rb
129
+ - lib/lunokhod.rb
130
+ - lib/lunokhod/ast.rb
131
+ - lib/lunokhod/backends/debug.rb
132
+ - lib/lunokhod/backends/webpage.rb
133
+ - lib/lunokhod/backends/webpage/evaluator.js
134
+ - lib/lunokhod/backends/webpage/jquery.min.js
135
+ - lib/lunokhod/backends/webpage/page.html.erb
136
+ - lib/lunokhod/compiler.rb
137
+ - lib/lunokhod/condition_parsing.rb
138
+ - lib/lunokhod/error_report.rb
139
+ - lib/lunokhod/parser.rb
140
+ - lib/lunokhod/rule_parser.kpeg
141
+ - lib/lunokhod/rule_parser.rb
142
+ - lib/lunokhod/rule_parsing.rb
143
+ - lib/lunokhod/verifier.rb
144
+ - lib/lunokhod/version.rb
145
+ - lib/lunokhod/visitation.rb
146
+ - lunokhod.gemspec
147
+ - spec/lunokhod/error_report_spec.rb
148
+ - spec/lunokhod/parser_spec.rb
149
+ - spec/spec_helper.rb
150
+ homepage: https://github.com/NUBIC/lunokhod
151
+ licenses:
152
+ - MIT
153
+ post_install_message:
154
+ rdoc_options: []
155
+ require_paths:
156
+ - lib
157
+ required_ruby_version: !ruby/object:Gem::Requirement
158
+ none: false
159
+ requirements:
160
+ - - ! '>='
161
+ - !ruby/object:Gem::Version
162
+ version: '0'
163
+ segments:
164
+ - 0
165
+ hash: -1254807072621896079
166
+ required_rubygems_version: !ruby/object:Gem::Requirement
167
+ none: false
168
+ requirements:
169
+ - - ! '>'
170
+ - !ruby/object:Gem::Version
171
+ version: 1.3.1
172
+ requirements: []
173
+ rubyforge_project:
174
+ rubygems_version: 1.8.25
175
+ signing_key:
176
+ specification_version: 3
177
+ summary: Parsing and compilation tools for Surveyor surveys
178
+ test_files:
179
+ - spec/lunokhod/error_report_spec.rb
180
+ - spec/lunokhod/parser_spec.rb
181
+ - spec/spec_helper.rb