673 lines
16 KiB
C
673 lines
16 KiB
C
#ifndef _WIN32
|
|
#include <dlfcn.h>
|
|
#else
|
|
#include <windows.h>
|
|
#endif
|
|
#include <stdint.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <time.h>
|
|
|
|
#include "config.h"
|
|
#include "execute.h"
|
|
#include "stack.h"
|
|
|
|
#define NAME stack->program[stack->program_counter].name
|
|
#define DATA stack->program[stack->program_counter].data
|
|
#define ARG stack->program[stack->program_counter].arg
|
|
|
|
typedef enum
|
|
{
|
|
MPUSH = '>',
|
|
MDROP = '$',
|
|
MDUP = ':',
|
|
MSWAP = '\\',
|
|
MINC = '+',
|
|
MDEC = '-',
|
|
|
|
PDRAIN = '/', // Haha drain gang
|
|
PPIPE = '|'
|
|
} OP;
|
|
|
|
static void prefix_operator(Stack *stack, char op)
|
|
{
|
|
if (NAME[0] != '@' && (NAME[1] == '\0' || NAME[1] == '\n'))
|
|
{
|
|
printf("%c\n", op);
|
|
kms(stack, "Invalid usage of prefix operators");
|
|
}
|
|
|
|
switch (op)
|
|
{
|
|
case PDRAIN:
|
|
NAME++;
|
|
|
|
stack_push(stack, DATA);
|
|
|
|
DATA = 0;
|
|
|
|
break;
|
|
|
|
case PPIPE:
|
|
NAME++;
|
|
|
|
DATA = stack_pop(stack);
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void expression(Stack *stack, char *code)
|
|
{
|
|
uint16_t ax, ax2;
|
|
|
|
for (uint16_t i = 0; code[i] != '\0'; i++)
|
|
{
|
|
switch (code[i])
|
|
{
|
|
case MPUSH:
|
|
stack_push(stack, 0);
|
|
break;
|
|
|
|
case MDROP:
|
|
stack_pop(stack);
|
|
break;
|
|
|
|
case MDUP:
|
|
stack_push(stack, stack_peek(stack));
|
|
break;
|
|
|
|
case MSWAP:
|
|
ax = stack_pop(stack);
|
|
ax2 = stack_pop(stack);
|
|
stack_push(stack, ax);
|
|
stack_push(stack, ax2);
|
|
break;
|
|
|
|
case MINC:
|
|
ax = stack_pop(stack);
|
|
stack_push(stack, ax + 1);
|
|
break;
|
|
|
|
case MDEC:
|
|
ax = stack_pop(stack);
|
|
stack_push(stack, ax - 1);
|
|
break;
|
|
|
|
default:
|
|
printf("INVALID EXPRESSION: UNKNOWN OPERATOR '%c'\n", code[i]);
|
|
kms(stack, "INVALID EXPRESSION");
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void find_labels(Stack *stack)
|
|
{
|
|
for (; stack->program_counter < stack->program_size; stack->program_counter++)
|
|
{
|
|
if (NAME[0] == '@')
|
|
{
|
|
if (DATA > MAX_LABELS)
|
|
{
|
|
printf("INVALID LABEL: %i\n", DATA);
|
|
kms(stack, "INVALID LABEL");
|
|
}
|
|
|
|
if (stack->labels[DATA] != -1)
|
|
{
|
|
printf("LABEL ALREADY DEFINED: %i\n", DATA);
|
|
kms(stack, "LABEL ALREADY DEFINED");
|
|
}
|
|
|
|
if (DATA == 0)
|
|
{
|
|
stack->labels[0] = stack->program_counter;
|
|
continue;
|
|
}
|
|
|
|
stack->labels[DATA] = stack->program_counter;
|
|
}
|
|
}
|
|
if (stack->labels[0] != -1)
|
|
stack->program_counter = stack->labels[0];
|
|
else
|
|
stack->program_counter = 0;
|
|
}
|
|
|
|
void execute(Stack *stack, Stack *originstack, char *modname)
|
|
{
|
|
find_labels(stack);
|
|
|
|
for (; stack->program_counter < stack->program_size; stack->program_counter++)
|
|
{
|
|
#ifdef DEBUG
|
|
fprintf(stderr, "Inst: %s, IP: %d, Stack top: %d, Module: %s\n", NAME, stack->program_counter,
|
|
stack_peek(stack), modname);
|
|
#endif
|
|
prefix_operator(stack, NAME[0]);
|
|
|
|
if (NAME[0] == '#' || NAME[0] == '$')
|
|
NAME++;
|
|
|
|
if (NAME[0] == '@')
|
|
continue;
|
|
|
|
// Stack operations
|
|
|
|
if (!strcmp(NAME, "push")) // arg <uint16>
|
|
{
|
|
stack_push(stack, DATA);
|
|
continue;
|
|
}
|
|
|
|
if (!strcmp(NAME, "pop"))
|
|
{
|
|
stack_pop(stack);
|
|
continue;
|
|
}
|
|
|
|
if (!strcmp(NAME, "dup"))
|
|
{
|
|
stack_push(stack, stack_peek(stack));
|
|
continue;
|
|
}
|
|
|
|
if (!strcmp(NAME, "swap"))
|
|
{
|
|
uint16_t a = stack_pop(stack);
|
|
uint16_t b = stack_pop(stack);
|
|
stack_push(stack, a);
|
|
stack_push(stack, b);
|
|
continue;
|
|
}
|
|
|
|
if (!strcmp(NAME, "pick"))
|
|
{
|
|
uint16_t arg = stack_pop(stack);
|
|
|
|
if (arg >= stack->pointer)
|
|
kms(stack, "ATTEMPTED TO PICK BELOW STACK");
|
|
|
|
stack_push(stack, stack->memory[stack->pointer - arg - 1]);
|
|
continue;
|
|
}
|
|
|
|
if (!strcmp(NAME, "string"))
|
|
{
|
|
for (char *i = ARG; *i != '\0'; i++)
|
|
stack_push(stack, *i);
|
|
|
|
stack_push(stack, strlen(ARG));
|
|
|
|
continue;
|
|
}
|
|
|
|
// Math
|
|
|
|
if (!strcmp(NAME, "add"))
|
|
{
|
|
uint16_t a = stack_pop(stack);
|
|
uint16_t b = stack_pop(stack);
|
|
stack_push(stack, b + a);
|
|
continue;
|
|
}
|
|
|
|
if (!strcmp(NAME, "sub"))
|
|
{
|
|
uint16_t a = stack_pop(stack);
|
|
uint16_t b = stack_pop(stack);
|
|
stack_push(stack, b - a);
|
|
continue;
|
|
}
|
|
|
|
if (!strcmp(NAME, "mul"))
|
|
{
|
|
uint16_t a = stack_pop(stack);
|
|
uint16_t b = stack_pop(stack);
|
|
stack_push(stack, b * a);
|
|
continue;
|
|
}
|
|
|
|
if (!strcmp(NAME, "div"))
|
|
{
|
|
uint16_t a = stack_pop(stack);
|
|
uint16_t b = stack_pop(stack);
|
|
stack_push(stack, b / a);
|
|
continue;
|
|
}
|
|
|
|
if (!strcmp(NAME, "mod"))
|
|
{
|
|
uint16_t a = stack_pop(stack);
|
|
uint16_t b = stack_pop(stack);
|
|
stack_push(stack, b % a);
|
|
continue;
|
|
}
|
|
|
|
// Logic
|
|
|
|
if (!strcmp(NAME, "and"))
|
|
{
|
|
uint16_t a = stack_pop(stack);
|
|
uint16_t b = stack_pop(stack);
|
|
stack_push(stack, a && b);
|
|
continue;
|
|
}
|
|
|
|
if (!strcmp(NAME, "or"))
|
|
{
|
|
uint16_t a = stack_pop(stack);
|
|
uint16_t b = stack_pop(stack);
|
|
stack_push(stack, a || b);
|
|
continue;
|
|
}
|
|
|
|
if (!strcmp(NAME, "xor"))
|
|
{
|
|
uint16_t a = stack_pop(stack);
|
|
uint16_t b = stack_pop(stack);
|
|
stack_push(stack, !a != !b);
|
|
continue;
|
|
}
|
|
|
|
if (!strcmp(NAME, "nand"))
|
|
{
|
|
uint16_t a = stack_pop(stack);
|
|
uint16_t b = stack_pop(stack);
|
|
stack_push(stack, !(a && b));
|
|
continue;
|
|
}
|
|
|
|
if (!strcmp(NAME, "not"))
|
|
{
|
|
stack_push(stack, !stack_pop(stack));
|
|
continue;
|
|
}
|
|
|
|
// Bitwise
|
|
|
|
if (!strcmp(NAME, "band"))
|
|
{
|
|
uint16_t a = stack_pop(stack);
|
|
uint16_t b = stack_pop(stack);
|
|
stack_push(stack, a & b);
|
|
continue;
|
|
}
|
|
|
|
if (!strcmp(NAME, "bor"))
|
|
{
|
|
uint16_t a = stack_pop(stack);
|
|
uint16_t b = stack_pop(stack);
|
|
stack_push(stack, a | b);
|
|
continue;
|
|
}
|
|
|
|
if (!strcmp(NAME, "bxor"))
|
|
{
|
|
uint16_t a = stack_pop(stack);
|
|
uint16_t b = stack_pop(stack);
|
|
stack_push(stack, a ^ b);
|
|
continue;
|
|
}
|
|
|
|
if (!strcmp(NAME, "bnand"))
|
|
{
|
|
uint16_t a = stack_pop(stack);
|
|
uint16_t b = stack_pop(stack);
|
|
stack_push(stack, ~(a & b));
|
|
continue;
|
|
}
|
|
|
|
if (!strcmp(NAME, "bnot"))
|
|
{
|
|
stack_push(stack, ~stack_pop(stack));
|
|
continue;
|
|
}
|
|
|
|
// Flow control
|
|
|
|
if (!strcmp(NAME, "jmp")) // arg <uint16>
|
|
{
|
|
if (stack->labels[DATA] == -1)
|
|
{
|
|
printf("UNKNOWN LABEL: %i\n", DATA);
|
|
kms(stack, "UNKNOWN LABEL");
|
|
}
|
|
|
|
stack->program_counter = stack->labels[DATA];
|
|
continue;
|
|
}
|
|
|
|
if (!strcmp(NAME, "jnz")) // arg <uint16>
|
|
{
|
|
if (stack->labels[DATA] == -1)
|
|
{
|
|
printf("UNKNOWN LABEL: %i\n", DATA);
|
|
kms(stack, "UNKNOWN LABEL");
|
|
}
|
|
|
|
if (stack_pop(stack) != 0)
|
|
stack->program_counter = stack->labels[DATA];
|
|
continue;
|
|
}
|
|
|
|
if (!strcmp(NAME, "jz")) // arg <uint16>
|
|
{
|
|
if (stack->labels[DATA] == -1)
|
|
{
|
|
printf("UNKNOWN LABEL: %i\n", DATA);
|
|
kms(stack, "UNKNOWN LABEL");
|
|
}
|
|
|
|
if (stack_pop(stack) == 0)
|
|
stack->program_counter = stack->labels[DATA];
|
|
continue;
|
|
}
|
|
|
|
if (!strcmp(NAME, "kjnz")) // arg <uint16>
|
|
{
|
|
if (stack->labels[DATA] == -1)
|
|
{
|
|
printf("UNKNOWN LABEL: %i\n", DATA);
|
|
kms(stack, "UNKNOWN LABEL");
|
|
}
|
|
|
|
if (stack_peek(stack) != 0)
|
|
stack->program_counter = stack->labels[DATA];
|
|
continue;
|
|
}
|
|
|
|
if (!strcmp(NAME, "kjz")) // arg <uint16>
|
|
{
|
|
if (stack->labels[DATA] == -1)
|
|
{
|
|
printf("UNKNOWN LABEL: %i\n", DATA);
|
|
kms(stack, "UNKNOWN LABEL");
|
|
}
|
|
|
|
if (stack_peek(stack) == 0)
|
|
stack->program_counter = stack->labels[DATA];
|
|
continue;
|
|
}
|
|
|
|
// I/O
|
|
|
|
if (!strcmp(NAME, "putc"))
|
|
{
|
|
putchar(stack_pop(stack));
|
|
continue;
|
|
}
|
|
|
|
if (!strcmp(NAME, "getc"))
|
|
{
|
|
stack_push(stack, getchar());
|
|
continue;
|
|
}
|
|
|
|
if (!strcmp(NAME, "scan"))
|
|
{
|
|
uint16_t num;
|
|
scanf("%hu", &num);
|
|
stack_push(stack, num);
|
|
continue;
|
|
}
|
|
|
|
if (!strcmp(NAME, "meow"))
|
|
{
|
|
printf("%i\n", stack_pop(stack));
|
|
continue;
|
|
}
|
|
|
|
if (!strcmp(NAME, "println")) // arg <string>
|
|
{
|
|
printf("%s\n", ARG);
|
|
continue;
|
|
}
|
|
|
|
if (!strcmp(NAME, "print")) // arg <string>
|
|
{
|
|
printf("%s", ARG);
|
|
continue;
|
|
}
|
|
|
|
// Modules
|
|
|
|
if (!strcmp(NAME, "args")) // arg <uint16>
|
|
{
|
|
if (!originstack)
|
|
kms(stack, "ATTEMPTED TO USE ARGS IN THE MAIN MODULE");
|
|
|
|
if (DATA > STACK_SIZE)
|
|
kms(stack, "ATTEMPTED TO POP MORE THAN STACK_SIZE ARGUMENTS");
|
|
|
|
if (DATA == 0)
|
|
DATA = stack_pop(originstack);
|
|
|
|
uint16_t ddata = DATA;
|
|
|
|
while (ddata != 0)
|
|
{
|
|
stack_push(stack, stack_pop(originstack));
|
|
ddata--;
|
|
}
|
|
continue;
|
|
}
|
|
|
|
if (!strcmp(NAME, "exec")) // arg <const>
|
|
{
|
|
|
|
if (!strcmp(ARG, modname))
|
|
{
|
|
printf("MODULE %s: CAN'T EXECUTE ITSELF\n", ARG);
|
|
kms(stack, "CAN'T EXECUTE ITSELF");
|
|
}
|
|
|
|
FILE *file = fopen(ARG, "r");
|
|
if (!file)
|
|
{
|
|
printf("ERROR OPENING MODULE: %s\n", ARG);
|
|
kms(stack, "ERROR OPENING MODULE");
|
|
}
|
|
|
|
DATA = DATA ? DATA : STACK_SIZE;
|
|
|
|
Stack *tempstack = program_init(DATA);
|
|
|
|
parse_and_process(tempstack, file);
|
|
|
|
fclose(file);
|
|
|
|
execute(tempstack, stack, ARG);
|
|
|
|
for (int i = 0; i < tempstack->pointer; i++)
|
|
stack_push(stack, tempstack->memory[i]);
|
|
|
|
free(tempstack);
|
|
|
|
continue;
|
|
}
|
|
|
|
if (!strcmp(NAME, "call"))
|
|
{
|
|
|
|
unsigned short len = stack_pop(stack);
|
|
char *string = calloc(len, sizeof(char));
|
|
|
|
for (int i = len - 1; i != -1; i--)
|
|
string[i] = stack_pop(stack);
|
|
|
|
if (!strcmp(string, modname))
|
|
{
|
|
printf("MODULE %s: CAN'T EXECUTE ITSELF\n", string);
|
|
kms(stack, "CAN'T EXECUTE ITSELF");
|
|
}
|
|
|
|
FILE *file = fopen(string, "r");
|
|
if (!file)
|
|
{
|
|
printf("ERROR OPENING MODULE: %s\n", string);
|
|
kms(stack, "ERROR OPENING MODULE");
|
|
}
|
|
|
|
DATA = DATA ? DATA : STACK_SIZE;
|
|
|
|
Stack *tempstack = program_init(DATA);
|
|
|
|
parse_and_process(tempstack, file);
|
|
|
|
fclose(file);
|
|
|
|
execute(tempstack, stack, string);
|
|
|
|
for (int i = 0; i < tempstack->pointer; i++)
|
|
stack_push(stack, tempstack->memory[i]);
|
|
|
|
free(tempstack);
|
|
free(string);
|
|
|
|
continue;
|
|
}
|
|
|
|
// Native
|
|
|
|
#ifndef _WIN32
|
|
if (!strcmp(NAME, "native"))
|
|
{
|
|
char *modulename = malloc(strlen(ARG) + 6 * sizeof(char));
|
|
strcpy(modulename, "./");
|
|
strcat(modulename, ARG);
|
|
strcat(modulename, ".so");
|
|
void *module = dlopen(modulename, RTLD_NOW);
|
|
|
|
if (!module)
|
|
kms(stack, "UNABLE TO OPEN DYNAMIC LIBRARY");
|
|
|
|
void (*labashka)(unsigned short (*pop)(void), void (*push)(unsigned short), size_t (*len)(void),
|
|
size_t max_size);
|
|
labashka = (void (*)(unsigned short (*pop)(void), void (*push)(unsigned short), size_t (*len)(void),
|
|
size_t max_size))dlsym(module, "labashka");
|
|
|
|
stack_init_callback(stack);
|
|
|
|
labashka(stack_pop_callback, stack_push_callback, stack_len_callback, stack->stacksize);
|
|
|
|
dlclose(module);
|
|
|
|
free(modulename);
|
|
|
|
continue;
|
|
}
|
|
#else
|
|
if (!strcmp(NAME, "native"))
|
|
{
|
|
char *modulename = malloc(strlen(ARG) + 7 * sizeof(char));
|
|
strcpy(modulename, "./");
|
|
strcat(modulename, ARG);
|
|
strcat(modulename, ".dll");
|
|
void *module = LoadLibrary(TEXT(modulename));
|
|
|
|
if (!module)
|
|
kms(stack, "UNABLE TO OPEN DYNAMIC LIBRARY");
|
|
|
|
int (*labashka)(unsigned short (*pop)(void), void (*push)(unsigned short), size_t (*len)(void),
|
|
size_t max_size);
|
|
labashka = (int (*)(unsigned short (*pop)(void), void (*push)(unsigned short), size_t (*len)(void),
|
|
size_t max_size))GetProcAddress(module, "labashka");
|
|
|
|
stack_init_callback(stack);
|
|
|
|
labashka(stack_pop_callback, stack_push_callback, stack_len_callback, stack->stacksize);
|
|
|
|
FreeLibrary(module);
|
|
|
|
free(modulename);
|
|
|
|
continue;
|
|
}
|
|
#endif
|
|
|
|
// Self-modifying
|
|
|
|
if (!strcmp(NAME, "insert")) // arg <const>
|
|
{
|
|
if (ARG[0] == '#' || ARG[0] == '$')
|
|
kms(stack, "ATTEMPTED TO INSERT A HASH/STRING-INSTRUCTION");
|
|
|
|
if (ARG[0] == '\n')
|
|
kms(stack, "ATTEMPTED TO INSERT AN EMPTY CONSTANT");
|
|
|
|
if (ARG[0] == '@' && ARG[1] == ' ')
|
|
kms(stack, "ATTEMPTED TO INSERT A LABEL");
|
|
|
|
stack->program[stack->program_size++] = (Instruction){ARG, "\n", DATA};
|
|
|
|
continue;
|
|
}
|
|
|
|
// Special
|
|
|
|
if (!strcmp(NAME, "dump"))
|
|
{
|
|
for (int i = 0; i < stack->pointer; i++)
|
|
printf("%d: %d\n", i, stack->memory[i]);
|
|
continue;
|
|
}
|
|
|
|
if (!strcmp(NAME, "size"))
|
|
{
|
|
stack_push(stack, stack->pointer);
|
|
continue;
|
|
}
|
|
|
|
if (!strcmp(NAME, "maxsize"))
|
|
{
|
|
stack_push(stack, stack->stacksize);
|
|
continue;
|
|
}
|
|
|
|
if (!strcmp(NAME, "expr")) // arg <const>
|
|
{
|
|
expression(stack, ARG);
|
|
continue;
|
|
}
|
|
|
|
if (!strcmp(NAME, "field"))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (!strcmp(NAME, "quit"))
|
|
{
|
|
break;
|
|
}
|
|
|
|
if (!strcmp(NAME, "exit"))
|
|
{
|
|
exit(stack_pop(stack));
|
|
}
|
|
|
|
// Platform-specific
|
|
|
|
#ifdef __unix__
|
|
|
|
if (!strcmp(NAME, "_unix_random"))
|
|
{
|
|
srand(getc(fopen("/dev/urandom", "r")));
|
|
stack_push(stack, rand() % stack_pop(stack));
|
|
continue;
|
|
}
|
|
|
|
#endif
|
|
|
|
// LBR-SPECIFIC
|
|
printf("UNKNOWN INSTRUCTION: %s\n", NAME);
|
|
kms(stack, "UNKNOWN INSTRUCTION");
|
|
}
|
|
}
|
|
|
|
#undef NAME
|
|
#undef ARG
|
|
#undef DATA
|