Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Compile Trilangle to C #26

Merged
merged 18 commits into from
Sep 28, 2023
Merged
2 changes: 1 addition & 1 deletion .github/workflows/lint-build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ name: Lint Builds
paths:
# It would be nice if I could specify this per-job, but I'm not making a whole new file just to avoid running
# clang for web-only changes.
- src/*
- src/**
- wasm/*
- .clang-format
- .github/workflows/lint-build.yml
Expand Down
6 changes: 4 additions & 2 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ name: Run Tests
branches:
- '**'
paths:
- src/*
- src/**
- tests/**
- .github/workflows/test.yml
- .gitmodules
Expand Down Expand Up @@ -38,11 +38,13 @@ jobs:
run: |
g++ -Og -std=gnu++17 src/*.cpp -o trilangle
export PATH="${PATH}:${PWD}"
for script in tests/cli/*/index.sh
export CC="${CC:-gcc}"
for script in tests/cli/*/index.sh tests/compiler/*/index.sh
do
echo "Running ${script}..."
"$script"
done
echo 'Done.'
cli-tests-windows:
runs-on: windows-latest
name: Run CLI Tests for Windows
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,4 @@ wasm/*.css
*.obj
trilangle
*.nativecodeanalysis.xml
out.c
152 changes: 152 additions & 0 deletions src/compiler.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
// This file is not in the compiler folder because the MSVC build script requires this folder to contain all the .cpp
// files.
#include "compiler.hh"
#include <iostream>
#include "compiler/strings.hh"

using std::cerr;
using std::endl;

void compiler::write_state(std::ostream& os) {
if (m_fragments == nullptr) {
build_state();
}

os << header;

for (size_t i = 0; i < m_fragments->size(); ++i) {
const std::vector<instruction>& frag = *m_fragments->at(i);
for (size_t j = 0; j < frag.size(); ++j) {
os << "\nlbl" << i << '_' << j << ": ";
get_c_code(frag[j], os);
}
}

os << footer << std::flush;
}

void compiler::get_c_code(const instruction& i, std::ostream& os) {
using op = instruction::operation;

switch (i.m_op) {
case op::BNG: {
os << "if (lws_top(stack) < 0) goto lbl";
const auto& choice = i.m_arg.choice;
const auto &dest1 = choice.first, &dest2 = choice.second;
os << dest2.first << '_' << dest2.second << "; goto lbl" << dest1.first << '_' << dest1.second << ';';
return;
}
case op::JMP: {
os << "goto lbl";
const auto& dest = i.m_arg.next;
os << dest.first << '_' << dest.second << ';';
return;
}
case op::TKL:
case op::EXT:
os << "lws_deinit(stack); return 0;";
return;
case op::TJN:
case op::TSP:
cerr << "Threading is not supported for compiled programs." << endl;
exit(EXIT_FAILURE);
case op::NOP:
break;
case op::ADD:
os << "lws_push(stack, lws_pop(stack) + lws_pop(stack));";
return;
case op::SUB:
// The calls need to be well-ordered
os << "{ int32_t tmp = lws_pop(stack); lws_push(stack, lws_pop(stack) - tmp); }";
return;
case op::MUL:
os << "lws_push(stack, lws_pop(stack) * lws_pop(stack));";
return;
case op::DIV:
os << "{ int32_t tmp = lws_pop(stack); lws_push(stack, lws_pop(stack) / tmp); }";
return;
case op::MOD:
os << "{ int32_t tmp = lws_pop(stack); lws_push(stack, lws_pop(stack) % tmp); }";
return;
case op::PSC: {
os << "lws_push(stack,";
const auto value = i.m_arg.number;
os << static_cast<int32_t>(value) << ");";
return;
}
case op::PSI: {
os << "lws_push(stack,";
const auto value = i.m_arg.number;
os << static_cast<int32_t>(value - (int24_t)'0') << ");";
return;
}
case op::POP:
os << "(void)lws_pop(stack);";
return;
case op::INC:
os << "lws_push(stack, (lws_pop(stack) + 1) << 8 >> 8);";
return;
case op::DEC:
os << "lws_push(stack, (lws_pop(stack) - 1) << 8 >> 8);";
return;
case op::AND:
os << "lws_push(stack, lws_pop(stack) & lws_pop(stack));";
return;
case op::IOR:
os << "lws_push(stack, lws_pop(stack) | lws_pop(stack));";
return;
case op::XOR:
os << "lws_push(stack, lws_pop(stack) ^ lws_pop(stack));";
return;
case op::NOT:
os << "lws_push(stack, ~lws_pop(stack));";
return;
case op::GTC:
// FIXME: reports (some?) high unicode characters as WEOF (on some systems?)
bbrk24 marked this conversation as resolved.
Show resolved Hide resolved
os << "{ wint_t temp = getwchar(); lws_push(stack, temp == WEOF ? -1 : temp); }";
return;
case op::PTC:
os << "putwchar(lws_top(stack));";
return;
case op::GTI:
os << "{"
"int32_t i = -1;"
"while (!(feof(stdin) || scanf(\"%\" SCNi32, &i))) (void)getchar();"
"lws_push(stack, i);"
"}";
return;
case op::PTI:
os << R"( printf("%" PRId32 "\n", lws_top(stack)); )";
return;
case op::IDX:
os << "lws_push(stack, lws_index(stack, lws_pop(stack)));";
return;
case op::DUP:
os << "lws_push(stack, lws_top(stack));";
return;
case op::DP2: {
const char* str = "lws_push(stack, lws_index(stack, 1));";
os << str << str;
return;
}
case op::RND:
case op::GDT:
case op::GTM:
cerr << "Nondeterministic instructions are not yet supported for compiled programs." << endl;
Comment on lines +140 to +143
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be doable with time() and rand(), but seeding the RNG is a problem

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

time() doesn't provide sub-second precision however, so it might work for GDT but not GTM.

exit(EXIT_FAILURE);
case op::EXP:
os << "lws_push(stack, 1 << lws_pop(stack));";
return;
case op::SWP:
os << "{"
"int32_t temp1 = lws_pop(stack);"
"int32_t temp2 = lws_pop(stack);"
"lws_push(stack, temp1);"
"lws_push(stack, temp2);"
"}";
return;
default:
cerr << "Unknown opcode." << endl;
exit(EXIT_FAILURE);
}
}
11 changes: 11 additions & 0 deletions src/compiler.hh
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#pragma once

#include "instruction_scanner.hh"

class compiler : public instruction_scanner {
public:
CONSTEXPR_VECTOR compiler(NONNULL_PTR(const program) p) : instruction_scanner(p) {}
void write_state(std::ostream& os);
private:
static void get_c_code(const instruction& i, std::ostream& os);
};
54 changes: 54 additions & 0 deletions src/compiler/lightweight_stack.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
// This entire file is a string. Comment out the opening quote marker to get syntax highlighting while editing.
R"(
#include <stdlib.h>
#include <stdint.h>

#ifndef __GNUC__
#define __builtin_expect(exp, c) exp
#endif

struct lightweight_stack_s {
int32_t* base;
size_t length;
size_t capacity;
};

typedef struct lightweight_stack_s* lws;

static inline int32_t lws_pop(lws stack) {
return stack->base[--stack->length];
}

static inline int lws_push(lws stack, int32_t value) {
if (__builtin_expect(stack->length == stack->capacity, 0L)) {
bbrk24 marked this conversation as resolved.
Show resolved Hide resolved
size_t new_cap = stack->capacity * 7 / 4;
int32_t* new_ptr = (int32_t*)realloc(stack->base, new_cap * sizeof(int32_t));
if (new_ptr == NULL) {
return 0;
}
stack->base = new_ptr;
stack->capacity = new_cap;
}
stack->base[stack->length++] = value;
return 1;
}

static inline int32_t lws_index(const lws stack, int32_t index) {
return stack->base[stack->length - index - 1];
}

static inline int32_t lws_top(const lws stack) {
return lws_index(stack, 0);
}

static inline void lws_init(lws stack) {
stack->length = 0;
stack->base = (int32_t*)calloc(4, sizeof(int32_t));
// Note: actually handling errors prevents the compiler from doing optimizations we want
stack->capacity = 4;
}

static inline void lws_deinit(const lws stack) {
free(stack->base);
}
//)"
16 changes: 16 additions & 0 deletions src/compiler/strings.hh
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
#pragma once

constexpr const char* header =
#include "lightweight_stack.h"
R"#(
#include <stdio.h>
#include <inttypes.h>
#include <wchar.h>

int main(int argc, const char** argv) {
struct lightweight_stack_s stack_storage;
lws stack = &stack_storage;
lws_init(stack);
)#";

constexpr const char* footer = "}\n";
Loading