source: text-formbuilder/trunk/lib/Text/FormBuilder/grammar @ 87

Last change on this file since 87 was 87, checked in by peichman, 19 years ago

added !fb directive to hold FB parameters as YAML serialized values
updated and expanded the documentation

File size: 8.2 KB
Line 
1# line directives
2# block directives
3# field lines
4# comments
5
6{
7    #$::RD_TRACE = 1;
8    my (
9        @sections,     # master data structure
10        $section_head,
11        $section_id,
12        @lines,        # lines in each section
13        %lists,
14        %patterns,
15        %subs,         # validation subs
16        @group,        # current group
17        %groups,       # stored groups of fields
18        $type,
19        @options,
20        $required,
21        $multiple,
22        $list_var,
23        $size,
24        $maxlength,
25        $rows,
26        $cols,
27        @submit,
28    );
29    my $context = 'line';       # start in line context by default
30    my %formspec;
31   
32    # TODO: helper sub?
33    sub alert ($) {
34        warn '[' . (split(/::/, (caller(1))[3]))[-1] . '] ' . shift() . "\n";
35    }
36}
37
38form_spec:
39    {
40        %formspec = ();  # clear the old formspec data
41    }
42    (list_def | description_def | group_def | note | fb_params | unknown_block_directive | line)(s)
43    {
44        # grab the last section, if there is any
45        if (@lines) {
46            push @sections,
47                {
48                    id   => $section_id,
49                    head => $section_head,
50                    lines => [ @lines ],
51                };
52        }
53       
54        $return = {
55            fb_params   => $formspec{fb_params},
56            title       => $formspec{title},
57            author      => $formspec{author},
58            description => $formspec{description},
59            lists       => \%lists,
60            patterns    => \%patterns,
61            subs        => \%subs,
62            groups      => \%groups,
63            sections    => \@sections,
64            ( @submit ?
65                (submit => @submit == 1 ? $submit[0] : \@submit) :
66                ()
67            ),
68            reset       => $formspec{reset},
69        }
70    }
71
72list_def: '!list' var_name (static_list | dynamic_list)
73    { $lists{$item{var_name}} = [ @options ]; @options = () }
74
75static_list: '{' /\s*/ option(s /\s*,\s*/) /,?/ /\s*/ '}'
76
77dynamic_list: '&' <perl_codeblock>
78    { warn "[Text::FormBuilder] Dynamic lists have been removed from the formspec grammar"; }
79
80description_def: '!description' block
81    {
82        warn "[Text::FormBuilder] Description redefined at input text line $thisline\n" if defined $formspec{description};
83        $formspec{description} = $item{block};
84    }
85
86group_def: '!group' { $context = 'group' } var_name '{' field_line(s) '}' { $context = 'line' }
87    {
88        #warn "$item{var_name} group; context $context\n"
89        $groups{$item{var_name}} = [ @group ];
90        @group = ();
91    }
92
93note: '!note' block  { push @lines, [ 'note', $item{block} ]; }
94
95
96# curly-brace delimited block, that can contain properly
97# nested curly braces, along with any other characters
98# inner blocks return with the '{...}' so that nested
99# blocks get the braces treated as literals
100block: '{' <skip:''> block_content(s) '}' { join('', @{ $item[3] }) }
101inner_block: '{' <skip:''> block_content(s) '}'  { '{' . join('', @{ $item[3] }) . '}' }
102block_content: /[^\{\}]+?/ | inner_block
103
104# square brace delimited block, that can contain properly
105# nested square brackets, along with any other characters
106# inner bracket blocks return with the '[...]' so that nested
107# blocks get the braces treated as literals
108bracket_block: '[' <skip:''> bracket_block_content(s) ']' { join('', @{ $item[3] }) }
109inner_bracket_block: '[' <skip:''> bracket_block_content(s) ']' { '[' . join('', @{ $item[3] }) . ']'; }
110bracket_block_content: /[^\[\]]+?/ | inner_bracket_block
111
112
113# field lines are the subset of lines that are allowed in a !group directive
114field_line: <skip:'[ \t]*'> ( field | comment | blank ) "\n"
115
116line: <skip:'[ \t]*'> ( title | author | pattern_def | section_head | heading | submit | reset | group_field | field_group | unknown_directive | field | comment | blank ) /\n+/
117
118title: '!title' /.*/
119    {
120        warn "[Text::FormBuilder] Title redefined at input text line $thisline\n" if defined $formspec{title};
121        $formspec{title} = $item[2];
122    }
123
124author: '!author' /.*/
125    {
126        warn "[Text::FormBuilder] Author redefined at input text line $thisline\n" if defined $formspec{author};
127        $formspec{author} = $item[2];
128    }
129
130pattern_def: '!pattern' var_name pattern
131    { $patterns{$item{var_name}} = $item{pattern} }
132
133pattern: /.*/
134
135section_head: '!section' identifier /.*/
136    {
137        #warn "starting section $item{identifier}\n";
138        #warn "  with heading $item[3]\n" if $item[3];
139       
140        if (@lines) {
141            push @sections,
142                {
143                    id   => $section_id,
144                    head => $section_head,
145                    lines => [ @lines ],
146                };
147        }
148       
149        $section_id = $item{identifier};
150        $section_head = $item[3];
151        @lines = ();
152    }
153
154heading: '!head' /.*/    { push @lines, [ 'head', $item[2] ] }
155
156submit: '!submit' string(s /\s*,\s*/)
157    {
158        #warn scalar(@{ $item[2] }) . ' submit button(s)';
159        push @submit, @{ $item[2] };
160    }
161
162reset: '!reset' string
163    {
164        warn "[Text::FormBuilder] Reset button redefined at input text line $thisline\n" if defined $formspec{reset};
165        $formspec{reset} = $item{string};
166    }
167
168group_field: '!field' group_name name label(?)
169    {
170        warn "WARNING line $thisline: The '!field' directive has been DEPRECATED. Use the 'name:GROUP' style instead.\n";
171        push @lines, [ 'group', { name => $item{name}, label => $item{'label(?)'}[0], group => $item{group_name} } ];
172    }
173
174group_name: /%[A-Z_]+/
175
176# parameters that get passed to the FB constructor; these are serialized in YAML
177fb_params: '!fb' block
178    { $formspec{fb_params} .= $item{block}; }
179
180field_group: name label(?) hint(?) group_type comment(?)
181    {
182        #warn "[$thisline] comment = $item{'hint(?)'}[0]\n" if $item{'hint(?)'}[0];
183        #warn "[$thisline] field $item{name} is $item{group_type}\n";
184        push @lines, [ 'group', {
185            name    => $item{name},
186            label   => $item{'label(?)'}[0],
187            comment => $item{'hint(?)'}[0],
188            group   => $item{group_type},
189        } ];
190    }
191
192group_type: ':' var_name
193
194# this is the real heart of the thing
195field: name field_size(?) growable(?) label(?) hint(?) type(?) multi(?) other(?) default(?) option_list(?) validate(?) comment(?)
196    {   
197        my $field = {
198            name     => $item{name},
199            growable => $item{'growable(?)'}[0],
200            label    => $item{'label(?)'}[0],
201            comment  => $item{'hint(?)'}[0],
202            multiple => $item{'multi(?)'}[0],
203            type     => $item{'type(?)'}[0],
204            other    => $item{'other(?)'}[0],
205            value    => $item{'default(?)'}[0],
206            list     => $list_var,
207            validate => $item{'validate(?)'}[0],
208            required => $required,
209        };
210       
211        $$field{options} = [ @options ] if @options;
212       
213        $$field{rows} = $rows if defined $rows;
214        $$field{cols} = $cols if defined $cols;
215        $$field{size} = $size if defined $size;
216        $$field{maxlength} = $maxlength if defined $maxlength;
217       
218        #warn "[$thisline] field $item{name}; context $context\n";
219        if ($context eq 'group') {
220            push @group, $field;
221        } else {
222            push @lines, [ 'field', $field ];
223        }
224       
225        $type = undef;
226        $required = undef;
227        $multiple = undef;
228        $list_var = undef;
229        $size = undef;
230        $rows = undef;
231        $cols = undef;
232        $maxlength = undef;
233        @options = ();
234       
235        $field;
236    }
237   
238name: identifier
239
240var_name: /[A-Z_]+/
241
242field_size: '[' ( row_col | size ) ']'
243
244size: /\d+/ bang(?)
245    { $maxlength = $item[1] if $item[2][0]; $size = $item[1] }
246
247bang: '!'
248
249row_col: /\d+/ /,\s*/ /\d+/
250    { $rows = $item[1]; $cols = $item[3] }
251
252growable: '*' limit(?) { $item{'limit(?)'}[0] || 1 }
253
254limit: /\d+/
255
256label: '|' string { $item[2] }
257
258hint: bracket_block
259
260type: ':' builtin_field
261
262builtin_field: /textarea|text|password|file|checkbox|radio|select|hidden|static/
263
264multi: '*' { 1 }
265
266other: '+' 'other' { 1 }
267
268default: '=' string { $item[2] }
269
270string: simple_multiword | quoted_string
271
272# for simple multiword values not involving punctuation
273simple_multiword: /\w/ <skip:''> /[\w\t ]*/ { $item[1] . $item[3] }
274
275# my attempt at a single-quoted, non-interpolating string
276# where the backslash can escape literal single quotes
277quoted_string: "'" <skip:''> /(\\'|[^'])*/ "'"
278    { $item[3] =~ s/\\'/'/g; $item[3] }
279
280option_list: options | list_var
281   
282options: '{' option(s /\s*,\s*/) '}'
283
284list_var: /@[A-Z_]+/ { $list_var = $item[1] }
285
286option: string display_text(?)
287    { push @options, { $item[1] => $item{'display_text(?)'}[0] } }
288
289value: identifier
290
291display_text: bracket_block
292
293
294validate: '//' (optional_pattern | required_pattern)
295
296optional_pattern: var_name '?'  { $required = 0; $item[1] }
297
298required_pattern: var_name { $required = 1; $item[1] }
299
300comment: '#' /.*/
301blank:
302
303identifier: /\w+/
304
305# skip unknown directives with a warning
306unknown_directive: /\!\S*/ /.*/
307    { warn "[Text::Formbuilder] Skipping unknown directive '$item[1]' at input text line $thisline\n"; }
308
309unknown_block_directive: /\!\S*/ var_name(?) block
310    { warn "[Text::Formbuilder] Skipping unknown block directive '$item[1]' at input text line $thisline\n"; }
Note: See TracBrowser for help on using the repository browser.