diff --git a/crypto/func/func.cpp b/crypto/func/func.cpp index 43bba4ea..b260c885 100644 --- a/crypto/func/func.cpp +++ b/crypto/func/func.cpp @@ -178,6 +178,18 @@ void usage(const char* progname) { std::exit(2); } +void output_inclusion_stack() { + while (!funC::inclusion_locations.empty()) { + src::SrcLocation loc = funC::inclusion_locations.top(); + funC::inclusion_locations.pop(); + if (loc.fdescr) { + std::cerr << "note: included from "; + loc.show(std::cerr); + std::cerr << std::endl; + } + } +} + std::string output_filename; int main(int argc, char* const argv[]) { @@ -241,7 +253,7 @@ int main(int argc, char* const argv[]) { int ok = 0, proc = 0; try { while (optind < argc) { - funC::generated_from += std::string{"`"} + argv[optind] + "` "; + // funC::generated_from += std::string{"`"} + argv[optind] + "` "; ok += funC::parse_source_file(argv[optind++]); proc++; } @@ -268,14 +280,17 @@ int main(int argc, char* const argv[]) { funC::generate_output(); } catch (src::Fatal& fatal) { std::cerr << "fatal: " << fatal << std::endl; + output_inclusion_stack(); std::exit(1); } catch (src::Error& error) { std::cerr << error << std::endl; + output_inclusion_stack(); std::exit(1); } catch (funC::UnifyError& unif_err) { std::cerr << "fatal: "; unif_err.print_message(std::cerr); std::cerr << std::endl; + output_inclusion_stack(); std::exit(1); } } diff --git a/crypto/func/func.h b/crypto/func/func.h index 751e50a9..2f9877a0 100644 --- a/crypto/func/func.h +++ b/crypto/func/func.h @@ -35,10 +35,11 @@ namespace funC { extern int verbosity; extern bool op_rewrite_comments; +extern std::string generated_from; constexpr int optimize_depth = 20; -const std::string func_version{"0.1.0"}; +const std::string func_version{"0.2.0"}; enum Keyword { _Eof = -1, @@ -110,7 +111,8 @@ enum Keyword { _Infixl, _Infixr, _Const, - _PragmaHashtag + _PragmaHashtag, + _IncludeHashtag }; void define_keywords(); @@ -828,9 +830,11 @@ extern std::vector glob_func, glob_vars; // defined in parse-func.cpp bool parse_source(std::istream* is, const src::FileDescr* fdescr); -bool parse_source_file(const char* filename); +bool parse_source_file(const char* filename, src::Lexem lex = {}); bool parse_source_stdin(); +extern std::stack inclusion_locations; + /* * * EXPRESSIONS diff --git a/crypto/func/keywords.cpp b/crypto/func/keywords.cpp index b7bba0c2..3f0fa230 100644 --- a/crypto/func/keywords.cpp +++ b/crypto/func/keywords.cpp @@ -127,7 +127,9 @@ void define_keywords() { .add_keyword("infixl", Kw::_Infixl) .add_keyword("infixr", Kw::_Infixr) .add_keyword("const", Kw::_Const); - sym::symbols.add_keyword("#pragma", Kw::_PragmaHashtag); + + sym::symbols.add_keyword("#pragma", Kw::_PragmaHashtag) + .add_keyword("#include", Kw::_IncludeHashtag); } } // namespace funC diff --git a/crypto/func/parse-func.cpp b/crypto/func/parse-func.cpp index 929f5b3c..c33a3cda 100644 --- a/crypto/func/parse-func.cpp +++ b/crypto/func/parse-func.cpp @@ -23,6 +23,7 @@ #include "block/block.h" #include "block-parse.h" #include +#include namespace sym { @@ -1607,12 +1608,35 @@ void parse_pragma(Lexer& lex) { std::vector source_fdescr; +std::vector source_files; +std::stack inclusion_locations; + +void parse_include(Lexer& lex, const src::FileDescr* fdescr) { + auto include = lex.cur(); + lex.expect(_IncludeHashtag); + if (lex.tp() != _String) { + lex.expect(_String, "source file name"); + } + std::string val = lex.cur().str; + std::string parent_dir = fdescr->filename; + if (parent_dir.rfind('/') != std::string::npos) { + val = parent_dir.substr(0, parent_dir.rfind('/') + 1) + val; + } + lex.next(); + lex.expect(';'); + if (!parse_source_file(val.c_str(), include)) { + include.error(std::string{"failed parsing included file `"} + val + "`"); + } +} + bool parse_source(std::istream* is, src::FileDescr* fdescr) { src::SourceReader reader{is, fdescr}; Lexer lex{reader, true, ";,()[] ~."}; while (lex.tp() != _Eof) { if (lex.tp() == _PragmaHashtag) { parse_pragma(lex); + } else if (lex.tp() == _IncludeHashtag) { + parse_include(lex, fdescr); } else if (lex.tp() == _Global) { parse_global_var_decls(lex); } else if (lex.tp() == _Const) { @@ -1624,17 +1648,48 @@ bool parse_source(std::istream* is, src::FileDescr* fdescr) { return true; } -bool parse_source_file(const char* filename) { +bool parse_source_file(const char* filename, src::Lexem lex) { if (!filename || !*filename) { - throw src::Fatal{"source file name is an empty string"}; + auto msg = "source file name is an empty string"; + if (lex.tp) { + lex.error(msg); + } else { + throw src::Fatal{msg}; + } } + char realpath_buf[PATH_MAX] = {0, }; + realpath(filename, realpath_buf); + std::string real_filename = std::string{realpath_buf}; + if (std::count(source_files.begin(), source_files.end(), real_filename)) { + if (verbosity >= 2) { + if (lex.tp) { + lex.loc.show_warning(std::string{"skipping file "} + real_filename + " because it was already included"); + } else { + std::cerr << "warning: skipping file " << real_filename << " because it was already included" << std::endl; + } + } + return true; + } + if (lex.tp) { // included + funC::generated_from += std::string{"incl:"}; + } + funC::generated_from += std::string{"`"} + filename + "` "; + source_files.push_back(real_filename); src::FileDescr* cur_source = new src::FileDescr{filename}; source_fdescr.push_back(cur_source); std::ifstream ifs{filename}; if (ifs.fail()) { - throw src::Fatal{std::string{"cannot open source file `"} + filename + "`"}; + auto msg = std::string{"cannot open source file `"} + filename + "`"; + if (lex.tp) { + lex.error(msg); + } else { + throw src::Fatal{msg}; + } } - return parse_source(&ifs, cur_source); + inclusion_locations.push(lex.loc); + bool res = parse_source(&ifs, cur_source); + inclusion_locations.pop(); + return res; } bool parse_source_stdin() { diff --git a/crypto/func/test/i1.fc b/crypto/func/test/i1.fc new file mode 100644 index 00000000..2b95e2dd --- /dev/null +++ b/crypto/func/test/i1.fc @@ -0,0 +1,14 @@ +global int i; + +#include "i1sub1.fc"; + +() sub0() impure { i = 0; } + +#include "i1sub2.fc"; + +() main() impure { + sub0(); + sub1(); + sub2(); + i = 9; +} diff --git a/crypto/func/test/i1sub1.fc b/crypto/func/test/i1sub1.fc new file mode 100644 index 00000000..c905bf22 --- /dev/null +++ b/crypto/func/test/i1sub1.fc @@ -0,0 +1,6 @@ +;; DO NOT COMPILE DIRECTLY! +;; Compile i1.fc + +() sub1() impure { + i = 1; +} diff --git a/crypto/func/test/i1sub2.fc b/crypto/func/test/i1sub2.fc new file mode 100644 index 00000000..b7f200db --- /dev/null +++ b/crypto/func/test/i1sub2.fc @@ -0,0 +1,8 @@ +;; DO NOT COMPILE DIRECTLY! +;; Compile i1.fc + +() sub2() impure { + sub1(); + sub0(); + i = 2; +}