diff --git a/executable_semantics/ast/BUILD b/executable_semantics/ast/BUILD index 0e3d0644e964..5e6c53f75528 100644 --- a/executable_semantics/ast/BUILD +++ b/executable_semantics/ast/BUILD @@ -9,7 +9,10 @@ package(default_visibility = ["//executable_semantics:__subpackages__"]) cc_library( name = "declaration", srcs = ["declaration.cpp"], - hdrs = ["declaration.h"], + hdrs = [ + "abstract_syntax_tree.h", + "declaration.h", + ], deps = [ ":function_definition", ":member", diff --git a/executable_semantics/ast/abstract_syntax_tree.h b/executable_semantics/ast/abstract_syntax_tree.h new file mode 100644 index 000000000000..009684cb9d23 --- /dev/null +++ b/executable_semantics/ast/abstract_syntax_tree.h @@ -0,0 +1,16 @@ +// Part of the Carbon Language project, under the Apache License v2.0 with LLVM +// Exceptions. See /LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#ifndef EXECUTABLE_SEMANTICS_AST_ABSTRACT_SYNTAX_TREE_H_ +#define EXECUTABLE_SEMANTICS_AST_ABSTRACT_SYNTAX_TREE_H_ + +#include + +#include "executable_semantics/ast/declaration.h" + +namespace Carbon { +using AST = std::list*; +} + +#endif // EXECUTABLE_SEMANTICS_AST_ABSTRACT_SYNTAX_TREE_H_ diff --git a/executable_semantics/main.cpp b/executable_semantics/main.cpp index 759844d09e1f..a0e3e4038c06 100644 --- a/executable_semantics/main.cpp +++ b/executable_semantics/main.cpp @@ -6,50 +6,33 @@ #include #include -#include "executable_semantics/syntax/parser.h" +#include "executable_semantics/syntax/parse.h" #include "executable_semantics/syntax/syntax_helpers.h" #include "executable_semantics/tracing_flag.h" #include "llvm/Support/CommandLine.h" -extern FILE* yyin; - int main(int argc, char* argv[]) { // yydebug = 1; using llvm::cl::desc; using llvm::cl::opt; - opt quiet_option("quiet", desc("Disable tracing")); - opt input_filename(llvm::cl::Positional, desc("")); - llvm::cl::ParseCommandLineOptions(argc, argv); + opt quietOption("quiet", desc("Disable tracing")); + opt inputFileName(llvm::cl::Positional, desc(""), + llvm::cl::Required); - if (input_filename.getNumOccurrences() > 0) { - Carbon::input_filename = input_filename.c_str(); - yyin = fopen(input_filename.c_str(), "r"); - if (yyin == nullptr) { - std::cerr << "Error opening '" << input_filename - << "': " << strerror(errno) << std::endl; - return 1; - } - } - if (quiet_option) { + llvm::cl::ParseCommandLineOptions(argc, argv); + if (quietOption) { Carbon::tracing_output = false; } - // No AST yet - std::optional parsedInput = std::nullopt; + std::variant astOrError = + Carbon::parse(inputFileName); - // Parse and handle syntax errors - auto syntaxErrorCode = yyparse(parsedInput); - if (syntaxErrorCode != 0) { - return syntaxErrorCode; - } - - if (parsedInput == std::nullopt) { - std::cerr << "Internal error: parser validated syntax yet didn't produce " - "an AST.\n"; - return 1; + if (auto* error = std::get_if(&astOrError)) { + // Diagnostic already reported to std::cerr; this is just a return code. + return *error; } // Typecheck and run the parsed program. - Carbon::ExecProgram(*parsedInput); + Carbon::ExecProgram(std::get(astOrError)); } diff --git a/executable_semantics/syntax/BUILD b/executable_semantics/syntax/BUILD index 60b4e3589389..4fd6560ed823 100644 --- a/executable_semantics/syntax/BUILD +++ b/executable_semantics/syntax/BUILD @@ -13,11 +13,17 @@ cc_library( name = "syntax", srcs = [ "lexer.cpp", + "parse.cpp", + "parse_and_lex_context.cpp", + "parse_and_lex_context.h", "parser.cpp", + "parser.h", "syntax_helpers.cpp", "syntax_helpers.h", ], - hdrs = ["parser.h"], + hdrs = [ + "parse.h", + ], # Disable warnings for generated code. copts = [ "-Wno-unneeded-internal-declaration", diff --git a/executable_semantics/syntax/lexer.lpp b/executable_semantics/syntax/lexer.lpp index 06102b2c510e..50f62f9e7b75 100644 --- a/executable_semantics/syntax/lexer.lpp +++ b/executable_semantics/syntax/lexer.lpp @@ -6,7 +6,7 @@ SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception %{ #include -#include "executable_semantics/syntax/parser.h" +#include "executable_semantics/syntax/parse_and_lex_context.h" %} /* Turn off legacy bits we don't need */ diff --git a/executable_semantics/syntax/parse.cpp b/executable_semantics/syntax/parse.cpp new file mode 100644 index 000000000000..6bdd9e4a4540 --- /dev/null +++ b/executable_semantics/syntax/parse.cpp @@ -0,0 +1,44 @@ +// Part of the Carbon Language project, under the Apache License v2.0 with LLVM +// Exceptions. See /LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include "executable_semantics/syntax/parse.h" + +#include + +#include "executable_semantics/syntax/parse_and_lex_context.h" +#include "executable_semantics/tracing_flag.h" + +extern FILE* yyin; + +namespace Carbon { + +// Returns an abstract representation of the program contained in the +// well-formed input file, or if the file was malformed, a description of the +// problem. +auto parse(const std::string& inputFileName) + -> std::variant { + yyin = fopen(inputFileName.c_str(), "r"); + if (yyin == nullptr) { + std::cerr << "Error opening '" << inputFileName + << "': " << std::strerror(errno) << std::endl; + exit(1); + } + + std::optional parsedInput = std::nullopt; + ParseAndLexContext context(inputFileName); + + auto syntaxErrorCode = yyparse(parsedInput, context); + if (syntaxErrorCode != 0) { + return syntaxErrorCode; + } + + if (parsedInput == std::nullopt) { + std::cerr << "Internal error: parser validated syntax yet didn't produce " + "an AST.\n"; + exit(1); + } + return *parsedInput; +} + +} // namespace Carbon diff --git a/executable_semantics/syntax/parse.h b/executable_semantics/syntax/parse.h new file mode 100644 index 000000000000..592b70b1bd87 --- /dev/null +++ b/executable_semantics/syntax/parse.h @@ -0,0 +1,25 @@ +// Part of the Carbon Language project, under the Apache License v2.0 with LLVM +// Exceptions. See /LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#ifndef EXECUTABLE_SEMANTICS_SYNTAX_PARSE_H_ +#define EXECUTABLE_SEMANTICS_SYNTAX_PARSE_H_ + +#include +#include + +#include "executable_semantics/ast/abstract_syntax_tree.h" + +namespace Carbon { + +// This is the code given us by Bison, for now. +using SyntaxErrorCode = int; + +// Returns the AST representing the contents of the named file, or an error code +// if parsing fails. +auto parse(const std::string& inputFileName) + -> std::variant; + +} // namespace Carbon + +#endif // EXECUTABLE_SEMANTICS_SYNTAX_PARSE_H_ diff --git a/executable_semantics/syntax/parse_and_lex_context.cpp b/executable_semantics/syntax/parse_and_lex_context.cpp new file mode 100644 index 000000000000..eb39b12a5780 --- /dev/null +++ b/executable_semantics/syntax/parse_and_lex_context.cpp @@ -0,0 +1,20 @@ +// Part of the Carbon Language project, under the Apache License v2.0 with LLVM +// Exceptions. See /LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include "executable_semantics/syntax/parse_and_lex_context.h" + +#include +#include + +#include "executable_semantics/tracing_flag.h" + +// Writes a syntax error diagnostic, containing message, for the input file at +// the given line, to standard error. +auto Carbon::ParseAndLexContext::PrintDiagnostic(const char* message, + int line_num) -> void { + std::cerr << inputFileName << ":" << line_num << ": " << message << std::endl; + exit(-1); // TODO: do we really want this here? It makes the comment and the + // name a lie, and renders some of the other yyparse() result + // propagation code moot. +} diff --git a/executable_semantics/syntax/parse_and_lex_context.h b/executable_semantics/syntax/parse_and_lex_context.h new file mode 100644 index 000000000000..ec81c3f025a4 --- /dev/null +++ b/executable_semantics/syntax/parse_and_lex_context.h @@ -0,0 +1,40 @@ +// Part of the Carbon Language project, under the Apache License v2.0 with LLVM +// Exceptions. See /LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#ifndef EXECUTABLE_SEMANTICS_SYNTAX_DRIVER_H_ +#define EXECUTABLE_SEMANTICS_SYNTAX_DRIVER_H_ + +#include + +#include "executable_semantics/ast/abstract_syntax_tree.h" +#include "executable_semantics/syntax/parser.h" // from parser.ypp + +namespace Carbon { + +// The state and functionality that is threaded "globally" through the +// lexing/parsing process. +class ParseAndLexContext { + public: + // Creates an instance analyzing the given input file. + ParseAndLexContext(const std::string& inputFile) : inputFileName(inputFile) {} + + // Writes a syntax error diagnostic, containing message, for the input file at + // the given line, to standard error. + auto PrintDiagnostic(const char* message, int lineNumber) -> void; + + private: + // A path to the file processed, relative to the current working directory + // when *this is called. + const std::string inputFileName; +}; + +} // namespace Carbon + +// Gives flex the yylex prototype we want. +#define YY_DECL int yylex(Carbon::ParseAndLexContext& context) + +// Declares yylex for the parser's sake. +YY_DECL; + +#endif // EXECUTABLE_SYNTAX_DRIVER_H_ diff --git a/executable_semantics/syntax/parser.ypp b/executable_semantics/syntax/parser.ypp index 6c5d4efa6b0f..6c83b92bb977 100644 --- a/executable_semantics/syntax/parser.ypp +++ b/executable_semantics/syntax/parser.ypp @@ -11,33 +11,46 @@ #include #include "executable_semantics/syntax/syntax_helpers.h" +#include "executable_semantics/syntax/parse_and_lex_context.h" } %code requires { #include +#include "executable_semantics/ast/abstract_syntax_tree.h" #include "executable_semantics/ast/declaration.h" #include "executable_semantics/ast/field_list.h" #include "executable_semantics/ast/function_definition.h" namespace Carbon { -// A representation of the parsed program. -using AST = std::list*; +class ParseAndLexContext; } } %code { + extern int yylineno; -extern int yylex(); -void yyerror(std::optional const& parsedProgram, char* error) { - Carbon::PrintSyntaxError(error, yylineno); +void yyerror( + std::optional const& /* parsedProgram */, + Carbon::ParseAndLexContext& context, + char* message +) { + context.PrintDiagnostic(message, yylineno); } } +// +// Parameters to yyparse and yylex. +// + +// "out" parameter passed to the parser, where the AST is written. %parse-param {std::optional& parsedProgram} +// "inout" parameter passed to both the parser and the lexer. +%param {Carbon::ParseAndLexContext& context} + %union { char* str; int num; diff --git a/executable_semantics/syntax/syntax_helpers.cpp b/executable_semantics/syntax/syntax_helpers.cpp index 3279f6ab56b6..eb9600600e0d 100644 --- a/executable_semantics/syntax/syntax_helpers.cpp +++ b/executable_semantics/syntax/syntax_helpers.cpp @@ -12,13 +12,6 @@ namespace Carbon { -const char* input_filename = nullptr; - -void PrintSyntaxError(char* error, int line_num) { - std::cerr << input_filename << ":" << line_num << ": " << error << std::endl; - exit(-1); -} - void ExecProgram(std::list* fs) { if (tracing_output) { std::cout << "********** source program **********" << std::endl; diff --git a/executable_semantics/syntax/syntax_helpers.h b/executable_semantics/syntax/syntax_helpers.h index 531b85912181..c3916684fc2e 100644 --- a/executable_semantics/syntax/syntax_helpers.h +++ b/executable_semantics/syntax/syntax_helpers.h @@ -15,12 +15,6 @@ namespace Carbon { -// Initialized by main(), and used when printing errors. -extern const char* input_filename; - -// Prints errors. -void PrintSyntaxError(char* error, int line_num); - // Runs the top-level declaration list. void ExecProgram(std::list* fs);