Compiler/virtual machine interpreter: Difference between revisions

Content added Content deleted
Line 3,444: Line 3,444:
count is: 9
count is: 9
</pre>
</pre>

=={{header|Icon}}==
<lang icon># -*- Icon -*-
#
# The Rosetta Code virtual machine in Icon. Migrated from the
# ObjectIcon.
#
# See https://rosettacode.org/wiki/Compiler/virtual_machine_interpreter
#

record VirtualMachine(code, global_data, strings, stack, pc)

global opcode_names
global opcode_values
global op_halt
global op_add
global op_sub
global op_mul
global op_div
global op_mod
global op_lt
global op_gt
global op_le
global op_ge
global op_eq
global op_ne
global op_and
global op_or
global op_neg
global op_not
global op_prtc
global op_prti
global op_prts
global op_fetch
global op_store
global op_push
global op_jmp
global op_jz

global whitespace_chars

procedure main(args)
local f_inp, f_out
local vm

whitespace_chars := ' \t\n\r\f\v'
initialize_opcodes()

if 3 <= *args then {
write("Usage: ", &progname, " [INPUT_FILE [OUTPUT_FILE]]")
exit(1)
}

if 1 <= *args then {
f_inp := open(args[1], "r") | {
write(&errout, "Failed to open ", args[1], " for reading.")
exit(1)
}
} else {
f_inp := &input
}

if 2 <= *args then {
f_out := open(args[2], "w") | {
write(&errout, "Failed to open ", args[2], " for writing.")
exit(1)
}
} else {
f_out := &output
}

vm := VirtualMachine()
read_assembly_code(f_inp, vm)
run_vm(f_out, vm)
end

procedure initialize_opcodes()
local i

opcode_names :=
["halt", "add", "sub", "mul", "div",
"mod", "lt", "gt", "le", "ge",
"eq", "ne", "and", "or", "neg",
"not", "prtc", "prti", "prts", "fetch",
"store", "push", "jmp", "jz"]

opcode_values := table()
every i := 1 to *opcode_names do
opcode_values[opcode_names[i]] := char(i)

op_halt := opcode_values["halt"]
op_add := opcode_values["add"]
op_sub := opcode_values["sub"]
op_mul := opcode_values["mul"]
op_div := opcode_values["div"]
op_mod := opcode_values["mod"]
op_lt := opcode_values["lt"]
op_gt := opcode_values["gt"]
op_le := opcode_values["le"]
op_ge := opcode_values["ge"]
op_eq := opcode_values["eq"]
op_ne := opcode_values["ne"]
op_and := opcode_values["and"]
op_or := opcode_values["or"]
op_neg := opcode_values["neg"]
op_not := opcode_values["not"]
op_prtc := opcode_values["prtc"]
op_prti := opcode_values["prti"]
op_prts := opcode_values["prts"]
op_fetch := opcode_values["fetch"]
op_store := opcode_values["store"]
op_push := opcode_values["push"]
op_jmp := opcode_values["jmp"]
op_jz := opcode_values["jz"]
end

procedure int2bytes (n)
local bytes

# The VM is little-endian.

bytes := "****"
bytes[1] := char (iand(n, 16rFF))
bytes[2] := char(iand(ishift(n, -8), 16rFF))
bytes[3] := char(iand(ishift(n, -16), 16rFF))
bytes[4] := char(iand(ishift(n, -24), 16rFF))
return bytes
end

procedure bytes2int(bytes, i)
local n0, n1, n2, n3, n

# The VM is little-endian.

n0 := ord(bytes[i])
n1 := ishift(ord(bytes[i + 1]), 8)
n2 := ishift(ord(bytes[i + 2]), 16)
n3 := ishift(ord(bytes[i + 3]), 24)
n := ior (n0, ior (n1, ior (n2, n3)))

# Do not forget to extend the sign bit.
return (if n3 <= 16r7F then n else ior(n, icom(16rFFFFFFFF)))
end

procedure read_assembly_code(f, vm)
local data_size, number_of_strings
local line, ch
local i
local address
local opcode

# Read the header line.
line := read(f) | bad_vm()
line ? {
tab(many(whitespace_chars))
tab(match("Datasize")) | bad_vm()
tab(many(whitespace_chars))
tab(any(':')) | bad_vm()
tab(many(whitespace_chars))
data_size :=
integer(tab(many(&digits))) | bad_vm()
tab(many(whitespace_chars))
tab(match("Strings")) | bad_vm()
tab(many(whitespace_chars))
tab(any(':')) | bad_vm()
tab(many(whitespace_chars))
number_of_strings :=
integer(tab(many(&digits))) | bad_vm()
}

# Read the strings.
vm.strings := list(number_of_strings)
every i := 1 to number_of_strings do {
vm.strings[i] := ""
line := read(f) | bad_vm()
line ? {
tab(many(whitespace_chars))
tab(any('"')) | bad_vm()
while ch := tab(any(~'"')) do {
if ch == '\\' then {
ch := tab(any('n\\')) | bad_vm()
vm.strings[i] ||:=
(if (ch == "n") then "\n" else "\\")
} else {
vm.strings[i] ||:= ch
}
}
}
}

# Read the code.
vm.code := ""
while line := read(f) do {
line ? {
tab(many(whitespace_chars))
address := integer(tab(many(&digits))) | bad_vm()
tab(many(whitespace_chars))
opcode := tab(many(~whitespace_chars)) | bad_vm()
vm.code ||:= opcode_values[opcode]
case opcode of {
"push": {
tab(many(whitespace_chars))
vm.code ||:=
int2bytes(integer(tab(many(&digits)))) |
int2bytes(integer(tab(any('-')) ||
tab(many(&digits)))) |
bad_vm()
}
"fetch" | "store": {
tab(many(whitespace_chars))
tab(any('[')) | bad_vm()
tab(many(whitespace_chars))
vm.code ||:=
int2bytes(integer(tab(many(&digits)))) |
bad_vm()
tab(many(whitespace_chars))
tab(any(']')) | bad_vm()
}
"jmp" | "jz": {
tab(many(whitespace_chars))
tab(any('(')) | bad_vm()
tab(many(whitespace_chars))
vm.code ||:=
int2bytes(integer(tab(many(&digits)))) |
int2bytes(integer(tab(any('-')) ||
tab(many(&digits)))) |
bad_vm()
tab(many(whitespace_chars))
tab(any(')')) | bad_vm()
tab(many(whitespace_chars))
tab(many(&digits)) | bad_vm()
}
default: {
# Do nothing
}
}
}
}

# Create a global data area.
vm.global_data := list(data_size, &null)

initialize_vm(vm)
end

procedure run_vm(f_out, vm)
initialize_vm(vm)
continue_vm(f_out, vm)
end

procedure continue_vm(f_out, vm)
while vm.code[vm.pc] ~== op_halt do
step_vm(f_out, vm)
end

procedure step_vm(f_out, vm)
local opcode

opcode := vm.code[vm.pc]
vm.pc +:= 1
case opcode of {
op_add: binop(vm, "+")
op_sub: binop(vm, "-")
op_mul: binop(vm, "*")
op_div: binop(vm, "/")
op_mod: binop(vm, "%")
op_lt: comparison(vm, "<")
op_gt: comparison(vm, ">")
op_le: comparison(vm, "<=")
op_ge: comparison(vm, ">=")
op_eq: comparison(vm, "=")
op_ne: comparison(vm, "~=")
op_and: logical_and(vm)
op_or: logical_or(vm)
op_neg: negate(vm)
op_not: logical_not(vm)
op_prtc: printc(f_out, vm)
op_prti: printi(f_out, vm)
op_prts: prints(f_out, vm)
op_fetch: fetch_global(vm)
op_store: store_global(vm)
op_push: push_argument(vm)
op_jmp: jump(vm)
op_jz: jump_if_zero(vm)
default: bad_opcode()
}
end

procedure negate(vm)
vm.stack[1] := -vm.stack[1]
end

procedure binop(vm, func)
vm.stack[2] := func(vm.stack[2], vm.stack[1])
pop(vm.stack)
end

procedure comparison(vm, func)
vm.stack[2] := (if func(vm.stack[2], vm.stack[1]) then 1 else 0)
pop(vm.stack)
end

procedure logical_and(vm)
vm.stack[2] :=
(if vm.stack[2] ~= 0 & vm.stack[1] ~= 0 then 1 else 0)
pop(vm.stack)
end

procedure logical_or(vm)
vm.stack[2] :=
(if vm.stack[2] ~= 0 | vm.stack[1] ~= 0 then 1 else 0)
pop(vm.stack)
end

procedure logical_not(vm)
vm.stack[1] := (if vm.stack[1] ~= 0 then 0 else 1)
end

procedure printc(f_out, vm)
/f_out := &output
writes(f_out, char(pop(vm.stack)))
end

procedure printi(f_out, vm)
/f_out := &output
writes(f_out, pop(vm.stack))
end

procedure prints(f_out, vm)
/f_out := &output
writes(f_out, vm.strings[pop(vm.stack) + 1])
end

procedure fetch_global(vm)
push(vm.stack, vm.global_data[get_argument(vm) + 1])
vm.pc +:= 4
end

procedure store_global(vm)
vm.global_data[get_argument(vm) + 1] := pop(vm.stack)
vm.pc +:= 4
end

procedure push_argument(vm)
push(vm.stack, get_argument(vm))
vm.pc +:= 4
end

procedure jump(vm)
vm.pc +:= get_argument(vm)
end

procedure jump_if_zero(vm)
if pop(vm.stack) = 0 then
vm.pc +:= get_argument(vm)
else
vm.pc +:= 4
end

procedure get_argument(vm)
return bytes2int(vm.code, vm.pc)
end

procedure initialize_vm(vm)
# The program counter starts at 1, for convenient indexing into
# the code[] array. Icon indexing starts at 1 (for a *very* good
# reason, but that’s a topic for another day).
vm.pc := 1
vm.stack := []
end

procedure bad_vm()
write(&errout, "Bad VM.")
exit(1)
end

procedure bad_opcode()
write(&errout, "Bad opcode.")
exit(1)
end</lang>


=={{header|J}}==
=={{header|J}}==