spitewaste 0.1.001 → 0.1.006
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile +1 -6
- data/LICENSE +13 -0
- data/README.md +2 -5
- data/Rakefile +75 -2
- data/TUTORIAL.md +1 -1
- data/bin/spw +1 -0
- data/lib/spitewaste.rb +1 -1
- data/lib/spitewaste/assembler.rb +4 -4
- data/lib/spitewaste/cli.rb +21 -19
- data/lib/spitewaste/cli/asm.rb +2 -0
- data/lib/spitewaste/cli/compile.rb +2 -0
- data/lib/spitewaste/cli/convert.rb +2 -0
- data/lib/spitewaste/cli/docs.rb +51 -0
- data/lib/spitewaste/cli/exec.rb +12 -13
- data/lib/spitewaste/cli/image.rb +2 -0
- data/lib/spitewaste/emitters/assembly.rb +1 -1
- data/lib/spitewaste/libspw/array.spw +75 -20
- data/lib/spitewaste/libspw/bits.spw +70 -68
- data/lib/spitewaste/libspw/case.spw +34 -26
- data/lib/spitewaste/libspw/docs.json +1 -0
- data/lib/spitewaste/libspw/fun.spw +40 -24
- data/lib/spitewaste/libspw/io.spw +2 -0
- data/lib/spitewaste/libspw/math.spw +120 -79
- data/lib/spitewaste/libspw/prime.spw +3 -2
- data/lib/spitewaste/libspw/random.spw +2 -0
- data/lib/spitewaste/libspw/rational.spw +153 -0
- data/lib/spitewaste/libspw/stack.spw +54 -30
- data/lib/spitewaste/libspw/string.spw +228 -92
- data/lib/spitewaste/libspw/test.spw +3 -0
- data/lib/spitewaste/libspw/util.spw +97 -41
- data/lib/spitewaste/parsers/fucktional.rb +1 -0
- data/lib/spitewaste/parsers/spitewaste.rb +25 -19
- data/lib/spitewaste/version.rb +1 -1
- data/spitewaste.gemspec +17 -12
- metadata +64 -8
- data/demo/factorial-nicespace.png +0 -0
- data/demo/factorial.asm +0 -47
- data/demo/factorial.png +0 -0
- data/demo/factorial.wsa +0 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 17fcfab8a738ff0666343f1456c4608f0b721a7e8a1a1dc89887c18b258d538c
|
4
|
+
data.tar.gz: 7d923d804a932d6a055804a616ae6b518db37ca87006bf049ecd2a590477c10a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c2b108463c434fed7f09d5a6b922c0363cf979ed9ba89a3bd731dba0436e7ed39da71d9ffc76ad1f8f3c9b5ba3ad58405bf9777443e8d3f116a4eb7001d4ba44
|
7
|
+
data.tar.gz: 07e9108b800fa22b36cf033b57eb6d8fdc5df3a73e3d19e2385093ea9a5dc4fc91bfddb23620ab4df6cbe28dc17f24a3ce86d3bb5c88b156b5ffc223184376c9
|
data/Gemfile
CHANGED
data/LICENSE
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
2
|
+
Version 2, December 2004
|
3
|
+
|
4
|
+
Copyright (C) 2004 Sam Hocevar <sam@hocevar.net>
|
5
|
+
|
6
|
+
Everyone is permitted to copy and distribute verbatim or modified
|
7
|
+
copies of this license document, and changing it is allowed as long
|
8
|
+
as the name is changed.
|
9
|
+
|
10
|
+
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
11
|
+
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
12
|
+
|
13
|
+
0. You just DO WHAT THE FUCK YOU WANT TO.
|
data/README.md
CHANGED
@@ -1,7 +1,4 @@
|
|
1
|
-
<p align="center">
|
2
|
-
<img src="abc" /><br >
|
3
|
-
"Code should be invisible." — someone, surely
|
4
|
-
</p>
|
1
|
+
<p align="center">"Code should be invisible." — someone, surely</p>
|
5
2
|
|
6
3
|
# Spitewaste
|
7
4
|
|
@@ -21,7 +18,7 @@ Spitewaste is the pseudo-assembly language referred to throughout this document,
|
|
21
18
|
|
22
19
|
**spw** is a command-line utility whose primary purpose is to "lower" Spitewaste programs to Whitespace for execution by some external interpreter, but it's capable of several (perhaps too many) other transformations; see `spw help convert` for further details. In addition to losslessly converting between various representations, spw can generate pretty pictures of Whitespace code syntax-highlighted in your favorite color scheme via `spw image`.
|
23
20
|
|
24
|
-
Spitewaste is not [a Whitespace interpreter](
|
21
|
+
Spitewaste is not [a Whitespace interpreter](../../../spiceweight), but `spw exec` will convert its input to something your interpreter of choice should understand and ferry it along. Finally, `spw compile` will blindly translate Whitespace instructions to functionally equivalent C++ code and feed that into a compiler; the results aren't spectacular (compilers loathe `goto`), but it's fun to pretend Whitespace is a compiled language.
|
25
22
|
|
26
23
|
## Where <sub>can I get it?</sub>
|
27
24
|
|
data/Rakefile
CHANGED
@@ -1,10 +1,83 @@
|
|
1
1
|
require 'bundler/gem_tasks'
|
2
2
|
require 'rake/testtask'
|
3
|
+
require 'json'
|
3
4
|
|
4
5
|
Rake::TestTask.new(:test) do |t|
|
5
6
|
t.libs << 'test'
|
6
|
-
t.libs << 'lib'
|
7
7
|
t.test_files = FileList['test/*_test.rb']
|
8
8
|
end
|
9
9
|
|
10
|
-
task :
|
10
|
+
task test: :docs
|
11
|
+
|
12
|
+
desc 'Generate standard library documentation.'
|
13
|
+
task :docs do |t|
|
14
|
+
docs = {}
|
15
|
+
|
16
|
+
Dir.chdir('lib/spitewaste/libspw') do |d|
|
17
|
+
Dir['*.spw'].each do |path|
|
18
|
+
next if path['random']
|
19
|
+
lib = File.basename path, '.spw'
|
20
|
+
docs[lib] = extract_docs path
|
21
|
+
end
|
22
|
+
|
23
|
+
File.open('docs.json', ?w) { |f| JSON.dump docs, f }
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def extract_docs path
|
28
|
+
docs = {}
|
29
|
+
buffer = ''
|
30
|
+
|
31
|
+
File.open(path).each_line do |line|
|
32
|
+
if line.strip.empty?
|
33
|
+
buffer.clear
|
34
|
+
elsif line[/^([^_]\S+):/]
|
35
|
+
docs[$1] = parse_doc buffer.dup unless buffer.empty?
|
36
|
+
buffer.clear
|
37
|
+
elsif line[/^;/]
|
38
|
+
buffer << line
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
docs
|
43
|
+
end
|
44
|
+
|
45
|
+
def strpack s
|
46
|
+
s.bytes.zip(0..).sum { |b, e| b * 128 ** e }
|
47
|
+
end
|
48
|
+
|
49
|
+
def parse_stack s
|
50
|
+
s.scan(/"[^"]*"|\S+/).map { |v|
|
51
|
+
begin
|
52
|
+
Integer v
|
53
|
+
rescue
|
54
|
+
if v[/R\((-?\d+),(-?\d+)\)/]
|
55
|
+
n, d = $1.to_i, $2.to_i
|
56
|
+
s = (n<=>0) * (d<=>0)
|
57
|
+
(n.abs * 2**31 + d.abs) * 2 + (s == -1 ? 0 : 1)
|
58
|
+
else
|
59
|
+
strpack v.delete %('")
|
60
|
+
end
|
61
|
+
end
|
62
|
+
}
|
63
|
+
end
|
64
|
+
|
65
|
+
StackRx = /(?<=\[)[^\]]*(?=\])/ # match [.*], but don't capture the brackets
|
66
|
+
|
67
|
+
def parse_doc doc
|
68
|
+
# strip leading comment to margin and any implementation details (!)
|
69
|
+
doc.gsub!(/^; */, '').gsub!(/^!.+\n/, '')
|
70
|
+
|
71
|
+
head, specs = doc.split "\n\n"
|
72
|
+
*desc, effect = head.split ?\n
|
73
|
+
desc *= ?\n
|
74
|
+
|
75
|
+
cases = []
|
76
|
+
specs.scan(StackRx).each_slice 2 do |spec|
|
77
|
+
cases << spec.map { parse_stack _1 }
|
78
|
+
end if specs
|
79
|
+
|
80
|
+
{full: doc, desc: desc, effect: effect, cases: cases}
|
81
|
+
end
|
82
|
+
|
83
|
+
task default: :test
|
data/TUTORIAL.md
CHANGED
@@ -120,6 +120,6 @@ A standard library! Writing a Whitespace program from scratch can be a daunting
|
|
120
120
|
|
121
121
|
### Strings
|
122
122
|
|
123
|
-
Pretty much every program needs strings, and Spitewaste has 'em! But how? Well, a string is ultimately just a sequence of bytes, and we can use the power of exponents to smush multiple numbers into a single value. If we had the numbers 3, 9, and 4, we could easily encode them as a single base-10 number: 3 × 10<sup>2</sup> + 9 × 10<sup>1</sup> + 4 × 10<sup>0</sup
|
123
|
+
Pretty much every program needs strings, and Spitewaste has 'em! But how? Well, a string is ultimately just a sequence of bytes, and we can use the power of exponents to smush multiple numbers into a single value. If we had the numbers 3, 9, and 4, we could easily encode them as a single base-10 number: 3 × 10<sup>2</sup> + 9 × 10<sup>1</sup> + 4 × 10<sup>0</sup> = 394. Strings in Spitewaste are similarly encoded, except backwards (as an implementation detail) and in base-128 rather than decimal.
|
124
124
|
|
125
125
|
When you say <code>push "ABC"</code>, the assembler transforms it into <code>push 1106241</code>, the value of: 67 ('C') × 128<sup>2</sup> + 66 × 128<sup>1</sup> + 65 × 128<sup>0</sup>. Whitespace interpreters SHOULD (and many do) support arbitrarily large integers, so we don't have to worry about these representations not fitting into 64 bits. Once we're able to pass strings around as individual values, a plethora of possibilities opens up, and the standard library contains many string functions to reflect this.
|
data/bin/spw
CHANGED
data/lib/spitewaste.rb
CHANGED
data/lib/spitewaste/assembler.rb
CHANGED
@@ -9,7 +9,7 @@ module Spitewaste
|
|
9
9
|
raise ArgumentError, "unknown format for parse: #{format}"
|
10
10
|
end
|
11
11
|
|
12
|
-
|
12
|
+
parser =
|
13
13
|
case format
|
14
14
|
when :whitespace
|
15
15
|
WhitespaceParser
|
@@ -19,9 +19,9 @@ module Spitewaste
|
|
19
19
|
SpitewasteParser
|
20
20
|
end
|
21
21
|
|
22
|
-
parser =
|
23
|
-
@instructions = parser.instructions
|
24
|
-
@src = parser.src if format == :spitewaste
|
22
|
+
@parser = parser.new(program, **options).tap &:parse
|
23
|
+
@instructions = @parser.instructions
|
24
|
+
@src = @parser.src if format == :spitewaste
|
25
25
|
end
|
26
26
|
|
27
27
|
def assemble! format:, io: STDOUT, **options
|
data/lib/spitewaste/cli.rb
CHANGED
@@ -11,29 +11,31 @@ class SpitewasteCLI < Thor
|
|
11
11
|
exit 1
|
12
12
|
end
|
13
13
|
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
aliases: '-a'
|
28
|
-
|
29
|
-
class_option :coexist,
|
30
|
-
desc: <<DESC,
|
14
|
+
def self.shared_options
|
15
|
+
option :format,
|
16
|
+
desc: 'input format (in case auto-detection misguesses)',
|
17
|
+
aliases: '-f'
|
18
|
+
|
19
|
+
option :aliases,
|
20
|
+
desc: 'augment or override one or more of the default instruction mnemonics [WIP]',
|
21
|
+
banner: 'pop:drop...',
|
22
|
+
type: :array,
|
23
|
+
aliases: '-a'
|
24
|
+
|
25
|
+
option :coexist,
|
26
|
+
desc: <<DESC,
|
31
27
|
allow multiple mnemonics to refer to the same instruction where possible [WIP]
|
32
28
|
|
33
29
|
\e[1mNOTE: \e[0mIf --no-coexist (the default), aliases take precedence and render the original mnemonics invalid.
|
34
30
|
DESC
|
35
|
-
|
36
|
-
|
31
|
+
type: :boolean,
|
32
|
+
aliases: '-x'
|
33
|
+
end
|
34
|
+
|
35
|
+
# TODO: Figure out how to invoke a command from this class method.
|
36
|
+
def self.handle_no_command_error *args
|
37
|
+
exec $0, 'exec', *args
|
38
|
+
end
|
37
39
|
|
38
40
|
def self.validate_format options
|
39
41
|
if fmt = options[:format]&.to_sym and !VALID_FORMATS.include?(fmt)
|
data/lib/spitewaste/cli/asm.rb
CHANGED
@@ -3,6 +3,8 @@ class SpitewasteCLI
|
|
3
3
|
'Generate plain Whitespace assembly from INPUT and write it to OUTPUT.'
|
4
4
|
long_desc 'Mostly just a convenience wrapper around `spw convert in -o asm out`'
|
5
5
|
|
6
|
+
shared_options
|
7
|
+
|
6
8
|
def asm input, output = '/dev/stdout'
|
7
9
|
as = Spitewaste::Assembler.new File.read input
|
8
10
|
File.open(output, ?w) { |of| as.assemble! format: :assembly, io: of }
|
@@ -21,6 +21,8 @@ DESC
|
|
21
21
|
- use % to write to $HOME/.cache/spitewaste directory, where Spiceweight knows to look',
|
22
22
|
aliases: '-s'
|
23
23
|
|
24
|
+
shared_options
|
25
|
+
|
24
26
|
def convert input = '/dev/stdin', output = '/dev/stdout'
|
25
27
|
ext = File.extname output
|
26
28
|
Kernel.exec 'spw', 'image', input, output if ext == '.png'
|
@@ -0,0 +1,51 @@
|
|
1
|
+
def bold s; "\e[1m#{s}\e[0m" end
|
2
|
+
def under s; "\e[4m#{s}\e[0m" end
|
3
|
+
def hi s; "\e[7m#{s}\e[0m" end
|
4
|
+
|
5
|
+
DOCS = File.expand_path '../libspw/docs.json', __dir__
|
6
|
+
|
7
|
+
class SpitewasteCLI
|
8
|
+
desc 'docs [OPTIONS] [TERMS...]',
|
9
|
+
'Search for matching terms in the documentation of the standard library.'
|
10
|
+
|
11
|
+
option :show_specs,
|
12
|
+
desc: 'display the in-situ specs showing correct outputs for given inputs',
|
13
|
+
type: :boolean,
|
14
|
+
default: false,
|
15
|
+
aliases: '-s'
|
16
|
+
|
17
|
+
option :match_all,
|
18
|
+
desc: 'require that all provided search terms match instead of 1+',
|
19
|
+
type: :boolean,
|
20
|
+
default: false,
|
21
|
+
aliases: '-m'
|
22
|
+
|
23
|
+
def docs *terms
|
24
|
+
abort "need at least one search term" if terms.empty?
|
25
|
+
|
26
|
+
docs = JSON.load File.read DOCS
|
27
|
+
found = []
|
28
|
+
min = options[:match_all] ? terms.size : 1
|
29
|
+
|
30
|
+
docs.each do |lib, fns|
|
31
|
+
fns.each do |fn, data|
|
32
|
+
full = data['full']
|
33
|
+
ms = terms.count { |t| full[/#{t}/i] }
|
34
|
+
found << [lib, fn, full, ms] if ms >= min
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
abort "no matches for terms: #{terms}" if found.empty?
|
39
|
+
|
40
|
+
found.sort_by! { |v| -v[-1] }
|
41
|
+
|
42
|
+
IO.popen('less -R', 'w') do |io|
|
43
|
+
found.each do |lib, fn, full|
|
44
|
+
full.gsub! /#{terms * ?|}/i, &method(:hi)
|
45
|
+
desc, specs = full.split "\n\n"
|
46
|
+
io.puts "#{?- * 10}\n#{lib}/#{bold under fn} #{desc}\n\n"
|
47
|
+
io.puts specs if options[:show_specs]
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
data/lib/spitewaste/cli/exec.rb
CHANGED
@@ -16,28 +16,27 @@ class SpitewasteCLI
|
|
16
16
|
- use % to write to $HOME/.cache/spitewaste directory, where Spiceweight knows to look',
|
17
17
|
aliases: '-s'
|
18
18
|
|
19
|
-
|
19
|
+
shared_options
|
20
|
+
|
21
|
+
def exec input = '/dev/stdin'
|
20
22
|
fmt = SpitewasteCLI.validate_format options
|
21
23
|
|
22
|
-
raise LoadError, "No such file '#{
|
24
|
+
raise LoadError, "No such file '#{input}'", [] unless File.exists? input
|
23
25
|
|
24
26
|
opts = options.dup # options is frozen
|
25
27
|
if opts[:symbol_file] == '%'
|
26
|
-
opts[:symbol_file] = SpitewasteCLI.make_cache_path
|
28
|
+
opts[:symbol_file] = SpitewasteCLI.make_cache_path input
|
27
29
|
end
|
28
30
|
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
else
|
36
|
-
file
|
37
|
-
end
|
31
|
+
if File.extname(input) != '.ws'
|
32
|
+
io = Tempfile.new
|
33
|
+
as = Spitewaste::Assembler.new File.read(input), format: fmt, **opts
|
34
|
+
as.assemble! format: :whitespace, io: io
|
35
|
+
input = io.tap(&:close).path
|
36
|
+
end
|
38
37
|
|
39
38
|
cmd = options[:interpreter].split
|
40
|
-
cmd.map! { |c| c == '%' ?
|
39
|
+
cmd.map! { |c| c == '%' ? input : c }
|
41
40
|
Kernel.exec *cmd
|
42
41
|
end
|
43
42
|
end
|
data/lib/spitewaste/cli/image.rb
CHANGED
@@ -1,17 +1,57 @@
|
|
1
|
+
;;; Pseudo-arrays
|
2
|
+
|
3
|
+
; An "array" is really just a sequence of elements on the stack followed by
|
4
|
+
; the number of elements, so care must be taken to ensure one knows what kind
|
5
|
+
; of data is currently being operated on.
|
6
|
+
|
7
|
+
import math ; divmod, max, min, pos?
|
8
|
+
import stack ; bury, dig, ncopy, nslide, roll
|
9
|
+
import util ; dec, die!, eq
|
10
|
+
|
1
11
|
$amax = 1000
|
2
12
|
|
13
|
+
; returns the sum of the array A
|
14
|
+
; [A] => [sum]
|
15
|
+
;
|
16
|
+
; [3 1] => [3]
|
17
|
+
; [4 3 2 1 4] => [10]
|
18
|
+
; [-10 -5 7 5 10 5] => [7]
|
3
19
|
arysum: reduce (add) ret
|
4
20
|
|
21
|
+
; duplicates the array at the top of the stack
|
22
|
+
; [A] => [A A]
|
23
|
+
;
|
24
|
+
; [1 2 3 3] => [1 2 3 3 1 2 3 3]
|
25
|
+
; [7 50 10 2] => [7 50 10 2 50 10 2]
|
5
26
|
arydup:
|
6
27
|
dup $++ push -2 copy 1 store
|
7
28
|
times (push -2 load $-- :ncopy) ret
|
8
29
|
|
30
|
+
; removes the element at the end of the array A
|
31
|
+
; [A] => [A']
|
32
|
+
;
|
33
|
+
; [10 1] => [0]
|
34
|
+
; [1 2 3 4 4] => [1 2 3 3]
|
35
|
+
; [7 10 20 30 3] => [7 10 20 2]
|
9
36
|
arypop: slide 1 $-- ret
|
10
37
|
|
38
|
+
; removes the element at the beginning of the array A
|
39
|
+
; [A] => [A']
|
40
|
+
;
|
41
|
+
; [10 1] => [0]
|
42
|
+
; [1 2 3 4 4] => [2 3 4 3]
|
43
|
+
; [7 10 20 30 3] => [7 20 30 2]
|
11
44
|
aryshift: dup :dig $-- ret
|
12
45
|
|
46
|
+
; returns the concatenation of arrays A and B
|
47
|
+
; [A B] => [A+B]
|
48
|
+
;
|
49
|
+
; [1 2 3 3 4 5 6 3] => [1 2 3 4 5 6 6]
|
50
|
+
; [7 10 2 5 4 3 3] => [7 10 5 4 3 5]
|
13
51
|
arycat: dup $++ :roll add ret
|
14
52
|
|
53
|
+
;;;
|
54
|
+
|
15
55
|
arypack:
|
16
56
|
:arydup reduce (:max) $++ push -1 swap store
|
17
57
|
$-- times (push -1 load mul add)
|
@@ -26,36 +66,43 @@ aryunpack:
|
|
26
66
|
|
27
67
|
arylen: push $amax mod ret
|
28
68
|
|
29
|
-
|
30
|
-
|
69
|
+
; returns the index I of element E in array A (or -1 if not found)
|
70
|
+
; [A E] => [I]
|
71
|
+
;
|
72
|
+
; [1 2 3 4 4 2] => [1]
|
73
|
+
; [1 2 3 4 4 4] => [3]
|
74
|
+
; [1 2 3 4 4 5] => [-1]
|
31
75
|
aryindex: copy 1 ; two copies of length, one gets decremented, index is diff
|
32
|
-
_aryindex_loop:
|
33
|
-
dup jn _aryindex_notfound
|
76
|
+
_aryindex_loop: dup jn _aryindex_notfound
|
34
77
|
dup push 2 add :roll copy 2 :eq jz _aryindex_no
|
35
78
|
copy 2 copy 1 sub swap $++ :nslide ret
|
36
|
-
|
37
79
|
_aryindex_no: $-- jump _aryindex_loop
|
38
|
-
|
39
80
|
_aryindex_notfound: slide 1 ret
|
40
81
|
|
41
|
-
|
42
|
-
|
43
|
-
;
|
44
|
-
|
82
|
+
; returns the element E at index I in array A (or dies on out of bounds)
|
83
|
+
; [A I] => [E]
|
84
|
+
;
|
85
|
+
; [1 2 3 4 4 0] => [1]
|
86
|
+
; [1 2 3 4 4 2] => [3]
|
87
|
+
; [1 2 3 4 4 -1] => [4] negative index counts from the end
|
88
|
+
aryat:
|
45
89
|
dup jn _aryat_neg
|
46
90
|
copy 1 swap sub dup :pos? jz _aryat_oob
|
47
91
|
:roll swap $-- :nslide ret
|
48
|
-
|
49
92
|
_aryat_neg: copy 1 add dup jn _aryat_oob jump aryat
|
50
|
-
|
51
|
-
|
52
|
-
|
93
|
+
_aryat_oob: push "(aryat) index out of bounds!" :die!
|
94
|
+
|
95
|
+
; returns the minimum and maximum elements of array A
|
96
|
+
; [A] => [min max]
|
97
|
+
;
|
98
|
+
; [4 3 2 1 4] => [1 4]
|
99
|
+
; [6 8 -3 4 0 5] => [-3 8]
|
100
|
+
; [7 1] => [7 7]
|
53
101
|
minmax:
|
54
|
-
:arydup reduce (:max)
|
55
|
-
push -1 swap store
|
102
|
+
:arydup reduce (:max) push -1 swap store
|
56
103
|
reduce (:min) push -1 load ret
|
57
104
|
|
58
|
-
;;;
|
105
|
+
;;; TODO: make sort not atrociously inefficient
|
59
106
|
|
60
107
|
sort: push -3 copy 1 store ; preserve length
|
61
108
|
_sort_loop:
|
@@ -66,17 +113,25 @@ _sort_loop:
|
|
66
113
|
push 0 copy 1 sub jn _sort_loop
|
67
114
|
push 3 sub load ret
|
68
115
|
|
116
|
+
; reverses the array A
|
117
|
+
; [A] => [A']
|
118
|
+
;
|
119
|
+
; [1 2 3 3] => [3 2 1 3]
|
120
|
+
; [7 1] => [7 1]
|
121
|
+
; [5 4 3 2 1 5] => [1 2 3 4 5 5]
|
69
122
|
aryrev: push -3 copy 1 store
|
70
123
|
_aryrev_loop:
|
71
124
|
swap copy 1 :bury $--
|
72
125
|
push 0 copy 1 sub jn _aryrev_loop
|
73
126
|
push 3 sub load ret
|
74
127
|
|
75
|
-
|
76
|
-
|
128
|
+
; returns the array A replicated N times
|
129
|
+
; ! N must be greater than 1
|
130
|
+
; [A N] => [A']
|
131
|
+
;
|
132
|
+
; [3 2 1 3 4] => [3 2 1 3 2 1 3 2 1 3 2 1 12]
|
77
133
|
aryrep: push -1 swap push -3 copy 1 store store
|
78
134
|
_aryrep_loop:
|
79
135
|
push -1 dup :dec load jz _aryrep_done
|
80
136
|
:arydup jump _aryrep_loop
|
81
|
-
|
82
137
|
_aryrep_done: push -3 load $-- times (:arycat) ret
|