Skip to content

Instantly share code, notes, and snippets.

@themisir
Last active April 16, 2023 14:28
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save themisir/6947e44e1adf394e673a607fd1b5ebaa to your computer and use it in GitHub Desktop.
Save themisir/6947e44e1adf394e673a607fd1b5ebaa to your computer and use it in GitHub Desktop.
A Turing complete virtual machine implementation with AWK..
#!/usr/bin/awk -f
BEGIN {
split("", stack);
split("", locals);
split("", lookback);
split("", labels);
skip_ops = 0;
exec_from = -1;
debug_mode = ENVIRON["VM_DEBUG"];
}
function push(v) {
stack[length(stack)] = v;
}
function pop() {
i = length(stack) - 1;
v = stack[i];
delete stack[i];
return v;
}
function peek() {
return stack[length(stack) - 1];
}
function jump(to) {
if (substr(to, 1, 1) == ":") {
label = substr(to, 2);
if (label in labels) {
exec_from = labels[label];
} else {
printf "error: label '%s' is not exists\n", label
}
} else {
skip_ops = int(operand);
}
}
function print_memory() {
print "DEBUG: ---- STACK ----";
for (i = 0; i < length(stack); i++) {
printf "\t%i. %s\n", i, stack[i];
}
print "DEBUG: ---- LOCALS ----";
for (name in locals) {
printf "\t%s = %s\n", name, locals[name];
}
print ""
}
function exec(operation, operand) {
if (debug_mode) {
printf "DEBUG: executing %s(%s)\n", operation, operand;
}
# MEMORY
if (operation == "push") {
push(operand);
} else if (operation == "pop") {
pop();
} else if (operation == "setloc") {
locals[operand] = pop();
} else if (operation == "getloc") {
push(locals[operand]);
}
# CONTROL
else if (operation == "exit") {
exit;
} else if (operation == "jmp") {
jump(operand);
} else if (operation == "jmpif") {
if (pop()) {
jump(operand);
}
}
# ALU
else if (operation == "add") {
push(pop() + pop());
} else if (operation == "sub") {
push(pop() - pop());
} else if (operation == "mul") {
push(pop() * pop());
} else if (operation == "div") {
push(pop() / pop());
} else if (operation == "mod") {
push(pop() % pop());
} else if (operation == "cmpgt") {
push(pop() > pop());
} else if (operation == "cmplt") {
push(pop() < pop());
} else if (operation == "cmpgte") {
push(pop() >= pop());
} else if (operation == "cmplte") {
push(pop() <= pop());
} else if (operation == "cmpeq") {
push(pop() == pop());
} else if (operation == "cmpneq") {
push(pop() != pop());
} else if (operation == "not") {
push(!pop());
}
# DEBUG
else if (operation == "print") {
print peek();
} else if (operation == "debug") {
print_memory();
} else if (operation == "") {
return;
} else {
printf "error: invalid operation '%s'\n", operation;
}
if (debug_mode) {
print_memory();
}
}
{
if (NF == 0) {
next;
}
if (substr($1, 1, 1) == "#") {
next;
}
if (substr($1, 1, 1) == ":") {
label = substr($1, 2);
labels[label] = length(lookback);
next;
}
if (length(labels) > 0) {
lookback[length(lookback)] = $0;
}
if (skip_ops > 0) {
skip_ops--;
next;
}
exec($1, $2);
if (exec_from >= 0) {
while (exec_from < length(lookback)) {
split(lookback[exec_from], operands);
exec_from++;
if (skip_ops > 0) {
skip_ops--;
continue;
}
exec(operands[1], operands[2]);
}
exec_from = -1;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment