We’ve been busy getting a couple of different things done. These will show up in the next releases of the core Fabulator gem and the grammar gem.
Grammars
The following test grammar works.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
<g:grammar xmlns:g="http://dh.tamu.edu/ns/fabulator/grammar/1.0#">
<g:token g:name="LETTER" g:matches="[:alpha:]" />
<g:token g:name="NUMBER" g:matches="[:digit:]" />
<g:rule g:name="something">
<g:when g:matches="LETTER NUMBER LETTER" />
</g:rule>
<g:rule g:name="other">
<g:when g:matches="a := LETTER b := NUMBER c := LETTER" />
</g:rule>
<g:rule g:name="or">
<g:when g:matches="other(s) d := LETTER(s)" />
</g:rule>
<g:rule g:name="ooor">
<g:when g:matches="other(s ',') d := LETTER(s)" />
</g:rule>
</g:grammar>
|
This is a little different than what I mentioned in the previous post. Tokens are the same, but the rules are very different. They borrow some ideas from the Perl Parse::RecDescent module with a little Perl 6 thrown in.
None of the rules are anchored, so they will skip any leading text until they find something that matches.
The ‘something’ rule will match any sequence of letter-number-letter and return a structure representing the matches. For example, matching ‘a1c’ will result in ./LETTER equalling ‘a’ and ‘c’ and ./NUMBER equalling ‘1’.
The ‘other’ rule will match the same sequence as the ‘something’ rule, but instead of naming the resulting values LETTER and NUMBER, it will use ‘a’, ‘b’, ‘c’ as the names (the := construct). In the case of matching against ‘a1c’, the result is ./a = ‘a’, ./b = ‘1’, ./c = ‘c’.
Things start getting interesting with the ‘or’ rule. This will match any sequence of one or more ‘other’ rules followed by one or more LETTER tokens.
Finally, the ‘ooor’ rule will match one or more ‘other’ rules that are separated by commas followed by one or more LETTER tokens.
We’ll start getting modes and additional quantifiers working next.
Structure
We’ve had the Fabulator::Action class for a while for defining new actions, but structural elements have still been hand-coded with a lot of duplicated code. Today’s work changes that.
The first thing is that in the action lib class that defines all of the components of the library (and we’ll probably be changing ‘ActionLib’ to ‘TagLib’), you specify that an element is structural like so (borrowed from the core library):
1
2
3
4
5
6
7
8
9
10
11
12
13
|
structural 'application', Fabulator::Core::StateMachine
structural 'view', Fabulator::Core::State
structural 'goes-to', Fabulator::Core::Transition
structural 'params', Fabulator::Core::Group
structural 'group', Fabulator::Core::Group
structural 'param', Fabulator::Core::Parameter
structural 'value', Fabulator::Core::Constraint
structural 'constraint', Fabulator::Core::Constraint
structural 'filter', Fabulator::Core::Filter
structural 'sort', Sort
structural 'when', When
structural 'otherwise', When
|
Each of these can be used in an action or structural class as a structural element instead of an action.
For example, the view element class is defined as follows (minus some run-time stuff):
1
2
3
4
5
6
7
8
9
10
|
class State < Fabulator::Structural
attr_accessor :name, :transitions
namespace Fabulator::FAB_NS
attribute :name, :static => true
contains 'goes-to', :as => :transitions
end
|
Parameters (the ‘param’ element):
1
2
3
4
5
6
7
8
9
10
11
12
13
|
class Parameter < Fabulator::Structural
attr_accessor :name
namespace Fabulator::FAB_NS
attribute :name, :eval => false, :static => true
attribute :required, :static => true, :default => 'false'
contains :constraint
contains :filter
contains :value, :as => :constraints
end
|
By default, the contained structural elements are stored in an instance variable named after the element, but pluralized (e.g., ‘constraint’ elements will be stored in @constraints). By default, this is an array. It can be a hash if the ‘:storage => :hash’ option is set. If no ‘:name => :method’ (for whichever method is appropriate) option is given, it defaults to the ‘name’ method to determine the key for the hash. If two structural elements are assigned to the same variable, they are combined instead of one overwriting the other.
This gets us a little closer to being able to have libraries or packages as useful concepts in the Fabulator+Radiant system.