Skip to content

Instantly share code, notes, and snippets.

@asbish
Last active December 9, 2019 14:17
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save asbish/6fcaa961ce44f29a4cbdb260538a3d34 to your computer and use it in GitHub Desktop.
Save asbish/6fcaa961ce44f29a4cbdb260538a3d34 to your computer and use it in GitHub Desktop.
unlambda - ehime.rb
inherit_from: .rubocop_todo.yml
AllCops:
TargetRubyVersion: 2.5
Metrics/AbcSize:
Max: 40
Metrics/CyclomaticComplexity:
Max: 25
Metrics/MethodLength:
Max: 50
Metrics/BlockNesting:
Max: 5
Metrics/BlockLength:
Max: 30
Metrics/PerceivedComplexity:
Max: 10
Style/FrozenStringLiteralComment:
EnforcedStyle: always
Style/Encoding:
Enabled: false
Style/AsciiComments:
Enabled: false
Style/Documentation:
Enabled: false
Style/Semicolon:
Enabled: false
Style/WordArray:
Enabled: false
# This configuration was generated by
# `rubocop --auto-gen-config`
# on 2019-12-09 23:15:58 +0900 using RuboCop version 0.77.0.
# The point is for the user to remove these configuration records
# one by one as the offenses are removed from the code base.
# Note that changes in the inspected code, or installation of new
# versions of RuboCop, may require this file to be generated again.
# Offense count: 1
# Cop supports --auto-correct.
# Configuration parameters: EnforcedStyle.
# SupportedStyles: always, never
Style/FrozenStringLiteralComment:
Exclude:
- 'Gemfile'
# Offense count: 1
# Cop supports --auto-correct.
Style/IfUnlessModifier:
Exclude:
- 'unl.rb'
# Offense count: 95
# Cop supports --auto-correct.
# Configuration parameters: EnforcedStyle, ConsistentQuotesInMultiline.
# SupportedStyles: single_quotes, double_quotes
Style/StringLiterals:
Exclude:
- 'Gemfile'
- 'unl.rb'
- 'unl_spec.rb'
---
include:
- "**/*.rb"
require: []
domains: []
reporters:
- rubocop
require_paths: []
max_files: 5000
source "https://rubygems.org"
gem "rspec"
GEM
remote: https://rubygems.org/
specs:
diff-lcs (1.3)
rspec (3.9.0)
rspec-core (~> 3.9.0)
rspec-expectations (~> 3.9.0)
rspec-mocks (~> 3.9.0)
rspec-core (3.9.0)
rspec-support (~> 3.9.0)
rspec-expectations (3.9.0)
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.9.0)
rspec-mocks (3.9.0)
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.9.0)
rspec-support (3.9.0)
PLATFORMS
ruby
DEPENDENCIES
rspec
BUNDLED WITH
2.0.2
# coding: utf-8
# frozen_string_literal: true
# unl.rb
# Emil Jeřábek氏(http://users.math.cas.cz/~jerabek/ptakoviny/index.html#unl) の
# サイトに掲載されている`unl.pl`のRubyクローン。`/' を除いた以外は同じです
# すこしくらいは工夫をしたかったけれど、特にアイデアもなく、Rubyに慣れるという
# 目的から離れていってしまいそうだったので、ここまで
#
# 参考サイト
# Unlambda 公式: http://www.madore.org/~david/programs/unlambda/
# unl.c: http://users.math.cas.cz/~jerabek/ptakoviny/index.html#unl
# 実装の情報: https://qiita.com/irori/items/fed279436464bc7c68be
# SKコンビネータ: http://people.cs.uchicago.edu/~odonnell/Teacher/Lectures/Formal_Organization_of_Knowledge/Examples/combinator_calculus/
# 継続: http://practical-scheme.net/docs/cont-j.html
#
# 実行
# wget http://users.math.cas.cz/~jerabek/unlambda/sierp.unl
# ruby unl.rb sierp.unl
DEBUG = false
class Unlambda
def initialize(stk, cin, cout)
@stack = stk
@rest = []
@in = cin
@out = cout
@cur_ch = nil # current char
dbg_log "initialize"
end
def eval
until @stack.empty?
while (intr_op = @stack.pop)
case intr_op
when "E"
@rest = @stack.pop
@rest[0] == "`" && @stack += [@rest[2], "F", @rest[1], "E"]
break
when "F"
if @rest[0] == "d"
# delay: の場合、式を評価せずプロミスへ
@rest = ["D", @stack.pop]
else
@stack += [@rest, "A", @stack.pop, "E"]
end
break
else # when `A'
apply
end
end
end
end
private
def apply
while (a = @stack.pop)
dbg_log "apply"
case a[0]
when "s"
# subsitution: 関数(\X,Y,Z -> XZ, YZ)に引数を適用し、
# 関数(\Y,Z -> XZ,YZ)を返す
@rest = ["S", @rest]; break
when "S"
# subsitution first partial: 関数(\Y,Z -> XZ,YZ)に引数を適用し、
# 関数(\Z -> XZ,YZ)を返す
@rest = ["T", a[1], @rest]; break
when "T"
# subsitution application: 関数(\Z -> XZ,YZ) に引数を与えた式を返す
@stack += [["`", ["`", a[1], @rest], ["`", a[2], @rest]], "E"]; break
when "k"
# constant generator: 関数(\X,Y -> X)に引数を適用し、関数(\Y -> X)を返す
@rest = ["K", @rest]; break
when "K"
# constant function: 関数(\Y -> X)に引数を与えた値を返す
@rest = a[1]; break
when "i"
# identity: 恒等関数
break
when "v"
# void: 自分自身(v)をそのまま返す
@rest = a; break
when "e"
# exit: 直ちに終了、ただし仕様には引数をとり評価した結果をプログラムの
# exit-status にする? と記載がある
@stack = []; break
when "D"
# promise: `d' によって作成されたプロミス(評価されていない引数)に
# D の引数を与えた式を返す
@stack += [["`", a[1], @rest], "E"]; break
when "c"
# call/cc: 現在の計算(継続)を引数 X の引数として与えた式を返す
@stack += [["`", @rest, ["C", [@stack]]], "E"]; break
when "C"
# cont: 継続を取り出す
@stack = a[1].pop; break
when "."
# print: 引数を出力し、その値をそのまま返す
@out.call(a[1]); break
when "@"
# read: 入力から一文字受取り current character(@cur_ch) にセット
# 成功した場合は `Xi' 失敗の場合 `Xv' を返す(このとき X は @ の引数)
@stack += [["`", @rest, [(@cur_ch = @in.call) ? "i" : "v"]], "E"]
break
when "?"
# compare character read: current character(@cur_ch) がセットされ、
# その値が ? の次に続く文字(X)と等しい場合 `Yi' を返す、
# 等しくない場合 `Yv' を返す (このとき Y は ?X の引数)
@stack += [["`", @rest, [a[1] == @cur_ch ? "i" : "v"]], "E"]
break
when "|"
# reprint character read: current character(@cur_ch) がセットされている
# 場合、`X.@cur_ch' を返す、そうでない場合 `Xv' を返す
# (このとき X は | の引数)
@stack += [["`", @rest, @cur_ch ? [".", @cur_ch] : ["v"]], "E"]
break
else
raise "`#{a[0]}' -- WTF?\n"
end
end
end
def dbg_log(name)
return unless DEBUG
print "DEBUG(#{name}): "
p @stack
end
end
def parse(io)
while (cc = io.getc)
c = cc.downcase
case c
when ".", "?"
# `.', `?' に続くEOFでない文字は受け入れる
if (nc = io.getc) # rubocop:disable Style/GuardClause
return [c, nc]
else
raise "unexpected EOF\n"
end
when "`"
return [c, parse(io), parse(io)]
when /^[skivcde@|]/
return [c]
when "r"
return [".", "\n"]
when /\s/
next
when "#"
io.readline
next
else
raise "unknown character `#{c}'\n"
end
end
end
def run(filename, cin = proc { $stdin.getc }, cout = proc { |x| print x })
stack = File.open(filename, "r") { |io| parse io }
Unlambda.new([stack, "E"], cin, cout).eval
end
if $PROGRAM_NAME == __FILE__
run ARGV[0]
end
# frozen_string_literal: true
require "./unl.rb"
def h_run(src, input = [])
stack = parse StringIO.new(src)
output = String.new("")
Unlambda.new([stack, "E"], proc { input.pop }, proc { |x| output << x }).eval
output
end
describe "Unlambda" do
it "write, space, comment" do
src = <<~"UNL"
# Hello world
`r``` ````` ```.H
.e .l
.l
.o. .w .o .r
.l .di
UNL
expect(h_run(src)).to eq("Hello world\n")
end
it "read, current char" do
expect(h_run("`@.X", ["_"])).to eq("X")
expect(h_run("````@i.Xi```?Yi.Zi", ["X"])).to eq("X")
expect(h_run("````@i.Xi```?Yi.Zi", ["Y"])).to eq("XZ")
expect(h_run("````@i.Xi``|.Yi", ["Z"])).to eq("XYZ")
expect(h_run("````@i.Xi``|.Yi", [])).to eq("Y")
end
it "s, k, i" do
expect(h_run("````s.X.Y.Zi")).to eq("XYZZ")
expect(h_run("```k.X.Yi")).to eq("X")
expect(h_run("`.Xi")).to eq("X")
expect(h_run("``````s``s`ks``s`kk``s`ks``s`k`sik`kk.X.Yii")).to eq("XY")
end
it "c, d" do
expect(h_run("``cd`.Xi")).to eq("XX")
expect(h_run("``ci`d`c`@|", ["XYZ"])).to eq("XYZ")
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment