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__ |