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

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

added growable field support (release pending release of FB 3.002)
upped FB required version to 3.001
added a belated thank you to Nate

File size: 6.6 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    {
62        my @results = (eval $item[2]);
63        if (ref $results[0] eq 'HASH') {
64            @options = @results;
65        } else {   
66            @options = map { { $_ => $_ } } @results;
67        }
68    }
69
[57]70description_def: '!description' block
[14]71    { warn "[Text::FormBuilder] Description redefined at input text line $thisline\n" if defined $description;
72   
73    $description = $item[2];
74    $description =~ s/^{\s*|\s*}$//g;
75    }
76
[39]77validate_def: '!validate' var_name <perl_codeblock>
[42]78    { $subs{$item{var_name}} = eval "sub $item[3]" }
[39]79
[21]80group_def: '!group' { $context = 'group' } var_name '{' field_line(s) '}' { $context = 'line' }
81    {
82        #warn "$item{var_name} group; context $context\n"
83        $groups{$item{var_name}} = [ @group ];
84        @group = ();
85    }
[1]86
[57]87note: '!note' block
[50]88    {   
89        (my $note = $item[2]) =~ s/^{\s*|\s*}$//g;
90        push @lines, [ 'note', $note ];
91    }
92
[57]93# curly-brace delimited block, that can contain properly
94# nested curly brackets, along with any other characters
95# return with the '{...}' so that nested blocks get the
96# brackets treated as literals
97block: '{' <skip:''> block_content(s) '}'
98    {
99        '{' . join('', @{ $item[3] }) . '}';
100    }
[50]101
[57]102block_content: /[^\{\}]+?/ | block
103
104
[21]105field_line: <skip:'[ \t]*'> ( field | comment | blank ) "\n"
[29]106line: <skip:'[ \t]*'> ( title | author | pattern_def | section_head | heading | group_field | 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
[61]153field: name field_size(?) growable(?) label(?) hint(?) type(?) default(?) option_list(?) validate(?)
[1]154    {
155        my $field = {
156            name     => $item{name},
[61]157            growable => ($item{'growable(?)'}[0] ? 1 : 0),
[1]158            label    => $item{'label(?)'}[0],
159            comment  => $item{'hint(?)'}[0],
160            type     => $item{'type(?)'}[0],
161            value    => $item{'default(?)'}[0],
162            list     => $list_var,
163            validate => $item{'validate(?)'}[0],
[24]164            required => $required || 0,
[1]165        };
166       
167        $$field{options} = [ @options ] if @options;
168       
169        $$field{rows} = $rows if defined $rows;
170        $$field{cols} = $cols if defined $cols;
171        $$field{size} = $size if defined $size;
[56]172        $$field{maxlength} = $maxlength if defined $maxlength;
[1]173       
[28]174        #warn "[$thisline] field $item{name}; context $context\n";
[21]175        if ($context eq 'group') {
176            push @group, $field;
177        } else {
178            push @lines, [ 'field', $field ];
179        }
[1]180       
181        $type = undef;
[24]182        $required = 0;
[1]183        $list_var = undef;
184        $size = undef;
185        $rows = undef;
186        $cols = undef;
[56]187        $maxlength = undef;
[1]188        @options = ();
189       
190    }
191   
192name: identifier
193
[16]194var_name: /[A-Z_]+/
195
[1]196field_size: '[' ( row_col | size ) ']'
197
[56]198size: /\d+/ bang(?)
199    { $maxlength = $item[1] if $item[2][0]; $size = $item[1] }
[1]200
[56]201bang: '!'
202
[1]203row_col: /\d+/ /,\s*/ /\d+/
204    { $rows = $item[1]; $cols = $item[3] }
205
[61]206growable: '*'
207
[22]208label: '|' (simple_multiword | quoted_string) { $item[2] }
[1]209
210hint: '[' /[^\]]+/ ']'    { $item[2] }
211
[55]212# TODO: differentiate between builtins and custom field groups
213type: ':' ( builtin_field | var_name { warn "[Text::FormBuilder] Using field group names directly is not (yet) supported (input line $thisline)\n"; 'text' } )
[1]214
[55]215builtin_field: /textarea|text|password|file|checkbox|radio|select|hidden|static/
216
217
[22]218default: '=' (simple_multiword | quoted_string) { $item[2] }
[1]219
[22]220# for simple multiword values not involving punctuation
221simple_multiword: <skip:''> /[\w\t ]+/ { $item[2] }
222
223# my attempt at a single-quoted, non-interpolating string
224# where the backslash can escape literal single quotes
225quoted_string: <skip:''> "'" /(\\'|[^'])*/ "'"
226    { $item[3] =~ s/\\'/'/g; $item[3] }
227
[1]228option_list: options | list_var
229   
230options: '{' option(s /,\s*/) '}'
231
232list_var: /@[A-Z_]+/ { $list_var = $item[1] }
233
[25]234option: (simple_multiword | value | quoted_string) display_text(?)
[23]235    { push @options, { $item[1] => $item{'display_text(?)'}[0] } }
[1]236
237value: identifier
238
[59]239display_text: brace_block
240    { (my $text = $item[1]) =~ s/^\[\s*|\s*\]$//g; $text }
[1]241
[59]242# square brace delimited block, that can contain properly
243# nested square braces, along with any other characters
244# return with the '[...]' so that nested blocks get the
245# braces treated as literals
246brace_block: '[' <skip:''> brace_block_content(s) ']'
247    {
248        '[' . join('', @{ $item[3] }) . ']';
249    }
250brace_block_content: /[^\[\]]+?/ | brace_block
251
252
[24]253validate: '//' (optional_pattern | required_pattern)    { $item[2] }
[1]254
[24]255optional_pattern: /[A-Z_]+/ '?' { $required = 0; $item[1] }
256
257required_pattern: /[A-Z_]+/ { $required = 1; $item[1] }
258
[1]259comment: '#' /.*/
260blank:
261
262identifier: /\w+/
[16]263
264# skip unknown directives with a warning
265unknown_directive: /\!\S*/ /.*/
266    { warn "[Text::Formbuilder] Skipping unknown directive '$item[1]' at input text line $thisline\n"; }
Note: See TracBrowser for help on using the repository browser.