rasel 1.0.0 → 1.1.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/bin/rasel-annotated +6 -0
- data/bin/rasel-convert +34 -0
- data/bin/rasel-ide +280 -0
- data/lib/rasel.rb +67 -20
- data/rasel.gemspec +6 -4
- metadata +37 -5
- data/test.rb +0 -169
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b5abd1a7bf87953fd9c52ed5362369df0b553236
|
4
|
+
data.tar.gz: 3cbe087b724031a6a18c8e1cca4d4e86f97dd4ef
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 96191f88b08d9133b083e078aa62f6e5c99b6c052ce01bce82891c0d0f808d21ac84e3afd01cab7ba5b56706c3b6b044f5ee329c43bf8cd7462c7955f7ac7fbd
|
7
|
+
data.tar.gz: 6bb8862d98584c2241c4d19c1d6f23b1da7b3936eb0f1b654c6c6b3dc10f5e5e9187d88ce0ecfac31c75dc1abc1557a80ca5957ee32785a51a6203c890b8a987
|
data/bin/rasel-annotated
ADDED
data/bin/rasel-convert
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
abort_usage = lambda do
|
4
|
+
abort <<~HEREDOC
|
5
|
+
usage:
|
6
|
+
\t$ rasel-convert <file.rasel> <file.rasela>
|
7
|
+
\t$ rasel-convert <file.rasela> <file.rasel>
|
8
|
+
\t(pipe support is not implemented)
|
9
|
+
HEREDOC
|
10
|
+
end
|
11
|
+
abort_usage.call unless ARGV[0] and File.file? ARGV[0]
|
12
|
+
abort_usage.call unless ARGV[0] and ARGV[1] and (!File.exist?(ARGV[1]) || puts("overwriting")) || File.file?(ARGV[1])
|
13
|
+
|
14
|
+
require "json"
|
15
|
+
case ARGV.map &File.method(:extname)
|
16
|
+
when %w{ .rasel .rasela }
|
17
|
+
File.write ARGV[1], JSON.pretty_generate(
|
18
|
+
File.read(ARGV[0]).
|
19
|
+
tap{ |_| abort "empty source" if _.empty? }.
|
20
|
+
gsub(/ +$/,"").split(?\n).
|
21
|
+
flat_map.with_index{ |row, i| row.chars.map.with_index{ |c, j| [i, j, c] } }
|
22
|
+
)
|
23
|
+
when %w{ .rasela .rasel }
|
24
|
+
File.open ARGV[1], "w" do |file|
|
25
|
+
file.puts( JSON.load(File.read ARGV[0]).each_with_object([]) do |(y, x, c, a), code|
|
26
|
+
code[y] ||= []
|
27
|
+
code[y][x] = c
|
28
|
+
end.map{ |row| row.map{ |_| _ || " " }.join } )
|
29
|
+
end
|
30
|
+
else
|
31
|
+
abort_usage.call
|
32
|
+
end
|
33
|
+
|
34
|
+
puts :OK
|
data/bin/rasel-ide
ADDED
@@ -0,0 +1,280 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
abort "usage:\trasel-ide <file.rasela>" unless 1 == ARGV.size
|
4
|
+
abort "invalid directory" unless File.directory? File.dirname File.expand_path ARGV.first
|
5
|
+
require "json"
|
6
|
+
File.write ARGV.first, JSON.pretty_generate([[0,0,"@"]]) unless File.exist? ARGV.first
|
7
|
+
abort "the argument should be a file" unless File.file? ARGV.first if File.exist? ARGV.first
|
8
|
+
|
9
|
+
require "sinatra"
|
10
|
+
enable :run
|
11
|
+
# require "sinatra/reloader" # uncomment only during the development
|
12
|
+
get "/jquery.js" do
|
13
|
+
send_file "#{__dir__}/../jquery-3.6.0.min.js"
|
14
|
+
end
|
15
|
+
|
16
|
+
get "/" do
|
17
|
+
<<~HEREDOC
|
18
|
+
<!DOCTYPE html>
|
19
|
+
<html>
|
20
|
+
<head>
|
21
|
+
<meta charset="utf-8">
|
22
|
+
<title>RASEL IDE</title>
|
23
|
+
<script src="jquery.js" type="text/javascript"></script>
|
24
|
+
<script type="text/javascript">
|
25
|
+
window.onload = function() {
|
26
|
+
window.jsend = function(mtd, path, options, callback) {
|
27
|
+
$("#preloader").show();
|
28
|
+
$[mtd](path, options).fail( function(xhr) {
|
29
|
+
alert(xhr.responseText || "something went wrong");
|
30
|
+
} ).done( function(text) {
|
31
|
+
const json = JSON.parse(text);
|
32
|
+
if (json.status == "error")
|
33
|
+
alert("SERVER ERROR: " + json.message);
|
34
|
+
else if (json.status == "success") {
|
35
|
+
callback(json.data);
|
36
|
+
} else alert("server response is weird");
|
37
|
+
} ).always( function() {
|
38
|
+
$("#preloader").hide();
|
39
|
+
} );
|
40
|
+
};
|
41
|
+
|
42
|
+
$("textarea").keyup( function(event) {
|
43
|
+
event.stopPropagation();
|
44
|
+
} );
|
45
|
+
|
46
|
+
$("button").click( function() {
|
47
|
+
jsend("post", "run", {stdin: JSON.stringify($("textarea").val())}, function(data) {
|
48
|
+
console.log(data);
|
49
|
+
const [stdout, exitstatus] = data;
|
50
|
+
$("#result").empty().
|
51
|
+
append(`<div><b>EXIT CODE:</b> ${exitstatus}</div>`).
|
52
|
+
append("<div><b>LOG:</b></div>");
|
53
|
+
stdout.forEach( function(msg) {
|
54
|
+
const [reason, str] = JSON.parse(msg);
|
55
|
+
const div = $("<div style='display: flex; gap: 0.3rem; align-items: baseline'></div>");
|
56
|
+
switch (reason) {
|
57
|
+
case "print":
|
58
|
+
div.text("print:").append(`<span>${str}</span>`);
|
59
|
+
break;
|
60
|
+
case "abort":
|
61
|
+
div.text("aborted:").append(`<span>${str}</span>`);
|
62
|
+
break;
|
63
|
+
case "loop":
|
64
|
+
div.text("stack:");
|
65
|
+
str.forEach( function(item) {
|
66
|
+
const div_item = $("<div class='stackitem'></div>").appendTo(div);
|
67
|
+
if (item instanceof Array) {
|
68
|
+
const [value, annotation] = item;
|
69
|
+
div_item.append(value);
|
70
|
+
$("<div style='font-size: xx-small'></div>").text(annotation).appendTo(div_item);
|
71
|
+
} else {
|
72
|
+
div_item.append(item);
|
73
|
+
};
|
74
|
+
} );
|
75
|
+
break;
|
76
|
+
default:
|
77
|
+
div.text("unknown msg type:").append(`<span>${reason}</span>`);
|
78
|
+
};
|
79
|
+
div.appendTo($("#result"));
|
80
|
+
} );
|
81
|
+
} );
|
82
|
+
} );
|
83
|
+
window.post_code = function() {
|
84
|
+
let dump = [];
|
85
|
+
$("#grid").children().each( function(i, row) {
|
86
|
+
$(row).children().each( function(j, cell) {
|
87
|
+
if (cell.innerText.length || cell.dataset.a) dump.push([i, j, cell.innerText, cell.dataset.a]);
|
88
|
+
} );
|
89
|
+
} );
|
90
|
+
jsend("post", "write", {dump: JSON.stringify(dump)}, ()=>{});
|
91
|
+
};
|
92
|
+
|
93
|
+
window.annotate = function() {
|
94
|
+
window.selected_cell.css("background-color", "#eee")[0].dataset.a = $("input").val();
|
95
|
+
};
|
96
|
+
$("input").keyup( function(event) {
|
97
|
+
event.stopPropagation();
|
98
|
+
if (event.keyCode != 13) return;
|
99
|
+
if ($("input").val().length)
|
100
|
+
annotate();
|
101
|
+
else {
|
102
|
+
window.selected_cell.css("background-color", "")[0];
|
103
|
+
delete window.selected_cell[0].dataset.a;
|
104
|
+
};
|
105
|
+
post_code();
|
106
|
+
} );
|
107
|
+
$("body").keyup( function(event) {
|
108
|
+
if (!event.key) return;
|
109
|
+
const char = (event.key == "Delete" || event.key == "Backspace") ? "" :
|
110
|
+
event.key.length == 1 ? event.key : null;
|
111
|
+
if (!char) return;
|
112
|
+
window.selected_cell[0].innerText = char;
|
113
|
+
post_code();
|
114
|
+
} );
|
115
|
+
|
116
|
+
window.create_span = function() {
|
117
|
+
return $("<span></span>").click( function() {
|
118
|
+
if (window.selected_cell) window.selected_cell.css("border-color", "white");
|
119
|
+
window.selected_cell = $(this);
|
120
|
+
window.selected_cell.css("border-color", "black");
|
121
|
+
$("input").val(this.dataset.a);
|
122
|
+
} );
|
123
|
+
};
|
124
|
+
window.add_row = function(i = 0) {
|
125
|
+
add_row_adder();
|
126
|
+
add_row_remover();
|
127
|
+
const row = $("<span style='display: flex'></span>");
|
128
|
+
let w = $("#column_removers").children().length;
|
129
|
+
if (i && w)
|
130
|
+
row.insertAfter($(`#grid>*:eq(${i-1})`));
|
131
|
+
else
|
132
|
+
row.prependTo($("#grid"));
|
133
|
+
while (w--) create_span().appendTo(row);
|
134
|
+
};
|
135
|
+
window.add_row_adder = function() {
|
136
|
+
const i = $("#row_adders").children().length;
|
137
|
+
$("<span>+</span>").click( function() {
|
138
|
+
add_row(i);
|
139
|
+
post_code();
|
140
|
+
} ).appendTo($("#row_adders"));
|
141
|
+
};
|
142
|
+
window.add_row_remover = function() {
|
143
|
+
const i = $("#row_removers").children().length;
|
144
|
+
$("<span>✕</span>").click( function() {
|
145
|
+
$("#row_adders>*").last().remove();
|
146
|
+
$("#row_removers>*").last().remove();
|
147
|
+
let removed = false;
|
148
|
+
$($("#grid").children()[i]).children().each( function() {
|
149
|
+
if (selected_cell[0] == this) removed = true;
|
150
|
+
} );
|
151
|
+
$("#grid").children()[i].remove();
|
152
|
+
if (removed) $("#grid>*:eq(0)>*:eq(0)").click();
|
153
|
+
post_code();
|
154
|
+
} ).appendTo($("#row_removers"));
|
155
|
+
};
|
156
|
+
window.add_column = function(i = 0) {
|
157
|
+
add_column_adder();
|
158
|
+
add_column_remover();
|
159
|
+
$("#grid").children().each( function() {
|
160
|
+
if (i && $(this).children().length)
|
161
|
+
create_span().insertAfter($(this).children()[i-1]);
|
162
|
+
else
|
163
|
+
create_span().prependTo($(this));
|
164
|
+
} );
|
165
|
+
};
|
166
|
+
window.add_column_adder = function() {
|
167
|
+
const i = $("#column_adders").children().length;
|
168
|
+
$("<span>+</span>").click( function() {
|
169
|
+
add_column(i);
|
170
|
+
post_code();
|
171
|
+
} ).appendTo($("#column_adders"));
|
172
|
+
};
|
173
|
+
window.add_column_remover = function() {
|
174
|
+
const i = $("#column_removers").children().length;
|
175
|
+
$("<span>✕</span>").click( function() {
|
176
|
+
$("#column_adders>*").last().remove();
|
177
|
+
$("#column_removers>*").last().remove();
|
178
|
+
let removed = false;
|
179
|
+
$("#grid").children().each( function() {
|
180
|
+
if (selected_cell[0] == $(this).children()[i]) removed = true;
|
181
|
+
$($(this).children()[i]).remove();
|
182
|
+
} );
|
183
|
+
if (removed) $("#grid>*:eq(0)>*:eq(0)").click();
|
184
|
+
post_code();
|
185
|
+
} ).appendTo($("#column_removers"));
|
186
|
+
};
|
187
|
+
add_row_adder();
|
188
|
+
add_column_adder();
|
189
|
+
|
190
|
+
jsend("get", "read", null, function(data) {
|
191
|
+
const code = JSON.parse(data);
|
192
|
+
code.forEach( function([y, x, c, a]) {
|
193
|
+
while(y >= $("#row_removers").children().length) add_row();
|
194
|
+
while(x >= $("#column_removers").children().length) add_column();
|
195
|
+
} );
|
196
|
+
code.forEach( function([y, x, c, a]) {
|
197
|
+
$(`#grid>*:eq(${y})>*:eq(${x})`).text(c).click();
|
198
|
+
if (a) {
|
199
|
+
$("input").val(a);
|
200
|
+
annotate();
|
201
|
+
};
|
202
|
+
} );
|
203
|
+
$("#grid>*:eq(0)>*:eq(0)").click();
|
204
|
+
} );
|
205
|
+
};
|
206
|
+
</script>
|
207
|
+
<body style="margin: 0; padding-left: 1rem; padding-right: 1rem; padding-bottom: 0.5rem; font-family: monospace; font-size: xx-large">
|
208
|
+
<style>
|
209
|
+
span{
|
210
|
+
display: inline-block; line-height: 1.2rem; min-width: 1.2rem; max-height: 1.2rem; min-height: 1.2rem; text-align: center;
|
211
|
+
border: 1px; border-style: solid; border-color: white;
|
212
|
+
}
|
213
|
+
label{ font-size: initial }
|
214
|
+
.stackitem{
|
215
|
+
flex-direction: column; display: flex; align-items: center;
|
216
|
+
background: #eee; padding-left: 0.2rem; padding-right: 0.2rem; margin-top: 0.2rem;
|
217
|
+
}
|
218
|
+
</style>
|
219
|
+
<div id="preloader" style="
|
220
|
+
position: fixed;
|
221
|
+
width: 100%; height: 100%;
|
222
|
+
left: 0; top: 0;
|
223
|
+
overflow: visible; z-index: 999;
|
224
|
+
background: hsla(0,0%,95%,0.8)
|
225
|
+
url("data:image/svg+xml,%3Csvg fill='%23000000' height='24' viewBox='0 0 24 24' width='24' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M0 0h24v24H0z' fill='none'/%3E%3Cpath d='M15 1H9v2h6V1zm-4 13h2V8h-2v6zm8.03-6.61l1.42-1.42c-.43-.51-.9-.99-1.41-1.41l-1.42 1.42C16.07 4.74 14.12 4 12 4c-4.97 0-9 4.03-9 9s4.02 9 9 9 9-4.03 9-9c0-2.12-.74-4.07-1.97-5.61zM12 20c-3.87 0-7-3.13-7-7s3.13-7 7-7 7 3.13 7 7-3.13 7-7 7z'/%3E%3C/svg%3E")
|
226
|
+
no-repeat center center;
|
227
|
+
background-size: 10%;
|
228
|
+
"></div>
|
229
|
+
<label>STDIN:</label>
|
230
|
+
<textarea style="flex: auto; margin: 1rem; height: 2rem; vertical-align: middle"></textarea>
|
231
|
+
<button>RUN</button>
|
232
|
+
<div style="display: flex; align-items: baseline">
|
233
|
+
<label>Selected cell annotation:</label>
|
234
|
+
<input type="text" style="flex: auto; margin: 1rem">
|
235
|
+
</div>
|
236
|
+
<hr>
|
237
|
+
<div style="
|
238
|
+
display: grid;
|
239
|
+
grid-template-rows: repeat(2, max-content max-content auto);
|
240
|
+
grid-template-columns: repeat(2, max-content max-content auto);
|
241
|
+
">
|
242
|
+
<div id="column_adders" style="grid-column: 3; display: flex"></div>
|
243
|
+
<div id="column_removers" style="grid-column: 3; display: flex; padding-left: 0.5rem"></div>
|
244
|
+
<div id="row_adders" style="grid-row: 3; display: flex; flex-direction: column"></div>
|
245
|
+
<div id="row_removers" style="grid-row: 3; display: flex; flex-direction: column; padding-top: 0.5rem"></div>
|
246
|
+
<div id="grid" style="grid-row: 3; padding-top: 0.4rem; padding-left: 0.5rem"></div>
|
247
|
+
</div>
|
248
|
+
<hr>
|
249
|
+
<div id="result" style="font-size: initial"></div>
|
250
|
+
</body>
|
251
|
+
</html>
|
252
|
+
HEREDOC
|
253
|
+
end
|
254
|
+
|
255
|
+
get "/read" do
|
256
|
+
JSON.dump({status: :success, data: File.read(ARGV.first)})
|
257
|
+
end
|
258
|
+
post "/write" do
|
259
|
+
File.write ARGV.first, JSON.pretty_generate(JSON.load params["dump"])
|
260
|
+
JSON.dump({status: :success})
|
261
|
+
end
|
262
|
+
|
263
|
+
require_relative "../lib/rasel"
|
264
|
+
require "open3"
|
265
|
+
post "/run" do
|
266
|
+
begin
|
267
|
+
require "timeout"
|
268
|
+
result = Timeout.timeout(2) do
|
269
|
+
RASEL \
|
270
|
+
JSON.load(File.read ARGV.first),
|
271
|
+
StringIO.new,
|
272
|
+
StringIO.new.tap{ |_| JSON.load(params.fetch "stdin").bytes.reverse_each &_.method(:ungetbyte) }
|
273
|
+
end
|
274
|
+
JSON.dump({status: :success, data: [result.stdout.string.split("\n"), result.exitcode]})
|
275
|
+
rescue Timeout::Error
|
276
|
+
# TODO: test this branch
|
277
|
+
JSON.dump({status: :error, message: :timeout, data: ["", 0]})
|
278
|
+
end
|
279
|
+
end
|
280
|
+
|
data/lib/rasel.rb
CHANGED
@@ -1,19 +1,58 @@
|
|
1
1
|
Encoding::default_internal = Encoding::default_external = "ASCII-8BIT"
|
2
2
|
END { RubyProf::FlatPrinter.new(RubyProf.stop).print STDERR, min_percent: 1 } if ENV["PROFILE"]
|
3
3
|
|
4
|
+
require "delegate"
|
5
|
+
class RASELStackItem < DelegateClass Rational
|
6
|
+
attr_reader :annotation
|
7
|
+
def initialize n, annotation
|
8
|
+
super n
|
9
|
+
@annotation = annotation || (n.annotation if n.respond_to? :annotation)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
RASELResultStruct = Struct.new :stdout, :stack, :exitcode
|
4
14
|
def RASEL source, stdout = StringIO.new, stdin = STDIN
|
5
|
-
lines = source.tap{ |_| fail "empty source" if _.empty? }.gsub(/ +$/,"").split(?\n)
|
6
|
-
code = lines.map{ |_| _.ljust(lines.map(&:size).max).bytes }
|
7
15
|
stack = []
|
8
16
|
pop = ->{ stack.pop || 0 }
|
17
|
+
error = Proc.new{ return RASELResultStruct.new stdout, stack, 255 }
|
18
|
+
|
19
|
+
case source
|
20
|
+
when String
|
21
|
+
lines = source.tap{ |_| raise ArgumentError.new "empty source" if _.empty? }.gsub(/ +$/,"").split(?\n)
|
22
|
+
code = lines.map{ |_| _.ljust(lines.map(&:size).max).bytes }
|
23
|
+
when Array
|
24
|
+
annotate = true
|
25
|
+
code = Array.new(source.map{|y,|y}.max+1){ Array.new(source.map{|_,x,|x}.max+1){ " ".ord } }
|
26
|
+
source.each{ |y, x, c, a| code[y][x] = [c.ord, a] }
|
27
|
+
stdout.instance_eval do
|
28
|
+
pos = self.pos
|
29
|
+
old_puts = method :puts
|
30
|
+
prev = nil
|
31
|
+
define_singleton_method :puts do |str, reason|
|
32
|
+
next if prev == dump = JSON.dump([reason, str])
|
33
|
+
old_puts.call prev = dump
|
34
|
+
if 200_000 < stdout.pos - pos
|
35
|
+
old_puts.call JSON.dump [:abort, "printed size"]
|
36
|
+
error.call
|
37
|
+
end
|
38
|
+
end
|
39
|
+
define_singleton_method :print do |str|
|
40
|
+
puts str, :print
|
41
|
+
end
|
42
|
+
end
|
43
|
+
else
|
44
|
+
raise ArgumentError.new "unsupported source class: #{source.class}"
|
45
|
+
end
|
9
46
|
dx, dy = 1, 0
|
10
47
|
x, y = -1, 0
|
11
48
|
|
49
|
+
# debugging and profiling (currently not maintained)
|
12
50
|
history = {}
|
51
|
+
debug_history = ENV.key? "DEBUG_HISTORY"
|
13
52
|
move = lambda do
|
14
53
|
y = (y + dy) % code.size
|
15
54
|
x = (x + dx) % code[y].size
|
16
|
-
next unless
|
55
|
+
next unless debug_history && (code[y][x] == 32 || code[y][x][0] == 32)
|
17
56
|
history[[x, y]] ||= 0
|
18
57
|
history[[x, y]] += 1
|
19
58
|
end
|
@@ -26,7 +65,7 @@ def RASEL source, stdout = StringIO.new, stdin = STDIN
|
|
26
65
|
end
|
27
66
|
sleep 0.1
|
28
67
|
end
|
29
|
-
end if
|
68
|
+
end if debug_history
|
30
69
|
if ENV["PROFILE"]
|
31
70
|
require "ruby-prof"
|
32
71
|
RubyProf.start
|
@@ -34,29 +73,36 @@ def RASEL source, stdout = StringIO.new, stdin = STDIN
|
|
34
73
|
|
35
74
|
reverse = ->{ dy, dx = -dy, -dx }
|
36
75
|
stringmode = false
|
37
|
-
|
76
|
+
debug = ENV.key? "DEBUG"
|
38
77
|
loop do
|
78
|
+
if annotate && 1 < Time.now - time
|
79
|
+
stdout.puts "timeout", :abort
|
80
|
+
error.call
|
81
|
+
end
|
82
|
+
stdout.puts stack.map{ |_| _.respond_to?(:annotation) && _.annotation ? [_, _.annotation] : _ }, :loop if annotate
|
83
|
+
|
39
84
|
move[]
|
40
|
-
byte = code[y][x]
|
85
|
+
byte, annotation = code[y][x]
|
41
86
|
char = byte.chr
|
42
|
-
STDERR.puts [char, stringmode, (stack.last Integer ENV["DEBUG"] rescue stack)].inspect if
|
87
|
+
STDERR.puts [char, stringmode, (stack.last Integer ENV["DEBUG"] rescue stack)].inspect if debug
|
43
88
|
|
44
89
|
next stack.push byte if stringmode && char != ?"
|
45
|
-
return
|
90
|
+
return RASELResultStruct.new stdout, stack, (
|
46
91
|
t = pop[]
|
47
92
|
1 != t.denominator || t < 0 || t > 255 ? 255 : t.to_i
|
48
93
|
) if char == ?@
|
49
94
|
case char
|
50
95
|
when ?\s
|
51
96
|
|
52
|
-
when ?0..?9
|
97
|
+
when ?0..?9 ; stack.push RASELStackItem.new byte - 48, annotation
|
98
|
+
when ?A..?Z ; stack.push RASELStackItem.new byte - 55, annotation
|
53
99
|
when ?" ; stringmode ^= true
|
54
100
|
when ?# ; move[]
|
55
101
|
when ?$ ; pop[]
|
56
|
-
when ?: ;
|
57
|
-
when ?- ; stack.push -(pop[] - pop[])
|
58
|
-
when ?/ ; b, a = pop[], pop[]; stack.push
|
59
|
-
when ?% ; b, a = pop[], pop[]; stack.push
|
102
|
+
when ?: ; popped = pop[]; stack.push popped; stack.push RASELStackItem.new popped, annotation
|
103
|
+
when ?- ; stack.push RASELStackItem.new -(pop[] - pop[]), annotation
|
104
|
+
when ?/ ; b, a = pop[], pop[]; stack.push RASELStackItem.new b.zero? ? 0 : Rational(a) / b, annotation
|
105
|
+
when ?% ; b, a = pop[], pop[]; stack.push RASELStackItem.new b.zero? ? 0 : Rational(a) % b, annotation
|
60
106
|
when ?v ; dx, dy = 0, 1
|
61
107
|
when ?> ; dx, dy = 1, 0
|
62
108
|
when ?^ ; dx, dy = 0, -1
|
@@ -64,28 +110,29 @@ def RASEL source, stdout = StringIO.new, stdin = STDIN
|
|
64
110
|
when ?? ; move[] if pop[] > 0
|
65
111
|
when ?\\
|
66
112
|
t = pop[]
|
67
|
-
error
|
113
|
+
error.call if 1 != t.denominator
|
68
114
|
stack.unshift 0 until stack.size > t
|
69
|
-
stack[-t-1], stack[-1] = stack[-1], stack[-t-1] unless 0 > t
|
115
|
+
stack[-t-1], stack[-1] = RASELStackItem.new(stack[-1], annotation), stack[-t-1] unless 0 > t
|
116
|
+
# TODO: annotate prints
|
70
117
|
when ?. ; stdout.print "#{_ = pop[]; 1 != _.denominator ? _.to_f : _.to_i} "
|
71
|
-
when ?, ; stdout.print "#{_ = pop[]; 1 != _.denominator ? error
|
72
|
-
when ?~ ; if _ = stdin.getbyte then stack.push _; move[] end
|
118
|
+
when ?, ; stdout.print "#{_ = pop[]; 1 != _.denominator ? error.call : _ < 0 || _ > 255 ? error.call : _.to_i.chr}"
|
119
|
+
when ?~ ; if _ = stdin.getbyte then stack.push RASELStackItem.new _, annotation; move[] end
|
73
120
|
when ?&
|
74
121
|
getc = ->{ stdin.getc or throw nil }
|
75
122
|
catch nil do
|
76
123
|
nil until (?0..?9).include? c = getc[]
|
77
124
|
while (?0..?9).include? cc = stdin.getc ; c << cc end
|
78
125
|
stdin.ungetbyte cc if cc
|
79
|
-
stack.push c.to_i
|
126
|
+
stack.push RASELStackItem.new c.to_i, annotation
|
80
127
|
move[]
|
81
128
|
end
|
82
129
|
when ?j
|
83
130
|
t = pop[]
|
84
|
-
error
|
131
|
+
error.call if 1 != t.denominator
|
85
132
|
y = (y + dy * t.to_i) % code.size
|
86
133
|
x = (x + dx * t.to_i) % code[y].size
|
87
134
|
|
88
|
-
else ; error
|
135
|
+
else ; error.call
|
89
136
|
end
|
90
137
|
end
|
91
138
|
end
|
data/rasel.gemspec
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
Gem::Specification.new do |spec|
|
2
2
|
spec.name = "rasel"
|
3
|
-
spec.version = "1.
|
3
|
+
spec.version = "1.1.2"
|
4
4
|
spec.summary = "Random Access Stack Esoteric Language"
|
5
5
|
|
6
6
|
spec.author = "Victor Maslov aka Nakilon"
|
@@ -8,11 +8,13 @@ Gem::Specification.new do |spec|
|
|
8
8
|
spec.license = "MIT"
|
9
9
|
spec.metadata = {"source_code_uri" => "https://github.com/nakilon/rasel"}
|
10
10
|
|
11
|
+
spec.add_dependency "webrick"
|
12
|
+
spec.add_dependency "sinatra"
|
11
13
|
spec.add_development_dependency "minitest-around"
|
12
14
|
spec.add_development_dependency "ruby-prof" unless ENV["CI"]
|
13
15
|
|
16
|
+
spec.files = %w{ LICENSE rasel.gemspec lib/rasel.rb
|
17
|
+
bin/rasel bin/rasel-annotated bin/rasel-convert bin/rasel-ide }
|
18
|
+
spec.executables = %w{ rasel rasel-annotated rasel-convert rasel-ide }
|
14
19
|
spec.bindir = "bin"
|
15
|
-
spec.executable = "rasel"
|
16
|
-
spec.test_file = "test.rb"
|
17
|
-
spec.files = %w{ LICENSE rasel.gemspec lib/rasel.rb bin/rasel }
|
18
20
|
end
|
metadata
CHANGED
@@ -1,15 +1,43 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rasel
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.1.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Victor Maslov aka Nakilon
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-
|
11
|
+
date: 2021-08-25 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: webrick
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: sinatra
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
13
41
|
- !ruby/object:Gem::Dependency
|
14
42
|
name: minitest-around
|
15
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -42,14 +70,19 @@ description:
|
|
42
70
|
email: nakilon@gmail.com
|
43
71
|
executables:
|
44
72
|
- rasel
|
73
|
+
- rasel-annotated
|
74
|
+
- rasel-convert
|
75
|
+
- rasel-ide
|
45
76
|
extensions: []
|
46
77
|
extra_rdoc_files: []
|
47
78
|
files:
|
48
79
|
- LICENSE
|
49
80
|
- bin/rasel
|
81
|
+
- bin/rasel-annotated
|
82
|
+
- bin/rasel-convert
|
83
|
+
- bin/rasel-ide
|
50
84
|
- lib/rasel.rb
|
51
85
|
- rasel.gemspec
|
52
|
-
- test.rb
|
53
86
|
homepage:
|
54
87
|
licenses:
|
55
88
|
- MIT
|
@@ -75,5 +108,4 @@ rubygems_version: 2.5.2.3
|
|
75
108
|
signing_key:
|
76
109
|
specification_version: 4
|
77
110
|
summary: Random Access Stack Esoteric Language
|
78
|
-
test_files:
|
79
|
-
- test.rb
|
111
|
+
test_files: []
|
data/test.rb
DELETED
@@ -1,169 +0,0 @@
|
|
1
|
-
require "minitest/autorun"
|
2
|
-
require "minitest/around/spec"
|
3
|
-
require "timeout"
|
4
|
-
|
5
|
-
require_relative "lib/rasel"
|
6
|
-
|
7
|
-
# TODO: assert that all RASEL() return with 0 exit code unless the opposite is expected
|
8
|
-
|
9
|
-
describe "bin" do
|
10
|
-
around{ |test| Timeout.timeout(RUBY_PLATFORM == "java" ? 4 : 1){ test.call } }
|
11
|
-
describe "executable" do
|
12
|
-
require "open3"
|
13
|
-
[
|
14
|
-
["cat.rasel < examples/cat.rasel", 0, File.read("examples/cat.rasel")],
|
15
|
-
["helloworld.rasel", 0, "Hello, World!\n"],
|
16
|
-
["naive_if_zero.rasel", 0, "false\nfalse\ntrue\nfalse\nfalse\n"],
|
17
|
-
["short_if_zero.rasel", 0, "false\nfalse\ntrue\nfalse\nfalse\n"],
|
18
|
-
["factorial.rasel", 0, "120 ", "echo 5 | "],
|
19
|
-
["fibonacci.rasel", 5, "3 ", "echo 5 | "],
|
20
|
-
["project_euler/1.rasel", 0, "233168 ", "echo 1000 | "],
|
21
|
-
].each do |cmd, expected_status, expected_stdout, prefix|
|
22
|
-
it cmd do
|
23
|
-
string, status = Open3.capture2e "#{prefix}./bin/rasel examples/#{cmd}"
|
24
|
-
assert_equal [expected_status, expected_stdout], [status.exitstatus, string]
|
25
|
-
end
|
26
|
-
end
|
27
|
-
end
|
28
|
-
end
|
29
|
-
|
30
|
-
describe "lib" do
|
31
|
-
around{ |test| Timeout.timeout(1){ test.call } }
|
32
|
-
def assert_stack expectation, *args
|
33
|
-
result = RASEL *args
|
34
|
-
assert_equal expectation, [*result.stack, result.exitcode]
|
35
|
-
end
|
36
|
-
|
37
|
-
describe "non-instructional" do
|
38
|
-
it "trim spaces and empty lines" do
|
39
|
-
assert_stack [64, 32, 118],
|
40
|
-
" v @\n" +
|
41
|
-
"\n" +
|
42
|
-
" > v\n" +
|
43
|
-
" \"\n" +
|
44
|
-
"\n \n\n"
|
45
|
-
assert_stack [1, 2],
|
46
|
-
' v@ '"\n"\
|
47
|
-
' 2 '"\n"\
|
48
|
-
' @ '"\n"\
|
49
|
-
'@v># '"\n"\
|
50
|
-
' >1v '"\n"\
|
51
|
-
' # '"\n"\
|
52
|
-
' '
|
53
|
-
end
|
54
|
-
it "exit status code 255 on invalid character" do
|
55
|
-
assert_equal 255, RASEL("Ы").exitcode
|
56
|
-
end
|
57
|
-
it "print to STDOUT" do
|
58
|
-
# TODO: mock
|
59
|
-
begin
|
60
|
-
STDOUT, stdout = StringIO.new, STDOUT
|
61
|
-
RASEL ",@", STDOUT
|
62
|
-
ensure
|
63
|
-
STDOUT, stdout = stdout, STDOUT
|
64
|
-
end
|
65
|
-
assert_equal ?\0, stdout.string
|
66
|
-
end
|
67
|
-
it "print to StringIO" do
|
68
|
-
string = StringIO.new
|
69
|
-
RASEL ",@", string
|
70
|
-
assert_equal ?\0, string.string
|
71
|
-
end
|
72
|
-
end
|
73
|
-
|
74
|
-
describe "instructional" do
|
75
|
-
|
76
|
-
describe "old" do
|
77
|
-
it '"' do assert_stack ["@".ord], '"@' end
|
78
|
-
it '#' do assert_stack [1], '##1@' end
|
79
|
-
it '#"Ы' do assert_stack [64, 208, 171, 35, 64], '#@"@Ы' end
|
80
|
-
it '#><^vЫ' do
|
81
|
-
assert_stack [1, 2, 3],
|
82
|
-
<<~HEREDOC
|
83
|
-
1v@
|
84
|
-
v>2\#
|
85
|
-
>3v
|
86
|
-
\#
|
87
|
-
Ы
|
88
|
-
HEREDOC
|
89
|
-
end
|
90
|
-
it "0..9, A..Z" do assert_stack [*0..35], "#{[*0..9].join}#{[*?A..?Z].join}@" end
|
91
|
-
it "$" do assert_stack [1], "$12$@" end
|
92
|
-
it ":" do assert_stack [0, 0, 1, 1], ":1:@" end
|
93
|
-
it "><^v" do
|
94
|
-
assert_stack [1, 2, 3, 4],
|
95
|
-
<<~HEREDOC
|
96
|
-
<@^1
|
97
|
-
3v>
|
98
|
-
5425
|
99
|
-
HEREDOC
|
100
|
-
end
|
101
|
-
it "-" do assert_stack [-90000, 0], "--"+"9-"*10000+"0@" end
|
102
|
-
it "/" do assert_stack [0, 0, 0.5, 1, 2], "//10/12/22/21/@" end
|
103
|
-
it "%" do assert_stack [0, 0, 1, 0, 0], "%%10%12%22%21%@" end
|
104
|
-
it "/-" do assert_equal [-0.5], RASEL("1-2/0@").stack end
|
105
|
-
it "%-" do assert_equal [1], RASEL("1-2%0@").stack end
|
106
|
-
it ".-" do assert_equal "0 10 255 0 ", RASEL(".A."+"5-"*51+"-..@").stdout.string end
|
107
|
-
it ",-" do assert_equal "\x00\x0A\xFF\x00".b, RASEL(",A,"+"5-"*51+"-,,@").stdout.string end
|
108
|
-
it ".- negative float" do assert_equal "-0.3333333333333333 ", RASEL("13/-.@").stdout.string end
|
109
|
-
it ",- errors" do
|
110
|
-
assert_equal 255, RASEL("1-,@").exitcode
|
111
|
-
assert_equal 255, RASEL("GG*,@").exitcode
|
112
|
-
assert_equal 255, RASEL("12/,@").exitcode
|
113
|
-
end
|
114
|
-
end
|
115
|
-
|
116
|
-
describe "changed" do
|
117
|
-
it "@" do
|
118
|
-
assert_equal 0, RASEL("@").exitcode
|
119
|
-
assert_equal 2, RASEL("2@").exitcode
|
120
|
-
assert_equal 255, RASEL("2-@").exitcode
|
121
|
-
assert_equal 255, RASEL("12/@").exitcode
|
122
|
-
end
|
123
|
-
it "~" do
|
124
|
-
assert_stack [1], "~1@2", StringIO.new, StringIO.new
|
125
|
-
assert_stack [0, 10, 255, 0], "~0~0~0~0@", StringIO.new,
|
126
|
-
StringIO.new.tap{ |s| [0, 10, 255, 0].reverse_each &s.method(:ungetbyte) }
|
127
|
-
end
|
128
|
-
it "&" do
|
129
|
-
assert_stack [1], "&1@2", StringIO.new, StringIO.new
|
130
|
-
[0, 10, 255].each do |c|
|
131
|
-
assert_stack [12, 34, c, 56], "&0&0~0&0@", StringIO.new,
|
132
|
-
StringIO.new.tap{ |s| "#{c.chr}-12#{c.chr}-34#{c.chr}-56".bytes.reverse_each &s.method(:ungetbyte) }
|
133
|
-
end
|
134
|
-
end
|
135
|
-
[
|
136
|
-
[[1], ""],
|
137
|
-
[[0], "1"],
|
138
|
-
[[1], "1-"],
|
139
|
-
[[0], "12/"],
|
140
|
-
[[1], "12/-"],
|
141
|
-
].each do |expectation, code|
|
142
|
-
it "? #{code}" do
|
143
|
-
assert_stack expectation, "#{code}?1@"
|
144
|
-
assert_stack expectation, "v#{code}?1@".chars.zip.transpose.join(?\n)
|
145
|
-
assert_stack expectation, "<@1?#{code.reverse}"
|
146
|
-
assert_stack expectation, "^@1?#{code.reverse}".chars.zip.transpose.join(?\n)
|
147
|
-
end
|
148
|
-
end
|
149
|
-
it "\\" do assert_stack [2, 0, 4, 0], "4321001-02-\\\\\\\\\\@" end
|
150
|
-
it "exit status code 255 on non-integer \\ index" do
|
151
|
-
assert_equal 0, RASEL("01/\\@").exitcode
|
152
|
-
assert_equal 255, RASEL("12/\\@").exitcode
|
153
|
-
assert_equal 255, RASEL("12/-\\@").exitcode
|
154
|
-
end
|
155
|
-
end
|
156
|
-
|
157
|
-
describe "new" do
|
158
|
-
# TODO: non-instructional-like tests about jumping over spaces on the edges
|
159
|
-
it "j" do assert_stack [1], "j1@" end
|
160
|
-
it 'j"' do assert_stack [5, 6], '3j2"456@' end
|
161
|
-
it "j-" do assert_stack [2, 3], "6-j123@" end
|
162
|
-
it "exit status code 255 on non-integer jump" do
|
163
|
-
assert_equal 255, RASEL("12/j@").exitcode
|
164
|
-
end
|
165
|
-
end
|
166
|
-
|
167
|
-
end
|
168
|
-
|
169
|
-
end
|