screaming over here... anyhow you can add yo shit now
This commit is contained in:
parent
5cd0ea9206
commit
8a1dd20bd9
@ -6,6 +6,7 @@ project(xlang
|
|||||||
enable_language(CXX)
|
enable_language(CXX)
|
||||||
set(CMAKE_CXX_COMPILER /bin/clang++)
|
set(CMAKE_CXX_COMPILER /bin/clang++)
|
||||||
set(CXX_STANDARD 23)
|
set(CXX_STANDARD 23)
|
||||||
|
set(CMAKE_CXX_FLAGS "-std=c++${CXX_STANDARD}")
|
||||||
|
|
||||||
include_directories(BEFORE SYSTEM /usr/include ./)
|
include_directories(BEFORE SYSTEM /usr/include ./)
|
||||||
link_directories(BEFORE $ENV{LD_LIBRARY_PATH} ${CMAKE_BINARY_DIR}/ )
|
link_directories(BEFORE $ENV{LD_LIBRARY_PATH} ${CMAKE_BINARY_DIR}/ )
|
||||||
|
|||||||
@ -2,9 +2,29 @@
|
|||||||
Basically just an outline of how to type things out and how things work syntactically.
|
Basically just an outline of how to type things out and how things work syntactically.
|
||||||
$$
|
$$
|
||||||
\begin{align}
|
\begin{align}
|
||||||
[\text{exit}] &\to exit([\text{expr}]);
|
[\text{prog}] &\to [\text{stmt}]^* \\
|
||||||
\\
|
[\text{stmt}]^* &\to
|
||||||
[\text{expr}] &\to \text{int\_lit}
|
\begin{cases}
|
||||||
|
\text{exit}([\text{expr}]); \\
|
||||||
|
\text{var}\space\text{ident} = [\text{expr}]; \\
|
||||||
|
\end{cases} \\
|
||||||
|
[\text{expr}] &\to
|
||||||
|
\begin{cases}
|
||||||
|
\text{[Term]}
|
||||||
|
\text{[BinExpr]} \\
|
||||||
|
\end{cases} \\
|
||||||
|
[\text{BinExpr}]
|
||||||
|
\begin{cases}
|
||||||
|
[\text[{Expr}] * \text{[Expr]}] &prec=3\\
|
||||||
|
[\text[{Expr}] \div \text{[Expr]}] &prec=2\\
|
||||||
|
[\text[{Expr}] + \text{[Expr]}] &prec=1\\
|
||||||
|
[\text[{Expr}] - \text{[Expr]}] &prec=0\\
|
||||||
|
\end{cases} \\
|
||||||
|
[\text{Term}] &\to
|
||||||
|
\begin{cases}
|
||||||
|
\text{int\_lit} \\
|
||||||
|
\text{ident} \\
|
||||||
|
\end{cases} \\
|
||||||
\end{align}
|
\end{align}
|
||||||
$$
|
$$
|
||||||
### Note: i hate laytec or however you spell it. Its hell.
|
### Note: i hate katex or however you spell it. Its hell.
|
||||||
1
examples/compact/exit.x
Normal file
1
examples/compact/exit.x
Normal file
@ -0,0 +1 @@
|
|||||||
|
exit(69);
|
||||||
2
examples/compact/exitadd.x
Normal file
2
examples/compact/exitadd.x
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
var x = 69 + 69 + 42;
|
||||||
|
exit(x + 2);
|
||||||
3
examples/compact/exitvar.x
Normal file
3
examples/compact/exitvar.x
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
var x = 7;
|
||||||
|
var y = 8;
|
||||||
|
exit(x);
|
||||||
@ -1 +0,0 @@
|
|||||||
exit 11;
|
|
||||||
33
src/arena.hxx
Normal file
33
src/arena.hxx
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <optional>
|
||||||
|
#include <iostream>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
class ArenaAllocator {
|
||||||
|
public:
|
||||||
|
inline explicit ArenaAllocator(size_t bytes) : m_size(bytes) {
|
||||||
|
m_buffer = static_cast<std::byte*>(malloc(m_size));
|
||||||
|
m_offset = m_buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
inline T* alloc() {
|
||||||
|
void* offset = m_offset;
|
||||||
|
m_offset += sizeof(T);
|
||||||
|
return static_cast<T*>(offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline ArenaAllocator(const ArenaAllocator& other) = delete;
|
||||||
|
|
||||||
|
inline ArenaAllocator operator=(const ArenaAllocator& other) = delete;
|
||||||
|
|
||||||
|
inline ~ArenaAllocator() {
|
||||||
|
free(m_buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
size_t m_size;
|
||||||
|
std::byte* m_buffer;
|
||||||
|
std::byte* m_offset;
|
||||||
|
};
|
||||||
42
src/error.hxx
Normal file
42
src/error.hxx
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
|
namespace err {
|
||||||
|
inline void syntax(std::optional<std::string> err) {
|
||||||
|
std::cerr << "xlang: \033[1;31merror:\033[0m \033[1;37mexpected '" << err.value() << "'\033[0m" << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void expression(std::optional<std::string> err) {
|
||||||
|
if (err.has_value()) {
|
||||||
|
std::cerr << "xlang: \033[1;31merror:\033[0m \033[1;37minvalid expression `" << err.value() << "'\033[0m" << std::endl;
|
||||||
|
} else {
|
||||||
|
std::cerr << "xlang: \033[1;31merror:\033[0m \033[1;37minvalid expression\033[0m" << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void unknowntype(std::optional<std::string> err) {
|
||||||
|
if (err.has_value()) {
|
||||||
|
std::cerr << "xlang: \033[1;31merror:\033[0m \033[1;37munknown type name '" << err.value() << "'\033[0m" << std::endl;
|
||||||
|
} else {
|
||||||
|
std::cerr << "xlang: \033[1;31merror:\033[0m \033[1;37munknown type name\033[0m" << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void statement(std::optional<std::string> err) {
|
||||||
|
if (err.has_value()) {
|
||||||
|
std::cerr << "xlang: \033[1;31merror:\033[0m \033[1;37minvalid statement '" << err.value() << "'\033[0m" << std::endl;
|
||||||
|
} else {
|
||||||
|
std::cerr << "xlang: \033[1;31merror:\033[0m \033[1;37minvalid statement\033[0m" << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void redefine(std::optional<std::string> err) {
|
||||||
|
std::cerr << "xlang: \033[1;31merror:\033[0m \033[1;37mredefinition of '" << err.value() << "'\033[0m" << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void undeclared(std::optional<std::string> err) {
|
||||||
|
std::cerr << "xlang: \033[1;31merror:\033[0m \033[1;37mundeclared varible '" << err.value() << "'\033[0m" << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -2,23 +2,117 @@
|
|||||||
|
|
||||||
#include <optional>
|
#include <optional>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
#include <cassert>
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
#include "parser.hxx"
|
#include "parser.hxx"
|
||||||
|
|
||||||
class Generator {
|
class Generator {
|
||||||
public:
|
public:
|
||||||
inline explicit Generator(node::Exit root) : m_root(std::move(root)) {
|
inline explicit Generator(node::Prog prog) : m_prog(std::move(prog)) {
|
||||||
// Erm!!!
|
// Erm!!!
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] inline std::string generate() const {
|
void gen_stmt(const node::Stmt* stmt) {
|
||||||
std::stringstream output;
|
struct StmtVisitor { // Visiting the homies
|
||||||
output << "global _start\n_start:\n";
|
Generator* gen;
|
||||||
output << " mov rax, 60\n";
|
void operator()(const node::StmtExit* stmt_exit) const {
|
||||||
output << " mov rdi, " << m_root.expr.int_lit.value.value() << "\n";
|
gen->gen_expr(stmt_exit->expr);
|
||||||
output << " syscall\n";
|
gen->m_output << " mov rax, 60\n";
|
||||||
return output.str();
|
gen->pop("rdi");
|
||||||
|
gen->m_output << " syscall\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
void operator()(const node::StmtVar* stmt_var) {
|
||||||
|
if (gen->m_vars.contains(stmt_var->ident.value.value())) {
|
||||||
|
err::redefine(stmt_var->ident.value.value());
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
gen->m_vars.insert({stmt_var->ident.value.value(), Var {.stack_loc = gen->m_stack_size}});
|
||||||
|
gen->gen_expr(stmt_var->expr);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
StmtVisitor visitor {.gen = this};
|
||||||
|
std::visit(visitor, stmt->var);
|
||||||
|
}
|
||||||
|
|
||||||
|
void gen_term(const node::Term* term) {
|
||||||
|
struct TermVisitor {
|
||||||
|
Generator* gen;
|
||||||
|
|
||||||
|
void operator()(const node::TermIntLit* term_int_lit) const {
|
||||||
|
gen->m_output << " mov rax, " << term_int_lit->int_lit.value.value() << "\n";
|
||||||
|
gen->push("rax");
|
||||||
|
}
|
||||||
|
|
||||||
|
void operator()(const node::TermIdent* term_ident) const {
|
||||||
|
if (!gen->m_vars.contains(term_ident->ident.value.value())) {
|
||||||
|
err::undeclared(term_ident->ident.value.value());
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
const auto& var = gen->m_vars.at(term_ident->ident.value.value());
|
||||||
|
std::stringstream offset;
|
||||||
|
offset << "QWORD [rsp + " << (gen->m_stack_size - var.stack_loc - 1) * 8 << "]";
|
||||||
|
gen->push(offset.str());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
TermVisitor visitor ({.gen = this});
|
||||||
|
std::visit(visitor, term->var);
|
||||||
|
}
|
||||||
|
|
||||||
|
void gen_expr(const node::Expr* expr) {
|
||||||
|
struct ExprVisitor {
|
||||||
|
Generator* gen;
|
||||||
|
|
||||||
|
void operator()(const node::Term* term) const {
|
||||||
|
gen->gen_term(term);
|
||||||
|
}
|
||||||
|
|
||||||
|
void operator()(const node::BinExpr* bin_expr) const {
|
||||||
|
gen->gen_expr(bin_expr->var->lhs);
|
||||||
|
gen->gen_expr(bin_expr->var->rhs);
|
||||||
|
gen->pop("rax");
|
||||||
|
gen->pop("rbx");
|
||||||
|
gen->m_output << " add rax, rbx\n";
|
||||||
|
gen->push("rax");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
ExprVisitor visitor {.gen = this};
|
||||||
|
std::visit(visitor, expr->var);
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] inline std::string gen_prog() {
|
||||||
|
m_output << "global _start\n_start:\n";
|
||||||
|
|
||||||
|
for (const node::Stmt* stmt : m_prog.stmts) {
|
||||||
|
gen_stmt(stmt);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_output << " mov rax, 60\n";
|
||||||
|
m_output << " mov rdi, 0\n";
|
||||||
|
m_output << " syscall\n";
|
||||||
|
return m_output.str();
|
||||||
}
|
}
|
||||||
private:
|
private:
|
||||||
const node::Exit m_root;
|
|
||||||
|
void push(const std::string& reg) {
|
||||||
|
m_output << " push " << reg << "\n";
|
||||||
|
m_stack_size++;
|
||||||
|
}
|
||||||
|
|
||||||
|
void pop(const std::string& reg) {
|
||||||
|
m_output << " pop " << reg << "\n";
|
||||||
|
m_stack_size--;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Var {
|
||||||
|
size_t stack_loc;
|
||||||
|
};
|
||||||
|
|
||||||
|
const node::Prog m_prog;
|
||||||
|
std::stringstream m_output;
|
||||||
|
size_t m_stack_size = 0;
|
||||||
|
std::unordered_map<std::string, Var> m_vars {};
|
||||||
};
|
};
|
||||||
@ -4,6 +4,7 @@
|
|||||||
#include <optional>
|
#include <optional>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
#include "arena.hxx"
|
||||||
#include "parser.hxx"
|
#include "parser.hxx"
|
||||||
#include "generation.hxx"
|
#include "generation.hxx"
|
||||||
#include "tokenization.hxx"
|
#include "tokenization.hxx"
|
||||||
@ -26,18 +27,18 @@ int main(int argc, char* argv[]) {
|
|||||||
std::vector<Token> tokens = tokenizer.tokenize();
|
std::vector<Token> tokens = tokenizer.tokenize();
|
||||||
|
|
||||||
Parser parser(std::move(tokens));
|
Parser parser(std::move(tokens));
|
||||||
std::optional<node::Exit> tree = parser.parse();
|
std::optional<node::Prog> prog = parser.parse_prog();
|
||||||
|
|
||||||
if (!tree.has_value()) {
|
if (!prog.has_value()) {
|
||||||
std::cerr << "xlang: \033[1;31merror:\033[0m \033[1;37mparse tree has no value, possibly empty file?\033[0m" << std::endl;
|
std::cerr << "xlang: \033[1;31merror:\033[0m \033[1;37mparse tree has no value, possibly empty file?\033[0m" << std::endl;
|
||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
|
|
||||||
Generator generator(tree.value());
|
Generator generator(prog.value());
|
||||||
|
|
||||||
{
|
{
|
||||||
std::fstream output("out.asm", std::ios::out);
|
std::fstream output("out.asm", std::ios::out);
|
||||||
output << generator.generate();
|
output << generator.gen_prog();
|
||||||
}
|
}
|
||||||
|
|
||||||
system("nasm -felf64 out.asm");
|
system("nasm -felf64 out.asm");
|
||||||
|
|||||||
177
src/parser.hxx
177
src/parser.hxx
@ -2,63 +2,176 @@
|
|||||||
|
|
||||||
#include <optional>
|
#include <optional>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
#include <variant>
|
||||||
|
|
||||||
#include "tokenization.hxx"
|
#include "tokenization.hxx"
|
||||||
|
#include "error.hxx"
|
||||||
|
#include "arena.hxx"
|
||||||
|
|
||||||
namespace node {
|
namespace node {
|
||||||
struct Expr {
|
struct TermIntLit {
|
||||||
Token int_lit;
|
Token int_lit;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Exit {
|
struct TermIdent {
|
||||||
Expr expr;
|
Token ident;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Expr;
|
||||||
|
|
||||||
|
struct BinExprAdd {
|
||||||
|
Expr* lhs;
|
||||||
|
Expr* rhs;
|
||||||
|
};
|
||||||
|
|
||||||
|
// struct BinExprMul {
|
||||||
|
// Expr* lhs;
|
||||||
|
// Expr* rhs;
|
||||||
|
// };
|
||||||
|
|
||||||
|
struct BinExpr {
|
||||||
|
BinExprAdd* var;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Term {
|
||||||
|
std::variant<TermIntLit*, TermIdent*> var;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Expr {
|
||||||
|
std::variant<Term*, BinExpr*> var;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct StmtExit {
|
||||||
|
Expr* expr;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct StmtVar {
|
||||||
|
Token ident;
|
||||||
|
Expr* expr;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Stmt {
|
||||||
|
std::variant<StmtExit*, StmtVar*> var;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Prog {
|
||||||
|
std::vector<Stmt*> stmts;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
class Parser {
|
class Parser {
|
||||||
public:
|
public:
|
||||||
inline explicit Parser(std::vector<Token> tokens) : m_tokens(std::move(tokens)) {
|
inline explicit Parser(std::vector<Token> tokens)
|
||||||
|
: m_tokens(std::move(tokens)),
|
||||||
|
m_allocator(1024 * 1024 * 4) { // 4 MB should be ok..?
|
||||||
// Erm!
|
// Erm!
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<node::Expr> parse_expr() {
|
std::optional<node::Term*> parse_term() {
|
||||||
if(peek().has_value() && peek().value().type == TokenType::int_lit) {
|
if (auto int_lit = try_consume(TokenType::int_lit)) {
|
||||||
return node::Expr{.int_lit = consume()};
|
auto term_int_lit = m_allocator.alloc<node::TermIntLit>();
|
||||||
|
term_int_lit->int_lit = int_lit.value();
|
||||||
|
auto term = m_allocator.alloc<node::Term>();
|
||||||
|
term->var = term_int_lit;
|
||||||
|
return term;
|
||||||
|
} else if(auto ident = try_consume(TokenType::ident)) {
|
||||||
|
auto term_ident = m_allocator.alloc<node::TermIdent>();
|
||||||
|
term_ident->ident = ident.value();
|
||||||
|
auto term = m_allocator.alloc<node::Term>();
|
||||||
|
term->var = term_ident;
|
||||||
|
return term;
|
||||||
} else {
|
} else {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<node::Exit> parse() {
|
std::optional<node::Expr*> parse_expr() {
|
||||||
std::optional<node::Exit> exit_node;
|
if (auto term = parse_term()) {
|
||||||
while(peek().has_value()) {
|
if (try_consume(TokenType::add).has_value()) {
|
||||||
if (peek().value().type == TokenType::exit) {
|
auto bin_expr = m_allocator.alloc<node::BinExpr>();
|
||||||
consume();
|
auto bin_expr_add = m_allocator.alloc<node::BinExprAdd>();
|
||||||
if (auto node_expr = parse_expr()) {
|
auto lhs_expr = m_allocator.alloc<node::Expr>();
|
||||||
exit_node = node::Exit {.expr = node_expr.value()};
|
lhs_expr->var = term.value();
|
||||||
|
bin_expr_add->lhs = lhs_expr;
|
||||||
|
if (auto rhs = parse_expr()) {
|
||||||
|
bin_expr_add->rhs = rhs.value();
|
||||||
|
bin_expr->var = bin_expr_add;
|
||||||
|
auto expr = m_allocator.alloc<node::Expr>();
|
||||||
|
expr->var = bin_expr;
|
||||||
|
return expr;
|
||||||
} else {
|
} else {
|
||||||
std::cerr << "xlang: \033[1;31merror:\033[0m \033[1;37minvalid expression\033[0m" << std::endl;
|
err::expression({});
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
auto expr = m_allocator.alloc<node::Expr>();
|
||||||
|
expr->var = term.value();
|
||||||
|
return expr;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<node::Stmt*> parse_stmt() {
|
||||||
|
if (peek().has_value() && peek().value().type == TokenType::exit && peek(1).has_value() && peek(1).value().type == TokenType::open_paren) {
|
||||||
|
consume();
|
||||||
|
consume();
|
||||||
|
auto stmt_exit = m_allocator.alloc<node::StmtExit>();
|
||||||
|
if (auto node_expr = parse_expr()) {
|
||||||
|
stmt_exit->expr = node_expr.value();
|
||||||
|
} else {
|
||||||
|
err::expression({});
|
||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (peek().has_value() && peek().value().type == TokenType::semi) {
|
try_consume(TokenType::close_paren, ")");
|
||||||
|
try_consume(TokenType::semi, ";");
|
||||||
|
|
||||||
|
auto stmt = m_allocator.alloc<node::Stmt>();
|
||||||
|
stmt->var = stmt_exit;
|
||||||
|
return stmt;
|
||||||
|
} else if (peek().has_value() && peek().value().type == TokenType::var
|
||||||
|
&& peek(1).has_value() && peek(1).value().type == TokenType::ident
|
||||||
|
&& peek(2).has_value() && peek(2).value().type == TokenType::equals) { // Holy else-if statement
|
||||||
consume();
|
consume();
|
||||||
|
auto stmt_var = m_allocator.alloc<node::StmtVar>();
|
||||||
|
stmt_var->ident = consume();
|
||||||
|
consume();
|
||||||
|
if (auto expr = parse_expr()) {
|
||||||
|
stmt_var->expr = expr.value();
|
||||||
} else {
|
} else {
|
||||||
std::cerr << "xlang: \033[1;31merror:\033[0m \033[1;37minvalid syntax\033[0m" << std::endl;
|
err::expression({});
|
||||||
|
}
|
||||||
|
try_consume(TokenType::semi, ";");
|
||||||
|
|
||||||
|
auto stmt = m_allocator.alloc<node::Stmt>();
|
||||||
|
stmt->var = stmt_var;
|
||||||
|
return stmt;
|
||||||
|
} else {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<node::Prog> parse_prog() {
|
||||||
|
node::Prog prog;
|
||||||
|
while (peek().has_value()) {
|
||||||
|
if (auto stmt = parse_stmt()) {
|
||||||
|
prog.stmts.push_back(stmt.value());
|
||||||
|
} else {
|
||||||
|
err::statement({});
|
||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
return prog;
|
||||||
m_index = 0;
|
|
||||||
return exit_node;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
[[nodiscard]] inline std::optional<Token> peek(int ahead = 1) const {
|
[[nodiscard]] inline std::optional<Token> peek(int offset = 0) const {
|
||||||
if (m_index + ahead > m_tokens.size()) {
|
if (m_index + offset >= m_tokens.size()) {
|
||||||
return {};
|
return {};
|
||||||
} else {
|
} else {
|
||||||
return m_tokens.at(m_index);
|
return m_tokens.at(m_index + offset);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -66,6 +179,24 @@ private:
|
|||||||
return m_tokens.at(m_index++);
|
return m_tokens.at(m_index++);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline Token try_consume(TokenType type, const std::string& err) {
|
||||||
|
if (peek().has_value() && peek().value().type == type) {
|
||||||
|
return consume();
|
||||||
|
} else {
|
||||||
|
err::syntax(err);
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline std::optional<Token> try_consume(TokenType type) {
|
||||||
|
if (peek().has_value() && peek().value().type == type) {
|
||||||
|
return consume();
|
||||||
|
} else {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const std::vector<Token> m_tokens;
|
const std::vector<Token> m_tokens;
|
||||||
size_t m_index = 0;
|
size_t m_index = 0;
|
||||||
|
ArenaAllocator m_allocator;
|
||||||
};
|
};
|
||||||
@ -6,10 +6,21 @@
|
|||||||
#include <optional>
|
#include <optional>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
#include "error.hxx"
|
||||||
|
|
||||||
enum class TokenType {
|
enum class TokenType {
|
||||||
exit,
|
exit,
|
||||||
int_lit,
|
int_lit,
|
||||||
semi,
|
semi,
|
||||||
|
open_paren,
|
||||||
|
close_paren,
|
||||||
|
ident,
|
||||||
|
var,
|
||||||
|
equals,
|
||||||
|
add,
|
||||||
|
mul,
|
||||||
|
div,
|
||||||
|
sub
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Token {
|
struct Token {
|
||||||
@ -37,9 +48,14 @@ public:
|
|||||||
tokens.push_back({.type = TokenType::exit});
|
tokens.push_back({.type = TokenType::exit});
|
||||||
buffer.clear();
|
buffer.clear();
|
||||||
continue;
|
continue;
|
||||||
|
} else if (buffer == "var") {
|
||||||
|
tokens.push_back({.type = TokenType::var});
|
||||||
|
buffer.clear();
|
||||||
|
continue;
|
||||||
} else {
|
} else {
|
||||||
std::cerr << "xlang: \033[1;31merror:\033[0m \033[1;37munknown type name \'" << buffer << "\'\033[0m" << std::endl;
|
tokens.push_back({.type = TokenType::ident, .value = buffer});
|
||||||
exit(EXIT_FAILURE);
|
buffer.clear();
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
} else if (std::isdigit(peek().value())) {
|
} else if (std::isdigit(peek().value())) {
|
||||||
buffer.push_back(consume());
|
buffer.push_back(consume());
|
||||||
@ -49,15 +65,43 @@ public:
|
|||||||
tokens.push_back({.type = TokenType::int_lit, .value = buffer});
|
tokens.push_back({.type = TokenType::int_lit, .value = buffer});
|
||||||
buffer.clear();
|
buffer.clear();
|
||||||
continue;
|
continue;
|
||||||
|
} else if (peek().value() == '(') {
|
||||||
|
consume();
|
||||||
|
tokens.push_back({.type = TokenType::open_paren});
|
||||||
|
continue;
|
||||||
|
} else if (peek().value() == ')') {
|
||||||
|
consume();
|
||||||
|
tokens.push_back({.type = TokenType::close_paren});
|
||||||
|
continue;
|
||||||
} else if (peek().value() == ';') {
|
} else if (peek().value() == ';') {
|
||||||
consume();
|
consume();
|
||||||
tokens.push_back({.type = TokenType::semi});
|
tokens.push_back({.type = TokenType::semi});
|
||||||
continue;
|
continue;
|
||||||
|
} else if (peek().value() == '=') {
|
||||||
|
consume();
|
||||||
|
tokens.push_back({.type = TokenType::equals});
|
||||||
|
continue;
|
||||||
|
} else if (peek().value() == '+') {
|
||||||
|
consume();
|
||||||
|
tokens.push_back({.type = TokenType::add});
|
||||||
|
continue;
|
||||||
|
} else if (peek().value() == '*') {
|
||||||
|
consume();
|
||||||
|
tokens.push_back({.type = TokenType::mul});
|
||||||
|
continue;
|
||||||
|
} else if (peek().value() == '-') {
|
||||||
|
consume();
|
||||||
|
tokens.push_back({.type = TokenType::sub});
|
||||||
|
continue;
|
||||||
|
} else if (peek().value() == '/') {
|
||||||
|
consume();
|
||||||
|
tokens.push_back({.type = TokenType::div});
|
||||||
|
continue;
|
||||||
} else if (std::isspace(peek().value())) {
|
} else if (std::isspace(peek().value())) {
|
||||||
consume();
|
consume();
|
||||||
continue;
|
continue;
|
||||||
} else {
|
} else {
|
||||||
std::cerr << "xlang: \033[1;31merror:\033[0m \033[1;37munknown type name \'" << buffer << "\'\033[0m" << std::endl;
|
err::unknowntype(buffer);
|
||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -67,11 +111,11 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
[[nodiscard]] inline std::optional<char> peek(int ahead = 1) const {
|
[[nodiscard]] inline std::optional<char> peek(int offset = 0) const {
|
||||||
if (m_index + ahead > m_src.length()) {
|
if (m_index + offset >= m_src.length()) {
|
||||||
return {};
|
return {};
|
||||||
} else {
|
} else {
|
||||||
return m_src.at(m_index);
|
return m_src.at(m_index + offset);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user