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

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

added a !reset directive to name and include a reset button

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