Data update
This commit is contained in:
parent
29a5eea0d4
commit
5c1bb7bfa9
2011 changed files with 35081 additions and 3229 deletions
|
|
@ -1,9 +1,7 @@
|
|||
Virtual Machine Interpreter
|
||||
{{task heading|Virtual Machine Interpreter}}
|
||||
|
||||
A virtual machine implements a computer in software.
|
||||
|
||||
{{task heading}}
|
||||
|
||||
Write a virtual machine interpreter. This interpreter should be able to run virtual
|
||||
assembly language programs created via the [[Compiler/code_generator|task]]. This is a
|
||||
byte-coded, 32-bit word stack based virtual machine.
|
||||
|
|
@ -11,7 +9,7 @@ byte-coded, 32-bit word stack based virtual machine.
|
|||
The program should read input from a file and/or stdin, and write output to a file and/or
|
||||
stdout.
|
||||
|
||||
Input format:
|
||||
{{task heading|Input format}}
|
||||
|
||||
Given the following program:
|
||||
|
||||
|
|
@ -67,7 +65,7 @@ Next comes the actual virtual assembly code. The first number is the code addre
|
|||
instruction. After that is the instruction mnemonic, followed by optional operands,
|
||||
depending on the instruction.
|
||||
|
||||
Registers:
|
||||
{{task heading|Registers}}
|
||||
|
||||
sp:
|
||||
the stack pointer - points to the next top of stack. The stack is a 32-bit integer
|
||||
|
|
@ -81,7 +79,7 @@ Data:
|
|||
data
|
||||
string pool
|
||||
|
||||
Instructions:
|
||||
{{task heading|Instructions}}
|
||||
|
||||
Each instruction is one byte. The following instructions also have a 32-bit integer
|
||||
operand:
|
||||
|
|
|
|||
|
|
@ -0,0 +1,190 @@
|
|||
#include <cstdint>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
std::vector<std::string> split_string(const std::string& text, const char& delimiter) {
|
||||
std::vector<std::string> lines;
|
||||
std::istringstream stream(text);
|
||||
std::string line;
|
||||
while ( std::getline(stream, line, delimiter) ) {
|
||||
if ( ! line.empty() ) {
|
||||
lines.emplace_back(line);
|
||||
}
|
||||
}
|
||||
return lines;
|
||||
}
|
||||
|
||||
std::string parseString(const std::string& text) {
|
||||
std::string result = "";
|
||||
uint32_t i = 0;
|
||||
while ( i < text.length() ) {
|
||||
if ( text[i] == '\\' && i + 1 < text.length() ) {
|
||||
if ( text[i + 1] == 'n' ) {
|
||||
result += "\n";
|
||||
i++;
|
||||
} else if ( text[i + 1] == '\\') {
|
||||
result += "\\";
|
||||
i++;
|
||||
}
|
||||
} else {
|
||||
result += text[i];
|
||||
}
|
||||
i++;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void add_to_codes(const uint32_t& number, std::vector<uint8_t>& codes) {
|
||||
for ( uint32_t i = 0; i < 32; i += 8 ) {
|
||||
codes.emplace_back((number >> i) & 0xff);
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t operand(const uint32_t& index, const std::vector<uint8_t>& codes) {
|
||||
uint32_t result = 0;
|
||||
for ( uint32_t i = index + 3; i >= index; --i ) {
|
||||
result = ( result << 8 ) + codes[i];
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
struct VirtualMachineInfo {
|
||||
uint32_t data_size;
|
||||
std::vector<std::string> vm_strings;
|
||||
std::vector<uint8_t> codes;
|
||||
};
|
||||
|
||||
enum class Op_code {
|
||||
HALT, ADD, SUB, MUL, DIV, MOD, LT, GT, LE, GE, EQ, NE, AND, OR, NEG, NOT,
|
||||
PRTC, PRTI, PRTS, FETCH, STORE, PUSH, JMP, JZ
|
||||
};
|
||||
|
||||
std::unordered_map<std::string, Op_code> string_to_enum = {
|
||||
{ "halt", Op_code::HALT }, { "add", Op_code::ADD }, { "sub", Op_code::SUB },
|
||||
{ "mul", Op_code::MUL }, { "div", Op_code::DIV }, { "mod", Op_code::MOD },
|
||||
{ "lt", Op_code::LT }, { "gt", Op_code::GT }, { "le", Op_code::LE },
|
||||
{ "ge", Op_code::GE }, { "eq", Op_code::EQ }, { "ne", Op_code::NE },
|
||||
{ "and", Op_code::AND }, { "or", Op_code::OR }, { "neg", Op_code::NEG },
|
||||
{ "not", Op_code::NOT }, { "prtc", Op_code::PRTC }, { "prti", Op_code::PRTI },
|
||||
{ "prts", Op_code::PRTS }, { "fetch", Op_code::FETCH }, { "store", Op_code::STORE },
|
||||
{ "push", Op_code::PUSH }, { "jmp", Op_code::JMP }, { "jz", Op_code::JZ }
|
||||
};
|
||||
|
||||
VirtualMachineInfo load_code(const std::string& file_path) {
|
||||
std::ifstream stream(file_path);
|
||||
std::vector<std::string> lines;
|
||||
std::string line;
|
||||
|
||||
while ( std::getline(stream, line) ) {
|
||||
lines.emplace_back(line);
|
||||
}
|
||||
|
||||
line = lines.front();
|
||||
if ( line.substr(0, 3) == "lex" ) {
|
||||
lines.erase(lines.begin());
|
||||
line = lines.front();
|
||||
}
|
||||
|
||||
std::vector<std::string> sections = split_string(line, ' ');
|
||||
const uint32_t data_size = std::stoi(sections[1]);
|
||||
const uint32_t string_count = std::stoi(sections[3]);
|
||||
|
||||
std::vector<std::string> vm_strings = { };
|
||||
for ( uint32_t i = 1; i <= string_count; ++i ) {
|
||||
std::string content = lines[i].substr(1, lines[i].length() - 2);
|
||||
vm_strings.emplace_back(parseString(content));
|
||||
}
|
||||
|
||||
uint32_t offset = 0;
|
||||
std::vector<uint8_t> codes = { };
|
||||
for ( uint32_t i = string_count + 1; i < lines.size(); ++i ) {
|
||||
sections = split_string(lines[i], ' ');
|
||||
offset = std::stoi(sections[0]);
|
||||
Op_code op_code = string_to_enum[sections[1]];
|
||||
codes.emplace_back(static_cast<uint8_t>(op_code));
|
||||
|
||||
switch ( op_code ) {
|
||||
case Op_code::FETCH :
|
||||
case Op_code::STORE :
|
||||
add_to_codes(std::stoi(sections[2]
|
||||
.substr(1, sections[2].length() - 2)), codes); break;
|
||||
case Op_code::PUSH : add_to_codes(std::stoi(sections[2]), codes); break;
|
||||
case Op_code::JMP :
|
||||
case Op_code::JZ : add_to_codes(std::stoi(sections[3]) - offset - 1, codes); break;
|
||||
default : break;
|
||||
}
|
||||
}
|
||||
|
||||
return VirtualMachineInfo(data_size, vm_strings, codes);
|
||||
}
|
||||
|
||||
void runVirtualMachine(
|
||||
const uint32_t& data_size, const std::vector<std::string>& vm_strings, const std::vector<uint8_t>& codes) {
|
||||
const uint32_t word_size = 4;
|
||||
std::vector<int32_t> stack(data_size, 0);
|
||||
uint32_t index = 0;
|
||||
Op_code op_code;
|
||||
|
||||
while ( op_code != Op_code::HALT ) {
|
||||
op_code = static_cast<Op_code>(codes[index]);
|
||||
index++;
|
||||
|
||||
switch ( op_code ) {
|
||||
case Op_code::HALT : break;
|
||||
case Op_code::ADD : stack[stack.size() - 2] += stack.back(); stack.pop_back(); break;
|
||||
case Op_code::SUB : stack[stack.size() - 2] -= stack.back(); stack.pop_back(); break;
|
||||
case Op_code::MUL : stack[stack.size() - 2] *= stack.back(); stack.pop_back(); break;
|
||||
case Op_code::DIV : stack[stack.size() - 2] /= stack.back(); stack.pop_back(); break;
|
||||
case Op_code::MOD : stack[stack.size() - 2] %= stack.back(); stack.pop_back(); break;
|
||||
case Op_code::LT : { stack[stack.size() - 2] = ( stack[stack.size() - 2] < stack.back() ) ? 1 : 0;
|
||||
stack.pop_back(); break;
|
||||
}
|
||||
case Op_code::GT : { stack[stack.size() - 2] = ( stack[stack.size() - 2] > stack.back() ) ? 1 : 0;
|
||||
stack.pop_back(); break;
|
||||
}
|
||||
case Op_code::LE : { stack[stack.size() - 2] = ( stack[stack.size() - 2] <= stack.back() ) ? 1 : 0;
|
||||
stack.pop_back(); break;
|
||||
}
|
||||
case Op_code::GE : { stack[stack.size() - 2] = ( stack[stack.size() - 2] >= stack.back() ) ? 1 : 0;
|
||||
stack.pop_back(); break;
|
||||
}
|
||||
case Op_code::EQ : { stack[stack.size() - 2] = ( stack[stack.size() - 2] == stack.back() ) ? 1 : 0;
|
||||
stack.pop_back(); break;
|
||||
}
|
||||
case Op_code::NE : { stack[stack.size() - 2] = ( stack[stack.size() - 2] != stack.back() ) ? 1 : 0;
|
||||
stack.pop_back(); break;
|
||||
}
|
||||
case Op_code::AND : { uint32_t value = ( stack[stack.size() - 2] != 0 && stack.back() != 0 ) ? 1 : 0;
|
||||
stack[stack.size() - 2] = value; stack.pop_back(); break;
|
||||
}
|
||||
case Op_code::OR : { uint32_t value = ( stack[stack.size() - 2] != 0 || stack.back() != 0 ) ? 1 : 0;
|
||||
stack[stack.size() - 2] = value; stack.pop_back(); break;
|
||||
}
|
||||
case Op_code::NEG : stack.back() = -stack.back(); break;
|
||||
case Op_code::NOT : stack.back() = ( stack.back() == 0 ) ? 1 : 0; break;
|
||||
case Op_code::PRTC : std::cout << static_cast<char>(stack.back()); stack.pop_back(); break;
|
||||
case Op_code::PRTI : std::cout << stack.back(); stack.pop_back(); break;
|
||||
case Op_code::PRTS : std::cout << vm_strings[stack.back()]; stack.pop_back(); break;
|
||||
case Op_code::FETCH : stack.emplace_back(stack[operand(index, codes)]); index += word_size; break;
|
||||
case Op_code::STORE : { stack[operand(index, codes)] = stack.back(); index += word_size;
|
||||
stack.pop_back(); break;
|
||||
}
|
||||
case Op_code::PUSH : stack.emplace_back(operand(index, codes)); index += word_size; break;
|
||||
case Op_code::JMP : index += operand(index, codes); break;
|
||||
case Op_code::JZ : { index += ( stack.back() == 0 ) ? operand(index, codes) : word_size;
|
||||
stack.pop_back(); break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int main() {
|
||||
VirtualMachineInfo info = load_code("Compiler Test Cases/AsciiMandlebrot.txt");
|
||||
runVirtualMachine(info.data_size, info.vm_strings, info.codes);
|
||||
}
|
||||
|
|
@ -0,0 +1,173 @@
|
|||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Stack;
|
||||
|
||||
public final class CompilerVirtualMachineInterpreter {
|
||||
|
||||
public static void main(String[] args) throws IOException {
|
||||
Path filePath = Path.of("Compiler Test Cases/AsciiMandlebrot.txt");
|
||||
VirtualMachineInfo info = loadCode(filePath);
|
||||
runVirtualMachine(info.dataSize, info.vmStrings, info.codes());
|
||||
}
|
||||
|
||||
private static void runVirtualMachine(int dataSize, List<String> vmStrings, List<Byte> codes) {
|
||||
final int wordSize = 4;
|
||||
Stack<Integer> stack = new Stack<Integer>();
|
||||
for ( int i = 0; i < dataSize; i++ ) {
|
||||
stack.push(0);
|
||||
}
|
||||
|
||||
int index = 0;
|
||||
OpCode opCode = null;
|
||||
|
||||
while ( opCode != OpCode.HALT ) {
|
||||
opCode = OpCode.havingCode(codes.get(index));
|
||||
index += 1;
|
||||
|
||||
switch ( opCode ) {
|
||||
case HALT -> { }
|
||||
case ADD -> stack.set(stack.size() - 2, stack.get(stack.size() - 2) + stack.pop());
|
||||
case SUB -> stack.set(stack.size() - 2, stack.get(stack.size() - 2) - stack.pop());
|
||||
case MUL -> stack.set(stack.size() - 2, stack.get(stack.size() - 2) * stack.pop());
|
||||
case DIV -> stack.set(stack.size() - 2, stack.get(stack.size() - 2) / stack.pop());
|
||||
case MOD -> stack.set(stack.size() - 2, Math.floorMod(stack.get(stack.size() - 2), stack.pop()));
|
||||
case LT -> stack.set(stack.size() - 2, ( stack.get(stack.size() - 2) < stack.pop() ) ? 1 : 0);
|
||||
case GT -> stack.set(stack.size() - 2, ( stack.get(stack.size() - 2) > stack.pop() ) ? 1 : 0);
|
||||
case LE -> stack.set(stack.size() - 2, ( stack.get(stack.size() - 2) <= stack.pop() ) ? 1 : 0);
|
||||
case GE -> stack.set(stack.size() - 2, ( stack.get(stack.size() - 2) >= stack.pop() ) ? 1 : 0);
|
||||
case EQ -> stack.set(stack.size() - 2, ( stack.get(stack.size() - 2) == stack.pop() ) ? 1 : 0);
|
||||
case NE -> stack.set(stack.size() - 2, ( stack.get(stack.size() - 2) != stack.pop() ) ? 1 : 0);
|
||||
case AND -> { final int value = ( stack.get(stack.size() - 2) != 0 && stack.pop() != 0 ) ? 1 : 0;
|
||||
stack.set(stack.size() - 1, value);
|
||||
}
|
||||
case OR -> { final int value = ( stack.get(stack.size() - 2) != 0 || stack.pop() != 0 ) ? 1 : 0;
|
||||
stack.set(stack.size() - 1, value);
|
||||
}
|
||||
case NEG -> stack.set(stack.size() - 1, -stack.peek());
|
||||
case NOT -> stack.set(stack.size() - 1, ( stack.peek() == 0 ) ? 1 : 0);
|
||||
case PRTC -> System.out.print((char) stack.pop().intValue());
|
||||
case PRTI -> System.out.print(stack.pop());
|
||||
case PRTS -> System.out.print(vmStrings.get(stack.pop()));
|
||||
case FETCH -> { stack.push(stack.get(operand(index, codes))); index += wordSize; }
|
||||
case STORE -> { stack.set(operand(index, codes), stack.pop()); index += wordSize; }
|
||||
case PUSH -> { stack.push(operand(index, codes)); index += wordSize; }
|
||||
case JMP -> index += operand(index, codes);
|
||||
case JZ -> index += ( stack.pop() == 0 ) ? operand(index, codes) : wordSize;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static VirtualMachineInfo loadCode(Path filePath) throws IOException {
|
||||
List<String> lines = Files.readAllLines(filePath, StandardCharsets.UTF_8);
|
||||
|
||||
String line = lines.getFirst();
|
||||
if ( line.startsWith("lex") ) {
|
||||
lines.removeFirst();
|
||||
line = lines.getFirst();
|
||||
}
|
||||
|
||||
String[] sections = line.trim().split(" ");
|
||||
final int dataSize = Integer.parseInt(sections[1]);
|
||||
final int stringCount = Integer.parseInt(sections[3]);
|
||||
|
||||
List<String> VMstrings = new ArrayList<String>();
|
||||
for ( int i = 1; i <= stringCount; i++ ) {
|
||||
String content = lines.get(i).substring(1, lines.get(i).length() - 1);
|
||||
VMstrings.addLast(parseString(content));
|
||||
}
|
||||
|
||||
int offset = 0;
|
||||
List<Byte> codes = new ArrayList<Byte>();
|
||||
for ( int i = stringCount + 1; i < lines.size(); i++ ) {
|
||||
sections = lines.get(i).trim().split("\\s+");
|
||||
offset = Integer.parseInt(sections[0]);
|
||||
OpCode opCode = OpCode.valueOf(sections[1].toUpperCase());
|
||||
codes.addLast(opCode.byteCode());
|
||||
|
||||
switch ( opCode ) {
|
||||
case FETCH, STORE -> addToCodes(Integer.parseInt(sections[2]
|
||||
.substring(1, sections[2].length() - 1)), codes);
|
||||
case PUSH -> addToCodes(Integer.parseInt(sections[2]), codes);
|
||||
case JMP, JZ -> addToCodes(Integer.parseInt(sections[3]) - offset - 1, codes);
|
||||
default -> { }
|
||||
}
|
||||
}
|
||||
|
||||
return new VirtualMachineInfo(dataSize, VMstrings, codes);
|
||||
}
|
||||
|
||||
private static int operand(int index, List<Byte> codes) {
|
||||
byteBuffer.clear();
|
||||
for ( int i = index; i < index + 4; i++ ) {
|
||||
byteBuffer.put(codes.get(i));
|
||||
}
|
||||
byteBuffer.flip();
|
||||
|
||||
return byteBuffer.getInt();
|
||||
}
|
||||
|
||||
private static void addToCodes(int number, List<Byte> codes) {
|
||||
byteBuffer.clear();
|
||||
byteBuffer.putInt(number);
|
||||
byteBuffer.flip();
|
||||
for ( byte bb : byteBuffer.array() ) {
|
||||
codes.addLast(bb);
|
||||
}
|
||||
}
|
||||
|
||||
private static String parseString(String text) {
|
||||
StringBuilder result = new StringBuilder();
|
||||
int i = 0;
|
||||
while ( i < text.length() ) {
|
||||
if ( text.charAt(i) == '\\' && i + 1 < text.length() ) {
|
||||
if ( text.charAt(i + 1) == 'n' ) {
|
||||
result.append("\n");
|
||||
i += 1;
|
||||
} else if ( text.charAt(i + 1) == '\\') {
|
||||
result.append("\\");
|
||||
i += 1;
|
||||
}
|
||||
} else {
|
||||
result.append(text.charAt(i));
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
private static ByteBuffer byteBuffer = ByteBuffer.allocate(4).order(ByteOrder.LITTLE_ENDIAN);
|
||||
|
||||
private static enum OpCode {
|
||||
|
||||
HALT(0), ADD(1), SUB(2), MUL(3), DIV(4), MOD(5), LT(6), GT(7), LE(8), GE(9), EQ(10), NE(11),
|
||||
AND(12), OR(13), NEG(14), NOT(15),
|
||||
PRTC(16), PRTI(17), PRTS(18), FETCH(19), STORE(20), PUSH(21), JMP(22), JZ(23);
|
||||
|
||||
public byte byteCode() {
|
||||
return (byte) byteCode;
|
||||
}
|
||||
|
||||
public static OpCode havingCode(Byte byteCode) {
|
||||
return op_codes[(int) byteCode];
|
||||
}
|
||||
|
||||
private OpCode(int aByteCode) {
|
||||
byteCode = aByteCode;
|
||||
}
|
||||
|
||||
private int byteCode;
|
||||
|
||||
private static OpCode[] op_codes = values();
|
||||
|
||||
}
|
||||
|
||||
private static record VirtualMachineInfo(int dataSize, List<String> vmStrings, List<Byte> codes) {}
|
||||
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue