After solving problem 88, problem 89 is not a particularly difficult problem, it involves doing stuff with Roman Numerals. As I expected the problem to not be too difficult, I looked for a new language that would not be super easy to use to solve this problem. So, I stumbled across and decided to use PASM, the Parrot ASseMbly language for the Parrot Virtual Machine (
http://www.parrot.org/). Now, though it is an assembly language, it is an assembly language for a virtual machine, so it a CISC language, and it notably has a number of language one does not find in most assembly languages, most notably a number of string manipulation functions (used below are length, substr, ord. Also readline and a simple print command are nice to have). So, the string manipulation niceties were nice, especially as problem 89 involves reading in and processing a large text file. The main difficulties I had working in PASM was 1) figuring out how to declare constants...even though this ended up being unnecessary, .const and .constant are both documented as existing but don't, so this took a while. And 2) Attempting to use an array. One disadvantage of PASM running on a VM is that, unlike a normal assembly language, memory management is hard, and I could not figure out how to allocate/use an array (I could use a stack, but random access in a stack is...not fun). This difficulty cost a lot of time while writing the program, and also resulted in the GET_CHAR_COUNT method not being too nice.
I think a number of the issues described above were due to my choice to use PASM, while Parrot apparently intends for most users to use (and of course by "use" I mean have their compilers output) PIR (Parrot Intermediate Representation) code which is then converted to PASM. And because this is the intent, good PASM documentation was harder to find than PIR. Luckily, there was a list of opcodes, and a whopping 4 examples that came with Parrot, so those two things combined are what I used to get through most of this.
The last issue was stuff with the input file having Windows newlines while I only tested on Unix newlines to begin with. But oh well, that was fixed relatively easily.
My use of PASM has inspired me to look into soon using a similar language...Java ByteCode (and, if it is sufficiently different, PIR).
Anyway, even if it runs on a VM, it is still basically an assembly language, so my solution below runs very quickly: about 25ms on my machine.
.loadlib 'io_ops'
#ASCII values of important letters
.macro_const M 77
.macro_const D 68
.macro_const C 67
.macro_const L 76
.macro_const X 88
.macro_const V 86
.macro_const I 73
#Impromptu convention:
#arguments passed in I/S 1, returned in I/S 0
.pcc_sub :main main:
#'The Stack'
new P10,'ResizableIntegerArray'
getstdin P0
set I9, 0
MAINLOOP:
readline S1, P0
unless S1, FINAL
#get length of string
length I8, S1
#subtract 2 from length because windows newline
sub I8, I8, 2
inc I8
#cutoff one char of newline, as methods expect
#UNIX newlines
substr S1, S1, 0, I8
dec I8
#Now that that is done...
local_branch P10, GET_STRING_VALUE
local_branch P10, GET_CHAR_COUNT
sub I0, I8, I0
add I9, I9, I0
branch MAINLOOP
FINAL:
say I9
end
#Basically a switch statement
#input: I0
#output: I0
GET_CHAR_VALUE:
eq I0, .M, M
eq I0, .D, D
eq I0, .C, C
eq I0, .L, L
eq I0, .X, X
eq I0, .V, V
I:
set I0, 1
local_return P10
V:
set I0, 5
local_return P10
X:
set I0, 10
local_return P10
L:
set I0, 50
local_return P10
C:
set I0, 100
local_return P10
D:
set I0, 500
local_return P10
M:
set I0, 1000
local_return P10
#compute integer corresponding to Roman Numeral
#input: S1
#output: I0
#used: I2, I3, I4, I5, S2
GET_STRING_VALUE:
length I2, S1
dec I2
set I3, 0
set I4, 0
set I5, 0
LOOP:
ge I3, I2, ENDLOOP
substr S2, S1, I3, 1
ord I0, S2
local_branch P10, GET_CHAR_VALUE
set I4, I0
inc I3
substr S2, S1, I3, 1
ord I0, S2
local_branch P10, GET_CHAR_VALUE
lt I4, I0, _SUB
add I5, I5, I4
branch LOOP
_SUB:
sub I5, I5, I4
branch LOOP
ENDLOOP:
set I0, I5
local_return P10
#This could be written much more nicely
#If I could figure out how to do arrays
#input: I0
#used: I1, I6, I7
GET_CHAR_COUNT:
#Move input into I1
#And compute number of M's
set I1, I0
div I6, I1, 1000
mul I0, I6, 1000
sub I1, I1, I0
set I7, I6
#CM
div I6, I1, 900
mul I0, I6, 900
sub I1, I1, I0
mul I6, I6, 2
add I7, I7, I6
#D
div I6, I1, 500
mul I0, I6, 500
sub I1, I1, I0
add I7, I7, I6
#CD
div I6, I1, 400
mul I0, I6, 400
sub I1, I1, I0
mul I6, I6, 2
add I7, I7, I6
#C
div I6, I1, 100
mul I0, I6, 100
sub I1, I1, I0
add I7, I7, I6
#XC
div I6, I1, 90
mul I0, I6, 90
sub I1, I1, I0
mul I6, I6, 2
add I7, I7, I6
#L
div I6, I1, 50
mul I0, I6, 50
sub I1, I1, I0
add I7, I7, I6
#XL
div I6, I1, 40
mul I0, I6, 40
sub I1, I1, I0
mul I6, I6, 2
add I7, I7, I6
#X
div I6, I1, 10
mul I0, I6, 10
sub I1, I1, I0
add I7, I7, I6
#IX
div I6, I1, 9
mul I0, I6, 9
sub I1, I1, I0
mul I6, I6, 2
add I7, I7, I6
#V
div I6, I1, 5
mul I0, I6, 5
sub I1, I1, I0
add I7, I7, I6
#IV
div I6, I1, 4
mul I0, I6, 4
sub I1, I1, I0
mul I6, I6, 2
add I7, I7, I6
#I
add I0, I7, I1
local_return P10