PreviousUpNext

15.4.158  src/app/yacc/src/yacc.pkg

#  Mythryl-Yacc Parser Generator (c) 1989, 1990 Andrew W. Appel, David R. Tarditi 

# Compiled by:
#     src/app/yacc/src/mythryl-yacc.lib



###            "Computer programming is tremendous fun.
###
###             Like music, it is a skill that derives
###             from an unknown blend of innate talent
###             and constant practice.
###
###             Like drawing, it can be shaped to a
###             variety of ends -- commercial, artistic,
###             and pure entertainment.
###
###             Programmers have a well-deserved reputation
###             for working long hours but are rarely
###             credited with being driven by creative fevers.
###
###             Programmers talk about software development
###             on weekends, vacations, and over meals not
###             because they lack imagination, but because
###             their imagination reveals worlds that others
###             cannot see."
###
###                         -- Larry O'Brien and Bruce Eckel


stipulate
    package fil =  file__premicrothread;                        # file__premicrothread  is from   src/lib/std/src/posix/file--premicrothread.pkg
herein

    generic package   parser_generator_g   (
        #             ==================
        #
        package parse_gen_parser:  Parse_Gen_Parser;            # Parse_Gen_Parser      is from   src/app/yacc/src/parse-gen-parser.api
        package make_table:        Make_Lr_Table;               # Make_Lr_Table         is from   src/app/yacc/src/make-lr-table.api
        package verbose:           Verbose;                     # Verbose               is from   src/app/yacc/src/verbose.api
        package print_package:     Print_Package;               # Print_Package         is from   src/app/yacc/src/print-package.api

            sharing make_table::lr_table == print_package::lr_table;
            sharing make_table::errs     == verbose::errs;

            package deep_syntax:  Deep_Syntax;                  # Deep_Syntax           is from   src/app/yacc/src/deep-syntax.api
    )

    : (weak)  Parser_Generator_G                                # Parser_Generator_G    is from   src/app/yacc/src/parser-generator-g.api

    {
        include package   rw_vector;
        include package   list;

        infix my 9 sub;

        package grammar =  make_table::grammar;
        package header  =  parse_gen_parser::header;

        include package   header;
        include package   grammar;


        line_length = 200;              #  Approx. maximum length of a line 

        # Record type describing names of packages
        # in the program being generated:
        #
        Names = NAMES { lr_vals_pkg_macro_name: String,         #  Misc { n } package name 
                        lr_table_pkg_name:              String,         #  LR table package 
                        #
                        tokens_pkg_name:                String,         #  tokens { n } package name 
                        actions_pkg_name:               String,         #  Actions package 
                        #
                        values_pkg_name:                String,         #  semantic value package 
                        error_recovery_pkg_name:        String,         #  error correction package 
                        #
                        arg:                    String,         #  user argument for parser 
                        #
                        tokens_api_name:                String,         #  TOKENS { n } api 
                        lrvals_api_name:                String,         #  API for Misc package 
                        #
                        parser_data_pkg_name:   String,         #  Name of package which holds parser data 
                        parser_data_api_name:   String          #  Api for this package 
                      };

        to_lower =   string::map  char::to_lower;

        debug = TRUE;

        exception SEMANTIC;

        # Common functions and values used in printing out program 
        #
        Values = VALUES   { say:                String -> Void,
                            say_colon_colon: String -> Void,
                            sayln:              String -> Void,
                            pure_actions:       Bool,
                            pos_type:   String,
                            arg_type:   String,
                            ntvoid:             String,
                            termvoid:   String,
                            start:              grammar::Nonterminal,
                            has_type:   grammar::Symbol -> Bool,

                            #  Actual (user) name of terminal 

                            term_to_string:             grammar::Terminal -> String,
                            symbol_to_string:   grammar::Symbol   -> String,

                            # type Symbol comes from the HDR package,
                            # and is now abstract:


                            term:               List( (header::Symbol, Null_Or( Type )) ),
                            nonterm:    List( (header::Symbol, Null_Or( Type )) ),
                            terms:              List( grammar::Terminal ),

                            # token_info is the user inserted
                            # spec in the *_Token api

                            token_info: Null_Or( String )
                          };

        package symbol_hash
            =
            typelocked_hashtable_g (
                Element = String;
                gt = (>) : (String, String) -> Bool;
            );

        package term_table
            =
            table_g (
                Key =   grammar::Terminal;

                fun gt (TERM i, TERM j) =   i > j;
            );

        package symbolmapstack
            = 
            table_g (

                Key = grammar::Symbol;

                fun gt (   TERMINAL (  TERM  i),    TERMINAL (  TERM  j)) =>  i > j;
                    gt (NONTERMINAL (NONTERM i), NONTERMINAL (NONTERM j)) =>  i > j;
                    gt (NONTERMINAL      _,         TERMINAL           _) =>   TRUE;
                    gt (   TERMINAL _,           NONTERMINAL _          ) =>  FALSE;
                end;
            );

        fun force_uppercase string      # Leave "FOO" alone, map "foo" to "QQ_FOO"
            =
            case (list::find char::is_lower (string::explode string))
                #
                NULL  => string;

                THE _ => "QQ_" + (
                             string::implode (
                                 map char::to_upper (string::explode string)
                             )
                         );
            esac;

        #   print_types: function to print the following types in the lr_values
        #   package and a package containing the type Semantic_Value:
        #
        #           Semantic_Value  -- it holds semantic values on the parse stack
        #           Source_Position -- the type of line numbers
        #           Result          -- the type of the value that results from the parse
        #
        #       The type Semantic_Value is set equal to the type Semantic_Value declared
        #       in the package named by values_pkg_name.  The type Semantic_Value
        #       is declared inside the package named by values_pkg_name to deal
        #       with the scope of constructors.
        #
        fun print_types (   VALUES { say, sayln, term, nonterm, symbol_to_string, pos_type, arg_type, termvoid, ntvoid, say_colon_colon, has_type, start, pure_actions, ... },
                            NAMES { values_pkg_name, ... },
                            symbol_type
                        )
            =
            {   fun print_constructors (symbol, THE s)
                        => 
                        say (   " | "
                            +   (force_uppercase (symbol_name symbol))
                            +   " "
                            +   (if pure_actions  ""; else "Void -> ";fi)
                            +   " ("
                            +   name_of_type s
                            +   ")"
                            );

                    print_constructors _ => ();
                end;

                sayln "stipulate include package   header; herein";
                sayln ("Source_Position = " + pos_type + ";");
                sayln ("Arg = " + arg_type + ";");
                sayln ("package " + values_pkg_name + " { ");

                say (   "Semantic_Value = "
                    +   termvoid
                    +   " | "
                    +   ntvoid
                    +   " "
                    +   (if pure_actions  " Void"; else " Void -> Void";fi)
                    );

                apply print_constructors term;
                apply print_constructors nonterm;
                sayln ";\n};";

                sayln (   "Semantic_Value = "
                      +   values_pkg_name
                      +   "::Semantic_Value;"
                      );

                say "Result = ";

                case (symbol_type (NONTERMINAL start))
                    #
                    NULL  =>   sayln "Void;";
                    THE t =>   { say (name_of_type t);   sayln ";"; };
                esac;

                sayln "end;";
            };

        # function to print tokens { n } package 
        #
        fun print_tokens_pkg (  VALUES  { say, sayln, term_to_string, has_type, termvoid, terms, pure_actions, token_info, ... },
                                NAMES { lr_vals_pkg_macro_name, lr_table_pkg_name, values_pkg_name, tokens_pkg_name, tokens_api_name, parser_data_pkg_name, ... }
                             )
            =
            {   sayln ("package " + tokens_pkg_name + " : (weak) " + tokens_api_name + " {");

                case token_info
                    NULL => ();
                    _    => sayln ("include package " + parser_data_pkg_name + "::header;");
                esac;

                sayln ("Semantic_Value = " + parser_data_pkg_name + "::Semantic_Value;");

                sayln "Token (X,Y) = token::Token(X,Y);";

                # Following function generates a per-terminal
                # terminal-making function looking like one of
                # the following (depending whether the terminal
                # has an associated value):
                #
                #     fun int  (i, p1, p2) = token::TOKEN (parser_data::lr_table::TERM 14, (parser_data::values::INT (\\ () => i), p1, p2));
                #     fun keyword (p1, p2) = token::TOKEN (parser_data::lr_table::TERM 15, (parser_data::values::TM_VOID,          p1, p2));
                #
                fun print_term_function (term as TERM i)
                    =
                    {   say "fun "; say (to_lower (term_to_string term));
                        say " (";

                        if (has_type (TERMINAL term))
                            #
                            say "i, ";
                        fi;

                        say "p1, p2) = token::TOKEN (";
                        say (parser_data_pkg_name + "::" + lr_table_pkg_name + "::TERM ");
                        say (int::to_string i);
                        say ", (";
                        say (parser_data_pkg_name + "::" + values_pkg_name + "::");

                        if (has_type (TERMINAL term))
                            #                    
                            say (term_to_string term);

                            if pure_actions   say " i";
                            else              say " (\\\\ () = i)";
                            fi;
                        else
                            say termvoid;
                        fi;

                        say ", ";
                        sayln "p1, p2));";
                    };

                apply print_term_function terms;

                sayln "};";
            };


        # Function to print out api - takes print function
        # which does not need to insert line breaks:
        #
        fun print_apis (   VALUES  { term, token_info, ... },
                          NAMES { tokens_api_name, tokens_pkg_name, lrvals_api_name, parser_data_pkg_name, parser_data_api_name, ... },
                          say
                      )
            =
            say  (   "api " + tokens_api_name + " {\n" +
                     case token_info    NULL => "";  THE s => s + "\n"; esac +
                     "    Token (X,Y);\n" +
                     "    Semantic_Value;\n" +
                     (   list::fold_backward
                             (   \\ ((s, type), r)
                                    =>
                                    string::cat [
                                        "    ",
                                        to_lower (symbol_name s),
                                        case type

                                             NULL  =>  ": ("; 
                                             THE l =>  ": ((" + (name_of_type l) + "), ";
                                         esac,

                                        "X, X) -> Token (Semantic_Value,X);\n",
                                        r
                                    ]; end 
                             )
                             ""
                             term
                     ) +
                     "};\n" +
                     "api " + lrvals_api_name + "{\n" +
                     "    package tokens:  " + tokens_api_name +  ";\n" +
                     "    package " + parser_data_pkg_name + ": " + parser_data_api_name + ";\n" +
                     "    sharing " + parser_data_pkg_name + "::token::Token == tokens::Token;\n" +
                     "    sharing " + parser_data_pkg_name + "::Semantic_Value == tokens::Semantic_Value;\n" +
                     "};\n"
                  );

        # Function to print package for error recovery
        #
        fun print_error_recovery (
                        keyword:            List( Terminal ),
                        preferred_change:   List( (List( Terminal ), List( Terminal ))),
                        noshift:            List( Terminal ),
                        value:              List( (Terminal, String) ),

                        VALUES  { term_to_string, say, sayln, terms, say_colon_colon, has_type, termvoid, pure_actions, ... },
                        NAMES { error_recovery_pkg_name, lr_table_pkg_name, values_pkg_name, ... }
                    )
            =
            {   fun sayterm (TERM i)
                    =
                    {   say "(TERM ";
                        say (int::to_string i);
                        say ")";
                    };

                fun print_boolean_case ( l:  List( Terminal ))
                    =
                    {   say "\\\\ ";

                        apply
                            (\\ t =  { sayterm t; say " => TRUE"; say "; ";})
                            l;

                        sayln "_ => FALSE; end;";
                    };

                fun print_terminals_list (l:  List( Terminal ))
                    =
                    {   sayln "NIL";

                        apply
                            (\\ t =  { say " @@ "; sayterm t;})
                            (reverse l);
                    };


                fun print_change ()
                    =
                    {   sayln "my preferred_change:   List( (List( Terminal ), List( Terminal )) ) = ";

                        apply
                            (\\ (d, i)
                                 =>
                                 {   say"(";
                                     print_terminals_list d;
                                     say ", ";
                                     print_terminals_list i; 
                                     sayln ") ! ";
                                 }; end 
                            )
                            preferred_change;

                        sayln "NIL;";
                    };

                fun print_error_values (l:   List( (Terminal, String) ))
                    =
                    {   sayln "stipulate include package   header; herein";
                        sayln "errtermvalue=";
                        say "\\\\ ";

                        apply
                            (\\ (t, s)
                                =>
                                {   sayterm t;
                                    say " => ";
                                    say_colon_colon values_pkg_name;
                                    say (term_to_string t);
                                    say "(";
                                    if (not pure_actions ) say "\\\\ () = "; fi;
                                    say "(";
                                    say s;
                                    say "))";
                                    sayln "; ";
                                }; end 
                            )
                            l;

                       say "_ => ";
                       say (values_pkg_name + "::");
                       sayln (termvoid + ";");
                       sayln " end; end;";
                   };


                fun print_names ()
                    =
                    {   fun f term
                            =
                            {   sayterm term; say " => ";
                                sayln (string::cat ["\"", term_to_string term, "\""]);
                                say "; ";
                            };

                        sayln "show_terminal =";
                        say "\\\\ ";
                        apply f terms;
                        sayln "_ => \"bogus-term\"; end;";
                    };

                error_recovery_terms
                    = 
                    list::fold_backward
                        (   \\ (t, r)
                                =
                                if (has_type (TERMINAL t) or exists (\\ (a, _) =   a == t) value)
                                         r;
                                else t ! r;
                                fi
                        )
                        []
                        terms;

                say "package ";
                say error_recovery_pkg_name;
                sayln "{";
                sayln ("include package " + lr_table_pkg_name + ";");
                sayln "infix my 60 @@;";
                sayln "fun x @@ y = y ! x;";
                sayln "is_keyword =";

                print_boolean_case keyword;
                print_change();

                sayln "no_shift = ";

                print_boolean_case noshift;
                print_names ();
                print_error_values value;

                say "my terms:  List( Terminal ) = ";

                print_terminals_list error_recovery_terms;

                sayln ";\n};";
            };


        fun print_actions (  rules,
                             VALUES { has_type, say, sayln, termvoid, ntvoid, symbol_to_string, say_colon_colon, start, pure_actions, ... },
                             NAMES { actions_pkg_name, values_pkg_name, lr_table_pkg_name, arg, ... },
                             term_hash,
                             symbol_hash
                         )
            =
            {   print_deep_syntax_tree_rule
                    =
                    deep_syntax::print_rule (say, sayln);

                fun is_nonterm (NONTERMINAL i) =>   TRUE;
                    is_nonterm  _              =>   FALSE;
                end;

                fun number_rhs r
                    =
                    list::fold_forward
                        (\\ (e, (r, table))
                            =
                            { num = case (symbolmapstack::find (e, table))
                                        THE i => i;
                                        NULL => 1;
                                    esac;
                              ((e, num, has_type e or is_nonterm e) ! r,
                                 symbolmapstack::set((e, num+1), table));
                            }
                        )
                        (NIL, symbolmapstack::empty)
                        r;

                fun print_rule (   i: Int,
                                   r as { lhs as (NONTERM lhs_num), prec, rhs, code, rulenum }
                               )
                    =
                    {   include package   deep_syntax;
                        #

                        # Build an argument:
                        # 
                        fun make_token (sym, num:  Int, typed)
                            =
                            {   symbol_string =   symbol_to_string sym;

                                uc_symbol_string =   force_uppercase   symbol_string;

                                symbol_string =   to_lower symbol_string;

                                symbol_number =   symbol_string + (int::to_string num);

                                PTUPLE [

                                    WILD,

                                    PTUPLE [

                                        if (not (has_type sym))

                                            if   (is_nonterm sym)

                                                 PAPP (
                                                     values_pkg_name + "::" + ntvoid,
                                                     PVAR symbol_number
                                                 );
                                            else
                                                 WILD;
                                            fi;
                                        else    
                                            PAPP (
                                                values_pkg_name + "::" + uc_symbol_string,

                                                if   (num == 1   and   pure_actions)

                                                     AS (symbol_number, PVAR symbol_string);
                                                else
                                                     PVAR symbol_number;
                                                fi
                                            );
                                        fi,

                                        if (num == 1)

                                            AS (symbol_string + "left", PVAR (symbol_number + "left"));
                                        else
                                            PVAR (symbol_number + "left");
                                        fi,

                                        if (num == 1)

                                            AS (symbol_string + "right", PVAR (symbol_number + "right"));
                                        else
                                            PVAR (symbol_number + "right");
                                        fi
                                    ]
                                ];
                            };

                        numbered_rhs =   #1 (number_rhs rhs);

                        # Construct case pattern 

                        pattern = PTUPLE [ PINT i, PLIST (map make_token numbered_rhs,
                                                      THE (PVAR "rest671"))];

                        # Remove terminals in argument list w/o types 
                        #       
                        args_with_types
                            =
                            list::fold_backward
                                \\ ((_, _, FALSE), r)      =>       r;
                                    (s as (_, _, TRUE), r) =>   s ! r;
                                end
                                NIL
                                numbered_rhs;

                        # Construct case body 
                        #
                        default_position = EVAR "default_position";
                        resultexp        = EVAR "result";
                        resultpat        = PVAR "result";
                        code             = CODE code;
                        rest             = EVAR "rest671";

                        body =  LET ( [ NAMED_VALUE
                                          (resultpat,

                                            EAPP (  EVAR (   values_pkg_name + "::"
                                                            +
                                                            if (has_type (NONTERMINAL lhs))
                                                                force_uppercase (symbol_to_string (NONTERMINAL lhs));
                                                            else
                                                                ntvoid;
                                                            fi
                                                         ),

                                                    if pure_actions
                                                        #
                                                        code;

                                                    elif (args_with_types==NIL)
                                                        #
                                                        FN (WILD, code);

                                                    else
                                                        FN ( WILD,

                                                             if (has_type (NONTERMINAL lhs))   body;
                                                             else                              SEQ (body, UNIT);
                                                             fi
                                                             where
                                                                 body = LET (map (\\ (sym, num: Int, _)
                                                                                     =
                                                                                     { symbol_string = to_lower (symbol_to_string sym);
                                                                                          symbol_number = symbol_string + int::to_string num;
                                                                                       NAMED_VALUE (if (num==1 )
                                                                                                 AS (symbol_string, PVAR symbol_number);
                                                                                            else PVAR symbol_number;fi,
                                                                                            EAPP (EVAR symbol_number, UNIT));
                                                                                     }
                                                                                 )
                                                                                 (reverse args_with_types),
                                                                                 code
                                                                            );
                                                             end
                                                           );
                                                    fi
                                               )
                                             )
                                      ],

                                      ETUPLE
                                        [ EAPP (EVAR (lr_table_pkg_name + "::NONTERM"), EINT (lhs_num)),

                                          case rhs

                                              NIL =>
                                                  ETUPLE [resultexp, default_position, default_position];

                                              r   =>
                                                  {   my (rsym, rnum, _) = head (numbered_rhs);
                                                      my (lsym, lnum, _) = head (reverse numbered_rhs);

                                                      ETUPLE
                                                        [ resultexp,
                                                          EVAR ((to_lower (symbol_to_string lsym)) + int::to_string lnum + "left"),
                                                          EVAR ((to_lower (symbol_to_string rsym)) + int::to_string rnum + "right")
                                                        ];
                                                 };
                                          esac,

                                          rest
                                        ]
                                    );

                        print_deep_syntax_tree_rule (RULE (pattern, body));

                    };          # fun print_rule

                fun print_rules ()
                    =
                    {   sayln "\\\\ (i392, default_position, stack, ";
                        say   "    (";
                        say   arg;
                        sayln "): Arg) = ";
                        sayln "case (i392, stack)";
                        say   " ";

                        apply
                            (\\ (rule as { rulenum, ... } )
                                =
                                {   print_rule (rulenum, rule);
                                    say "; ";
                                }
                            )
                            rules;

                         sayln "_ => raise exception (MLY_ACTION i392);";
                         sayln "esac;";
                    };

                say "package ";
                say actions_pkg_name;
                sayln " {";
                sayln "exception MLY_ACTION Int;";
                sayln "stipulate include package   header; herein";
                sayln "actions = ";

                print_rules ();

                sayln "end;";
                say "void = ";
                say_colon_colon values_pkg_name;
                sayln (termvoid + ";");
                say "extract = ";
                say "\\\\ a = (\\\\ ";
                say_colon_colon values_pkg_name;

                if (has_type (NONTERMINAL start))

                    say (force_uppercase (symbol_to_string (NONTERMINAL start)));
                else
                    say "ntVOID";
                fi;

                sayln " x => x;";
                sayln " _ => { exception PARSE_INTERNAL;";
                say "\t raise exception PARSE_INTERNAL; }; end ) a ";
                sayln (if pure_actions  ";"; else "();";fi);
                sayln "};";
            };                  # fun print_actions

        fun make_parser (
                (   header,
                    DECL { eop, change, keyword, nonterm, prec, term, control, value } : Decl_Data,
                    rules:  List( Rule )
                ),
                spec,
                error:  Source_Position -> String -> Void,
                was_error:  Void -> Bool
            )
            =
            {   verbose
                    =
                    list::exists
                        \\ VERBOSE => TRUE;
                           _       => FALSE;
                        end
                        control;

                default_reductions
                    =
                    not (
                        list::exists
                            \\ NODEFAULT => TRUE;
                               _         => FALSE;
                            end
                            control
                    );

                pos_type
                    =
                    f control
                    where 
                        fun f NIL           =>   NULL;
                            f ((POS s) ! r) =>   THE s; 
                            f (_ ! r)       =>   f r;
                        end;
                    end;

                start
                    =
                    f control
                    where 
                        fun f NIL => NULL;
                            f ((START_SYM s) ! r) => THE s; 
                            f (_ ! r) => f r;
                        end;
                    end;

                name
                    =
                    f control
                    where 
                        fun f NIL => NULL;
                            f ((PARSER_NAME s) ! r) => THE s; 
                            f (_ ! r) => f r;
                        end;
                   end;

                header_decl
                    =
                    f control
                    where 
                        fun f NIL => NULL;
                            f ((GENERIC s) ! r) => THE s; 
                            f (_ ! r) => f r;
                        end;
                    end;

                token_api_info_decl
                    =
                    f control
                    where 
                        fun f NIL => NULL;
                            f ((TOKEN_API_INFO s) ! _) => THE s;
                            f (_ ! r) => f r;
                        end;
                    end;

                arg_decl
                    =
                    f control
                    where 
                        fun f NIL => ("()", "Void");
                            f ((PARSE_ARG s) ! r) => s; 
                            f (_ ! r) => f r;
                        end;
                    end;

                noshift
                    =
                    f control
                    where 
                        fun f NIL => NIL;
                            f ((NSHIFT s) ! r) => s; 
                            f (_ ! r) => f r;
                        end;
                    end;

                pure_actions
                    =
                    f control
                    where 
                        fun f NIL            =>   FALSE;
                            f ((PURE) ! r) =>   TRUE; 
                            f (_ ! r)      =>   f r;
                        end;
                    end;

                term
                    =
                    case term
                        NULL  =>   { error 1 "missing %term definition"; NIL;};
                        THE l =>   l;
                    esac;

                nonterm
                    =
                    case nonterm
                         NULL  =>   {   error 1 "missing %nonterm definition";
                                        NIL;
                                    };
                         THE l =>   l;
                    esac;

                pos_type
                    =
                    case pos_type

                         NULL  =>   { error 1 "missing %pos definition"; "";};
                         THE l =>   l;
                    esac;


                term_hash
                    = 
                    list::fold_backward
                        (\\ ((symbol, _), table)
                            =
                            {   name =   symbol_name symbol;

                                if (symbol_hash::exists (name, table))
                                    error (symbol_pos symbol) ("duplicate definition of " + name + " in %term");
                                    table;
                                else
                                    symbol_hash::add (name, table);
                                fi;
                            }
                        )
                        symbol_hash::empty
                        term;

                fun is_term name
                    =
                    symbol_hash::exists (name, term_hash);

                symbol_hash
                    = 
                    list::fold_backward
                        (\\ ((symbol, _), table)
                            =
                            {   name = symbol_name symbol;

                                if  (symbol_hash::exists (name, table))

                                    error (symbol_pos symbol)
                                        (if   (is_term name)

                                              name + " is defined as a terminal and a nonterminal";
                                         else 
                                              "duplicate definition of " + name + " in %nonterm";
                                         fi);
                                    table;

                                else
                                    symbol_hash::add (name, table);
                                fi;
                            }
                        )
                        term_hash
                        nonterm;

                fun make_unique_id s
                    =
                    symbol_hash::exists (s, symbol_hash)
                      ??  make_unique_id (s + "'")
                      ::  s;

                if (was_error())   raise exception SEMANTIC;   fi;

                num_terms    = symbol_hash::size term_hash;
                num_nonterms = symbol_hash::size symbol_hash - num_terms;

                fun symbol_error sym err symbol
                    =
                    error (symbol_pos symbol)
                          (symbol_name symbol + " in " + err + " is not defined as a " + sym);

                stipulate
                    term_error =  symbol_error  "terminal";
                herein 

                    fun term_num statement
                        =
                        {   statement_error =  term_error  statement;
                            #
                            \\ symbol
                                =
                                case (symbol_hash::find (symbol_name symbol, symbol_hash))
                                    #
                                    NULL  => {   statement_error symbol;
                                                 TERM -1;
                                             };

                                    THE i => TERM if (i < num_terms)
                                                      i;
                                                  else
                                                      statement_error symbol;
                                                      -1;
                                                  fi;
                                esac;
                        };
                end;

                stipulate
                    nonterm_error  =  symbol_error  "nonterminal";
                herein   

                    fun nonterm_num  statement
                        =
                        {   statement_error =  nonterm_error  statement;
                            #
                            \\ symbol
                                =
                                case (symbol_hash::find (symbol_name symbol, symbol_hash))
                                    #
                                    NULL  => {   statement_error symbol;
                                                 NONTERM -1;
                                             };

                                    THE i => if (i >= num_terms)
                                                 NONTERM (i-num_terms);
                                             else
                                                 statement_error symbol;
                                                 NONTERM -1;
                                             fi;
                                esac;
                        };
                end;

                my symbol_num:  String -> header::Symbol -> grammar::Symbol
                    =
                    {   symbol_error
                            =
                            symbol_error "symbol"; 

                        \\ statement
                            =
                            {   statement_error
                                    =
                                    symbol_error statement;

                                \\ symbol
                                    =
                                    case (symbol_hash::find (symbol_name symbol, symbol_hash))

                                        NULL  =>    {   statement_error symbol;
                                                        NONTERMINAL (NONTERM -1);
                                                    };

                                        THE i =>    if (i >= num_terms)  NONTERMINAL (NONTERM (i-num_terms));
                                                    else                 TERMINAL (TERM i);
                                                    fi;
                                    esac;
                            };
                    };

                #  Map all symbols in the following values to terminals and check that
                #  the symbols are defined as terminals:
                #
                #           eop:  List( symbol )
                #           keyword: List( symbol )
                #           prec:  List( lexvalue * ( List( symbol ) ))
                #           change:  List( List( symbol ) * List( symbol ) )


                eop     =  map (term_num "%eop")     eop;
                keyword =  map (term_num "%keyword") keyword;

                prec = map (\\ (a, l)
                                =
                                (a, case a
                                       LEFT     => map (term_num "%left")     l;
                                       RIGHT    => map (term_num "%right")    l;
                                       NONASSOC => map (term_num "%nonassoc") l;
                                    esac
                                )
                           )
                           prec;

                change
                    =
                    {   map_term
                            =
                            term_num "%prefer, %subst, or %change";

                        map 
                            (\\ (a, b) =  (map map_term a, map map_term b))
                            change;
                    };

                noshift
                    =
                    map
                        (term_num "%noshift")
                        noshift;

                value
                    =
                    {   map_term = term_num "%value";

                        map (\\ (a, b) = (map_term a, b))
                            value;
                    };

                my (rules, _)
                    =
                    {   symbol_num  = symbol_num "rule";
                        nonterm_num = nonterm_num "rule";

                        term_num = term_num "%prec tag";

                        list::fold_backward

                            (\\ (RULE { lhs, rhs, code, prec }, (l, n))
                                =
                                ( { lhs=>nonterm_num lhs,
                                    rhs=>map symbol_num rhs,
                                    code,
                                    prec=>case prec
                                              THE t =>  THE (term_num t);
                                              NULL  =>  NULL;
                                          esac,
                                    rulenum=>n
                                  }
                                  ! l,
                                  n - 1
                                )
                            )
                            (NIL, length rules - 1)
                            rules;
                    };

                if (was_error ())   raise exception SEMANTIC;   fi;

                # term_to_string: map terminals back to strings 
                #
                stipulate 
                    #
                    data =  make_rw_vector (num_terms, "");

                    fun unmap (symbol, _)
                        =
                        {   name =  symbol_name  symbol;
                            #
                            set (
                                data,

                                case (symbol_hash::find (name, symbol_hash))
                                    #
                                    THE i =>   i;
                                    NULL  =>   raise exception DIE "term_to_string";
                                esac,

                                name
                            );
                        };

                herein
                                            my _ =
                    apply unmap term;

                    fun term_to_string (TERM i)
                        =
                        if (debug  and  (i < 0  or  i >= num_terms))
                            #                   
                            "bogus-num" + (int::to_string i);
                        else
                            data[ i ];
                        fi;
                end;

                stipulate
                    #
                    data = make_rw_vector (num_nonterms, "");

                    fun unmap (symbol, _)
                        =
                        {   name =  symbol_name  symbol;
                            #
                            set
                              ( data,

                                case (symbol_hash::find (name, symbol_hash))
                                    #
                                    THE i =>   i - num_terms;
                                    NULL  =>   raise exception DIE "nonterm_to_string";
                                esac,

                                name
                              );
                        };


                herein
                                            my _ =
                    apply unmap nonterm;

                    fun nonterm_to_string (NONTERM i)
                        =
                        if (debug  and  (i < 0 or i >= num_nonterms))
                            #                   
                            "bogus-num" + (int::to_string i);
                        else
                             data[ i ];
                        fi;
                end;

                #  create functions mapping terminals to precedence numbers and rules to
                #  precedence numbers.
                #
                #  Precedence statements are listed in order of ascending (tighter naming)
                #  precedence in the specification.   We receive a list composed of pairs
                #  containing the kind of precedence (left, right, or assoc) and a list of
                #  terminals associated with that precedence.  The list has the same order as
                #  the corresponding declarations did in the specification.
                #
                #  Internally, a tighter naming has a higher precedence number.  We give
                #  precedences using multiples of 3:
                #
                #               p+2 = right associative (force shift of symbol)
                #               p+1 = precedence for rule
                #               p = left associative (force reduction of rule)
                #
                #  Nonassociative terminals are given also given a precedence of p+1.  The
                #  table generator detects when the associativity of a nonassociative terminal
                #  is being used to resolve a shift/reduce conflict by checking if the
                #  precedences of the rule and the terminal are equal.
                #
                #  A rule is given the precedence of its rightmost terminal
                #
                stipulate

                    prec_data =   make_rw_vector (num_terms, NULL:  Null_Or( Int ));

                    fun add_prec term_prec (term as (TERM i))
                        =
                        case (prec_data[ i ])

                             NULL  =>   set (prec_data, i, term_prec);
                             THE _ =>   error 1 ("multiple precedences specified for terminal " + (term_to_string term));
                        esac;

                    fun term_prec ((LEFT, _),     i) => i;
                        term_prec ((RIGHT, _),    i) => i+2;
                        term_prec ((NONASSOC, l), i) => i+1;
                    end;

                herein 
                                            my _ =
                    list::fold_forward
                        (\\ (args as ((_, l), i))
                             =
                             { apply (add_prec (THE (term_prec args))) l; i+3;}
                        )
                        0 prec;

                    fun term_prec (TERM i)
                        =
                        if   (debug and (i < 0 or i >= num_terms))

                             NULL;
                        else
                             prec_data[ i ];
                        fi;
                end;

                fun elim_assoc i
                    =
                    (i - (i % 3) + 1);

                stipulate

                   fun find_right_term (NIL, r)
                           =>
                           r;

                       find_right_term (TERMINAL t ! tail, r)
                           =>
                           find_right_term (tail, THE t);

                       find_right_term (_ ! tail, r)
                           =>
                           find_right_term (tail, r);
                   end;

                herein

                    fun rule_prec rhs
                        =
                        case (find_right_term (rhs, NULL))

                            THE term => case (term_prec term)
                                            THE i =>  THE  (elim_assoc i);
                                            a     =>  a;
                                        esac;

                            NULL     => NULL;
                        esac;
                end;

                grammar_rules
                    =
                    map conv rules
                    where 

                        fun conv { lhs, rhs, code, prec, rulenum }
                            =
                            { rulenum,
                              lhs,
                              rhs,
                              precedence
                                  =>
                                  case prec

                                      THE t =>   case (term_prec t)
                                                     THE i =>   THE (elim_assoc i);
                                                     a     =>   a;
                                                 esac;

                                       _    =>   rule_prec rhs;
                                  esac
                            };

                    end;


                # Get start symbol 
                #
                start = case start
                            #
                            NULL     =>   .lhs (head grammar_rules);
                            THE name =>   nonterm_num "%start" name;
                        esac;


                # fun symbol_type
                #
                stipulate

                    data = make_rw_vector (   num_terms + num_nonterms,
                                              NULL:  Null_Or( Type )
                                          );

                    fun unmap (symbol, type)
                        =
                        set (

                            data,

                            case (symbol_hash::find (symbol_name symbol, symbol_hash))
                                #                          
                                THE i =>   i;
                                NULL  =>   raise exception DIE "symbol_type";
                            esac,

                            type
                        );

                herein
                                                    my _ =
                    apply unmap term;           my _ =
                    apply unmap nonterm;

                    fun symbol_type (NONTERMINAL (NONTERM i)) =>   if  (debug and (i<0 or i>=num_nonterms)  )   NULL; else   data[ i + num_terms ];   fi;
                        symbol_type (   TERMINAL (   TERM i)) =>   if  (debug and (i<0 or i>=num_terms)     )   NULL; else   data[ i             ];   fi;
                    end;
                end;

                fun symbol_to_string (NONTERMINAL i) =>   nonterm_to_string i;
                    symbol_to_string (   TERMINAL i) =>      term_to_string i;
                end;

                grammar  = GRAMMAR { rules             =>  grammar_rules,
                                     terms             =>  num_terms,
                                     nonterms          =>  num_nonterms,

                                     precedence        =>  term_prec,

                                     eop,
                                     start,
                                     noshift,

                                     term_to_string,
                                     nonterm_to_string
                                   };

                mixed_case_name
                    =
                    case name
                        THE s =>  symbol_name s;
                        NULL  =>  "";
                    esac;

                lowercase_name =   to_lower mixed_case_name;

                names = NAMES {    lr_vals_pkg_macro_name  => lowercase_name + "_lr_vals_fun",
                                   values_pkg_name         => "values",
                                   lr_table_pkg_name       => "lr_table",

                                   tokens_pkg_name         => "tokens",
                                   actions_pkg_name        => "actions",
                                   error_recovery_pkg_name => "error_recovery",
                                   arg                     => #1 arg_decl,

                                   tokens_api_name         => mixed_case_name + "_Tokens",
                                   lrvals_api_name         => mixed_case_name + "_Lrvals",

                                   parser_data_pkg_name    => "parser_data",
                                   parser_data_api_name    => "Parser_Data"
                              };

                (make_table::make_table (grammar, default_reductions))
                    ->
                    (table, state_errors, core_print, errs);

                entries = REF 0;                # Track number of action table entries here 


                {   result = fil::open_for_write (spec + ".pkg");               # Save    the synthesized code for foo.grammar in foo.grammar.pkg.
                    apis   = fil::open_for_write (spec + ".api");               # Declare the synthesized code for foo.grammar in foo.grammar.api.

                    pos = REF 0;

                    fun pr s
                        =
                        fil::write (result, s);

                    fun say s
                        =
                        {   l = string::length_in_bytes s;

                            new_pos =  *pos + l;

                            if (new_pos > line_length) 
                                #                            
                                pr "\n";
                                pos := l;
                            else
                                pos := new_pos;
                            fi;

                            pr s;
                        };

                    fun say_colon_colon s
                        =
                        say (s + "::");

                    fun sayln t
                        =
                        {   pr t;
                            pr "\n";
                            pos := 0;
                        };

                    termvoid =   make_unique_id "TM_VOID";
                    ntvoid   =   make_unique_id "NT_VOID";

                    fun has_type s
                        =
                        case (symbol_type s)
                            #
                            NULL =>   FALSE;
                            _    =>   TRUE;
                        esac;

                    terms =   f 0
                              where 
                                  fun f n =   if (n == num_terms)   NIL;
                                              else                      (TERM n) ! f (n+1);
                                              fi;

                              end;

                    values = VALUES { say, sayln, say_colon_colon,
                                      termvoid, ntvoid,
                                      has_type, pos_type,
                                      start, pure_actions,
                                      term_to_string, symbol_to_string,
                                      term, nonterm, terms,

                                      arg_type  =>   #2 arg_decl,
                                      token_info =>   token_api_info_decl
                                  };

                    names
                        ->
                        NAMES
                          { lr_vals_pkg_macro_name,
                            lr_table_pkg_name,
                            parser_data_pkg_name,
                            tokens_api_name,
                            tokens_pkg_name,
                            parser_data_api_name,
                            ...
                          };


                    case header_decl
                        #                 
                        THE s => say s;

                        NULL  => {   say "generic package "; say lr_vals_pkg_macro_name; 
                                     sayln "(package token:  Token;)";

                                     say " : (weak) api { package ";
                                     say parser_data_pkg_name;
                                     say " : ";
                                     sayln (parser_data_api_name + ";");

                                     say "       package ";
                                     say tokens_pkg_name; say " : ";
                                     sayln (tokens_api_name + ";");
                                     sayln "   }";
                                 };
                    esac;

                    sayln " { ";
                    sayln ("package " + parser_data_pkg_name + "{");
                    sayln "package header { ";
                    sayln header;
                    sayln "};";
                    sayln "package lr_table = token::lr_table;";
                    sayln "package token = token;";
                    sayln "stipulate include package   lr_table; herein ";

                    entries
                        :=
                        print_package::make_package {
                            table,
                            print => pr,
                            name  => "table",
                            verbose
                        };

                    sayln "end;";

                    print_types (values, names, symbol_type);
                    print_error_recovery (keyword, change, noshift, value, values, names);
                    print_actions (rules, values, names, term_hash, symbol_hash );

                    sayln "};";

                    print_tokens_pkg (values, names);

                    sayln "};";

                    print_apis (values, names, \\ s = fil::write (apis, s));    

                    fil::close_output  apis;
                    fil::close_output  result;

                    make_table::errs::print_summary
                        (\\ s = fil::write (fil::stdout, s))
                        errs;
                };

                if verbose
                    #
                    f =  fil::open_for_write  (spec + ".desc");

                    fun say s
                        =
                        fil::write (f, s);

                    # print_rule:
                    #
                    stipulate
                        #
                        rules =  rw_vector::from_list  grammar_rules;
                        #
                    herein

                        fun print_rule  say
                            =
                            (\\ i =  print_rule' rules[ i ])
                            where 

                                fun print_rule' { lhs, rhs, precedence, rulenum }
                                    =
                                    {   (say o nonterm_to_string) lhs;

                                        say " : ";

                                        apply
                                            (\\ s =  { say (symbol_to_string s); say " ";})
                                            rhs;
                                    };
                            end;
                    end;

                    verbose::print_verbose
                      {
                        term_to_string,
                        nonterm_to_string,
                        table,
                        state_errors,
                        errs,
                        entries    => *entries,
                        print      => say,
                        print_cores => core_print,
                        print_rule
                      };

                    fil::close_output  f;
                fi;                             # if verbose
            };                                  # fun make_parser

        fun parse_fn spec
            =
            {   (parse_gen_parser::parse  spec)
                    ->
                    (result, input_source);

                make_parser (
                    get_result result,
                    spec,
                    header::error input_source,
                    error_occurred input_source
                );
            };
    };
end;


Comments and suggestions to: bugs@mythryl.org

PreviousUpNext