File Coverage

blib/lib/Soar/Production/Parser/PRDGrammar.pm
Criterion Covered Total %
statement 6 6 100.0
branch n/a
condition n/a
subroutine 2 2 100.0
pod n/a
total 8 8 100.0


line stmt bran cond sub pod time code
1             #
2             # This file is part of Soar-Production
3             #
4             # This software is copyright (c) 2012 by Nathan Glenn.
5             #
6             # This is free software; you can redistribute it and/or modify it under
7             # the same terms as the Perl 5 programming language system itself.
8             #
9             package Soar::Production::Parser::PRDGrammar;
10             # ABSTRACT: Parse::RecDescent grammar for Soar productions
11              
12 26     26   733 use strict;
  26         50  
  26         838  
13 26     26   131 use warnings;
  26         49  
  26         3799  
14              
15             our $VERSION = '0.03'; # VERSION
16              
17             #this grammar will return a parse tree of a production
18             our $GRAMMAR = <<'EOGRAMMAR';
19            
20             parse:
21            
22             (?: \s+ # Whitespace
23             | (?:;\s*)?\# [^\n]* \n? # End of line comment
24             )
25             )*}> production /\Z/
26             {
27             $item[2]
28             }
29             #future work: can be sp or tp (normal or template production)
30             production: /sp/ "{" beginning LHS "-->" RHS "}"
31             {
32             my %return;
33             @return{ keys %{$item[3]}} = values %{$item[3]};
34             $return{LHS} = $item{LHS};
35             $return{RHS} = $item{RHS}->{rhsActions};
36             \%return;
37             }
38             beginning: prodname documentation(?) flag(s?)
39             {
40             {name => $item[1], doc => $item[2] ? $item[2][0] : undef, flags => $item[3]}
41             }
42             prodname: /[\dA-Za-z][\dA-Za-z\$%&*=>
43              
44             #documentation can span many lines
45             documentation: '"' /[^"]*/ms '"'
46             {
47             $item[3]
48             }
49             flag: ':' /o-support|i-support|chunk|default|interrupt|template/
50             LHS: cond(s)
51             {
52             { conditions => $item[1] }
53             }
54             condType: "state" | "impasse"
55             cond:
56             positiveCond
57             {
58             { negative => 'no', condition => $item{positiveCond} }
59             }
60             | negativeCond
61             {
62             { negative => 'yes', condition => $item{negativeCond} }
63             }
64             negativeCond: "-" positiveCond
65             positiveCond: condsForOneId
66             {
67             $item{condsForOneId};
68             }
69             | "{" cond(s) "}"
70             {
71             { 'conjunction' => $item[3] }
72             }
73             condsForOneId: "(" condType(?) idTest(?) attrValueTests(s?) ")"
74             #only a state_imp_cond can be missing an idTest or attrValueTests
75            
76             not defined $item[3] and (
77             not defined $item[4] or $#{$item[5]} == -1
78             )
79             } >
80             {
81             {
82             condType => ($#{$item[3]} != -1 ? $item[3][0] : undef),
83             idTest => ($#{$item[4]} != -1 ? $item[4][0]->{test} : undef),
84             attrValueTests => $item[5],
85             }
86             }
87             idTest: test
88             {
89             { test => $item{test} }
90             }
91             attrValueTests: /-?/ attTest valueTest(s?)
92             {
93             {
94             negative => ($item[1] ? 'yes' : 'no'),
95             attrs => $item[2],
96             values => $item[3],
97             }
98             }
99             attTest: "^" test(s /\./)
100             valueTest: test /\+?/
101             {
102             {
103             test => $item{test},
104             '+' => ($item[2] ? 'yes' : 'no'),
105             }
106             }
107             | condsForOneId /\+?/
108             {
109             {
110             conds => $item{condsForOneId},
111             '+' => ($item[2] ? 'yes' : 'no'),
112             }
113             }
114             test:
115             conjunctiveTest
116             {
117             { conjunctiveTest => $item{conjunctiveTest} }
118             }
119             | simpleTest
120             {
121             { simpleTest => $item{simpleTest} }
122             }
123             conjunctiveTest: "\{" simpleTest(s) "\}"
124             {
125             $item[3]
126             }
127             simpleTest:
128             disjunctionTest
129             {
130             { disjunctionTest => $item{disjunctionTest} }
131             }
132             | relationalTest
133             {
134             { relationTest => $item{relationalTest} }
135             }
136             | singleTest
137             {
138             $item{singleTest}
139             }
140             disjunctionTest: /<<(?=\s)/ constant(s) />>/ #don't have to worry about look for whitespace on second one; if no space is there, the parser will think it's a string and fail.
141             {
142             $item[3]
143             }
144             relationalTest: relation singleTest #note that I removed a (?) from relation, and added singleTest to simpleTest
145             {
146             { relation => ($item{relation} || undef), test => $item{singleTest} }
147             }
148             # /<(?=\s)/ ensures we don't match the beginning of a variable
149             relation: "<=>" | "<>" | "<=" | ">=" | ">" | /<(?=\s)/ | "="
150              
151             singleTest:
152             variable
153             {
154             $item{variable}
155             }
156             | constant
157             {
158             $item{constant}
159             }
160              
161             #change skip so we can't have
162             variable: /<[A-Za-z0-9\$%&*+\/:=?_<>-]+(?)>/
163             {
164             $item[1] =~ s/^<(.*)>$/$1/;
165             {variable => $item[1] }
166             }
167              
168             RHS: rhsAction(s?)
169             {
170             { rhsActions => $item[1] }
171             }
172              
173             rhsAction:
174             funcCall
175             {
176             { funcCall => $item{funcCall} }
177             }
178             | "(" variable attrValueMake(s) ")"
179             {
180             { variable => $item{variable}->{variable}, attrValueMake => $item[3] }
181             }
182              
183             funcCall: "(" funcName rhsValue(s?) ")"
184             {
185             { function => $item{funcName}, args => $item[4] }
186             }
187             funcName: "+" | "-" | "*" | "/" | symConstant
188             rhsValue: variable | constant | "(crlf)" | funcCall
189             attrValueMake: valueMake(s)
190             {
191             { attr => $item[1], valueMake => $item[2] }
192             }
193             attr: "^" variableOrSymConstant
194             variableOrSymConstant: variable | symConstant
195             valueMake: rhsValue preferenceSpecifier(s?)
196             {
197             #add an acceptable preference if no preference is specified
198             my $preferences = $item[2];
199             if($#$preferences == -1){
200             $preferences = [{
201             'value' => '+',
202             'type' => 'unary'
203             }];
204             }
205             { rhsValue => $item{rhsValue}, preferences => $preferences }
206             }
207              
208             preferenceSpecifier:
209             unaryOrBinaryPreference rhsValue comma(?)
210             {
211             { type => 'binary', value => $item{unaryOrBinaryPreference}, compareTo => $item{rhsValue} }
212             }
213             | unaryPreference comma(?)
214             {
215             { type => 'unary', value => $item{unaryPreference} };
216             }
217              
218             comma: ","
219              
220             unaryPreference: "+" | "-" | "!" | "~" | "@" | unaryOrBinaryPreference
221              
222             #negative lookahead necessary to prevent matching as two specifiers and a constant
223             unaryOrBinaryPreference: ">" | ...!variable "<" | "=" | "&"
224              
225             #put float and int first, since symConstant can technically match the same values.
226             constant:
227             floatConstant
228             {
229             { constant => $item{floatConstant}, type => 'float' }
230             }
231             | intConstant
232             {
233             { constant => $item{intConstant}, type => 'int' }
234             }
235             | symConstant
236             {
237             { constant => $item{symConstant}, type => 'sym' }
238             }
239             symConstant: string { $item{string} } | quoted { $item{quoted} }
240             string: /[A-Za-z0-9\$%&*+\/:=?_><-]+/
241             $/} > #reject if we've actually found a variable
242            
243             $item[1] =~ /^ [+!~><=-]+ $/x and
244             $item[1] !~ /^ (?: >< | [<>]{3,}) $/x
245              
246             }
247             > #reject if the name contains only preference characters
248            
249             # "" look like typos. Could have missed a pointy brace.
250             if( $item[1] =~ /^<.*|.*>$/ ){
251             use Carp;
252             carp "Suspicious string constant: \"$item[1]\". Did you mean to use a variable or disjunction?";
253             }
254             }>
255             {
256             # "" look like typos. Could have missed a pointy brace. Convert to quoted like Soar does.
257             if( $item[1] =~ /^<.*|.*>$/ ){
258             $return = { type => 'quoted', value => $item[1] };
259             }else{
260             $return = { type => 'string', value => $item[1] };
261             }
262             }
263             #TODO: note that in Soar, || is ignored and treated like .
264             quoted: /\|(?:\\[|]|[^|])*\|/
265             {
266             #remove leading and trailing vertical bar
267             $item[1] =~ s{^\|}{};
268             $item[1] =~ s{\|$}{};
269              
270             #unescape other vertical bars
271             $item[1] =~ s{\\\|}{|}g;
272              
273             { type => 'quoted', value => $item[1] }
274             }
275             intConstant: /-?[0-9]+/
276             floatConstant:
277             scientific { $item{scientific} }
278             | normal { $item{normal} }
279              
280             #strangely enough, the section after the period is optional; '1.' is legal.
281             normal: /^[-+]?[0-9]*\.[0-9]*/
282             scientific: /[+-]?[0-9]\.[0-9]+[eE][-+]?[0-9]+/
283             EOGRAMMAR
284              
285             1;
286              
287             __END__