25 Commits
Author SHA1 Message Date
hasslesstech 81deb637c0 improve emulator output 2026-11-07 22:10:11 +02:00
hasslesstech f43a16d1b2 unify operator weights, add recursive weight method to TreeNode 2026-11-07 21:50:26 +02:00
hasslesstech 7a90247dc0 lab5: improve weighting during compilation, fix assembly operator translation bug 2026-11-07 21:02:02 +02:00
hasslesstech 36b4596bd4 lab5: add assembler and dynamic pipeline system emulator 2026-11-07 20:44:35 +02:00
hasslesstech 79a96aea77 cleanup + minor fixes 2026-10-15 16:42:52 +03:00
hasslesstech ab4afb48e7 remove unneccesary operation 2026-10-15 10:16:29 +03:00
hasslesstech b15ae562fd lab2 feat: prevent division by zero in non-folded operations 2026-10-15 10:16:01 +03:00
hasslesstech 0329ed8326 fix range selection during bracket removal scanning 2026-10-15 09:58:31 +03:00
hasslesstech 4c26353876 fix get_prior_mop selector, loosen overly strict checks in remove_extra_brackets logic 2026-10-15 09:33:07 +03:00
hasslesstech 360628d65c fix logic for selecting brackets to open; same outer level op ranges select incorrectly 2026-10-15 09:03:54 +03:00
hasslesstech e837671d15 fix post-processing 2026-10-14 21:49:59 +03:00
hasslesstech 10e0a487ab failed attempt to hotfix wrong post-processing bracket adding algorithm 2026-10-14 21:45:17 +03:00
hasslesstech 1a258cd051 apply empty brackets removal until full fix 2026-10-14 21:18:27 +03:00
hasslesstech f430b3616a fix unchecked selection of brackets that caught and removed unrelated tokens 2026-10-14 21:17:32 +03:00
hasslesstech ddcf261af7 remove excessive debug logging from Parser 2026-10-14 21:16:09 +03:00
hasslesstech 89335fbdc6 fix remove_extra_brackets undoing work of post-processing step 2026-10-14 20:44:20 +03:00
hasslesstech 1b4693029f update config 2026-10-14 20:31:24 +03:00
hasslesstech e8c2293b50 add bracket opening logs 2026-10-14 20:31:10 +03:00
hasslesstech 0d39290709 fix infitite search for prior operator when opening brackets 2026-10-14 20:07:28 +03:00
hasslesstech 84d28d3845 rework syntax checks, rework priority correction algorithm 2026-10-14 19:57:02 +03:00
hasslesstech 433ee2bb2e lab2 feat: add constant folding 2026-10-14 15:16:08 +03:00
hasslesstech 35e38127e7 add essential token preprocessing 2026-10-14 12:00:41 +03:00
hasslesstech 1d056a736f improve output stage naming 2026-10-12 23:55:16 +03:00
hasslesstech a94e584f35 add color highlighting during token post-processing 2026-10-12 21:59:50 +03:00
hasslesstech d97160e6ed fix missing bracket folds for SUB and DIV operator replacement parallelization 2026-10-12 21:29:02 +03:00
15 changed files with 1625 additions and 500 deletions
+20 -1
View File
@@ -1,6 +1,6 @@
cmake_minimum_required(VERSION 3.10) cmake_minimum_required(VERSION 3.10)
SET(CMAKE_CXX_FLAGS_DEBUG "-g") SET(CMAKE_CXX_FLAGS_DEBUG "-g -Wall -Wextra -Wpedantic")
project(pzks) project(pzks)
@@ -26,6 +26,23 @@ target_include_directories(syntaxer PRIVATE
${GLOBAL_INC} ${GLOBAL_INC}
) )
add_library(dyn-pipeline STATIC)
target_sources(dyn-pipeline PRIVATE
src/dyn-pipeline.cpp
)
target_include_directories(dyn-pipeline PRIVATE
${GLOBAL_INC}
)
add_library(assembler STATIC)
target_sources(assembler PRIVATE
src/assembler.cpp
)
target_include_directories(assembler PRIVATE
${GLOBAL_INC}
)
add_executable(tester) add_executable(tester)
target_sources(tester PRIVATE target_sources(tester PRIVATE
src/test.cpp src/test.cpp
@@ -51,4 +68,6 @@ target_include_directories(main PRIVATE
target_link_libraries(main PRIVATE target_link_libraries(main PRIVATE
parser parser
syntaxer syntaxer
assembler
dyn-pipeline
) )
+8 -1
View File
@@ -1,6 +1,13 @@
#pragma once #pragma once
#define DEBUG 1 #define PRINT_DEBUG 1
#define PRINT_INFO 1
#define PRINT_DISABLE_PREFIXES 1 #define PRINT_DISABLE_PREFIXES 1
#define MAX_TOKEN_STRING_LEN 256 #define MAX_TOKEN_STRING_LEN 256
#define LAB2_EXCLUDE_COMPTIME_OP_WRITES 1
#define LAB2_PREVENT_NON_FOLDED_DIV_BY_ZERO 1
#define DYN_PIPELINE_MEM_SIZE 512
#define DYN_PIPELINE_LAYERS 3
+16
View File
@@ -0,0 +1,16 @@
#pragma once
#include <vector>
#include "expression.hpp"
#include "dyn-pipeline.hpp"
namespace Assembler {
void assemble(
const Expression::TreeNode& root,
std::vector<DynamicPipeline::Command>& program,
std::array<double, DYN_PIPELINE_MEM_SIZE>& memory,
int& mem_used);
}
+61
View File
@@ -0,0 +1,61 @@
#pragma once
#include <array>
#include <vector>
#include <cstdint>
#include "config.h"
namespace DynamicPipeline {
enum CommandType : char
{
ADD,
SUB,
MUL,
DIV,
POW,
CMD_COUNT
};
struct Command
{
enum CommandType t;
uint16_t op1;
uint16_t op2;
uint16_t res;
Command(enum CommandType t, uint16_t op1, uint16_t op2, uint16_t res)
: t(t), op1(op1), op2(op2), res(res)
{
}
};
struct MemCell
{
double v;
int ready;
};
class System
{
private:
std::vector<Command> *program_memory;
std::array<MemCell, DYN_PIPELINE_MEM_SIZE> data_memory;
std::array<int, DYN_PIPELINE_LAYERS> layer_cmd {-1};
unsigned int pc {0};
unsigned int system_time;
void print_pipeline(void) const;
bool pipeline_empty(void) const;
bool next_instruction(void) const;
bool instruction_ready(void) const;
void step(void);
public:
void init(const std::vector<Command> * const prg, const std::array<double, DYN_PIPELINE_MEM_SIZE>& mem, const int init_mem_size);
void run(void);
};
}
+110 -4
View File
@@ -5,6 +5,8 @@
#include <memory> #include <memory>
#include <string> #include <string>
#include "print.h"
namespace Expression { namespace Expression {
class Token { class Token {
@@ -24,9 +26,35 @@ public:
SUB, SUB,
MUL, MUL,
DIV, DIV,
POW POW,
NOP // not an operator
}; };
static int weight_op(Operator o)
{
switch (o)
{
case Expression::Token::OPA:
case Expression::Token::CPA:
case Expression::Token::NOP:
pr_warn("Unweightable operator - this is probably a bug!");
return 0;
case Expression::Token::ADD:
return 1;
case Expression::Token::SUB:
return 1;
case Expression::Token::MUL:
return 2;
case Expression::Token::DIV:
return 2;
case Expression::Token::POW:
return 3;
default:
pr_warn("Impossible operator - this is a bug!");
return 0;
}
}
union TokenValue { union TokenValue {
std::shared_ptr<std::string> s; std::shared_ptr<std::string> s;
double d; double d;
@@ -52,6 +80,7 @@ public:
} }
explicit TokenValue(void) explicit TokenValue(void)
: s(std::shared_ptr<std::string>())
{ {
} }
@@ -144,6 +173,7 @@ public:
switch (t) switch (t)
{ {
case VAR: case VAR:
new(&v.s) std::shared_ptr<std::string>;
v.s = other.v.s; v.s = other.v.s;
break; break;
case CONST: case CONST:
@@ -174,7 +204,6 @@ public:
return v.s; return v.s;
case CONST: case CONST:
{ {
char buffer[128] = {0};
return std::make_shared<std::string>((std::ostringstream() << v.d).str()); return std::make_shared<std::string>((std::ostringstream() << v.d).str());
} }
case OPERATOR: case OPERATOR:
@@ -192,11 +221,14 @@ public:
return v.s; return v.s;
case CONST: case CONST:
{ {
char buffer[128] = {0};
return std::make_shared<std::string>((std::ostringstream() << v.d).str()); return std::make_shared<std::string>((std::ostringstream() << v.d).str());
} }
case OPERATOR: case OPERATOR:
return std::make_shared<std::string>(op_math_str()); return std::make_shared<std::string>(op_math_str());
case CV:
return std::make_shared<std::string>((std::ostringstream() << "t" << v.cv.i).str());
case NAT:
return std::make_shared<std::string>("");
default: default:
return std::make_shared<std::string>("Not a valid token!"); return std::make_shared<std::string>("Not a valid token!");
} }
@@ -272,6 +304,58 @@ public:
return "Invalid operator"; return "Invalid operator";
} }
} }
int negative_operator(void) const
{
if (t != OPERATOR)
{
return 0;
}
return (v.o == SUB || v.o == DIV);
}
static int is_negative(Operator o)
{
return (o == SUB || o == DIV);
}
int is_math_op(void) const
{
if (t != OPERATOR)
{
return 0;
}
switch (v.o)
{
case OPA:
case CPA:
return 0;
default:
return 1;
}
}
int is_opa(void) const
{
if (t != OPERATOR)
{
return 0;
}
return v.o == OPA;
}
int is_cpa(void) const
{
if (t != OPERATOR)
{
return 0;
}
return v.o == CPA;
}
}; };
struct Operation struct Operation
@@ -302,7 +386,7 @@ public:
// if value, left and right hands should be null // if value, left and right hands should be null
explicit TreeNode(void) explicit TreeNode(void)
: l(nullptr), r(nullptr), o() : l(nullptr), r(nullptr), o(Token::NOP)
{ {
} }
@@ -323,6 +407,28 @@ public:
delete r; delete r;
} }
} }
int weight()
{
int w = 0;
if (l != nullptr)
{
w += l->weight();
}
if (r != nullptr)
{
w += r->weight();
}
if (o.t == Token::OPERATOR)
{
Token::weight_op(o.v.o);
}
return w;
}
}; };
} }
+1
View File
@@ -9,5 +9,6 @@ std::vector<Expression::Token> tokenize_input(const char * const input_string);
int assert_same(const std::vector<Expression::Token>& a, const std::vector<Expression::Token>& b); int assert_same(const std::vector<Expression::Token>& a, const std::vector<Expression::Token>& b);
void print_token_vector(const std::vector<Expression::Token>& tv); void print_token_vector(const std::vector<Expression::Token>& tv);
void print_math_vector(const std::vector<Expression::Token>& tv); void print_math_vector(const std::vector<Expression::Token>& tv);
void print_math_vector(const std::vector<Expression::Token>& tv, const std::vector<int>& highlight_locations);
} }
+18 -1
View File
@@ -1,9 +1,10 @@
#pragma once #pragma once
#include <iostream> #include <iostream>
#include <iomanip>
#include "config.h" #include "config.h"
#if DEBUG == 1 #if PRINT_DEBUG == 1
#if PRINT_DISABLE_PREFIXES == 1 #if PRINT_DISABLE_PREFIXES == 1
#define pr_debug(...) std::cout << "debug: " << __VA_ARGS__ << "\n" #define pr_debug(...) std::cout << "debug: " << __VA_ARGS__ << "\n"
#define pri_debug(...) std::cout << __VA_ARGS__ #define pri_debug(...) std::cout << __VA_ARGS__
@@ -19,6 +20,22 @@
#define prs_debug(...) do {} while (0) #define prs_debug(...) do {} while (0)
#endif #endif
#if PRINT_INFO == 1
#if PRINT_DISABLE_PREFIXES == 1
#define pr_info(...) std::cout << "info: " << __VA_ARGS__ << "\n"
#define pri_info(...) std::cout << __VA_ARGS__
#define prs_info(...) std::cout << "info: " << __VA_ARGS__
#else
#define pr_info(...) std::cout << "[" << __FILE__ << ":" << __LINE__ << " " << __func__ << "] info: " << __VA_ARGS__ << "\n"
#define pri_info(...) std::cout << __VA_ARGS__
#define prs_info(...) std::cout << "[" << __FILE__ << ":" << __LINE__ << " " << __func__ << "] info: " << __VA_ARGS__
#endif
#else
#define pr_info(...) do {} while (0)
#define pri_info(...) do {} while (0)
#define prs_info(...) do {} while (0)
#endif
#if PRINT_DISABLE_PREFIXES == 1 #if PRINT_DISABLE_PREFIXES == 1
#define pr_warn(...) std::cout << "warning: " << __VA_ARGS__ << "\n" #define pr_warn(...) std::cout << "warning: " << __VA_ARGS__ << "\n"
#else #else
+3 -4
View File
@@ -6,10 +6,9 @@
namespace Syntax namespace Syntax
{ {
int check_syntax(std::vector<Expression::Token>& tv); int prepare_token_vector(std::vector<Expression::Token>& tv);
void apply_corrections(std::vector<Expression::Token>& tv);
std::vector<Expression::Operation> build_op_vector(const std::vector<Expression::Token>& tv); std::vector<Expression::Operation> build_op_vector(const std::vector<Expression::Token>& tv);
Expression::TreeNode ov_to_tree(const std::vector<Expression::Token> tv, const std::vector<Expression::Operation>& ov); Expression::TreeNode ov_to_tree(const std::vector<Expression::Token>& tv, const std::vector<Expression::Operation>& ov);
void print_op_tree(const std::vector<Expression::Token> tv, const std::vector<Expression::Operation>& ov); void print_op_tree(const std::vector<Expression::Token>& tv, const std::vector<Expression::Operation>& ov);
} }
+99
View File
@@ -0,0 +1,99 @@
#include "assembler.hpp"
namespace Assembler {
// operand source
enum OpSrc
{
PRELOAD, // static memory
DYNAMIC // dynamic memory
};
struct Command
{
DynamicPipeline::CommandType opcode;
OpSrc left_src;
int left_index;
OpSrc right_src;
int right_index;
Command(DynamicPipeline::CommandType opcode, OpSrc left_src, OpSrc right_src, int left_index, int right_index)
: opcode(opcode), left_src(left_src), left_index(left_index), right_src(right_src), right_index(right_index)
{
}
};
int descend_var(const Expression::TreeNode& node, int& preload_mem)
{
(void) node;
preload_mem++;
return preload_mem - 1; // this index in preload memory
}
int descend_operator(const Expression::TreeNode& node, int& preload_mem, std::vector<Command>& commands)
{
int left, right;
OpSrc lo, ro;
if (node.l->o.t == Expression::Token::OPERATOR)
{
left = descend_operator(*node.l, preload_mem, commands);
lo = DYNAMIC;
}
else
{
left = descend_var(*node.l, preload_mem);
lo = PRELOAD;
}
if (node.r->o.t == Expression::Token::OPERATOR)
{
right = descend_operator(*node.r, preload_mem, commands);
ro = DYNAMIC;
}
else
{
right = descend_var(*node.r, preload_mem);
ro = PRELOAD;
}
commands.emplace_back((DynamicPipeline::CommandType) (node.o.v.o - 2), lo, ro, left, right);
return commands.size() - 1; // this index in dynamic memory
}
void assemble(
const Expression::TreeNode& root,
std::vector<DynamicPipeline::Command>& program,
std::array<double, DYN_PIPELINE_MEM_SIZE>& memory,
int& mem_used)
{
(void) memory;
int preload_mem = 0;
std::vector<Command> commands;
if (root.o.t == Expression::Token::OPERATOR)
{
(void) descend_operator(root, preload_mem, commands);
}
else
{
(void) descend_var(root, preload_mem);
}
mem_used = preload_mem;
program.reserve(commands.size());
for (int i = 0, l = commands.size(); i < l; i++)
{
Command &cmd = commands[i];
program.emplace_back(cmd.opcode, cmd.left_src * preload_mem + cmd.left_index, cmd.right_src * preload_mem + cmd.right_index, preload_mem + i);
}
}
}
View File
+188
View File
@@ -0,0 +1,188 @@
#include "dyn-pipeline.hpp"
#include "print.h"
#include "expression.hpp"
namespace DynamicPipeline {
static int weight_op(DynamicPipeline::CommandType o)
{
return Expression::Token::weight_op(static_cast<Expression::Token::Operator>(o + 2));
}
static const char *op_to_str(DynamicPipeline::CommandType o)
{
switch (o)
{
case ADD:
return "ADD";
case SUB:
return "SUB";
case MUL:
return "MUL";
case DIV:
return "DIV";
case POW:
return "POW";
default:
pr_warn("Impossible token - this is a bug!");
return "Impossible token";
}
}
void System::init(const std::vector<Command> * const prg, const std::array<double, DYN_PIPELINE_MEM_SIZE>& mem, const int init_mem_size)
{
pr_info("Dynamic Pipeline System initialized with:\n"
<< prg->size() << " commands in program memory\n"
<< init_mem_size << " values in data memory");
program_memory = const_cast<std::vector<Command> *>(prg);
for (int i = 0; i < init_mem_size; i++)
{
data_memory[i].v = mem[i];
data_memory[i].ready = 1;
}
for (int i = 0; i < DYN_PIPELINE_LAYERS; i++)
{
layer_cmd[i] = -1;
}
pc = 0;
system_time = 0;
}
void System::print_pipeline(void) const
{
prs_info(" ");
for (int i = 0; i < DYN_PIPELINE_LAYERS - 1; i++)
{
if (layer_cmd[i] != -1)
{
pri_info(std::setw(3) << layer_cmd[i] << " (" << op_to_str((*program_memory)[layer_cmd[i]].t) << ") | ");
}
else
{
pri_info("--- (---) | ");
}
}
if (layer_cmd[DYN_PIPELINE_LAYERS - 1] != -1)
{
int addr = layer_cmd[DYN_PIPELINE_LAYERS - 1];
pri_info(std::setw(3) << addr << " (" << op_to_str((*program_memory)[addr].t) << ")\n");
}
else
{
pri_info("--- (---)\n");
}
}
bool System::pipeline_empty(void) const
{
for (int i = 0; i < DYN_PIPELINE_LAYERS; i++)
{
if (layer_cmd[i] != -1)
{
return false;
}
}
return true;
}
bool System::next_instruction(void) const
{
return pc < program_memory->size();
}
bool System::instruction_ready(void) const
{
if (!next_instruction())
{
return false;
}
Command &cmd = (*program_memory)[pc];
if (!data_memory[cmd.op1].ready)
{
return false;
}
if (!data_memory[cmd.op2].ready)
{
return false;
}
return true;
}
void System::step(void)
{
pr_info("Step start time: " << system_time << " ticks");
// count time
int time_taken = 1;
for (int i = 0; i < DYN_PIPELINE_LAYERS; i++)
{
if (layer_cmd[i] == -1)
{
continue;
}
if (const int d = weight_op((*program_memory)[layer_cmd[i]].t); d > time_taken)
{
time_taken = d;
}
}
system_time += time_taken;
// handle output
if (int prg_mem_addr = layer_cmd[DYN_PIPELINE_LAYERS - 1]; prg_mem_addr != -1)
{
Command &cmd = (*program_memory)[prg_mem_addr];
pr_info("Instruction " << prg_mem_addr << " (" << op_to_str(cmd.t) << ") completes, writes output to " << cmd.res);
data_memory[cmd.res].ready = 1;
}
// progress pipeline
for (int i = DYN_PIPELINE_LAYERS - 2; i >= 0; i--)
{
layer_cmd[i+1] = layer_cmd[i];
}
layer_cmd[0] = -1;
// load new operation if ready
if (instruction_ready())
{
Command &cmd = (*program_memory)[pc];
pr_info("Instruction " << pc << " (" << op_to_str(cmd.t) << ") reads " << cmd.op1 << " and " << cmd.op2 << " and enters the pipeline");
layer_cmd[0] = pc;
pc++;
}
pr_info("Step took " << time_taken << " ticks");
}
void System::run(void)
{
while (next_instruction() || !pipeline_empty())
{
print_pipeline();
pri_info("\n");
step();
pri_info("\n");
}
pr_info("System finished in " << system_time << " ticks");
}
}
+14 -3
View File
@@ -1,5 +1,9 @@
#include "config.h"
#include "parser.hpp" #include "parser.hpp"
#include "syntaxer.hpp" #include "syntaxer.hpp"
#include "assembler.hpp"
#include "dyn-pipeline.hpp"
int main(int argc, char **argv) int main(int argc, char **argv)
{ {
@@ -10,14 +14,21 @@ int main(int argc, char **argv)
std::vector<Expression::Token> tv = Parser::tokenize_input(argv[1]); std::vector<Expression::Token> tv = Parser::tokenize_input(argv[1]);
if (Syntax::check_syntax(tv)) if (Syntax::prepare_token_vector(tv))
{ {
return 0; return 0;
} }
Syntax::apply_corrections(tv);
std::vector<Expression::Operation> ov = Syntax::build_op_vector(tv); std::vector<Expression::Operation> ov = Syntax::build_op_vector(tv);
Expression::TreeNode root = Syntax::ov_to_tree(tv, ov); Expression::TreeNode root = Syntax::ov_to_tree(tv, ov);
std::vector<DynamicPipeline::Command> program;
std::array<double, DYN_PIPELINE_MEM_SIZE> memory;
int mem_used;
Assembler::assemble(root, program, memory, mem_used);
DynamicPipeline::System machine;
machine.init(&program, memory, mem_used);
machine.run();
} }
+29 -4
View File
@@ -35,8 +35,6 @@ class Parser
int switch_state(enum State e) int switch_state(enum State e)
{ {
pr_debug("Leaving " << state_str(s) << ", entering " << state_str(e));
int cumulative_backtrack = 0; int cumulative_backtrack = 0;
switch (s) switch (s)
@@ -79,8 +77,6 @@ class Parser
s = e; s = e;
pr_debug("Cumulative backtrack is " << cumulative_backtrack);
return cumulative_backtrack; return cumulative_backtrack;
} }
@@ -397,4 +393,33 @@ void print_math_vector(const std::vector<Expression::Token>& tv)
} }
} }
void print_math_vector(const std::vector<Expression::Token>& tv, const std::vector<int>& highlight_locations)
{
int i, l, h, hl;
for (i = 0, l = tv.size() - 1, h = 0, hl = highlight_locations.size(); i < l; i++)
{
if (h < hl && highlight_locations[h] == i)
{
std::cout << "\033[31m" << *tv[i].to_math_str() << "\033[0m";
h++;
}
else
{
std::cout << *tv[i].to_math_str();;
}
}
if (tv.size())
{
if (highlight_locations[highlight_locations.size()-1] == (long) (tv.size() - 1))
{
std::cout << "\033[31m" << *tv[tv.size() - 1].to_math_str() << "\033[0m\n";
}
else
{
std::cout << *tv[tv.size() - 1].to_math_str() << "\n";
}
}
}
} }
+1024 -451
View File
File diff suppressed because it is too large Load Diff
+4 -1
View File
@@ -99,7 +99,10 @@ TEST(tree_proper_test_1)
TEST(tree_proper_test_2) TEST(tree_proper_test_2)
{ {
std::vector<Expression::Token> o = Parser::tokenize_input("(1**5 * (1-2-3-4-5-6-7-8))"); std::vector<Expression::Token> o = Parser::tokenize_input("(1**5 * (1-2-3-4-5-6-7-8))");
Syntax::apply_corrections(o); if (Syntax::prepare_token_vector(o))
{
return 1;
}
std::vector<Expression::Operation> ov = Syntax::build_op_vector(o); std::vector<Expression::Operation> ov = Syntax::build_op_vector(o);
return 0; return 0;