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

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

worked on the rules for parsing lists in the grammar
allow end of field line comments
growable fields can specify a limit; e.g. "person*4" to limit to 4
use '+other' to specify that a field should have an 'Other' option (FB 3.02)
rearranged TODOs in the main documentation

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