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

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

added support for the 'multiple' attribute on fields

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