summaryrefslogtreecommitdiff
path: root/tiger-compiler/src/parse
diff options
context:
space:
mode:
Diffstat (limited to 'tiger-compiler/src/parse')
-rw-r--r--tiger-compiler/src/parse/fwd.hh38
-rwxr-xr-xtiger-compiler/src/parse/generate-prelude.sh31
-rw-r--r--tiger-compiler/src/parse/libparse.cc137
-rw-r--r--tiger-compiler/src/parse/libparse.hh76
-rw-r--r--tiger-compiler/src/parse/local.am125
-rw-r--r--tiger-compiler/src/parse/metavar-map.hh48
-rw-r--r--tiger-compiler/src/parse/metavar-map.hxx69
-rw-r--r--tiger-compiler/src/parse/tasks.cc53
-rw-r--r--tiger-compiler/src/parse/tasks.hh51
-rw-r--r--tiger-compiler/src/parse/tiger-driver.cc215
-rw-r--r--tiger-compiler/src/parse/tiger-driver.hh110
-rw-r--r--tiger-compiler/src/parse/tiger-factory.hh144
-rw-r--r--tiger-compiler/src/parse/tiger-factory.hxx272
-rw-r--r--tiger-compiler/src/parse/tweast.cc87
-rw-r--r--tiger-compiler/src/parse/tweast.hh79
-rw-r--r--tiger-compiler/src/parse/tweast.hxx75
16 files changed, 1610 insertions, 0 deletions
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 <misc/variant.hh>
+
+// 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<ast::Exp*, ast::ChunkList*>;
+
+} // 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 <ast/chunk-interface.hh>
+#include <ast/chunk-list.hh>
+#include <misc/file-library.hh>
+#include <misc/indent.hh>
+#include <misc/symbol.hh>
+#include <parse/libparse.hh>
+#include <parse/location.hh>
+#include <parse/tasks.hh>
+#include <parse/tiger-driver.hh>
+#include <parse/tweast.hh>
+
+// Define exported parse functions.
+namespace parse
+{
+ ast::ChunkList* parse_prelude()
+ {
+ if (tasks::prelude.empty())
+ {
+ return nullptr;
+ }
+
+ TigerDriver td;
+
+ return std::get<ast::ChunkList*>(td.parse(td.prelude()));
+ }
+
+ // Parse a Tiger file, return the corresponding abstract syntax.
+ std::pair<ast::ChunkList*, misc::error> 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<ast::Exp*>(&tree);
+ ast::ChunkList** chunks = std::get_if<ast::ChunkList*>(&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<ast::ChunkList*>(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 <set>
+#include <string>
+#include <utility>
+
+#include <ast/fwd.hh>
+#include <misc/error.hh>
+#include <misc/file-library.hh>
+#include <parse/fwd.hh>
+
+/// 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<ast::ChunkList*, misc::error>
+ 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 <string>
+
+#include <misc/map.hh>
+
+namespace parse
+{
+ /// A generic map of metavariables.
+ template <typename Data> 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<unsigned, Data*>;
+ map_type map_;
+ };
+
+ /// Output \a m onto \a ostr.
+ template <typename Data>
+ std::ostream& operator<<(std::ostream& ostr, const MetavarMap<Data>& m);
+
+} // namespace parse
+
+#include <parse/metavar-map.hxx>
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 <sstream>
+#include <string>
+
+#include <misc/contract.hh>
+#include <misc/indent.hh>
+#include <parse/metavar-map.hh>
+
+namespace parse
+{
+ template <typename Data>
+ MetavarMap<Data>::MetavarMap(const std::string& name)
+ : name_(name)
+ , map_()
+ {}
+
+ template <typename Data> MetavarMap<Data>::~MetavarMap()
+ {
+ // At this point, every metavariable should have been taken from the map.
+ assertion(map_.empty()) << *this << "not empty, aborting.\n";
+ }
+
+ template <typename Data>
+ std::string MetavarMap<Data>::show(unsigned key) const
+ {
+ return '_' + name_ + '(' + std::to_string(key) + ')';
+ }
+
+ template <typename Data>
+ std::ostream& MetavarMap<Data>::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 <typename Data>
+ std::string MetavarMap<Data>::append_(unsigned& count, Data* data)
+ {
+ map_[count] = data;
+ return show(count++);
+ }
+
+ template <typename Data> Data* MetavarMap<Data>::take_(unsigned key)
+ {
+ return map_.take(key);
+ }
+
+ template <typename Data>
+ std::ostream& operator<<(std::ostream& ostr, const MetavarMap<Data>& 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 <cstdlib>
+#include <iostream>
+#include <memory>
+
+#include <assert/tasks.hh>
+#include <ast/tasks.hh>
+#include <common.hh>
+#include <misc/file-library.hh>
+#include <object/tasks.hh>
+#include <parse/libparse.hh>
+#define DEFINE_TASKS 1
+#include <parse/tasks.hh>
+#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<ast::ChunkList*, misc::error> 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<ast::ChunkList>{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 <misc/fwd.hh>
+#include <task/libtask.hh>
+
+/// 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 <cstdlib>
+#include <fstream>
+
+#include <parse/parsetiger.hh>
+#include <parse/scantiger.hh>
+#include <parse/tiger-driver.hh>
+
+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<std::string>(&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<std::istream> in;
+ if (fn == nullptr)
+ // Parse a Tweast.
+ in = (std::make_shared<std::stringstream>(
+ std::get<Tweast*>(input_)->input_get()));
+ else if (*fn == "-")
+ // Parse from the standard input.
+ in.reset(&std::cin, [](...) {});
+ else
+ {
+ // Parse from a file.
+ in = std::make_shared<std::ifstream>(*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<ast::Exp*>(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 <map>
+#include <memory>
+#include <stack>
+
+#include <ast/fwd.hh>
+#include <common.hh>
+#include <misc/error.hh>
+#include <misc/file-library.hh>
+#include <parse/parsetiger.hh>
+#include <parse/tweast.hh>
+
+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<std::string, Tweast*>;
+
+ 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<misc::path, location> 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 <ast/all.hh>
+#include <ast/fwd.hh>
+#include <parse/location.hh>
+
+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 <class... T> 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 <class... T> 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 <class... T>
+ 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 <class... T> ast::FunctionChunk* make_FunctionChunk(T... args);
+
+ // For the custom tiger extension
+ ast::AssertExp* make_AssertExp(const location& location,
+ ast::Exp* condition);
+} // namespace parse
+
+#include <parse/tiger-factory.hxx>
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 <parse/tiger-factory.hh>
+
+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 <class... T> 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 <class... T> 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 <class... T>
+ 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 <class... T> 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 <sstream>
+
+#include <boost/algorithm/string/replace.hpp>
+
+#include <parse/tweast.hh>
+
+namespace parse
+{
+ unsigned Tweast::count_ = 0;
+
+ Tweast::Tweast()
+ : Tweast("")
+ {}
+
+ Tweast::Tweast(const std::string& str)
+ : MetavarMap<ast::Exp>::MetavarMap("exp")
+ , MetavarMap<ast::Var>::MetavarMap("lvalue")
+ , MetavarMap<ast::NameTy>::MetavarMap("namety")
+ , MetavarMap<ast::ChunkList>::MetavarMap("chunks")
+ , MetavarMap<Tweast>::MetavarMap("tweast")
+ , input_(str)
+ {}
+
+ void Tweast::flatten()
+ {
+ using tweasts_type = MetavarMap<Tweast>;
+ 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_<ast::Exp>(tweast, tweast_input);
+ move_metavars_<ast::Var>(tweast, tweast_input);
+ move_metavars_<ast::NameTy>(tweast, tweast_input);
+ move_metavars_<ast::ChunkList>(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<ast::Exp>::dump(ostr);
+ MetavarMap<ast::Var>::dump(ostr);
+ MetavarMap<ast::NameTy>::dump(ostr);
+ MetavarMap<ast::ChunkList>::dump(ostr);
+ MetavarMap<Tweast>::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 <iostream>
+#include <map>
+#include <sstream>
+
+#include <ast/fwd.hh>
+
+#include <misc/map.hh>
+#include <misc/symbol.hh>
+#include <parse/metavar-map.hh>
+
+namespace parse
+{
+ /// \brief TWEAST stands for ``Text With Embedded Abstract Syntax Trees''.
+ ///
+ /// Aggregate string to parse and tables of metavariables.
+ class Tweast
+ : public MetavarMap<ast::Exp>
+ , public MetavarMap<ast::Var>
+ , public MetavarMap<ast::NameTy>
+ , public MetavarMap<ast::ChunkList>
+ , public MetavarMap<parse::Tweast>
+ {
+ public:
+ Tweast();
+ Tweast(const std::string& str);
+
+ /// \brief Stream manipulator.
+ ///
+ /// Append Tiger expressions to the string to parse.
+ template <typename T> Tweast& operator<<(const T& t);
+
+ /// Metavariables manipulator.
+ template <typename T> 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<ast::Exp>::append_;
+ using MetavarMap<ast::Var>::append_;
+ using MetavarMap<ast::NameTy>::append_;
+ using MetavarMap<ast::ChunkList>::append_;
+ using MetavarMap<Tweast>::append_;
+
+ /// Fake append (default case, i.e. when \a data is not a metavariable).
+ template <typename T> T& append_(unsigned&, T& data) const;
+
+ template <typename T>
+ 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 <parse/tweast.hxx>
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 <algorithm>
+
+#include <boost/algorithm/string/replace.hpp>
+
+#include <misc/algorithm.hh>
+#include <misc/error.hh>
+#include <parse/tweast.hh>
+
+namespace parse
+{
+ template <typename T> T& Tweast::append_(unsigned&, T& data) const
+ {
+ return data;
+ }
+
+ template <typename T> Tweast& Tweast::operator<<(const T& t)
+ {
+ input_ << append_(count_, t);
+ return *this;
+ }
+
+ template <typename T> T* Tweast::take(unsigned s)
+ {
+ T* t = nullptr;
+ try
+ {
+ t = MetavarMap<T>::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 <typename T>
+ void Tweast::move_metavars_(Tweast& tweast, std::string& input)
+ {
+ using metavars_type = MetavarMap<T>;
+ 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