Skip to content

Instantly share code, notes, and snippets.

@yamasushi
Last active December 2, 2023 06:11
Show Gist options
  • Save yamasushi/d250be27cb9115aeb55ec81a9c9b28a8 to your computer and use it in GitHub Desktop.
Save yamasushi/d250be27cb9115aeb55ec81a9c9b28a8 to your computer and use it in GitHub Desktop.
integer <----> roman numeral
# integer ----> roman numeral
# Compilers 1st ed. ex. P2.1 (p. 81)
# Compilers 2nd ed. ex. 2.3.3 (p. 60)
# https://gist.github.com/yamasushi/d250be27cb9115aeb55ec81a9c9b28a8
#-----------------------------------------------
# integer --> roman numeral
#-----------------------------------------------
# N -> N D | D
# N -> D R1 { N.s = n->roman(R1.k , D.d ) + R1.s }
# R -> D R1 { R.k = R1.k + 1 , R.s = n->roman(R1.k , D.d ) + R1.s }
# R -> ε { R.k = 0 , R.s= "" }
def integer2roman(num)
s,xs=I2r.N(num.to_s.chars)
#printf "s=#{s} , xs=#{xs}"
s
end
class I2r
@@num_table=[
["I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX"], # 1 ~ 9
["X", "XX", "XXX", "XL", "L", "LX", "LXX", "LXXX", "XC"], # 10 ~ 90
["C", "CC", "CCC", "CD", "D", "DC", "DCC", "DCCC", "CM"], # 100 ~ 900
["M", "MM", "MMM" ] ] # 1000 ~ 3000
def self.n2roman(t,n)
return "" if n <= 0
@@num_table[t][n-1]
end
# N -> D R1 { N.s = n->roman(R1.k , D.d ) + R1.s }
def self.N(xs)
raise "error N: xs=#{xs}" if xs.nil? or xs.empty? or xs[0]!~/[0-9]/
d=xs[0].to_i
k,s,xs=R(xs[1..])
[n2roman(k,d)+s , xs]
end
# R -> D R1 { R.k = R1.k + 1 , R.s = n->roman(R1.k , D.d ) + R1.s }
# R -> ε { R.k = 0 , R.s= "" }
def self.R(xs)
return raise "error R:" if xs.nil?
return [0,"",xs] if xs.empty? or xs[0] !~ /[0-9]/ #ε
d=xs[0].to_i
k,s,xs=R(xs[1..])
[k+1 , n2roman(k,d)+s , xs]
end
end
# roman numeral ---> integer
# https://gist.github.com/yamasushi/d250be27cb9115aeb55ec81a9c9b28a8
# Compilers 2nd ed. ex. 2.3.4 (p. 60)
#-----------------------------------------------
# roman numeral ---> integer
#-----------------------------------------------
def roman2integer(str)
n,xs=R2i.N (str.chars)
n
end
class R2i
def self.M0(xs)
M("I","V","X",xs)
end
def self.M1(xs)
M("X","L","C",xs)
end
def self.M2(xs)
M("C","D","M",xs)
end
def self.M3(xs)
M("M",nil,nil,xs)
end
def self.N(xs)
n3,xs=M3(xs)
n2,xs=M2(xs)
n1,xs=M1(xs)
n0,xs=M0(xs)
[ n3*1000 + n2*100 + n1*10 + n0 , xs]
end
def self.M( _I , _V, _X , xs)
# R -> I {R.n = 1} | ε {R.n = 0}
_R=lambda{|xs|
return [0,xs] if xs.empty? # ε
case xs[0]
when _I
return [ 1 , xs[1..]]
else
return [ 0 , xs ] # ε
end
}
# P -> I R {P.n = 1 + R.n }
# | V {P.n = 3 }
# | X {P.n = 8 }
# | ε {P.n = 0 }
_P=lambda{ |xs|
return [0,xs] if xs.empty? # ε
case xs[0]
when _I
n, xs = _R::(xs[1..])
return [ 1+n , xs ]
when _V
return [ 3 , xs[1..] ]
when _X
return [ 8 , xs[1..] ]
else
return [ 0 , xs ] # ε
end
}
# S -> I R {S.n = 1 + R.n} | ε {S.n = 0}
_S=lambda{|xs|
return [0,xs] if xs.empty? # ε
case xs[0]
when _I
n, xs = _R::(xs[1..])
return [ n+1 , xs ]
else
return [ 0 , xs ] # ε
end
}
# Q -> I S {Q.n = 1 + S.n} | ε {Q.n = 0}
_Q=lambda{|xs|
return [0,xs] if xs.empty? # ε
case xs[0]
when _I
n, xs= _S::( xs[1..] )
return [ n+1 , xs ]
else
return [ 0 , xs ] # ε
end
}
# M -> I P {M.n = 1 + P.n }
# | V Q {M.n = 5 + Q.n }
# | ε M.n = 0
return [0,xs] if xs.empty? # ε
case xs[0]
when _I
n, xs = _P::(xs[1..])
return [ n+1 , xs ]
when _V
n, xs = _Q::(xs[1..])
return [ 5+n , xs ]
else
return [ 0 , xs ] # ε
end
end # def self.M
end # class
# test roman numeral <--> integer
# https://gist.github.com/yamasushi/d250be27cb9115aeb55ec81a9c9b28a8
load "./i2r.rb"
load "./r2i.rb"
def test_roman(max)
max.times do |i|
r=integer2roman(i)
j=roman2integer(r)
puts "#{i} --i2r--> #{r} --r2i--> #{j}"
raise "error!" if i!=j
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment