X-Git-Url: https://git.sur5r.net/?a=blobdiff_plain;f=generate-command-parser.pl;h=f69f715e569594f479d1458493f5d935cbaebfcf;hb=85018de433fc5d59659298f9d337e155eeca9259;hp=175d7101592839e379b546845f347cd3df70187d;hpb=2daa8d422ae63d55e4b952070e8e894c0963618f;p=i3%2Fi3 diff --git a/generate-command-parser.pl b/generate-command-parser.pl index 175d7101..f69f715e 100755 --- a/generate-command-parser.pl +++ b/generate-command-parser.pl @@ -12,8 +12,18 @@ use strict; use warnings; use Data::Dumper; +use Getopt::Long; use v5.10; +my $input = ''; +my $prefix = ''; +my $result = GetOptions( + 'input=s' => \$input, + 'prefix=s' => \$prefix +); + +die qq|Input file "$input" does not exist!| unless -e $input; + # reads in a whole file sub slurp { open my $fh, '<', shift; @@ -24,8 +34,6 @@ sub slurp { # Stores the different states. my %states; -# XXX: don’t hardcode input and output -my $input = '../parser-specs/commands.spec'; my @raw_lines = split("\n", slurp($input)); my @lines; @@ -101,25 +109,30 @@ for my $line (@lines) { # Second step: Generate the enum values for all states. # It is important to keep the order the same, so we store the keys once. -my @keys = keys %states; +# We sort descendingly by length to be able to replace occurences of the state +# name even when one state’s name is included in another one’s (like FOR_WINDOW +# is in FOR_WINDOW_COMMAND). +my @keys = sort { length($b) <=> length($a) } keys %states; -open(my $enumfh, '>', 'GENERATED_enums.h'); +open(my $enumfh, '>', "GENERATED_${prefix}_enums.h"); # XXX: we might want to have a way to do this without a trailing comma, but gcc # seems to eat it. +my %statenum; say $enumfh 'typedef enum {'; my $cnt = 0; for my $state (@keys, '__CALL') { say $enumfh " $state = $cnt,"; + $statenum{$state} = $cnt; $cnt++; } say $enumfh '} cmdp_state;'; close($enumfh); # Third step: Generate the call function. -open(my $callfh, '>', 'GENERATED_call.h'); -say $callfh 'static char *GENERATED_call(const int call_identifier) {'; -say $callfh ' char *output = NULL;'; +open(my $callfh, '>', "GENERATED_${prefix}_call.h"); +my $resultname = uc(substr($prefix, 0, 1)) . substr($prefix, 1) . 'Result'; +say $callfh "static void GENERATED_call(const int call_identifier, struct $resultname *result) {"; say $callfh ' switch (call_identifier) {'; my $call_id = 0; for my $state (@keys) { @@ -133,29 +146,44 @@ for my $state (@keys) { $next_state ||= 'INITIAL'; my $fmt = $cmd; # Replace the references to identified literals (like $workspace) with - # calls to get_string(). + # calls to get_string(). Also replaces state names (like FOR_WINDOW) + # with their ID (useful for cfg_criteria_init(FOR_WINDOW) e.g.). + $cmd =~ s/$_/$statenum{$_}/g for @keys; $cmd =~ s/\$([a-z_]+)/get_string("$1")/g; - # Used only for debugging/testing. + $cmd =~ s/\&([a-z_]+)/get_long("$1")/g; + # For debugging/testing, we print the call using printf() and thus need + # to generate a format string. The format uses %d for s, + # literal numbers or state IDs and %s for NULL, s and literal + # strings. + $fmt =~ s/$_/%d/g for @keys; $fmt =~ s/\$([a-z_]+)/%s/g; + $fmt =~ s/\&([a-z_]+)/%ld/g; + $fmt =~ s/NULL/%s/g; $fmt =~ s/"([a-z0-9_]+)"/%s/g; + $fmt =~ s/(?:-?|\b)[0-9]+\b/%d/g; say $callfh " case $call_id:"; + say $callfh " result->next_state = $next_state;"; say $callfh '#ifndef TEST_PARSER'; my $real_cmd = $cmd; if ($real_cmd =~ /\(\)/) { - $real_cmd =~ s/\(/(¤t_match/; + $real_cmd =~ s/\(/(¤t_match, result/; } else { - $real_cmd =~ s/\(/(¤t_match, /; + $real_cmd =~ s/\(/(¤t_match, result, /; } - say $callfh " output = $real_cmd;"; + say $callfh " $real_cmd;"; say $callfh '#else'; # debug $cmd =~ s/[^(]+\(//; $cmd =~ s/\)$//; $cmd = ", $cmd" if length($cmd) > 0; - say $callfh qq| printf("$fmt\\n"$cmd);|; + say $callfh qq| fprintf(stderr, "$fmt\\n"$cmd);|; + # The cfg_criteria functions have side-effects which are important for + # testing. They are implemented as stubs in the test parser code. + if ($real_cmd =~ /^cfg_criteria/) { + say $callfh qq| $real_cmd;|; + } say $callfh '#endif'; - say $callfh " state = $next_state;"; say $callfh " break;"; $token->{next_state} = "call $call_id"; $call_id++; @@ -163,18 +191,19 @@ for my $state (@keys) { } say $callfh ' default:'; say $callfh ' printf("BUG in the parser. state = %d\n", call_identifier);'; +say $callfh ' assert(false);'; say $callfh ' }'; -say $callfh ' return output;'; +say $callfh ' state = result->next_state;'; say $callfh '}'; close($callfh); # Fourth step: Generate the token datastructures. -open(my $tokfh, '>', 'GENERATED_tokens.h'); +open(my $tokfh, '>', "GENERATED_${prefix}_tokens.h"); for my $state (@keys) { my $tokens = $states{$state}; - say $tokfh 'cmdp_token tokens_' . $state . '[' . scalar @$tokens . '] = {'; + say $tokfh 'static cmdp_token tokens_' . $state . '[' . scalar @$tokens . '] = {'; for my $token (@$tokens) { my $call_identifier = 0; my $token_name = $token->{token}; @@ -194,7 +223,7 @@ for my $state (@keys) { say $tokfh '};'; } -say $tokfh 'cmdp_token_ptr tokens[' . scalar @keys . '] = {'; +say $tokfh 'static cmdp_token_ptr tokens[' . scalar @keys . '] = {'; for my $state (@keys) { my $tokens = $states{$state}; say $tokfh ' { tokens_' . $state . ', ' . scalar @$tokens . ' },';