@@ -0,0 +1,26 @@
|
|||||||
|
type arr = array $s8 11
|
||||||
|
|
||||||
|
func println $arr = native println
|
||||||
|
|
||||||
|
public func main = #main
|
||||||
|
main:
|
||||||
|
array $arr
|
||||||
|
store %0
|
||||||
|
push $s32 0
|
||||||
|
loop:
|
||||||
|
dup
|
||||||
|
push $s32 10
|
||||||
|
eq
|
||||||
|
jnz #end
|
||||||
|
body:
|
||||||
|
dup
|
||||||
|
load %0
|
||||||
|
push $s8 69
|
||||||
|
set
|
||||||
|
|
||||||
|
jmp #loop
|
||||||
|
end:
|
||||||
|
drop
|
||||||
|
load %0
|
||||||
|
call $arr println
|
||||||
|
ret
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
#ifndef FURAS_GEN_HPP
|
||||||
|
#define FURAS_GEN_HPP
|
||||||
|
|
||||||
|
#include "furas/lexer.hpp"
|
||||||
|
#include "furvm/module.hpp"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace furas {
|
||||||
|
|
||||||
|
struct generator_error {
|
||||||
|
enum type {
|
||||||
|
Success = 0,
|
||||||
|
Eof = 1,
|
||||||
|
|
||||||
|
UnexpectedEof = -1,
|
||||||
|
UnexpectedToken = -2,
|
||||||
|
UnknownCharacter = -3,
|
||||||
|
UnknownType = -4,
|
||||||
|
} type = Success;
|
||||||
|
std::string message;
|
||||||
|
};
|
||||||
|
|
||||||
|
class generator {
|
||||||
|
public:
|
||||||
|
struct result {
|
||||||
|
generator_error error;
|
||||||
|
furvm::mod mod;
|
||||||
|
};
|
||||||
|
public:
|
||||||
|
static result generate(lexer lexer);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace furas
|
||||||
|
|
||||||
|
#endif // FURAS_GEN_HPP
|
||||||
@@ -18,9 +18,14 @@ struct token {
|
|||||||
Sha256, /**< Label marker(`#`). */
|
Sha256, /**< Label marker(`#`). */
|
||||||
Percent, /**< Variable marker(`%`). The more the better. */
|
Percent, /**< Variable marker(`%`). The more the better. */
|
||||||
|
|
||||||
|
EqSign, /**< `=` */
|
||||||
Dot, /**< . */
|
Dot, /**< . */
|
||||||
|
Colon, /**< `:` */
|
||||||
|
|
||||||
// Keywords
|
// Keywords
|
||||||
|
Func, /**< `func` keyword for defining functions. */
|
||||||
|
Type, /**< `type` keyword for defining types. */
|
||||||
|
Native, /**< `native` keyword for native functions. :v: */
|
||||||
Import, /**< `import` keyword for importing functions and types. */
|
Import, /**< `import` keyword for importing functions and types. */
|
||||||
Public, /**< `public` access specifier. */
|
Public, /**< `public` access specifier. */
|
||||||
Private, /**< `private` access specifier. */
|
Private, /**< `private` access specifier. */
|
||||||
@@ -29,6 +34,7 @@ struct token {
|
|||||||
Push,
|
Push,
|
||||||
Array,
|
Array,
|
||||||
Get,
|
Get,
|
||||||
|
Set,
|
||||||
Drop,
|
Drop,
|
||||||
Dup,
|
Dup,
|
||||||
Clone,
|
Clone,
|
||||||
@@ -50,7 +56,7 @@ struct token {
|
|||||||
Load,
|
Load,
|
||||||
Store,
|
Store,
|
||||||
Call,
|
Call,
|
||||||
Jump,
|
Jmp,
|
||||||
Jnz,
|
Jnz,
|
||||||
Ret,
|
Ret,
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,546 @@
|
|||||||
|
#include "furas/gen.hpp"
|
||||||
|
|
||||||
|
#include "furas/token.hpp"
|
||||||
|
#include "furvm/function.hpp"
|
||||||
|
#include "furvm/fwd.hpp"
|
||||||
|
#include "furvm/instruction.hpp"
|
||||||
|
|
||||||
|
#include <cassert>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <limits>
|
||||||
|
#include <optional>
|
||||||
|
#include <stdexcept>
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <utility>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace furas {
|
||||||
|
|
||||||
|
using namespace std::string_literals;
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
struct instruction {
|
||||||
|
furvm::instruction_t fur{};
|
||||||
|
enum arg_type {
|
||||||
|
None = 0,
|
||||||
|
Type,
|
||||||
|
Constant,
|
||||||
|
Variable,
|
||||||
|
Function,
|
||||||
|
Label,
|
||||||
|
} arg = None;
|
||||||
|
};
|
||||||
|
|
||||||
|
// NOLINTBEGIN
|
||||||
|
std::unordered_map<enum token::type, instruction> instructions = {
|
||||||
|
{ token::Array, { furvm::instruction_t::Array, instruction::Type } },
|
||||||
|
{ token::Get, { furvm::instruction_t::Get } },
|
||||||
|
{ token::Set, { furvm::instruction_t::Set } },
|
||||||
|
{ token::Drop, { furvm::instruction_t::Drop } },
|
||||||
|
{ token::Dup, { furvm::instruction_t::Duplicate } },
|
||||||
|
{ token::Clone, { furvm::instruction_t::Clone } },
|
||||||
|
{ token::Ref, { furvm::instruction_t::Reference } },
|
||||||
|
{ token::Add, { furvm::instruction_t::Add } },
|
||||||
|
{ token::Sub, { furvm::instruction_t::Sub } },
|
||||||
|
{ token::Mul, { furvm::instruction_t::Mul } },
|
||||||
|
{ token::Div, { furvm::instruction_t::Div } },
|
||||||
|
{ token::Mod, { furvm::instruction_t::Mod } },
|
||||||
|
{ token::Eq, { furvm::instruction_t::Equals } },
|
||||||
|
{ token::Neq, { furvm::instruction_t::NotEquals } },
|
||||||
|
{ token::Lt, { furvm::instruction_t::LessThan } },
|
||||||
|
{ token::Gt, { furvm::instruction_t::GreaterThan } },
|
||||||
|
{ token::Le, { furvm::instruction_t::LessEqual } },
|
||||||
|
{ token::Ge, { furvm::instruction_t::GreaterEqual } },
|
||||||
|
{ token::Ptrof, { furvm::instruction_t::Pointerof } },
|
||||||
|
{ token::Sizeof, { furvm::instruction_t::Sizeof } },
|
||||||
|
{ token::Lenof, { furvm::instruction_t::Lengthof } },
|
||||||
|
{ token::Load, { furvm::instruction_t::Load, instruction::Variable } },
|
||||||
|
{ token::Store, { furvm::instruction_t::Store, instruction::Variable } },
|
||||||
|
{ token::Call, { furvm::instruction_t::Call, instruction::Function } },
|
||||||
|
{ token::Jmp, { furvm::instruction_t::Jump, instruction::Label } },
|
||||||
|
{ token::Jnz, { furvm::instruction_t::JumpNotZero, instruction::Label } },
|
||||||
|
{ token::Ret, { furvm::instruction_t::Return } },
|
||||||
|
};
|
||||||
|
// NOLINTEND
|
||||||
|
|
||||||
|
const char* token_type(enum token::type type) {
|
||||||
|
switch (type) {
|
||||||
|
case token::Identifier: return "identifier";
|
||||||
|
case token::Signed: return "signed";
|
||||||
|
case token::Unsigned: return "unsigned";
|
||||||
|
case token::Monkey: return "'@'";
|
||||||
|
case token::Dolar: return "'$'";
|
||||||
|
case token::Sha256: return "'#'";
|
||||||
|
case token::Percent: return "'%'";
|
||||||
|
case token::EqSign: return "'='";
|
||||||
|
case token::Dot: return "'.'";
|
||||||
|
case token::Colon: return "':'";
|
||||||
|
case token::Func: return "func";
|
||||||
|
case token::Type: return "type";
|
||||||
|
case token::Native: return "native";
|
||||||
|
case token::Import: return "import";
|
||||||
|
case token::Public: return "public";
|
||||||
|
case token::Private: return "private";
|
||||||
|
case token::Push: return "push";
|
||||||
|
case token::Array: return "array";
|
||||||
|
case token::Get: return "get";
|
||||||
|
case token::Set: return "set";
|
||||||
|
case token::Drop: return "drop";
|
||||||
|
case token::Dup: return "dup";
|
||||||
|
case token::Clone: return "clone";
|
||||||
|
case token::Ref: return "ref";
|
||||||
|
case token::Add: return "add";
|
||||||
|
case token::Sub: return "sub";
|
||||||
|
case token::Mul: return "mul";
|
||||||
|
case token::Div: return "div";
|
||||||
|
case token::Mod: return "mod";
|
||||||
|
case token::Eq: return "eq";
|
||||||
|
case token::Neq: return "neq";
|
||||||
|
case token::Lt: return "lt";
|
||||||
|
case token::Gt: return "gt";
|
||||||
|
case token::Le: return "le";
|
||||||
|
case token::Ge: return "ge";
|
||||||
|
case token::Ptrof: return "ptrof";
|
||||||
|
case token::Sizeof: return "sizeof";
|
||||||
|
case token::Lenof: return "lenof";
|
||||||
|
case token::Load: return "load";
|
||||||
|
case token::Store: return "store";
|
||||||
|
case token::Call: return "call";
|
||||||
|
case token::Jmp: return "jmp";
|
||||||
|
case token::Jnz: return "jnz";
|
||||||
|
case token::Ret: return "ret";
|
||||||
|
case token::Count: break;
|
||||||
|
}
|
||||||
|
throw std::runtime_error("unreachable");
|
||||||
|
}
|
||||||
|
|
||||||
|
struct mod_context {
|
||||||
|
struct label_context {
|
||||||
|
struct function_info {
|
||||||
|
std::string name;
|
||||||
|
furvm::function_sig signature;
|
||||||
|
bool pub;
|
||||||
|
};
|
||||||
|
|
||||||
|
static constexpr std::size_t INVALID = std::numeric_limits<std::size_t>::max();
|
||||||
|
|
||||||
|
std::size_t offset = INVALID;
|
||||||
|
std::vector<function_info> functions;
|
||||||
|
std::vector<std::size_t> unknowns;
|
||||||
|
};
|
||||||
|
|
||||||
|
furvm::mod mod;
|
||||||
|
std::unordered_map<std::pair<std::string, furvm::function_sig>,
|
||||||
|
furvm::function_h,
|
||||||
|
furlang::utility::
|
||||||
|
pair_hash<std::string, furvm::function_sig, std::hash<std::string>, furvm::detail::function_sig_hash>>
|
||||||
|
functions;
|
||||||
|
std::unordered_map<std::string, furvm::mod_type_h> types;
|
||||||
|
|
||||||
|
std::unordered_map<std::string, label_context> labels;
|
||||||
|
|
||||||
|
struct token_result {
|
||||||
|
generator_error error;
|
||||||
|
token token;
|
||||||
|
|
||||||
|
struct token* operator->() { return &token; }
|
||||||
|
struct token& operator*() { return token; }
|
||||||
|
|
||||||
|
bool operator!() const { return error.type != generator_error::Success; }
|
||||||
|
};
|
||||||
|
|
||||||
|
mod_context() {
|
||||||
|
types.emplace("s8", mod.emplace_type(furvm::mod_type::S8));
|
||||||
|
types.emplace("u8", mod.emplace_type(furvm::mod_type::U8));
|
||||||
|
types.emplace("s16", mod.emplace_type(furvm::mod_type::S16));
|
||||||
|
types.emplace("u16", mod.emplace_type(furvm::mod_type::U16));
|
||||||
|
types.emplace("s32", mod.emplace_type(furvm::mod_type::S32));
|
||||||
|
types.emplace("u32", mod.emplace_type(furvm::mod_type::U32));
|
||||||
|
types.emplace("s64", mod.emplace_type(furvm::mod_type::S64));
|
||||||
|
types.emplace("u64", mod.emplace_type(furvm::mod_type::U64));
|
||||||
|
}
|
||||||
|
|
||||||
|
static token_result next_token(lexer& lexer) {
|
||||||
|
auto token = lexer.next_token();
|
||||||
|
if (token.has_error()) {
|
||||||
|
switch (token.error().type) {
|
||||||
|
case lexer_error::EndOfFile:
|
||||||
|
return { { generator_error::UnexpectedEof, "Unexpected end of file" }, { token::Count } };
|
||||||
|
case lexer_error::UnknownCharacter:
|
||||||
|
return { { generator_error::UnknownCharacter, token.error().message }, { token::Count } };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return { { generator_error::Success }, *token };
|
||||||
|
}
|
||||||
|
|
||||||
|
static token_result eat_token(lexer& lexer, enum token::type type) {
|
||||||
|
auto token = next_token(lexer);
|
||||||
|
if (!token) return token;
|
||||||
|
if (token.token.type != type) {
|
||||||
|
return { { generator_error::UnexpectedToken,
|
||||||
|
"Expected "s + token_type(type) + ", but got " + token_type(token->type) },
|
||||||
|
{ token::Count } };
|
||||||
|
}
|
||||||
|
return { { generator_error::Success }, token.token };
|
||||||
|
}
|
||||||
|
|
||||||
|
generator_error generate(lexer& lexer) {
|
||||||
|
auto result = next_token(lexer);
|
||||||
|
if (result.error.type == generator_error::UnexpectedEof) return { generator_error::Eof };
|
||||||
|
if (!result) return result.error;
|
||||||
|
switch (result->type) {
|
||||||
|
case token::Identifier: {
|
||||||
|
std::string labelName = std::string(result->value.string);
|
||||||
|
|
||||||
|
result = eat_token(lexer, token::Colon);
|
||||||
|
if (!result) return result.error;
|
||||||
|
|
||||||
|
auto& label = labels[labelName];
|
||||||
|
if (label.offset != label_context::INVALID)
|
||||||
|
return { generator_error::UnexpectedToken, "Label "s + labelName + " already exists" };
|
||||||
|
label.offset = mod.bytecode().size();
|
||||||
|
|
||||||
|
for (auto& func : label.functions) {
|
||||||
|
furvm::function_h handle =
|
||||||
|
(func.pub ? mod.emplace_function(func.name, std::move(func.signature), label.offset)
|
||||||
|
: mod.emplace_function(std::move(func.signature), label.offset));
|
||||||
|
|
||||||
|
functions.emplace(std::make_pair(std::move(func.name), std::move(func.signature)), handle);
|
||||||
|
|
||||||
|
handle.dispatch();
|
||||||
|
}
|
||||||
|
for (auto unknown : label.unknowns) {
|
||||||
|
std::ptrdiff_t jmpOff = static_cast<std::ptrdiff_t>(label.offset - unknown);
|
||||||
|
if (jmpOff < std::numeric_limits<std::int8_t>::min() ||
|
||||||
|
jmpOff > std::numeric_limits<std::int8_t>::max()) {
|
||||||
|
assert(false); // TODO: Further jumps are not implemented
|
||||||
|
}
|
||||||
|
mod.bytecode()[unknown - 1] = jmpOff;
|
||||||
|
}
|
||||||
|
label.functions = {};
|
||||||
|
label.unknowns = {};
|
||||||
|
return { generator_error::Success };
|
||||||
|
}
|
||||||
|
|
||||||
|
case token::Func:
|
||||||
|
case token::Type:
|
||||||
|
case token::Public:
|
||||||
|
case token::Private: {
|
||||||
|
bool pub = false;
|
||||||
|
if (result->type == token::Public) {
|
||||||
|
pub = true;
|
||||||
|
result = next_token(lexer);
|
||||||
|
if (!result) return { result.error };
|
||||||
|
} else if (result->type == token::Private) {
|
||||||
|
result = next_token(lexer);
|
||||||
|
if (!result) return { result.error };
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result->type == token::Func) {
|
||||||
|
auto nameRes = next_token(lexer);
|
||||||
|
if (!nameRes) return nameRes.error;
|
||||||
|
|
||||||
|
furvm::function_sig signature;
|
||||||
|
|
||||||
|
result = next_token(lexer);
|
||||||
|
if (!result) return result.error;
|
||||||
|
while (result->type == token::Dolar) {
|
||||||
|
result = eat_token(lexer, token::Identifier);
|
||||||
|
if (!result) return result.error;
|
||||||
|
if (auto it = types.find(std::string(result->value.string)); it != types.end()) {
|
||||||
|
signature.params.push_back(it->second);
|
||||||
|
} else {
|
||||||
|
return { generator_error::UnknownType, "Unknown type "s + std::string(result->value.string) };
|
||||||
|
}
|
||||||
|
|
||||||
|
result = next_token(lexer);
|
||||||
|
if (!result) return result.error;
|
||||||
|
}
|
||||||
|
if (result->type != token::EqSign) return result.error;
|
||||||
|
|
||||||
|
result = next_token(lexer);
|
||||||
|
if (!result) return result.error;
|
||||||
|
if (result->type == token::Dolar) {
|
||||||
|
result = eat_token(lexer, token::Identifier);
|
||||||
|
if (!result) return result.error;
|
||||||
|
if (auto it = types.find(std::string(result->value.string)); it != types.end()) {
|
||||||
|
signature.returnType = it->second;
|
||||||
|
} else {
|
||||||
|
return { generator_error::UnknownType, "Unknown type "s + std::string(result->value.string) };
|
||||||
|
}
|
||||||
|
|
||||||
|
result = next_token(lexer);
|
||||||
|
if (!result) return result.error;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (result->type) {
|
||||||
|
case token::Sha256: {
|
||||||
|
result = eat_token(lexer, token::Identifier);
|
||||||
|
if (!result) return result.error;
|
||||||
|
|
||||||
|
std::string name = std::string(nameRes->value.string);
|
||||||
|
|
||||||
|
std::string labelName = std::string(result->value.string);
|
||||||
|
std::size_t offset = 0;
|
||||||
|
if (auto it = labels.find(labelName); it != labels.end()) {
|
||||||
|
offset = it->second.offset;
|
||||||
|
} else {
|
||||||
|
labels[labelName].functions.emplace_back(
|
||||||
|
label_context::function_info{ std::move(name), std::move(signature), pub });
|
||||||
|
return { generator_error::Success };
|
||||||
|
}
|
||||||
|
|
||||||
|
furvm::function_h handle =
|
||||||
|
(pub ? mod.emplace_function(name, signature, offset) : mod.emplace_function(signature, offset));
|
||||||
|
|
||||||
|
functions.emplace(std::make_pair(name, std::move(signature)), handle);
|
||||||
|
|
||||||
|
handle.dispatch();
|
||||||
|
return { generator_error::Success };
|
||||||
|
}
|
||||||
|
case token::Native: {
|
||||||
|
result = eat_token(lexer, token::Identifier);
|
||||||
|
if (!result) return result.error;
|
||||||
|
|
||||||
|
std::string nativeName = std::string(result->value.string);
|
||||||
|
|
||||||
|
std::string name = std::string(nameRes->value.string);
|
||||||
|
|
||||||
|
furvm::function_h handle = (pub ? mod.emplace_function(name, signature, std::move(nativeName))
|
||||||
|
: mod.emplace_function(signature, std::move(nativeName)));
|
||||||
|
|
||||||
|
functions.emplace(std::make_pair(name, std::move(signature)), handle);
|
||||||
|
|
||||||
|
handle.dispatch();
|
||||||
|
return { generator_error::Success };
|
||||||
|
}
|
||||||
|
case token::Import: {
|
||||||
|
throw std::runtime_error("unimplemented");
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return { generator_error::UnexpectedToken,
|
||||||
|
"Unexpected token "s + token_type(result->type) +
|
||||||
|
", expected label name, `native`, or `import`" };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result->type == token::Type) {
|
||||||
|
auto nameRes = eat_token(lexer, token::Identifier);
|
||||||
|
if (!nameRes) return nameRes.error;
|
||||||
|
|
||||||
|
result = eat_token(lexer, token::EqSign);
|
||||||
|
if (!result) return result.error;
|
||||||
|
|
||||||
|
result = next_token(lexer);
|
||||||
|
if (!result) return result.error;
|
||||||
|
switch (result->type) {
|
||||||
|
case token::Import: {
|
||||||
|
auto typeNameRes = eat_token(lexer, token::Identifier);
|
||||||
|
if (!typeNameRes) return typeNameRes.error;
|
||||||
|
return { generator_error::Success };
|
||||||
|
}
|
||||||
|
case token::Array: {
|
||||||
|
result = eat_token(lexer, token::Dolar);
|
||||||
|
if (!result) return result.error;
|
||||||
|
|
||||||
|
auto typeNameRes = eat_token(lexer, token::Identifier);
|
||||||
|
if (!typeNameRes) return typeNameRes.error;
|
||||||
|
|
||||||
|
auto size = eat_token(lexer, token::Unsigned);
|
||||||
|
if (!size) return size.error;
|
||||||
|
|
||||||
|
furvm::mod_type_id innerId = 0;
|
||||||
|
if (auto it = types.find(std::string(typeNameRes->value.string)); it != types.end()) {
|
||||||
|
innerId = it->second.id();
|
||||||
|
} else {
|
||||||
|
return { generator_error::UnknownType,
|
||||||
|
"Unknown type "s + std::string(typeNameRes->value.string) };
|
||||||
|
}
|
||||||
|
|
||||||
|
auto type = mod.emplace_type(innerId, size->value.uint);
|
||||||
|
types.emplace(std::string(nameRes->value.string), type);
|
||||||
|
type.dispatch();
|
||||||
|
|
||||||
|
return { generator_error::Success };
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return { generator_error::UnexpectedToken,
|
||||||
|
"Unexpected token "s + token_type(result->type) +
|
||||||
|
", expected either type, `import`, or `array`" };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return { generator_error::UnexpectedToken,
|
||||||
|
"Unexpected token "s + token_type(result->type) + ", expected either `func` or `type`" };
|
||||||
|
}
|
||||||
|
|
||||||
|
case token::Push: {
|
||||||
|
auto result = eat_token(lexer, token::Dolar);
|
||||||
|
if (!result) return result.error;
|
||||||
|
auto typeName = eat_token(lexer, token::Identifier);
|
||||||
|
if (!typeName) return result.error;
|
||||||
|
auto it = types.find(std::string(typeName->value.string));
|
||||||
|
if (it == types.end())
|
||||||
|
return { generator_error::UnexpectedToken, "Unknown type "s + std::string(typeName->value.string) };
|
||||||
|
auto value = eat_token(lexer, token::Unsigned);
|
||||||
|
if (!value) return result.error;
|
||||||
|
auto type = it->second;
|
||||||
|
switch (type->type) {
|
||||||
|
case furvm::mod_type::S8: {
|
||||||
|
mod.bytecode().push_back(static_cast<furvm::byte>(furvm::instruction_t::PushS8));
|
||||||
|
} break;
|
||||||
|
case furvm::mod_type::U8: {
|
||||||
|
mod.bytecode().push_back(static_cast<furvm::byte>(furvm::instruction_t::PushU8));
|
||||||
|
} break;
|
||||||
|
case furvm::mod_type::S16: {
|
||||||
|
mod.bytecode().push_back(static_cast<furvm::byte>(furvm::instruction_t::PushS16));
|
||||||
|
} break;
|
||||||
|
case furvm::mod_type::U16: {
|
||||||
|
mod.bytecode().push_back(static_cast<furvm::byte>(furvm::instruction_t::PushU16));
|
||||||
|
} break;
|
||||||
|
case furvm::mod_type::S32: {
|
||||||
|
mod.bytecode().push_back(static_cast<furvm::byte>(furvm::instruction_t::PushS32));
|
||||||
|
} break;
|
||||||
|
case furvm::mod_type::U32: {
|
||||||
|
mod.bytecode().push_back(static_cast<furvm::byte>(furvm::instruction_t::PushU32));
|
||||||
|
} break;
|
||||||
|
default:
|
||||||
|
return { generator_error::UnexpectedToken, "Unexpected type "s + std::string(typeName->value.string) };
|
||||||
|
}
|
||||||
|
return { generator_error::Success };
|
||||||
|
}
|
||||||
|
|
||||||
|
case token::Array:
|
||||||
|
case token::Get:
|
||||||
|
case token::Set:
|
||||||
|
case token::Drop:
|
||||||
|
case token::Dup:
|
||||||
|
case token::Clone:
|
||||||
|
case token::Ref:
|
||||||
|
case token::Add:
|
||||||
|
case token::Sub:
|
||||||
|
case token::Mul:
|
||||||
|
case token::Div:
|
||||||
|
case token::Mod:
|
||||||
|
case token::Eq:
|
||||||
|
case token::Neq:
|
||||||
|
case token::Lt:
|
||||||
|
case token::Gt:
|
||||||
|
case token::Le:
|
||||||
|
case token::Ge:
|
||||||
|
case token::Ptrof:
|
||||||
|
case token::Sizeof:
|
||||||
|
case token::Lenof:
|
||||||
|
case token::Load:
|
||||||
|
case token::Store:
|
||||||
|
case token::Call:
|
||||||
|
case token::Jmp:
|
||||||
|
case token::Jnz:
|
||||||
|
case token::Ret: {
|
||||||
|
auto it = instructions.find(result->type);
|
||||||
|
assert(it != instructions.end());
|
||||||
|
mod.bytecode().push_back(static_cast<furvm::byte>(it->second.fur));
|
||||||
|
switch (it->second.arg) {
|
||||||
|
case instruction::None: break;
|
||||||
|
case instruction::Type: {
|
||||||
|
result = eat_token(lexer, token::Dolar);
|
||||||
|
if (!result) return result.error;
|
||||||
|
result = eat_token(lexer, token::Identifier);
|
||||||
|
if (!result) return result.error;
|
||||||
|
auto type = types.find(std::string(result->value.string));
|
||||||
|
if (type == types.end())
|
||||||
|
return { generator_error::UnknownType, "Unknown type "s + std::string(result->value.string) };
|
||||||
|
auto id = type->second.id();
|
||||||
|
mod.bytecode().push_back((id >> 0) & 0xFF);
|
||||||
|
mod.bytecode().push_back((id >> 8) & 0xFF);
|
||||||
|
mod.bytecode().push_back((id >> 16) & 0xFF);
|
||||||
|
mod.bytecode().push_back((id >> 24) & 0xFF);
|
||||||
|
} break;
|
||||||
|
case instruction::Constant: {
|
||||||
|
assert(false); // TODO: Unimplemented
|
||||||
|
} break;
|
||||||
|
case instruction::Variable: {
|
||||||
|
result = eat_token(lexer, token::Percent);
|
||||||
|
if (!result) return result.error;
|
||||||
|
result = eat_token(lexer, token::Unsigned);
|
||||||
|
if (!result) return result.error;
|
||||||
|
std::uint16_t var = result->value.uint;
|
||||||
|
mod.bytecode().push_back((var >> 0) & 0xFF);
|
||||||
|
mod.bytecode().push_back((var >> 8) & 0xFF);
|
||||||
|
} break;
|
||||||
|
case instruction::Function: {
|
||||||
|
furvm::function_sig signature;
|
||||||
|
while ((result = next_token(lexer)).error.type == generator_error::Success &&
|
||||||
|
result->type == token::Dolar) {
|
||||||
|
result = eat_token(lexer, token::Identifier);
|
||||||
|
if (!result) return result.error;
|
||||||
|
auto type = types.find(std::string(result->value.string));
|
||||||
|
if (type == types.end())
|
||||||
|
return { generator_error::UnknownType, "Unknown type "s + std::string(result->value.string) };
|
||||||
|
signature.params.push_back(type->second);
|
||||||
|
}
|
||||||
|
if (!result || result->type != token::Identifier) return result.error;
|
||||||
|
std::string name(result->value.string);
|
||||||
|
|
||||||
|
auto func = functions.find(std::make_pair(name, signature));
|
||||||
|
if (func == functions.end())
|
||||||
|
return { generator_error::UnknownType, "Unknown type "s + std::string(result->value.string) };
|
||||||
|
auto id = func->second.id();
|
||||||
|
mod.bytecode().push_back((id >> 0) & 0xFF);
|
||||||
|
mod.bytecode().push_back((id >> 8) & 0xFF);
|
||||||
|
} break;
|
||||||
|
case instruction::Label: {
|
||||||
|
result = eat_token(lexer, token::Sha256);
|
||||||
|
if (!result) return result.error;
|
||||||
|
result = eat_token(lexer, token::Identifier);
|
||||||
|
if (!result) return result.error;
|
||||||
|
auto& label = labels[std::string(result->value.string)];
|
||||||
|
auto offset = label.offset;
|
||||||
|
if (offset == label_context::INVALID) {
|
||||||
|
label.unknowns.push_back(mod.bytecode().size() + 1);
|
||||||
|
mod.bytecode().push_back(0);
|
||||||
|
return { generator_error::Success };
|
||||||
|
}
|
||||||
|
std::ptrdiff_t jmpOff = static_cast<std::ptrdiff_t>(offset - mod.bytecode().size() + 1);
|
||||||
|
if (jmpOff < std::numeric_limits<std::int8_t>::min() ||
|
||||||
|
jmpOff > std::numeric_limits<std::int8_t>::max()) {
|
||||||
|
assert(false); // TODO: Further jumps are not implemented
|
||||||
|
}
|
||||||
|
mod.bytecode().push_back(jmpOff);
|
||||||
|
} break;
|
||||||
|
}
|
||||||
|
return { generator_error::Success };
|
||||||
|
}
|
||||||
|
|
||||||
|
case token::Signed:
|
||||||
|
case token::Unsigned:
|
||||||
|
case token::Colon:
|
||||||
|
case token::Monkey:
|
||||||
|
case token::Dolar:
|
||||||
|
case token::Sha256:
|
||||||
|
case token::Percent:
|
||||||
|
case token::EqSign:
|
||||||
|
case token::Dot:
|
||||||
|
case token::Native:
|
||||||
|
case token::Import:
|
||||||
|
case token::Count: break;
|
||||||
|
}
|
||||||
|
return { generator_error::UnexpectedToken, "Unexpected token "s + token_type(result->type) };
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
generator::result generator::generate(lexer lexer) {
|
||||||
|
mod_context context;
|
||||||
|
generator_error error;
|
||||||
|
do {
|
||||||
|
error = context.generate(lexer);
|
||||||
|
} while (error.type == generator_error::Success);
|
||||||
|
if (error.type == generator_error::Eof) return { { generator_error::Success }, std::move(context.mod) };
|
||||||
|
return { error };
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace furas
|
||||||
+7
-1
@@ -33,6 +33,9 @@ token_r lexer::next_token() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static std::unordered_map<std::string_view, enum token::type> s_tokens = {
|
static std::unordered_map<std::string_view, enum token::type> s_tokens = {
|
||||||
|
{ "func", token::Func },
|
||||||
|
{ "type", token::Type },
|
||||||
|
{ "native", token::Native },
|
||||||
{ "import", token::Import },
|
{ "import", token::Import },
|
||||||
{ "public", token::Public },
|
{ "public", token::Public },
|
||||||
{ "private", token::Private },
|
{ "private", token::Private },
|
||||||
@@ -40,6 +43,7 @@ token_r lexer::next_token() {
|
|||||||
{ "push", token::Push },
|
{ "push", token::Push },
|
||||||
{ "array", token::Array },
|
{ "array", token::Array },
|
||||||
{ "get", token::Get },
|
{ "get", token::Get },
|
||||||
|
{ "set", token::Set },
|
||||||
{ "drop", token::Drop },
|
{ "drop", token::Drop },
|
||||||
{ "dup", token::Dup },
|
{ "dup", token::Dup },
|
||||||
{ "clone", token::Clone },
|
{ "clone", token::Clone },
|
||||||
@@ -61,7 +65,7 @@ token_r lexer::next_token() {
|
|||||||
{ "load", token::Load },
|
{ "load", token::Load },
|
||||||
{ "store", token::Store },
|
{ "store", token::Store },
|
||||||
{ "call", token::Call },
|
{ "call", token::Call },
|
||||||
{ "jump", token::Jump },
|
{ "jmp", token::Jmp },
|
||||||
{ "jnz", token::Jnz },
|
{ "jnz", token::Jnz },
|
||||||
{ "ret", token::Ret },
|
{ "ret", token::Ret },
|
||||||
};
|
};
|
||||||
@@ -80,7 +84,9 @@ token_r lexer::next_token() {
|
|||||||
case '$': ++m_cursor; return { token::Dolar };
|
case '$': ++m_cursor; return { token::Dolar };
|
||||||
case '#': ++m_cursor; return { token::Sha256 };
|
case '#': ++m_cursor; return { token::Sha256 };
|
||||||
case '%': ++m_cursor; return { token::Percent };
|
case '%': ++m_cursor; return { token::Percent };
|
||||||
|
case '=': ++m_cursor; return { token::EqSign };
|
||||||
case '.': ++m_cursor; return { token::Dot };
|
case '.': ++m_cursor; return { token::Dot };
|
||||||
|
case ':': ++m_cursor; return { token::Colon };
|
||||||
default:
|
default:
|
||||||
return token_r{
|
return token_r{
|
||||||
lexer_error{ lexer_error::UnknownCharacter, location(), "Unknown character '"s + m_content[m_cursor] + "'" }
|
lexer_error{ lexer_error::UnknownCharacter, location(), "Unknown character '"s + m_content[m_cursor] + "'" }
|
||||||
|
|||||||
+30
-9
@@ -1,18 +1,39 @@
|
|||||||
|
#include "furas/gen.hpp"
|
||||||
#include "furas/lexer.hpp"
|
#include "furas/lexer.hpp"
|
||||||
|
|
||||||
|
#include <fstream>
|
||||||
|
#include <furvm/module.hpp>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
int main(void) { // NOLINT
|
int main(int argc, char** argv) { // NOLINT
|
||||||
std::string source = "%uwu 10 public private int le";
|
if (argc < 2) {
|
||||||
furas::lexer lexer("<NONE>", source);
|
std::cerr << "feed me more arguments daddy >_<\n";
|
||||||
while (true) {
|
return 1;
|
||||||
furas::token_r token = lexer.next_token();
|
|
||||||
if (token.has_error()) {
|
|
||||||
std::cout << token.error().message << '\n';
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
std::cout << token->type << '\n';
|
if (argc > 2) {
|
||||||
|
std::cerr << "too much O_O\n";
|
||||||
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const char* filepath = argv[1];
|
||||||
|
|
||||||
|
std::ifstream file(filepath, std::ios::binary | std::ios::ate | std::ios::in);
|
||||||
|
if (!file.is_open()) {
|
||||||
|
std::cerr << "file won't open >~<\n";
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
std::string content;
|
||||||
|
content.resize(file.tellg());
|
||||||
|
file.seekg(0);
|
||||||
|
file.read(content.data(), static_cast<std::streamsize>(content.size()));
|
||||||
|
|
||||||
|
auto result = furas::generator::generate(furas::lexer(filepath, content));
|
||||||
|
if (result.error.type != furas::generator_error::Success) {
|
||||||
|
std::cerr << result.error.message << '\n';
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
result.mod.serialize(std::cout);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user