summaryrefslogtreecommitdiff
path: root/willow/tools/willowc/lib
diff options
context:
space:
mode:
authorStefan Weigl-Bosker <stefan@s00.xyz>2026-02-23 22:18:22 -0500
committerGitHub <noreply@github.com>2026-02-23 22:18:22 -0500
commit8f98dc579af1993ec85bd849656c4835b4039dd6 (patch)
tree3ee45620d83b209c1c11248afc9ab83ffcf39691 /willow/tools/willowc/lib
parentc2d4209f85f46cc91163bc47cc43db252c94acf6 (diff)
downloadcompiler-8f98dc579af1993ec85bd849656c4835b4039dd6.tar.gz
[willow]: frontend plumbing (#13)
...
Diffstat (limited to 'willow/tools/willowc/lib')
-rw-r--r--willow/tools/willowc/lib/compiler.cpp66
-rw-r--r--willow/tools/willowc/lib/driver.cpp76
-rw-r--r--willow/tools/willowc/lib/parser.cpp21
-rw-r--r--willow/tools/willowc/lib/sourcemanager.cpp75
-rw-r--r--willow/tools/willowc/lib/tokenizer.cpp67
5 files changed, 293 insertions, 12 deletions
diff --git a/willow/tools/willowc/lib/compiler.cpp b/willow/tools/willowc/lib/compiler.cpp
new file mode 100644
index 0000000..b933f65
--- /dev/null
+++ b/willow/tools/willowc/lib/compiler.cpp
@@ -0,0 +1,66 @@
+#include <compiler.hpp>
+
+#include <willow/IR/Diagnostic.h>
+#include <willow/IR/Location.h>
+#include <willow/Util/Color.h>
+
+#include <parser.hpp>
+
+#include <iostream>
+#include <print>
+
+namespace willowc {
+
+Compiler::Compiler()
+ : sourcemanager_(), log_level_(willow::Severity::Error),
+ diagnostic_engine_([this](const willow::Diagnostic &d) { emitDiagnostic(d); }) {}
+
+void Compiler::run() {
+ assert(sourcemanager_.numFiles() == 1);
+ Compiler::compile(0);
+}
+
+void Compiler::compile(FileID file) {
+ Parser parser{sourcemanager_.getFile(file), diagnostic_engine_};
+
+ auto x = parser.run();
+}
+
+void Compiler::emitDiagnostic(const willow::Diagnostic &d) {
+ using namespace willow::termcolor;
+ if (log_level_ > d.severity)
+ return;
+
+ if (d.location) {
+ std::print(std::cerr, "{}{}: ", TextStyle{AnsiColor::None, Emphasis::Bold},
+ d.location.value());
+ }
+
+ std::print(std::cerr, "{}{}: {}{}{}\n", willow::getSeverityColor(d.severity),
+ willow::getSeverityName(d.severity),
+ TextStyle{AnsiColor::Default, Emphasis::Bold}, d.message, TextStyle{});
+ // TODO: trace
+}
+
+willow::LogicalResult Compiler::addSourceFile(const std::string &path) {
+ std::optional<FileID> maybe_source_file = sourcemanager_.addFile(path);
+ if (!maybe_source_file) {
+ std::println(std::cerr, "error: failed to open input file '{}'", path);
+ return willow::failure();
+ }
+
+ return willow::success();
+}
+
+willow::LogicalResult Compiler::addStdIn() {
+ std::optional<FileID> maybestdin = sourcemanager_.addStdIn();
+
+ if (!maybestdin) {
+ std::println(std::cerr, "error: failed to read from stdin");
+ return willow::failure();
+ }
+
+ return willow::success();
+}
+
+}; // namespace willowc
diff --git a/willow/tools/willowc/lib/driver.cpp b/willow/tools/willowc/lib/driver.cpp
index e69de29..1962da4 100644
--- a/willow/tools/willowc/lib/driver.cpp
+++ b/willow/tools/willowc/lib/driver.cpp
@@ -0,0 +1,76 @@
+#include <print>
+#include <compiler.hpp>
+#include <driver.hpp>
+
+#include <argparse/argparse.hpp>
+
+namespace willowc {
+
+int willowc_main(int argc, char **argv) {
+ argparse::ArgumentParser cl("willowc");
+ cl.set_prefix_chars("-");
+ cl.set_assign_chars("=");
+
+ willowc::Compiler compiler;
+
+ auto &ll = cl.add_mutually_exclusive_group();
+ ll.add_argument("-Lerror")
+ .help("only emit diagnostics on error (default)")
+ .default_value(true)
+ .implicit_value(true)
+ .action(
+ [&](const auto &) { compiler.setLogLevel(willow::Severity::Error); });
+ ll.add_argument("-Lwarn")
+ .help("emit warning messages")
+ .default_value(false)
+ .implicit_value(true)
+ .action([&](const auto &) {
+ compiler.setLogLevel(willow::Severity::Warning);
+ });
+ ll.add_argument("-Linfo")
+ .help("emit remarks")
+ .default_value(false)
+ .implicit_value(true)
+ .action([&](const auto &) {
+ compiler.setLogLevel(willow::Severity::Remark);
+ });
+ ll.add_argument("-Ldebug")
+ .help("emit all diagnostics")
+ .default_value(false)
+ .implicit_value(true)
+ .action(
+ [&](const auto &) { compiler.setLogLevel(willow::Severity::Debug); });
+
+ cl.add_argument("<file>").help("input file path").required().nargs(1);
+
+ if (argc < 2) {
+ std::cerr << cl;
+ return 1;
+ }
+
+ try {
+ cl.parse_args(argc, argv);
+ } catch (std::exception &e) {
+ std::println("error: {}", e.what());
+ }
+
+ std::string f;
+ try {
+ f = cl.get("<file>");
+ } catch (std::exception &e) {
+ std::println("error: {}", e.what());
+ return 1;
+ }
+
+ if (f == "-") {
+ if (willow::failed(compiler.addStdIn()))
+ return 1;
+ } else if (willow::failed(compiler.addSourceFile(f)))
+ return 1;
+
+ compiler.run();
+
+ return 0;
+}
+
+} // namespace willowc
diff --git a/willow/tools/willowc/lib/parser.cpp b/willow/tools/willowc/lib/parser.cpp
index becc171..e658909 100644
--- a/willow/tools/willowc/lib/parser.cpp
+++ b/willow/tools/willowc/lib/parser.cpp
@@ -2,8 +2,27 @@
namespace willowc {
-std::optional<std::unique_ptr<ModuleAST>> parse() {
+std::optional<std::unique_ptr<ModuleAST>> Parser::run() {
+ bool failed = false;
+ while (true) {
+ Token t = tokenizer_.scan();
+ if (t.kind == TokenKind::Eof) {
+ willow::emit(diagnostic_engine_, willow::Severity::Debug)
+ << std::format("[ <eof> :: {} : ({}, {}) ]", t.kind, t.start, t.end);
+ break;
+ }
+ willow::emit(diagnostic_engine_, willow::Severity::Debug)
+ << std::format("[ '{}' :: {} : ({}, {}) ]",
+ tokenizer_.buf.substr(t.start, t.end - t.start), t.kind,
+ t.start, t.end);
+ }
+ return std::nullopt;
}
+willow::DiagnosticBuilder Parser::emitParserError(Token t, willow::Severity severity) {
+ willow::Location loc = file_.getLoc(t.start);
+ return willow::DiagnosticBuilder(diagnostic_engine_, severity, loc);
}
+
+} // namespace willowc
diff --git a/willow/tools/willowc/lib/sourcemanager.cpp b/willow/tools/willowc/lib/sourcemanager.cpp
index e2a8e72..a30c76e 100644
--- a/willow/tools/willowc/lib/sourcemanager.cpp
+++ b/willow/tools/willowc/lib/sourcemanager.cpp
@@ -1,6 +1,9 @@
+#include <cassert>
#include <filesystem>
-
#include <fstream>
+#include <iostream>
+#include <string_view>
+
#include <sourcemanager.hpp>
namespace willowc {
@@ -11,9 +14,14 @@ std::optional<FileID> SourceManager::addFile(std::string_view _path) {
std::filesystem::path uncanonical_path{_path};
auto path = std::filesystem::weakly_canonical(uncanonical_path, ec);
if (ec) {
- return false;
+ return std::nullopt;
}
- std::string display_path = path.make_preferred();
+
+ std::filesystem::path display_path = path;
+ display_path.make_preferred();
+ std::string display = display_path.string();
+
+ assert(!getFileID(display));
if (!std::filesystem::exists(path, ec) || ec)
return std::nullopt;
@@ -33,9 +41,68 @@ std::optional<FileID> SourceManager::addFile(std::string_view _path) {
f.read(buf.get(), filesize);
const FileID id = file_table.size();
- file_table.push_back(File{std::move(display_path), std::move(buf)});
+ file_table.emplace_back(std::move(display), std::string(_path), std::move(buf), filesize);
+ ids[file_table.back().path] = id;
return id;
}
+std::optional<FileID> SourceManager::getFileID(const std::string &path) {
+ auto it = ids.find(path);
+
+ if (it != ids.end())
+ return it->second;
+
+ return std::nullopt;
+}
+
+std::optional<FileID> SourceManager::addStdIn() {
+ std::string content{std::istreambuf_iterator<char>(std::cin),
+ std::istreambuf_iterator<char>()};
+
+ if (std::cin.bad())
+ return std::nullopt;
+
+ std::size_t size = content.size();
+ auto buf = std::make_unique<char[]>(size);
+ const FileID id = file_table.size();
+ file_table.emplace_back("<stdin>", "<stdin>", std::move(buf), size);
+ ids[file_table.back().path] = id;
+ return id;
+}
+
+willow::Location SourceManager::File::getLoc(std::size_t offset) {
+ size_t line_start = offset;
+ while (line_start != 0) {
+ if (this->buf[--line_start] != '\n')
+ continue;
+
+ line_start++;
+ break;
+ }
+
+ uint32_t col = offset - line_start + 1;
+ auto it = linecache_.find(line_start);
+
+ if (it != linecache_.end())
+ return willow::Location{display_path, it->second, col, offset};
+
+ auto back = linecache_.rbegin();
+ auto i = back->first;
+ auto line = back->second;
+ assert(i < line_start);
+ for (; i < line_start; i++) {
+ if (buf[i] == '\n') {
+ line = line + 1;
+ size_t next_start = i + 1;
+
+ if (next_start <= size)
+ linecache_.insert({next_start, ++line});
+ }
+ }
+ linecache_.insert({line_start, line});
+ return willow::Location{
+ display_path, line, static_cast<uint32_t>(offset - line_start + 1), offset};
+}
+
} // namespace willowc
diff --git a/willow/tools/willowc/lib/tokenizer.cpp b/willow/tools/willowc/lib/tokenizer.cpp
index 0c1f917..7ad28a6 100644
--- a/willow/tools/willowc/lib/tokenizer.cpp
+++ b/willow/tools/willowc/lib/tokenizer.cpp
@@ -38,11 +38,11 @@ bool Tokenizer::scan_id(bool accept_digits = true) {
}
Token Tokenizer::scan() {
- std::size_t start = this->offset;
-
- while (isspace(peek()))
+ while (is_space(peek()))
skip();
+ std::size_t start = this->offset;
+
TokenKind k = [&] {
switch (peek()) {
case '@':
@@ -63,6 +63,9 @@ Token Tokenizer::scan() {
case ',':
skip();
return TokenKind::Comma;
+ case ':':
+ skip();
+ return TokenKind::Colon;
case ';':
skip();
return TokenKind::Semicolon;
@@ -99,9 +102,8 @@ Token Tokenizer::scan() {
return TokenKind::Invalid;
skip();
- char c = eat();
- while (c != '\0' && c != '\n')
- c = eat();
+ while (peek() != '\0' && peek() != '\n')
+ skip();
return TokenKind::Comment;
}
@@ -124,12 +126,20 @@ Token Tokenizer::scan() {
return TokenKind::Inst;
}
+ skip();
return TokenKind::Invalid;
}
}
}();
- return Token{start, offset, k};
+ Token t{start, offset, k};
+ if (t.kind == TokenKind::Invalid) {
+ if (t.start == t.end)
+ t.end++;
+ recover();
+ }
+
+ return t;
}
bool Tokenizer::scan_dec() {
@@ -173,4 +183,47 @@ bool Tokenizer::scan_constant() {
return false;
}
+void Tokenizer::recover() {
+ auto is_boundary = [&](char c) {
+ switch (c) {
+ case ' ':
+ [[fallthrough]];
+ case '\n':
+ [[fallthrough]];
+ case '\t':
+ [[fallthrough]];
+ case '\\':
+ [[fallthrough]];
+ case ',':
+ [[fallthrough]];
+ case '%':
+ [[fallthrough]];
+ case '@':
+ [[fallthrough]];
+ case ':':
+ [[fallthrough]];
+ case ';':
+ [[fallthrough]];
+ case '(':
+ [[fallthrough]];
+ case ')':
+ [[fallthrough]];
+ case '{':
+ [[fallthrough]];
+ case '}':
+ [[fallthrough]];
+ case '=':
+ [[fallthrough]];
+ case '\0':
+ return true;
+ default:
+ return false;
+ }
+ };
+
+ while (!is_boundary(peek())) {
+ skip();
+ }
+}
+
} // namespace willowc