Last active
December 9, 2019 14:17
-
-
Save asbish/6fcaa961ce44f29a4cbdb260538a3d34 to your computer and use it in GitHub Desktop.
unlambda - ehime.rb
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
*.unl |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# 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' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
2.5.5 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
--- | |
include: | |
- "**/*.rb" | |
require: [] | |
domains: [] | |
reporters: | |
- rubocop | |
require_paths: [] | |
max_files: 5000 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
source "https://rubygems.org" | |
gem "rspec" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# 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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# 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