Skip to content

Commit

Permalink
[script] make separate name spaces for structs
Browse files Browse the repository at this point in the history
This is okay for now.. fixes #50
  • Loading branch information
jd28 committed Nov 2, 2023
1 parent 8e97710 commit c4c8787
Show file tree
Hide file tree
Showing 4 changed files with 108 additions and 47 deletions.
94 changes: 62 additions & 32 deletions lib/nw/script/AstResolver.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,10 @@
namespace nw::script {

struct ScopeDecl {
bool ready = false;
bool decl_ready = false;
bool struct_decl_ready = false;
Declaration* decl = nullptr;
StructDecl* struct_decl = nullptr;
};

struct AstResolver : BaseVisitor {
Expand Down Expand Up @@ -43,26 +45,41 @@ struct AstResolver : BaseVisitor {
scope_stack_.push_back(ScopeMap{});
}

void declare(NssToken token, Declaration* decl)
void declare(NssToken token, Declaration* decl, bool is_type = false)
{
auto s = std::string(token.loc.view());
auto it = scope_stack_.back().find(s);
if (it != std::end(scope_stack_.back())) {
if (dynamic_cast<FunctionDecl*>(it->second.decl)
&& dynamic_cast<FunctionDefinition*>(decl)) {
it->second.decl = decl;
if (is_type) {
if (it->second.struct_decl) {
ctx_->semantic_error(parent_,
fmt::format("declaring '{}' in the same scope twice", token.loc.view()),
token.loc);
} else {
it->second.struct_decl = dynamic_cast<StructDecl*>(decl);
}
} else {
ctx_->semantic_error(parent_,
fmt::format("declaring '{}' in the same scope twice", token.loc.view()),
token.loc);
return;
if (!it->second.decl) {
it->second.decl = decl;
} else if (dynamic_cast<FunctionDecl*>(it->second.decl)
&& dynamic_cast<FunctionDefinition*>(decl)) {
it->second.decl = decl;
} else {
ctx_->semantic_error(parent_,
fmt::format("declaring '{}' in the same scope twice", token.loc.view()),
token.loc);
}
}
} else {
scope_stack_.back().insert({s, {false, decl}});
if (is_type) {
scope_stack_.back().insert({s, {false, false, nullptr, dynamic_cast<StructDecl*>(decl)}});
} else {
scope_stack_.back().insert({s, {false, false, decl, nullptr}});
}
}
}

void define(NssToken token)
void define(NssToken token, bool is_type = false)
{
auto s = std::string(token.loc.view());
auto it = scope_stack_.back().find(s);
Expand All @@ -71,50 +88,63 @@ struct AstResolver : BaseVisitor {
fmt::format("defining unknown variable '{}'", token.loc.view()),
token.loc);
}
it->second.ready = true;
if (is_type) {
it->second.struct_decl_ready = true;
} else {
it->second.decl_ready = true;
}
}

void end_scope()
{
scope_stack_.pop_back();
}

Declaration* locate(std::string_view token, Nss* script)
Declaration* locate(std::string_view token, Nss* script, bool is_type)
{
if (auto decl = script->locate_export(token)) {
if (auto decl = script->locate_export(token, is_type)) {
return decl;
} else {
for (auto& it : reverse(script->ast().includes)) {
if (auto decl = locate(token, it)) {
if (auto decl = locate(token, it, is_type)) {
return decl;
}
}
}
return nullptr;
}

Declaration* resolve(std::string_view token, SourceLocation loc)
Declaration* resolve(std::string_view token, SourceLocation loc, bool is_type)
{
auto s = std::string(token);

// Look first through the scope stack in the current script
for (const auto& map : reverse(scope_stack_)) {
auto it = map.find(s);
if (it == std::end(map)) { continue; }
if (!it->second.ready) {
ctx_->semantic_error(parent_,
fmt::format("using declared variable '{}' in init", token),
loc);
if (is_type) {
if (!it->second.struct_decl_ready) {
ctx_->semantic_error(parent_,
fmt::format("recursive use of struct '{}' in declaration", token),
loc);
}
return it->second.struct_decl;
} else {
if (it->second.decl && !it->second.decl_ready) {
ctx_->semantic_error(parent_,
fmt::format("using declared variable '{}' in init", token),
loc);
}
return it->second.decl;
}
return it->second.decl;
}

// Next look through all dependencies
for (auto it : reverse(parent_->ast().includes)) {
if (auto decl = locate(token, it)) { return decl; }
if (auto decl = locate(token, it, is_type)) { return decl; }
}

return ctx_->command_script_->locate_export(token);
return ctx_->command_script_->locate_export(token, is_type);
}

// == Visitor =============================================================
Expand Down Expand Up @@ -215,7 +245,7 @@ struct AstResolver : BaseVisitor {
virtual void visit(FunctionDecl* decl) override
{
// Check to see if there's been a function definition, if so got to match.
auto fd = resolve(decl->identifier.loc.view(), decl->identifier.loc);
auto fd = resolve(decl->identifier.loc.view(), decl->identifier.loc, false);

decl->type_id_ = ctx_->type_id(decl->type);
declare(decl->identifier, decl);
Expand All @@ -237,7 +267,7 @@ struct AstResolver : BaseVisitor {
{
++func_def_stack_;
// Check to see if there's been a function declaration, if so got to match.
auto fd = resolve(decl->decl_inline->identifier.loc.view(), decl->decl_inline->identifier.loc);
auto fd = resolve(decl->decl_inline->identifier.loc.view(), decl->decl_inline->identifier.loc, false);
decl->decl_external = dynamic_cast<FunctionDecl*>(fd);

decl->type_id_ = decl->decl_inline->type_id_ = ctx_->type_id(decl->decl_inline->type);
Expand All @@ -263,15 +293,15 @@ struct AstResolver : BaseVisitor {

virtual void visit(StructDecl* decl) override
{
declare(decl->type.struct_id, decl);
declare(decl->type.struct_id, decl, true);
decl->type_id_ = ctx_->type_id(decl->type, true);
begin_scope();
for (auto& it : decl->decls) {
it->accept(this);
}
end_scope();
// Define down here so there's no recursive elements
define(decl->type.struct_id);
define(decl->type.struct_id, true);
}

virtual void visit(VarDecl* decl) override
Expand Down Expand Up @@ -355,7 +385,7 @@ struct AstResolver : BaseVisitor {

FunctionDecl* func_decl = nullptr;
FunctionDecl* orig_decl = nullptr;
auto decl = resolve(ve->var.loc.view(), ve->var.loc);
auto decl = resolve(ve->var.loc.view(), ve->var.loc, false);
if (auto fd = dynamic_cast<FunctionDecl*>(decl)) {
func_decl = fd;
} else if (auto fd = dynamic_cast<FunctionDefinition*>(decl)) {
Expand Down Expand Up @@ -470,7 +500,7 @@ struct AstResolver : BaseVisitor {
if (auto de = dynamic_cast<DotExpression*>(expr->lhs.get())) {
expr->lhs->accept(this);
struct_type = ctx_->type_name(expr->lhs->type_id_);
struct_decl = struct_decl = dynamic_cast<StructDecl*>(resolve(struct_type, expr->dot.loc));
struct_decl = struct_decl = dynamic_cast<StructDecl*>(resolve(struct_type, expr->dot.loc, true));
} else if (auto ve = dynamic_cast<VariableExpression*>(expr->lhs.get())) {
ve->accept(this);

Expand All @@ -483,8 +513,8 @@ struct AstResolver : BaseVisitor {
return;
}

struct_type = ctx_->type_name(ve->type_id_); // ve->var.loc.view();
struct_decl = dynamic_cast<StructDecl*>(resolve(ctx_->type_name(ve->type_id_), ve->var.loc));
struct_type = ctx_->type_name(ve->type_id_);
struct_decl = dynamic_cast<StructDecl*>(resolve(ctx_->type_name(ve->type_id_), ve->var.loc, true));
}

if (!struct_decl) {
Expand Down Expand Up @@ -559,7 +589,7 @@ struct AstResolver : BaseVisitor {

virtual void visit(VariableExpression* expr) override
{
auto decl = resolve(expr->var.loc.view(), expr->var.loc);
auto decl = resolve(expr->var.loc.view(), expr->var.loc, false);
if (decl) {
expr->type_id_ = decl->type_id_;
expr->is_const_ = decl->is_const_;
Expand Down
43 changes: 30 additions & 13 deletions lib/nw/script/Nss.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,17 +35,27 @@ Nss::Nss(ResourceData data, Context* ctx)
void Nss::add_export(std::string_view name, Declaration* decl)
{
absl::string_view n{name.data(), name.size()};
auto it = exports_.find(n);
if (it == std::end(exports_)) {
exports_.insert({std::string(name), decl});

if (dynamic_cast<StructDecl*>(decl)) {
auto it = type_exports_.find(n);
if (it == std::end(type_exports_)) {
type_exports_.insert({std::string(name), decl});
} else {
throw std::runtime_error(fmt::format("duplicate export: {}", name));
}
} else {
if (dynamic_cast<FunctionDecl*>(it->second) && dynamic_cast<FunctionDefinition*>(decl)) {
it->second = decl;
auto it = exports_.find(n);
if (it == std::end(exports_)) {
exports_.insert({std::string(name), decl});
} else {
// [TODO] This will get flagged in the AstResolver as an error, if outside of that context,
// throw I guess
if (errors_ == 0) {
throw std::runtime_error(fmt::format("duplicate export: {}", name));
if (dynamic_cast<FunctionDecl*>(it->second) && dynamic_cast<FunctionDefinition*>(decl)) {
it->second = decl;
} else {
// [TODO] This will get flagged in the AstResolver as an error, if outside of that context,
// throw I guess
if (errors_ == 0) {
throw std::runtime_error(fmt::format("duplicate export: {}", name));
}
}
}
}
Expand All @@ -66,12 +76,19 @@ std::set<std::string> Nss::dependencies() const
return result;
}

Declaration* Nss::locate_export(std::string_view name)
Declaration* Nss::locate_export(std::string_view name, bool is_type)
{
absl::string_view n{name.data(), name.size()};
auto it = exports_.find(n);
if (it != std::end(exports_)) {
return it->second;
if (is_type) {
auto it = type_exports_.find(n);
if (it != std::end(type_exports_)) {
return it->second;
}
} else {
auto it = exports_.find(n);
if (it != std::end(exports_)) {
return it->second;
}
}
return nullptr;
}
Expand Down
3 changes: 2 additions & 1 deletion lib/nw/script/Nss.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ struct Nss {

/// Locate export
/// @note This function is not recursive
Declaration* locate_export(std::string_view name);
Declaration* locate_export(std::string_view name, bool is_type);

/// Script name
std::string_view name() const noexcept;
Expand Down Expand Up @@ -79,6 +79,7 @@ struct Nss {
NssParser parser_;
Ast ast_;
absl::flat_hash_map<std::string, Declaration*> exports_;
absl::flat_hash_map<std::string, Declaration*> type_exports_;
size_t errors_ = 0;
size_t warnings_ = 0;
bool resolved_ = false;
Expand Down
15 changes: 14 additions & 1 deletion tests/script_nss.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ TEST(Nss, ParseNwscript)
EXPECT_NO_THROW(nss.parse());
EXPECT_NO_THROW(nss.resolve());
EXPECT_TRUE(nss.errors() == 0);
auto decl = nss.locate_export("IntToString");
auto decl = nss.locate_export("IntToString", false);
EXPECT_NE(decl, nullptr);
auto d = dynamic_cast<nw::script::FunctionDecl*>(decl);
if (d) {
Expand Down Expand Up @@ -423,6 +423,19 @@ TEST(Nss, Struct)
EXPECT_NO_THROW(nss4.resolve());
EXPECT_EQ(nss4.errors(), 0);

// Struct is separate namespace
script::Nss nss5(R"(
struct MyStruct { int test1; };
int MyStruct() { return 42; }
void main() { }
)"sv,
ctx.get());

EXPECT_NO_THROW(nss5.parse());
EXPECT_NO_THROW(nss5.resolve());
EXPECT_EQ(nss5.errors(), 0);

// == Bad =================================================================

script::Nss nss3(R"(
Expand Down

0 comments on commit c4c8787

Please sign in to comment.