Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 3 additions & 5 deletions CMakePresets.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,7 @@
"configurePresets": [
{
"name": "default",
"inherits": "gcc-debug",
"cacheVariables": {
"NOCTERN_TEST_COLOR": "ON"
}
"inherits": "gcc-debug"
},
{
"name": "gcc-debug",
Expand Down Expand Up @@ -59,7 +56,8 @@
"generator": "Ninja",
"binaryDir": "build/${presetName}",
"inherits": [
"_ccache"
"_ccache",
"_compile_commands_json"
]
}
],
Expand Down
7 changes: 7 additions & 0 deletions cmake/PresetMixins.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,13 @@
"CMAKE_C_COMPILER_LAUNCHER": "ccache",
"CMAKE_CXX_COMPILER_LAUNCHER": "ccache"
}
},
{
"name": "_compile_commands_json",
"hidden": true,
"cacheVariables": {
"CMAKE_EXPORT_COMPILE_COMMANDS": "ON"
}
}
]
}
2 changes: 1 addition & 1 deletion examples/basic_math.nct
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
def silly_add(x, y): {
def silly_add(x: f64, y: f64): f64 = {
let z = y - 0.2;
return y + z + x * 2. - 2 + .1;
};
8 changes: 4 additions & 4 deletions examples/simple_main.nct
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
def Main(): {
let x = 3;
let y = 4;
let z = y - 0.2;
def Main(): f64 = {
let x: f64 = 3;
let y: f64 = 4;
let z: f64 = y - 0.2;
return y + z + x * 2. - 2 + .1;
};
107 changes: 107 additions & 0 deletions src/nir.main.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
#include <cerrno>
#include <cstddef>
#include <cstdio>
#include <cstring>
#include <ranges>
#include <string>

#include <fmt/core.h>
#include <fmt/ranges.h>

#include "noctern/compilation_unit.hpp"
#include "noctern/intern_table.hpp"
#include "noctern/interpreter.hpp"
#include "noctern/nir.hpp"
#include "noctern/parser.hpp"
#include "noctern/symbol_table.hpp"
#include "noctern/tokenize.hpp"

int main(int argc, char** argv) {
if (argc != 2) {
fmt::println(stderr, "Usage: nocternc <file.nct>");
return 1;
}

// TODO: mmap
std::FILE* file = std::fopen(argv[1], "rb");
if (file == nullptr) {
std::string err(std::strerror(errno));
fmt::println(stderr, "Couldn't find file {}: {}", argv[1], err);
return 1;
}
if (std::fseek(file, 0, SEEK_END) != 0) {
std::string err(std::strerror(errno));
fmt::println(stderr, "fseek failed: {}", err);
return 1;
}
long length = std::ftell(file);
if (length == -1) {
std::string err(std::strerror(errno));
fmt::println(stderr, "ftell failed: {}", err);
return 1;
}
if (std::fseek(file, 0, SEEK_SET) != 0) {
std::string err(std::strerror(errno));
fmt::println(stderr, "fseek failed: {}", err);
return 1;
}
std::string source(length, '\0');
[[maybe_unused]] size_t c = std::fread(source.data(), source.size(), length, file);
if (std::ferror(file) != 0) {
std::string err(std::strerror(errno));
fmt::println(stderr, "fread failed: {}", err);
return 1;
}
if (std::feof(file) == 0) {
std::string err(std::strerror(errno));
fmt::println(stderr, "failed to read entire file; didn't find eof.");
return 1;
}

noctern::tokens tokens = noctern::tokenize_all(source);
tokens = noctern::parse(std::move(tokens));

noctern::compilation_unit compile_unit(tokens);
noctern::string_intern_table global_symbols;
noctern::symbol_table symbol_table(tokens, compile_unit, global_symbols);

std::optional<noctern::token> main = symbol_table.find_fn_decl(global_symbols.intern("Main"));
if (!main.has_value()) {
fmt::println(stderr, "No `Main()` function found!");
return 1;
}

noctern::nir::instructions instructions;
noctern::nir::instructions::function fn
= instructions.compile_function(tokens, *main, global_symbols);

instructions.visit(fn,
[&]<noctern::nir::instructions::opcode op, typename Register, typename... Args>(
noctern::val_t<op>, Register reg, Args&&... args) {
using enum noctern::nir::instructions::opcode;
if constexpr (op == return_) {
fmt::println(" return %r{}", reg);
} else {
fmt::print(" %r{} = {}", reg, stringify(op));

if constexpr (op == load_int_lit || op == load_real_lit) {
fmt::print(" {}", [](auto x) { return x; }(args...));
} else if constexpr (op == load_nonlocal) {
fmt::print(" {}", [&](noctern::interned_string id) {
return global_symbols.get(id);
}(args...));
} else if constexpr (op == call) {
[&]<typename Reg>(Reg fn, std::span<const Reg> args) {
fmt::print(" %r{}({})", fn,
fmt::join(std::ranges::transform_view(
args, [](auto reg) { return fmt::format("%r{}", reg); }),
", "));
}(args...);
}

fmt::println("");
}
});

return 0;
}
2 changes: 1 addition & 1 deletion src/noctern/compilation_unit.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,6 @@ namespace noctern {

compilation_unit::compilation_unit(const tokens& input)
: fn_defs_(noctern::from_range(std::ranges::filter_view(
input, [&](token t) { return input.id(t) == token_id::fn_intro; }))) {
input, [&](token t) { return input.id(t) == token_id::def; }))) {
}
}
4 changes: 2 additions & 2 deletions src/noctern/enum.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ namespace noctern {

/////
// The key functions. These are not hidden friends to work around a Clang bug.

template <typename Enum>
requires std::is_enum_v<Enum>
constexpr std::string_view stringify(Enum e) {
Expand All @@ -108,7 +108,7 @@ namespace noctern {
return std::invoke(std::forward<Fn>(fn), val<es>...);
});
}

// End key functions.
/////

Expand Down
1 change: 1 addition & 0 deletions src/noctern/inout.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
#include "./inout.hpp"
109 changes: 109 additions & 0 deletions src/noctern/inout.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
#pragma once

#include <concepts>

// Helpers for specifying in/out/inout parameters with odd lifetime requirements.
//
// If the lifetime requirements are standard (i.e. "live until function ends"), this isn't needed,
// but if we're holding a reference onto the parameter, we should pass the parameter via one of
// these aliases, constructed via CTAD.
//
// Example usage:
//
// void do_something(int x, int y, inout<vector<int>> storage) {
// do_something_else(x, inout(storage));
// do_something_else(y, inout(storage));
// }

namespace noctern {
// Marks inout parameters with odd lifetime requirements.
template <typename T>
class inout {
public:
explicit constexpr inout(T& ref)
: ref_(&ref) {
}

template <typename U>
requires std::convertible_to<U&, T&>
explicit constexpr inout(inout<U>& rhs)
: ref_(rhs.ref_) {
}

// This is not a value type.
constexpr inout(const inout&) = delete;

T& operator*() {
return *ref_;
}

T* operator->() {
return ref_;
}

private:
T* ref_;
};

// Marks input parameters with odd lifetime requirements.
template <typename T>
class in {
public:
explicit constexpr in(T& ref)
: ref_(&ref) {
}

template <typename U>
requires std::convertible_to<U&, T&>
explicit constexpr in(in<U>& rhs)
: ref_(rhs.ref_) {
}

// This is not a value type.
constexpr in(const in&) = delete;

T& operator*() {
return *ref_;
}

T* operator->() {
return ref_;
}

private:
T* ref_;
};

// Marks output parameters with odd lifetime requirements.
template <typename T>
class out {
public:
explicit constexpr out(T& ref)
: ref_(&ref) {
}

template <typename U>
requires std::convertible_to<U&, T&>
explicit constexpr out(out<U>& rhs)
: ref_(rhs.ref_) {
}

// This is not a value type.
constexpr out(const out&) = delete;

T& operator*() {
return *ref_;
}

T* operator->() {
return ref_;
}

private:
T* ref_;
};

static_assert(!std::copy_constructible<inout<int>>);
static_assert(!std::copy_constructible<in<int>>);
static_assert(!std::copy_constructible<out<int>>);
}
54 changes: 54 additions & 0 deletions src/noctern/inout.test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
#include "./inout.hpp"

#include <catch2/catch.hpp>

namespace noctern {
namespace {
struct point {
int x;
int y;
};

TEST_CASE("in works") {
int x = 42;
in<int> param = in(x);

CHECK(*param == 42);

SECTION("->") {
point x {1, 2};
in<point> param = in(x);
CHECK(param->x == 1);
}
}

TEST_CASE("out works") {
int x = 42;
out<int> param = out(x);
*param = 45;

CHECK(x == 45);

SECTION("->") {
point x {1, 2};
out<point> param = out(x);
CHECK(param->x == 1);
}
}

TEST_CASE("inout works") {
int x = 42;
inout<int> param = inout(x);
CHECK(*param == 42);
*param = 45;

CHECK(x == 45);

SECTION("->") {
point x {1, 2};
inout<point> param = inout(x);
CHECK(param->x == 1);
}
}
}
}
1 change: 1 addition & 0 deletions src/noctern/intern_table.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
#include "./intern_table.hpp"
Loading