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 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