diff --git a/executable_semantics/syntax/BUILD b/executable_semantics/syntax/BUILD index e0f654c0eac5..c14e1cdaa059 100644 --- a/executable_semantics/syntax/BUILD +++ b/executable_semantics/syntax/BUILD @@ -12,6 +12,16 @@ cc_library( deps = ["//common:check"], ) +cc_test( + name = "parse_test", + srcs = ["parse_test.cpp"], + deps = [ + ":syntax", + "//executable_semantics/common:arena", + "@com_google_googletest//:gtest_main", + ], +) + cc_library( name = "syntax", srcs = [ diff --git a/executable_semantics/syntax/parse.cpp b/executable_semantics/syntax/parse.cpp index 6d6a4a8e67de..dfc451d1d4df 100644 --- a/executable_semantics/syntax/parse.cpp +++ b/executable_semantics/syntax/parse.cpp @@ -12,19 +12,9 @@ namespace Carbon { -auto Parse(Nonnull arena, const std::string& input_file_name, - bool trace) -> std::variant { - FILE* input_file = fopen(input_file_name.c_str(), "r"); - if (input_file == nullptr) { - FATAL_PROGRAM_ERROR_NO_LINE() << "Error opening '" << input_file_name - << "': " << std::strerror(errno); - } - - // Prepare the lexer. - yyscan_t scanner; - yylex_init(&scanner); - yyset_in(input_file, scanner); - +auto ParseImpl(yyscan_t scanner, Nonnull arena, + std::string_view input_file_name, bool trace) + -> std::variant { // Prepare other parser arguments. std::optional ast = std::nullopt; ParseAndLexContext context(arena->New(input_file_name), trace); @@ -36,10 +26,6 @@ auto Parse(Nonnull arena, const std::string& input_file_name, } auto syntax_error_code = parser(); - // Clean up the lexer. - fclose(input_file); - yylex_destroy(scanner); - // Return an error if appropriate. if (syntax_error_code != 0) { return syntax_error_code; @@ -51,4 +37,48 @@ auto Parse(Nonnull arena, const std::string& input_file_name, return *ast; } +auto Parse(Nonnull arena, std::string_view input_file_name, bool trace) + -> std::variant { + FILE* input_file = fopen(std::string(input_file_name).c_str(), "r"); + if (input_file == nullptr) { + FATAL_PROGRAM_ERROR_NO_LINE() << "Error opening '" << input_file_name + << "': " << std::strerror(errno); + } + + // Prepare the lexer. + yyscan_t scanner; + yylex_init(&scanner); + auto buffer = yy_create_buffer(input_file, YY_BUF_SIZE, scanner); + yy_switch_to_buffer(buffer, scanner); + + std::variant result = + ParseImpl(scanner, arena, input_file_name, trace); + + // Clean up the lexer. + yy_delete_buffer(buffer, scanner); + yylex_destroy(scanner); + fclose(input_file); + + return result; +} + +auto ParseFromString(Nonnull arena, std::string_view input_file_name, + std::string_view file_contents, bool trace) + -> std::variant { + // Prepare the lexer. + yyscan_t scanner; + yylex_init(&scanner); + auto buffer = + yy_scan_bytes(file_contents.data(), file_contents.size(), scanner); + yy_switch_to_buffer(buffer, scanner); + + std::variant result = + ParseImpl(scanner, arena, input_file_name, trace); + + // Clean up the lexer. + yy_delete_buffer(buffer, scanner); + yylex_destroy(scanner); + + return result; +} } // namespace Carbon diff --git a/executable_semantics/syntax/parse.h b/executable_semantics/syntax/parse.h index 4669f6043859..9038fcabe02c 100644 --- a/executable_semantics/syntax/parse.h +++ b/executable_semantics/syntax/parse.h @@ -18,8 +18,15 @@ using SyntaxErrorCode = int; // Returns the AST representing the contents of the named file, or an error code // if parsing fails. Allocations go into the provided arena. -auto Parse(Nonnull arena, const std::string& input_file_name, - bool trace) -> std::variant; +auto Parse(Nonnull arena, std::string_view input_file_name, bool trace) + -> std::variant; + +// Equivalent to `Parse`, but parses the contents of `file_contents`. +// `input_file_name` is used only for reporting source locations, and does +// not need to name a real file. +auto ParseFromString(Nonnull arena, std::string_view input_file_name, + std::string_view file_contents, bool trace) + -> std::variant; } // namespace Carbon diff --git a/executable_semantics/syntax/parse_test.cpp b/executable_semantics/syntax/parse_test.cpp new file mode 100644 index 000000000000..73b12fdaae9e --- /dev/null +++ b/executable_semantics/syntax/parse_test.cpp @@ -0,0 +1,33 @@ +// 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 + +#include +#include + +#include "executable_semantics/common/arena.h" + +namespace Carbon { +namespace { + +static constexpr std::string_view FileContents = R"( +package ExecutableSemanticsTest api; + +fn Foo() {} +)"; + +TEST(ParseTest, ParseFromString) { + Arena arena; + std::variant parse_result = + ParseFromString(&arena, "file.carbon", FileContents, /*trace=*/false); + ASSERT_TRUE(std::holds_alternative(parse_result)); + EXPECT_EQ(std::get(parse_result).declarations.size(), 1); +} + +} // namespace +} // namespace Carbon