fabulator-grammar 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
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>