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

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

added a !submit directive to rename the submit button or to have multiple submit buttons
upped version number

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