File Coverage

blib/lib/Marpa/R2/SLG.pm
Criterion Covered Total %
statement 511 538 94.9
branch 148 196 75.5
condition 37 49 75.5
subroutine 32 33 96.9
pod n/a
total 728 816 89.2


line stmt bran cond sub pod time code
1             # Copyright 2022 Jeffrey Kegler
2             # This file is part of Marpa::R2. Marpa::R2 is free software: you can
3             # redistribute it and/or modify it under the terms of the GNU Lesser
4             # General Public License as published by the Free Software Foundation,
5             # either version 3 of the License, or (at your option) any later version.
6             #
7             # Marpa::R2 is distributed in the hope that it will be useful,
8             # but WITHOUT ANY WARRANTY; without even the implied warranty of
9             # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
10             # Lesser General Public License for more details.
11             #
12             # You should have received a copy of the GNU Lesser
13             # General Public License along with Marpa::R2. If not, see
14             # http://www.gnu.org/licenses/.
15              
16             package Marpa::R2::Scanless::G;
17              
18 135     135   2630 use 5.010001;
  135         553  
19 135     135   864 use strict;
  135         413  
  135         3077  
20 135     135   737 use warnings;
  135         363  
  135         5832  
21              
22 135     135   811 use vars qw($VERSION $STRING_VERSION);
  135         400  
  135         12915  
23             $VERSION = '13.001_000';
24             $STRING_VERSION = $VERSION;
25             ## no critic(BuiltinFunctions::ProhibitStringyEval)
26             $VERSION = eval $VERSION;
27             ## use critic
28              
29             package Marpa::R2::Internal::Scanless::G;
30              
31 135     135   1056 use Scalar::Util 'blessed';
  135         1768  
  135         10715  
32 135     135   1154 use English qw( -no_match_vars );
  135         388  
  135         975  
33              
34             # names of packages for strings
35             our $PACKAGE = 'Marpa::R2::Scanless::G';
36              
37             sub Marpa::R2::Internal::Scanless::meta_grammar {
38              
39 75     75   289 my $meta_slg = bless [], 'Marpa::R2::Scanless::G';
40 75         351 state $hashed_metag = Marpa::R2::Internal::MetaG::hashed_grammar();
41 75         431 $meta_slg->[Marpa::R2::Internal::Scanless::G::TRACE_TERMINALS] = 0;
42 75         766 Marpa::R2::Internal::Scanless::G::hash_to_runtime( $meta_slg,
43             $hashed_metag,
44             { bless_package => 'Marpa::R2::Internal::MetaAST_Nodes' } );
45              
46 75         507 my $thick_g1_grammar =
47             $meta_slg->[Marpa::R2::Internal::Scanless::G::THICK_G1_GRAMMAR];
48 75         265 my @mask_by_rule_id;
49             $mask_by_rule_id[$_] = $thick_g1_grammar->_rule_mask($_)
50 75         567 for $thick_g1_grammar->rule_ids();
51 75         561 $meta_slg->[Marpa::R2::Internal::Scanless::G::MASK_BY_RULE_ID] =
52             \@mask_by_rule_id;
53 75         260 $meta_slg->[Marpa::R2::Internal::Scanless::G::TRACE_TERMINALS] = 0;
54              
55 75         481 return $meta_slg;
56              
57             } ## end sub Marpa::R2::Internal::Scanless::meta_grammar
58              
59             sub Marpa::R2::Scanless::G::new {
60 203     203   77389 my ( $class, @hash_ref_args ) = @_;
61              
62 203         609 my $slg = [];
63 203         578 bless $slg, $class;
64              
65 203         959 my ($dsl, $g1_args) = Marpa::R2::Internal::Scanless::G::set ( $slg, 'new', @hash_ref_args );
66 203         1402 my $ast = Marpa::R2::Internal::MetaAST->new( $dsl );
67 200         1063 my $hashed_ast = $ast->ast_to_hash();
68 200         1336 Marpa::R2::Internal::Scanless::G::hash_to_runtime($slg, $hashed_ast, $g1_args);
69 184         36794 return $slg;
70             } ## end sub Marpa::R2::Scanless::G::new
71              
72             sub Marpa::R2::Scanless::G::set {
73 1     1   1204 my ( $slg, @hash_ref_args ) = @_;
74 1         6 Marpa::R2::Internal::Scanless::G::set ( $slg, 'set', @hash_ref_args );
75 1         3 return $slg;
76             }
77              
78             # The context flag indicates whether this ::set() is called directly by the user;
79             # is for the external constructor; or is for the internal ("meta") constructor.
80             # "Context" flags of this kind
81             # are much decried practice, and for good reason, but in this case
82             # I think it is justified.
83             # This logic really needs to be all in one place, and so a flag
84             # to trigger the minor differences needed by the various calling
85             # contexts is a small price to pay.
86             sub Marpa::R2::Internal::Scanless::G::set {
87 204     204   750 my ( $slg, $method, @hash_ref_args ) = @_;
88              
89             # Other possible grammar options:
90             # default_rank
91             # inaccessible_ok
92             # unproductive_ok
93             # warnings
94              
95             state $copy_to_g1_args =
96 204         574 { map { ( $_, 1 ); }
  284         914  
97             qw(trace_file_handle action_object default_action bless_package) };
98             state $set_method_args =
99 204         568 { map { ( $_, 1 ); } qw(trace_file_handle trace_terminals) };
  142         466  
100             state $new_method_args = {
101 204         599 map { ( $_, 1 ); } qw(source trace_terminals), keys %{$copy_to_g1_args}
  426         880  
  71         323  
102             };
103 204         802 for my $args (@hash_ref_args) {
104 204         610 my $ref_type = ref $args;
105 204 50       766 if ( not $ref_type ) {
106 0         0 Marpa::R2::exception( q{$slg->}
107             . $method
108             . qq{() expects args as ref to HASH; got non-reference instead}
109             );
110             } ## end if ( not $ref_type )
111 204 50       934 if ( $ref_type ne 'HASH' ) {
112 0         0 Marpa::R2::exception( q{$slg->}
113             . $method
114             . qq{() expects args as ref to HASH, got ref to $ref_type instead}
115             );
116             } ## end if ( $ref_type ne 'HASH' )
117             } ## end for my $args (@hash_ref_args)
118              
119 204         562 my %flat_args = ();
120 204         549 for my $hash_ref (@hash_ref_args) {
121 204         405 ARG: for my $arg_name ( keys %{$hash_ref} ) {
  204         756  
122 243         911 $flat_args{$arg_name} = $hash_ref->{$arg_name};
123             }
124             }
125              
126 204         476 my $ok_args = $set_method_args;
127 204 100       766 $ok_args = $new_method_args if $method eq 'new';
128 204         723 my @bad_args = grep { not $ok_args->{$_} } keys %flat_args;
  243         1021  
129 204 50       1106 if ( scalar @bad_args ) {
130 0         0 Marpa::R2::exception(
131             q{Bad named argument(s) to $slg->}
132             . $method
133             . q{() method: }
134             . join q{ },
135             @bad_args
136             );
137             } ## end if ( scalar @bad_args )
138              
139 204         475 my $dsl;
140 204 100       737 if ( $method eq 'new' ) {
141 203         488 state $arg_name = 'source';
142 203         553 $dsl = $flat_args{$arg_name};
143 203 50       621 Marpa::R2::exception(
144             qq{Marpa::R2::Scanless::G::new() called without a "$arg_name" argument}
145             ) if not defined $dsl;
146 203         578 my $ref_type = ref $dsl;
147 203 50       708 if ( $ref_type ne 'SCALAR' ) {
148 0 0       0 my $desc = $ref_type ? "a ref to $ref_type" : 'not a ref';
149 0         0 Marpa::R2::exception(
150             qq{'$arg_name' name argument to Marpa::R2::Scanless::G->new() is $desc\n},
151             " It should be a ref to a string\n"
152             );
153             } ## end if ( $ref_type ne 'SCALAR' )
154 203 50       419 if ( not defined ${$dsl} ) {
  203         773  
155 0         0 Marpa::R2::exception(
156             qq{'$arg_name' name argument to Marpa::R2::Scanless::G->new() is a ref to a an undef\n},
157             " It should be a ref to a string\n"
158             );
159             } ## end if ( $ref_type ne 'SCALAR' )
160             } ## end if ( $method eq 'new' )
161              
162             # A bit hack-ish, but some named args will be copies straight to a member of
163             # the Scanless::G class, so this maps named args to the index of the array
164             # that holds the members.
165 204         598 state $copy_arg_to_index = {
166             trace_file_handle => Marpa::R2::Internal::Scanless::G::TRACE_FILE_HANDLE,
167             trace_terminals => Marpa::R2::Internal::Scanless::G::TRACE_TERMINALS
168             };
169              
170 204         639 ARG: for my $arg_name ( keys %flat_args ) {
171 243         589 my $index = $copy_arg_to_index->{$arg_name};
172 243 100       948 next ARG if not defined $index;
173 1         3 my $value = $flat_args{$arg_name};
174 1         3 $slg->[$index] = $value;
175             } ## end ARG: for my $arg_name ( keys %flat_args )
176              
177             # Normalize trace_terminals
178 204 100       2107 $slg->[Marpa::R2::Internal::Scanless::G::TRACE_TERMINALS] = 0
179             if not Scalar::Util::looks_like_number(
180             $slg->[Marpa::R2::Internal::Scanless::G::TRACE_TERMINALS] );
181              
182             # Trace file handle needs to be populated downwards
183 204 100       1048 if ( defined( my $trace_file_handle = $flat_args{trace_file_handle} ) ) {
184             GRAMMAR:
185 1         3 for my $naif_grammar (
186             $slg->[Marpa::R2::Internal::Scanless::G::THICK_G1_GRAMMAR],
187 1         4 @{ $slg->[Marpa::R2::Internal::Scanless::G::THICK_LEX_GRAMMARS] }
188             )
189             {
190 2 50       6 next GRAMMAR if not defined $naif_grammar;
191 2         8 $naif_grammar->set( { trace_file_handle => $trace_file_handle } );
192             } ## end GRAMMAR: for my $naif_grammar ( $slg->[...])
193             } ## end if ( defined( my $trace_file_handle = $flat_args{...}))
194              
195 204 100       777 if ( $method eq 'new' ) {
196              
197             # Prune flat args of all those named args which are NOT to be copied
198             # into the NAIF recce args
199 203         576 for my $arg_name ( keys %flat_args ) {
200             delete $flat_args{$arg_name}
201 242 100       941 if not $copy_to_g1_args->{$arg_name};
202             }
203              
204             # trace file handle must always be defined
205 203   50     1505 $slg->[Marpa::R2::Internal::Scanless::G::TRACE_FILE_HANDLE] //= \*STDERR;
206              
207 203         874 return ($dsl, \%flat_args);
208             } ## end if ( $method eq 'new' )
209              
210 1         3 return;
211              
212             } ## end sub Marpa::R2::Internal::Scanless::G::set
213              
214             sub Marpa::R2::Internal::Scanless::G::hash_to_runtime {
215 275     275   1066 my ( $slg, $hashed_source, $g1_args ) = @_;
216              
217 275         815 my $trace_terminals =
218             $slg->[Marpa::R2::Internal::Scanless::G::TRACE_TERMINALS];
219              
220             # Pre-lexer G1 processing
221              
222             my $start_lhs = $hashed_source->{'start_lhs'}
223 275   66     1424 // $hashed_source->{'first_lhs'};
224 275 50       994 Marpa::R2::exception('No rules in SLIF grammar')
225             if not defined $start_lhs;
226 275         1481 Marpa::R2::Internal::MetaAST::start_rule_create( $hashed_source,
227             $start_lhs );
228              
229 275         902 $slg->[Marpa::R2::Internal::Scanless::G::CACHE_RULEIDS_BY_LHS_NAME] = {};
230             $slg->[Marpa::R2::Internal::Scanless::G::DEFAULT_G1_START_ACTION] =
231 275         835 $hashed_source->{'default_g1_start_action'};
232              
233             my $trace_fh =
234             $slg->[Marpa::R2::Internal::Scanless::G::TRACE_FILE_HANDLE] =
235 275   50     1832 $g1_args->{trace_file_handle} // \*STDERR;
236              
237             my $if_inaccessible_default =
238 275   100     1612 $hashed_source->{defaults}->{if_inaccessible} // 'warn';
239              
240             # Prepare the arguments for the G1 grammar
241 275         821 $g1_args->{rules} = $hashed_source->{rules}->{G1};
242 275         717 $g1_args->{symbols} = $hashed_source->{symbols}->{G1};
243 275         657 state $g1_target_symbol = '[:start]';
244 275         736 $g1_args->{start} = $g1_target_symbol;
245 275         1080 $g1_args->{'_internal_'} =
246             { 'if_inaccessible' => $if_inaccessible_default };
247              
248 275         1882 my $thick_g1_grammar = Marpa::R2::Grammar->new($g1_args);
249 271         1349 my $g1_tracer = $thick_g1_grammar->tracer();
250 271         1139 my $g1_thin = $g1_tracer->grammar();
251              
252 271         669 my $symbol_ids_by_event_name_and_type = {};
253 271         728 $slg->[
254             Marpa::R2::Internal::Scanless::G::SYMBOL_IDS_BY_EVENT_NAME_AND_TYPE]
255             = $symbol_ids_by_event_name_and_type;
256              
257 271         700 my $completion_events_by_name = $hashed_source->{completion_events};
258 271         847 my $completion_events_by_id =
259             $slg->[Marpa::R2::Internal::Scanless::G::COMPLETION_EVENT_BY_ID] = [];
260 271         553 for my $symbol_name ( keys %{$completion_events_by_name} ) {
  271         1163  
261             my ( $event_name, $is_active ) =
262 54         89 @{ $completion_events_by_name->{$symbol_name} };
  54         136  
263 54         155 my $symbol_id = $g1_tracer->symbol_by_name($symbol_name);
264 54 50       131 if ( not defined $symbol_id ) {
265 0         0 Marpa::R2::exception(
266             "Completion event defined for non-existent symbol: $symbol_name\n"
267             );
268             }
269              
270             # Must be done before precomputation
271 54         163 $g1_thin->symbol_is_completion_event_set( $symbol_id, 1 );
272 54 100       139 $g1_thin->completion_symbol_activate( $symbol_id, 0 )
273             if not $is_active;
274 54         120 $slg->[Marpa::R2::Internal::Scanless::G::COMPLETION_EVENT_BY_ID]
275             ->[$symbol_id] = $event_name;
276             push
277 54         88 @{ $symbol_ids_by_event_name_and_type->{$event_name}->{completion}
278 54         295 }, $symbol_id;
279             } ## end for my $symbol_name ( keys %{$completion_events_by_name...})
280              
281 271         817 my $nulled_events_by_name = $hashed_source->{nulled_events};
282 271         809 my $nulled_events_by_id =
283             $slg->[Marpa::R2::Internal::Scanless::G::NULLED_EVENT_BY_ID] = [];
284 271         548 for my $symbol_name ( keys %{$nulled_events_by_name} ) {
  271         911  
285             my ( $event_name, $is_active ) =
286 46         67 @{ $nulled_events_by_name->{$symbol_name} };
  46         89  
287 46         106 my $symbol_id = $g1_tracer->symbol_by_name($symbol_name);
288 46 50       111 if ( not defined $symbol_id ) {
289 0         0 Marpa::R2::exception(
290             "nulled event defined for non-existent symbol: $symbol_name\n"
291             );
292             }
293              
294             # Must be done before precomputation
295 46         132 $g1_thin->symbol_is_nulled_event_set( $symbol_id, 1 );
296 46 100       113 $g1_thin->nulled_symbol_activate( $symbol_id, 0 ) if not $is_active;
297 46         105 $slg->[Marpa::R2::Internal::Scanless::G::NULLED_EVENT_BY_ID]
298             ->[$symbol_id] = $event_name;
299 46         74 push @{ $symbol_ids_by_event_name_and_type->{$event_name}->{nulled} },
  46         213  
300             $symbol_id;
301             } ## end for my $symbol_name ( keys %{$nulled_events_by_name} )
302              
303 271         704 my $prediction_events_by_name = $hashed_source->{prediction_events};
304 271         705 my $prediction_events_by_id =
305             $slg->[Marpa::R2::Internal::Scanless::G::PREDICTION_EVENT_BY_ID] = [];
306 271         559 for my $symbol_name ( keys %{$prediction_events_by_name} ) {
  271         857  
307             my ( $event_name, $is_active ) =
308 52         84 @{ $prediction_events_by_name->{$symbol_name} };
  52         105  
309 52         123 my $symbol_id = $g1_tracer->symbol_by_name($symbol_name);
310 52 50       119 if ( not defined $symbol_id ) {
311 0         0 Marpa::R2::exception(
312             "prediction event defined for non-existent symbol: $symbol_name\n"
313             );
314             }
315              
316             # Must be done before precomputation
317 52         149 $g1_thin->symbol_is_prediction_event_set( $symbol_id, 1 );
318 52 100       125 $g1_thin->prediction_symbol_activate( $symbol_id, 0 )
319             if not $is_active;
320 52         103 $slg->[Marpa::R2::Internal::Scanless::G::PREDICTION_EVENT_BY_ID]
321             ->[$symbol_id] = $event_name;
322             push
323 52         64 @{ $symbol_ids_by_event_name_and_type->{$event_name}->{prediction}
324 52         678 }, $symbol_id;
325             } ## end for my $symbol_name ( keys %{$prediction_events_by_name...})
326              
327 271         801 my $lexeme_events_by_id =
328             $slg->[Marpa::R2::Internal::Scanless::G::LEXEME_EVENT_BY_ID] = [];
329              
330 271 100       1245 if (defined(
331             my $precompute_error =
332             Marpa::R2::Internal::Grammar::slif_precompute(
333             $thick_g1_grammar)
334             )
335             )
336             {
337 1 50       6 if ( $precompute_error == $Marpa::R2::Error::UNPRODUCTIVE_START ) {
338              
339             # Maybe someday improve this by finding the start rule and showing
340             # its RHS -- for now it is clear enough
341 1         4 Marpa::R2::exception(qq{Unproductive start symbol});
342             } ## end if ( $precompute_error == ...)
343             Marpa::R2::exception(
344 0         0 'Internal errror: unnkown precompute error code ',
345             $precompute_error );
346             } ## end if ( defined( my $precompute_error = ...))
347              
348             # Find out the list of lexemes according to G1
349 267         729 my %g1_id_by_lexeme_name = ();
350 267         1681 SYMBOL: for my $symbol_id ( 0 .. $g1_thin->highest_symbol_id() ) {
351              
352             # Not a lexeme, according to G1
353 13271 100       27143 next SYMBOL if not $g1_thin->symbol_is_terminal($symbol_id);
354              
355 7989         14872 my $symbol_name = $g1_tracer->symbol_name($symbol_id);
356 7989         14902 $g1_id_by_lexeme_name{$symbol_name} = $symbol_id;
357              
358             } ## end SYMBOL: for my $symbol_id ( 0 .. $g1_thin->highest_symbol_id(...))
359              
360             # A first phase of applying defaults
361 267         1185 my $discard_default_adverbs = $hashed_source->{discard_default_adverbs};
362 267         795 my $lexeme_declarations = $hashed_source->{lexeme_declarations};
363 267         671 my $lexeme_default_adverbs = $hashed_source->{lexeme_default_adverbs};
364 267   100     1265 my $latm_default_value = $lexeme_default_adverbs->{latm} // 0;
365              
366             # Current lexeme data is spread out in many places.
367             # Change so that it all resides in this hash, indexed by
368             # name
369 267         700 my %lexeme_data = ();
370              
371             # Determine "latm" status
372 267         1925 LEXEME: for my $lexeme_name ( keys %g1_id_by_lexeme_name ) {
373 7989         10713 my $declarations = $lexeme_declarations->{$lexeme_name};
374 7989   100     17809 my $latm_value = $declarations->{latm} // $latm_default_value;
375 7989         19494 $lexeme_data{$lexeme_name}{latm} = $latm_value;
376             }
377              
378             # Lexers
379              
380 267         1084 my $lexer_id = 0;
381 267         704 my $lexer_name = 'L0';
382              
383 267         655 my %lexer_id_by_name = ();
384 267         575 my %thick_grammar_by_lexer_name = ();
385 267         702 my @discard_event_by_lexer_rule_id = ();
386 267         549 my %lexer_and_rule_to_g1_lexeme = ();
387 267         521 my %character_class_table_by_lexer_name = ();
388 267         529 state $lex_start_symbol_name = '[:start_lex]';
389 267         503 state $discard_symbol_name = '[:discard]';
390              
391 267         703 my $lexer_rules = $hashed_source->{rules}->{$lexer_name};
392 267         574 my $character_class_hash = $hashed_source->{character_classes};
393 267         680 my $lexer_symbols = $hashed_source->{symbols}->{'L'};
394              
395             # If no lexer rules, fake a lexer
396             # Fake a lexer -- it discards symbols in character classes which
397             # never matches
398 267 100       933 if ( not $lexer_rules ) {
399 2         13 $character_class_hash = { '[[^\\d\\D]]' => [ '[^\\d\\D]', '' ] };
400 2         14 $lexer_rules = [
401             { 'rhs' => [ '[[^\\d\\D]]' ],
402             'lhs' => '[:discard]',
403             'symbol_as_event' => '[^\\d\\D]',
404             'description' => 'Discard rule for <[[^\\d\\D]]>'
405             },
406             ];
407 2         15 $lexer_symbols = {
408             '[:discard]' => {
409             'display_form' => ':discard',
410             'description' => 'Internal LHS for lexer "L0" discard'
411             },
412             '[[^\\d\\D]]' => {
413             'dsl_form' => '[^\\d\\D]',
414             'display_form' => '[^\\d\\D]',
415             'description' => 'Character class: [^\\d\\D]'
416             }
417             };
418             } ## end if ( not $lexer_rules )
419              
420 267         565 my %lex_lhs = ();
421 267         586 my %lex_rhs = ();
422 267         480 my %lex_separator = ();
423 267         509 my %lexer_rule_by_tag = ();
424              
425 267         601 my $rule_tag = 'rule0';
426 267         504 for my $lex_rule ( @{$lexer_rules} ) {
  267         763  
427 12463         24213 $lex_rule->{tag} = ++$rule_tag;
428 12463         15443 my %lex_rule_copy = %{$lex_rule};
  12463         55091  
429 12463         35138 $lexer_rule_by_tag{$rule_tag} = \%lex_rule_copy;
430 12463         17067 delete $lex_rule->{event};
431 12463         15367 delete $lex_rule->{symbol_as_event};
432 12463         22403 $lex_lhs{ $lex_rule->{lhs} } = 1;
433 12463         15325 $lex_rhs{$_} = 1 for @{ $lex_rule->{rhs} };
  12463         39271  
434 12463 100       29269 if ( defined( my $separator = $lex_rule->{separator} ) ) {
435 150         809 $lex_separator{$separator} = 1;
436             }
437             } ## end for my $lex_rule ( @{$lexer_rules} )
438              
439 267         1003 my %this_lexer_symbols = ();
440             SYMBOL:
441 267         4552 for my $symbol_name ( ( keys %lex_lhs ), ( keys %lex_rhs ),
442             ( keys %lex_separator ) )
443             {
444 18574         24954 my $symbol_data = $lexer_symbols->{$symbol_name};
445 18574 100       35615 $this_lexer_symbols{$symbol_name} = $symbol_data
446             if defined $symbol_data;
447             } ## end SYMBOL: for my $symbol_name ( ( keys %lex_lhs ), ( keys %lex_rhs...))
448              
449 8176         14980 my %is_lexeme_in_this_lexer = map { $_ => 1 }
450 267   100     3096 grep { not $lex_rhs{$_} and not $lex_separator{$_} }
  10732         26483  
451             keys %lex_lhs;
452              
453 267         2930 my @lex_lexeme_names = keys %is_lexeme_in_this_lexer;
454              
455 267 50       1134 Marpa::R2::exception( "No lexemes in lexer: $lexer_name\n",
456             " An SLIF grammar must have at least one lexeme\n" )
457             if not scalar @lex_lexeme_names;
458              
459             # Do I need this?
460             my @unproductive =
461 0         0 map {"<$_>"}
462 267   66     1745 grep { not $lex_lhs{$_} and not $_ =~ /\A \[\[ /xms }
  7842         24650  
463             ( keys %lex_rhs, keys %lex_separator );
464 267 50       1308 if (@unproductive) {
465 0         0 Marpa::R2::exception( 'Unproductive lexical symbols: ',
466             join q{ }, @unproductive );
467             }
468              
469             $this_lexer_symbols{$lex_start_symbol_name}->{display_form} =
470 267         1157 ':start_lex';
471             $this_lexer_symbols{$lex_start_symbol_name}->{description} =
472 267         875 'Internal L0 (lexical) start symbol';
473 267         4130 push @{$lexer_rules}, map {
474 267         586 ;
475 8176         34463 { description => "Internal lexical start rule for <$_>",
476             lhs => $lex_start_symbol_name,
477             rhs => [$_]
478             }
479             } sort keys %is_lexeme_in_this_lexer;
480              
481             # Prepare the arguments for the lex grammar
482 267         1565 my %lex_args = ();
483 267         805 $lex_args{trace_file_handle} = $trace_fh;
484 267         770 $lex_args{start} = $lex_start_symbol_name;
485 267         1051 $lex_args{'_internal_'} =
486             { 'if_inaccessible' => $if_inaccessible_default };
487 267         691 $lex_args{rules} = $lexer_rules;
488 267         733 $lex_args{symbols} = \%this_lexer_symbols;
489              
490             # Create the thick lex grammar
491 267         1445 my $lex_grammar = Marpa::R2::Grammar->new( \%lex_args );
492 267         1074 $thick_grammar_by_lexer_name{$lexer_name} = $lex_grammar;
493 267         1156 my $lex_tracer = $lex_grammar->tracer();
494 267         1025 my $lex_thin = $lex_tracer->grammar();
495              
496 267   100     1197 my $lex_discard_symbol_id =
497             $lex_tracer->symbol_by_name($discard_symbol_name) // -1;
498 267         680 my @lex_lexeme_to_g1_symbol;
499 267         4803 $lex_lexeme_to_g1_symbol[$_] = -1 for 0 .. $g1_thin->highest_symbol_id();
500              
501 267         1407 LEXEME_NAME: for my $lexeme_name (@lex_lexeme_names) {
502 8175 100       14927 next LEXEME_NAME if $lexeme_name eq $discard_symbol_name;
503 7988 50       12656 next LEXEME_NAME if $lexeme_name eq $lex_start_symbol_name;
504 7988         11592 my $g1_symbol_id = $g1_id_by_lexeme_name{$lexeme_name};
505 7988 100       12947 if ( not defined $g1_symbol_id ) {
506 4         19 Marpa::R2::exception(
507             qq{<$lexeme_name> is a lexeme but it is not a legal lexeme in G1:\n},
508             qq{ Lexemes must be G1 symbols that do not appear on a G1 LHS.\n}
509             );
510             }
511 7984 100       19700 if ( not $g1_thin->symbol_is_accessible($g1_symbol_id) ) {
512 2         8 my $message =
513             "A lexeme in lexer $lexer_name is not accessible from the G1 start symbol: $lexeme_name";
514 2 50       7 say {$trace_fh} $message
  0         0  
515             if $if_inaccessible_default eq 'warn';
516 2 50       6 Marpa::R2::exception($message)
517             if $if_inaccessible_default eq 'fatal';
518             } ## end if ( not $g1_thin->symbol_is_accessible($g1_symbol_id...))
519 7984         16620 my $lex_symbol_id = $lex_tracer->symbol_by_name($lexeme_name);
520 7984         27768 $lexeme_data{$lexeme_name}{lexers}{$lexer_name}{'id'} =
521             $lex_symbol_id;
522 7984         13989 $lex_lexeme_to_g1_symbol[$lex_symbol_id] = $g1_symbol_id;
523             } ## end LEXEME_NAME: for my $lexeme_name (@lex_lexeme_names)
524              
525 263         798 my @lex_rule_to_g1_lexeme;
526 263         1059 my $lex_start_symbol_id =
527             $lex_tracer->symbol_by_name($lex_start_symbol_name);
528 263         1526 RULE_ID: for my $rule_id ( 0 .. $lex_thin->highest_rule_id() ) {
529 20627         33961 my $lhs_id = $lex_thin->rule_lhs($rule_id);
530 20627 100       34240 if ( $lhs_id == $lex_discard_symbol_id ) {
531 301         904 $lex_rule_to_g1_lexeme[$rule_id] = -2;
532 301         629 next RULE_ID;
533             }
534 20326 100       32330 if ( $lhs_id != $lex_start_symbol_id ) {
535 12156         16057 $lex_rule_to_g1_lexeme[$rule_id] = -1;
536 12156         16771 next RULE_ID;
537             }
538 8170         15117 my $lexer_lexeme_id = $lex_thin->rule_rhs( $rule_id, 0 );
539 8170 100       13996 if ( $lexer_lexeme_id == $lex_discard_symbol_id ) {
540 187         603 $lex_rule_to_g1_lexeme[$rule_id] = -1;
541 187         487 next RULE_ID;
542             }
543 7983   50     14084 my $lexeme_id = $lex_lexeme_to_g1_symbol[$lexer_lexeme_id] // -1;
544 7983         11133 $lex_rule_to_g1_lexeme[$rule_id] = $lexeme_id;
545 7983 50       13447 next RULE_ID if $lexeme_id < 0;
546 7983         15504 my $lexeme_name = $g1_tracer->symbol_name($lexeme_id);
547              
548             # If 1 is the default, we don't need an assertion
549 7983 100       16632 next RULE_ID if not $lexeme_data{$lexeme_name}{latm};
550              
551             my $assertion_id =
552 7550         11695 $lexeme_data{$lexeme_name}{lexers}{$lexer_name}{'assertion'};
553 7550 50       12452 if ( not defined $assertion_id ) {
554 7550         13903 $assertion_id = $lex_thin->zwa_new(0);
555              
556 7550 50       13236 if ( $trace_terminals >= 2 ) {
557 0         0 say {$trace_fh} "Assertion $assertion_id defaults to 0";
  0         0  
558             }
559              
560 7550         12855 $lexeme_data{$lexeme_name}{lexers}{$lexer_name}{'assertion'} =
561             $assertion_id;
562             } ## end if ( not defined $assertion_id )
563 7550         18712 $lex_thin->zwa_place( $assertion_id, $rule_id, 0 );
564 7550 50       15373 if ( $trace_terminals >= 2 ) {
565 0         0 say {$trace_fh}
  0         0  
566             "Assertion $assertion_id applied to $lexer_name rule ",
567             slg_rule_show( $slg, $rule_id, $lex_grammar );
568             }
569             } ## end RULE_ID: for my $rule_id ( 0 .. $lex_thin->highest_rule_id() )
570              
571 263         1652 Marpa::R2::Internal::Grammar::slif_precompute($lex_grammar);
572              
573 263         986 my @class_table = ();
574              
575             CLASS_SYMBOL:
576 263         685 for my $class_symbol ( sort keys %{$character_class_hash} ) {
  263         4164  
577 5282         11753 my $symbol_id = $lex_tracer->symbol_by_name($class_symbol);
578 5282 50       9954 next CLASS_SYMBOL if not defined $symbol_id;
579 5282         8675 my $cc_components = $character_class_hash->{$class_symbol};
580 5282         10085 my ( $compiled_re, $error ) =
581             Marpa::R2::Internal::MetaAST::char_class_to_re($cc_components);
582 5282 50       10226 if ( not $compiled_re ) {
583 0         0 $error =~ s/^/ /gxms; #indent all lines
584 0         0 Marpa::R2::exception(
585             "Failed belatedly to evaluate character class\n", $error );
586             }
587 5282         12735 push @class_table, [ $symbol_id, $compiled_re ];
588             } ## end CLASS_SYMBOL: for my $class_symbol ( sort keys %{...})
589 263         1615 $character_class_table_by_lexer_name{$lexer_name} = \@class_table;
590              
591 263         936 $lexer_and_rule_to_g1_lexeme{$lexer_name} = \@lex_rule_to_g1_lexeme;
592              
593             # Apply defaults to determine the discard event for every
594             # rule id of the lexer.
595              
596 263         837 my $default_discard_event = $discard_default_adverbs->{event};
597 263         1614 RULE_ID: for my $rule_id ( 0 .. $lex_thin->highest_rule_id() ) {
598 20627         36561 my $tag = $lex_grammar->tag($rule_id);
599 20627 100       37709 next RULE_ID if not defined $tag;
600 12457         15120 my $event;
601             FIND_EVENT: {
602 12457         14658 $event = $lexer_rule_by_tag{$tag}->{event};
  12457         21564  
603 12457 100       20161 last FIND_EVENT if defined $event;
604 12406         21969 my $lhs_id = $lex_thin->rule_lhs($rule_id);
605 12406 100       22596 last FIND_EVENT if $lhs_id != $lex_discard_symbol_id;
606 250         642 $event = $default_discard_event;
607             } ## end FIND_EVENT:
608 12457 100       23484 next RULE_ID if not defined $event;
609              
610 60         104 my ( $event_name, $event_starts_active ) = @{$event};
  60         137  
611 60 100       170 if ( $event_name eq q{'symbol} ) {
612             my @event = (
613             $lexer_rule_by_tag{$tag}->{symbol_as_event},
614 15         36 $event_starts_active
615             );
616 15         28 $discard_event_by_lexer_rule_id[$rule_id] = \@event;
617 15         27 next RULE_ID;
618             } ## end if ( $event_name eq q{'symbol} )
619 45 50       178 if ( ( substr $event_name, 0, 1 ) ne q{'} ) {
620 45         81 $discard_event_by_lexer_rule_id[$rule_id] = $event;
621 45         89 next RULE_ID;
622             }
623             Marpa::R2::exception(
624 0         0 qq{Discard event has unknown name: "$event_name"}
625             );
626              
627             } ## end RULE_ID: for my $rule_id ( 0 .. $lex_thin->highest_rule_id() )
628              
629             # Post-lexer G1 processing
630              
631 263         1224 my $thick_L0 = $thick_grammar_by_lexer_name{'L0'};
632 263         923 my $thin_L0 = $thick_L0->[Marpa::R2::Internal::Grammar::C];
633 263         1139 my $thin_slg = $slg->[Marpa::R2::Internal::Scanless::G::C] =
634             Marpa::R2::Thin::SLG->new( $thin_L0, $g1_tracer->grammar() );
635              
636             # Relies on default lexer being given number zero
637 263         973 $lexer_id_by_name{'L0'} = 0;
638              
639 263         2241 LEXEME: for my $lexeme_name ( keys %g1_id_by_lexeme_name ) {
640             Marpa::R2::exception(
641             "A lexeme in G1 is not a lexeme in any of the lexers: $lexeme_name"
642 7985 100       14522 ) if not defined $lexeme_data{$lexeme_name}{'lexers'};
643             }
644              
645             # At this point we know which symbols are lexemes.
646             # So now let's check for inconsistencies
647              
648             # Check for lexeme declarations for things which are not lexemes
649 259         1049 for my $lexeme_name ( keys %{$lexeme_declarations} ) {
  259         1100  
650             Marpa::R2::exception(
651             "Symbol <$lexeme_name> is declared as a lexeme, but it is not used as one.\n"
652 59 50       216 ) if not defined $g1_id_by_lexeme_name{$lexeme_name};
653             }
654              
655             # Now that we know the lexemes, check attempts to defined a
656             # completion or a nulled event for one
657 259         616 for my $symbol_name ( keys %{$completion_events_by_name} ) {
  259         814  
658             Marpa::R2::exception(
659             "A completion event is declared for <$symbol_name>, but it is a lexeme.\n",
660             " Completion events are only valid for symbols on the LHS of G1 rules.\n"
661 54 50       167 ) if defined $g1_id_by_lexeme_name{$symbol_name};
662             } ## end for my $symbol_name ( keys %{$completion_events_by_name...})
663              
664 259         545 for my $symbol_name ( keys %{$nulled_events_by_name} ) {
  259         754  
665             Marpa::R2::exception(
666             "A nulled event is declared for <$symbol_name>, but it is a G1 lexeme.\n",
667             " nulled events are only valid for symbols on the LHS of G1 rules.\n"
668 46 50       121 ) if defined $g1_id_by_lexeme_name{$symbol_name};
669             } ## end for my $symbol_name ( keys %{$nulled_events_by_name} )
670              
671             # Mark the lexemes, and set their data
672             # Now that we have created the SLG, we can set the latm value,
673             # already determined above.
674 259         1581 LEXEME: for my $lexeme_name ( keys %g1_id_by_lexeme_name ) {
675 7979         10624 my $g1_lexeme_id = $g1_id_by_lexeme_name{$lexeme_name};
676 7979         9884 my $declarations = $lexeme_declarations->{$lexeme_name};
677 7979   100     18505 my $priority = $declarations->{priority} // 0;
678 7979         16166 $thin_slg->g1_lexeme_set( $g1_lexeme_id, $priority );
679 7979   50     14505 my $latm_value = $lexeme_data{$lexeme_name}{latm} // 0;
680 7979         16744 $thin_slg->g1_lexeme_latm_set( $g1_lexeme_id, $latm_value );
681 7979         10309 my $pause_value = $declarations->{pause};
682 7979 100       15998 if ( defined $pause_value ) {
683 54         220 $thin_slg->g1_lexeme_pause_set( $g1_lexeme_id, $pause_value );
684 54         97 my $is_active = 1;
685              
686 54 100       161 if ( defined( my $event_data = $declarations->{'event'} ) ) {
687 49         75 my $event_name;
688 49         79 ( $event_name, $is_active ) = @{$event_data};
  49         114  
689 49         125 $lexeme_events_by_id->[$g1_lexeme_id] = $event_name;
690 49         92 push @{ $symbol_ids_by_event_name_and_type->{$event_name}
691 49         241 ->{lexeme} }, $g1_lexeme_id;
692             } ## end if ( defined( my $event_data = $declarations->{'event'...}))
693              
694 54         239 $thin_slg->g1_lexeme_pause_activate( $g1_lexeme_id, $is_active );
695             } ## end if ( defined $pause_value )
696              
697             } ## end LEXEME: for my $lexeme_name ( keys %g1_id_by_lexeme_name )
698              
699             # Second phase of lexer processing
700 259         1315 my $lexer_rule_to_g1_lexeme = $lexer_and_rule_to_g1_lexeme{$lexer_name};
701              
702 259         692 RULE_ID: for my $lexer_rule_id ( 0 .. $#{$lexer_rule_to_g1_lexeme} ) {
  259         983  
703 20617         26093 my $g1_lexeme_id = $lexer_rule_to_g1_lexeme->[$lexer_rule_id];
704 20617         35472 my $lexeme_name = $g1_tracer->symbol_name($g1_lexeme_id);
705             my $assertion_id =
706 20617   100     45215 $lexeme_data{$lexeme_name}{lexers}{$lexer_name}{'assertion'}
707             // -1;
708 20617         42427 $thin_slg->lexer_rule_to_g1_lexeme_set( $lexer_rule_id,
709             $g1_lexeme_id, $assertion_id );
710 20617         25796 my $discard_event = $discard_event_by_lexer_rule_id[$lexer_rule_id];
711 20617 100       38189 if ( defined $discard_event ) {
712 60         94 my ( $event_name, $is_active ) = @{$discard_event};
  60         143  
713 60         139 $slg->[
714             Marpa::R2::Internal::Scanless::G::DISCARD_EVENT_BY_LEXER_RULE
715             ]->[$lexer_rule_id] = $event_name;
716 60         88 push @{ $symbol_ids_by_event_name_and_type->{$event_name}
717 60         284 ->{discard} }, $lexer_rule_id;
718 60         226 $thin_slg->discard_event_set( $lexer_rule_id, 1 );
719 60 100       206 $thin_slg->discard_event_activate( $lexer_rule_id, 1 )
720             if $is_active;
721             } ## end if ( defined $discard_event )
722             } ## end RULE_ID: for my $lexer_rule_id ( 0 .. $#{$lexer_rule_to_g1_lexeme...})
723              
724             # Second phase of G1 processing
725              
726 259         1966 $thin_slg->precompute();
727 259         616 $slg->[Marpa::R2::Internal::Scanless::G::THICK_G1_GRAMMAR] =
728             $thick_g1_grammar;
729              
730             # More lexer processing
731             # Determine events by lexer rule, applying the defaults
732              
733             {
734 259         534 my $character_class_table =
735 259         617 $character_class_table_by_lexer_name{$lexer_name};
736 259         710 $slg->[Marpa::R2::Internal::Scanless::G::CHARACTER_CLASS_TABLES]
737             ->[$lexer_id] = $character_class_table;
738             $slg->[Marpa::R2::Internal::Scanless::G::THICK_LEX_GRAMMARS]
739 259         844 ->[$lexer_id] = $thick_grammar_by_lexer_name{$lexer_name};
740             }
741              
742             # This section violates the NAIF interface, directly changing some
743             # of its internal structures.
744             #
745             # Some lexeme default adverbs are applied in earlier phases.
746             #
747             APPLY_DEFAULT_LEXEME_ADVERBS: {
748 259 50       528 last APPLY_DEFAULT_LEXEME_ADVERBS if not $lexeme_default_adverbs;
  259         911  
749              
750 259         675 my $action = $lexeme_default_adverbs->{action};
751 259         578 my $g1_symbols =
752             $thick_g1_grammar->[Marpa::R2::Internal::Grammar::SYMBOLS];
753             LEXEME:
754 259         2023 for my $lexeme_name ( keys %g1_id_by_lexeme_name ) {
755 7979         11170 my $g1_lexeme_id = $g1_id_by_lexeme_name{$lexeme_name};
756 7979         11756 my $g1_symbol = $g1_symbols->[$g1_lexeme_id];
757 7979 100       18694 next LEXEME if $lexeme_name =~ m/ \] \z/xms;
758 1629   66     5765 $g1_symbol->[Marpa::R2::Internal::Symbol::LEXEME_SEMANTICS] //=
759             $action;
760             } ## end LEXEME: for my $lexeme_name ( keys %g1_id_by_lexeme_name )
761              
762 259         1218 my $blessing = $lexeme_default_adverbs->{bless};
763 259 100       1211 last APPLY_DEFAULT_LEXEME_ADVERBS if not $blessing;
764 79 50       443 last APPLY_DEFAULT_LEXEME_ADVERBS if $blessing eq '::undef';
765              
766             LEXEME:
767 79         1031 for my $lexeme_name ( keys %g1_id_by_lexeme_name ) {
768 7455         10684 my $g1_lexeme_id = $g1_id_by_lexeme_name{$lexeme_name};
769 7455         9271 my $g1_symbol = $g1_symbols->[$g1_lexeme_id];
770 7455 100       17060 next LEXEME if $lexeme_name =~ m/ \] \z/xms;
771 1366 50       2797 if ( $blessing eq '::name' ) {
772 1366 50       3219 if ( $lexeme_name =~ / [^ [:alnum:]] /xms ) {
773 0         0 Marpa::R2::exception(
774             qq{Lexeme blessing by '::name' only allowed if lexeme name is whitespace and alphanumerics\n},
775             qq{ Problematic lexeme was <$lexeme_name>\n}
776             );
777             } ## end if ( $lexeme_name =~ / [^ [:alnum:]] /xms )
778 1366         1903 my $blessing_by_name = $lexeme_name;
779 1366         3856 $blessing_by_name =~ s/[ ]/_/gxms;
780 1366   33     5349 $g1_symbol->[Marpa::R2::Internal::Symbol::BLESSING] //=
781             $blessing_by_name;
782 1366         2354 next LEXEME;
783             } ## end if ( $blessing eq '::name' )
784 0 0       0 if ( $blessing =~ / [\W] /xms ) {
785 0         0 Marpa::R2::exception(
786             qq{Blessing lexeme as '$blessing' is not allowed\n},
787             qq{ Problematic lexeme was <$lexeme_name>\n}
788             );
789             } ## end if ( $blessing =~ / [\W] /xms )
790 0   0     0 $g1_symbol->[Marpa::R2::Internal::Symbol::BLESSING] //= $blessing;
791             } ## end LEXEME: for my $lexeme_name ( keys %g1_id_by_lexeme_name )
792              
793             } ## end APPLY_DEFAULT_LEXEME_ADVERBS:
794              
795 259         19650 return $slg;
796              
797             } ## end sub Marpa::R2::Internal::Scanless::G::hash_to_runtime
798              
799             sub thick_subgrammar_by_name {
800 665     665   1059 my ( $slg, $subgrammar ) = @_;
801              
802             # Allow G0 as legacy synonym for L0
803 665         995 state $grammar_names = { 'G0' => 1, 'G1' => 1, 'L0' => 1 };
804 665   100     1990 $subgrammar //= 'G1';
805              
806             Marpa::R2::exception(qq{No lexer named "$subgrammar"})
807 665 50       1397 if not defined $grammar_names->{$subgrammar};
808              
809 665 100       2057 return $slg->[Marpa::R2::Internal::Scanless::G::THICK_G1_GRAMMAR]
810             if $subgrammar eq 'G1';
811              
812 203         425 return $slg->[Marpa::R2::Internal::Scanless::G::THICK_LEX_GRAMMARS]
813             ->[0];
814             } ## end sub thick_subgrammar_by_name
815              
816             sub Marpa::R2::Scanless::G::start_symbol_id {
817 1     1   16 my ( $slg, $rule_id, $subgrammar ) = @_;
818 1         7 return thick_subgrammar_by_name( $slg, $subgrammar )->start_symbol();
819             }
820              
821             sub Marpa::R2::Scanless::G::rule_name {
822 5     5   17 my ( $slg, $rule_id, $subgrammar ) = @_;
823 5         10 return thick_subgrammar_by_name( $slg, $subgrammar )->rule_name($rule_id);
824             }
825              
826             sub Marpa::R2::Scanless::G::rule_expand {
827 60     60   483 my ( $slg, $rule_id, $subgrammar ) = @_;
828 60         101 return thick_subgrammar_by_name( $slg, $subgrammar )->tracer()
829             ->rule_expand($rule_id);
830             }
831              
832             sub Marpa::R2::Scanless::G::symbol_name {
833 171     171   823 my ( $slg, $symbol_id, $subgrammar ) = @_;
834 171         336 return thick_subgrammar_by_name($slg, $subgrammar)->tracer()
835             ->symbol_name($symbol_id);
836             }
837              
838             sub Marpa::R2::Scanless::G::symbol_display_form {
839 139     139   490 my ( $slg, $symbol_id, $subgrammar ) = @_;
840 139         262 return thick_subgrammar_by_name( $slg, $subgrammar )
841             ->symbol_in_display_form($symbol_id);
842             }
843              
844             sub Marpa::R2::Scanless::G::symbol_dsl_form {
845 59     59   286 my ( $slg, $symbol_id, $subgrammar ) = @_;
846 59         86 return thick_subgrammar_by_name( $slg, $subgrammar )
847             ->symbol_dsl_form($symbol_id);
848             }
849              
850             sub Marpa::R2::Scanless::G::symbol_description {
851 59     59   298 my ( $slg, $symbol_id, $subgrammar ) = @_;
852 59         93 return thick_subgrammar_by_name($slg, $subgrammar)
853             ->symbol_description($symbol_id);
854             }
855              
856             sub Marpa::R2::Scanless::G::rule_show
857             {
858 136     136   497 my ( $slg, $rule_id, $subgrammar) = @_;
859 136         290 return slg_rule_show($slg, $rule_id, thick_subgrammar_by_name($slg, $subgrammar));
860             }
861              
862             sub slg_rule_show {
863 136     136   249 my ( $slg, $rule_id, $subgrammar ) = @_;
864 136         325 my $tracer = $subgrammar->tracer();
865 136         231 my $subgrammar_c = $subgrammar->[Marpa::R2::Internal::Grammar::C];
866 136         333 my @symbol_ids = $tracer->rule_expand($rule_id);
867 136 50       325 return if not scalar @symbol_ids;
868             my ( $lhs, @rhs ) =
869 136         237 map { $subgrammar->symbol_in_display_form($_) } @symbol_ids;
  329         655  
870 136         368 my $minimum = $subgrammar_c->sequence_min($rule_id);
871 136         234 my @quantifier = ();
872              
873 136 100       267 if ( defined $minimum ) {
874 8 100       25 @quantifier = ( $minimum <= 0 ? q{*} : q{+} );
875             }
876 136         694 return join q{ }, $lhs, q{::=}, @rhs, @quantifier;
877             } ## end sub slg_rule_show
878              
879             sub Marpa::R2::Scanless::G::show_rules {
880 10     10   3695 my ( $slg, $verbose, $subgrammar ) = @_;
881 10         31 my $text = q{};
882 10   100     54 $verbose //= 0;
883 10   100     49 $subgrammar //= 'G1';
884              
885 10         41 my $thick_grammar = thick_subgrammar_by_name($slg, $subgrammar);
886              
887 10         28 my $rules = $thick_grammar->[Marpa::R2::Internal::Grammar::RULES];
888 10         20 my $grammar_c = $thick_grammar->[Marpa::R2::Internal::Grammar::C];
889              
890 10         21 for my $rule ( @{$rules} ) {
  10         28  
891 183         266 my $rule_id = $rule->[Marpa::R2::Internal::Rule::ID];
892              
893 183         366 my $minimum = $grammar_c->sequence_min($rule_id);
894 183 100       347 my @quantifier =
    100          
895             defined $minimum ? $minimum <= 0 ? (q{*}) : (q{+}) : ();
896 183         348 my $lhs_id = $grammar_c->rule_lhs($rule_id);
897 183         323 my $rule_length = $grammar_c->rule_length($rule_id);
898             my @rhs_ids =
899 183         318 map { $grammar_c->rule_rhs( $rule_id, $_ ) }
  266         573  
900             ( 0 .. $rule_length - 1 );
901             $text .= join q{ }, $subgrammar, "R$rule_id",
902             $thick_grammar->symbol_in_display_form($lhs_id),
903             '::=',
904 183         491 ( map { $thick_grammar->symbol_in_display_form($_) } @rhs_ids ),
  266         500  
905             @quantifier;
906 183         333 $text .= "\n";
907              
908 183 100       388 if ( $verbose >= 2 ) {
909              
910 48         73 my $description = $rule->[Marpa::R2::Internal::Rule::DESCRIPTION];
911 48 100       99 $text .= " $description\n" if $description;
912 48         69 my @comment = ();
913 48 50       127 $grammar_c->rule_length($rule_id) == 0
914             and push @comment, 'empty';
915 48 50       108 $thick_grammar->rule_is_used($rule_id)
916             or push @comment, '!used';
917 48 50       128 $grammar_c->rule_is_productive($rule_id)
918             or push @comment, 'unproductive';
919 48 50       139 $grammar_c->rule_is_accessible($rule_id)
920             or push @comment, 'inaccessible';
921 48 50       91 $rule->[Marpa::R2::Internal::Rule::DISCARD_SEPARATION]
922             and push @comment, 'discard_sep';
923              
924 48 50       83 if (@comment) {
925 0         0 $text .= q{ } . ( join q{ }, q{/*}, @comment, q{*/} ) . "\n";
926             }
927              
928             $text .= " Symbol IDs: <$lhs_id> ::= "
929 48         96 . ( join q{ }, map {"<$_>"} @rhs_ids ) . "\n";
  72         206  
930              
931             } ## end if ( $verbose >= 2 )
932              
933 183 100       402 if ( $verbose >= 3 ) {
934              
935 48         118 my $tracer = $thick_grammar->tracer();
936              
937             $text
938             .= " Internal symbols: <"
939             . $tracer->symbol_name($lhs_id)
940             . q{> ::= }
941             . (
942             join q{ },
943 48         95 map { '<' . $tracer->symbol_name($_) . '>' } @rhs_ids
  72         138  
944             ) . "\n";
945              
946             } ## end if ( $verbose >= 3 )
947              
948             } ## end for my $rule ( @{$rules} )
949              
950 10         97 return $text;
951             } ## end sub Marpa::R2::Scanless::G::show_rules
952              
953             sub Marpa::R2::Scanless::G::show_symbols {
954 4     4   722 my ( $slg, $verbose, $subgrammar ) = @_;
955 4         12 my $text = q{};
956 4   100     18 $verbose //= 0;
957 4   100     20 $subgrammar //= 'G1';
958              
959 4         13 my $thick_grammar = thick_subgrammar_by_name($slg, $subgrammar);
960              
961 4         10 my $symbols = $thick_grammar->[Marpa::R2::Internal::Grammar::SYMBOLS];
962 4         10 my $grammar_c = $thick_grammar->[Marpa::R2::Internal::Grammar::C];
963              
964 4         8 for my $symbol ( @{$symbols} ) {
  4         13  
965 68         112 my $symbol_id = $symbol->[Marpa::R2::Internal::Symbol::ID];
966              
967 68         152 $text .= join q{ }, $subgrammar, "S$symbol_id",
968             $thick_grammar->symbol_in_display_form($symbol_id);
969              
970 68         122 my $description = $symbol->[Marpa::R2::Internal::Symbol::DESCRIPTION];
971 68 100       118 if ($description) {
972 49         78 $text .= " -- $description";
973             }
974 68         94 $text .= "\n";
975              
976 68 100       120 if ( $verbose >= 2 ) {
977              
978 59         73 my @tag_list = ();
979 59 50       145 $grammar_c->symbol_is_productive($symbol_id)
980             or push @tag_list, 'unproductive';
981 59 50       134 $grammar_c->symbol_is_accessible($symbol_id)
982             or push @tag_list, 'inaccessible';
983 59 50       126 $grammar_c->symbol_is_nulling($symbol_id)
984             and push @tag_list, 'nulling';
985 59 100       132 $grammar_c->symbol_is_terminal($symbol_id)
986             and push @tag_list, 'terminal';
987              
988 59 100       102 if (@tag_list) {
989 30         56 $text
990             .= q{ } . ( join q{ }, q{/*}, @tag_list, q{*/} ) . "\n";
991             }
992              
993 59         118 my $tracer = $thick_grammar->tracer();
994 59         111 $text .= " Internal name: <"
995             . $tracer->symbol_name($symbol_id) . qq{>\n};
996              
997             } ## end if ( $verbose >= 2 )
998              
999 68 100       129 if ( $verbose >= 3 ) {
1000              
1001 59         87 my $dsl_form = $symbol->[Marpa::R2::Internal::Symbol::DSL_FORM];
1002 59 100       105 if ($dsl_form) { $text .= qq{ SLIF name: $dsl_form\n}; }
  42         105  
1003              
1004             } ## end if ( $verbose >= 3 )
1005              
1006             } ## end for my $symbol ( @{$symbols} )
1007              
1008 4         25 return $text;
1009             } ## end sub Marpa::R2::Scanless::G::show_symbols
1010              
1011             sub Marpa::R2::Scanless::G::show_dotted_rule {
1012 428     428   4427 my ( $slg, $rule_id, $dot_position ) = @_;
1013 428         688 my $grammar = $slg->[Marpa::R2::Internal::Scanless::G::THICK_G1_GRAMMAR];
1014 428         885 my $tracer = $grammar->tracer();
1015 428         647 my $grammar_c = $grammar->[Marpa::R2::Internal::Grammar::C];
1016             my ( $lhs, @rhs ) =
1017 428         895 map { $grammar->symbol_in_display_form($_) } $tracer->rule_expand($rule_id);
  2870         5526  
1018 428         846 my $rhs_length = scalar @rhs;
1019              
1020 428         969 my $minimum = $grammar_c->sequence_min($rule_id);
1021 428         616 my @quantifier = ();
1022 428 100       817 if (defined $minimum) {
1023 4 50       18 @quantifier = ($minimum <= 0 ? q{*} : q{+} );
1024             }
1025 428 100       752 $dot_position += ($rhs_length + 1) if $dot_position < 0;
1026 428 50       752 $dot_position = 0 if $dot_position < 0;
1027 428 100       752 if ($dot_position < $rhs_length) {
1028 324         656 splice @rhs, $dot_position, 0, q{.};
1029 324         1548 return join q{ }, $lhs, q{->}, @rhs, @quantifier;
1030             } else {
1031 104         549 return join q{ }, $lhs, q{->}, @rhs, @quantifier, q{.};
1032             }
1033             } ## end sub Marpa::R2::Grammar::show_dotted_rule
1034              
1035             sub Marpa::R2::Scanless::G::rule {
1036 6     6   55 my ( $slg, @args ) = @_;
1037 6         14 return $slg->[Marpa::R2::Internal::Scanless::G::THICK_G1_GRAMMAR]
1038             ->rule(@args);
1039             }
1040              
1041             sub Marpa::R2::Scanless::G::rule_ids {
1042 11     11   4360 my ($slg, $subgrammar) = @_;
1043 11         41 return thick_subgrammar_by_name($slg, $subgrammar)->rule_ids();
1044             }
1045              
1046             sub Marpa::R2::Scanless::G::symbol_ids {
1047 6     6   2248 my ($slg, $subgrammar) = @_;
1048 6         19 return thick_subgrammar_by_name($slg, $subgrammar)->symbol_ids();
1049             }
1050              
1051             sub Marpa::R2::Scanless::G::g1_rule_ids {
1052 1     1   958 my ($slg) = @_;
1053 1         5 return $slg->rule_ids();
1054             }
1055              
1056             sub Marpa::R2::Scanless::G::g0_rule_ids {
1057 1     1   15 my ($slg) = @_;
1058 1         4 return $slg->rule_ids('L0');
1059             }
1060              
1061             sub Marpa::R2::Scanless::G::g0_rule {
1062 17     17   158 my ( $slg, @args ) = @_;
1063 17         41 return $slg->[Marpa::R2::Internal::Scanless::G::THICK_LEX_GRAMMARS]->[0]
1064             ->rule(@args);
1065             }
1066              
1067             # Internal methods, not to be documented
1068              
1069             sub Marpa::R2::Scanless::G::thick_g1_grammar {
1070 0     0   0 my ($slg) = @_;
1071 0         0 return $slg->[Marpa::R2::Internal::Scanless::G::THICK_G1_GRAMMAR];
1072             }
1073              
1074             sub Marpa::R2::Scanless::G::show_irls {
1075 2     2   10 my ($slg, $subgrammar) = @_;
1076 2         7 return thick_subgrammar_by_name($slg, $subgrammar)->show_irls();
1077             }
1078              
1079             sub Marpa::R2::Scanless::G::show_isys {
1080 2     2   1558 my ($slg, $subgrammar) = @_;
1081 2         10 return thick_subgrammar_by_name($slg, $subgrammar)->show_isys();
1082             }
1083              
1084             1;
1085              
1086             # vim: expandtab shiftwidth=4: