rasel 1.0.0 → 1.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|