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

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

fixed grammar so that outer {} and [] are no longer included in the (bracket_)block values
using bracket_block instead of hardcoded pattern for matching hints in formspec
removed dynamic lists from the grammar
some general code cleanup and tidying, in FormBuilder.pm and grammar
added fb.pl to the MANIFEST
bumped back FB version requirement to 3.0 (Makefile.PL)
test for FB3.02 for using growable fields; warn and ignore if verison isn't high enough

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