spitewaste 0.1.002 → 0.1.007
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/Rakefile +30 -11
- 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/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 +83 -45
- data/lib/spitewaste/libspw/prime.spw +36 -35
- 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/spitewaste.rb +23 -17
- data/lib/spitewaste/version.rb +1 -1
- data/spitewaste.gemspec +17 -12
- metadata +63 -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: 266085412e216eb7705988b1832d0d409db6fccbd67f598dcfe2de5138bad9b3
|
4
|
+
data.tar.gz: 2155b872a192198984f4d8476b70845f6ca8cb040d65c5324edd42bf7b75fc3f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b9c9b8f395fafda61356d2ce1dc8dce7637fb11e4e92851bf670355fc27b90e0c2f83fb48a1241c4100a6d61af29361677ae4605fb02e336b8bc5e123a97d0ad
|
7
|
+
data.tar.gz: 973ee87e4e313d8e7aa3eda6b87740d104112db2f06ae216d6d005ea2c31752bfe5dfadb764bcda88b8f2753026811d22da82dacea5e715bd7ee6bc097f96064
|
data/Gemfile
CHANGED
data/Rakefile
CHANGED
@@ -7,14 +7,21 @@ Rake::TestTask.new(:test) do |t|
|
|
7
7
|
t.test_files = FileList['test/*_test.rb']
|
8
8
|
end
|
9
9
|
|
10
|
-
|
10
|
+
task test: :docs
|
11
|
+
|
12
|
+
desc 'Generate standard library documentation.'
|
11
13
|
task :docs do |t|
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
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 }
|
16
24
|
end
|
17
|
-
File.open('docs.json', ?w) { |f| JSON.dump all_docs, f }
|
18
25
|
end
|
19
26
|
|
20
27
|
def extract_docs path
|
@@ -35,19 +42,31 @@ def extract_docs path
|
|
35
42
|
docs
|
36
43
|
end
|
37
44
|
|
38
|
-
StackRx = /(?<=\[)[^\]]+(?=\])/
|
39
|
-
|
40
45
|
def strpack s
|
41
46
|
s.bytes.zip(0..).sum { |b, e| b * 128 ** e }
|
42
47
|
end
|
43
48
|
|
44
49
|
def parse_stack s
|
45
|
-
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
|
+
}
|
46
63
|
end
|
47
64
|
|
65
|
+
StackRx = /(?<=\[)[^\]]*(?=\])/ # match [.*], but don't capture the brackets
|
66
|
+
|
48
67
|
def parse_doc doc
|
49
|
-
# strip comment
|
50
|
-
doc.gsub!(
|
68
|
+
# strip leading comment to margin and any implementation details (!)
|
69
|
+
doc.gsub!(/^; */, '').gsub!(/^!.+\n/, '')
|
51
70
|
|
52
71
|
head, specs = doc.split "\n\n"
|
53
72
|
*desc, effect = head.split ?\n
|
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
|