Skip to content

Instantly share code, notes, and snippets.

@makenowjust
Created March 14, 2023 11:03
Show Gist options
  • Save makenowjust/f3a5eb81a3c3f443c475c44e04a92961 to your computer and use it in GitHub Desktop.
Save makenowjust/f3a5eb81a3c3f443c475c44e04a92961 to your computer and use it in GitHub Desktop.
Emmental interpreter and Quine program
#!/usr/bin/env ruby
module Emmental
class Error < StandardError
STACK_UNDERFLOW = 'Stack Underflow'
QUEUE_UNDERFLOW = 'Queue Underflow'
end
class State
def initialize
@stack = []
@queue = []
end
attr_reader :stack, :queue
def top
@stack.last
end
def push(char)
@stack.push(char)
end
def pop
raise Error, Error::STACK_UNDERFLOW if @stack.empty?
@stack.pop
end
def pop_string
str = []
while char = @stack.pop
break if char == ';'
str.unshift(char)
end
raise Error, Error::STACK_UNDERFLOW if char != ';'
str.join
end
def enqueue(char)
@queue.push(char)
end
def dequeue
@queue.shift
end
end
class OpMap
def initialize
@map = {}
end
def supplant(char, op)
@map[char] = op
end
def fetch(char)
@map[char]
end
end
module Op
NOP = ->(_state, _ops) {}
SUPPLANT = ->(state, ops) {
char = state.pop
string = state.pop_string
op_list = string.chars.flat_map { |c| ops.fetch(c) }
ops.supplant(char, ->(state, ops) {
op_list.each { _1.(state, ops) }
})
}
EVAL = ->(state, ops) {
char = state.pop
ops.fetch(char)&.call(state, ops)
}
INPUT = ->(state, _ops) {
char = $stdin.getc
state.push(state)
}
OUTPUT = ->(state, _ops) {
char = state.pop
$stdout.putc(char)
}
ADD = ->(state, _ops) {
char1 = state.pop
char2 = state.pop
state.push(((char2.ord + char1.ord) % 256).chr)
}
SUB = ->(state, _ops) {
char1 = state.pop
char2 = state.pop
state.push(((char2.ord - char1.ord + 256) % 256).chr)
}
def self.discrete_log(n)
return 8 if n == 0
log = 0
while n > 1
log += 1
n /= 2
end
log
end
DISCRETE_LOG = ->(state, _ops) {
char = state.pop
state.push(discrete_log(char.ord).chr)
}
ENQUEUE_COPY = ->(state, _ops) {
state.enqueue(state.top)
}
DEQUEUE = ->(state, _ops) {
state.push(state.dequeue)
}
DUPLICATE = ->(state, _ops) {
stack.push(stack.top)
}
def self.push_value(char)
->(state, _ops) {
state.push(char)
}
end
def self.accum_value(n)
->(state, _ops) {
char = state.pop
state.push(((char.ord * 10 + n) % 256).chr)
}
end
end
def self.execute(source)
ops = OpMap.new
ops.supplant('!', Op::SUPPLANT)
ops.supplant('?', Op::EVAL)
ops.supplant(';', Op.push_value(';'))
ops.supplant('.', Op::INPUT)
ops.supplant('.', Op::OUTPUT)
ops.supplant('#', Op.push_value("\x00"))
(0..9).each do |n|
ops.supplant(n.to_s, Op.accum_value(n))
end
ops.supplant('+', Op::ADD)
ops.supplant('-', Op::SUB)
ops.supplant('~', Op::DISCRETE_LOG)
ops.supplant('^', Op::ENQUEUE_COPY)
ops.supplant('v', Op::DEQUEUE)
ops.supplant(':', Op::DUPLICATE)
state = State.new
source.each_char do |char|
ops.fetch(char)&.call(state, ops)
end
end
end
source = ARGF.read
Emmental.execute(source)
def show(s)
s.chars.map { |c| "##{c.ord}." }.join
end
def quote(s)
';' + s.chars.map { |c| "##{c.ord}" }.join
end
def encode(s)
s.chars.map { |c| "#{c.ord - OFFSET}@" }.join
end
USED = '0123456789;.#@!?+-^v'
sorted = USED.chars.map { |c| c.ord }.sort
OFFSET = (1..sorted.first - 2).to_a.reverse.each do |n|
if (sorted.map { |x| x - n } & sorted).empty?
break n
end
end
PROLOGUE = quote('#^+^#@') + '!#' # '@' = '#^+^#',
OUTPUT1 = '#1^' + # queue "\x01"
sorted.map { |n| quote(show("#{n - OFFSET}@")) + "##{n - OFFSET}!" }.join +
quote("v^?v^?") + '#!' + ';#1!' + # "\x00" = "v^?v^?", "\x01" = ''
'v?'
OUTPUT2 = quote("v##{OFFSET}+.v?") + '#!' + ';#1!#?#10.;#10!'
source = show(PROLOGUE) + OUTPUT1 + OUTPUT2
puts PROLOGUE + encode(source) + source
;#35#94#43#94#35#64!#8@26@30@19@8@24@26@19@8@26@22@19@8@26@24@19@8@24@26@19@8@26@28@19@8@26@23@19@8@24@26@19@8@26@23@19@8@26@22@19@8@24@26@19@8@26@28@19@8@26@23@19@8@24@26@19@8@26@22@19@8@26@24@19@8@24@26@19@8@26@25@19@8@26@23@19@8@24@24@19@8@24@26@19@8@22@67@32@8@24@26@8@26@24@8@26@23@8@25@27@8@24@26@8@26@25@8@26@23@8@25@27@8@27@6@32@8@24@26@8@26@24@8@26@25@8@25@27@8@24@26@8@26@25@8@26@23@8@25@27@8@29@6@32@8@24@26@8@26@23@8@26@28@8@25@27@8@24@26@8@26@24@8@26@23@8@25@27@8@24@26@8@26@25@8@26@23@8@25@27@8@22@27@6@32@8@24@26@8@26@23@8@26@28@8@25@27@8@24@26@8@26@24@8@26@25@8@25@27@8@24@26@8@26@25@8@26@23@8@25@27@8@22@29@6@32@8@24@26@8@26@23@8@26@28@8@25@27@8@24@26@8@26@24@8@26@26@8@25@27@8@24@26@8@26@25@8@26@23@8@25@27@8@22@30@6@32@8@24@26@8@26@24@8@25@29@8@25@27@8@24@26@8@26@23@8@26@28@8@25@27@8@24@26@8@26@25@8@26@23@8@25@27@8@23@22@6@32@8@24@26@8@26@24@8@25@29@8@25@27@8@24@26@8@26@24@8@25@29@8@25@27@8@24@26@8@26@25@8@26@23@8@25@27@8@23@23@6@32@8@24@26@8@26@24@8@25@29@8@25@27@8@24@26@8@26@24@8@25@30@8@25@27@8@24@26@8@26@25@8@26@23@8@25@27@8@23@24@6@32@8@24@26@8@26@24@8@25@29@8@25@27@8@24@26@8@26@24@8@26@21@8@25@27@8@24@26@8@26@25@8@26@23@8@25@27@8@23@25@6@32@8@24@26@8@26@24@8@25@29@8@25@27@8@24@26@8@26@24@8@26@22@8@25@27@8@24@26@8@26@25@8@26@23@8@25@27@8@23@26@6@32@8@24@26@8@26@24@8@25@29@8@25@27@8@24@26@8@26@24@8@26@23@8@25@27@8@24@26@8@26@25@8@26@23@8@25@27@8@23@27@6@32@8@24@26@8@26@24@8@25@29@8@25@27@8@24@26@8@26@24@8@26@24@8@25@27@8@24@26@8@26@25@8@26@23@8@25@27@8@23@28@6@32@8@24@26@8@26@24@8@25@29@8@25@27@8@24@26@8@26@24@8@26@25@8@25@27@8@24@26@8@26@25@8@26@23@8@25@27@8@23@29@6@32@8@24@26@8@26@24@8@25@29@8@25@27@8@24@26@8@26@24@8@26@26@8@25@27@8@24@26@8@26@25@8@26@23@8@25@27@8@23@30@6@32@8@24@26@8@26@24@8@25@30@8@25@27@8@24@26@8@26@23@8@26@27@8@25@27@8@24@26@8@26@25@8@26@23@8@25@27@8@24@21@6@32@8@24@26@8@26@24@8@25@30@8@25@27@8@24@26@8@26@24@8@25@29@8@25@27@8@24@26@8@26@25@8@26@23@8@25@27@8@24@23@6@32@8@24@26@8@26@24@8@25@30@8@25@27@8@24@26@8@26@24@8@26@23@8@25@27@8@24@26@8@26@25@8@26@23@8@25@27@8@24@27@6@32@8@24@26@8@26@24@8@25@30@8@25@27@8@24@26@8@26@24@8@26@24@8@25@27@8@24@26@8@26@25@8@26@23@8@25@27@8@24@28@6@32@8@24@26@8@26@24@8@26@23@8@25@27@8@24@26@8@26@24@8@26@24@8@25@27@8@24@26@8@26@25@8@26@23@8@25@27@8@27@28@6@32@8@24@26@8@26@24@8@26@26@8@25@27@8@24@26@8@26@23@8@26@28@8@25@27@8@24@26@8@26@25@8@26@23@8@25@27@8@30@22@6@32@8@22@22@29@8@30@25@8@27@24@8@22@22@29@8@30@25@8@27@24@8@6@32@8@22@6@91@36@32@8@22@22@29@8@24@26@8@26@21@8@26@26@8@25@24@8@25@27@8@22@22@29@8@27@24@8@6@32@8@22@6@8@36@8@22@21@19@32@8@22@21@6@#59.#35.#51.#53.#35.#57.#52.#35.#52.#51.#35.#57.#52.#35.#51.#53.#35.#54.#52.#33.#35.#1^;#35#53#52#46#35#54#52#46#6!;#35#53#54#46#35#54#52#46#8!;#35#52#57#46#35#53#52#46#35#54#52#46#16!;#35#52#57#46#35#53#54#46#35#54#52#46#18!;#35#52#57#46#35#53#55#46#35#54#52#46#19!;#35#53#48#46#35#52#57#46#35#54#52#46#21!;#35#53#48#46#35#53#48#46#35#54#52#46#22!;#35#53#48#46#35#53#49#46#35#54#52#46#23!;#35#53#48#46#35#53#50#46#35#54#52#46#24!;#35#53#48#46#35#53#51#46#35#54#52#46#25!;#35#53#48#46#35#53#52#46#35#54#52#46#26!;#35#53#48#46#35#53#53#46#35#54#52#46#27!;#35#53#48#46#35#53#54#46#35#54#52#46#28!;#35#53#48#46#35#53#55#46#35#54#52#46#29!;#35#53#49#46#35#52#56#46#35#54#52#46#30!;#35#53#49#46#35#53#48#46#35#54#52#46#32!;#35#53#49#46#35#53#52#46#35#54#52#46#36!;#35#53#49#46#35#53#53#46#35#54#52#46#37!;#35#53#52#46#35#53#53#46#35#54#52#46#67!;#35#53#55#46#35#52#57#46#35#54#52#46#91!;#118#94#63#118#94#63#!;#1!v?;#118#35#50#55#43#46#118#63#!;#1!#?#10.;#10!
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment