Skip to content

Instantly share code, notes, and snippets.

@sampersand
Last active September 25, 2022 09:05
Show Gist options
  • Save sampersand/0442484fd4fdc0205b3095763acaac13 to your computer and use it in GitHub Desktop.
Save sampersand/0442484fd4fdc0205b3095763acaac13 to your computer and use it in GitHub Desktop.
Monkeypatched knight in ruby
#!/usr/bin/env ruby
# Knight in ruby, but with copious amounts of monkey patching :-)
class Fn
@fns = {}
def self.register(name, ...) = @fns[name] = Fn.new(...)
def self.lookup(name) = @fns.fetch(name)
def initialize(call_args: true, &block) @call_args, @block = call_args, block end
def create(&b) = @block.arity.times.map(&b).then { |x| proc { call(x) } }
def call(args) = @block.call(*(@call_args ? args.map(&:call) : args))
register 'P' do gets chomp: true end
register 'R' do rand 0..0xffff_ffff end
register 'B', call_args: false do _1 end
register 'C' do _1.call end
register 'Q' do exit _1.to_i end
register 'D' do _1.tap { |x| print x.inspect } end # cant use `p` as it adds a newline
register 'O' do print _1.to_s.dup.tap { |x| x.slice! /\\\z/ or x << "\n" } end
register 'L' do _1.to_a.length end
register '!' do !_1 end
register '~' do -_1.to_i end
register 'A' do _1.ord rescue _1.chr end
register ',' do [_1] end
register '[' do _1[0] end
register ']' do _1[1..] end
register '+' do _1 + _2.cast(to: _1) end
register '-' do _1 - _2.to_i end
register '*' do _1 * _2.to_i end
register '/' do _1.fdiv(_2.to_i).truncate end
register '%' do _1 % _2.to_i end
register '^' do _1 ** _2.cast(to: _1.is_a?(Array) ? "" : _1) end
register '<' do _1 < _2.cast(to: _1) end
register '>' do _1 > _2.cast(to: _1) end
register '?' do _1 == _2 end
register ';' do _2 end
register '&', call_args: false do !(lhs = _1.call) ? lhs : _2.call end
register '|', call_args: false do !(lhs = _1.call) ? _2.call : lhs end
register '=', call_args: false do _2.call.tap &_1.method(:call=) end
register 'W', call_args: false do _2.call until !_1 end
register 'I', call_args: false do (!_1 ? _3 : _2).call end
register 'G' do _1[_2.to_i, _3.to_i] end
register 'S' do (x = _1.dup)[_2.to_i, _3.to_i] = _4.cast to: x; x end
end
class Object
alias call itself
def to_i = call.to_i
def to_s = call.to_s
def to_a = call.to_a
def coerce(rhs) = [rhs, to_i]
def cast(to:)
case to
when Integer then to_i
when String then to_s
when Array then to_a
when true, false then !!self
end
end
end
class Var
@vars = {}
def self.new(name) = @vars[name] ||= super()
attr_accessor :call
def ! = !call
end
class Proc
def ! = !call
end
class TrueClass
def to_i = 1
def to_a = [self]
def <(_) = false
def >(x) = !x
end
class FalseClass
def to_i = 0
def to_a = []
def <(x) = !!x
def >(_) = false
end
class NilClass
def inspect = 'null'
end
class Array
include Comparable
alias ** join
alias to_i length
def to_s = join("\n")
alias ! empty?
end
class Integer
undef ord
alias ! zero?
def to_a = abs.digits.tap { negative? and _1.map! &:-@ }.reverse
end
class String
alias to_a chars
alias old_to_i to_i
def to_i = sub(/d/i, '@').old_to_i # `0d10`.to_i == 10, not 0.
alias ! empty?
def parse!
case
when slice!(/\A([():\s]+|#.*\n)/) then return replace($').parse!
when slice!(/\A\d+/) then $&.to_i
when slice!(/\A[a-z_][a-z_\d]*/) then Var.new $&
when slice!(/\A(?:'([^']*?)'|"([^"]*?)")/) then $+
when slice!(/\A([TF])[A-Z_]*/) then $1 == 'T'
when slice!(/\AN[A-Z_]*/) then nil
when slice!(/\A@/) then []
when slice!(/\A([A-Z][A-Z_]*|.)/) then return Fn.lookup($&[0]).create { parse! }
end.tap { replace $' }
end
end
$0 == __FILE__ and ($*.shift == '-e' ? $*.shift.dup : File.read($*.shift)).parse!.call
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment