Compiler/virtual machine interpreter: Difference between revisions
New post.
(New post.) |
|||
(12 intermediate revisions by 5 users not shown) | |||
Line 153:
; A simple example virtual machine:
<
int stack[data_size + 1000]
set stack[0..data_size - 1] to 0
Line 190:
elif op == PRTS: print the constant string referred to by stack[-1]; stack.pop()
elif op == PRTI: print stack[-1] as an integer; stack.pop()
elif op == HALT: break</
; Additional examples
Line 214:
This program outputs only the standard output, because I did not feel like implementing stream output to a named file. (Text I/O would have appended a newline or some such page-ender to the output.) One does not really need more than standard output for this task.
This Ada program is one of the faster implementations I have written, but you have to turn off runtime checks to get that speed.
<syntaxhighlight lang="ada">--
-- The Rosetta Code Virtual Machine, in Ada.
--
Line 408 ⟶ 413:
t : Unbounded_String;
i : Positive;
--
-- A little trick to get around mistaken highlighting on the
-- Rosetta Code site.
--
quote_string : constant String := """";
quote : constant Character := quote_string (1);
begin
t := To_Unbounded_String ("");
i := s'First;
while i <= s'Last and then s (i) /=
i := i + 1;
end loop;
if s'Last < i or else s (i) /=
raise bad_vm with "expected a '""'";
end if;
i := i + 1;
while i <= s'Last and then s (i) /=
if s (i) /= '\' then
Append (t, s (i));
Line 1,078 ⟶ 1,091:
end if;
else
-- Treat anything unrecognized as equivalent to
halt := True;
end if;
Line 1,140 ⟶ 1,153:
Set_Exit_Status (status);
end VM;</
Line 1,154 ⟶ 1,167:
count is: 8
count is: 9</pre>
=={{header|Aime}}==
<syntaxhighlight lang="text">integer n, pc, sp;
file f;
text s;
Line 1,246 ⟶ 1,255:
isk_greater(code, pc, pc);
}
}</
=={{header|ALGOL W}}==
<
% string literals %
string(256) array stringValue ( 0 :: 256 );
Line 1,555 ⟶ 1,564:
end while_not_halted
end
end.</
=={{header|ATS}}==
===Interpreter===
{{works with|ATS|Postiats 0.4.1}}
Line 1,566 ⟶ 1,578:
(Without the C optimizer, ATS code can run much, much more slowly. It is worth comparing the Mandelbrot example with and without the optimizer.)
<
Usage: vm [INPUTFILE [OUTPUTFILE]]
If INPUTFILE or OUTPUTFILE is "-" or missing, then standard input
Line 3,368 ⟶ 3,380:
}
(********************************************************************)</
{{out}}
Line 3,381 ⟶ 3,393:
count is: 8
count is: 9</pre>
===Compiler===
It seemed interesting to write translators from virtual machine code to other languages. Find at https://pastebin.com/pntTVTN3 a translator from Rosetta Code VM assembly language to ATS. The ATS program can be compiled to native code, which should run pretty fast if you use the C optimizer.
An ongoing project, to extend the translator to output languages other than ATS, is at https://sourceforge.net/p/chemoelectric/rosettacode-contributions/ci/default/tree/vmc.dats
=={{header|AWK}}==
Tested with gawk 4.1.1 and mawk 1.3.4.
<syntaxhighlight lang="awk">
function error(msg) {
printf("%s\n", msg)
Line 3,539 ⟶ 3,557:
run_vm(data_size)
}
</syntaxhighlight>
{{out|case=count}}
<b>
Line 3,557 ⟶ 3,575:
=={{header|C}}==
Tested with gcc 4.81 and later, compiles warning free with -Wall -Wextra
<
#include <stdlib.h>
#include <stdarg.h>
Line 3,823 ⟶ 3,841:
int data[1000 + data_size];
run_vm(object, data, data_size, string_pool);
}</
=={{header|C++}}==
This examples passes all tests, although for brevity of output only one test result is shown.
<syntaxhighlight lang="c++">
#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);
}
</syntaxhighlight>
{{ out }}
<pre>
1111111111111111111111122222222222222222222222222222222222222222222222222222222222222222222222222211111
1111111111111111111122222222222222222222222222222222222222222222222222222222222222222222222222222222211
1111111111111111112222222222222222222222222222222222222222222222222222222222222222222222222222222222222
1111111111111111222222222222222222233333333333333333333333222222222222222222222222222222222222222222222
1111111111111112222222222222333333333333333333333333333333333333222222222222222222222222222222222222222
1111111111111222222222233333333333333333333333344444456655544443333332222222222222222222222222222222222
1111111111112222222233333333333333333333333444444445567@@6665444444333333222222222222222222222222222222
11111111111222222333333333333333333333334444444445555679@@@@7654444443333333222222222222222222222222222
1111111112222223333333333333333333333444444444455556789@@@@98755544444433333332222222222222222222222222
1111111122223333333333333333333333344444444445556668@@@ @@@76555544444333333322222222222222222222222
1111111222233333333333333333333344444444455566667778@@ @987666555544433333333222222222222222222222
111111122333333333333333333333444444455556@@@@@99@@@@@@ @@@@@@877779@5443333333322222222222222222222
1111112233333333333333333334444455555556679@ @@@ @@@@@@ 8544333333333222222222222222222
1111122333333333333333334445555555556666789@@@ @86554433333333322222222222222222
1111123333333333333444456666555556666778@@ @ @@87655443333333332222222222222222
111123333333344444455568@887789@8777788@@@ @@@@65444333333332222222222222222
111133334444444455555668@@@@@@@@@@@@99@@@ @@765444333333333222222222222222
111133444444445555556778@@@ @@@@ @855444333333333222222222222222
11124444444455555668@99@@ @ @655444433333333322222222222222
11134555556666677789@@ @86655444433333333322222222222222
111 @@876555444433333333322222222222222
11134555556666677789@@ @86655444433333333322222222222222
11124444444455555668@99@@ @ @655444433333333322222222222222
111133444444445555556778@@@ @@@@ @855444333333333222222222222222
111133334444444455555668@@@@@@@@@@@@99@@@ @@765444333333333222222222222222
111123333333344444455568@887789@8777788@@@ @@@@65444333333332222222222222222
1111123333333333333444456666555556666778@@ @ @@87655443333333332222222222222222
1111122333333333333333334445555555556666789@@@ @86554433333333322222222222222222
1111112233333333333333333334444455555556679@ @@@ @@@@@@ 8544333333333222222222222222222
111111122333333333333333333333444444455556@@@@@99@@@@@@ @@@@@@877779@5443333333322222222222222222222
1111111222233333333333333333333344444444455566667778@@ @987666555544433333333222222222222222222222
1111111122223333333333333333333333344444444445556668@@@ @@@76555544444333333322222222222222222222222
1111111112222223333333333333333333333444444444455556789@@@@98755544444433333332222222222222222222222222
11111111111222222333333333333333333333334444444445555679@@@@7654444443333333222222222222222222222222222
1111111111112222222233333333333333333333333444444445567@@6665444444333333222222222222222222222222222222
1111111111111222222222233333333333333333333333344444456655544443333332222222222222222222222222222222222
1111111111111112222222222222333333333333333333333333333333333333222222222222222222222222222222222222222
1111111111111111222222222222222222233333333333333333333333222222222222222222222222222222222222222222222
1111111111111111112222222222222222222222222222222222222222222222222222222222222222222222222222222222222
1111111111111111111122222222222222222222222222222222222222222222222222222222222222222222222222222222211
</pre>
=={{header|COBOL}}==
Code by Steve Williams (with changes to work around code highlighting issues). Tested with GnuCOBOL 2.2.
<
identification division.
*> this code is dedicated to the public domain
Line 4,249 ⟶ 4,505:
end program emitword.
end program vminterpreter.</
{{out|case=Count}}
Line 4,273 ⟶ 4,529:
<
#|-*- mode:lisp -*-|#
#|
Line 4,997 ⟶ 5,253:
(uiop:quit 0)))
;;; vim: set ft=lisp lisp:</
Line 5,023 ⟶ 5,279:
<syntaxhighlight lang="d">//
// The Rosetta Code Virtual Machine in D.
//
Line 5,929 ⟶ 6,185:
return 0;
}</
Line 5,948 ⟶ 6,204:
=={{header|Forth}}==
Tested with Gforth 0.7.3
<
: PEEK BUF @ 0= IF KEY BUF ! THEN BUF @ ;
: GETC PEEK 0 BUF ! ;
Line 6,030 ⟶ 6,286:
: RUN BYTECODE @ A !
BEGIN C@A+ CELLS OPS + @ EXECUTE AGAIN ;
>HEADER >BYTECODE RUN</
=={{header|Fortran}}==
{{works with|gfortran|11.2.1}}
Fortran 2008/2018 code with some limited use of the C preprocessor. If you are on a platform with case-sensitive filenames, and call the source file vm.F90, then gfortran will know to use the C preprocessor.
<
use, intrinsic :: iso_fortran_env, only: int32
use, intrinsic :: iso_fortran_env, only: int64
Line 7,568 ⟶ 7,824:
end subroutine print_usage
end program vm</
{{out}}
Line 7,584 ⟶ 7,840:
=={{header|Go}}==
{{trans|Python}}
<
import (
Line 7,886 ⟶ 8,142:
scanner = bufio.NewScanner(codeGen)
runVM(loadCode())
}</
{{out}}
Line 7,904 ⟶ 8,160:
=={{header|Icon}}==
{{trans|ObjectIcon}}
<
#
# The Rosetta Code virtual machine in Icon. Migrated from the
Line 8,278 ⟶ 8,534:
write(&errout, "Bad opcode.")
exit(1)
end</
{{out}}
Line 8,294 ⟶ 8,550:
=={{header|J}}==
Implementation:
<
fetch store push add sub mul div mod lt gt le ge
eq ne and or neg not jmp jz prtc prts prti halt
Line 8,369 ⟶ 8,625:
pc=: pc+k
end.
}}</
Task example:
<
count = 1;
while (count < 10) {
Line 8,390 ⟶ 8,646:
count is: 8
count is: 9
</syntaxhighlight>
=={{header|Java}}==
This examples passes all tests, although for brevity of output only one test result is shown.
<syntaxhighlight lang="java">
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) {}
}
</syntaxhighlight>
{{ out }}
<pre>
1111111111111111111111122222222222222222222222222222222222222222222222222222222222222222222222222211111
1111111111111111111122222222222222222222222222222222222222222222222222222222222222222222222222222222211
1111111111111111112222222222222222222222222222222222222222222222222222222222222222222222222222222222222
1111111111111111222222222222222222233333333333333333333333222222222222222222222222222222222222222222222
1111111111111112222222222222333333333333333333333333333333333333222222222222222222222222222222222222222
1111111111111222222222233333333333333333333333344444456655544443333332222222222222222222222222222222222
1111111111112222222233333333333333333333333444444445567@@6665444444333333222222222222222222222222222222
11111111111222222333333333333333333333334444444445555679@@@@7654444443333333222222222222222222222222222
1111111112222223333333333333333333333444444444455556789@@@@98755544444433333332222222222222222222222222
1111111122223333333333333333333333344444444445556668@@@ @@@76555544444333333322222222222222222222222
1111111222233333333333333333333344444444455566667778@@ @987666555544433333333222222222222222222222
111111122333333333333333333333444444455556@@@@@99@@@@@@ @@@@@@877779@5443333333322222222222222222222
1111112233333333333333333334444455555556679@ @@@ @@@@@@ 8544333333333222222222222222222
1111122333333333333333334445555555556666789@@@ @86554433333333322222222222222222
1111123333333333333444456666555556666778@@ @ @@87655443333333332222222222222222
111123333333344444455568@887789@8777788@@@ @@@@65444333333332222222222222222
111133334444444455555668@@@@@@@@@@@@99@@@ @@765444333333333222222222222222
111133444444445555556778@@@ @@@@ @855444333333333222222222222222
11124444444455555668@99@@ @ @655444433333333322222222222222
11134555556666677789@@ @86655444433333333322222222222222
111 @@876555444433333333322222222222222
11134555556666677789@@ @86655444433333333322222222222222
11124444444455555668@99@@ @ @655444433333333322222222222222
111133444444445555556778@@@ @@@@ @855444333333333222222222222222
111133334444444455555668@@@@@@@@@@@@99@@@ @@765444333333333222222222222222
111123333333344444455568@887789@8777788@@@ @@@@65444333333332222222222222222
1111123333333333333444456666555556666778@@ @ @@87655443333333332222222222222222
1111122333333333333333334445555555556666789@@@ @86554433333333322222222222222222
1111112233333333333333333334444455555556679@ @@@ @@@@@@ 8544333333333222222222222222222
111111122333333333333333333333444444455556@@@@@99@@@@@@ @@@@@@877779@5443333333322222222222222222222
1111111222233333333333333333333344444444455566667778@@ @987666555544433333333222222222222222222222
1111111122223333333333333333333333344444444445556668@@@ @@@76555544444333333322222222222222222222222
1111111112222223333333333333333333333444444444455556789@@@@98755544444433333332222222222222222222222222
11111111111222222333333333333333333333334444444445555679@@@@7654444443333333222222222222222222222222222
1111111111112222222233333333333333333333333444444445567@@6665444444333333222222222222222222222222222222
1111111111111222222222233333333333333333333333344444456655544443333332222222222222222222222222222222222
1111111111111112222222222222333333333333333333333333333333333333222222222222222222222222222222222222222
1111111111111111222222222222222222233333333333333333333333222222222222222222222222222222222222222222222
1111111111111111112222222222222222222222222222222222222222222222222222222222222222222222222222222222222
1111111111111111111122222222222222222222222222222222222222222222222222222222222222222222222222222222211
</pre>
=={{header|Julia}}==
<
code::Vector{UInt8}
stack::Vector{Int32}
Line 8,496 ⟶ 8,973:
const vm = assemble(iob)
runvm(vm)
</
count is: 1
count is: 2
Line 8,510 ⟶ 8,987:
=={{header|M2000 Interpreter}}==
===Using Select Case===
<syntaxhighlight lang="m2000 interpreter">
Module Virtual_Machine_Interpreter (a$){
\\ function to extract string, replacing escape codes.
Line 8,683 ⟶ 9,160:
65 halt
}
</syntaxhighlight>
===Using Lambda functions===
Line 8,689 ⟶ 9,166:
A call local to function pass the current scope to function, so it's like a call to subroutine, but faster.
<syntaxhighlight lang="m2000 interpreter">
Module Virtual_Machine_Interpreter (a$){
\\ function to extract string, replacing escape codes.
Line 8,838 ⟶ 9,315:
65 halt
}
</syntaxhighlight>
=={{header|Mercury}}==
Line 8,851 ⟶ 9,328:
<
%%%
%%% The Rosetta Code Virtual Machine, in Mercury.
Line 9,742 ⟶ 10,219:
%%% prolog-indent-width: 2
%%% end:
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%</
Line 9,764 ⟶ 10,241:
=={{header|Nim}}==
<
type
Line 10,084 ⟶ 10,561:
vm.load(code)
vm.run()</
All tests passed.
=={{header|ObjectIcon}}==
<
#
# The Rosetta Code virtual machine in Object Icon.
Line 10,496 ⟶ 10,973:
exit(1)
end
end</
{{out}}
Line 10,512 ⟶ 10,989:
=={{header|Perl}}==
Tested with perl v5.26.1
<
# http://www.rosettacode.org/wiki/Compiler/virtual_machine_interpreter
Line 10,561 ⟶ 11,038:
}
$ops[vec($binary, $pc++, 8)][1]->() while 1; # run it</
Passes all tests.
=={{header|Phix}}==
Reusing cgen.e from the [[Compiler/code_generator#Phix|Code Generator task]]
<!--<
<span style="color: #000080;font-style:italic;">--
-- demo\rosetta\Compiler\vm.exw
Line 10,606 ⟶ 11,083:
<span style="color: #000080;font-style:italic;">--main(command_line())</span>
<span style="color: #000000;">main</span><span style="color: #0000FF;">({</span><span style="color: #000000;">0</span><span style="color: #0000FF;">,</span><span style="color: #000000;">0</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"count.c"</span><span style="color: #0000FF;">})</span>
<!--</
{{out}}
<pre>
Line 10,624 ⟶ 11,101:
<
%%%
%%% The Rosetta Code Virtual Machine, for GNU Prolog.
Line 10,990 ⟶ 11,467:
%%% prolog-indent-width: 2
%%% end:
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%</
Line 11,007 ⟶ 11,484:
=={{header|Python}}==
Tested with Python 2.7 and 3.x
<
import sys, struct
Line 11,174 ⟶ 11,651:
data_size = load_code()
run_vm(data_size)</
=={{header|Racket}}==
Line 11,185 ⟶ 11,662:
<
;;;
;;; The Rosetta Code Virtual Machine, in Typed Racket.
Line 11,932 ⟶ 12,409:
(close-output-port outf))
(exit 0)))))</
Line 11,957 ⟶ 12,434:
{{trans|Perl}}
<syntaxhighlight lang="raku"
Datasize: 3 Strings: 2
"count is: "
Line 12,034 ⟶ 12,511:
$pc += $w;
%ops{%n2op{ $opcode }}();
}</
{{out}}
<pre>count is: 1
Line 12,045 ⟶ 12,522:
count is: 8
count is: 9</pre>
=={{header|RATFOR}}==
{{works with|ratfor77|[https://sourceforge.net/p/chemoelectric/ratfor77/ public domain 1.0]}}
{{works with|gfortran|11.3.0}}
{{works with|f2c|20100827}}
<syntaxhighlight lang="ratfor">######################################################################
#
# The Rosetta Code code virtual machine in Ratfor 77.
#
# The implementation assumes your FORTRAN compiler supports 1-byte
# INTEGER*1 and 4-byte INTEGER*4. Integer storage will be
# native-endian, achieved via EQUIVALENCE. (GNU Fortran and f2c both
# should work.)
#
#
# How to deal with FORTRAN 77 input is a problem. I use formatted
# input, treating each line as an array of type CHARACTER--regrettably
# of no more than some predetermined, finite length. It is a very
# simple method and presents no significant difficulties, aside from
# the restriction on line length of the input.
#
#
# On a POSIX platform, the program can be compiled with f2c and run
# somewhat as follows:
#
# ratfor77 vm-in-ratfor.r > vm-in-ratfor.f
# f2c -C -Nc40 vm-in-ratfor.f
# cc vm-in-ratfor.c -lf2c
# ./a.out < compiler-tests/primes.vm
#
# With gfortran, a little differently:
#
# ratfor77 vm-in-ratfor.r > vm-in-ratfor.f
# gfortran -fcheck=all -std=legacy vm-in-ratfor.f
# ./a.out < compiler-tests/primes.vm
#
#
# I/O is strictly from default input and to default output, which, on
# POSIX systems, usually correspond respectively to standard input and
# standard output. (I did not wish to have to deal with unit numbers;
# these are now standardized in ISO_FORTRAN_ENV, but that is not
# available in FORTRAN 77.)
#
#---------------------------------------------------------------------
# Some parameters you may wish to modify.
define(LINESZ, 256) # Size of an input line.
define(OUTLSZ, 1024) # Size of an output line.
define(STRNSZ, 4096) # Size of the string pool.
define(STCKSZ, 4096) # Size of stacks.
define(MAXVAR, 256) # Maximum number of variables.
define(MAXSTR, 256) # Maximum number of strings.
define(CODESZ, 16384) # Maximum size of a compiled program.
define(STRSZ, 2) # Size of an entry in the VM strings array.
define(STRI, 1) # Index of the string within strngs.
define(STRN, 2) # Length of the string.
#---------------------------------------------------------------------
define(NEWLIN, 10) # The Unix newline character (ASCII LF).
define(DQUOTE, 34) # The double quote character.
define(BACKSL, 92) # The backslash character.
#---------------------------------------------------------------------
define(OPHALT, 1)
define(OPADD, 2)
define(OPSUB, 3)
define(OPMUL, 4)
define(OPDIV, 5)
define(OPMOD, 6)
define(OPLT, 7)
define(OPGT, 8)
define(OPLE, 9)
define(OPGE, 10)
define(OPEQ, 11)
define(OPNE, 12)
define(OPAND, 13)
define(OPOR, 14)
define(OPNEG, 15)
define(OPNOT, 16)
define(OPPRTC, 17)
define(OPPRTI, 18)
define(OPPRTS, 19)
define(OPFTCH, 20)
define(OPSTOR, 21)
define(OPPUSH, 22)
define(OPJMP, 23)
define(OPJZ, 24)
#---------------------------------------------------------------------
function issp (c)
# Is a character a space character?
implicit none
character c
logical issp
integer ic
ic = ichar (c)
issp = (ic == 32 || (9 <= ic && ic <= 13))
end
function isalph (c)
# Is c character code for a letter?
implicit none
integer c
logical isalph
#
# The following is correct for ASCII and Unicode, but not for
# EBCDIC.
#
isalph = (ichar ('a') <= c && c <= ichar ('z')) _
|| (ichar ('A') <= c && c <= ichar ('Z'))
end
function isdgt (c)
# Is c character code for a digit?
implicit none
integer c
logical isdgt
isdgt = (ichar ('0') <= c && c <= ichar ('9'))
end
function skipsp (str, i, imax)
# Skip past spaces in a string.
implicit none
character str(*)
integer i
integer imax
integer skipsp
logical issp
logical done
skipsp = i
done = .false.
while (!done)
{
if (imax <= skipsp)
done = .true.
else if (!issp (str(skipsp)))
done = .true.
else
skipsp = skipsp + 1
}
end
function skipns (str, i, imax)
# Skip past non-spaces in a string.
implicit none
character str(*)
integer i
integer imax
integer skipns
logical issp
logical done
skipns = i
done = .false.
while (!done)
{
if (imax <= skipns)
done = .true.
else if (issp (str(skipns)))
done = .true.
else
skipns = skipns + 1
}
end
function trimrt (str, n)
# Find the length of a string, if one ignores trailing spaces.
implicit none
character str(*)
integer n
integer trimrt
logical issp
logical done
trimrt = n
done = .false.
while (!done)
{
if (trimrt == 0)
done = .true.
else if (!issp (str(trimrt)))
done = .true.
else
trimrt = trimrt - 1
}
end
function skipal (str, i, imax)
# Skip past alphabetic characters in a string.
implicit none
character str(*)
integer i
integer imax
integer skipal
logical isalph
logical done
skipal = i
done = .false.
while (!done)
{
if (imax <= skipal)
done = .true.
else if (!isalph (ichar (str(skipal))))
done = .true.
else
skipal = skipal + 1
}
end
function skipdg (str, i, imax)
# Skip past digits in a string.
implicit none
character str(*)
integer i
integer imax
integer skipdg
logical isdgt
logical done
skipdg = i
done = .false.
while (!done)
{
if (imax <= skipdg)
done = .true.
else if (!isdgt (ichar (str(skipdg))))
done = .true.
else
skipdg = skipdg + 1
}
end
function skipnd (str, i, imax)
# Skip past nondigits in a string.
implicit none
character str(*)
integer i
integer imax
integer skipnd
logical isdgt
logical done
skipnd = i
done = .false.
while (!done)
{
if (imax <= skipnd)
done = .true.
else if (isdgt (ichar (str(skipnd))))
done = .true.
else
skipnd = skipnd + 1
}
end
function skipd1 (str, i, imax)
# Skip past digits and '-' in a string.
implicit none
character str(*)
integer i
integer imax
integer skipd1
logical isdgt
logical done
skipd1 = i
done = .false.
while (!done)
{
if (imax <= skipd1)
done = .true.
else if (!isdgt (ichar (str(skipd1))) && str(skipd1) != '-')
done = .true.
else
skipd1 = skipd1 + 1
}
end
function skipn1 (str, i, imax)
# Skip past nondigits in a string, except '-'.
implicit none
character str(*)
integer i
integer imax
integer skipn1
logical isdgt
logical done
skipn1 = i
done = .false.
while (!done)
{
if (imax <= skipn1)
done = .true.
else if (isdgt (ichar (str(skipn1))) || str(skipn1) == '-')
done = .true.
else
skipn1 = skipn1 + 1
}
end
function tolowr (c)
implicit none
character c
character tolowr
integer ic
# The following is correct for ASCII, and will work with Unicode
# code points, but is incorrect for EBCDIC.
ic = ichar (c)
if (ichar ('A') <= ic && ic <= ichar ('Z'))
ic = ic - ichar('A') + ichar('a')
tolowr = char (ic)
end
#---------------------------------------------------------------------
subroutine addstq (strngs, istrng, src, i0, n0, i, n)
# Add a quoted string to the string pool.
implicit none
character strngs(STRNSZ) # String pool.
integer istrng # String pool's next slot.
character src(*) # Source string.
integer i0, n0 # Index and length in source string.
integer i, n # Index and length in string pool.
integer j
logical done
1000 format ('attempt to treat an unquoted string as a quoted string')
if (src(i0) != char (DQUOTE) || src(i0 + n0 - 1) != char (DQUOTE))
{
write (*, 1000)
stop
}
i = istrng
n = 0
j = i0 + 1
done = .false.
while (j != i0 + n0 - 1)
if (i == STRNSZ)
{
write (*, '(''string pool exhausted'')')
stop
}
else if (src(j) == char (BACKSL))
{
if (j == i0 + n0 - 1)
{
write (*, '(''incorrectly formed quoted string'')')
stop
}
if (src(j + 1) == 'n')
strngs(istrng) = char (NEWLIN)
else if (src(j + 1) == char (BACKSL))
strngs(istrng) = src(j + 1)
else
{
write (*, '(''unrecognized escape sequence'')')
stop
}
istrng = istrng + 1
n = n + 1
j = j + 2
}
else
{
strngs(istrng) = src(j)
istrng = istrng + 1
n = n + 1
j = j + 1
}
end
#---------------------------------------------------------------------
subroutine push (stack, sp, i)
implicit none
integer stack(STCKSZ)
integer sp # Stack pointer.
integer i # Value to push.
if (sp == STCKSZ)
{
write (*, '(''stack overflow in push'')')
stop
}
stack(sp) = i
sp = sp + 1
end
function pop (stack, sp)
implicit none
integer stack(STCKSZ)
integer sp # Stack pointer.
integer pop
if (sp == 1)
{
write (*, '(''stack underflow in pop'')')
stop
}
sp = sp - 1
pop = stack(sp)
end
function nstack (sp)
implicit none
integer sp # Stack pointer.
integer nstack
nstack = sp - 1 # Current cardinality of the stack.
end
#---------------------------------------------------------------------
subroutine flushl (outbuf, noutbf)
# Flush a line from the output buffer.
implicit none
character outbuf(OUTLSZ) # Output line buffer.
integer noutbf # Number of characters in outbuf.
character*20 fmt
integer i
if (noutbf == 0)
write (*, '()')
else
{
write (fmt, 1000) noutbf
1000 format ('(', I10, 'A)')
write (*, fmt) (outbuf(i), i = 1, noutbf)
noutbf = 0
}
end
subroutine wrtchr (outbuf, noutbf, ch)
# Write a character to output.
implicit none
character outbuf(OUTLSZ) # Output line buffer.
integer noutbf # Number of characters in outbuf.
character ch # The character to output.
# This routine silently truncates anything that goes past the buffer
# boundary.
if (ch == char (NEWLIN))
call flushl (outbuf, noutbf)
else if (noutbf < OUTLSZ)
{
noutbf = noutbf + 1
outbuf(noutbf) = ch
}
end
subroutine wrtstr (outbuf, noutbf, str, i, n)
# Write a substring to output.
implicit none
character outbuf(OUTLSZ) # Output line buffer.
integer noutbf # Number of characters in outbuf.
character str(*) # The string from which to output.
integer i, n # Index and length of the substring.
integer j
for (j = 0; j < n; j = j + 1)
call wrtchr (outbuf, noutbf, str(i + j))
end
subroutine wrtint (outbuf, noutbf, ival, colcnt)
# Write an integer to output.
implicit none
character outbuf(OUTLSZ) # Output line buffer.
integer noutbf # Number of characters in outbuf.
integer ival # The non-negative integer to print.
integer colcnt # Column count, or zero for free format.
integer skipsp
character*40 buf
integer i, j
write (buf, '(I40)') ival
i = skipsp (buf, 1, 41)
if (0 < colcnt)
for (j = 1; j < colcnt - (40 - i); j = j + 1)
call wrtchr (outbuf, noutbf, ' ')
while (i <= 40)
{
call wrtchr (outbuf, noutbf, buf(i:i))
i = i + 1
}
end
#---------------------------------------------------------------------
function strnat (str, i, n)
# Convert a string to a non-negative integer.
implicit none
character str(*)
integer i, n
integer strnat
integer j
strnat = 0
for (j = 0; j < n; j = j + 1)
strnat = (10 * strnat) + (ichar (str(i + j)) - ichar ('0'))
end
function strint (str, i, n)
# Convert a string to an integer
implicit none
character str(*)
integer i, n
integer strint
integer strnat
if (str(i) == '-')
strint = -strnat (str, i + 1, n - 1)
else
strint = strnat (str, i, n)
end
#---------------------------------------------------------------------
subroutine put1 (code, i, opcode)
# Store a 1-byte operation.
implicit none
integer*1 code(0 : CODESZ - 1) # Byte code.
integer i # Address to put the code at.
integer*1 opcode
if (CODESZ - i < 1)
{
write (*, '(''address beyond the size of memory'')')
stop
}
code(i) = opcode
end
subroutine put5 (code, i, opcode, ival)
# Store a 5-byte operation.
implicit none
integer*1 code(0 : CODESZ - 1) # Byte code.
integer i # Address to put the code at.
integer*1 opcode #
integer ival # Immediate integer value.
integer*4 ival32
integer*1 ival8(4)
equivalence (ival32, ival8)
if (CODESZ - i < 5)
{
write (*, '(''address beyond the size of memory'')')
stop
}
code(i) = opcode
# Native-endian storage.
ival32 = ival
code(i + 1) = ival8(1)
code(i + 2) = ival8(2)
code(i + 3) = ival8(3)
code(i + 4) = ival8(4)
end
function getimm (code, i)
# Get an immediate value from the code, at address i.
implicit none
integer*1 code(0 : CODESZ - 1) # Byte code.
integer i # Address at which the integer resides.
integer getimm # Immediate integer value.
integer*4 ival32
integer*1 ival8(4)
equivalence (ival32, ival8)
if (i < 0 || CODESZ <= i + 3)
{
write (*, '(''code address out of range'')')
stop
}
# Native-endian storage.
ival8(1) = code(i)
ival8(2) = code(i + 1)
ival8(3) = code(i + 2)
ival8(4) = code(i + 3)
getimm = ival32
end
#---------------------------------------------------------------------
subroutine rdhead (datsiz, strsiz)
# Read the header line.
implicit none
integer datsiz
integer strsiz
integer skipnd
integer skipdg
integer strnat
character line(LINESZ)
character*20 fmt
integer i1, j1, i2, j2
# Read a line of text as an array of characters.
write (fmt, '(''('', I10, ''A)'')') LINESZ
read (*, fmt) line
i1 = skipnd (line, 1, LINESZ + 1)
j1 = skipdg (line, i1, LINESZ + 1)
i2 = skipnd (line, j1, LINESZ + 1)
j2 = skipdg (line, i2, LINESZ + 1)
if (i1 == j1 || i2 == j2)
{
write (*, '(''bad header line'')')
stop
}
datsiz = strnat (line, i1, j1 - i1)
strsiz = strnat (line, i2, j2 - i2)
end
subroutine rdstrs (strs, strsiz, strngs, istrng)
implicit none
integer strs(1:STRSZ, 0 : MAXSTR - 1)
integer strsiz
character strngs(STRNSZ) # String pool.
integer istrng # String pool's next slot.
integer trimrt
integer skipsp
character line(LINESZ)
character*20 fmt
integer j
integer i, n
integer i0, n0
# Read lines of text as an array of characters.
write (fmt, '(''('', I10, ''A)'')') LINESZ
for (j = 0; j < strsiz; j = j + 1)
{
read (*, fmt) line
n0 = trimrt (line, LINESZ)
i0 = skipsp (line, 1, n0 + 1)
if (i0 == n0 + 1)
{
write (*, '(''blank line where a string should be'')')
stop
}
call addstq (strngs, istrng, line, i0, n0 - i0 + 1, i, n)
strs(STRI, j) = i
strs(STRN, j) = n
}
end
function stropc (str, i, n)
# Convert substring to an opcode.
implicit none
character str(*)
integer i, n
integer*1 stropc
stropc = -1
if (n == 2)
{
if (str(i) == 'l')
{
if (str(i + 1) == 't')
stropc = OPLT
else if (str(i + 1) == 'e')
stropc = OPLE
}
else if (str(i) == 'g')
{
if (str(i + 1) == 't')
stropc = OPGT
else if (str(i + 1) == 'e')
stropc = OPGE
}
else if (str(i) == 'e' && str(i + 1) == 'q')
stropc = OPEQ
else if (str(i) == 'n' && str(i + 1) == 'e')
stropc = OPNE
else if (str(i) == 'o' && str(i + 1) == 'r')
stropc = OPOR
else if (str(i) == 'j' && str(i + 1) == 'z')
stropc = OPJZ
}
else if (n == 3)
{
if (str(i) == 'a')
{
if (str(i + 1) == 'd' && str(i + 2) == 'd')
stropc = OPADD
else if (str(i + 1) == 'n' && str(i + 2) == 'd')
stropc = OPAND
}
else if (str(i) == 'm')
{
if (str(i + 1) == 'o' && str(i + 2) == 'd')
stropc = OPMOD
else if (str(i + 1) == 'u' && str(i + 2) == 'l')
stropc = OPMUL
}
else if (str(i) == 'n')
{
if (str(i + 1) == 'e' && str(i + 2) == 'g')
stropc = OPNEG
else if (str(i + 1) == 'o' && str(i + 2) == 't')
stropc = OPNOT
}
else if (str(i) == 's' && str(i + 1) == 'u' _
&& str(i + 2) == 'b')
stropc = OPSUB
else if (str(i) == 'd' && str(i + 1) == 'i' _
&& str(i + 2) == 'v')
stropc = OPDIV
else if (str(i) == 'j' && str(i + 1) == 'm' _
&& str(i + 2) == 'p')
stropc = OPJMP
}
else if (n == 4)
{
if (str(i) == 'p')
{
if (str(i + 1) == 'r' && str(i + 2) == 't')
{
if (str(i + 3) == 'c')
stropc = OPPRTC
else if (str(i + 3) == 'i')
stropc = OPPRTI
else if (str(i + 3) == 's')
stropc = OPPRTS
}
if (str(i + 1) == 'u' && str(i + 2) == 's' _
&& str(i + 3) == 'h')
stropc = OPPUSH
}
else if (str(i) == 'h' && str(i + 1) == 'a' _
&& str(i + 2) == 'l' && str(i + 3) == 't')
stropc = OPHALT
}
else if (n == 5)
{
if (str(i) == 'f' && str(i + 1) == 'e' && str(i + 2) == 't' _
&& str(i + 3) == 'c' && str(i + 4) == 'h')
stropc = OPFTCH
if (str(i) == 's' && str(i + 1) == 't' && str(i + 2) == 'o' _
&& str(i + 3) == 'r' && str(i + 4) == 'e')
stropc = OPSTOR
}
if (stropc == -1)
{
write (*, '(''unrecognized opcode name'')')
stop
}
end
subroutine rdops (code)
# Read the opcodes and their immediate values.
implicit none
integer*1 code(0 : CODESZ - 1) # The byte code.
integer trimrt
integer skipsp
integer skipal
integer skipdg
integer skipd1
integer skipn1
integer strnat
integer strint
integer*1 stropc
character tolowr
character line(LINESZ)
character*20 fmt
integer stat
integer n
integer j
integer iaddr, jaddr # Address index and size.
integer iopnm, jopnm # Opcode name index and size.
integer iarg, jarg
integer addr
integer arg
integer*1 opcode
# Read lines of text as an array of characters.
write (fmt, '(''('', I10, ''A)'')') LINESZ
read (*, fmt, iostat = stat) line
while (stat == 0)
{
n = trimrt (line, LINESZ)
for (j = 1; j <= n; j = j + 1)
line(j) = tolowr (line(j))
iaddr = skipsp (line, 1, n + 1)
jaddr = skipdg (line, iaddr, n + 1)
addr = strnat (line, iaddr, jaddr - iaddr)
iopnm = skipsp (line, jaddr, n + 1)
jopnm = skipal (line, iopnm, n + 1)
opcode = stropc (line, iopnm, jopnm - iopnm)
if (opcode == OPPUSH || opcode == OPFTCH || opcode == OPSTOR _
|| opcode == OPJMP || opcode == OPJZ)
{
iarg = skipn1 (line, jopnm, n + 1)
jarg = skipd1 (line, iarg, n + 1)
arg = strint (line, iarg, jarg - iarg)
call put5 (code, addr, opcode, arg)
}
else
call put1 (code, addr, opcode)
read (*, fmt, iostat = stat) line
}
end
subroutine rdcode (strs, strngs, istrng, code)
# Read and parse the "assembly" code.
implicit none
integer strs(1:STRSZ, 0 : MAXSTR - 1)
character strngs(STRNSZ) # String pool.
integer istrng # String pool's next slot.
integer*1 code(0 : CODESZ - 1) # The byte code.
integer datsiz
integer strsiz
call rdhead (datsiz, strsiz)
if (MAXVAR < datsiz)
{
write (*, '(''too many variables'')')
stop
}
if (MAXSTR < strsiz)
{
write (*, '(''too many strings'')')
stop
}
call rdstrs (strs, strsiz, strngs, istrng)
call rdops (code)
end
#---------------------------------------------------------------------
subroutine stkbin (sp)
implicit none
integer sp
if (sp < 3)
{
write (*, '(''stack underflow in binary operation'')')
stop
}
end
subroutine stkun (sp)
implicit none
integer sp
if (sp < 2)
{
write (*, '(''stack underflow in unary operation'')')
stop
}
end
function logl2i (b)
implicit none
logical b
integer logl2i
if (b)
logl2i = 1
else
logl2i = 0
end
subroutine rncode (strs, strngs, code, outbuf, noutbf)
# Run the code.
implicit none
integer strs(1:STRSZ, 0 : MAXSTR - 1)
character strngs(STRNSZ) # String pool.
integer*1 code(0 : CODESZ - 1) # The byte code.
character outbuf(OUTLSZ) # Output line buffer.
integer noutbf # Number of characters in outbuf.
integer logl2i
integer getimm
integer pop
integer stack(STCKSZ)
integer data(0 : MAXVAR - 1)
integer sp # Stack pointer.
integer pc # Program counter.
integer ip # Instruction pointer.
equivalence (pc, ip) # LOL, use either name. :)
integer i, n
integer*1 opcode
logical done
sp = 1
ip = 0
done = .false.
while (!done)
{
if (ip < 0 || CODESZ <= ip)
{
write (*, '(''code address out of range'')')
stop
}
opcode = code(ip)
ip = ip + 1
if (opcode == OPADD)
{
call stkbin (sp)
sp = sp - 1
stack(sp - 1) = stack (sp - 1) + stack(sp)
}
else if (opcode == OPSUB)
{
call stkbin (sp)
sp = sp - 1
stack(sp - 1) = stack (sp - 1) - stack(sp)
}
else if (opcode == OPMUL)
{
call stkbin (sp)
sp = sp - 1
stack(sp - 1) = stack (sp - 1) * stack(sp)
}
else if (opcode == OPDIV)
{
call stkbin (sp)
sp = sp - 1
stack(sp - 1) = stack (sp - 1) / stack(sp)
}
else if (opcode == OPMOD)
{
call stkbin (sp)
sp = sp - 1
stack(sp - 1) = mod (stack (sp - 1), stack(sp))
}
else if (opcode == OPLT)
{
call stkbin (sp)
sp = sp - 1
stack(sp - 1) = logl2i (stack (sp - 1) < stack(sp))
}
else if (opcode == OPGT)
{
call stkbin (sp)
sp = sp - 1
stack(sp - 1) = logl2i (stack (sp - 1) > stack(sp))
}
else if (opcode == OPLE)
{
call stkbin (sp)
sp = sp - 1
stack(sp - 1) = logl2i (stack (sp - 1) <= stack(sp))
}
else if (opcode == OPGE)
{
call stkbin (sp)
sp = sp - 1
stack(sp - 1) = logl2i (stack (sp - 1) >= stack(sp))
}
else if (opcode == OPEQ)
{
call stkbin (sp)
sp = sp - 1
stack(sp - 1) = logl2i (stack (sp - 1) == stack(sp))
}
else if (opcode == OPNE)
{
call stkbin (sp)
sp = sp - 1
stack(sp - 1) = logl2i (stack (sp - 1) != stack(sp))
}
else if (opcode == OPAND)
{
call stkbin (sp)
sp = sp - 1
stack(sp - 1) = _
logl2i (stack (sp - 1) != 0 && stack(sp) != 0)
}
else if (opcode == OPOR)
{
call stkbin (sp)
sp = sp - 1
stack(sp - 1) = _
logl2i (stack (sp - 1) != 0 || stack(sp) != 0)
}
else if (opcode == OPNEG)
{
call stkun (sp)
stack(sp - 1) = -stack(sp - 1)
}
else if (opcode == OPNOT)
{
call stkun (sp)
stack(sp - 1) = logl2i (stack(sp - 1) == 0)
}
else if (opcode == OPPRTC)
{
call wrtchr (outbuf, noutbf, char (pop (stack, sp)))
}
else if (opcode == OPPRTI)
{
call wrtint (outbuf, noutbf, pop (stack, sp), 0)
}
else if (opcode == OPPRTS)
{
i = pop (stack, sp)
if (i < 0 || MAXSTR <= i)
{
write (*, '(''string address out of range'')')
stop
}
n = strs(STRN, i)
i = strs(STRI, i)
call wrtstr (outbuf, noutbf, strngs, i, n)
}
else if (opcode == OPFTCH)
{
i = getimm (code, ip)
ip = ip + 4
if (i < 0 || MAXVAR <= i)
{
write (*, '(''data address out of range'')')
stop
}
call push (stack, sp, data(i))
}
else if (opcode == OPSTOR)
{
i = getimm (code, ip)
ip = ip + 4
if (i < 0 || MAXVAR <= i)
{
write (*, '(''data address out of range'')')
stop
}
data(i) = pop (stack, sp)
}
else if (opcode == OPPUSH)
{
call push (stack, sp, getimm (code, ip))
ip = ip + 4
}
else if (opcode == OPJMP)
{
ip = ip + getimm (code, ip)
}
else if (opcode == OPJZ)
{
if (pop (stack, sp) == 0)
ip = ip + getimm (code, ip)
else
ip = ip + 4
}
else
{
# Halt on OPHALT or any unrecognized code.
done = .true.
}
}
end
#---------------------------------------------------------------------
program vm
implicit none
integer strs(1:STRSZ, 0 : MAXSTR - 1)
character strngs(STRNSZ) # String pool.
integer istrng # String pool's next slot.
integer*1 code(0 : CODESZ - 1) # The byte code.
character outbuf(OUTLSZ) # Output line buffer.
integer noutbf # Number of characters in outbuf.
integer j
istrng = 1
noutbf = 0
for (j = 0; j < CODESZ; j = j + 1)
code(j) = OPHALT
call rdcode (strs, strngs, istrng, code)
call rncode (strs, strngs, code, outbuf, noutbf)
if (noutbf != 0)
call flushl (outbuf, noutbf)
end
######################################################################</syntaxhighlight>
{{out}}
<pre>$ ratfor77 vm-in-ratfor.r > vm-in-ratfor.f && gfortran -O2 -fcheck=all -std=legacy vm-in-ratfor.f && ./a.out < compiler-tests/primes.vm
3 is prime
5 is prime
7 is prime
11 is prime
13 is prime
17 is prime
19 is prime
23 is prime
29 is prime
31 is prime
37 is prime
41 is prime
43 is prime
47 is prime
53 is prime
59 is prime
61 is prime
67 is prime
71 is prime
73 is prime
79 is prime
83 is prime
89 is prime
97 is prime
101 is prime
Total primes found: 26</pre>
=={{header|Scala}}==
Line 12,051 ⟶ 13,798:
The following code implements a virtual machine for the output of the [http://rosettacode.org/wiki/Compiler/code_generator#Scala code generator].
<
package xyz.hyperreal.rosettacodeCompiler
Line 12,268 ⟶ 14,015:
}
</syntaxhighlight>
The above code depends on the function <tt>unescape()</tt> to perform string escape sequence translation. That function is defined in the following separate source file.
<
package xyz.hyperreal
Line 12,300 ⟶ 14,047:
}
</syntaxhighlight>
=={{header|Scheme}}==
Line 12,308 ⟶ 14,055:
All of the "Compiler/Sample programs" are correctly interpreted.
<
(import (scheme base)
(scheme char)
Line 12,471 ⟶ 14,218:
(run-program data strings code))
(display "Error: pass a .asm filename\n"))
</syntaxhighlight>
=={{header|Wren}}==
Line 12,479 ⟶ 14,226:
{{libheader|Wren-fmt}}
{{libheader|Wren-ioutil}}
<
import "./crypto" for Bytes
import "./fmt" for Conv
import "./ioutil" for FileUtil
var codes = [
Line 12,745 ⟶ 14,492:
lines = FileUtil.readLines("codegen.txt")
lineCount = lines.count
runVM.call(loadCode.call())</
{{out}}
Line 12,761 ⟶ 14,508:
=={{header|Zig}}==
<
const std = @import("std");
Line 13,084 ⟶ 14,831:
}
}
</syntaxhighlight>
=={{header|zkl}}==
{{trans|Python}}
File rvm.zkl:
<
const WORD_SIZE=4;
const{ var _n=-1; var[proxy]N=fcn{ _n+=1 } } // enumerator
Line 13,150 ⟶ 14,897:
code.del(0,sz+2);
}
run_vm(code,1000);</
The binary code file code.bin:
{{out}}
|