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

Last change on this file since 59 was 59, checked in by peichman, 20 years ago

added a class="fieldgroup" attr to field group spans
added a brace_block to the parser, to parse [...] with nested [...]'s

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
[1]153field: name field_size(?) label(?) hint(?) type(?) default(?) option_list(?) validate(?)
154    {
155        my $field = {
156            name     => $item{name},
157            label    => $item{'label(?)'}[0],
158            comment  => $item{'hint(?)'}[0],
159            type     => $item{'type(?)'}[0],
160            value    => $item{'default(?)'}[0],
161            list     => $list_var,
162            validate => $item{'validate(?)'}[0],
[24]163            required => $required || 0,
[1]164        };
165       
166        $$field{options} = [ @options ] if @options;
167       
168        $$field{rows} = $rows if defined $rows;
169        $$field{cols} = $cols if defined $cols;
170        $$field{size} = $size if defined $size;
[56]171        $$field{maxlength} = $maxlength if defined $maxlength;
[1]172       
[28]173        #warn "[$thisline] field $item{name}; context $context\n";
[21]174        if ($context eq 'group') {
175            push @group, $field;
176        } else {
177            push @lines, [ 'field', $field ];
178        }
[1]179       
180        $type = undef;
[24]181        $required = 0;
[1]182        $list_var = undef;
183        $size = undef;
184        $rows = undef;
185        $cols = undef;
[56]186        $maxlength = undef;
[1]187        @options = ();
188       
189    }
190   
191name: identifier
192
[16]193var_name: /[A-Z_]+/
194
[1]195field_size: '[' ( row_col | size ) ']'
196
[56]197size: /\d+/ bang(?)
198    { $maxlength = $item[1] if $item[2][0]; $size = $item[1] }
[1]199
[56]200bang: '!'
201
[1]202row_col: /\d+/ /,\s*/ /\d+/
203    { $rows = $item[1]; $cols = $item[3] }
204
[22]205label: '|' (simple_multiword | quoted_string) { $item[2] }
[1]206
207hint: '[' /[^\]]+/ ']'    { $item[2] }
208
[55]209# TODO: differentiate between builtins and custom field groups
210type: ':' ( builtin_field | var_name { warn "[Text::FormBuilder] Using field group names directly is not (yet) supported (input line $thisline)\n"; 'text' } )
[1]211
[55]212builtin_field: /textarea|text|password|file|checkbox|radio|select|hidden|static/
213
214
[22]215default: '=' (simple_multiword | quoted_string) { $item[2] }
[1]216
[22]217# for simple multiword values not involving punctuation
218simple_multiword: <skip:''> /[\w\t ]+/ { $item[2] }
219
220# my attempt at a single-quoted, non-interpolating string
221# where the backslash can escape literal single quotes
222quoted_string: <skip:''> "'" /(\\'|[^'])*/ "'"
223    { $item[3] =~ s/\\'/'/g; $item[3] }
224
[1]225option_list: options | list_var
226   
227options: '{' option(s /,\s*/) '}'
228
229list_var: /@[A-Z_]+/ { $list_var = $item[1] }
230
[25]231option: (simple_multiword | value | quoted_string) display_text(?)
[23]232    { push @options, { $item[1] => $item{'display_text(?)'}[0] } }
[1]233
234value: identifier
235
[59]236display_text: brace_block
237    { (my $text = $item[1]) =~ s/^\[\s*|\s*\]$//g; $text }
[1]238
[59]239# square brace delimited block, that can contain properly
240# nested square braces, along with any other characters
241# return with the '[...]' so that nested blocks get the
242# braces treated as literals
243brace_block: '[' <skip:''> brace_block_content(s) ']'
244    {
245        '[' . join('', @{ $item[3] }) . ']';
246    }
247brace_block_content: /[^\[\]]+?/ | brace_block
248
249
[24]250validate: '//' (optional_pattern | required_pattern)    { $item[2] }
[1]251
[24]252optional_pattern: /[A-Z_]+/ '?' { $required = 0; $item[1] }
253
254required_pattern: /[A-Z_]+/ { $required = 1; $item[1] }
255
[1]256comment: '#' /.*/
257blank:
258
259identifier: /\w+/
[16]260
261# skip unknown directives with a warning
262unknown_directive: /\!\S*/ /.*/
263    { warn "[Text::Formbuilder] Skipping unknown directive '$item[1]' at input text line $thisline\n"; }
Note: See TracBrowser for help on using the repository browser.