This commit is contained in:
2026-05-06 23:47:46 -04:00
commit 6643f33e7f
14 changed files with 1117 additions and 0 deletions

3
.gitignore vendored Normal file
View File

@@ -0,0 +1,3 @@
build/*
trivial*
trivial

26
Makefile Normal file
View File

@@ -0,0 +1,26 @@
# Compiler and flags
CXX = g++
CXXFLAGS = -std=c++17 -Wall -g
LIBS = -lncurses -lm
# Source files
TRIVIAL_SRC = main.cpp $(wildcard src/*.cpp) $(wildcard src/network/*.cpp)
# Object files in build/
TRIVIAL_OBJ = $(patsubst %.cpp,build/%.o,$(TRIVIAL_SRC))
# Default target
all: trivial
# Link
trivial: $(TRIVIAL_OBJ)
$(CXX) $(CXXFLAGS) -o $@ $(TRIVIAL_OBJ) $(LIBS)
# Compile rule
build/%.o: %.cpp
@mkdir -p $(dir $@)
$(CXX) $(CXXFLAGS) -c $< -o $@
# Clean
clean:
rm -rf build trivial

63
ReadME.md Normal file
View File

@@ -0,0 +1,63 @@
# 10. User Manual
## Installation Requirements
To build and run this project, the following requirements must be met:
- A C++ compiler (such as `g++` or `clang++`)
- The `make` build system installed
- A Unix-based environment:
- **macOS** is fully supported
- **Linux** is supported, but may require additional standard library headers depending on the distribution and compiler configuration
Windows is not natively supported without a compatibility layer such as WSL (Windows Subsystem for Linux).
---
## Cloning the Repository
To obtain the project source code, clone the repository using Git:
```bash
git clone URL_NOT_ADDED_YET
```
After cloning, navigate into the project directory:
```bash
cd <project-folder-name>
```
## Building the Project
The project uses a Makefile for compilation. To build the interpreter, run:
```bash
make trivial
```
This command compiles all necessary source files and generates the executable named trivial.
If compilation issues occur on Linux, it may be necessary to install additional development headers or ensure that a modern C++ compiler standard (e.g., C++11 or later) is enabled.
## Running the Interpreter
Once the project has been successfully built, there are two ways to run it:
1. Interactive Shell Mode
To launch the Trivial interactive interpreter:
```./trivial```
This starts a shell-like environment where Trivial statements can be entered and executed line by line.
2. File Execution Mode
To execute a Trivial program stored in a file:
```./trivial {filename}```
Replace {filename} with the path to the Trivial source file. The interpreter will read the file, process it, and execute the program sequentially.
Example:
```./trivial example.tri```

34
function.txt Normal file
View File

@@ -0,0 +1,34 @@
This is a record of the output
./trivial
>> i = 10
>> print i
10
>> i = i - i
>> print i
0
>> i = 10
>> i = i - 1
>> print i
9
arizotaz@Coltons-MacBook-Pro trivial_c++ % cat test.triv
f = function(a) { return a * 2 }
print f(10)
i = 0
while (i < 3) {
print i
i = i + 1
}
if (1 > 0) { print 10 } else { print 1 }
arizotaz@Coltons-MacBook-Pro trivial_c++ % ./trivial test.triv
20
0
1
2
10
arizotaz@Coltons-MacBook-Pro trivial_c++ %

4
include/errors.h Normal file
View File

@@ -0,0 +1,4 @@
#pragma once
#include <string>
void error(const std::string& msg);

39
include/evaluator.h Normal file
View File

@@ -0,0 +1,39 @@
#pragma once
#include "parser.h"
#include <memory>
#include <string>
#include <unordered_map>
#include <variant>
#include <vector>
struct Function;
struct Value;
using Object = std::unordered_map<std::string, Value>;
struct Value : std::variant<
std::nullptr_t,
bool,
double,
std::string,
std::vector<Value>,
Object,
std::shared_ptr<Function>> {
using variant::variant;
};
struct EvalResult {
Value value;
std::string control;
};
struct Function {
std::vector<std::string> params;
std::shared_ptr<AST> body;
std::unordered_map<std::string, Value> env;
};
using Env = std::unordered_map<std::string, Value>;
EvalResult evaluate(std::shared_ptr<AST> ast, Env& env);

21
include/parser.h Normal file
View File

@@ -0,0 +1,21 @@
#pragma once
#include "tokenizer.h"
#include <memory>
#include <string>
#include <vector>
struct AST {
std::string tag;
std::string value;
std::shared_ptr<AST> left;
std::shared_ptr<AST> right;
std::vector<std::shared_ptr<AST>> list;
// function parameters
std::vector<std::string> params;
};
std::shared_ptr<AST> parse(std::vector<Token> tokens);

12
include/tokenizer.h Normal file
View File

@@ -0,0 +1,12 @@
#pragma once
#include <string>
#include <vector>
struct Token {
std::string tag;
std::string value;
int line;
int column;
};
std::vector<Token> tokenize(const std::string& input);

75
main.cpp Normal file
View File

@@ -0,0 +1,75 @@
#include "include/tokenizer.h"
#include "include/parser.h"
#include "include/evaluator.h"
#include <iostream>
#include <fstream>
#include <sstream>
#include <unordered_map>
// ---------------- read entire file ----------------
static std::string read_file(const std::string& path) {
std::ifstream file(path);
if (!file.is_open()) {
throw std::runtime_error("Could not open file: " + path);
}
std::stringstream buffer;
buffer << file.rdbuf();
return buffer.str();
}
// ---------------- execute code ----------------
static void execute(const std::string& source, Env& env) {
auto tokens = tokenize(source);
auto ast = parse(tokens);
evaluate(ast, env);
}
// ---------------- REPL ----------------
static void repl() {
Env env;
std::string line;
while (true) {
std::cout << ">> ";
if (!std::getline(std::cin, line))
break;
if (line == "exit" || line == "quit")
break;
try {
execute(line, env);
} catch (const std::exception& e) {
std::cout << "Error: " << e.what() << "\n";
}
}
}
// ---------------- main ----------------
int main(int argc, char** argv) {
Env env;
try {
// ---------------- FILE MODE ----------------
if (argc > 1) {
std::string file_path = argv[1];
std::string source = read_file(file_path);
execute(source, env);
return 0;
}
// ---------------- REPL MODE ----------------
repl();
} catch (const std::exception& e) {
std::cout << "Fatal Error: " << e.what() << "\n";
return 1;
}
return 0;
}

8
src/errors.cpp Normal file
View File

@@ -0,0 +1,8 @@
#include "../include/errors.h"
#include <iostream>
#include <stdexcept>
void error(const std::string& msg)
{
throw std::runtime_error(msg);
}

321
src/evaluator.cpp Normal file
View File

@@ -0,0 +1,321 @@
#include "../include/evaluator.h"
#include <cmath>
#include <iostream>
#include <stdexcept>
static bool is_truthy(const Value& v)
{
if (std::holds_alternative<std::nullptr_t>(v))
return false;
if (auto b = std::get_if<bool>(&v))
return *b;
if (auto n = std::get_if<double>(&v))
return *n != 0;
if (auto s = std::get_if<std::string>(&v))
return !s->empty();
if (auto a = std::get_if<std::vector<Value>>(&v))
return !a->empty();
if (auto o = std::get_if<Object>(&v))
return !o->empty();
return true;
}
static double as_number(const Value& v)
{
if (auto n = std::get_if<double>(&v))
return *n;
throw std::runtime_error("Expected number");
}
static std::string as_string(const Value& v)
{
if (auto s = std::get_if<std::string>(&v))
return *s;
throw std::runtime_error("Expected string");
}
EvalResult evaluate(std::shared_ptr<AST> ast, Env& env);
// ======================= builtin =======================
static Value call_builtin(const std::string& name, const std::vector<Value>& args)
{
if (name == "length") {
if (auto a = std::get_if<std::vector<Value>>(&args[0]))
return (double)a->size();
if (auto s = std::get_if<std::string>(&args[0]))
return (double)s->size();
throw std::runtime_error("length() invalid arg");
}
if (name == "head") {
auto a = std::get<std::vector<Value>>(args[0]);
if (a.empty())
return nullptr;
return a[0];
}
if (name == "tail") {
auto a = std::get<std::vector<Value>>(args[0]);
if (a.size() <= 1)
return std::vector<Value> { };
return std::vector<Value>(a.begin() + 1, a.end());
}
if (name == "input") {
std::string s;
std::getline(std::cin, s);
return s;
}
throw std::runtime_error("Unknown builtin: " + name);
}
// ======================= evaluator =======================
EvalResult evaluate(std::shared_ptr<AST> ast, Env& env)
{
// -------- literals --------
if (ast->tag == "number")
return { std::stod(ast->value), "" };
if (ast->tag == "string")
return { ast->value, "" };
if (ast->tag == "boolean")
return { ast->value == "true", "" };
if (ast->tag == "null")
return { nullptr, "" };
// -------- identifier --------
if (ast->tag == "identifier") {
if (env.count(ast->value))
return { env[ast->value], "" };
throw std::runtime_error("Undefined variable: " + ast->value);
}
// -------- arithmetic --------
if (ast->tag == "+") {
auto l = evaluate(ast->left, env).value;
auto r = evaluate(ast->right, env).value;
if (l.index() == 3 && r.index() == 3)
return { std::get<std::string>(l) + std::get<std::string>(r), "" };
return { (as_number(l) + as_number(r)), "" };
}
if (ast->tag == "-")
return { as_number(evaluate(ast->left, env).value) - as_number(evaluate(ast->right, env).value), "" };
if (ast->tag == "*")
return { as_number(evaluate(ast->left, env).value) * as_number(evaluate(ast->right, env).value), "" };
if (ast->tag == "/")
return { as_number(evaluate(ast->left, env).value) / as_number(evaluate(ast->right, env).value), "" };
if (ast->tag == "%")
return { std::fmod(as_number(evaluate(ast->left, env).value),
as_number(evaluate(ast->right, env).value)),
"" };
// -------- comparison --------
if (ast->tag == "==")
return { evaluate(ast->left, env).value == evaluate(ast->right, env).value, "" };
if (ast->tag == "!=")
return { evaluate(ast->left, env).value != evaluate(ast->right, env).value, "" };
if (ast->tag == "<")
return { as_number(evaluate(ast->left, env).value) < as_number(evaluate(ast->right, env).value), "" };
if (ast->tag == ">")
return { as_number(evaluate(ast->left, env).value) > as_number(evaluate(ast->right, env).value), "" };
if (ast->tag == "<=")
return { as_number(evaluate(ast->left, env).value) <= as_number(evaluate(ast->right, env).value), "" };
if (ast->tag == ">=")
return { as_number(evaluate(ast->left, env).value) >= as_number(evaluate(ast->right, env).value), "" };
// -------- logical --------
if (ast->tag == "&&")
return { is_truthy(evaluate(ast->left, env).value) && is_truthy(evaluate(ast->right, env).value), "" };
if (ast->tag == "||")
return { is_truthy(evaluate(ast->left, env).value) || is_truthy(evaluate(ast->right, env).value), "" };
if (ast->tag == "!")
return { !is_truthy(evaluate(ast->left, env).value), "" };
// -------- list --------
if (ast->tag == "list") {
std::vector<Value> v;
for (auto& x : ast->list)
v.push_back(evaluate(x, env).value);
return { v, "" };
}
// -------- object --------
if (ast->tag == "object") {
Object obj;
for (auto& kv : ast->list) {
auto key = evaluate(kv->left, env).value;
auto val = evaluate(kv->right, env).value;
obj[as_string(key)] = val;
}
return { obj, "" };
}
// -------- assignment --------
if (ast->tag == "=") {
std::string name = ast->left->value;
Value val = evaluate(ast->right, env).value;
env[name] = val;
return { val, "" };
}
// -------- function --------
if (ast->tag == "function") {
auto fn = std::make_shared<Function>();
fn->params = ast->params;
fn->body = ast->right;
fn->env = env;
return { fn, "" };
}
// -------- call --------
if (ast->tag == "call") {
Value fnVal = evaluate(ast->left, env).value;
auto fn = std::get<std::shared_ptr<Function>>(fnVal);
Env local = fn->env;
for (size_t j = 0; j < fn->params.size(); j++) {
Value arg = nullptr;
if (j < ast->list.size())
arg = evaluate(ast->list[j], env).value;
local[fn->params[j]] = arg;
}
EvalResult result = evaluate(fn->body, local);
if (result.control == "return")
return { result.value, "" };
return result;
}
// -------- block --------
if (ast->tag == "statement_list") {
Value last;
for (auto& s : ast->list)
last = evaluate(s, env).value;
return { last, "" };
}
// -------- if --------
if (ast->tag == "if") {
bool cond = is_truthy(evaluate(ast->left, env).value);
auto thenBranch = ast->right->left;
if (cond) {
return evaluate(thenBranch, env);
} else {
if (ast->right->right)
return evaluate(ast->right->right, env);
}
return { nullptr, "" };
}
// -------- while --------
if (ast->tag == "while") {
while (is_truthy(evaluate(ast->left, env).value)) {
EvalResult result = evaluate(ast->right, env);
if (result.control == "break")
break;
if (result.control == "continue")
continue;
if (!result.control.empty())
return result;
}
return { nullptr, "" };
}
// -------- print --------
if (ast->tag == "print") {
if (!ast->left) {
std::cout << std::endl;
return { std::string("\n"), "" };
}
Value v = evaluate(ast->left, env).value;
if (std::holds_alternative<bool>(v)) {
std::cout << (std::get<bool>(v) ? "true" : "false") << std::endl;
return { std::string(std::get<bool>(v) ? "true" : "false") + "\n", "" };
}
if (std::holds_alternative<double>(v)) {
std::cout << std::get<double>(v) << std::endl;
return { std::to_string(std::get<double>(v)) + "\n", "" };
}
if (std::holds_alternative<std::string>(v)) {
std::cout << std::get<std::string>(v) << std::endl;
return { std::get<std::string>(v) + "\n", "" };
}
if (std::holds_alternative<std::nullptr_t>(v)) {
std::cout << "null" << std::endl;
return { std::string("null\n"), "" };
}
std::cout << "[object]" << std::endl;
return { std::string("[object]\n"), "" };
}
if (ast->tag == "return") {
Value val = nullptr;
if (ast->left)
val = evaluate(ast->left, env).value;
return { val, "return" };
}
if (ast->tag == "block") {
Value last = nullptr;
for (auto& stmt : ast->list) {
EvalResult result = evaluate(stmt, env);
if (!result.control.empty())
return result;
last = result.value;
}
return { last, "" };
}
throw std::runtime_error("Unknown AST tag: " + ast->tag);
}

391
src/parser.cpp Normal file
View File

@@ -0,0 +1,391 @@
#include "../include/parser.h"
#include <stdexcept>
static size_t i = 0;
std::shared_ptr<AST> parse_block(std::vector<Token>& t);
std::shared_ptr<AST> parse_statement(std::vector<Token>& t);
std::shared_ptr<AST> parse_block(std::vector<Token>& t);
std::shared_ptr<AST> parse_expression(std::vector<Token>& t);
static Token& cur(std::vector<Token>& t)
{
if (i >= t.size())
throw std::runtime_error("Unexpected end of input");
return t[i];
}
static Token& advance(std::vector<Token>& t)
{
return t[++i];
}
static bool match(std::vector<Token>& t, const std::string& tag)
{
if (cur(t).tag == tag) {
i++;
return true;
}
return false;
}
// forward declarations
std::shared_ptr<AST> parse_expression(std::vector<Token>& t);
std::shared_ptr<AST> parse_statement(std::vector<Token>& t);
// ---------------- primary expressions ----------------
std::shared_ptr<AST> parse_primary(std::vector<Token>& t)
{
Token token = cur(t);
auto node = std::make_shared<AST>();
if (token.tag == "function") {
i++; // consume function
if (!match(t, "("))
throw std::runtime_error("Expected '(' after function");
auto fn = std::make_shared<AST>();
fn->tag = "function";
// parameters
if (cur(t).tag != ")") {
while (true) {
if (cur(t).tag != "identifier")
throw std::runtime_error("Expected parameter name");
fn->params.push_back(cur(t).value);
i++;
if (cur(t).tag == ",") {
i++;
continue;
}
break;
}
}
if (!match(t, ")"))
throw std::runtime_error("Expected ')' after parameters");
fn->right = parse_block(t);
return fn;
}
if (token.tag == "identifier") {
auto node = std::make_shared<AST>();
node->tag = "identifier";
node->value = token.value;
i++;
// function call
if (cur(t).tag == "(") {
i++; // consume (
auto call = std::make_shared<AST>();
call->tag = "call";
call->left = node;
if (cur(t).tag != ")") {
while (true) {
call->list.push_back(parse_expression(t));
if (cur(t).tag == ",") {
i++;
continue;
}
break;
}
}
if (!match(t, ")"))
throw std::runtime_error("Expected ')' after arguments");
return call;
}
return node;
}
if (token.tag == "number" || token.tag == "string" || token.tag == "identifier") {
node->tag = token.tag;
node->value = token.value;
i++;
return node;
}
if (token.tag == "(") {
i++;
auto expr = parse_expression(t);
if (cur(t).tag != ")")
throw std::runtime_error("Expected ')'");
i++;
return expr;
}
throw std::runtime_error("Unexpected token: " + token.tag);
}
// ---------------- binary expressions (basic precedence) ----------------
std::shared_ptr<AST> parse_term(std::vector<Token>& t);
std::shared_ptr<AST> parse_factor(std::vector<Token>& t);
std::shared_ptr<AST> parse_factor(std::vector<Token>& t)
{
return parse_primary(t);
}
std::shared_ptr<AST> parse_arithmetic(std::vector<Token>& t)
{
auto left = parse_term(t);
while (
cur(t).tag == "+" || cur(t).tag == "-") {
std::string op = cur(t).tag;
i++;
auto right = parse_term(t);
auto node = std::make_shared<AST>();
node->tag = op;
node->left = left;
node->right = right;
left = node;
}
return left;
}
std::shared_ptr<AST> parse_term(std::vector<Token>& t)
{
auto left = parse_factor(t);
while (cur(t).tag == "*" || cur(t).tag == "/" || cur(t).tag == "%") {
std::string op = cur(t).tag;
i++;
auto right = parse_factor(t);
auto node = std::make_shared<AST>();
node->tag = op;
node->left = left;
node->right = right;
left = node;
}
return left;
}
std::shared_ptr<AST> parse_expression(std::vector<Token>& t)
{
auto left = parse_arithmetic(t);
while (
cur(t).tag == "<" || cur(t).tag == ">" || cur(t).tag == "<=" || cur(t).tag == ">=" || cur(t).tag == "==" || cur(t).tag == "!=") {
std::string op = cur(t).tag;
i++;
auto right = parse_arithmetic(t);
auto node = std::make_shared<AST>();
node->tag = op;
node->left = left;
node->right = right;
left = node;
}
return left;
}
// ---------------- statements ----------------
// std::shared_ptr<AST> parse_block(std::vector<Token>& t)
// {
// if (!match(t, "{"))
// throw std::runtime_error("Expected '{'");
// auto node = std::make_shared<AST>();
// node->tag = "statement_list";
// while (cur(t).tag != "}") {
// node->list.push_back(parse_statement(t));
// }
// i++; // consume }
// return node;
// }
std::shared_ptr<AST> parse_block(std::vector<Token>& t)
{
if (!match(t, "{"))
throw std::runtime_error("Expected '{'");
auto block = std::make_shared<AST>();
block->tag = "block";
while (cur(t).tag != "}") {
block->list.push_back(parse_statement(t));
if (cur(t).tag == ";")
i++;
}
match(t, "}");
return block;
}
std::shared_ptr<AST> parse_while(std::vector<Token>& t)
{
if (!match(t, "while"))
throw std::runtime_error("Expected 'while'");
if (!match(t, "("))
throw std::runtime_error("Expected '(' after while");
auto condition = parse_expression(t);
if (!match(t, ")"))
throw std::runtime_error("Expected ')'");
auto body = parse_statement(t);
auto node = std::make_shared<AST>();
node->tag = "while";
node->left = condition;
node->right = body;
return node;
}
std::shared_ptr<AST> parse_statement(std::vector<Token>& t)
{
if (cur(t).tag == "identifier" && (i + 1 < t.size()) && t[i + 1].tag == "=") {
std::string name = cur(t).value;
i += 2; // consume identifier and '='
auto value = parse_expression(t);
auto node = std::make_shared<AST>();
node->tag = "=";
node->left = std::make_shared<AST>();
node->left->tag = "identifier";
node->left->value = name;
node->right = value;
return node;
}
// -------- print --------
if (cur(t).tag == "print") {
i++;
auto node = std::make_shared<AST>();
node->tag = "print";
if (cur(t).tag != ";" && cur(t).tag != "}")
node->left = parse_expression(t);
return node;
}
// -------- if --------
if (cur(t).tag == "if") {
i++; // consume if
if (!match(t, "("))
throw std::runtime_error("Expected '(' after if");
auto condition = parse_expression(t);
if (!match(t, ")"))
throw std::runtime_error("Expected ')'");
auto thenBranch = parse_statement(t);
auto node = std::make_shared<AST>();
node->tag = "if";
node->left = condition;
// default: no else
node->right = std::make_shared<AST>();
node->right->tag = "if_body";
node->right->left = thenBranch;
// -------- handle optional else --------
if (cur(t).tag == "else") {
i++; // consume else
auto elseBranch = parse_statement(t);
node->right->right = elseBranch;
}
return node;
}
// -------- while --------
if (cur(t).tag == "while")
return parse_while(t);
if (cur(t).tag == "return") {
i++; // consume return
auto node = std::make_shared<AST>();
node->tag = "return";
// optional return expression
if (cur(t).tag != "}" && cur(t).tag != ";" && cur(t).tag != "EOF") {
node->left = parse_expression(t);
}
return node;
}
// -------- block --------
if (cur(t).tag == "{") {
return parse_block(t);
}
// -------- expression statement --------
return parse_expression(t);
}
// ---------------- entry ----------------
std::shared_ptr<AST> parse(std::vector<Token> tokens)
{
i = 0;
auto root = std::make_shared<AST>();
root->tag = "statement_list";
while (cur(tokens).tag != "") {
if (cur(tokens).tag == "")
break;
root->list.push_back(parse_statement(tokens));
}
return root;
}

110
src/tokenizer.cpp Normal file
View File

@@ -0,0 +1,110 @@
#include "../include/tokenizer.h"
#include <regex>
#include <stdexcept>
struct Pattern {
std::regex re;
std::string tag;
};
static std::vector<Pattern> patterns = {
{ std::regex(R"(//[^\n]*)"), "comment" },
{ std::regex(R"(\s+)"), "whitespace" },
{ std::regex(R"(\d*\.\d+|\d+\.\d*|\d+)"), "number" },
{ std::regex(R"("([^"]|"")*")"), "string" },
{ std::regex(R"(true|false)"), "boolean" },
{ std::regex(R"(null)"), "null" },
{ std::regex(R"(function)"), "function" },
{ std::regex(R"(return)"), "return" },
{ std::regex(R"(extern)"), "extern" },
{ std::regex(R"(if)"), "if" },
{ std::regex(R"(else)"), "else" },
{ std::regex(R"(while)"), "while" },
{ std::regex(R"(for)"), "for" },
{ std::regex(R"(break)"), "break" },
{ std::regex(R"(continue)"), "continue" },
{ std::regex(R"(print)"), "print" },
{ std::regex(R"(import)"), "import" },
{ std::regex(R"(exit)"), "exit" },
{ std::regex(R"(and)"), "&&" },
{ std::regex(R"(or)"), "||" },
{ std::regex(R"(not)"), "!" },
{ std::regex(R"([a-zA-Z_][a-zA-Z0-9_]*)"), "identifier" },
{ std::regex(R"(\+)"), "+" },
{ std::regex(R"(-)"), "-" },
{ std::regex(R"(\*)"), "*" },
{ std::regex(R"(\/)"), "/" },
{ std::regex(R"(\%)"), "%" },
{ std::regex(R"(\()"), "(" },
{ std::regex(R"(\))"), ")" },
{ std::regex(R"(\{)"), "{" },
{ std::regex(R"(\})"), "}" },
{ std::regex(R"(==)"), "==" },
{ std::regex(R"(!=)"), "!=" },
{ std::regex(R"(<=)"), "<=" },
{ std::regex(R"(>=)"), ">=" },
{ std::regex(R"(<)"), "<" },
{ std::regex(R"(>)"), ">" },
{ std::regex(R"(\|\|)"), "||" },
{ std::regex(R"(\|\|)"), "||" },
{ std::regex(R"(\!)"), "!" },
{ std::regex(R"(=)"), "=" },
{ std::regex(R"(\.)"), "." },
{ std::regex(R"(\[)"), "[" },
{ std::regex(R"(\])"), "]" },
{ std::regex(R"(,)"), "," },
{ std::regex(R"(:)"), ":" },
{ std::regex(R"(;)"), ";" },
{ std::regex(R"(.)"), "error" }
};
std::vector<Token> tokenize(const std::string& input)
{
std::vector<Token> tokens;
size_t pos = 0;
int line = 1, col = 1;
while (pos < input.size()) {
std::smatch match;
bool found = false;
for (auto& p : patterns) {
std::string s = input.substr(pos);
if (std::regex_search(s, match, p.re, std::regex_constants::match_continuous)) {
std::string value = match.str();
if (p.tag == "whitespace") {
for (char c : value) {
if (c == '\n') {
line++;
col = 1;
} else
col++;
}
} else if (p.tag == "error") {
throw std::runtime_error("Syntax error near: " + value);
} else if (p.tag != "comment") {
Token t;
t.tag = p.tag;
t.value = value;
t.line = line;
t.column = col;
tokens.push_back(t);
col += value.size();
} else {
col += value.size();
}
pos += value.size();
found = true;
break;
}
}
if (!found)
break;
}
tokens.push_back({ "", "", line, col });
return tokens;
}

10
test.triv Normal file
View File

@@ -0,0 +1,10 @@
f = function(a) { return a * 2 }
print f(10)
i = 0
while (i < 3) {
print i
i = i + 1
}
if (1 > 0) { print 10 } else { print 1 }