Skip to content

Commit

Permalink
Replace manual path mucking with std::filesystem.
Browse files Browse the repository at this point in the history
This has a few side effects:
 - DIRSEP_CHAR and a lot of manual string code are gone.
 - This fixes how file defines were created, which was mostly broken and
   working by accident.
 - A weird feature where non-".sp" extensions were rewritten on the
   command-line has been removed.
 - This fixes an unrelated crash on the command-line if the input file
   was not found.
  • Loading branch information
dvander committed Oct 8, 2023
1 parent 2c2665e commit c51d79a
Show file tree
Hide file tree
Showing 8 changed files with 151 additions and 201 deletions.
34 changes: 34 additions & 0 deletions compiler/builtins.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// vim: set ts=8 sts=4 sw=4 tw=99 et:
// Pawn compiler - File input, preprocessing and lexical analysis functions
//
// Copyright (c) 2023 AlliedModders LLC
//
// This software is provided "as-is", without any express or implied warranty.
// In no event will the authors be held liable for any damages arising from
// the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software. If you use this software in
// a product, an acknowledgment in the product documentation would be
// appreciated but is not required.
// 2. Altered source versions must be plainly marked as such, and must not be
// misrepresented as being the original software.
// 3. This notice may not be removed or altered from any source distribution.

#pragma once

namespace sp {

class BuiltinGenerator final {
public:
explicit BuiltinGenerator(CompileContext& cc);

private:
CompileContext& cc_;
};

} // namespace sp
61 changes: 19 additions & 42 deletions compiler/driver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@
// 3. This notice may not be removed or altered from any source distribution.
#include <amtl/experimental/am-argparser.h>

#include <filesystem>

#include "compile-options.h"
#include "errors.h"
#include "sc.h"
Expand All @@ -33,6 +35,8 @@
using namespace ke;
using namespace sp;

namespace fs = std::filesystem;

#if defined _WIN32
static HWND hwndFinish = 0;
#endif
Expand Down Expand Up @@ -69,22 +73,6 @@ args::ToggleOption opt_stderr(nullptr, "--use-stderr", Some(false),
args::ToggleOption opt_no_verify(nullptr, "--no-verify", Some(false),
"Disable opcode verification (for debugging).");

static std::string get_extension(const std::string& filename) {
auto pos = filename.rfind('.');
if (pos == std::string::npos)
return {};

/* ignore extension on a directory or at the start of the filename */
if (pos == 0)
return {};
if (filename[pos - 1] == DIRSEP_CHAR)
return {};
if (filename.find(DIRSEP_CHAR, pos) != std::string::npos)
return {};

return filename.substr(pos);
}

/* set_extension
* Set the default extension, or force an extension. To erase the
* extension of a filename, set "extension" to an empty string.
Expand All @@ -93,11 +81,11 @@ static void set_extension(std::string* filename, const char* extension, bool for
assert(extension != NULL && (*extension == '\0' || *extension == '.'));
assert(filename != NULL);

auto old_ext = get_extension(*filename);
if (force && !old_ext.empty())
*filename = filename->substr(0, filename->size() - old_ext.size());
if (force || old_ext.empty())
*filename += extension;
fs::path path(*filename);
if (force && !path.has_extension())
*filename = path.stem().string();
if (force || path.has_extension())
*filename = fs::path(*filename).replace_extension(extension).string();
}

static void Usage(CompileContext& cc, args::Parser& parser, int argc, char** argv)
Expand All @@ -113,7 +101,7 @@ static void parseoptions(CompileContext& cc, int argc, char** argv) {
args::Parser parser;
parser.enable_inline_values();
parser.collect_extra_args();
if (DIRSEP_CHAR != '/') {
if (fs::path::preferred_separator != '/') {
parser.allow_slashes();
}

Expand Down Expand Up @@ -179,8 +167,6 @@ static void parseoptions(CompileContext& cc, int argc, char** argv) {

if (str.empty())
continue;
if (str.back() != DIRSEP_CHAR)
str.push_back(DIRSEP_CHAR);

cc.options()->include_paths.emplace_back(str);
}
Expand All @@ -197,32 +183,23 @@ static void parseoptions(CompileContext& cc, int argc, char** argv) {
}

for (const auto& option : parser.extra_args()) {
char str[PATH_MAX];
const char* ptr = nullptr;
const char* arg = option.c_str();
if (arg[0] == '@') {
size_t pos;
if (option[0] == '@') {
fprintf(stderr, "Response files (@ prefix) are no longer supported.");
exit(1);
} else if ((ptr = strchr(arg, '=')) != NULL) {
int i = (int)(ptr - arg);
SafeStrcpyN(str, PATH_MAX, arg, i);
cc.options()->predefines.emplace_back(str, ptr + 1);
} else if ((pos = option.find('=')) != std::string::npos) {
std::string key = option.substr(0, pos);
std::string value = option.substr(pos + 1);
cc.options()->predefines.emplace_back(std::move(key), std::move(value));
} else {
std::string path = arg;
set_extension(&path, ".sp", false);
ke::SafeStrcpy(str, sizeof(str), path.c_str());
cc.options()->source_files.emplace_back(option);

cc.options()->source_files.emplace_back(str);
/* The output name is the first input name with a different extension,
* but it is stored in a different directory
*/
if (cc.outfname().empty()) {
if ((ptr = strrchr(str, DIRSEP_CHAR)) != NULL)
ptr++; /* strip path */
else
ptr = str;
assert(strlen(ptr) < PATH_MAX);
cc.set_outfname(ptr);
fs::path out_path(option);
cc.set_outfname(out_path.filename().string());
set_extension(&cc.outfname(), ".smx", true);
}
}
Expand Down
5 changes: 3 additions & 2 deletions compiler/errors.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,7 @@ MessageBuilder::~MessageBuilder()

if (report.fileno < cc.sources()->opened_files().size())
report.file = cc.sources()->opened_files().at(report.fileno);
else
else if (!cc.sources()->opened_files().empty())
report.file = cc.sources()->opened_files().at(0);

uint32_t actual_line = cc.sources()->GetLineAndCol(where_, &report.col);
Expand All @@ -194,7 +194,8 @@ MessageBuilder::~MessageBuilder()
report.type = DeduceErrorType(number_);

std::ostringstream out;
out << report.file->name() << "(" << report.lineno << ") : ";
if (report.file)
out << report.file->name() << "(" << report.lineno << ") : ";
out << GetErrorTypePrefix(report.type)
<< " " << ke::StringPrintf("%03d", report.number) << ": ";

Expand Down
120 changes: 44 additions & 76 deletions compiler/lexer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,9 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <string>

#include <filesystem>
#include <string>
#include <unordered_set>
#include <utility>

Expand Down Expand Up @@ -62,6 +63,8 @@

namespace sp {

namespace fs = std::filesystem;

// Flags for litchar().
//
// Decode utf-8 and error on failure. If unset, non-ASCII characters will be
Expand All @@ -70,7 +73,7 @@ static constexpr int kLitcharUtf8 = 0x1;
// Do not error, because the characters are being ignored.
static constexpr int kLitcharSkipping = 0x2;

bool Lexer::PlungeQualifiedFile(const char* name) {
bool Lexer::PlungeQualifiedFile(const std::string& name) {
auto fp = OpenFile(name);
if (!fp)
return false;
Expand Down Expand Up @@ -104,88 +107,54 @@ std::shared_ptr<SourceFile> Lexer::OpenFile(const std::string& name) {
}

bool
Lexer::PlungeFile(const char* name, int try_currentpath, int try_includepaths)
Lexer::PlungeFile(const std::string& name, int try_currentpath, int try_includepaths)
{
bool result = false;
char* pcwd = NULL;
char cwd[PATH_MAX];

if (try_currentpath) {
result = PlungeQualifiedFile(name);
if (!result) {
/* failed to open the file in the active directory, try to open the file
* in the same directory as the current file --but first check whether
* there is a (relative) path for the current file
*/
const char* ptr;
if ((ptr = strrchr(state_.inpf->name(), DIRSEP_CHAR)) != 0) {
int len = (int)(ptr - state_.inpf->name()) + 1;
if (len + strlen(name) < PATH_MAX) {
char path[PATH_MAX];
SafeStrcpyN(path, sizeof(path), state_.inpf->name(), len);
SafeStrcat(path, sizeof(path), name);
result = PlungeQualifiedFile(path);
}
}
} else {
pcwd = getcwd(cwd, sizeof(cwd));
if (!pcwd) {
report(435);
return false;
}
if (PlungeQualifiedFile(name))
return true;

#ifdef _WIN32
// make the drive letter on windows lower case to be in line with the rest of SP, as they have a small drive letter in the path
cwd[0] = tolower(cwd[0]);
#endif
// failed to open the file in the active directory, try to open the file
// in the same directory as the current file --but first check whether
// there is a (relative) path for the current file
fs::path current_path(state_.inpf->name());
auto parent_path = current_path.parent_path();
if (!parent_path.empty()) {
auto new_path = parent_path / name;
if (PlungeQualifiedFile(new_path.string()))
return true;
}
}

if (!result && try_includepaths && name[0] != DIRSEP_CHAR) {
if (try_includepaths && !fs::path(name).is_absolute()) {
auto& cc = CompileContext::get();
for (const auto& inc_path : cc.options()->include_paths) {
auto path = inc_path + name;
if (PlungeQualifiedFile(path.c_str())) {
result = true;
break;
}
auto path = fs::path(inc_path) / fs::path(name);
if (PlungeQualifiedFile(path.string()))
return true;
}
}

if (pcwd) {
char path[PATH_MAX];
SafeSprintf(path, sizeof(path), "%s%s", pcwd, state_.inpf->name());
SetFileDefines(path);
} else {
SetFileDefines(state_.inpf->name());
}

return result;
return false;
}

void
Lexer::SetFileDefines(std::string file)
{
auto sepIndex = file.find_last_of(DIRSEP_CHAR);

std::string fileName = sepIndex == std::string::npos ? file : file.substr(sepIndex + 1);

if (DIRSEP_CHAR == '\\') {
auto pos = file.find('\\');
while (pos != std::string::npos) {
file.insert(pos + 1, 1, '\\');
pos = file.find('\\', pos + 2);
}
std::string StringizePath(const fs::path& in_path) {
auto path = '"' + in_path.string() + '"';
auto pos = path.find('\\');
while (pos != std::string::npos) {
path.insert(pos + 1, 1, '\\');
pos = path.find('\\', pos + 2);
}
return path;
}

file.insert(file.begin(), '"');
fileName.insert(fileName.begin(), '"');
void Lexer::SetFileDefines(const std::string& file) {
fs::path path = fs::canonical(file);

file.push_back('"');
fileName.push_back('"');
auto full_path = StringizePath(path);
auto name = StringizePath(path.filename());

AddMacro("__FILE_PATH__", file.c_str());
AddMacro("__FILE_NAME__", fileName.c_str());
AddMacro("__FILE_PATH__", full_path.c_str());
AddMacro("__FILE_NAME__", name.c_str());
}

void Lexer::CheckLineEmpty(bool allow_semi) {
Expand Down Expand Up @@ -225,24 +194,22 @@ Lexer::SynthesizeIncludePathToken()

SkipLineWhitespace();

char name[PATH_MAX];
std::string name;

int i = 0;
while (true) {
char c = peek();
if (c == close_c || c == '\0' || i >= (int)sizeof(name) - 1 || IsNewline(c))
break;
if (DIRSEP_CHAR != '/' && c == '/') {
name[i++] = DIRSEP_CHAR;
if (fs::path::preferred_separator != '/' && c == '/') {
name.push_back(fs::path::preferred_separator);
advance();
} else {
name[i++] = advance();
name.push_back(advance());
}
}
while (i > 0 && name[i - 1] <= ' ')
i--; /* strip trailing whitespace */
assert(i < (int)sizeof name);
name[i] = '\0'; /* zero-terminate the string */
while (!name.empty() && name.back() == ' ')
name.pop_back();

if (close_c) {
if (advance() != close_c)
Expand All @@ -253,7 +220,7 @@ Lexer::SynthesizeIncludePathToken()

if (!open_c)
open_c = '"';
tok->atom = cc_.atom(ke::StringPrintf("%c%s", open_c, name));
tok->atom = cc_.atom(ke::StringPrintf("%c%s", open_c, name.c_str()));
}

/* ftoi
Expand Down Expand Up @@ -2344,6 +2311,7 @@ void Lexer::EnterFile(std::shared_ptr<SourceFile>&& sf, const token_pos_t& from)
state_.pos = state_.start;
state_.line_start = state_.pos;
SkipUtf8Bom();
SetFileDefines(state_.inpf->name());

tokens_on_line_ = 0;
}
Expand Down
Loading

0 comments on commit c51d79a

Please sign in to comment.