fabulator-grammar 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/README.markdown ADDED
@@ -0,0 +1,73 @@
1
+ Fabulator Grammar Extension
2
+ ---------------------------
3
+
4
+ This extension provides basic support for regular expressions modeled
5
+ loosely after [Perl 6 grammars][].
6
+
7
+ For now, this extension provides a single function: 'match(regex, string)'.
8
+ This function returns true or false depending on whether or not the regular
9
+ expression matches the given string.
10
+
11
+ The current implementation compiled regular expressions into Ruby
12
+ RegExp objects for faster execution. This may change as new capabilities
13
+ are added that might not be supportable by pure Ruby regular expressions.
14
+
15
+ The goal of the grammar extension is to provide a rich environment for
16
+ writing parsers that can be integrated into the Fabulator environment.
17
+
18
+ Namespace: "http://dh.tamu.edu/ns/fabulator/grammar/1.0#"
19
+
20
+
21
+ Regular Expressions
22
+ ===================
23
+
24
+ The following characters are considered special and should be escaped if
25
+ you are not using for their special meaning:
26
+
27
+ Parenthesis ( )
28
+ Brackets [ ]
29
+ Curly Brackets { }
30
+ Angle Brackets < >
31
+ Dot .
32
+ Question ?
33
+ Caret ^
34
+ Dollar $
35
+ Asterisk *
36
+ Plus +
37
+
38
+
39
+ Additionally, the hyphen '-' should be the first character in a character
40
+ class if it is matching itself instead of indicated a range. It can not
41
+ be used as either the beginning or end of a range at present.
42
+
43
+ Whitespace can separate tokens in the regular expression, but the amount of
44
+ Whitespace is not significant. All tokens assume the possible existance of
45
+ whitespace in the string being matched and will not fail due to the
46
+ whitespace being there.
47
+
48
+ [Perl 6 grammars]: http://feather.perl6.nl/syn/S05.html
49
+
50
+ LICENSE:
51
+ ========
52
+
53
+ Copyright (c) 2010 Texas A&M University
54
+
55
+ Permission is hereby granted, free of charge, to any person obtaining
56
+ a copy of this software and associated documentation files (the
57
+ 'Software'), to deal in the Software without restriction, including
58
+ without limitation the rights to use, copy, modify, merge, publish,
59
+ distribute, sublicense, and/or sell copies of the Software, and to
60
+ permit persons to whom the Software is furnished to do so, subject to
61
+ the following conditions:
62
+
63
+ The above copyright notice and this permission notice shall be
64
+ included in all copies or substantial portions of the Software.
65
+
66
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
67
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
68
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
69
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
70
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
71
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
72
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
73
+
data/Rakefile ADDED
@@ -0,0 +1,63 @@
1
+ begin
2
+ require 'jeweler'
3
+ Jeweler::Tasks.new do |gem|
4
+ gem.name = "fabulator-grammar"
5
+ gem.summary = %Q{Grammar extension to Fabulator.}
6
+ gem.description = %Q{The grammar Fabulator extension provides regular expression support.}
7
+ gem.email = "jgsmith@tamu.edu"
8
+ gem.homepage = "http://github.com/jgsmith/ruby-fabulator-grammar"
9
+ gem.authors = ["James Smith"]
10
+ gem.add_dependency(%q<fabulator>, [">= 0.0.1"])
11
+ # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
12
+ end
13
+ rescue LoadError
14
+ puts "Jeweler (or a dependency) not available. This is only required if you plan to package fabulator-exhibit as a gem."
15
+ end
16
+
17
+ require 'rake'
18
+ require 'rake/rdoctask'
19
+ require 'rake/testtask'
20
+
21
+ require 'cucumber'
22
+ require 'cucumber/rake/task'
23
+
24
+ task :features => 'spec:integration'
25
+
26
+ namespace :spec do
27
+
28
+ desc "Run the Cucumber features"
29
+ Cucumber::Rake::Task.new(:integration) do |t|
30
+ t.fork = true
31
+ t.cucumber_opts = ['--format', (ENV['CUCUMBER_FORMAT'] || 'pretty')]
32
+ # t.feature_pattern = "#{extension_root}/features/**/*.feature"
33
+ t.profile = "default"
34
+ end
35
+
36
+ end
37
+
38
+ namespace :update do
39
+ desc "update the manifest"
40
+ task :manifest do
41
+ system %q[touch Manifest.txt; rake check_manifest | grep -v "(in " | patch]
42
+ end
43
+ end
44
+
45
+ desc 'Generate documentation for the fabulator exhibit extension.'
46
+ Rake::RDocTask.new(:rdoc) do |rdoc|
47
+ rdoc.rdoc_dir = 'rdoc'
48
+ rdoc.title = 'Fabulator'
49
+ rdoc.options << '--line-numbers' << '--inline-source'
50
+ rdoc.rdoc_files.include('README')
51
+ rdoc.rdoc_files.include('lib/**/*.rb')
52
+ end
53
+
54
+ # For extensions that are in transition
55
+ desc 'Test the fabulator exhibit extension.'
56
+ Rake::TestTask.new(:test) do |t|
57
+ t.libs << 'lib'
58
+ t.pattern = 'test/**/*_test.rb'
59
+ t.verbose = true
60
+ end
61
+
62
+ # Load any custom rakefiles for extension
63
+ Dir[File.dirname(__FILE__) + '/tasks/*.rake'].sort.each { |f| require f }
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.0.1
@@ -0,0 +1,88 @@
1
+ Feature: Basic regex parsing
2
+
3
+ Scenario: Parsing a simple text string
4
+ Given a context
5
+ And the prefix g as "http://dh.tamu.edu/ns/fabulator/grammar/1.0#"
6
+ When I parse the regex (foo)
7
+ Then it should match "foo"
8
+
9
+ Scenario: Parsing a simple text string
10
+ Given a context
11
+ And the prefix g as "http://dh.tamu.edu/ns/fabulator/grammar/1.0#"
12
+ When I parse the regex (foo+)
13
+ Then it should match "foo"
14
+ And it should match "foooo"
15
+ And it should not match "fo"
16
+
17
+ Scenario: Parsing a simple text string
18
+ Given a context
19
+ And the prefix g as "http://dh.tamu.edu/ns/fabulator/grammar/1.0#"
20
+ When I parse the regex (foo+?)
21
+ Then it should match "fooooooo"
22
+
23
+ Scenario: Parsing a simple text string
24
+ Given a context
25
+ And the prefix g as "http://dh.tamu.edu/ns/fabulator/grammar/1.0#"
26
+ When I parse the regex (foo{1,4}$)
27
+ Then it should match "foo"
28
+ Then it should match "fooooo"
29
+ Then it should not match "foooooo"
30
+ Then it should not match "fo"
31
+
32
+ @chars
33
+ Scenario: Parsing a simple text string
34
+ Given a context
35
+ And the prefix g as "http://dh.tamu.edu/ns/fabulator/grammar/1.0#"
36
+ When I parse the regex (^[Ff]o[-a-zA-F01234-9]+?)
37
+ Then it should match "foo"
38
+ And it should match "Foo"
39
+ And it should match "FoF03z-"
40
+ And it should not match "hellofoo"
41
+
42
+ @chars
43
+ Scenario: Parsing a simple text string
44
+ Given a context
45
+ And the prefix g as "http://dh.tamu.edu/ns/fabulator/grammar/1.0#"
46
+ When I parse the regex (^[^0-9][a-z][0-9]$)
47
+ Then it should match "fo0"
48
+ And it should not match "0l0"
49
+
50
+ Scenario: Parsing a simple text string
51
+ Given a context
52
+ And the prefix g as "http://dh.tamu.edu/ns/fabulator/grammar/1.0#"
53
+ When I parse the regex (^[^0-9].[0-9]$)
54
+ Then it should match "fo0"
55
+ And it should not match "0l0"
56
+ And it should not match "00"
57
+
58
+ @chars
59
+ Scenario: Parsing a simple text string
60
+ Given a context
61
+ And the prefix g as "http://dh.tamu.edu/ns/fabulator/grammar/1.0#"
62
+ When I parse the regex ([\[-\]]o[a-z\]A-F01234-9]+?)
63
+
64
+ Scenario: Parsing a simple text string
65
+ Given a context
66
+ And the prefix g as "http://dh.tamu.edu/ns/fabulator/grammar/1.0#"
67
+ When I parse the regex (<foo><g:bar>*)
68
+
69
+ Scenario: Parsing a simple text string
70
+ Given a context
71
+ And the prefix g as "http://dh.tamu.edu/ns/fabulator/grammar/1.0#"
72
+ When I parse the regex (bar ( <foo> <g:bar> ) *)
73
+
74
+ Scenario: Adding two numbers together as a union
75
+ Given a context
76
+ And the prefix f as "http://dh.tamu.edu/ns/fabulator/1.0#"
77
+ And the prefix g as "http://dh.tamu.edu/ns/fabulator/grammar/1.0#"
78
+ When I run the expression (g:match('^foo', 'fooo'))
79
+ Then I should get 1 item
80
+ And item 0 should be true
81
+
82
+ Scenario: Adding two numbers together as a union
83
+ Given a context
84
+ And the prefix f as "http://dh.tamu.edu/ns/fabulator/1.0#"
85
+ And the prefix g as "http://dh.tamu.edu/ns/fabulator/grammar/1.0#"
86
+ When I run the expression (g:match('^foo', 'bfooo'))
87
+ Then I should get 1 item
88
+ And item 0 should be false
@@ -0,0 +1,103 @@
1
+ require 'yaml'
2
+
3
+ Transform /^(expression|context) \((.*)\)$/ do |n, arg|
4
+ @context ||= Fabulator::Expr::Context.new
5
+ @parser ||= Fabulator::Expr::Parser.new
6
+ @parser.parse(arg, @context)
7
+ end
8
+
9
+ Transform /^\[(.*)\]$/ do |arg|
10
+ @context ||= Fabulator::Expr::Context.new
11
+ @parser ||= Fabulator::Expr::Parser.new
12
+ @parser.parse(arg, @context)
13
+ end
14
+
15
+ Transform /^(\d+)$/ do |arg|
16
+ arg.to_i
17
+ end
18
+
19
+ Given 'a context' do
20
+ @context ||= Fabulator::Expr::Context.new
21
+ @parser ||= Fabulator::Expr::Parser.new
22
+ @grammar_parser ||= Fabulator::Grammar::Parser.new
23
+ end
24
+
25
+ Given /the prefix (\S+) as "([^"]+)"/ do |p,h|
26
+ @context ||= Fabulator::Expr::Context.new
27
+ @context.set_ns(p, h)
28
+ end
29
+
30
+ Given /that (\[.*\]) is set to (\[.*\])/ do |l,r|
31
+ @context.set_value(l, r)
32
+ end
33
+
34
+ When /I run the (expression \(.*\)) in the (context \(.*\))/ do |exp, cp|
35
+ @expr = exp
36
+ if cp.nil? || cp == ''
37
+ @result = []
38
+ @cp = @context.root
39
+ else
40
+ @cp = cp.run(@context).first || @context.root
41
+ @result = @expr.run(@context.with_root(@cp))
42
+ end
43
+ end
44
+
45
+ When /I run the (expression \(.*\))/ do |exp|
46
+ ## assume '/' as the context here
47
+ @expr = exp
48
+ @cp = @data
49
+ #puts YAML::dump(@expr)
50
+ @result = @expr.run(@context.with_root(@cp))
51
+ #puts YAML::dump(@result)
52
+ end
53
+
54
+ When /I unify the types? (.*)/ do |ts|
55
+ types = ts.split(/\s*,\s*/)
56
+ typea = types.collect { |t|
57
+ pn = t.split(/:/, 2)
58
+ [ @context.get_ns(pn[0]), pn[1] ]
59
+ }
60
+ @type_result = Fabulator::ActionLib.unify_types(
61
+ types.collect { |t|
62
+ pn = t.split(/:/, 2)
63
+ [ @context.get_ns(pn[0]), pn[1] ]
64
+ }
65
+ )
66
+ end
67
+
68
+ Then /I should get the type (.*)/ do |t|
69
+ pn = t.split(/:/, 2)
70
+ @type_result[0].should == @context.get_ns(pn[0])
71
+ @type_result[1].should == pn[1]
72
+ end
73
+
74
+ Then /I should get (\d+) items?/ do |count|
75
+ @result.length.should == count
76
+ end
77
+
78
+ Then /item (\d+) should be (\[.*\])/ do |i,t|
79
+ test = t.run(@context.with_root(@cp)).first
80
+ #puts "Result: #{@result[i.to_i].to_s.class.to_s}"
81
+ @result[i.to_i].to_s.should == test.to_s
82
+ end
83
+
84
+ Then /item (\d+) should be false/ do |i|
85
+ (!!@result[i.to_i].value).should == false
86
+ end
87
+
88
+ Then /item (\d+) should be true/ do |i|
89
+ (!!@result[i.to_i].value).should == true
90
+ end
91
+
92
+ Then /the (expression \(.*\)) should equal (\[.*\])/ do |x, y|
93
+ a = x.run(@context)
94
+ b = y.run(@context)
95
+ #puts YAML::dump(a)
96
+ #puts YAML::dump(b)
97
+ #puts YAML::dump(@context)
98
+ a.first.value.should == b.first.value
99
+ end
100
+
101
+ Then /the (expression \(.*\)) should be nil/ do |x|
102
+ x.run(@context).first.should == nil
103
+ end
@@ -0,0 +1,18 @@
1
+ require 'yaml'
2
+
3
+ When /^I parse the regex \((.*)\)$/ do |regex|
4
+ @context ||= Fabulator::Expr::Context.new
5
+ @grammar_parser ||= Fabulator::Grammar::Parser.new
6
+ @regex = @grammar_parser.parse(regex, @context)
7
+ # puts YAML::dump(r)
8
+ # puts @regex.to_regex
9
+ # pending # express the regexp above with the code you wish you had
10
+ end
11
+
12
+ Then /^it should match "(.*)"$/ do |str|
13
+ str.should =~ @regex.to_regex
14
+ end
15
+
16
+ Then /^it should not match "(.*)"$/ do |str|
17
+ str.should_not =~ @regex.to_regex
18
+ end
@@ -0,0 +1,25 @@
1
+ Given /^the template$/ do |doc_xml|
2
+ @template_text = doc_xml
3
+ end
4
+
5
+ When /^I render the template$/ do
6
+ parser = Fabulator::Template::Parser.new
7
+ @template_result = parser.parse(@context, @template_text)
8
+ end
9
+
10
+ When /^I set the captions to:$/ do |caption_table|
11
+ captions = { }
12
+ caption_table.hashes.each do |h|
13
+ captions[h['path']] = h['caption']
14
+ end
15
+
16
+ @template_result.add_captions(captions)
17
+ end
18
+
19
+ Then /^the rendered text should equal$/ do |doc|
20
+ @template_result.to_s.should == %{<?xml version="1.0" encoding="UTF-8"?>\n} + doc + "\n"
21
+ end
22
+
23
+ Then /^the rendered html should equal$/ do |doc|
24
+ @template_result.to_html.should == doc
25
+ end
@@ -0,0 +1,23 @@
1
+ Given /the statemachine/ do |doc_xml|
2
+ @context ||= Fabulator::Expr::Context.new
3
+
4
+ if @sm.nil?
5
+ @sm = Fabulator::Core::StateMachine.new.compile_xml(doc_xml)
6
+ else
7
+ @sm.compile_xml(doc_xml)
8
+ end
9
+ @sm.init_context(@context)
10
+ end
11
+
12
+ When /I run it with the following params:/ do |param_table|
13
+ params = { }
14
+ param_table.hashes.each do |hash|
15
+ params[hash['key']] = hash['value']
16
+ end
17
+ @sm.run(params)
18
+ #puts YAML::dump(@sm)
19
+ end
20
+
21
+ Then /it should be in the '(.*)' state/ do |s|
22
+ @sm.state.should == s
23
+ end
@@ -0,0 +1,7 @@
1
+ # This file makes it possible to install RubyCAS-Client as a Rails plugin.
2
+
3
+ $: << File.expand_path(File.dirname(__FILE__))+'/../../lib'
4
+
5
+ require 'fabulator'
6
+ require 'fabulator/grammar'
7
+ require 'spec/expectations'
@@ -0,0 +1,16 @@
1
+ require 'fabulator/grammar/parser'
2
+ require 'fabulator/grammar/actions'
3
+ require 'fabulator/grammar/expr/rules'
4
+ require 'fabulator/grammar/expr/rule'
5
+ require 'fabulator/grammar/expr/rule_ref'
6
+ require 'fabulator/grammar/expr/text'
7
+ require 'fabulator/grammar/expr/sequence'
8
+ require 'fabulator/grammar/expr/char_set'
9
+ require 'fabulator/grammar/expr/any'
10
+
11
+ module Fabulator
12
+ module Grammar
13
+ class ParserError < StandardError
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,65 @@
1
+ #require 'fabulator/grammar/actions/grammar'
2
+
3
+ module Fabulator
4
+ GRAMMAR_NS = "http://dh.tamu.edu/ns/fabulator/grammar/1.0#"
5
+ module Grammar
6
+ module Actions
7
+ class Lib
8
+ include Fabulator::ActionLib
9
+
10
+ register_namespace GRAMMAR_NS
11
+
12
+ #action 'grammar', Grammar
13
+
14
+ ## reference a grammar name
15
+ function 'match' do |ctx, args|
16
+ # first arg is the regex or <rule name>
17
+ regex = args[0].to_s
18
+ parser = Fabulator::Grammar::Parser.new
19
+ compiled = parser.parse(regex, ctx).to_regex
20
+ if args[1].is_a?(Array)
21
+ args[1].collect{|a|
22
+ if a.to_s =~ compiled
23
+ ctx.root.anon_node(true)
24
+ else
25
+ ctx.root.anon_node(false)
26
+ end
27
+ }
28
+ elsif args[1].to_s =~ compiled
29
+ [ ctx.root.anon_node(true) ]
30
+ else
31
+ [ ctx.root.anon_node(false) ]
32
+ end
33
+ end
34
+
35
+ # function 'tokenize' do |ctx, args|
36
+ # end
37
+ end
38
+ end
39
+ end
40
+ end
41
+
42
+ # modifiers: g:minimal, g:ignore-case, g:space, g:ratchet
43
+
44
+ # need the concept of hypotheticals here
45
+
46
+ # <g:grammar g:namespace=''>
47
+ # <g:token|g:regex g:name=''>
48
+ # <g:literal />
49
+ # <g:capture>...</g:capture>
50
+ # <g:group>...</g:group>
51
+ # <g:before>...</g:before>
52
+ # <g:after>...</g:after>
53
+ # <g:not-before>...</g:not-before>
54
+ # <g:not-after>...</g:not-after>
55
+ # <g:alternatives>...</g:alternatives>
56
+ # <g:token />
57
+ # <g:one-or-more>...</g:one-or-more>
58
+ # <g:zero-or-more>...</g:zero-or-more>
59
+ # <g:zero-or-one>...</g:zero-or-one>
60
+ # <g:many g:min='' g:max=''>...</g:many>
61
+ # </g:token>
62
+ # <g:rule g:name=''>
63
+ # <g:when><g:pattern>...</g:pattern>...</g:when>
64
+ # </g:rule>
65
+ # </g:grammar>