Hi, I'm Yev Ignatov
CS student at UCF · Math minor · STRONG-AI Scholar
Building compilers, exploring AI, and writing clean software.
/* vm.c — PM/0 Virtual Machine
Author: Yevgeniy Ignatov */
int base(int BP, int L) {
int arb = BP;
while (L-- > 0) arb = pas[arb];
return arb;
}
/* fetch-decode-execute */
int OP = pas[PC], L = pas[PC-1], M = pas[PC-2];
switch (OP) {
case LIT: pas[--SP] = M; break;
case LOD: pas[--SP] = pas[base(BP,L)-M]; break;
case STO: pas[base(BP,L)-M] = pas[SP++]; break;
case CAL:
pas[SP-1] = base(BP,L); /* static link */
pas[SP-2] = BP; /* dynamic link */
pas[SP-3] = PC-3; /* return addr */
BP = SP-1;
break;
case JPC:
if (!pas[SP++]) PC = (PAS_SIZE-1)-M;
break;
case SYS:
if (M==3) return 0; /* HALT */
}
Building things
from the ground up.
I'm a Computer Science student at the University of Central Florida, pursuing a minor in Mathematics and a recipient of the STRONG-AI scholarship. Based in Orlando, FL.
My interests sit at the intersection of systems programming, mathematics, and AI. I love understanding how things work at a fundamental level — which is why I built a compiler for the PL/0 language from scratch in C.
The math minor isn't incidental — Fourier analysis, linear algebra, and discrete math actively shape how I think about algorithms, signals, and computation. I'm drawn to problems where mathematical structure and code meet.
What I work with
Languages
- C
- Python
- Java
- JavaScript
- HTML/CSS
Concepts
Tools & Platforms
Things I've built
PL/0 Compiler
A full compilation pipeline implemented in C, modeled after the classic PL/0 machine. Includes a lexer, recursive-descent parser, intermediate representation, and a custom code generator that emits instructions for a register-based virtual machine.
- Custom register-based mini-computer architecture
- Full multi-stage pipeline: tokenize → parse → IR → codegen → execute
- Supports arithmetic, branching, loops, and procedure calls
- Implemented entirely in C with no external libraries
Full source files:
/* lex.c — Lexical Analyzer for PL/0
Author: Yevgeniy Ignatov */
#define MAX_IDENT_LEN 11
#define MAX_NUM_LEN 5
#define MAX_TOKENS 500
typedef struct {
char lexeme[25];
TokenType token;
char error_msg[50];
} Token;
void scan_file(const char *filename, TokenList *list) {
FILE *file = fopen(filename, "r");
int c;
while ((c = fgetc(file)) != EOF) {
if (is_invisible(c)) continue;
/* Skip block comments: /* ... */ */
if (c == '/') {
int next = fgetc(file);
if (next == '*') {
int prev = 0;
while ((c = fgetc(file)) != EOF) {
if (prev == '*' && c == '/') break;
prev = c;
}
} else { ungetc(next, file); add_token(list, "/", slashsym); }
continue;
}
/* Identifiers and reserved words */
else if (isalpha(c)) {
char buf[50]; int pos = 0;
buf[pos++] = c;
while ((c = fgetc(file)) != EOF && isalnum(c)) buf[pos++] = c;
buf[pos] = '\0';
if (c != EOF) ungetc(c, file);
if (pos > MAX_IDENT_LEN)
add_error_token(list, buf, "Identifier too long");
else
add_token(list, buf, get_reserved_token(buf));
}
/* Number literals */
else if (isdigit(c)) {
char buf[50]; int pos = 0;
buf[pos++] = c;
while ((c = fgetc(file)) != EOF && isdigit(c)) buf[pos++] = c;
buf[pos] = '\0'; ungetc(c, file);
if (pos > MAX_NUM_LEN) add_error_token(list, buf, "Number too long");
else add_token(list, buf, numbersym);
}
}
fclose(file);
}
/* parsercodegen_complete.c — Recursive-Descent Parser + Code Gen
Author: Yevgeniy Ignatov */
void statement() {
int sym_index, jmp_idx, jpc_indx, loop_addr;
/* Assignment: ident := expression */
if (token == identsym) {
sym_index = symbol_table_lookup(identifier);
if (sym_index == -1) error("undeclared identifier");
if (symbol_table[sym_index].kind != VAR)
error("only variable values may be altered");
get_token();
if (token != becomessym) error("assignment statements must use :=");
get_token();
expression();
int L = current_level - symbol_table[sym_index].level;
emit(STO, L, symbol_table[sym_index].addr);
}
/* While loop with backpatching */
else if (token == whilesym) {
get_token();
loop_addr = code_len; /* save top-of-loop address */
condition();
if (token != dosym) error("while must be followed by do");
get_token();
int index = code_len;
emit(JPC, 0, 0); /* placeholder — patch later */
statement();
emit(JMP, 0, 3 * loop_addr); /* jump back to condition */
code[index].m = 3 * code_len; /* backpatch exit jump */
}
/* If-then-else-fi */
else if (token == ifsym) {
get_token(); condition();
jpc_indx = code_len; emit(JPC, 0, 0);
if (token != thensym) error("if must be followed by then");
get_token(); statement();
if (token != elsesym) error("if statement must include else clause");
jmp_idx = code_len; emit(JMP, 0, 0);
code[jpc_indx].m = 3 * code_len; /* backpatch to else */
get_token(); statement();
if (token != fisym) error("else must be followed by fi");
get_token();
code[jmp_idx].m = 3 * code_len; /* backpatch past fi */
}
}
/* Code generation helpers — parsercodegen_complete.c */
typedef struct {
OpCode op; /* LIT OPR LOD STO CAL INC JMP JPC SYS */
int l; /* lexicographic level */
int m; /* modifier / address / constant */
} Instruction;
Instruction code[MAX_TABLE_SIZE];
int code_len = 0;
void emit(OpCode op, int l, int m) {
code[code_len].op = op;
code[code_len].l = l;
code[code_len].m = m;
code_len++;
}
/* block() — emit JMP over declarations, then INC to
allocate the activation record + local variables */
void block() {
int num_vars = 0;
int jmp_addr = code_len;
emit(JMP, 0, 0); /* forward jump — patched later */
if (token == constsym) const_declaration();
if (token == varsym) num_vars = var_declaration();
/* Compile nested procedures at current_level + 1 */
while (token == procsym) {
/* ... (procedure header parsing) ... */
symbol_table[pidx].addr = code_len * 3;
current_level++;
block(); /* recurse */
current_level--;
emit(OPR, 0, OPR_RTN); /* procedure return */
}
code[jmp_addr].m = code_len * 3; /* patch the opening JMP */
emit(INC, 0, 3 + num_vars); /* allocate AR + locals */
statement();
}
void print_assembly() {
FILE *out = fopen("elf.txt", "w");
for (int i = 0; i < code_len; i++) {
/* human-readable to stdout */
printf("%-6d%-6s%-6d%-6d\n", i,
opcode_to_string(code[i].op),
code[i].l, code[i].m);
/* machine format to elf.txt for the VM */
fprintf(out, "%d %d %d\n",
code[i].op, code[i].l, code[i].m);
}
fclose(out);
}
/* vm.c — PM/0 Virtual Machine
Author: Yevgeniy Ignatov */
#define PAS_SIZE 500
int pas[PAS_SIZE]; /* process address space */
/* Walk static links L levels up to find base of that frame */
int base(int BP, int L) {
int arb = BP;
while (L-- > 0) arb = pas[arb]; /* follow static link */
return arb;
}
/* Main fetch-decode-execute loop */
while (1) {
int OP = pas[PC];
int L = pas[PC - 1];
int M = pas[PC - 2];
int nextPC = PC - 3;
switch (OP) {
case LIT: pas[--SP] = M; break; /* push constant */
case LOD: pas[--SP] = pas[base(BP,L) - M]; break; /* load variable */
case STO: pas[base(BP,L) - M] = pas[SP++]; break; /* store variable */
case CAL: /* call procedure */
pas[SP-1] = base(BP, L); /* static link */
pas[SP-2] = BP; /* dynamic link */
pas[SP-3] = PC - 3; /* return addr */
BP = SP - 1;
nextPC = (PAS_SIZE - 1) - M;
break;
case INC: SP -= M; break; /* alloc locals */
case JMP: nextPC = (PAS_SIZE-1) - M; break; /* unconditional jump */
case JPC: if (!pas[SP++]) nextPC=(PAS_SIZE-1)-M; break; /* cond jump */
case OPR: /* return from proc */
if (M == 0) { SP = BP+1; BP = pas[BP-1]; nextPC = pas[BP-2]; }
else opr_arithmetic(M); /* ADD SUB MUL DIV EQL NEQ ... */
break;
case SYS: /* I/O + halt */
if (M == 1) printf("Output: %d\n", pas[SP++]);
if (M == 2) { scanf("%d", &input); pas[--SP] = input; }
if (M == 3) return 0; /* HALT */
break;
}
PC = nextPC;
print_state(OP, L, M, PC, BP, SP, textEnd);
}
Personal Portfolio
This site — built from scratch with HTML, CSS, and vanilla JavaScript. Custom design system, dark/light mode, and a code showcase section.
PL/0 Machine
A complete compiler pipeline in C — lexer, recursive-descent parser, symbol table, P-code generator, and a stack-machine VM. Modeled after the classic PM/0 architecture.
iOS App
A mobile application currently in development. Details coming soon.
Academic background
B.S. Computer Science, Minor in Mathematics
University of Central Florida (UCF)
STRONG-AI Scholarship recipient. Coursework in compilers, data structures, discrete math, algorithms, and AI.
Associate of Arts — General Studies
Valencia College
Digital Design & Web Development
Technical Education Center of Osceola (TECO)
Let's connect.
I'm open to internship opportunities, collaborations, and conversations about CS, math, or AI. Reach out anytime.