From 967be9e750221ab2ab783f95df79bb26d290a45e Mon Sep 17 00:00:00 2001 From: Martial Simon Date: Mon, 15 Sep 2025 01:07:58 +0200 Subject: add: added projects --- tiger-compiler/src/parse/fwd.hh | 38 ++++ tiger-compiler/src/parse/generate-prelude.sh | 31 +++ tiger-compiler/src/parse/libparse.cc | 137 ++++++++++++++ tiger-compiler/src/parse/libparse.hh | 76 ++++++++ tiger-compiler/src/parse/local.am | 125 ++++++++++++ tiger-compiler/src/parse/metavar-map.hh | 48 +++++ tiger-compiler/src/parse/metavar-map.hxx | 69 +++++++ tiger-compiler/src/parse/tasks.cc | 53 ++++++ tiger-compiler/src/parse/tasks.hh | 51 +++++ tiger-compiler/src/parse/tiger-driver.cc | 215 +++++++++++++++++++++ tiger-compiler/src/parse/tiger-driver.hh | 110 +++++++++++ tiger-compiler/src/parse/tiger-factory.hh | 144 ++++++++++++++ tiger-compiler/src/parse/tiger-factory.hxx | 272 +++++++++++++++++++++++++++ tiger-compiler/src/parse/tweast.cc | 87 +++++++++ tiger-compiler/src/parse/tweast.hh | 79 ++++++++ tiger-compiler/src/parse/tweast.hxx | 75 ++++++++ 16 files changed, 1610 insertions(+) create mode 100644 tiger-compiler/src/parse/fwd.hh create mode 100755 tiger-compiler/src/parse/generate-prelude.sh create mode 100644 tiger-compiler/src/parse/libparse.cc create mode 100644 tiger-compiler/src/parse/libparse.hh create mode 100644 tiger-compiler/src/parse/local.am create mode 100644 tiger-compiler/src/parse/metavar-map.hh create mode 100644 tiger-compiler/src/parse/metavar-map.hxx create mode 100644 tiger-compiler/src/parse/tasks.cc create mode 100644 tiger-compiler/src/parse/tasks.hh create mode 100644 tiger-compiler/src/parse/tiger-driver.cc create mode 100644 tiger-compiler/src/parse/tiger-driver.hh create mode 100644 tiger-compiler/src/parse/tiger-factory.hh create mode 100644 tiger-compiler/src/parse/tiger-factory.hxx create mode 100644 tiger-compiler/src/parse/tweast.cc create mode 100644 tiger-compiler/src/parse/tweast.hh create mode 100644 tiger-compiler/src/parse/tweast.hxx (limited to 'tiger-compiler/src/parse') diff --git a/tiger-compiler/src/parse/fwd.hh b/tiger-compiler/src/parse/fwd.hh new file mode 100644 index 0000000..19ed898 --- /dev/null +++ b/tiger-compiler/src/parse/fwd.hh @@ -0,0 +1,38 @@ +/** + ** \file parse/fwd.hh + ** \brief Forward declarations for the parse module. + */ + +#pragma once + +#include + +// From ast/. +namespace ast +{ + class Exp; + class ChunkList; + +} // namespace ast + +namespace parse +{ + // From scantiger.hh. + class Lexer; + + // From parsetiger.yy. + class parser; + + // From location.hh. + class location; + + // From tiger-driver.hh. + class TigerDriver; + + // From tweast.hh + class Tweast; + + /// Result of a parse: an Exp (*.tig) or a ChunkList (*.tih). + using ast_type = misc::variant; + +} // namespace parse diff --git a/tiger-compiler/src/parse/generate-prelude.sh b/tiger-compiler/src/parse/generate-prelude.sh new file mode 100755 index 0000000..d81d28b --- /dev/null +++ b/tiger-compiler/src/parse/generate-prelude.sh @@ -0,0 +1,31 @@ +#!/bin/sh + +[ -z "$1" ] && echo "$0: Missing input file" && exit +[ -z "$2" ] && echo "$0: Missing output file" && exit + +input=$1 +output=$2 + +awk 'BEGIN { + print("#include \"parse/tiger-driver.hh\""); + print(""); + print("namespace parse"); + print("{"); + print(" const char* TigerDriver::prelude () const"); + print(" {"); + print(" return"); + printf("R\"("); + } + /^#(<<|>>)/ { + next; + } + { + print($0); + } + END { + print(")\";"); + print(" }"); + print("} // namespace parse"); + }' "$input" > "$output".tmp + +mv "$output".tmp "$output" diff --git a/tiger-compiler/src/parse/libparse.cc b/tiger-compiler/src/parse/libparse.cc new file mode 100644 index 0000000..7c963b6 --- /dev/null +++ b/tiger-compiler/src/parse/libparse.cc @@ -0,0 +1,137 @@ +/** + ** \file parse/libparse.cc + ** \brief Functions and variables exported by the parse module. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// Define exported parse functions. +namespace parse +{ + ast::ChunkList* parse_prelude() + { + if (tasks::prelude.empty()) + { + return nullptr; + } + + TigerDriver td; + + return std::get(td.parse(td.prelude())); + } + + // Parse a Tiger file, return the corresponding abstract syntax. + std::pair parse(const std::string& prelude, + const std::string& fname, + misc::file_library& library, + bool scan_trace_p, + bool parse_trace_p, + bool enable_object_extensions_p, + bool enable_assert_extensions_p) + { + // Current directory must be that of the file currently processed. + library.push_current_directory(misc::path(fname).parent_path()); + + TigerDriver td(library); + td.scan_trace(scan_trace_p).parse_trace(parse_trace_p); + td.enable_object_extensions(enable_object_extensions_p); + td.enable_assert_extensions(enable_assert_extensions_p); + + ast::ChunkList* res = nullptr; + + ast_type tree = td.parse_file(fname); + + ast::Exp** exp = std::get_if(&tree); + ast::ChunkList** chunks = std::get_if(&tree); + + // Try to parse the program as an expression, and check that the + // parsing did not fail in that case. + if (exp && *exp) + { + Tweast in; + + if (!prelude.empty()) + { + ast::ChunkList* prelude_chunks = + (prelude == "builtin" + ? std::get(td.parse(td.prelude())) + : td.parse_import(prelude, location())); + if (prelude_chunks) + in << prelude_chunks; + } + in << "function _main() = (" << *exp << "; ())"; + res = td.parse(in); + } + // Try to parse the program as a list of declarations, and check + // that the parsing did not fail in that case. + else if (chunks && *chunks) + { + // Try to parse the program as a list of declarations. + res = *chunks; + } + // Otherwise, the parsing failed, and an error will be returned as + // second member of the return value. + + // Ensure that directory stack is not modified by parse. + library.pop_current_directory(); + + return std::pair(res, td.error_get()); + } + + ast_type parse(Tweast& input) + { + TigerDriver td; + td.enable_extensions(); + td.enable_object_extensions(); + ast_type res = td.parse(input); + if (td.error_get()) + { + misc::error e; + e << "Failed to resolve Tweast:" << misc::incendl << input; + e << "Got: " << td.error_get(); + e.ice_here(); + } + return res; + } + + ast::Exp* parse(const std::string& str, bool enable_object_extensions_p) + { + TigerDriver td; + td.enable_object_extensions(enable_object_extensions_p); + ast::Exp* res = td.parse(str); + td.error_get().ice_on_error_here(); + return res; + } + + ast::ChunkList* parse_unit(const std::string& str, + bool enable_object_extensions_p) + { + TigerDriver td; + td.enable_object_extensions(enable_object_extensions_p); + std::string rewrite = "function _main() = (" + str + "; ())"; + ast::ChunkList* res = td.parse(rewrite); + td.error_get().ice_on_error_here(); + return res; + } + + // Parse a set of declarations. + ast::ChunkInterface* parse_chunks(Tweast& in) + { + ast::ChunkList* dl = parse(in); + assertion(dl->chunks_get().size() == 1); + ast::ChunkInterface* res = dl->chunks_get().front(); + dl->chunks_get().pop_front(); + delete dl; + return res; + } + +} // namespace parse diff --git a/tiger-compiler/src/parse/libparse.hh b/tiger-compiler/src/parse/libparse.hh new file mode 100644 index 0000000..7781bb6 --- /dev/null +++ b/tiger-compiler/src/parse/libparse.hh @@ -0,0 +1,76 @@ +/** + ** \file parse/libparse.hh + ** \brief Declare functions and variables exported by parse module. + */ + +#pragma once + +#include +#include +#include + +#include +#include +#include +#include + +/// Parsing the input, delivering an ast::Ast. +namespace parse +{ + /// \brief Parse a Tiger file, return the corresponding abstract syntax tree. + /// + /// \param prelude name of the prelude file. + /// \param fname path and name of the tiger file. + /// \param library library for managing search path. + /// \param scan_trace_p display information on scan step. + /// \param parse_trace_p display information on parse step. + /// \param enable_object_extensions_p enable object constructions + /// + /// \return a pair composed of a pointer to an abstract parse tree + /// (set to `nullptr' upon failure) and an error status. + /// This function is the only interface available between + /// the scanner/parser and the rest of the program. + +#ifdef SWIG + %newobject parse; +#endif + + /** + * \brief Parse the default prelude and return it. + * Return null if no-prelude is set. + * \return an AST representing the EPITA Tiger default prelude + */ + ast::ChunkList* parse_prelude(); + + std::pair + parse(const std::string& prelude, + const std::string& fname, + misc::file_library& library, + bool scan_trace_p, + bool parse_trace_p, + bool enable_object_extensions_p = false, + bool enable_assert_extensions_p = false); + + /// \brief Parse a Tweast. + /// + /// Extensions are enabled. Raises an exception on errors. + ast_type parse(Tweast& input); + + /// Parse a std::string. Used for unit tests. + ast::Exp* parse(const std::string& str, + bool enable_object_extensions_p = false); + + /// Parse a std::string. Used for unit tests. + /// The declaration of the _main function is automatically added. + ast::ChunkList* parse_unit(const std::string& str, + bool enable_object_extensions_p = false); + + /// \brief Parse a set of declarations. + /// + /// Wrapper around parse::parse to return the single ast::ChunkInterface + /// to be found in the input (expected to contain ChunkList). + /// + /// Used by desugar::BoundsCheckingVisitor and object::ObjectDesugarVisitor. + ast::ChunkInterface* parse_chunks(Tweast& in); + +} // namespace parse diff --git a/tiger-compiler/src/parse/local.am b/tiger-compiler/src/parse/local.am new file mode 100644 index 0000000..1d80d2a --- /dev/null +++ b/tiger-compiler/src/parse/local.am @@ -0,0 +1,125 @@ +## -------------------- ## +## Scanner generation. ## +## -------------------- ## + +# Definition of flags used by reflex +REFLEX_HEADER = %D%/scantiger.hh +REFLEX_FLAGS = -d --flex --header-file=$(REFLEX_HEADER) +SOURCES_REFLEX = $(REFLEX_HEADER) %D%/scantiger.cc + +MAINTAINERCLEANFILES += %D%/scantiger.cc $(REFLEX_HEADER) +EXTRA_DIST += %D%/scantiger.ll + +# Rule to generate all files with reflex +%D%/scantiger.cc $(REFLEX_HEADER) : %D%/scantiger.ll + $(AM_V_GEN)mkdir -p $(@D) + $(AM_V_at)rm -f $@ +# Generate scantiger.cc and scantiger.hh + $(AM_V_at) $(REFLEX) $< -o %D%/scantiger.cc $(REFLEX_FLAGS) + +## ------------------- ## +## Parser generation. ## +## ------------------- ## +BISONXX = $(top_builddir)/build-aux/bin/bison++ +BISONXX_IN = $(top_srcdir)/build-aux/bin/bison++.in +BISONXXFLAGS = \ + $(if $(V:0=),--verbose) +AM_BISONFLAGS = \ + --warnings=all,dangling-alias \ + --report=all + +## Use this additional bison flag to display counterexamples in the tty. +#AM_BISONFLAGS += --warnings=counterexamples + +# We do not use Automake features here. +SOURCES_PARSETIGER_YY = \ + %D%/location.hh \ + %D%/parsetiger.cc \ + %D%/parsetiger.hh +BUILT_SOURCES += $(SOURCES_PARSETIGER_YY) + +# Ship %D%/stack.hh only if GLR is disabled, as Bison does not +# generate this file for GLR parsers. +dist-hook: dist-hook-parse +dist-hook-parse: + @grep '%glr-parser' $(srcdir)/%D%/parsetiger.yy >/dev/null \ + || cp -p $(srcdir)/%D%/stack.hh $(distdir)/src/parse/ + +# Compile the parser and save cycles. +# This code comes from "Handling Tools that Produce Many Outputs", +# from the Automake documentation. +EXTRA_DIST += \ + %D%/parsetiger.stamp \ + %D%/parsetiger.yy +# The dependency is on bison++.in and not bison++, since bison++ is +# regenerated at distribution time, and voids the time stamps (which +# we don't want!). +%D%/parsetiger.stamp: %D%/parsetiger.yy $(BISONXX_IN) + $(AM_V_GEN)mkdir -p $(@D) + $(AM_V_at)rm -f $@ $@.tmp + $(AM_V_at)echo '$@ rebuilt because of: $?' >$@.tmp + $(AM_V_at)$(MAKE) $(BISONXX) + $(AM_V_at)$(BISONXX) $(BISONXXFLAGS) \ + -r $(srcdir)/src \ + -- \ + $< $(srcdir)/%D%/parsetiger.cc \ + $(AM_BISONFLAGS) $(BISONFLAGS) + $(AM_V_at)mv -f $@.tmp $@ + +## If Make does not know it will generate in the srcdir, then when +## trying to compile from *.cc to *.lo, it will not apply VPATH +## lookup, since it expects the file to be in builddir. So *here*, +## make srcdir explicit. +$(addprefix $(srcdir)/, $(SOURCES_PARSETIGER_YY)): %D%/parsetiger.stamp + $(AM_V_GEN)if test -f $@; then :; else \ + rm -f $<; \ + $(MAKE) $(AM_MAKEFLAGS) $<; \ + fi + +# We tried several times to run make from ast/ to build location.hh. +# Unfortunately, because of different, but equivalent, paths, BSD Make +# was unable to build them. The following hook is here to address this. +.PHONY: generate-parser +generate-parser: $(SOURCES_PARSETIGER_YY) + +PARSE_PRELUDE_GENERATION = %D%/generate-prelude.sh +EXTRA_DIST += $(PARSE_PRELUDE_GENERATION) +CLEANFILES += %D%/prelude.cc +%D%/prelude.cc: $(top_srcdir)/data/prelude.tih + $(AM_V_GEN)$(srcdir)/$(PARSE_PRELUDE_GENERATION) $< $@ + +## ---------- ## +## libparse. ## +## ---------- ## + +src_libtc_la_SOURCES += \ + $(SOURCES_REFLEX) \ + $(SOURCES_PARSETIGER_YY) \ + %D%/fwd.hh \ + %D%/libparse.hh %D%/libparse.cc \ + %D%/metavar-map.hh %D%/metavar-map.hxx \ + %D%/tiger-driver.hh %D%/tiger-driver.cc \ + %D%/tweast.hh %D%/tweast.cc %D%/tweast.hxx +src_libtc_la_SOURCES += \ + %D%/tiger-factory.hh %D%/tiger-factory.hxx + +FORMAT_IGNORE += $REFLEX_HEADER + +nodist_src_libtc_la_SOURCES += \ + %D%/prelude.cc + +## ------- ## +## Tests. ## +## ------- ## + +check_PROGRAMS += \ + %D%/test-parse \ + %D%/test-tweast + +# Find the prelude. +%C%_test_parse_CPPFLAGS = $(AM_CPPFLAGS) -DPKGDATADIR=\"$(pkgdatadir)\" +%C%_test_parse_LDADD = src/libtc.la +%C%_test_tweast_LDADD = src/libtc.la + + +TASKS += %D%/tasks.hh %D%/tasks.cc diff --git a/tiger-compiler/src/parse/metavar-map.hh b/tiger-compiler/src/parse/metavar-map.hh new file mode 100644 index 0000000..113db0f --- /dev/null +++ b/tiger-compiler/src/parse/metavar-map.hh @@ -0,0 +1,48 @@ +/** + ** \file parse/metavar-map.hh + ** \brief Declaration of parse::MetavarMap. + */ + +#pragma once + +#include + +#include + +namespace parse +{ + /// A generic map of metavariables. + template class MetavarMap + { + public: + /// Build a map of metavariables of kind \a name. + MetavarMap(const std::string& name); + virtual ~MetavarMap(); + + /// Generate a (concrete syntax) Tiger statement for metavariable + /// number \a key (of kind \p Data). + std::string show(unsigned key) const; + + /// Print the MetavarMap on \a ostr. + std::ostream& dump(std::ostream& ostr) const; + + protected: + /// Append a metavariable to the map. + virtual std::string append_(unsigned& key, Data* data); + /// Extract a metavariable. + virtual Data* take_(unsigned key); + + /// Name of the kind of variable. + const std::string name_; + /// Metavariables. + using map_type = misc::map; + map_type map_; + }; + + /// Output \a m onto \a ostr. + template + std::ostream& operator<<(std::ostream& ostr, const MetavarMap& m); + +} // namespace parse + +#include diff --git a/tiger-compiler/src/parse/metavar-map.hxx b/tiger-compiler/src/parse/metavar-map.hxx new file mode 100644 index 0000000..7dc2bbc --- /dev/null +++ b/tiger-compiler/src/parse/metavar-map.hxx @@ -0,0 +1,69 @@ +/** + ** \file parse/metavar-map.hxx + ** \brief Implementation of parse::MetavarMap. + */ + +#pragma once + +#include +#include + +#include +#include +#include + +namespace parse +{ + template + MetavarMap::MetavarMap(const std::string& name) + : name_(name) + , map_() + {} + + template MetavarMap::~MetavarMap() + { + // At this point, every metavariable should have been taken from the map. + assertion(map_.empty()) << *this << "not empty, aborting.\n"; + } + + template + std::string MetavarMap::show(unsigned key) const + { + return '_' + name_ + '(' + std::to_string(key) + ')'; + } + + template + std::ostream& MetavarMap::dump(std::ostream& ostr) const + { + ostr << "MetavarMap<" << name_ << "> = ["; + + if (map_.empty()) + return ostr << " ]" << misc::iendl; + + ostr << misc::incindent; + + for (const auto& [k, v] : map_) + ostr << misc::iendl << show(k) << " -> " << v; + + return ostr << misc::decendl << "]" << misc::iendl; + } + + template + std::string MetavarMap::append_(unsigned& count, Data* data) + { + map_[count] = data; + return show(count++); + } + + template Data* MetavarMap::take_(unsigned key) + { + return map_.take(key); + } + + template + std::ostream& operator<<(std::ostream& ostr, const MetavarMap& m) + { + return m.dump(ostr); + } + +} // namespace parse diff --git a/tiger-compiler/src/parse/tasks.cc b/tiger-compiler/src/parse/tasks.cc new file mode 100644 index 0000000..1423696 --- /dev/null +++ b/tiger-compiler/src/parse/tasks.cc @@ -0,0 +1,53 @@ +/** + ** \file parse/tasks.cc + ** \brief Parse module related tasks' implementation. + **/ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#define DEFINE_TASKS 1 +#include +#undef DEFINE_TASKS + +namespace parse::tasks +{ + const char* tc_pkgdatadir = getenv("TC_PKGDATADIR"); + misc::file_library l = + misc::file_library(tc_pkgdatadir ? tc_pkgdatadir : PKGDATADIR); + + void no_prelude() { prelude = ""; } + + void parse() + { + precondition(filename != nullptr); + bool scan_trace = scan_trace_p || getenv("SCAN"); + bool parse_trace = parse_trace_p || getenv("PARSE"); + std::pair result = + ::parse::parse(prelude, filename, l, scan_trace, parse_trace, + object::tasks::enable_object_extensions_p, + assert::tasks::enable_assert_extensions_p); + + // If the parsing completely failed, stop. + task_error() << result.second; + if (!result.first) + task_error().exit(); + + // FIXME DONE: Some code was deleted here (Set `the_program' to the result of parsing). + ast::tasks::the_program = std::unique_ptr{result.first}; + } + + void library_display() { std::cout << l << '\n'; } + + void library_append(const std::string& dir) { l.append_dir(dir); } + + void library_prepend(const std::string& dir) { l.prepend_dir(dir); } + +} // namespace parse::tasks diff --git a/tiger-compiler/src/parse/tasks.hh b/tiger-compiler/src/parse/tasks.hh new file mode 100644 index 0000000..0ff37e1 --- /dev/null +++ b/tiger-compiler/src/parse/tasks.hh @@ -0,0 +1,51 @@ +/** + ** \file parse/tasks.hh + ** \brief Parse module tasks. + */ + +#pragma once + +#include +#include + +/// Tasks of the parse module. +namespace parse::tasks +{ + /// Global library for search path. + extern misc::file_library file_library; + + TASK_GROUP("1. Parsing"); + + /// Enable scanner trace. + BOOLEAN_TASK_DECLARE("scan-trace", "trace the scanning", scan_trace_p, ""); + /// Enable parser trace. + BOOLEAN_TASK_DECLARE("parse-trace", "trace the parse", parse_trace_p, ""); + /// Prelude declarations. + STRING_TASK_DECLARE("prelude", + "builtin", + "name of the prelude. Defaults to \"builtin\" " + "denoting the builtin prelude", + prelude, + ""); + /// Prelude declarations. + TASK_DECLARE("X|no-prelude", "don't include prelude", no_prelude, ""); + /// Parse the input file, store the ast into ast::tasks::the_program. + TASK_DECLARE("parse", "parse a file", parse, ""); + + /// Display library search path. + TASK_DECLARE("library-display", + "display library search path", + library_display, + ""); + /// Append directory DIR to the search path. + MULTIPLE_STRING_TASK_DECLARE("P|library-append", + "append directory DIR to the search path", + library_append, + ""); + /// Prepend directory DIR to the search path. + MULTIPLE_STRING_TASK_DECLARE("p|library-prepend", + "prepend directory DIR to the search path", + library_prepend, + ""); + +} // namespace parse::tasks diff --git a/tiger-compiler/src/parse/tiger-driver.cc b/tiger-compiler/src/parse/tiger-driver.cc new file mode 100644 index 0000000..d26d11d --- /dev/null +++ b/tiger-compiler/src/parse/tiger-driver.cc @@ -0,0 +1,215 @@ +/** + ** \file parse/tiger-driver.cc + ** \brief Implementation of parse::TigerDriver. + */ + +#include +#include + +#include +#include +#include + +namespace parse +{ + TigerDriver::TigerDriver(const misc::file_library& lib) + : library_(lib) + {} + + // Yes, this is exactly the default dtor. But because it is defined + // here, in the *.cc file, instead of "= default" in the *.hh file, + // there is no need for all the members to be complete defined in + // the header. In particular, the scanner_ member acts as a Pimpl, + // so we really want the generation of the dtor to be delayed to + // here, not in the header. + TigerDriver::~TigerDriver() = default; + + /// Set the scanner traces. + TigerDriver& TigerDriver::scan_trace(bool b) + { + scan_trace_p_ = b; + return *this; + } + + /// Set the parser traces. + TigerDriver& TigerDriver::parse_trace(bool b) + { + parse_trace_p_ = b; + return *this; + } + + /// Enable object extensions. + TigerDriver& TigerDriver::enable_object_extensions(bool b) + { + enable_object_extensions_p_ = b; + return *this; + } + + /// Enable assert extensions. + TigerDriver& TigerDriver::enable_assert_extensions(bool b) + { + enable_assert_extensions_p_ = b; + return *this; + } + + /// Enable syntax extensions. + TigerDriver& TigerDriver::enable_extensions(bool b) + { + enable_extensions_p_ = b; + return *this; + } + + /// Parse a Tiger file or string. + ast_type TigerDriver::parse_() + { + std::string* fn = std::get_if(&input_); + + /* The (address of) the string behind the symbol FILENAME is + guaranteed to remain valid even after the symbol has been + destroyed, so we can safely pass `&filename.name_get()' to + `location_.initialize()'. As for other symbols, the + corresponding string will be deallocated at the end of the + program. */ + misc::symbol filename(fn == nullptr ? "" + : *fn == "-" ? "standard input" + : *fn); + location_.initialize(&filename.get()); + + std::shared_ptr in; + if (fn == nullptr) + // Parse a Tweast. + in = (std::make_shared( + std::get(input_)->input_get())); + else if (*fn == "-") + // Parse from the standard input. + in.reset(&std::cin, [](...) {}); + else + { + // Parse from a file. + in = std::make_shared(*fn); + if (in->fail()) + error_ << misc::error::error_type::failure << program_name + << ": cannot open `" << filename << "': " << strerror(errno) + << std::endl + << &misc::error::exit; + } + + // FIXME DONE: Some code was deleted here (Initialize Lexer and enable scan traces). + Lexer lexer(*in); + lexer.set_debug(scan_trace_p_); + + // FIXME DONE: Some code was deleted here (Initialize the parser and enable parse traces). + parser parser(*this, lexer); + parser.set_debug_level(parse_trace_p_); + + // FIXME DONE: Some code was deleted here (Run the parser). + parser.parse(); + + ast_type res = ast_; + ast_ = static_cast(nullptr); + + return res; + } + + /*---------------. + | Parse a file. | + `---------------*/ + + ast_type TigerDriver::parse_file(const misc::path& name) + { + if (parse_trace_p_) + std::cerr << "Parsing file: " << name << '\n'; + input_ = name.string(); + return parse_(); + } + + /*----------------. + | Parse a Tweast. | + `----------------*/ + + ast_type TigerDriver::parse_input(Tweast& s, bool extensions) + { + std::swap(extensions, enable_extensions_p_); + // Merge all aggregated Tweasts into a single one. + s.flatten(); + if (parse_trace_p_) + std::cerr << "Parsing string: " << s.input_get() << '\n'; + input_ = &s; + ast_type res = parse_(); + std::swap(extensions, enable_extensions_p_); + return res; + } + + ast_type TigerDriver::parse(Tweast& s) { return parse_input(s, true); } + + /*-----------------. + | Parse a string. | + `-----------------*/ + + ast_type TigerDriver::parse(const std::string& s) + { + Tweast in(s); + return parse_input(in, false); + } + + /*-----------------------. + | Parse a Tiger import. | + `-----------------------*/ + + ast::ChunkList* TigerDriver::parse_import(const std::string& name, + const location& loc) + { + // Try to find directory containing the file to import. + misc::path directory_path = library_.find_file(name); + + if (directory_path.empty()) + { + error_ << misc::error::error_type::failure << loc << ": " << name + << ": file not found.\n"; + return nullptr; + } + + // Complete path of file (directory + filename). + misc::path absolute_path = + directory_path / misc::path(misc::path(name).filename()); + + // Detect recursive inclusion. + if (open_files_.find(absolute_path) != open_files_.end()) + { + error_ << misc::error::error_type::failure << loc << ": " << name + << ": recursive inclusion.\n" + << open_files_[absolute_path] + << ": initial inclusion was here.\n"; + return nullptr; + } + + library_.push_current_directory(directory_path); + open_files_[absolute_path] = loc; + // Save the inputs, and reset them. + input_type saved_input = input_; + location saved_location = location_; + // Parse the imported file. + ast::ChunkList* res = nullptr; + try + { + res = parse_file(absolute_path); + } + catch (const std::bad_variant_access& e) + { + error_ << misc::error::error_type::parse << absolute_path + << ": imported from " << loc + << ": syntax error, unexpected exp, expecting chunks.\n"; + error_.exit(); + } + // Set the inputs back to their original values. + input_ = saved_input; + location_ = saved_location; + + open_files_.erase(absolute_path); + library_.pop_current_directory(); + return res; + } + + const misc::error& TigerDriver::error_get() const { return error_; } + +} // namespace parse diff --git a/tiger-compiler/src/parse/tiger-driver.hh b/tiger-compiler/src/parse/tiger-driver.hh new file mode 100644 index 0000000..266f8c2 --- /dev/null +++ b/tiger-compiler/src/parse/tiger-driver.hh @@ -0,0 +1,110 @@ +/** + ** \file parse/tiger-driver.hh + ** \brief Declaration of parse::TigerDriver. + */ + +#pragma once + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +namespace parse +{ + /// Conduct the scanner and the parser. + class TigerDriver + { + public: + /// The parsed object is either a file, represented by the filename + /// or a Tweast. + using input_type = misc::variant; + + TigerDriver(const misc::file_library& lib = misc::file_library()); + ~TigerDriver(); + + friend class parser; + /// Parse the Tiger file \a name. + ast_type parse_file(const misc::path& name); + /// Parse the Tweast \a s. Extensions are enabled. + ast_type parse(Tweast& s); + /// Parse the string \a s. Extensions are automatically disabled. + ast_type parse(const std::string& s); + + /// Parse a Tiger prelude, return the list of chunk. + ast::ChunkList* parse_import(const std::string& name, const location& loc); + + /// Return the error status of the parsing. + const misc::error& error_get() const; + + /// The default prelude. + const char* prelude() const; + + /// Set the scanner traces. + TigerDriver& scan_trace(bool b = true); + + /// Set the parser traces. + TigerDriver& parse_trace(bool b = true); + + /// Enable object extensions. + TigerDriver& enable_object_extensions(bool b = true); + + /// Enable object extensions. + TigerDriver& enable_assert_extensions(bool b = true); + + /// Enable syntax extensions. + TigerDriver& enable_extensions(bool b = true); + + private: + /// \name Handling the scanner. + /// \{ + /// Verbose scanning? + bool scan_trace_p_ = getenv("SCAN"); + + /// The list of open files, and the location of their request. + std::map open_files_; + /// \} + + /// \name Running the parse. + /// \{ + /// Parse a Tiger program, return the AST. + ast_type parse_(); + + /// Parse a Tweast. \a extensions temporarily enable or disable + /// extensions for the string parsing. This method is used to factor + /// code between parse(Tweast) and parse(const std::string)) + ast_type parse_input(Tweast& input, bool extensions); + + /// Parse a Tiger prelude \a f, return the list of chunk. + ast::ChunkList* parse_prelude(const std::string& f); + + public: + /// The result of the parse. + ast_type ast_; + /// Parsing errors handler. + misc::error error_; + /// Verbose parsing? + bool parse_trace_p_ = getenv("PARSE"); + /// \} + + /// The location requesting the import. + location location_; + /// The source to parse. + input_type input_; + /// The file library for imports. + misc::file_library library_; + /// Allow object extensions? + bool enable_object_extensions_p_ = false; + /// Allow assert extensions? + bool enable_assert_extensions_p_ = false; + /// Allow language extensions (reserved identifiers, new keywords)? + bool enable_extensions_p_ = false; + }; + +} // namespace parse diff --git a/tiger-compiler/src/parse/tiger-factory.hh b/tiger-compiler/src/parse/tiger-factory.hh new file mode 100644 index 0000000..9165bb6 --- /dev/null +++ b/tiger-compiler/src/parse/tiger-factory.hh @@ -0,0 +1,144 @@ +/** + ** \file parse/tiger-factory.hh + ** \brief Declaration of parse::TigerFactory. + */ + +#pragma once + +#include +#include +#include + +namespace parse +{ + ast::IntExp* make_IntExp(const location& location, int num); + + ast::StringExp* make_StringExp(const location& location, std::string string); + + ast::ObjectExp* make_ObjectExp(const location& location, + ast::NameTy* type_name); + + ast::CallExp* make_CallExp(const location& location, + misc::symbol name, + ast::exps_type* args); + + ast::MethodCallExp* make_MethodCallExp(const location& location, + misc::symbol name, + ast::exps_type* args, + ast::Var* object); + + ast::RecordExp* make_RecordExp(const location& location, + ast::NameTy* type_name, + ast::fieldinits_type* fields); + + ast::ArrayExp* make_ArrayExp(const location& location, + ast::NameTy* type_name, + ast::Exp* size, + ast::Exp* init); + + ast::NilExp* make_NilExp(const location& location); + + ast::SeqExp* make_SeqExp(const location& location, ast::exps_type* exps); + + ast::AssignExp* + make_AssignExp(const location& location, ast::Var* var, ast::Exp* exp); + + ast::IfExp* make_IfExp(const location& location, + ast::Exp* test, + ast::Exp* thenclause, + ast::Exp* elseclause); + + ast::IfExp* + make_IfExp(const location& location, ast::Exp* test, ast::Exp* thenclause); + + ast::WhileExp* + make_WhileExp(const location& location, ast::Exp* test, ast::Exp* body); + + ast::ForExp* make_ForExp(const location& location, + ast::VarDec* vardec, + ast::Exp* hi, + ast::Exp* body); + + ast::BreakExp* make_BreakExp(const location& location); + + ast::LetExp* + make_LetExp(const location& location, ast::ChunkList* decs, ast::Exp* body); + + ast::OpExp* make_OpExp(const location& location, + ast::Exp* left, + ast::OpExp::Oper oper, + ast::Exp* right); + + ast::CastExp* + make_CastExp(const location& location, ast::Exp* exp, ast::Ty* ty); + + ast::SimpleVar* make_SimpleVar(const location& location, misc::symbol name); + + ast::FieldVar* + make_FieldVar(const location& location, ast::Var* var, misc::symbol name); + + ast::SubscriptVar* + make_SubscriptVar(const location& location, ast::Var* var, ast::Exp* index); + + /* Use expansion parameter pack to handle when we have one or empty arguments */ + template ast::exps_type* make_exps_type(T... exps); + + ast::ChunkList* make_ChunkList(const location& location); + + ast::TypeChunk* make_TypeChunk(const location& location); + + ast::TypeDec* + make_TypeDec(const location& location, misc::symbol name, ast::Ty* ty); + + ast::RecordTy* make_RecordTy(const location& location, + ast::fields_type* fields); + + ast::ArrayTy* make_ArrayTy(const location& location, ast::NameTy* base_type); + + template ast::fields_type* make_fields_type(T... types); + + ast::Field* make_Field(const location& location, + misc::symbol name, + ast::NameTy* type_name); + + ast::NameTy* make_NameTy(const location& location, misc::symbol name); + + template + ast::fieldinits_type* make_fieldinits_type(T... inits_types); + + ast::FieldInit* + make_FieldInit(const location& location, misc::symbol name, ast::Exp* init); + + ast::ClassTy* make_ClassTy(const location& location, + ast::NameTy* super, + ast::ChunkList* decs); + + ast::VarChunk* make_VarChunk(const location& location); + + ast::VarDec* make_VarDec(const location& location, + misc::symbol name, + ast::NameTy* type_name, + ast::Exp* init); + + ast::MethodChunk* make_MethodChunk(const location& location); + + ast::MethodDec* make_MethodDec(const location& location, + misc::symbol name, + ast::VarChunk* formals, + ast::NameTy* result, + ast::Exp* body); + + ast::FunctionDec* make_FunctionDec(const location& location, + misc::symbol name, + ast::VarChunk* formals, + ast::NameTy* result, + ast::Exp* body); + + template ast::FunctionChunk* make_FunctionChunk(T... args); + + // For the custom tiger extension + ast::AssertExp* make_AssertExp(const location& location, + ast::Exp* condition); +} // namespace parse + +#include diff --git a/tiger-compiler/src/parse/tiger-factory.hxx b/tiger-compiler/src/parse/tiger-factory.hxx new file mode 100644 index 0000000..39d8531 --- /dev/null +++ b/tiger-compiler/src/parse/tiger-factory.hxx @@ -0,0 +1,272 @@ +#pragma once +#include + +namespace parse +{ + inline ast::IntExp* make_IntExp(const location& location, int num) + { + return new ast::IntExp(location, num); + } + + inline ast::StringExp* make_StringExp(const location& location, + std::string string) + { + // FIXME DONE: Some code was deleted here (Constructor of StringExp). + return new ast::StringExp(location, string); + } + + inline ast::ObjectExp* make_ObjectExp(const location& location, + ast::NameTy* type_name) + { + // FIXME DONE: Some code was deleted here (Constructor of Object). + return new ast::ObjectExp(location, type_name); + } + + inline ast::CallExp* make_CallExp(const location& location, + misc::symbol name, + ast::exps_type* args) + { + // FIXME DONE: Some code was deleted here (Constructor of CallExp). + return new ast::CallExp(location, name, args); + } + + inline ast::MethodCallExp* make_MethodCallExp(const location& location, + misc::symbol name, + ast::exps_type* args, + ast::Var* object) + { + // FIXME DONE: Some code was deleted here (Constructor of MethodCallExp). + return new ast::MethodCallExp(location, name, args, object); + } + + inline ast::RecordExp* make_RecordExp(const location& location, + ast::NameTy* type_name, + ast::fieldinits_type* fields) + { + // FIXME DONE: Some code was deleted here (Constructor of RecordExp). + return new ast::RecordExp(location, type_name, fields); + } + + inline ast::ArrayExp* make_ArrayExp(const location& location, + ast::NameTy* type_name, + ast::Exp* size, + ast::Exp* init) + { + // FIXME DONE: Some code was deleted here (Constructor of ArrayExp). + return new ast::ArrayExp(location, type_name, size, init); + } + + inline ast::NilExp* make_NilExp(const location& location) + { + return new ast::NilExp(location); + } + + inline ast::SeqExp* make_SeqExp(const location& location, + ast::exps_type* exps) + { + // FIXME DONE: Some code was deleted here (Constructor of SeqExp). + return new ast::SeqExp(location, exps); + } + + inline ast::AssignExp* + make_AssignExp(const location& location, ast::Var* var, ast::Exp* exp) + { + // FIXME DONE: Some code was deleted here (Constructor of AssignExp). + return new ast::AssignExp(location, var, exp); + } + + inline ast::IfExp* make_IfExp(const location& location, + ast::Exp* test, + ast::Exp* thenclause, + ast::Exp* elseclause) + { + // FIXME DONE: Some code was deleted here (Constructor of IfExp). + return new ast::IfExp(location, test, thenclause, elseclause); + } + + inline ast::IfExp* + make_IfExp(const location& location, ast::Exp* test, ast::Exp* thenclause) + { + // FIXME DONE: Some code was deleted here (Constructor of IfExp). + return new ast::IfExp(location, test, thenclause); + } + + inline ast::WhileExp* + make_WhileExp(const location& location, ast::Exp* test, ast::Exp* body) + { + return new ast::WhileExp(location, test, body); + } + + inline ast::ForExp* make_ForExp(const location& location, + ast::VarDec* vardec, + ast::Exp* hi, + ast::Exp* body) + { + return new ast::ForExp(location, vardec, hi, body); + } + + inline ast::BreakExp* make_BreakExp(const location& location) + { + // FIXME DONE: Some code was deleted here (Constructor of BreakExp). + return new ast::BreakExp(location); + } + + inline ast::LetExp* + make_LetExp(const location& location, ast::ChunkList* decs, ast::Exp* body) + { + // FIXME DONE: Some code was deleted here (Constructor of LetExp). + return new ast::LetExp(location, decs, body); + } + + inline ast::OpExp* make_OpExp(const location& location, + ast::Exp* left, + ast::OpExp::Oper oper, + ast::Exp* right) + { + return new ast::OpExp(location, left, oper, right); + } + + inline ast::CastExp* + make_CastExp(const location& location, ast::Exp* exp, ast::Ty* ty) + { + return new ast::CastExp(location, exp, ty); + } + + inline ast::SimpleVar* make_SimpleVar(const location& location, + misc::symbol name) + { + return new ast::SimpleVar(location, name); + } + + inline ast::FieldVar* + make_FieldVar(const location& location, ast::Var* var, misc::symbol name) + { + // FIXME DONE: Some code was deleted here (Constructor of FieldVar). + return new ast::FieldVar(location, var, name); + } + + inline ast::SubscriptVar* + make_SubscriptVar(const location& location, ast::Var* var, ast::Exp* index) + { + return new ast::SubscriptVar(location, var, index); + } + + /* Use expansion parameter pack to handle one or empty arguments */ + template inline ast::exps_type* make_exps_type(T... exps) + { + return new ast::exps_type{exps...}; + } + + inline ast::ChunkList* make_ChunkList(const location& location) + { + return new ast::ChunkList(location); + } + + inline ast::TypeChunk* make_TypeChunk(const location& location) + { + return new ast::TypeChunk(location); + } + + inline ast::TypeDec* + make_TypeDec(const location& location, misc::symbol name, ast::Ty* ty) + { + return new ast::TypeDec(location, name, ty); + } + + inline ast::RecordTy* make_RecordTy(const location& location, + ast::fields_type* fields) + { + // FIXME DONE: Some code was deleted here (Constructor of RecordTy). + return new ast::RecordTy(location, fields); + } + + inline ast::ArrayTy* make_ArrayTy(const location& location, + ast::NameTy* base_type) + { + return new ast::ArrayTy(location, base_type); + } + + template inline ast::fields_type* make_fields_type(T... types) + { + return new ast::fields_type{types...}; + } + + inline ast::Field* make_Field(const location& location, + misc::symbol name, + ast::NameTy* type_name) + { + return new ast::Field(location, name, type_name); + } + + inline ast::NameTy* make_NameTy(const location& location, misc::symbol name) + { + return new ast::NameTy(location, name); + } + + template + inline ast::fieldinits_type* make_fieldinits_type(T... inits_types) + { + return new ast::fieldinits_type{inits_types...}; + } + + inline ast::FieldInit* + make_FieldInit(const location& location, misc::symbol name, ast::Exp* init) + { + return new ast::FieldInit(location, name, init); + } + + inline ast::ClassTy* make_ClassTy(const location& location, + ast::NameTy* super, + ast::ChunkList* decs) + { + return new ast::ClassTy(location, super, decs); + } + + inline ast::VarChunk* make_VarChunk(const location& location) + { + return new ast::VarChunk(location); + } + + inline ast::VarDec* make_VarDec(const location& location, + misc::symbol name, + ast::NameTy* type_name, + ast::Exp* init) + { + return new ast::VarDec(location, name, type_name, init); + } + + inline ast::MethodChunk* make_MethodChunk(const location& location) + { + return new ast::MethodChunk(location); + } + + inline ast::MethodDec* make_MethodDec(const location& location, + misc::symbol name, + ast::VarChunk* formals, + ast::NameTy* result, + ast::Exp* body) + { + return new ast::MethodDec(location, name, formals, result, body); + } + + inline ast::FunctionDec* make_FunctionDec(const location& location, + misc::symbol name, + ast::VarChunk* formals, + ast::NameTy* result, + ast::Exp* body) + { + return new ast::FunctionDec(location, name, formals, result, body); + } + + template inline ast::FunctionChunk* make_FunctionChunk(T... args) + { + return new ast::FunctionChunk(args...); + } + + // For the custom tiger extension + inline ast::AssertExp* make_AssertExp(const location& location, + ast::Exp* condition) + { + return new ast::AssertExp(location, condition); + } +} // namespace parse diff --git a/tiger-compiler/src/parse/tweast.cc b/tiger-compiler/src/parse/tweast.cc new file mode 100644 index 0000000..fbb21ac --- /dev/null +++ b/tiger-compiler/src/parse/tweast.cc @@ -0,0 +1,87 @@ +/** + ** \file parse/tweast.cc + ** \brief Implementation of parse::Tweast. + */ + +#include + +#include + +#include + +namespace parse +{ + unsigned Tweast::count_ = 0; + + Tweast::Tweast() + : Tweast("") + {} + + Tweast::Tweast(const std::string& str) + : MetavarMap::MetavarMap("exp") + , MetavarMap::MetavarMap("lvalue") + , MetavarMap::MetavarMap("namety") + , MetavarMap::MetavarMap("chunks") + , MetavarMap::MetavarMap("tweast") + , input_(str) + {} + + void Tweast::flatten() + { + using tweasts_type = MetavarMap; + using tweasts_map_type = tweasts_type::map_type; + + // Recursively flatten each Tweast metavariable. + for (const tweasts_map_type::value_type& t : tweasts_type::map_) + t.second->flatten(); + + // Grab each non-Tweast metavariable of Tweast metavariables, and + // renumber the metavariables. + for (const tweasts_map_type::value_type& t : tweasts_type::map_) + { + unsigned tweast_index = t.first; + Tweast& tweast = *t.second; + + // Tweast input. + std::string tweast_input = tweast.input_get(); + // Move metavariables to THIS and rename them. + move_metavars_(tweast, tweast_input); + move_metavars_(tweast, tweast_input); + move_metavars_(tweast, tweast_input); + move_metavars_(tweast, tweast_input); + + // Replace the tweast metavariable reference with its input string. + std::string tweast_var = tweasts_type::show(tweast_index); + std::string input = input_.str(); + boost::algorithm::replace_first(input, tweast_var, tweast_input); + input_.str(input); + } + + // Delete aggregated Tweasts, and remove their associated + // metavariable from the map of Tweasts. + for (const tweasts_map_type::value_type& t : tweasts_type::map_) + delete t.second; + tweasts_type::map_.clear(); + } + + std::string Tweast::input_get() const { return input_.str(); } + + std::ostream& Tweast::dump(std::ostream& ostr) const + { + ostr << "input = \"" << input_.str() << "\"" << misc::iendl; + + MetavarMap::dump(ostr); + MetavarMap::dump(ostr); + MetavarMap::dump(ostr); + MetavarMap::dump(ostr); + MetavarMap::dump(ostr); + + return ostr << misc::decendl; + } + + std::ostream& operator<<(std::ostream& ostr, const Tweast& in) + { + return in.dump(ostr); + } + +} // namespace parse diff --git a/tiger-compiler/src/parse/tweast.hh b/tiger-compiler/src/parse/tweast.hh new file mode 100644 index 0000000..910f694 --- /dev/null +++ b/tiger-compiler/src/parse/tweast.hh @@ -0,0 +1,79 @@ +/** + ** \file parse/tweast.hh + ** \brief Declaration of parse::Tweast. + */ + +#pragma once + +#include +#include +#include + +#include + +#include +#include +#include + +namespace parse +{ + /// \brief TWEAST stands for ``Text With Embedded Abstract Syntax Trees''. + /// + /// Aggregate string to parse and tables of metavariables. + class Tweast + : public MetavarMap + , public MetavarMap + , public MetavarMap + , public MetavarMap + , public MetavarMap + { + public: + Tweast(); + Tweast(const std::string& str); + + /// \brief Stream manipulator. + /// + /// Append Tiger expressions to the string to parse. + template Tweast& operator<<(const T& t); + + /// Metavariables manipulator. + template T* take(unsigned s); + + /// Move the contents of all aggregated Tweast metavariables into + /// the current Tweast. + void flatten(); + + /// Get the current input string. + std::string input_get() const; + + /// Print the table + std::ostream& dump(std::ostream& ostr) const; + + protected: + // Insert base class members in the current scope. + using MetavarMap::append_; + using MetavarMap::append_; + using MetavarMap::append_; + using MetavarMap::append_; + using MetavarMap::append_; + + /// Fake append (default case, i.e. when \a data is not a metavariable). + template T& append_(unsigned&, T& data) const; + + template + void move_metavars_(Tweast& tweast, std::string& input); + + protected: + /// The next identifier suffix to create. + static unsigned count_; + + /// The string to parse. + std::stringstream input_; + }; + + /// Display the content of the tweast. + std::ostream& operator<<(std::ostream& ostr, const Tweast& in); + +} // namespace parse + +#include diff --git a/tiger-compiler/src/parse/tweast.hxx b/tiger-compiler/src/parse/tweast.hxx new file mode 100644 index 0000000..3cb9592 --- /dev/null +++ b/tiger-compiler/src/parse/tweast.hxx @@ -0,0 +1,75 @@ +/** + ** \file parse/tweast.hxx + ** \brief implements inline methods of parse/tweast.hh + */ + +#pragma once + +#include + +#include + +#include +#include +#include + +namespace parse +{ + template T& Tweast::append_(unsigned&, T& data) const + { + return data; + } + + template Tweast& Tweast::operator<<(const T& t) + { + input_ << append_(count_, t); + return *this; + } + + template T* Tweast::take(unsigned s) + { + T* t = nullptr; + try + { + t = MetavarMap::take_(s); + } + catch (const std::range_error& e) + { + // Print the contents of the Tweast before dying. + misc::error error; + error << e.what() << std::endl; + error << *this; + error.ice_here(); + } + return t; + } + + template + void Tweast::move_metavars_(Tweast& tweast, std::string& input) + { + using metavars_type = MetavarMap; + for (const typename metavars_type::map_type::value_type& var : + tweast.metavars_type::map_) + { + // Append the variable from VAR to the enclosing Tweast. + unsigned old_num = var.first; + // Warning, this is not thread-safe. + unsigned new_num = count_; + T* data = var.second; + metavars_type::map_[new_num] = data; + ++count_; + + // Rename metavariables according to the numbering scheme + // within the input string. + std::string old_str = metavars_type::show(old_num); + std::string new_str = metavars_type::show(new_num); + // FIXME: This is inefficient, since the string is viewed + // each time a metavariable is processed. Better store + // each (old_num, new_num) pair in a map, and process + // the string in a single pass. + boost::algorithm::replace_first(input, old_str, new_str); + } + tweast.metavars_type::map_.clear(); + } + +} // namespace parse -- cgit v1.2.3