From c516c0c3bf67d3156badc76c30c215f191329145 Mon Sep 17 00:00:00 2001 From: ValKmjolnir Date: Mon, 26 Jun 2023 23:59:09 +0800 Subject: [PATCH] :sparkles: add new import & use c++17 --- CMakeLists.txt | 2 +- README.md | 2 +- ast/ast_dumper.cpp | 7 ++ ast/ast_dumper.h | 3 +- ast/ast_visitor.cpp | 4 + ast/ast_visitor.h | 1 + ast/nasal_new_ast.cpp | 4 + ast/nasal_new_ast.h | 16 +++ ast/nasal_new_import.cpp | 217 +++++++++++++++++++++++++++++++++++++++ ast/nasal_new_import.h | 40 ++++++++ ast/nasal_new_lexer.cpp | 8 +- ast/nasal_new_main.cpp | 9 +- ast/nasal_new_parse.h | 14 ++- makefile | 8 +- module/makefile | 2 +- 15 files changed, 322 insertions(+), 15 deletions(-) create mode 100644 ast/nasal_new_import.cpp create mode 100644 ast/nasal_new_import.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 9fe5c8e..4850541 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,7 +5,7 @@ project(nasal VERSION 10.1) message("CMAKE_HOST_SYSTEM_NAME: ${CMAKE_HOST_SYSTEM_NAME}") # -std=c++14 -Wshadow -Wall -set(CMAKE_CXX_STANDARD 14) +set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED True) set(CMAKE_CXX_FLAGS_RELEASE_INIT "-Wshadow -Wall") diff --git a/README.md b/README.md index df4a46f..5f72b5d 100644 --- a/README.md +++ b/README.md @@ -32,7 +32,7 @@ __Contact us if having great ideas to share!__ is an ECMAscript-like language used in [FlightGear](https://www.flightgear.org/). The designer is [Andy Ross](https://github.com/andyross). -This interpreter is totally rewritten by [ValKmjolnir](https://github.com/ValKmjolnir) using `C++`(`-std=c++14`) +This interpreter is totally rewritten by [ValKmjolnir](https://github.com/ValKmjolnir) using `C++`(`-std=c++17`) without reusing the code in [Andy Ross's nasal interpreter](https://github.com/andyross/nasal). But we really appreciate that Andy created this amazing programming language. diff --git a/ast/ast_dumper.cpp b/ast/ast_dumper.cpp index 47adbd2..21c7ff6 100644 --- a/ast/ast_dumper.cpp +++ b/ast/ast_dumper.cpp @@ -2,6 +2,13 @@ #include +bool ast_dumper::visit_file_info(file_info* node) { + dump_indent(); + std::cout << "file \"" << node->get_file_name() << "\""; + std::cout << format_location(node->get_location()); + return true; +} + bool ast_dumper::visit_null_expr(null_expr* node) { dump_indent(); std::cout << "null" << format_location(node->get_location()); diff --git a/ast/ast_dumper.h b/ast/ast_dumper.h index ce868a0..25a26e4 100644 --- a/ast/ast_dumper.h +++ b/ast/ast_dumper.h @@ -33,12 +33,13 @@ private: std::stringstream ss; ss << " -> "; ss << location.file << ":"; - ss << location.begin_line << ":" << location.begin_column; + ss << location.begin_line << ":" << location.begin_column + 1; ss << "\n"; return ss.str(); } public: + bool visit_file_info(file_info*) override; bool visit_null_expr(null_expr*) override; bool visit_nil_expr(nil_expr*) override; bool visit_number_literal(number_literal*) override; diff --git a/ast/ast_visitor.cpp b/ast/ast_visitor.cpp index 7284724..6d8c3e1 100644 --- a/ast/ast_visitor.cpp +++ b/ast/ast_visitor.cpp @@ -5,6 +5,10 @@ bool ast_visitor::visit_expr(expr* node) { return true; } +bool ast_visitor::visit_file_info(file_info* node) { + return true; +} + bool ast_visitor::visit_null_expr(null_expr* node) { return true; } diff --git a/ast/ast_visitor.h b/ast/ast_visitor.h index ff30483..e9b407f 100644 --- a/ast/ast_visitor.h +++ b/ast/ast_visitor.h @@ -5,6 +5,7 @@ class ast_visitor { public: virtual bool visit_expr(expr*); + virtual bool visit_file_info(file_info*); virtual bool visit_null_expr(null_expr*); virtual bool visit_nil_expr(nil_expr*); virtual bool visit_number_literal(number_literal*); diff --git a/ast/nasal_new_ast.cpp b/ast/nasal_new_ast.cpp index 4ad8131..cbc6bd1 100644 --- a/ast/nasal_new_ast.cpp +++ b/ast/nasal_new_ast.cpp @@ -5,6 +5,10 @@ void expr::accept(ast_visitor* visitor) { visitor->visit_expr(this); } +void file_info::accept(ast_visitor* visitor) { + visitor->visit_file_info(this); +} + void null_expr::accept(ast_visitor* visitor) { visitor->visit_null_expr(this); } diff --git a/ast/nasal_new_ast.h b/ast/nasal_new_ast.h index a16d6a9..0def471 100644 --- a/ast/nasal_new_ast.h +++ b/ast/nasal_new_ast.h @@ -8,6 +8,7 @@ enum class expr_type:u32 { ast_null=0, // null node + ast_file_info, // stores file info ast_block, // code block ast_nil, // nil keyword ast_num, // number, basic value type @@ -73,6 +74,21 @@ public: virtual void accept(ast_visitor*); }; +class file_info:public expr { +private: + uint16_t index; + std::string filename; + +public: + file_info(const span& location, uint16_t file_index, const std::string& name): + expr(location, expr_type::ast_file_info), + index(file_index), filename(name) {} + ~file_info() = default; + uint16_t get_index() const {return index;} + const std::string& get_file_name() const {return filename;} + void accept(ast_visitor*) override; +}; + class null_expr:public expr { public: null_expr(const span& location): diff --git a/ast/nasal_new_import.cpp b/ast/nasal_new_import.cpp new file mode 100644 index 0000000..535ee37 --- /dev/null +++ b/ast/nasal_new_import.cpp @@ -0,0 +1,217 @@ +#include "nasal_new_import.h" + +linker::linker(error& e): show_path(false), lib_loaded(false), err(e) { + char sep=is_windows()? ';':':'; + std::string PATH=getenv("PATH"); + usize last=0; + usize pos=PATH.find(sep, 0); + while(pos!=std::string::npos) { + std::string dirpath=PATH.substr(last, pos-last); + if (dirpath.length()) { + envpath.push_back(dirpath); + } + last=pos+1; + pos=PATH.find(sep, last); + } + if (last!=PATH.length()) { + envpath.push_back(PATH.substr(last)); + } +} + +std::string linker::get_path(call_expr* node) { + if (node->get_calls()[0]->get_type()==expr_type::ast_callf) { + auto tmp = (call_function*)node->get_calls()[0]; + return ((string_literal*)tmp->get_argument()[0])->get_content(); + } + auto fpath = std::string("."); + for(auto i : node->get_calls()) { + fpath += (is_windows()? "\\":"/") + ((call_hash*)i)->get_field(); + } + return fpath + ".nas"; +} + +std::string linker::find_file(const std::string& filename) { + // first add file name itself into the file path + std::vector fpath = {filename}; + + // generate search path from environ path + for(const auto& p : envpath) { + fpath.push_back(p + (is_windows()? "\\":"/") + filename); + } + + // search file + for(const auto& i : fpath) { + if (access(i.c_str(), F_OK)!=-1) { + return i; + } + } + + // we will find lib.nas in nasal std directory + if (filename=="lib.nas") { + return is_windows()? find_file("stl\\lib.nas"):find_file("stl/lib.nas"); + } + if (!show_path) { + err.err("link", "cannot find file <" + filename + ">"); + return ""; + } + std::string paths = ""; + for(const auto& i : fpath) { + paths += " " + i + "\n"; + } + err.err("link", "cannot find file <" + filename + "> in these paths:\n" + paths); + return ""; +} + +bool linker::import_check(expr* node) { +/* + call + |_id:import + |_callh:stl + |_callh:file +*/ + if (node->get_type()!=expr_type::ast_call) { + return false; + } + auto tmp = (call_expr*)node; + if (tmp->get_first()->get_type()!=expr_type::ast_id) { + return false; + } + if (((identifier*)tmp->get_first())->get_name()!="import") { + return false; + } + if (!tmp->get_calls().size()) { + return false; + } + // import.xxx.xxx; + if (tmp->get_calls()[0]->get_type()==expr_type::ast_callh) { + for(auto i : tmp->get_calls()) { + if (i->get_type()!=expr_type::ast_callh) { + return false; + } + } + return true; + } + // import("xxx"); + if (tmp->get_calls().size()!=1) { + return false; + } +/* + call + |_id:import + |_call_func + |_string:'filename' +*/ + if (tmp->get_calls()[0]->get_type()!=expr_type::ast_callf) { + return false; + } + auto func_call = (call_function*)tmp->get_calls()[0]; + if (func_call->get_argument().size()!=1) { + return false; + } + if (func_call->get_argument()[0]->get_type()!=expr_type::ast_str) { + return false; + } + return true; +} + +bool linker::exist(const std::string& file) { + // avoid importing the same file + for(const auto& fname : files) { + if (file==fname) { + return true; + } + } + files.push_back(file); + return false; +} + +void linker::link(code_block* new_tree_root, code_block* old_tree_root) { + // add children of add_root to the back of root + for(auto& i:old_tree_root->get_expressions()) { + new_tree_root->add_expression(i); + } + // clean old root + old_tree_root->get_expressions().clear(); +} + +code_block* linker::import_regular_file(call_expr* node) { + lexer lex(err); + parse par(err); + // get filename and set node to ast_null + auto filename = get_path(node); + // node.clear(); + + // avoid infinite loading loop + filename = find_file(filename); + if (!filename.length() || exist(filename)) { + return new code_block({0, 0, 0, 0, filename}); + } + + // start importing... + lex.scan(filename); + par.compile(lex); + auto tmp = par.swap(nullptr); + + // check if tmp has 'import' + return load(tmp, files.size()-1); +} + +code_block* linker::import_nasal_lib() { + lexer lex(err); + parse par(err); + auto filename = find_file("lib.nas"); + if (!filename.length()) { + return new code_block({0, 0, 0, 0, filename}); + } + + // avoid infinite loading loop + if (exist(filename)) { + return new code_block({0, 0, 0, 0, filename}); + } + + // start importing... + lex.scan(filename); + par.compile(lex); + auto tmp = par.swap(nullptr); + + // check if tmp has 'import' + return load(tmp, files.size()-1); +} + +code_block* linker::load(code_block* root, u16 fileindex) { + auto tree = new code_block({0, 0, 0, 0, files[fileindex]}); + if (!lib_loaded) { + auto tmp = import_nasal_lib(); + link(tree, tmp); + delete tmp; + lib_loaded = true; + } + for(auto i : root->get_expressions()) { + if (!import_check(i)) { + break; + } + auto tmp = import_regular_file((call_expr*)i); + link(tree, tmp); + delete tmp; + } + // add root to the back of tree + auto file_head = new file_info( + {0, 0, 0, 0, files[fileindex]}, fileindex, files[fileindex]); + tree->add_expression(file_head); + link(tree, root); + return tree; +} + +const error& linker::link( + parse& parse, const std::string& self, bool spath = false) { + show_path = spath; + // initializing + files = {self}; + // scan root and import files + // then generate a new ast and return to import_ast + // the main file's index is 0 + auto new_tree_root = load(parse.tree(), 0); + auto old_tree_root = parse.swap(new_tree_root); + delete old_tree_root; + return err; +} diff --git a/ast/nasal_new_import.h b/ast/nasal_new_import.h new file mode 100644 index 0000000..09d12b6 --- /dev/null +++ b/ast/nasal_new_import.h @@ -0,0 +1,40 @@ +#pragma once + +#ifndef _MSC_VER +#include +#else +#define _CRT_SECURE_NO_DEPRECATE 1 +#define _CRT_NONSTDC_NO_DEPRECATE 1 +#include +#endif + +#ifdef _MSC_VER +#define F_OK 0 +#endif + +#include "nasal_new_header.h" +#include "nasal_new_ast.h" +#include "nasal_new_lexer.h" +#include "nasal_new_parse.h" + +class linker{ +private: + bool show_path; + bool lib_loaded; + error& err; + std::vector files; + std::vector envpath; + + bool import_check(expr*); + bool exist(const std::string&); + void link(code_block*, code_block*); + std::string get_path(call_expr*); + std::string find_file(const std::string&); + code_block* import_regular_file(call_expr*); + code_block* import_nasal_lib(); + code_block* load(code_block*, u16); +public: + linker(error&); + const error& link(parse&, const std::string&, bool); + const std::vector& filelist() const {return files;} +}; diff --git a/ast/nasal_new_lexer.cpp b/ast/nasal_new_lexer.cpp index 3981794..b5fbcd2 100644 --- a/ast/nasal_new_lexer.cpp +++ b/ast/nasal_new_lexer.cpp @@ -68,16 +68,16 @@ void lexer::open(const std::string& file) { } // load - filename=file; + filename = file; std::ifstream in(file, std::ios::binary); if (in.fail()) { - err.err("lexer", "failed to open <"+file+">"); + err.err("lexer", "failed to open <" + file + ">"); } else { err.load(file); } std::stringstream ss; - ss<visit_code_block(parse.tree()); } - // linker gets parser's ast and load import files to this ast - // ld.link(parse, file, cmd&VM_DETAIL).chkerr(); - // optimizer does simple optimization on ast // optimize(parse.tree()); diff --git a/ast/nasal_new_parse.h b/ast/nasal_new_parse.h index 98b736c..bcef3fb 100644 --- a/ast/nasal_new_parse.h +++ b/ast/nasal_new_parse.h @@ -136,7 +136,14 @@ private: return_expr* return_expression(); public: - inline code_block* tree() {return root;} + code_block* tree() {return root;} + + // swap root pointer with another pointer(maybe nullptr) + code_block* swap(code_block* another) { + auto res = root; + root = another; + return res; + } public: parse(error& e): @@ -144,6 +151,11 @@ public: toks(nullptr), root(nullptr), err(e) {} + ~parse() { + if (root) { + delete root; + } + } const error& compile(const lexer&); void easter_egg() const; }; \ No newline at end of file diff --git a/makefile b/makefile index 3cc1563..915b1d8 100644 --- a/makefile +++ b/makefile @@ -16,7 +16,7 @@ SRC=\ nasal_dbg.h\ nasal.h -STD=c++14 +STD=c++17 nasal:$(SRC) $(CXX) -std=$(STD) -O3 main.cpp -o nasal -fno-exceptions -ldl -Wshadow -Wall @@ -76,6 +76,7 @@ test:nasal NASAL_NEW_AST=\ nasal_new_misc.o\ nasal_new_err.o\ + nasal_new_import.o\ nasal_new_lexer.o\ nasal_new_ast.o\ nasal_new_parse.o\ @@ -97,8 +98,11 @@ nasal_new_misc.o: ast/nasal_new_header.h ast/nasal_new_misc.cpp nasal_new_err.o: ast/nasal_new_err.h ast/nasal_new_err.cpp $(CXX) -std=$(STD) -c -O3 ast/nasal_new_err.cpp -fno-exceptions -fPIC -o nasal_new_err.o -I . +nasal_new_import.o: ast/nasal_new_import.h ast/nasal_new_import.cpp + $(CXX) --std=$(STD) -c -O3 ast/nasal_new_import.cpp -fno-exceptions -fPIC -o nasal_new_import.o -I . + nasal_new_lexer.o: ast/nasal_new_lexer.h ast/nasal_new_lexer.cpp - $(CXX) -std=$(STD) -c -O3 ast/nasal_new_lexer.cpp -fno-exceptions -fPIC -o nasal_new_lexer.o -I . + $(CXX) --std=$(STD) -c -O3 ast/nasal_new_lexer.cpp -fno-exceptions -fPIC -o nasal_new_lexer.o -I . nasal_new_ast.o: ast/nasal_new_ast.h ast/nasal_new_ast.cpp $(CXX) -std=$(STD) -c -O3 ast/nasal_new_ast.cpp -fno-exceptions -fPIC -o nasal_new_ast.o -I . diff --git a/module/makefile b/module/makefile index d506850..f3d4148 100644 --- a/module/makefile +++ b/module/makefile @@ -5,7 +5,7 @@ dynamic_libs_dll=libfib.dll libkey.dll libnasock.dll libmat.dll used_header= ../nasal.h ../nasal_gc.h -STD=c++14 +STD=c++17 all: $(dynamic_libs_so) @ echo "[Compiling] done"