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
Line 
1{
2    my (
3        $context,      # line or group
4        @sections,     # master data structure
5        $section_head,
6        $section_id,
7        @lines,        # lines in each section
8        $title,
9        $author,
10        $description,
11        %lists,
12        %patterns,
13        %subs,         # validation subs
14        @group,        # current group
15        %groups,       # stored groups of fields
16        $type,
17        @options,
18        $required,
19        $list_var,
20        $size,
21        $maxlength,
22        $rows,
23        $cols,
24    );
25    $context = 'line';
26}
27
28form_spec: (list_def | description_def | validate_def | group_def | note | line)(s)
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 = ();
43        $return = {
44            title    => $title,
45            author   => $author,
46            description => $description,
47            lists    => \%lists,
48            patterns => \%patterns,
49            subs     => \%subs,
50            groups   => \%groups,
51            sections => \@sections,
52        }
53    }
54
55list_def: '!list' var_name (static_list | dynamic_list)
56    { $lists{$item{var_name}} = [ @options ]; @options = () }
57
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
70description_def: '!description' block
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
77validate_def: '!validate' var_name <perl_codeblock>
78    { $subs{$item{var_name}} = eval "sub $item[3]" }
79
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    }
86
87note: '!note' block
88    {   
89        (my $note = $item[2]) =~ s/^{\s*|\s*}$//g;
90        push @lines, [ 'note', $note ];
91    }
92
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    }
101
102block_content: /[^\{\}]+?/ | block
103
104
105field_line: <skip:'[ \t]*'> ( field | comment | blank ) "\n"
106line: <skip:'[ \t]*'> ( title | author | pattern_def | section_head | heading | group_field | unknown_directive | field | comment | blank ) "\n"
107
108title: '!title' /.*/
109    {
110        warn "[Text::FormBuilder] Title redefined at input text line $thisline\n" if defined $title;
111        $title = $item[2];
112    }
113
114author: '!author' /.*/
115    {
116        warn "[Text::FormBuilder] Author redefined at input text line $thisline\n" if defined $author;
117        $author = $item[2];
118    }
119
120pattern_def: '!pattern' var_name pattern
121    { $patterns{$item{var_name}} = $item{pattern} }
122
123pattern: /.*/
124
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
144heading: '!head' /.*/    { push @lines, [ 'head', $item[2] ] }
145
146group_field: '!field' group_name name label(?)
147    {
148        push @lines, [ 'group', { name => $item{name}, label => $item{'label(?)'}[0], group => $item{group_name} } ];
149    }
150
151group_name: /%[A-Z_]+/
152
153field: name field_size(?) growable(?) label(?) hint(?) type(?) default(?) option_list(?) validate(?)
154    {
155        my $field = {
156            name     => $item{name},
157            growable => ($item{'growable(?)'}[0] ? 1 : 0),
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],
164            required => $required || 0,
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;
172        $$field{maxlength} = $maxlength if defined $maxlength;
173       
174        #warn "[$thisline] field $item{name}; context $context\n";
175        if ($context eq 'group') {
176            push @group, $field;
177        } else {
178            push @lines, [ 'field', $field ];
179        }
180       
181        $type = undef;
182        $required = 0;
183        $list_var = undef;
184        $size = undef;
185        $rows = undef;
186        $cols = undef;
187        $maxlength = undef;
188        @options = ();
189       
190    }
191   
192name: identifier
193
194var_name: /[A-Z_]+/
195
196field_size: '[' ( row_col | size ) ']'
197
198size: /\d+/ bang(?)
199    { $maxlength = $item[1] if $item[2][0]; $size = $item[1] }
200
201bang: '!'
202
203row_col: /\d+/ /,\s*/ /\d+/
204    { $rows = $item[1]; $cols = $item[3] }
205
206growable: '*'
207
208label: '|' (simple_multiword | quoted_string) { $item[2] }
209
210hint: '[' /[^\]]+/ ']'    { $item[2] }
211
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' } )
214
215builtin_field: /textarea|text|password|file|checkbox|radio|select|hidden|static/
216
217
218default: '=' (simple_multiword | quoted_string) { $item[2] }
219
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
228option_list: options | list_var
229   
230options: '{' option(s /,\s*/) '}'
231
232list_var: /@[A-Z_]+/ { $list_var = $item[1] }
233
234option: (simple_multiword | value | quoted_string) display_text(?)
235    { push @options, { $item[1] => $item{'display_text(?)'}[0] } }
236
237value: identifier
238
239display_text: brace_block
240    { (my $text = $item[1]) =~ s/^\[\s*|\s*\]$//g; $text }
241
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
253validate: '//' (optional_pattern | required_pattern)    { $item[2] }
254
255optional_pattern: /[A-Z_]+/ '?' { $required = 0; $item[1] }
256
257required_pattern: /[A-Z_]+/ { $required = 1; $item[1] }
258
259comment: '#' /.*/
260blank:
261
262identifier: /\w+/
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.