add new import & use c++17

This commit is contained in:
ValKmjolnir 2023-06-26 23:59:09 +08:00
parent 89f96d407b
commit c516c0c3bf
15 changed files with 322 additions and 15 deletions

View File

@ -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")

View File

@ -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.

View File

@ -2,6 +2,13 @@
#include <iostream>
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());

View File

@ -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;

View File

@ -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;
}

View File

@ -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*);

View File

@ -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);
}

View File

@ -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):

217
ast/nasal_new_import.cpp Normal file
View File

@ -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<std::string> 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;
}

40
ast/nasal_new_import.h Normal file
View File

@ -0,0 +1,40 @@
#pragma once
#ifndef _MSC_VER
#include <unistd.h>
#else
#define _CRT_SECURE_NO_DEPRECATE 1
#define _CRT_NONSTDC_NO_DEPRECATE 1
#include <io.h>
#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<std::string> files;
std::vector<std::string> 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<std::string>& filelist() const {return files;}
};

View File

@ -3,6 +3,7 @@
#include "nasal_new_lexer.h"
#include "nasal_new_ast.h"
#include "nasal_new_parse.h"
#include "nasal_new_import.h"
#include "ast_visitor.h"
#include "ast_dumper.h"
@ -78,7 +79,7 @@ void execute(
error err;
lexer lex(err);
parse parse(err);
// linker ld(err);
linker ld(err);
// codegen gen(err);
// vm ctx;
@ -87,14 +88,14 @@ void execute(
// parser gets lexer's token list to compile
parse.compile(lex).chkerr();
// linker gets parser's ast and load import files to this ast
ld.link(parse, file, cmd&VM_DETAIL).chkerr();
if (cmd&VM_AST) {
auto dumper = new ast_dumper();
dumper->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());

View File

@ -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;
};

View File

@ -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 .

View File

@ -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"