#! /usr/bin/env ruby # Build a grammar whose LALR(1) parser has a given number of states. # Useful to test edge cases (e.g., 256 and 257 states, etc.). # Copyright (C) 2020-2022 Free Software Foundation, Inc. # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # # Written by Akim Demaille. class Linear def initialize(states) @states = states - 4 @cols = Math.sqrt(@states).to_i @lines = @states / @cols @rest = @states % @cols @n = @lines * (@cols - 1) + (@rest == 0 ? 1 : @rest == 1 ? 2 : @rest) end def nterms last = @lines + ([0, 1].include?(@rest) ? 0 : 1) (0...last).map { |i| "t#{i}" }.join(' ') end def rules res = (0...@lines).map { |i| "t#{i}:#{' N' * (@cols - 1)}" }.join("\n") case @rest when 0 res += ' N' when 1 res += ' N N' else res += "\nt#{@lines}:#{" N" * @rest}" end res end def to_s puts <<~EOF // states: #{@states} // cols: #{@cols} // lines: #{@lines} // rest: #{@rest} // n: #{@n} %code { #include #include static int yylex (void); static void yyerror (const char *msg); } %debug %define api.value.type union %define parse.lac full %define parse.error verbose %printer { fprintf (yyo, "%ld", $$); } %token N %% exp: #{nterms} #{rules} %% static int yylex (void) { static long count = 0; if (count++ < #{@n}) { yylval.N = count; return N; } else return 0; } static void yyerror (const char *msg) { fprintf (stderr, "%s\\n", msg); } int main (void) { yydebug = !!getenv ("YYDEBUG"); return yyparse (); } EOF end end puts Linear.new(ARGV[0].to_i).to_s