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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 587ff1f458d557603238203069109e7515486684
4
- data.tar.gz: 847fabdaaf7016f1b8992004ab1fc7b965e4addb
3
+ metadata.gz: b5abd1a7bf87953fd9c52ed5362369df0b553236
4
+ data.tar.gz: 3cbe087b724031a6a18c8e1cca4d4e86f97dd4ef
5
5
  SHA512:
6
- metadata.gz: 6594fb5176f5cf7e21d06bbc5cc1f376ad97917d1ff6a844ba850a5930350c893886176a1c5df904cbf6f212864ce77a138249a62ad934e66937affc48ef0633
7
- data.tar.gz: 566dbc8e76fe9afa54a181051981a5f67f8009c75e67eb255f42caaf8086b10d43968ea54123173604b785717873e2473514970bc91fe780c3c4281404ceae5e
6
+ metadata.gz: 96191f88b08d9133b083e078aa62f6e5c99b6c052ce01bce82891c0d0f808d21ac84e3afd01cab7ba5b56706c3b6b044f5ee329c43bf8cd7462c7955f7ac7fbd
7
+ data.tar.gz: 6bb8862d98584c2241c4d19c1d6f23b1da7b3936eb0f1b654c6c6b3dc10f5e5e9187d88ce0ecfac31c75dc1abc1557a80ca5957ee32785a51a6203c890b8a987
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+ Signal.trap(:INT){ abort "\n(interrupted by SIGINT)" }
3
+
4
+ require_relative "../lib/rasel"
5
+ require "json"
6
+ exit RASEL(JSON.load(ARGF.read), STDOUT).exitcode
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(&quot;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&quot;)
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 ENV.key?("DEBUG_HISTORY") && code[y][x] == 32
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 ENV.key? "DEBUG_HISTORY"
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
- error = Proc.new{ return Struct.new(:stdout, :stack, :exitcode).new stdout, stack, 255 }
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 ENV.key? "DEBUG"
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 Struct.new(:stdout, :stack, :exitcode).new stdout, stack, (
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, ?A..?Z ; stack.push char.to_i 36
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 ?: ; stack.concat [pop[]] * 2
57
- when ?- ; stack.push -(pop[] - pop[])
58
- when ?/ ; b, a = pop[], pop[]; stack.push (b.zero? ? 0 : Rational(a) / b)
59
- when ?% ; b, a = pop[], pop[]; stack.push (b.zero? ? 0 : Rational(a) % b)
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[] if 1 != t.denominator
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[] : _ < 0 || _ > 255 ? error[] : _.to_i.chr}"
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[] if 1 != t.denominator
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.0.0"
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.0.0
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-05-25 00:00:00.000000000 Z
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