blob: a3131b05c618c9af5cc805fa2a73dc0e37e50221 [file] [log] [blame]
/* -*- C++ -*-
Copyright (C) 2020-2021 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 <https://www.gnu.org/licenses/>.
*/
/* Simplified C++ Type and Expression Grammar.
Written by Paul Hilfinger for Bison's test suite. */
%require "3.8"
%glr-parser
%skeleton "glr2.cc"
%define parse.assert
%define api.token.constructor
%header
%locations
%debug
// Custom error messages.
%define parse.error custom
%code requires
{
#include "ast.hh"
}
%define api.value.type variant
%code
{
#include <cassert>
#include <cctype>
#include <fstream>
#include <cstring>
// Merge two semantic values.
static Node
stmt_merge (const Node& x0, const Node& x1);
// Fetch a token.
static yy::parser::symbol_type
yylex ();
}
%expect-rr 1
%type <Node> TYPENAME ID stmt expr decl declarator
%printer { yyo << $$; } <Node>
%token
TYPENAME "typename"
ID "identifier"
SEMICOLON ";"
EQUAL "="
PLUS "+"
LPAREN "("
RPAREN ")"
%right "="
%left "+"
%%
prog : %empty
| prog stmt { std::cout << @2 << ": " << $2 << '\n'; }
;
stmt : expr ";" %merge <stmt_merge> { $$ = $1; }
| decl %merge <stmt_merge>
| error ";" { $$ = Nterm ("<error>"); }
;
expr : ID
| TYPENAME "(" expr ")" { $$ = Nterm ("<cast>", $3, $1); }
| expr "+" expr { $$ = Nterm ("+", $1, $3); }
| expr "=" expr { $$ = Nterm ("=", $1, $3); }
;
decl : TYPENAME declarator ";"
{ $$ = Nterm ("<declare>", $1, $2); }
| TYPENAME declarator "=" expr ";"
{ $$ = Nterm ("<init-declare>", $1, $2, $4); }
;
declarator
: ID
| "(" declarator ")" { $$ = $2; }
;
%%
std::istream* input = nullptr;
yy::parser::location_type loc;
/*---------.
| Parser. |
`---------*/
// Generate a custom error message.
void
yy::parser::report_syntax_error (const context& ctx) const
{
std::cerr << ctx.location () << ": syntax error";
if (!ctx.lookahead ().empty ())
std::cerr << " on token " << ctx.lookahead ().name ();
{
enum { TOKENMAX = 10 };
symbol_kind_type expected[TOKENMAX];
int n = ctx.expected_tokens (expected, TOKENMAX);
if (0 < n)
{
for (int i = 0; i < n; ++i)
std::cerr << (i == 0 ? " (expected " : " or ")
<< symbol_name (expected[i]);
std::cerr << ')';
}
}
std::cerr << '\n';
}
// Report the error to the user.
void
yy::parser::error (const location_type& l, const std::string& m)
{
std::cerr << l << ": " << m << '\n';
}
// Fetch the next token.
static yy::parser::symbol_type
yylex ()
{
while (true)
{
loc.step ();
loc += 1;
assert (!input->eof ());
switch (int c = input->get ())
{
case EOF:
return yy::parser::make_YYEOF (loc);
case '\t':
loc.end.column = (loc.end.column + 7) & ~7;
loc.step ();
break;
case ' ': case '\f':
loc.step ();
break;
case '\n':
loc.lines (1);
loc.end.column = 0;
loc.step ();
break;
case '+':
return yy::parser::make_PLUS (loc);
case '=':
return yy::parser::make_EQUAL (loc);
case '(':
return yy::parser::make_LPAREN (loc);
case ')':
return yy::parser::make_RPAREN (loc);
case ';':
return yy::parser::make_SEMICOLON (loc);
default:
if (isalpha (c))
{
std::string form;
do
{
form += static_cast<char> (c);
loc += 1;
c = input->get ();
}
while (isalnum (c) || c == '_');
input->unget ();
loc -= 1;
if (isupper (static_cast <unsigned char> (form[0])))
return yy::parser::make_TYPENAME (Term (form), loc);
else
return yy::parser::make_ID (Term (form), loc);
}
else
{
auto msg = "invalid character: " + std::string(1, static_cast<char> (c));
throw yy::parser::syntax_error (loc, msg);
}
}
}
}
// Merge two semantic values as an AST including both alternatives.
static Node
stmt_merge (const Node& x0, const Node& x1)
{
return Nterm ("<OR>", x0, x1);
}
/*-------.
| Main. |
`-------*/
// Parse `file` using parser `parse`.
int
process (yy::parser& parse, const std::string& file)
{
bool is_stdin = file == "-" || file.empty ();
if (is_stdin)
input = &std::cin;
else
input = new std::ifstream (file.c_str ());
loc.initialize (nullptr, 1, 0);
int status = parse ();
if (!is_stdin)
delete input;
return status;
}
int
main (int argc, char **argv)
{
yy::parser parse;
if (getenv ("YYDEBUG"))
parse.set_debug_level (1);
bool ran = false;
for (int i = 1; i < argc; ++i)
// Enable parse traces on option -p.
if (strcmp (argv[i], "-p") == 0)
parse.set_debug_level (1);
else
{
int status = process (parse, argv[i]);
ran = true;
if (!status)
return status;
}
if (!ran)
{
int status = process (parse, "");
if (!status)
return status;
}
return 0;
}