📝 change CRLF to LF

This commit is contained in:
ValKmjolnir 2023-12-02 19:42:21 +08:00
parent 476fbdb859
commit d42e4a5897
6 changed files with 1140 additions and 1141 deletions

View File

@ -1,245 +1,251 @@
#include "io_lib.h" #include "io_lib.h"
namespace nasal { #ifdef _MSC_VER
#define F_OK 0 // fuck msc
const auto file_type_name = "file"; #endif
void filehandle_destructor(void* ptr) { #include <sys/stat.h>
fclose(static_cast<FILE*>(ptr));
} namespace nasal {
var builtin_readfile(context* ctx, gc* ngc) { const auto file_type_name = "file";
auto filename = ctx->localr[1];
if (!filename.is_str()) { void filehandle_destructor(void* ptr) {
return nas_err("io::readfile", "\"filename\" must be string"); fclose(static_cast<FILE*>(ptr));
} }
std::ifstream in(filename.str(), std::ios::binary);
std::stringstream rd; var builtin_readfile(context* ctx, gc* ngc) {
if (!in.fail()) { auto filename = ctx->localr[1];
rd << in.rdbuf(); if (!filename.is_str()) {
} return nas_err("io::readfile", "\"filename\" must be string");
return ngc->newstr(rd.str()); }
} std::ifstream in(filename.str(), std::ios::binary);
std::stringstream rd;
var builtin_fout(context* ctx, gc* ngc) { if (!in.fail()) {
auto local = ctx->localr; rd << in.rdbuf();
auto filename = local[1]; }
auto source = local[2]; return ngc->newstr(rd.str());
if (!filename.is_str()) { }
return nas_err("io::fout", "\"filename\" must be string");
} var builtin_fout(context* ctx, gc* ngc) {
std::ofstream out(filename.str()); auto local = ctx->localr;
if (out.fail()) { auto filename = local[1];
return nas_err("io::fout", "cannot open <" + filename.str() + ">"); auto source = local[2];
} if (!filename.is_str()) {
out << source; return nas_err("io::fout", "\"filename\" must be string");
return nil; }
} std::ofstream out(filename.str());
if (out.fail()) {
var builtin_exists(context* ctx, gc* ngc) { return nas_err("io::fout", "cannot open <" + filename.str() + ">");
auto filename = ctx->localr[1]; }
if (!filename.is_str()) { out << source;
return zero; return nil;
} }
return access(filename.str().c_str(), F_OK)!=-1? one:zero;
} var builtin_exists(context* ctx, gc* ngc) {
auto filename = ctx->localr[1];
var builtin_open(context* ctx, gc* ngc) { if (!filename.is_str()) {
auto local = ctx->localr; return zero;
auto name = local[1]; }
auto mode = local[2]; return access(filename.str().c_str(), F_OK)!=-1? one:zero;
if (!name.is_str()) { }
return nas_err("io::open", "\"filename\" must be string");
} var builtin_open(context* ctx, gc* ngc) {
if (!mode.is_str()) { auto local = ctx->localr;
return nas_err("io::open", "\"mode\" must be string"); auto name = local[1];
} auto mode = local[2];
auto file_descriptor = fopen(name.str().c_str(), mode.str().c_str()); if (!name.is_str()) {
if (!file_descriptor) { return nas_err("io::open", "\"filename\" must be string");
return nas_err("io::open", "failed to open file <" + name.str() + ">"); }
} if (!mode.is_str()) {
var return_object = ngc->alloc(vm_type::vm_ghost); return nas_err("io::open", "\"mode\" must be string");
return_object.ghost().set( }
file_type_name, filehandle_destructor, nullptr, file_descriptor auto file_descriptor = fopen(name.str().c_str(), mode.str().c_str());
); if (!file_descriptor) {
return return_object; return nas_err("io::open", "failed to open file <" + name.str() + ">");
} }
var return_object = ngc->alloc(vm_type::vm_ghost);
var builtin_close(context* ctx, gc* ngc) { return_object.ghost().set(
var file_descriptor = ctx->localr[1]; file_type_name, filehandle_destructor, nullptr, file_descriptor
if (!file_descriptor.object_check(file_type_name)) { );
return nas_err("io::close", "not a valid filehandle"); return return_object;
} }
file_descriptor.ghost().clear();
return nil; var builtin_close(context* ctx, gc* ngc) {
} var file_descriptor = ctx->localr[1];
if (!file_descriptor.object_check(file_type_name)) {
var builtin_read(context* ctx, gc* ngc) { return nas_err("io::close", "not a valid filehandle");
auto local = ctx->localr; }
auto file_descriptor = local[1]; file_descriptor.ghost().clear();
auto buffer = local[2]; return nil;
auto length = local[3]; }
if (!file_descriptor.object_check(file_type_name)) {
return nas_err("io::read", "not a valid filehandle"); var builtin_read(context* ctx, gc* ngc) {
} auto local = ctx->localr;
if (!buffer.is_str() || buffer.val.gcobj->unmutable) { auto file_descriptor = local[1];
return nas_err("io::read", "\"buf\" must be mutable string"); auto buffer = local[2];
} auto length = local[3];
if (!length.is_num()) { if (!file_descriptor.object_check(file_type_name)) {
return nas_err("io::read", "\"len\" must be number"); return nas_err("io::read", "not a valid filehandle");
} }
if (length.num()<=0 || length.num()>=(1<<30)) { if (!buffer.is_str() || buffer.val.gcobj->unmutable) {
return nas_err("io::read", "\"len\" less than 1 or too large"); return nas_err("io::read", "\"buf\" must be mutable string");
} }
auto temp_buffer = new char[static_cast<usize>(length.num())+1]; if (!length.is_num()) {
if (!temp_buffer) { return nas_err("io::read", "\"len\" must be number");
return nas_err("io::read", "malloc failed"); }
} if (length.num()<=0 || length.num()>=(1<<30)) {
auto read_size = fread( return nas_err("io::read", "\"len\" less than 1 or too large");
temp_buffer, 1, length.num(), }
static_cast<FILE*>(file_descriptor.ghost().pointer) auto temp_buffer = new char[static_cast<usize>(length.num())+1];
); if (!temp_buffer) {
buffer.str() = temp_buffer; return nas_err("io::read", "malloc failed");
buffer.val.gcobj->unmutable = true; }
delete []temp_buffer; auto read_size = fread(
return var::num(read_size); temp_buffer, 1, length.num(),
} static_cast<FILE*>(file_descriptor.ghost().pointer)
);
var builtin_write(context* ctx, gc* ngc) { buffer.str() = temp_buffer;
auto local = ctx->localr; buffer.val.gcobj->unmutable = true;
auto file_descriptor = local[1]; delete []temp_buffer;
auto source = local[2]; return var::num(read_size);
if (!file_descriptor.object_check(file_type_name)) { }
return nas_err("io::write", "not a valid filehandle");
} var builtin_write(context* ctx, gc* ngc) {
if (!source.is_str()) { auto local = ctx->localr;
return nas_err("io::write", "\"str\" must be string"); auto file_descriptor = local[1];
} auto source = local[2];
return var::num(static_cast<f64>(fwrite( if (!file_descriptor.object_check(file_type_name)) {
source.str().c_str(), 1, source.str().length(), return nas_err("io::write", "not a valid filehandle");
static_cast<FILE*>(file_descriptor.ghost().pointer) }
))); if (!source.is_str()) {
} return nas_err("io::write", "\"str\" must be string");
}
var builtin_seek(context* ctx, gc* ngc) { return var::num(static_cast<f64>(fwrite(
auto local = ctx->localr; source.str().c_str(), 1, source.str().length(),
auto file_descriptor = local[1]; static_cast<FILE*>(file_descriptor.ghost().pointer)
auto position = local[2]; )));
auto whence = local[3]; }
if (!file_descriptor.object_check(file_type_name)) {
return nas_err("io::seek", "not a valid filehandle"); var builtin_seek(context* ctx, gc* ngc) {
} auto local = ctx->localr;
return var::num(static_cast<f64>(fseek( auto file_descriptor = local[1];
static_cast<FILE*>(file_descriptor.ghost().pointer), auto position = local[2];
position.num(), auto whence = local[3];
whence.num() if (!file_descriptor.object_check(file_type_name)) {
))); return nas_err("io::seek", "not a valid filehandle");
} }
return var::num(static_cast<f64>(fseek(
var builtin_tell(context* ctx, gc* ngc) { static_cast<FILE*>(file_descriptor.ghost().pointer),
auto file_descriptor = ctx->localr[1]; position.num(),
if (!file_descriptor.object_check(file_type_name)) { whence.num()
return nas_err("io::tell", "not a valid filehandle"); )));
} }
return var::num(static_cast<f64>(
ftell(static_cast<FILE*>(file_descriptor.ghost().pointer)) var builtin_tell(context* ctx, gc* ngc) {
)); auto file_descriptor = ctx->localr[1];
} if (!file_descriptor.object_check(file_type_name)) {
return nas_err("io::tell", "not a valid filehandle");
var builtin_readln(context* ctx, gc* ngc) { }
auto file_descriptor = ctx->localr[1]; return var::num(static_cast<f64>(
if (!file_descriptor.object_check(file_type_name)) { ftell(static_cast<FILE*>(file_descriptor.ghost().pointer))
return nas_err("io::readln", "not a valid filehandle"); ));
} }
auto result = ngc->alloc(vm_type::vm_str);
char c; var builtin_readln(context* ctx, gc* ngc) {
while((c = fgetc(static_cast<FILE*>(file_descriptor.ghost().pointer)))!=EOF) { auto file_descriptor = ctx->localr[1];
if (c=='\r') { if (!file_descriptor.object_check(file_type_name)) {
continue; return nas_err("io::readln", "not a valid filehandle");
} }
if (c=='\n') { auto result = ngc->alloc(vm_type::vm_str);
return result; char c;
} while((c = fgetc(static_cast<FILE*>(file_descriptor.ghost().pointer)))!=EOF) {
result.str().push_back(c); if (c=='\r') {
} continue;
if (result.str().length()) { }
return result; if (c=='\n') {
} return result;
return nil; }
} result.str().push_back(c);
}
var builtin_stat(context* ctx, gc* ngc) { if (result.str().length()) {
auto name = ctx->localr[1]; return result;
if (!name.is_str()) { }
return nas_err("io::stat", "\"filename\" must be string"); return nil;
} }
struct stat buffer;
if (stat(name.str().c_str(), &buffer)<0) { var builtin_stat(context* ctx, gc* ngc) {
return nas_err("io::stat", "failed to open file <" + name.str() + ">"); auto name = ctx->localr[1];
} if (!name.is_str()) {
auto result = ngc->alloc(vm_type::vm_vec); return nas_err("io::stat", "\"filename\" must be string");
result.vec().elems = { }
var::num(static_cast<f64>(buffer.st_dev)), struct stat buffer;
var::num(static_cast<f64>(buffer.st_ino)), if (stat(name.str().c_str(), &buffer)<0) {
var::num(static_cast<f64>(buffer.st_mode)), return nas_err("io::stat", "failed to open file <" + name.str() + ">");
var::num(static_cast<f64>(buffer.st_nlink)), }
var::num(static_cast<f64>(buffer.st_uid)), auto result = ngc->alloc(vm_type::vm_vec);
var::num(static_cast<f64>(buffer.st_gid)), result.vec().elems = {
var::num(static_cast<f64>(buffer.st_rdev)), var::num(static_cast<f64>(buffer.st_dev)),
var::num(static_cast<f64>(buffer.st_size)), var::num(static_cast<f64>(buffer.st_ino)),
var::num(static_cast<f64>(buffer.st_atime)), var::num(static_cast<f64>(buffer.st_mode)),
var::num(static_cast<f64>(buffer.st_mtime)), var::num(static_cast<f64>(buffer.st_nlink)),
var::num(static_cast<f64>(buffer.st_ctime)) var::num(static_cast<f64>(buffer.st_uid)),
}; var::num(static_cast<f64>(buffer.st_gid)),
return result; var::num(static_cast<f64>(buffer.st_rdev)),
} var::num(static_cast<f64>(buffer.st_size)),
var::num(static_cast<f64>(buffer.st_atime)),
var builtin_eof(context* ctx, gc* ngc) { var::num(static_cast<f64>(buffer.st_mtime)),
auto file_descriptor = ctx->localr[1]; var::num(static_cast<f64>(buffer.st_ctime))
if (!file_descriptor.object_check(file_type_name)) { };
return nas_err("io::readln", "not a valid filehandle"); return result;
} }
return var::num(static_cast<f64>(
feof(static_cast<FILE*>(file_descriptor.ghost().pointer)) var builtin_eof(context* ctx, gc* ngc) {
)); auto file_descriptor = ctx->localr[1];
} if (!file_descriptor.object_check(file_type_name)) {
return nas_err("io::readln", "not a valid filehandle");
var builtin_stdin(context* ctx, gc* ngc) { }
auto file_descriptor = ngc->alloc(vm_type::vm_ghost); return var::num(static_cast<f64>(
file_descriptor.ghost().set(file_type_name, nullptr, nullptr, stdin); feof(static_cast<FILE*>(file_descriptor.ghost().pointer))
return file_descriptor; ));
} }
var builtin_stdout(context* ctx, gc* ngc) { var builtin_stdin(context* ctx, gc* ngc) {
auto file_descriptor = ngc->alloc(vm_type::vm_ghost); auto file_descriptor = ngc->alloc(vm_type::vm_ghost);
file_descriptor.ghost().set(file_type_name, nullptr, nullptr, stdout); file_descriptor.ghost().set(file_type_name, nullptr, nullptr, stdin);
return file_descriptor; return file_descriptor;
} }
var builtin_stderr(context* ctx, gc* ngc) { var builtin_stdout(context* ctx, gc* ngc) {
auto file_descriptor = ngc->alloc(vm_type::vm_ghost); auto file_descriptor = ngc->alloc(vm_type::vm_ghost);
file_descriptor.ghost().set(file_type_name, nullptr, nullptr, stderr); file_descriptor.ghost().set(file_type_name, nullptr, nullptr, stdout);
return file_descriptor; return file_descriptor;
} }
var builtin_stderr(context* ctx, gc* ngc) {
nasal_builtin_table io_lib_native[] = { auto file_descriptor = ngc->alloc(vm_type::vm_ghost);
{"__readfile", builtin_readfile}, file_descriptor.ghost().set(file_type_name, nullptr, nullptr, stderr);
{"__fout", builtin_fout}, return file_descriptor;
{"__exists", builtin_exists}, }
{"__open", builtin_open},
{"__close", builtin_close},
{"__read", builtin_read}, nasal_builtin_table io_lib_native[] = {
{"__write", builtin_write}, {"__readfile", builtin_readfile},
{"__seek", builtin_seek}, {"__fout", builtin_fout},
{"__tell", builtin_tell}, {"__exists", builtin_exists},
{"__readln", builtin_readln}, {"__open", builtin_open},
{"__stat", builtin_stat}, {"__close", builtin_close},
{"__eof", builtin_eof}, {"__read", builtin_read},
{"__stdin", builtin_stdin}, {"__write", builtin_write},
{"__stdout", builtin_stdout}, {"__seek", builtin_seek},
{"__stderr", builtin_stderr}, {"__tell", builtin_tell},
{nullptr, nullptr} {"__readln", builtin_readln},
}; {"__stat", builtin_stat},
{"__eof", builtin_eof},
} {"__stdin", builtin_stdin},
{"__stdout", builtin_stdout},
{"__stderr", builtin_stderr},
{nullptr, nullptr}
};
}

View File

@ -1,41 +1,35 @@
#pragma once #pragma once
#include "nasal.h" #include "nasal.h"
#include "nasal_gc.h" #include "nasal_gc.h"
#include "nasal_builtin.h" #include "nasal_builtin.h"
#include <sys/stat.h> #ifndef _MSC_VER
#include <unistd.h>
#ifndef _MSC_VER #else
#include <unistd.h> #include <io.h>
#else #endif
#include <io.h>
#endif namespace nasal {
#ifdef _MSC_VER void filehandle_destructor(void*);
#define F_OK 0 // fuck msc
#endif var builtin_readfile(context*, gc*);
var builtin_fout(context*, gc*);
namespace nasal { var builtin_exists(context*, gc*);
var builtin_open(context*, gc*);
void filehandle_destructor(void*); var builtin_close(context*, gc*);
var builtin_read(context*, gc*);
var builtin_readfile(context*, gc*); var builtin_write(context*, gc*);
var builtin_fout(context*, gc*); var builtin_seek(context*, gc*);
var builtin_exists(context*, gc*); var builtin_tell(context*, gc*);
var builtin_open(context*, gc*); var builtin_readln(context*, gc*);
var builtin_close(context*, gc*); var builtin_stat(context*, gc*);
var builtin_read(context*, gc*); var builtin_eof(context*, gc*);
var builtin_write(context*, gc*); var builtin_stdin(context*, gc*);
var builtin_seek(context*, gc*); var builtin_stdout(context*, gc*);
var builtin_tell(context*, gc*); var builtin_stderr(context*, gc*);
var builtin_readln(context*, gc*);
var builtin_stat(context*, gc*); extern nasal_builtin_table io_lib_native[];
var builtin_eof(context*, gc*);
var builtin_stdin(context*, gc*); }
var builtin_stdout(context*, gc*);
var builtin_stderr(context*, gc*);
extern nasal_builtin_table io_lib_native[];
}

View File

@ -1,399 +1,403 @@
#include "nasal_import.h" #include "nasal_import.h"
#include "symbol_finder.h" #include "symbol_finder.h"
#include <memory> #include <memory>
#include <unordered_set> #include <unordered_set>
namespace nasal { #ifdef _MSC_VER
#define F_OK 0 // fuck msc
linker::linker(): show_path_flag(false), library_loaded(false), this_file("") { #endif
const auto seperator= is_windows()? ';':':';
const auto PATH = std::string(getenv("PATH")); namespace nasal {
usize last = 0, position = PATH.find(seperator, 0);
while(position!=std::string::npos) { linker::linker(): show_path_flag(false), library_loaded(false), this_file("") {
std::string dirpath = PATH.substr(last, position-last); const auto seperator= is_windows()? ';':':';
if (dirpath.length()) { const auto PATH = std::string(getenv("PATH"));
envpath.push_back(dirpath); usize last = 0, position = PATH.find(seperator, 0);
} while(position!=std::string::npos) {
last = position+1; std::string dirpath = PATH.substr(last, position-last);
position = PATH.find(seperator, last); if (dirpath.length()) {
} envpath.push_back(dirpath);
if (last!=PATH.length()) { }
envpath.push_back(PATH.substr(last)); last = position+1;
} position = PATH.find(seperator, last);
} }
if (last!=PATH.length()) {
std::string linker::get_path(expr* node) { envpath.push_back(PATH.substr(last));
if (node->get_type()==expr_type::ast_use) { }
auto file_relative_path = std::string(""); }
const auto& path = reinterpret_cast<use_stmt*>(node)->get_path();
for(auto i : path) { std::string linker::get_path(expr* node) {
file_relative_path += i->get_name(); if (node->get_type()==expr_type::ast_use) {
if (i!=path.back()) { auto file_relative_path = std::string("");
file_relative_path += (is_windows()? "\\":"/"); const auto& path = reinterpret_cast<use_stmt*>(node)->get_path();
} for(auto i : path) {
} file_relative_path += i->get_name();
return file_relative_path + ".nas"; if (i!=path.back()) {
} file_relative_path += (is_windows()? "\\":"/");
auto call_node = reinterpret_cast<call_expr*>(node); }
auto arguments = reinterpret_cast<call_function*>(call_node->get_calls()[0]); }
auto content = reinterpret_cast<string_literal*>(arguments->get_argument()[0]); return file_relative_path + ".nas";
return content->get_content(); }
} auto call_node = reinterpret_cast<call_expr*>(node);
auto arguments = reinterpret_cast<call_function*>(call_node->get_calls()[0]);
std::string linker::find_real_file_path( auto content = reinterpret_cast<string_literal*>(arguments->get_argument()[0]);
const std::string& filename, const span& location) { return content->get_content();
// first add file name itself into the file path }
std::vector<std::string> path_list = {filename};
std::string linker::find_real_file_path(
// generate search path from environ path const std::string& filename, const span& location) {
for(const auto& p : envpath) { // first add file name itself into the file path
path_list.push_back(p + (is_windows()? "\\":"/") + filename); std::vector<std::string> path_list = {filename};
}
// generate search path from environ path
// search file for(const auto& p : envpath) {
for(const auto& path : path_list) { path_list.push_back(p + (is_windows()? "\\":"/") + filename);
if (access(path.c_str(), F_OK)!=-1) { }
return path;
} // search file
} for(const auto& path : path_list) {
if (access(path.c_str(), F_OK)!=-1) {
// we will find lib.nas in nasal std directory return path;
if (filename=="lib.nas") { }
return is_windows()? }
find_real_file_path("std\\lib.nas", location):
find_real_file_path("std/lib.nas", location); // we will find lib.nas in nasal std directory
} if (filename=="lib.nas") {
if (!show_path_flag) { return is_windows()?
err.err("link", find_real_file_path("std\\lib.nas", location):
"in <" + location.file + ">: " + find_real_file_path("std/lib.nas", location);
"cannot find file <" + filename + ">, " + }
"use <-d> to get detail search path" if (!show_path_flag) {
); err.err("link",
return ""; "in <" + location.file + ">: " +
} "cannot find file <" + filename + ">, " +
auto path_list_info = std::string(""); "use <-d> to get detail search path"
for(const auto& path : path_list) { );
path_list_info += " -> " + path + "\n"; return "";
} }
err.err("link", auto path_list_info = std::string("");
"in <" + location.file + ">: " + for(const auto& path : path_list) {
"cannot find file <" + filename + path_list_info += " -> " + path + "\n";
"> in these paths:\n" + path_list_info }
); err.err("link",
return ""; "in <" + location.file + ">: " +
} "cannot find file <" + filename +
"> in these paths:\n" + path_list_info
bool linker::import_check(expr* node) { );
if (node->get_type()==expr_type::ast_use) { return "";
return true; }
}
/* bool linker::import_check(expr* node) {
call if (node->get_type()==expr_type::ast_use) {
|_id:import return true;
|_call_func }
|_string:'filename' /*
*/ call
if (node->get_type()!=expr_type::ast_call) { |_id:import
return false; |_call_func
} |_string:'filename'
auto call_node = reinterpret_cast<call_expr*>(node); */
auto first_expr = call_node->get_first(); if (node->get_type()!=expr_type::ast_call) {
if (first_expr->get_type()!=expr_type::ast_id) { return false;
return false; }
} auto call_node = reinterpret_cast<call_expr*>(node);
if (reinterpret_cast<identifier*>(first_expr)->get_name()!="import") { auto first_expr = call_node->get_first();
return false; if (first_expr->get_type()!=expr_type::ast_id) {
} return false;
if (!call_node->get_calls().size()) { }
return false; if (reinterpret_cast<identifier*>(first_expr)->get_name()!="import") {
} return false;
}
// import("xxx"); if (!call_node->get_calls().size()) {
if (call_node->get_calls().size()!=1) { return false;
return false; }
}
auto maybe_func_call = call_node->get_calls()[0]; // import("xxx");
if (maybe_func_call->get_type()!=expr_type::ast_callf) { if (call_node->get_calls().size()!=1) {
return false; return false;
} }
auto func_call = reinterpret_cast<call_function*>(maybe_func_call); auto maybe_func_call = call_node->get_calls()[0];
if (func_call->get_argument().size()!=1) { if (maybe_func_call->get_type()!=expr_type::ast_callf) {
return false; return false;
} }
if (func_call->get_argument()[0]->get_type()!=expr_type::ast_str) { auto func_call = reinterpret_cast<call_function*>(maybe_func_call);
return false; if (func_call->get_argument().size()!=1) {
} return false;
return true; }
} if (func_call->get_argument()[0]->get_type()!=expr_type::ast_str) {
return false;
bool linker::check_exist_or_record_file(const std::string& file) { }
// avoid importing the same file return true;
for(const auto& name : imported_files) { }
if (file==name) {
return true; bool linker::check_exist_or_record_file(const std::string& file) {
} // avoid importing the same file
} for(const auto& name : imported_files) {
imported_files.push_back(file); if (file==name) {
return false; return true;
} }
}
bool linker::check_self_import(const std::string& file) { imported_files.push_back(file);
for(const auto& name : module_load_stack) { return false;
if (file==name) { }
return true;
} bool linker::check_self_import(const std::string& file) {
} for(const auto& name : module_load_stack) {
return false; if (file==name) {
} return true;
}
std::string linker::generate_self_import_path(const std::string& filename) { }
std::string res = ""; return false;
for(const auto& i : module_load_stack) { }
res += "[" + i + "] -> ";
} std::string linker::generate_self_import_path(const std::string& filename) {
return res + "[" + filename + "]"; std::string res = "";
} for(const auto& i : module_load_stack) {
res += "[" + i + "] -> ";
void linker::link(code_block* new_tree_root, code_block* old_tree_root) { }
// add children of add_root to the back of root return res + "[" + filename + "]";
for(auto& i : old_tree_root->get_expressions()) { }
new_tree_root->add_expression(i);
} void linker::link(code_block* new_tree_root, code_block* old_tree_root) {
// clean old root // add children of add_root to the back of root
old_tree_root->get_expressions().clear(); for(auto& i : old_tree_root->get_expressions()) {
} new_tree_root->add_expression(i);
}
code_block* linker::import_regular_file( // clean old root
expr* node, std::unordered_set<std::string>& used_modules) { old_tree_root->get_expressions().clear();
// get filename }
auto filename = get_path(node);
code_block* linker::import_regular_file(
// avoid infinite loading loop expr* node, std::unordered_set<std::string>& used_modules) {
filename = find_real_file_path(filename, node->get_location()); // get filename
// if get empty string(error) or this file is used before, do not parse auto filename = get_path(node);
if (!filename.length() || used_modules.count(filename)) {
return new code_block({0, 0, 0, 0, filename}); // avoid infinite loading loop
} filename = find_real_file_path(filename, node->get_location());
// if get empty string(error) or this file is used before, do not parse
// check self import, avoid infinite loading loop if (!filename.length() || used_modules.count(filename)) {
if (check_self_import(filename)) { return new code_block({0, 0, 0, 0, filename});
err.err("link", }
"self-referenced module <" + filename + ">:\n" +
" reference path: " + generate_self_import_path(filename) // check self import, avoid infinite loading loop
); if (check_self_import(filename)) {
return new code_block({0, 0, 0, 0, filename}); err.err("link",
} "self-referenced module <" + filename + ">:\n" +
check_exist_or_record_file(filename); " reference path: " + generate_self_import_path(filename)
);
module_load_stack.push_back(filename); return new code_block({0, 0, 0, 0, filename});
// start importing... }
lexer nasal_lexer; check_exist_or_record_file(filename);
parse nasal_parser;
if (nasal_lexer.scan(filename).geterr()) { module_load_stack.push_back(filename);
err.err("link", "error occurred when analysing <" + filename + ">"); // start importing...
return new code_block({0, 0, 0, 0, filename}); lexer nasal_lexer;
} parse nasal_parser;
if (nasal_parser.compile(nasal_lexer).geterr()) { if (nasal_lexer.scan(filename).geterr()) {
err.err("link", "error occurred when analysing <" + filename + ">"); err.err("link", "error occurred when analysing <" + filename + ">");
return new code_block({0, 0, 0, 0, filename}); return new code_block({0, 0, 0, 0, filename});
} }
// swap result out if (nasal_parser.compile(nasal_lexer).geterr()) {
auto parse_result = nasal_parser.swap(nullptr); err.err("link", "error occurred when analysing <" + filename + ">");
return new code_block({0, 0, 0, 0, filename});
// check if parse result has 'import' }
auto result = load(parse_result, filename); // swap result out
module_load_stack.pop_back(); auto parse_result = nasal_parser.swap(nullptr);
return result;
} // check if parse result has 'import'
auto result = load(parse_result, filename);
code_block* linker::import_nasal_lib() { module_load_stack.pop_back();
auto path = find_real_file_path( return result;
"lib.nas", {0, 0, 0, 0, this_file} }
);
if (!path.length()) { code_block* linker::import_nasal_lib() {
return new code_block({0, 0, 0, 0, path}); auto path = find_real_file_path(
} "lib.nas", {0, 0, 0, 0, this_file}
);
// avoid infinite loading library if (!path.length()) {
if (check_exist_or_record_file(path)) { return new code_block({0, 0, 0, 0, path});
return new code_block({0, 0, 0, 0, path}); }
}
// avoid infinite loading library
// start importing... if (check_exist_or_record_file(path)) {
lexer nasal_lexer; return new code_block({0, 0, 0, 0, path});
parse nasal_parser; }
if (nasal_lexer.scan(path).geterr()) {
err.err("link", // start importing...
"error occurred when analysing library <" + path + ">" lexer nasal_lexer;
); parse nasal_parser;
return new code_block({0, 0, 0, 0, path}); if (nasal_lexer.scan(path).geterr()) {
} err.err("link",
if (nasal_parser.compile(nasal_lexer).geterr()) { "error occurred when analysing library <" + path + ">"
err.err("link", );
"error occurred when analysing library <" + path + ">" return new code_block({0, 0, 0, 0, path});
); }
return new code_block({0, 0, 0, 0, path}); if (nasal_parser.compile(nasal_lexer).geterr()) {
} err.err("link",
// swap result out "error occurred when analysing library <" + path + ">"
auto parse_result = nasal_parser.swap(nullptr); );
// check if library has 'import' (in fact it should not) return new code_block({0, 0, 0, 0, path});
return load(parse_result, path); }
} // swap result out
auto parse_result = nasal_parser.swap(nullptr);
std::string linker::generate_module_name(const std::string& file_path) { // check if library has 'import' (in fact it should not)
auto error_name = "module@[" + file_path + "]"; return load(parse_result, path);
if (!file_path.length()) { }
return error_name;
} std::string linker::generate_module_name(const std::string& file_path) {
auto error_name = "module@[" + file_path + "]";
// check file suffix and get file suffix position if (!file_path.length()) {
auto suffix_position = file_path.find(".nas"); return error_name;
if (suffix_position==std::string::npos) { }
err.warn("link",
"get invalid module name from <" + file_path + ">, " + // check file suffix and get file suffix position
"will not be easily accessed. " + auto suffix_position = file_path.find(".nas");
"\".nas\" suffix is required." if (suffix_position==std::string::npos) {
); err.warn("link",
return error_name; "get invalid module name from <" + file_path + ">, " +
} "will not be easily accessed. " +
if (suffix_position+4!=file_path.length()) { "\".nas\" suffix is required."
err.warn("link", );
"get invalid module name from <" + file_path + ">, " + return error_name;
"will not be easily accessed. " + }
"only one \".nas\" suffix is required in the path." if (suffix_position+4!=file_path.length()) {
); err.warn("link",
return error_name; "get invalid module name from <" + file_path + ">, " +
} "will not be easily accessed. " +
"only one \".nas\" suffix is required in the path."
// only get the file name as module name, directory path is not included );
auto split_position = file_path.find_last_of("/"); return error_name;
// find "\\" in windows platform }
if (split_position==std::string::npos) {
split_position = file_path.find_last_of("\\"); // only get the file name as module name, directory path is not included
} auto split_position = file_path.find_last_of("/");
// find "\\" in windows platform
// split file path to get module name if (split_position==std::string::npos) {
auto module_name = split_position==std::string::npos? split_position = file_path.find_last_of("\\");
file_path.substr(0, suffix_position): }
file_path.substr(split_position+1, suffix_position-split_position-1);
// split file path to get module name
// check validation of module name auto module_name = split_position==std::string::npos?
if (!module_name.length()) { file_path.substr(0, suffix_position):
err.warn("link", file_path.substr(split_position+1, suffix_position-split_position-1);
"get empty module name from <" + file_path + ">, " +
"will not be easily accessed." // check validation of module name
); if (!module_name.length()) {
return module_name; err.warn("link",
} "get empty module name from <" + file_path + ">, " +
if (std::isdigit(module_name[0]) || "will not be easily accessed."
module_name.find(".")!=std::string::npos || );
module_name.find("-")!=std::string::npos) { return module_name;
err.warn("link", }
"get module <" + module_name + "> from <" + file_path + ">, " + if (std::isdigit(module_name[0]) ||
"will not be easily accessed." module_name.find(".")!=std::string::npos ||
); module_name.find("-")!=std::string::npos) {
} err.warn("link",
return module_name; "get module <" + module_name + "> from <" + file_path + ">, " +
} "will not be easily accessed."
);
return_expr* linker::generate_module_return(code_block* block) { }
auto finder = std::unique_ptr<symbol_finder>(new symbol_finder); return module_name;
auto result = new return_expr(block->get_location()); }
auto value = new hash_expr(block->get_location());
result->set_value(value); return_expr* linker::generate_module_return(code_block* block) {
for(const auto& i : finder->do_find(block)) { auto finder = std::unique_ptr<symbol_finder>(new symbol_finder);
auto pair = new hash_pair(block->get_location()); auto result = new return_expr(block->get_location());
// do not export symbol begins with '_' auto value = new hash_expr(block->get_location());
if (i.name.length() && i.name[0]=='_') { result->set_value(value);
continue; for(const auto& i : finder->do_find(block)) {
} auto pair = new hash_pair(block->get_location());
pair->set_name(i.name); // do not export symbol begins with '_'
pair->set_value(new identifier(block->get_location(), i.name)); if (i.name.length() && i.name[0]=='_') {
value->add_member(pair); continue;
} }
return result; pair->set_name(i.name);
} pair->set_value(new identifier(block->get_location(), i.name));
value->add_member(pair);
definition_expr* linker::generate_module_definition(code_block* block) { }
auto def = new definition_expr(block->get_location()); return result;
def->set_identifier(new identifier( }
block->get_location(),
generate_module_name(block->get_location().file) definition_expr* linker::generate_module_definition(code_block* block) {
)); auto def = new definition_expr(block->get_location());
def->set_identifier(new identifier(
auto call = new call_expr(block->get_location()); block->get_location(),
auto func = new function(block->get_location()); generate_module_name(block->get_location().file)
func->set_code_block(block); ));
func->get_code_block()->add_expression(generate_module_return(block));
call->set_first(func); auto call = new call_expr(block->get_location());
call->add_call(new call_function(block->get_location())); auto func = new function(block->get_location());
func->set_code_block(block);
def->set_value(call); func->get_code_block()->add_expression(generate_module_return(block));
return def; call->set_first(func);
} call->add_call(new call_function(block->get_location()));
code_block* linker::load(code_block* program_root, const std::string& filename) { def->set_value(call);
auto tree = new code_block({0, 0, 0, 0, filename}); return def;
// load library, this ast will be linked with root directly }
// so no extra namespace is generated
if (!library_loaded) { code_block* linker::load(code_block* program_root, const std::string& filename) {
auto nasal_lib_code_block = import_nasal_lib(); auto tree = new code_block({0, 0, 0, 0, filename});
// insert nasal lib code to the back of tree // load library, this ast will be linked with root directly
link(tree, nasal_lib_code_block); // so no extra namespace is generated
delete nasal_lib_code_block; if (!library_loaded) {
library_loaded = true; auto nasal_lib_code_block = import_nasal_lib();
} // insert nasal lib code to the back of tree
link(tree, nasal_lib_code_block);
// load imported modules delete nasal_lib_code_block;
std::unordered_set<std::string> used_modules = {}; library_loaded = true;
for(auto& import_node : program_root->get_expressions()) { }
if (!import_check(import_node)) {
break; // load imported modules
} std::unordered_set<std::string> used_modules = {};
// parse file and get ast for(auto& import_node : program_root->get_expressions()) {
auto module_code_block = import_regular_file(import_node, used_modules); if (!import_check(import_node)) {
auto replace_node = new null_expr(import_node->get_location()); break;
// after importing the regular file as module, delete this node }
delete import_node; // parse file and get ast
// and replace the node with null_expr node auto module_code_block = import_regular_file(import_node, used_modules);
import_node = replace_node; auto replace_node = new null_expr(import_node->get_location());
// after importing the regular file as module, delete this node
// avoid repeatedly importing the same module delete import_node;
const auto& module_path = module_code_block->get_location().file; // and replace the node with null_expr node
if (used_modules.count(module_path)) { import_node = replace_node;
delete module_code_block;
continue; // avoid repeatedly importing the same module
} const auto& module_path = module_code_block->get_location().file;
if (used_modules.count(module_path)) {
// then we generate a function warping the code block, delete module_code_block;
// and export the necessary global symbols in this code block continue;
// by generate a return statement, with a hashmap return value }
used_modules.insert(module_path);
tree->add_expression(generate_module_definition(module_code_block)); // then we generate a function warping the code block,
} // and export the necessary global symbols in this code block
// by generate a return statement, with a hashmap return value
// insert program root to the back of tree used_modules.insert(module_path);
link(tree, program_root); tree->add_expression(generate_module_definition(module_code_block));
return tree; }
}
// insert program root to the back of tree
const error& linker::link( link(tree, program_root);
parse& parse, const std::string& self, bool spath = false) { return tree;
// switch for showing path when errors occur }
show_path_flag = spath;
const error& linker::link(
// initializing file map parse& parse, const std::string& self, bool spath = false) {
this_file = self; // switch for showing path when errors occur
imported_files = {self}; show_path_flag = spath;
module_load_stack = {self};
// initializing file map
// scan root and import files this_file = self;
// then generate a new ast and return to import_ast imported_files = {self};
auto new_tree_root = load(parse.tree(), self); module_load_stack = {self};
auto old_tree_root = parse.swap(new_tree_root);
delete old_tree_root; // scan root and import files
return err; // then generate a new ast and return to import_ast
} auto new_tree_root = load(parse.tree(), self);
auto old_tree_root = parse.swap(new_tree_root);
} delete old_tree_root;
return err;
}
}

View File

@ -1,59 +1,55 @@
#pragma once #pragma once
#ifndef _MSC_VER #ifndef _MSC_VER
#include <unistd.h> #include <unistd.h>
#else #else
#define _CRT_SECURE_NO_DEPRECATE 1 #define _CRT_SECURE_NO_DEPRECATE 1
#define _CRT_NONSTDC_NO_DEPRECATE 1 #define _CRT_NONSTDC_NO_DEPRECATE 1
#include <io.h> #include <io.h>
#endif #endif
#ifdef _MSC_VER #include "nasal.h"
#define F_OK 0 #include "nasal_ast.h"
#endif #include "nasal_lexer.h"
#include "nasal_parse.h"
#include "nasal.h" #include "symbol_finder.h"
#include "nasal_ast.h"
#include "nasal_lexer.h" #include <cstring>
#include "nasal_parse.h" #include <sstream>
#include "symbol_finder.h" #include <vector>
#include <unordered_set>
#include <cstring>
#include <sstream> namespace nasal {
#include <vector>
#include <unordered_set> class linker {
private:
namespace nasal { bool show_path_flag;
bool library_loaded;
class linker { std::string this_file;
private: error err;
bool show_path_flag; std::vector<std::string> imported_files;
bool library_loaded; std::vector<std::string> module_load_stack;
std::string this_file; std::vector<std::string> envpath;
error err;
std::vector<std::string> imported_files; private:
std::vector<std::string> module_load_stack; bool import_check(expr*);
std::vector<std::string> envpath; bool check_exist_or_record_file(const std::string&);
bool check_self_import(const std::string&);
private: std::string generate_self_import_path(const std::string&);
bool import_check(expr*); void link(code_block*, code_block*);
bool check_exist_or_record_file(const std::string&); std::string get_path(expr*);
bool check_self_import(const std::string&); std::string find_real_file_path(const std::string&, const span&);
std::string generate_self_import_path(const std::string&); code_block* import_regular_file(expr*, std::unordered_set<std::string>&);
void link(code_block*, code_block*); code_block* import_nasal_lib();
std::string get_path(expr*); std::string generate_module_name(const std::string&);
std::string find_real_file_path(const std::string&, const span&); return_expr* generate_module_return(code_block*);
code_block* import_regular_file(expr*, std::unordered_set<std::string>&); definition_expr* generate_module_definition(code_block*);
code_block* import_nasal_lib(); code_block* load(code_block*, const std::string&);
std::string generate_module_name(const std::string&);
return_expr* generate_module_return(code_block*); public:
definition_expr* generate_module_definition(code_block*); linker();
code_block* load(code_block*, const std::string&); const error& link(parse&, const std::string&, bool);
const auto& get_file_list() const {return imported_files;}
public: };
linker();
const error& link(parse&, const std::string&, bool); }
const auto& get_file_list() const {return imported_files;}
};
}

View File

@ -1,393 +1,396 @@
#ifdef _MSC_VER #ifdef _MSC_VER
#pragma warning (disable:4244) #pragma warning (disable:4244)
#pragma warning (disable:4267) #pragma warning (disable:4267)
#pragma warning (disable:4102) #pragma warning (disable:4102)
#endif #endif
#include "nasal_lexer.h" #include "nasal_lexer.h"
#include "repl.h" #include "repl.h"
namespace nasal { namespace nasal {
bool lexer::skip(char c) { bool lexer::skip(char c) {
return c==' ' || c=='\n' || c=='\t' || c=='\r' || c==0; return c==' ' || c=='\n' || c=='\t' || c=='\r' || c==0;
} }
bool lexer::is_id(char c) { bool lexer::is_id(char c) {
return (c=='_') || std::isalpha(c) || (c<0); return (c=='_') || std::isalpha(c) || (c<0);
} }
bool lexer::is_hex(char c) { bool lexer::is_hex(char c) {
return std::isxdigit(c); return std::isxdigit(c);
} }
bool lexer::is_oct(char c) { bool lexer::is_oct(char c) {
return '0'<=c && c<='7'; return '0'<=c && c<='7';
} }
bool lexer::is_dec(char c) { bool lexer::is_dec(char c) {
return std::isdigit(c); return std::isdigit(c);
} }
bool lexer::is_str(char c) { bool lexer::is_str(char c) {
return c=='\'' || c=='\"' || c=='`'; return c=='\'' || c=='\"' || c=='`';
} }
bool lexer::is_single_opr(char c) { bool lexer::is_single_opr(char c) {
return ( return (
c=='(' || c==')' || c=='[' || c==']' || c=='(' || c==')' || c=='[' || c==']' ||
c=='{' || c=='}' || c==',' || c==';' || c=='{' || c=='}' || c==',' || c==';' ||
c==':' || c=='?' || c=='`' || c=='@' || c==':' || c=='?' || c=='`' || c=='@' ||
c=='%' || c=='$' || c=='\\' c=='%' || c=='$' || c=='\\'
); );
} }
bool lexer::is_calc_opr(char c) { bool lexer::is_calc_opr(char c) {
return ( return (
c=='=' || c=='+' || c=='-' || c=='*' || c=='=' || c=='+' || c=='-' || c=='*' ||
c=='!' || c=='/' || c=='<' || c=='>' || c=='!' || c=='/' || c=='<' || c=='>' ||
c=='~' || c=='|' || c=='&' || c=='^' c=='~' || c=='|' || c=='&' || c=='^'
); );
} }
void lexer::skip_note() { void lexer::skip_note() {
// avoid note, after this process ptr will point to '\n' // avoid note, after this process ptr will point to '\n'
// so next loop line counter+1 // so next loop line counter+1
while(++ptr<res.size() && res[ptr]!='\n') {} while(++ptr<res.size() && res[ptr]!='\n') {}
} }
void lexer::err_char() { void lexer::err_char() {
++column; ++column;
char c = res[ptr++]; char c = res[ptr++];
err.err("lexer", err.err("lexer",
{line, column-1, line, column, filename}, {line, column-1, line, column, filename},
"invalid character 0x"+chrhex(c) "invalid character 0x"+chrhex(c)
); );
++invalid_char; ++invalid_char;
} }
void lexer::open(const std::string& file) { void lexer::open(const std::string& file) {
if (repl::info::instance()->in_repl_mode && if (repl::info::instance()->in_repl_mode &&
repl::info::instance()->repl_file_name==file) { repl::info::instance()->repl_file_name==file) {
err.load(file); err.load(file);
filename = file; filename = file;
res = repl::info::instance()->repl_file_source; res = repl::info::instance()->repl_file_source;
return; return;
} }
// check file exsits and it is a regular file // check file exsits and it is a regular file
struct stat buffer; #ifdef _MSC_VER
if (stat(file.c_str(), &buffer)==0 && !S_ISREG(buffer.st_mode)) { #define S_ISREG(m) (((m)&0xF000)==0x8000)
err.err("lexer", "<"+file+"> is not a regular file"); #endif
err.chkerr(); struct stat buffer;
} if (stat(file.c_str(), &buffer)==0 && !S_ISREG(buffer.st_mode)) {
err.err("lexer", "<"+file+"> is not a regular file");
// load err.chkerr();
filename = file; }
std::ifstream in(file, std::ios::binary);
if (in.fail()) { // load
err.err("lexer", "failed to open <" + file + ">"); filename = file;
res = ""; std::ifstream in(file, std::ios::binary);
return; if (in.fail()) {
} err.err("lexer", "failed to open <" + file + ">");
err.load(file); res = "";
std::stringstream ss; return;
ss << in.rdbuf(); }
res = ss.str(); err.load(file);
} std::stringstream ss;
ss << in.rdbuf();
tok lexer::get_type(const std::string& str) { res = ss.str();
return typetbl.count(str)? typetbl.at(str):tok::null; }
}
tok lexer::get_type(const std::string& str) {
std::string lexer::utf8_gen() { return typetbl.count(str)? typetbl.at(str):tok::null;
std::string str = ""; }
while(ptr<res.size() && res[ptr]<0) {
std::string tmp = ""; std::string lexer::utf8_gen() {
u32 nbytes = utf8_hdchk(res[ptr]); std::string str = "";
if (!nbytes) { while(ptr<res.size() && res[ptr]<0) {
++ptr; std::string tmp = "";
++column; u32 nbytes = utf8_hdchk(res[ptr]);
continue; if (!nbytes) {
} ++ptr;
++column;
tmp += res[ptr++]; continue;
for(u32 i = 0; i<nbytes; ++i, ++ptr) { }
if (ptr<res.size() && (res[ptr]&0xc0)==0x80) {
tmp += res[ptr]; tmp += res[ptr++];
} for(u32 i = 0; i<nbytes; ++i, ++ptr) {
} if (ptr<res.size() && (res[ptr]&0xc0)==0x80) {
tmp += res[ptr];
// utf8 character's total length is 1+nbytes }
if (tmp.length()!=1+nbytes) { }
++column;
std::string utf_info = "0x"+chrhex(tmp[0]); // utf8 character's total length is 1+nbytes
for(u32 i = 1; i<tmp.size(); ++i) { if (tmp.length()!=1+nbytes) {
utf_info += " 0x"+chrhex(tmp[i]); ++column;
} std::string utf_info = "0x"+chrhex(tmp[0]);
err.err("lexer", for(u32 i = 1; i<tmp.size(); ++i) {
{line, column-1, line, column, filename}, utf_info += " 0x"+chrhex(tmp[i]);
"invalid utf-8 <"+utf_info+">" }
); err.err("lexer",
++invalid_char; {line, column-1, line, column, filename},
} "invalid utf-8 <"+utf_info+">"
str += tmp; );
// may have some problems because not all the unicode takes 2 space ++invalid_char;
column += 2; }
} str += tmp;
return str; // may have some problems because not all the unicode takes 2 space
} column += 2;
}
token lexer::id_gen() { return str;
u32 begin_line = line; }
u32 begin_column = column;
std::string str = ""; token lexer::id_gen() {
while(ptr<res.size() && (is_id(res[ptr]) || is_dec(res[ptr]))) { u32 begin_line = line;
if (res[ptr]<0) { // utf-8 u32 begin_column = column;
str += utf8_gen(); std::string str = "";
} else { // ascii while(ptr<res.size() && (is_id(res[ptr]) || is_dec(res[ptr]))) {
str += res[ptr++]; if (res[ptr]<0) { // utf-8
++column; str += utf8_gen();
} } else { // ascii
} str += res[ptr++];
tok type = get_type(str); ++column;
return { }
{begin_line, begin_column, line, column, filename}, }
(type!=tok::null)? type:tok::id, str tok type = get_type(str);
}; return {
} {begin_line, begin_column, line, column, filename},
(type!=tok::null)? type:tok::id, str
token lexer::num_gen() { };
u32 begin_line = line; }
u32 begin_column = column;
// generate hex number token lexer::num_gen() {
if (ptr+1<res.size() && res[ptr]=='0' && res[ptr+1]=='x') { u32 begin_line = line;
std::string str = "0x"; u32 begin_column = column;
ptr += 2; // generate hex number
while(ptr<res.size() && is_hex(res[ptr])) { if (ptr+1<res.size() && res[ptr]=='0' && res[ptr+1]=='x') {
str += res[ptr++]; std::string str = "0x";
} ptr += 2;
column += str.length(); while(ptr<res.size() && is_hex(res[ptr])) {
// "0x" str += res[ptr++];
if (str.length()<3) { }
err.err("lexer", column += str.length();
{begin_line, begin_column, line, column, filename}, // "0x"
"invalid number `"+str+"`" if (str.length()<3) {
); err.err("lexer",
} {begin_line, begin_column, line, column, filename},
return {{begin_line, begin_column, line, column, filename}, tok::num, str}; "invalid number `"+str+"`"
} else if (ptr+1<res.size() && res[ptr]=='0' && res[ptr+1]=='o') { // generate oct number );
std::string str = "0o"; }
ptr += 2; return {{begin_line, begin_column, line, column, filename}, tok::num, str};
while(ptr<res.size() && is_oct(res[ptr])) { } else if (ptr+1<res.size() && res[ptr]=='0' && res[ptr+1]=='o') { // generate oct number
str += res[ptr++]; std::string str = "0o";
} ptr += 2;
bool erfmt = false; while(ptr<res.size() && is_oct(res[ptr])) {
while(ptr<res.size() && (is_dec(res[ptr]) || is_hex(res[ptr]))) { str += res[ptr++];
erfmt = true; }
str += res[ptr++]; bool erfmt = false;
} while(ptr<res.size() && (is_dec(res[ptr]) || is_hex(res[ptr]))) {
column += str.length(); erfmt = true;
if (str.length()==2 || erfmt) { str += res[ptr++];
err.err("lexer", }
{begin_line, begin_column, line, column, filename}, column += str.length();
"invalid number `"+str+"`" if (str.length()==2 || erfmt) {
); err.err("lexer",
} {begin_line, begin_column, line, column, filename},
return {{begin_line, begin_column, line, column, filename}, tok::num, str}; "invalid number `"+str+"`"
} );
// generate dec number }
// dec number -> [0~9][0~9]*(.[0~9]*)(e|E(+|-)0|[1~9][0~9]*) return {{begin_line, begin_column, line, column, filename}, tok::num, str};
std::string str = ""; }
while(ptr<res.size() && is_dec(res[ptr])) { // generate dec number
str += res[ptr++]; // dec number -> [0~9][0~9]*(.[0~9]*)(e|E(+|-)0|[1~9][0~9]*)
} std::string str = "";
if (ptr<res.size() && res[ptr]=='.') { while(ptr<res.size() && is_dec(res[ptr])) {
str += res[ptr++]; str += res[ptr++];
while(ptr<res.size() && is_dec(res[ptr])) { }
str += res[ptr++]; if (ptr<res.size() && res[ptr]=='.') {
} str += res[ptr++];
// "xxxx." is not a correct number while(ptr<res.size() && is_dec(res[ptr])) {
if (str.back()=='.') { str += res[ptr++];
column += str.length(); }
err.err("lexer", // "xxxx." is not a correct number
{begin_line, begin_column, line, column, filename}, if (str.back()=='.') {
"invalid number `"+str+"`" column += str.length();
); err.err("lexer",
return {{begin_line, begin_column, line, column, filename}, tok::num, "0"}; {begin_line, begin_column, line, column, filename},
} "invalid number `"+str+"`"
} );
if (ptr<res.size() && (res[ptr]=='e' || res[ptr]=='E')) { return {{begin_line, begin_column, line, column, filename}, tok::num, "0"};
str += res[ptr++]; }
if (ptr<res.size() && (res[ptr]=='-' || res[ptr]=='+')) { }
str += res[ptr++]; if (ptr<res.size() && (res[ptr]=='e' || res[ptr]=='E')) {
} str += res[ptr++];
while(ptr<res.size() && is_dec(res[ptr])) { if (ptr<res.size() && (res[ptr]=='-' || res[ptr]=='+')) {
str += res[ptr++]; str += res[ptr++];
} }
// "xxxe(-|+)" is not a correct number while(ptr<res.size() && is_dec(res[ptr])) {
if (str.back()=='e' || str.back()=='E' || str.back()=='-' || str.back()=='+') { str += res[ptr++];
column += str.length(); }
err.err("lexer", // "xxxe(-|+)" is not a correct number
{begin_line, begin_column, line, column, filename}, if (str.back()=='e' || str.back()=='E' || str.back()=='-' || str.back()=='+') {
"invalid number `"+str+"`" column += str.length();
); err.err("lexer",
return {{begin_line, begin_column, line, column, filename}, tok::num, "0"}; {begin_line, begin_column, line, column, filename},
} "invalid number `"+str+"`"
} );
column += str.length(); return {{begin_line, begin_column, line, column, filename}, tok::num, "0"};
return {{begin_line, begin_column, line, column, filename}, tok::num, str}; }
} }
column += str.length();
token lexer::str_gen() { return {{begin_line, begin_column, line, column, filename}, tok::num, str};
u32 begin_line = line; }
u32 begin_column = column;
std::string str = ""; token lexer::str_gen() {
const char begin = res[ptr]; u32 begin_line = line;
++column; u32 begin_column = column;
while(++ptr<res.size() && res[ptr]!=begin) { std::string str = "";
++column; const char begin = res[ptr];
if (res[ptr]=='\n') { ++column;
column = 0; while(++ptr<res.size() && res[ptr]!=begin) {
++line; ++column;
} if (res[ptr]=='\n') {
if (res[ptr]=='\\' && ptr+1<res.size()) { column = 0;
++column; ++line;
++ptr; }
switch(res[ptr]) { if (res[ptr]=='\\' && ptr+1<res.size()) {
case '0': str += '\0'; break; ++column;
case 'a': str += '\a'; break; ++ptr;
case 'b': str += '\b'; break; switch(res[ptr]) {
case 'e': str += '\033'; break; case '0': str += '\0'; break;
case 't': str += '\t'; break; case 'a': str += '\a'; break;
case 'n': str += '\n'; break; case 'b': str += '\b'; break;
case 'v': str += '\v'; break; case 'e': str += '\033'; break;
case 'f': str += '\f'; break; case 't': str += '\t'; break;
case 'r': str += '\r'; break; case 'n': str += '\n'; break;
case '?': str += '\?'; break; case 'v': str += '\v'; break;
case '\\':str += '\\'; break; case 'f': str += '\f'; break;
case '\'':str += '\''; break; case 'r': str += '\r'; break;
case '\"':str += '\"'; break; case '?': str += '\?'; break;
default: str += res[ptr];break; case '\\':str += '\\'; break;
} case '\'':str += '\''; break;
if (res[ptr]=='\n') { case '\"':str += '\"'; break;
column = 0; default: str += res[ptr];break;
++line; }
} if (res[ptr]=='\n') {
continue; column = 0;
} ++line;
str += res[ptr]; }
} continue;
// check if this string ends with a " or ' }
if (ptr++>=res.size()) { str += res[ptr];
err.err("lexer", }
{begin_line, begin_column, line, column, filename}, // check if this string ends with a " or '
"get EOF when generating string" if (ptr++>=res.size()) {
); err.err("lexer",
return {{begin_line, begin_column, line, column, filename}, tok::str, str}; {begin_line, begin_column, line, column, filename},
} "get EOF when generating string"
++column; );
return {{begin_line, begin_column, line, column, filename}, tok::str, str};
// if is not utf8, 1+utf8_hdchk should be 1 }
if (begin=='`' && str.length()!=1+utf8_hdchk(str[0])) { ++column;
err.err("lexer",
{begin_line, begin_column, line, column, filename}, // if is not utf8, 1+utf8_hdchk should be 1
"\'`\' is used for string including one character" if (begin=='`' && str.length()!=1+utf8_hdchk(str[0])) {
); err.err("lexer",
} {begin_line, begin_column, line, column, filename},
return {{begin_line, begin_column, line, column, filename}, tok::str, str}; "\'`\' is used for string including one character"
} );
}
token lexer::single_opr() { return {{begin_line, begin_column, line, column, filename}, tok::str, str};
u32 begin_line = line; }
u32 begin_column = column;
std::string str(1, res[ptr]); token lexer::single_opr() {
++column; u32 begin_line = line;
tok type = get_type(str); u32 begin_column = column;
if (type==tok::null) { std::string str(1, res[ptr]);
err.err("lexer", ++column;
{begin_line, begin_column, line, column, filename}, tok type = get_type(str);
"invalid operator `"+str+"`" if (type==tok::null) {
); err.err("lexer",
} {begin_line, begin_column, line, column, filename},
++ptr; "invalid operator `"+str+"`"
return {{begin_line, begin_column, line, column, filename}, type, str}; );
} }
++ptr;
token lexer::dots() { return {{begin_line, begin_column, line, column, filename}, type, str};
u32 begin_line = line; }
u32 begin_column = column;
std::string str = "."; token lexer::dots() {
if (ptr+2<res.size() && res[ptr+1]=='.' && res[ptr+2]=='.') { u32 begin_line = line;
str += ".."; u32 begin_column = column;
} std::string str = ".";
ptr += str.length(); if (ptr+2<res.size() && res[ptr+1]=='.' && res[ptr+2]=='.') {
column += str.length(); str += "..";
return {{begin_line, begin_column, line, column, filename}, get_type(str), str}; }
} ptr += str.length();
column += str.length();
token lexer::calc_opr() { return {{begin_line, begin_column, line, column, filename}, get_type(str), str};
u32 begin_line = line; }
u32 begin_column = column;
// get calculation operator token lexer::calc_opr() {
std::string str(1, res[ptr++]); u32 begin_line = line;
if (ptr<res.size() && res[ptr]=='=') { u32 begin_column = column;
str += res[ptr++]; // get calculation operator
} std::string str(1, res[ptr++]);
column += str.length(); if (ptr<res.size() && res[ptr]=='=') {
return {{begin_line, begin_column, line, column, filename}, get_type(str), str}; str += res[ptr++];
} }
column += str.length();
const error& lexer::scan(const std::string& file) { return {{begin_line, begin_column, line, column, filename}, get_type(str), str};
line = 1; }
column = 0;
ptr = 0; const error& lexer::scan(const std::string& file) {
toks = {}; line = 1;
open(file); column = 0;
ptr = 0;
while(ptr<res.size()) { toks = {};
while(ptr<res.size() && skip(res[ptr])) { open(file);
// these characters will be ignored, and '\n' will cause ++line
++column; while(ptr<res.size()) {
if (res[ptr++]=='\n') { while(ptr<res.size() && skip(res[ptr])) {
++line; // these characters will be ignored, and '\n' will cause ++line
column = 0; ++column;
} if (res[ptr++]=='\n') {
} ++line;
if (ptr>=res.size()) { column = 0;
break; }
} }
if (is_id(res[ptr])) { if (ptr>=res.size()) {
toks.push_back(id_gen()); break;
} else if (is_dec(res[ptr])) { }
toks.push_back(num_gen()); if (is_id(res[ptr])) {
} else if (is_str(res[ptr])) { toks.push_back(id_gen());
toks.push_back(str_gen()); } else if (is_dec(res[ptr])) {
} else if (is_single_opr(res[ptr])) { toks.push_back(num_gen());
toks.push_back(single_opr()); } else if (is_str(res[ptr])) {
} else if (res[ptr]=='.') { toks.push_back(str_gen());
toks.push_back(dots()); } else if (is_single_opr(res[ptr])) {
} else if (is_calc_opr(res[ptr])) { toks.push_back(single_opr());
toks.push_back(calc_opr()); } else if (res[ptr]=='.') {
} else if (res[ptr]=='#') { toks.push_back(dots());
skip_note(); } else if (is_calc_opr(res[ptr])) {
} else { toks.push_back(calc_opr());
err_char(); } else if (res[ptr]=='#') {
} skip_note();
if (invalid_char>10) { } else {
err.err("lexer", "too many invalid characters, stop"); err_char();
break; }
} if (invalid_char>10) {
} err.err("lexer", "too many invalid characters, stop");
if (toks.size()) { break;
// eof token's location is the last token's location }
toks.push_back({toks.back().loc, tok::eof, "<eof>"}); }
} else { if (toks.size()) {
// if token sequence is empty, generate a default location // eof token's location is the last token's location
toks.push_back({{line, column, line, column, filename}, tok::eof, "<eof>"}); toks.push_back({toks.back().loc, tok::eof, "<eof>"});
} } else {
res = ""; // if token sequence is empty, generate a default location
return err; toks.push_back({{line, column, line, column, filename}, tok::eof, "<eof>"});
} }
res = "";
} return err;
}
}

View File

@ -15,10 +15,6 @@
#include "nasal.h" #include "nasal.h"
#include "nasal_err.h" #include "nasal_err.h"
#ifdef _MSC_VER
#define S_ISREG(m) (((m)&0xF000)==0x8000)
#endif
namespace nasal { namespace nasal {
enum class tok:u32 { enum class tok:u32 {