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

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

fixed some typos in the docs
rearranged internals in the grammar, plus a minor bugfix for the skip pattern of inline lists (now allows padding space around the comma)
added new test scripts to the MANIFEST

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