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
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    }
[87]42    (list_def | description_def | group_def | note | fb_params | 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 = {
[87]55            fb_params   => $formspec{fb_params},
56            title       => $formspec{title},
57            author      => $formspec{author},
[77]58            description => $formspec{description},
[87]59            lists       => \%lists,
60            patterns    => \%patterns,
61            subs        => \%subs,
62            groups      => \%groups,
63            sections    => \@sections,
[80]64            ( @submit ?
65                (submit => @submit == 1 ? $submit[0] : \@submit) :
66                ()
67            ),
[87]68            reset       => $formspec{reset},
[1]69        }
70    }
71
[16]72list_def: '!list' var_name (static_list | dynamic_list)
73    { $lists{$item{var_name}} = [ @options ]; @options = () }
[1]74
[66]75static_list: '{' /\s*/ option(s /\s*,\s*/) /,?/ /\s*/ '}'
[10]76
77dynamic_list: '&' <perl_codeblock>
[77]78    { warn "[Text::FormBuilder] Dynamic lists have been removed from the formspec grammar"; }
[10]79
[57]80description_def: '!description' block
[64]81    {
[77]82        warn "[Text::FormBuilder] Description redefined at input text line $thisline\n" if defined $formspec{description};
83        $formspec{description} = $item{block};
[14]84    }
85
[21]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    }
[1]92
[64]93note: '!note' block  { push @lines, [ 'note', $item{block} ]; }
[50]94
[64]95
[57]96# curly-brace delimited block, that can contain properly
[64]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
[50]103
[64]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
[57]111
112
[64]113# field lines are the subset of lines that are allowed in a !group directive
[21]114field_line: <skip:'[ \t]*'> ( field | comment | blank ) "\n"
[64]115
[81]116line: <skip:'[ \t]*'> ( title | author | pattern_def | section_head | heading | submit | reset | group_field | field_group | unknown_directive | field | comment | blank ) /\n+/
[21]117
[1]118title: '!title' /.*/
[28]119    {
[77]120        warn "[Text::FormBuilder] Title redefined at input text line $thisline\n" if defined $formspec{title};
121        $formspec{title} = $item[2];
[28]122    }
[1]123
124author: '!author' /.*/
[28]125    {
[77]126        warn "[Text::FormBuilder] Author redefined at input text line $thisline\n" if defined $formspec{author};
127        $formspec{author} = $item[2];
[28]128    }
[1]129
[16]130pattern_def: '!pattern' var_name pattern
131    { $patterns{$item{var_name}} = $item{pattern} }
[1]132
133pattern: /.*/
134
[29]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
[28]154heading: '!head' /.*/    { push @lines, [ 'head', $item[2] ] }
[11]155
[80]156submit: '!submit' string(s /\s*,\s*/)
157    {
158        #warn scalar(@{ $item[2] }) . ' submit button(s)';
159        push @submit, @{ $item[2] };
160    }
161
[81]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
[21]168group_field: '!field' group_name name label(?)
[74]169    {
[77]170        warn "WARNING line $thisline: The '!field' directive has been DEPRECATED. Use the 'name:GROUP' style instead.\n";
[28]171        push @lines, [ 'group', { name => $item{name}, label => $item{'label(?)'}[0], group => $item{group_name} } ];
[21]172    }
173
174group_name: /%[A-Z_]+/
175
[87]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
[73]180field_group: name label(?) hint(?) group_type comment(?)
181    {
[74]182        #warn "[$thisline] comment = $item{'hint(?)'}[0]\n" if $item{'hint(?)'}[0];
[63]183        #warn "[$thisline] field $item{name} is $item{group_type}\n";
[73]184        push @lines, [ 'group', {
185            name    => $item{name},
186            label   => $item{'label(?)'}[0],
187            comment => $item{'hint(?)'}[0],
188            group   => $item{group_type},
189        } ];
[63]190    }
191
192group_type: ':' var_name
193
[64]194# this is the real heart of the thing
[84]195field: name field_size(?) growable(?) label(?) hint(?) type(?) multi(?) other(?) default(?) option_list(?) validate(?) comment(?)
[87]196    {   
[1]197        my $field = {
198            name     => $item{name},
[66]199            growable => $item{'growable(?)'}[0],
[1]200            label    => $item{'label(?)'}[0],
201            comment  => $item{'hint(?)'}[0],
[84]202            multiple => $item{'multi(?)'}[0],
[1]203            type     => $item{'type(?)'}[0],
[66]204            other    => $item{'other(?)'}[0],
[1]205            value    => $item{'default(?)'}[0],
[63]206            list     => $list_var,
207            validate => $item{'validate(?)'}[0],
[68]208            required => $required,
[1]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;
[56]216        $$field{maxlength} = $maxlength if defined $maxlength;
[1]217       
[28]218        #warn "[$thisline] field $item{name}; context $context\n";
[21]219        if ($context eq 'group') {
220            push @group, $field;
221        } else {
222            push @lines, [ 'field', $field ];
223        }
[1]224       
225        $type = undef;
[68]226        $required = undef;
[84]227        $multiple = undef;
[1]228        $list_var = undef;
229        $size = undef;
230        $rows = undef;
231        $cols = undef;
[56]232        $maxlength = undef;
[1]233        @options = ();
[63]234       
[62]235        $field;
[1]236    }
237   
238name: identifier
239
[16]240var_name: /[A-Z_]+/
241
[1]242field_size: '[' ( row_col | size ) ']'
243
[56]244size: /\d+/ bang(?)
245    { $maxlength = $item[1] if $item[2][0]; $size = $item[1] }
[1]246
[56]247bang: '!'
248
[1]249row_col: /\d+/ /,\s*/ /\d+/
250    { $rows = $item[1]; $cols = $item[3] }
251
[66]252growable: '*' limit(?) { $item{'limit(?)'}[0] || 1 }
[61]253
[66]254limit: /\d+/
255
[77]256label: '|' string { $item[2] }
[1]257
[64]258hint: bracket_block
[1]259
[63]260type: ':' builtin_field
[1]261
[55]262builtin_field: /textarea|text|password|file|checkbox|radio|select|hidden|static/
263
[84]264multi: '*' { 1 }
265
[66]266other: '+' 'other' { 1 }
[55]267
[77]268default: '=' string { $item[2] }
[1]269
[77]270string: simple_multiword | quoted_string
271
[22]272# for simple multiword values not involving punctuation
[80]273simple_multiword: /\w/ <skip:''> /[\w\t ]*/ { $item[1] . $item[3] }
[22]274
275# my attempt at a single-quoted, non-interpolating string
276# where the backslash can escape literal single quotes
[80]277quoted_string: "'" <skip:''> /(\\'|[^'])*/ "'"
[22]278    { $item[3] =~ s/\\'/'/g; $item[3] }
279
[1]280option_list: options | list_var
281   
[77]282options: '{' option(s /\s*,\s*/) '}'
[1]283
284list_var: /@[A-Z_]+/ { $list_var = $item[1] }
285
[77]286option: string display_text(?)
[23]287    { push @options, { $item[1] => $item{'display_text(?)'}[0] } }
[1]288
289value: identifier
290
[64]291display_text: bracket_block
[1]292
[59]293
[64]294validate: '//' (optional_pattern | required_pattern)
[59]295
[64]296optional_pattern: var_name '?'  { $required = 0; $item[1] }
[1]297
[64]298required_pattern: var_name { $required = 1; $item[1] }
[24]299
[1]300comment: '#' /.*/
301blank:
302
303identifier: /\w+/
[16]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"; }
[80]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.