spitewaste 0.1.003 → 0.1.004

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
  SHA256:
3
- metadata.gz: 0b4f49809a688762a3b50dab64b93163e493aa75a33d2565b9892ec5ad8c9bc0
4
- data.tar.gz: 6a06768b5a4edcf18ca0948900effd3c3fb01fbdea2e2da8d29080036870614f
3
+ metadata.gz: 5e6953a0755edbb33708f3cdc1ebeb33cdb6a47c1f3d6335716d25f693dec56c
4
+ data.tar.gz: 130d57daadcee3de1a465e4be9b28bba16d70c4dafa87c59195696be06e49a7c
5
5
  SHA512:
6
- metadata.gz: 99cc02efffe8e2ddecf76b1b8578c4eb5e3a6c96b0cf1d46d063b9268507120a718835822f906739962a0b36262ae08328c610db3b37b0a777b3ff84d5e2df02
7
- data.tar.gz: fa3425ae1c074a9a34f78bf110a7d8bb645b15205bb78f68787060e61c018cf63925c2c32d67bbf5ab0d4552c37727555dfcdd384824af2b6087c665bf969233
6
+ metadata.gz: b1f2075fb27a9ecf5599365cf029bfeebacfda5208a5df357ea94de00465111f61d42cc58a5220e16de710dfe4c778acf911e65ab3d86459c7039cee37f011a6
7
+ data.tar.gz: ea2003b53fe38c7fa092b2c43c383706980009fd1efa54f5733e40937b180fd76ea08e973dd5e16835fab1854f075add855af1957357aeab391df0dab6945e92
data/Rakefile CHANGED
@@ -7,15 +7,20 @@ Rake::TestTask.new(:test) do |t|
7
7
  t.test_files = FileList['test/*_test.rb']
8
8
  end
9
9
 
10
- desc "Generate standard library documentation."
10
+ task test: :docs
11
+
12
+ desc 'Generate standard library documentation.'
11
13
  task :docs do |t|
12
- all_docs = {}
13
- Dir['lib/spitewaste/libspw/math*'].each do |path|
14
- lib = File.basename path, '.spw'
15
- all_docs[lib] = extract_docs path
16
- end
14
+ docs = {}
15
+
16
+ Dir.chdir('lib/spitewaste/libspw') do |d|
17
+ %w[rational.spw array.spw stack.spw util.spw string.spw math.spw].each do |path|
18
+ lib = File.basename path, '.spw'
19
+ docs[lib] = extract_docs path
20
+ end
17
21
 
18
- File.open('docs.json', ?w) { |f| JSON.dump all_docs, f }
22
+ File.open('docs.json', ?w) { |f| JSON.dump docs, f }
23
+ end
19
24
  end
20
25
 
21
26
  def extract_docs path
@@ -41,12 +46,24 @@ def strpack s
41
46
  end
42
47
 
43
48
  def parse_stack s
44
- s.split.map { Integer(_1) rescue strpack _1.delete %('") }
49
+ s.scan(/"[^"]*"|\S+/).map { |v|
50
+ begin
51
+ Integer v
52
+ rescue
53
+ if v[/R\((-?\d+),(-?\d+)\)/]
54
+ n, d = $1.to_i, $2.to_i
55
+ s = (n<=>0) * (d<=>0)
56
+ (n.abs * 2**31 + d.abs) * 2 + (s == -1 ? 0 : 1)
57
+ else
58
+ strpack v.delete %('")
59
+ end
60
+ end
61
+ }
45
62
  end
46
63
 
47
- def parse_doc doc
48
- StackRx = /(?<=\[)[^\]]+(?=\])/ # match [.+], but don't capture the brackets
64
+ StackRx = /(?<=\[)[^\]]*(?=\])/ # match [.*], but don't capture the brackets
49
65
 
66
+ def parse_doc doc
50
67
  # strip comment character and any implementation details
51
68
  doc.gsub!(/; */, '').gsub!(/^!.+\n/, '')
52
69
 
@@ -17,7 +17,7 @@ module Spitewaste
17
17
  black = program.size - white
18
18
 
19
19
  return :whitespace if white > black
20
- program[/[^-\w\s]/] ? :spitewaste : :assembly # TODO: something more robust?
20
+ program[/import|[^-\w\s]/] ? :spitewaste : :assembly
21
21
  end
22
22
  end
23
23
 
@@ -16,28 +16,25 @@ class SpitewasteCLI
16
16
  - use % to write to $HOME/.cache/spitewaste directory, where Spiceweight knows to look',
17
17
  aliases: '-s'
18
18
 
19
- def exec file = '/dev/stdin'
19
+ def exec input = '/dev/stdin'
20
20
  fmt = SpitewasteCLI.validate_format options
21
21
 
22
- raise LoadError, "No such file '#{file}'", [] unless File.exists? file
22
+ raise LoadError, "No such file '#{input}'", [] unless File.exists? input
23
23
 
24
24
  opts = options.dup # options is frozen
25
25
  if opts[:symbol_file] == '%'
26
- opts[:symbol_file] = SpitewasteCLI.make_cache_path file
26
+ opts[:symbol_file] = SpitewasteCLI.make_cache_path input
27
27
  end
28
28
 
29
- path =
30
- if File.extname(file) != '.ws'
31
- io = Tempfile.new
32
- as = Spitewaste::Assembler.new File.read(file), format: fmt, **opts
33
- as.assemble! format: :whitespace, io: io
34
- io.tap(&:close).path
35
- else
36
- file
37
- end
29
+ if File.extname(input) != '.ws'
30
+ io = Tempfile.new
31
+ as = Spitewaste::Assembler.new File.read(input), format: fmt, **opts
32
+ as.assemble! format: :whitespace, io: io
33
+ input = io.tap(&:close).path
34
+ end
38
35
 
39
36
  cmd = options[:interpreter].split
40
- cmd.map! { |c| c == '%' ? path : c }
37
+ cmd.map! { |c| c == '%' ? input : c }
41
38
  Kernel.exec *cmd
42
39
  end
43
40
  end
@@ -1,3 +1,7 @@
1
+ import math ; divmod, max, min, pos?
2
+ import stack ; bury, dig, ncopy, nslide, roll
3
+ import util ; dec, die!, eq
4
+
1
5
  $amax = 1000
2
6
 
3
7
  arysum: reduce (add) ret
@@ -48,7 +52,7 @@ aryat: ; [a i]
48
52
 
49
53
  _aryat_neg: copy 1 add dup jn _aryat_oob jump aryat
50
54
 
51
- _aryat_oob: push "(aryat) index out of bounds!" :die
55
+ _aryat_oob: push "(aryat) index out of bounds!" :die!
52
56
 
53
57
  minmax:
54
58
  :arydup reduce (:max)
@@ -1,6 +1,8 @@
1
- import math
1
+ import math ; ilog, pow
2
+ import string ; strcat, strlen, strrev
3
+ import util ; bin, digits, itos
2
4
 
3
- ; TODO: don't use strings for bitwise operations
5
+ ; TODO: don't use strings for bitwise operations!
4
6
 
5
7
  ; bitwise bitwise_and
6
8
  ; ! clobbers heap address -1
@@ -1,4 +1,6 @@
1
- import bits
1
+ import bits ; bitwise_and, bitwise_or, bitwise_xor
2
+ import string ; isalpha, strcat, strunpack
3
+
2
4
  ;;; Case conversion
3
5
 
4
6
  upcase: :strunpack push 0 ; tally
@@ -1,4 +1,8 @@
1
- import string
1
+ import array ; sort
2
+ import case ; upcase
3
+ import stack ; to_a
4
+ import string ; isalpha, strcat, strjoin, strrev, strunpack
5
+ import util ; eq
2
6
 
3
7
  palindrome?: dup :strrev :eq ret
4
8
 
@@ -1,3 +1,5 @@
1
+ import string ; strcat, strpack, strrev, strunpack
2
+
1
3
  ; prints the character at the top of the stack until terminating zero
2
4
  print: :strunpack
3
5
  _print_loop:
@@ -1,4 +1,5 @@
1
- import util ; range for factorial, inc for ilog
1
+ import array ; arycat, arydup
2
+ import util ; eq, inc, range
2
3
 
3
4
  ; returns B raised to the power E
4
5
  ; [B E] => [B**E]
@@ -41,79 +42,116 @@ _isqrt_update: slide 1 jump _isqrt_loop
41
42
  ; ! clobbers heap address -1 TODO: maybe unnecessarily?
42
43
  ; [N B] => [logB(N)]
43
44
  ;
44
- ; [15 4] => [1], [16 4] => [2]
45
+ ; [15 4] => [1]
46
+ ; [16 4] => [2]
45
47
  ; [100 10] => [2]
46
- ; [42 2] => [5]
48
+ ; [42 2] => [5]
47
49
  ilog: push -1,0 store ; accumulator at -1
48
- _ilog_loop:
49
- swap dup jz _ilog_done
50
- push -1 :inc
51
- copy 1 div swap jump _ilog_loop
50
+ _ilog_loop: ; [n b]
51
+ swap copy 1 div dup jz _ilog_done
52
+ push -1 :inc swap jump _ilog_loop
52
53
  _ilog_done: push -1 load slide 2 ret
53
54
 
54
- ; greatest common divisor
55
- gcd: ; [a b]
56
- dup jz _gcd_done
57
- swap copy 1 mod
58
- jump gcd
59
-
55
+ ; returns the greatest common divisor of A and B
56
+ ; [A B] => [gcd(A, B)]
57
+ ;
58
+ ; [6 9] => [3]
59
+ ; [13 17] => [1]
60
+ ; [24 36] => [12]
61
+ gcd: dup jz _gcd_done swap copy 1 mod jump gcd
60
62
  _gcd_done: pop ret
61
63
 
62
- ; least common multiple
63
- lcm: ; [a b]
64
- copy 1 mul
65
- swap dup copy 2 swap div
66
- :gcd div ret
67
-
68
- ;;;
64
+ ; returns the least common multiple of A and B
65
+ ; [A B] => [lcm(A, B)]
66
+ ;
67
+ ; [6 9] => [18]
68
+ ; [13 17] => [221]
69
+ ; [24 36] => [72]
70
+ lcm: copy 1 mul swap dup copy 2 swap div :gcd div ret
69
71
 
70
- min: ; [a b]
71
- copy 1 copy 1 sub jn _min_done swap
72
+ ; keeps the minimum of the top two stack values
73
+ ; [A B] => [A < B ? A : B]
74
+ ;
75
+ ; [3 1] => [1]
76
+ ; [2 4] => [2]
77
+ min: copy 1 copy 1 sub jn _min_done swap
72
78
  _min_done: pop ret
73
79
 
74
- max: ; [a b]
75
- copy 1 copy 1 sub jn _max_done swap
80
+ ; keeps the maximum of the top two stack values
81
+ ; [A B] => [A > B ? A : B]
82
+ ;
83
+ ; [3 1] => [3]
84
+ ; [2 4] => [4]
85
+ max: copy 1 copy 1 sub jn _max_done swap
76
86
  _max_done: slide 1 ret
77
87
 
78
- ;;;
79
-
80
- ; -1, 0, or 1 to indicate the sign of the argument
81
- sign: ; [n]
82
- dup jz _sign_zero
83
- jn _sign_neg
84
- push 1 ret
88
+ ; returns -1, 0, or 1 to indicate the sign of N
89
+ ; [N] => [-1 | 0 | 1]
90
+ ;
91
+ ; [17] => [1]
92
+ ; [-25] => [-1]
93
+ ; [0] => [0]
94
+ sign: dup jz _sign_zero jn _sign_neg push 1 ret
85
95
  _sign_zero: ret
86
96
  _sign_neg: push -1 ret
87
97
 
88
- ; absolute value ; [n] => [abs(n)]
98
+ ; returns the absolute value of N
99
+ ; [N] => [N < 0 ? -N : N]
100
+ ;
101
+ ; [-5] => [5]
102
+ ; [10] => [10]
103
+ ; [0] => [0]
89
104
  abs: dup :sign mul ret
90
105
 
91
- ;;;
92
-
106
+ ; pops A and B and pushes both their quotient and modulus
107
+ ; [A B] => [A/B A%B]
108
+ ;
109
+ ; [17 5] => [3 2]
110
+ ; [42 6] => [7 0]
111
+ ; [ 1 5] => [0 1]
112
+ ; ! [9 0] => [!!] TODO: find a way to expect exceptions
93
113
  divmod:
94
114
  push -1 swap store
95
115
  dup push -1 load div
96
116
  swap push -1 load mod ret
97
117
 
98
- ;;;
99
-
118
+ ; returns whether N is greater than 0
119
+ ; [N] => [N > 0]
120
+ ;
121
+ ; [5] => [1] [-3] => [0] [0] => [0]
100
122
  pos?: :sign push 1 :eq ret
101
- neg?: :sign push -1 :eq ret
102
123
 
103
- ;;;
124
+ ; returns whether N is less than 0
125
+ ; [N] => [N < 0]
126
+ ;
127
+ ; [5] => [0] [-3] => [1] [0] => [0]
128
+ neg?: :sign push -1 :eq ret
104
129
 
130
+ ; returns a pseudo-array of the positive divisors of N; as an optimization,
131
+ ; they're not returned in ascending order, but rather in two "halves".
132
+ ; The alternative is unnecessarily invoking to_a and clobbering heap.
133
+ ; [N]
134
+ ;
135
+ ; [10] => [1 2 10 5 4]
136
+ ; [12] => [1 2 3 12 6 4 6]
137
+ ; [25] => [1 5 25 3] ; no duplicate for perfect squares
138
+ ; [60] => [1 2 3 4 5 6 60 30 20 15 12 10 12]
105
139
  divisors: ; [n]
106
- dup push -1 swap store ; preserve n because array operations
107
- :isqrt push 1 swap :range dup ; 1..n**0.5
140
+ dup push -1 swap store ; preserve N because array operations
141
+ :isqrt push 1 swap :range dup ; 1..isqrt(N)
108
142
  reject (push -1 load swap mod) :arydup ; get first half of divisors
109
143
  map (push -1 load swap div) :arycat ; map first half to second half
110
- push -1 load copy 2 dup mul sub jz _divisors_square
111
- ret
112
-
144
+ push -1 load copy 2 dup mul sub jz _divisors_square ret
113
145
  _divisors_square: slide 1 $-- ret ; de-duplicate when N is a perfect square
114
146
 
115
- ;;;
116
-
147
+ ; returns the number of ways to choose K elements from a set of N
148
+ ; [N K]
149
+ ;
150
+ ; [ 4 5] => [0]
151
+ ; [ 7 7] => [1]
152
+ ; [13 3] => [286]
153
+ ; [16 4] => [1820]
154
+ ; [50 3] => [19600]
117
155
  nCk:
118
156
  copy 1 copy 1 sub :factorial
119
157
  swap :factorial mul
@@ -1,3 +1,6 @@
1
+ import math ; divmod
2
+ import util ; inc
3
+
1
4
  ; [n] => [0|1]
2
5
  prime?: ; [n]
3
6
  dup push 3 sub jn _prime_special ; special-case < 3
@@ -40,7 +43,5 @@ _factor_loop: ; [d n]
40
43
  _divisor_loop:
41
44
  dup copy 2 :divmod jz _divisor_keep
42
45
  pop swap :next_prime swap jump _factor_loop
43
-
44
46
  _divisor_keep: push -2 :inc slide 1 copy 1 swap jump _factor_loop
45
-
46
47
  _factor_done: slide 1 push 3 sub load ret
@@ -1,3 +1,5 @@
1
+ import math ; pow
2
+
1
3
  srand: push $seed swap store ret
2
4
 
3
5
  rand:
@@ -0,0 +1,153 @@
1
+ ;;; Rational numbers
2
+
3
+ import math ; abs, divmod, gcd, pow, sign
4
+ import util ; die!, neq
5
+
6
+ ; maximum value of either component of a rational number before behavior is
7
+ ; undefined; 2^31 by default to give interpreters without bignums a chance,
8
+ ; but customizable from userland.
9
+ $RAT = push 2,31 :pow
10
+
11
+ ; encodes a numerator N and a denominator D as a rational number R with the
12
+ ; following structure: ((N × $RAT + D) << 1) + sign (0 for negative, else 1).
13
+ ; This representation is nice because, with care, it makes it so that we never
14
+ ; have to involve the heap (besides divmod) just to do fairly basic arithmetic.
15
+ ; [N D] => [R]
16
+ ;
17
+ ; [22 7] => [R(22,7)]
18
+ ; [4 8] => [R(4,8)] ; no implicit simplification TODO: make it configurable?
19
+ ; [5 1] => [R(5,1)] ; no conversion to integer (duh)
20
+ ; [-3 4] => [R(-3,4)]
21
+ ; [3 -4] => [R(-3,4)] ; sign always held in the numerator
22
+ ; [-1 -3] => [R(1,3)]
23
+ to_r: dup jz _to_r_dbz
24
+ dup :sign copy 2 :sign mul push -1 :neq
25
+ copy 2 :abs copy 2 :abs
26
+ $RAT copy 2 mul add push 2 mul
27
+ copy 2 add slide 4 ret
28
+ _to_r_dbz: push "0 in denominator!" :die!
29
+
30
+ ; returns the numerator N of the rational number R, which may be negative
31
+ ; [R] => [N]
32
+ ;
33
+ ; [R(22,7)] => [22]
34
+ ; [R(-3,4)] => [-3]
35
+ ; [R(3,-4)] => [-3]
36
+ ratnum: push 2 :divmod push 2 mul push 1 sub swap $RAT div mul ret
37
+
38
+ ; returns the denominator D of the rational number R; always positive
39
+ ; [R] => [D]
40
+ ;
41
+ ; [R(22,7)] => [7]
42
+ ; [R(-3,4)] => [4]
43
+ ; [R(3,-4)] => [4]
44
+ ratden: push 2 div $RAT mod ret
45
+
46
+ ; decomposes the rational number R into its numerator N and denominator D
47
+ ; [R] => [N D]
48
+ ;
49
+ ; [R(22,7)] => [22 7]
50
+ ; [R(-3,4)] => [-3 4]
51
+ ; [R(3,-4)] => [-3 4]
52
+ ; [R(4,8)] => [4 8] ; no implicit simplification
53
+ from_r: dup :ratnum swap :ratden ret
54
+
55
+ ; fully simplifies the rational number R by dividing both of its components
56
+ ; by their greatest common divisor
57
+ ; [R] => [R, simplified]
58
+ ;
59
+ ; [R(4,8)] => [R(1,2)]
60
+ ; [R(-3,12)] => [R(-1,4)]
61
+ ; [R(17,-51)] => [R(-1,3)]
62
+ ratsimp:
63
+ push 2 :divmod swap $RAT :divmod dup copy 2 :gcd
64
+ swap copy 1 div copy 2 copy 2 div swap :to_r
65
+ slide 2 push 2 div push 2 mul add ret
66
+
67
+ ; helper prologue shared by all but ratmul
68
+ _rathead:
69
+ copy 1 :ratnum copy 1 :ratden mul
70
+ copy 2 :ratden copy 2 :ratnum mul ret
71
+
72
+ ; helper epilogue shared by all but ratdiv
73
+ _rattail:
74
+ copy 2 :ratden copy 2 :ratden mul
75
+ :to_r :ratsimp slide 2 ret
76
+
77
+ ; returns the simplified sum of the rational numbers Ra and Rb
78
+ ; [Ra Rb] => [Ra + Rb]
79
+ ;
80
+ ; [R(-9,-14) R(-3,19)] => [R(129,266)]
81
+ ; [R(17,30) R(18,10)] => [R(71,30)]
82
+ ; [R(-27,14) R(15,-23)] => [R(-831,322)]
83
+ ; [R(3,-9) R(8,3)] => [R(7,3)]
84
+ ; [R(-5,27) R(-2,-27)] => [R(-1,9)]
85
+ ; [R(-27,-8) R(-15,22)] => [R(237,88)]
86
+ ; [R(-9,-29) R(-27,3)] => [R(-252,29)]
87
+ ; [R(2,-21) R(4,6)] => [R(4,7)]
88
+ ratadd: :_rathead add :_rattail ret
89
+
90
+ ; returns the simplified difference of the rational numbers Ra and Rb
91
+ ; [Ra Rb] => [Ra - Rb]
92
+ ;
93
+ ; [R(21,25) R(-28,27)] => [R(1267,675)]
94
+ ; [R(14,7) R(13,6)] => [R(-1,6)]
95
+ ; [R(-24,-9) R(-5,-21)] => [R(17,7)]
96
+ ; [R(-27,-2) R(-2,26)] => [R(353,26)]
97
+ ; [R(-27,3) R(2,-22)] => [R(-98,11)]
98
+ ; [R(-4,23) R(-9,13)] => [R(155,299)]
99
+ ; [R(-14,19) R(-18,-11)] => [R(-496,209)]
100
+ ; [R(-29,21) R(-15,-16)] => [R(-779,336)]
101
+ ratsub: :_rathead sub :_rattail ret
102
+
103
+ ; returns the simplified product of the rational numbers Ra and Rb
104
+ ; [Ra Rb] => [Ra × Rb]
105
+ ;
106
+ ; [R(-24,26) R(-1,-30)] => [R(-2,65)]
107
+ ; [R(19,4) R(27,2)] => [R(513,8)]
108
+ ; [R(25,27) R(4,-11)] => [R(-100,297)]
109
+ ; [R(1,18) R(4,8)] => [R(1,36)]
110
+ ; [R(1,27) R(-8,29)] => [R(-8,783)]
111
+ ; [R(25,-13) R(-6,24)] => [R(25,52)]
112
+ ; [R(6,-13) R(9,23)] => [R(-54,299)]
113
+ ; [R(11,8) R(-19,-19)] => [R(11,8)]
114
+ ratmul: copy 1 :ratnum copy 1 :ratnum mul :_rattail ret
115
+
116
+ ; returns the simplified quotient of the rational numbers Ra and Rb
117
+ ; [Ra Rb] => [Ra / Rb]
118
+ ;
119
+ ; [R(-30,-22) R(15,12)] => [R(12,11)]
120
+ ; [R(13,28) R(-15,29)] => [R(-377,420)]
121
+ ; [R(7,-30) R(-22,-12)] => [R(-7,55)]
122
+ ; [R(15,4) R(8,-8)] => [R(-15,4)]
123
+ ; [R(-23,28) R(-16,-15)] => [R(-345,448)]
124
+ ; [R(-18,12) R(6,18)] => [R(-9,2)]
125
+ ; [R(29,-2) R(11,-21)] => [R(609,22)]
126
+ ; [R(-23,25) R(25,-3)] => [R(69,625)]
127
+ ratdiv: :_rathead :to_r :ratsimp slide 2 ret
128
+
129
+ ; returns the simplified modulus of the rational numbers Ra and Rb
130
+ ; [Ra Rb] => [Ra % Rb]
131
+ ;
132
+ ; [R(-15,-3) R(-16,-10)] => [R(1,5)]
133
+ ; [R(4,2) R(21,21)] => [R(0,1)]
134
+ ; [R(24,10) R(-18,-3)] => [R(12,5)]
135
+ ; [R(3,-7) R(-2,16)] => [R(-3,56)]
136
+ ; [R(4,28) R(-29,7)] => [R(-4,1)]
137
+ ; [R(7,-27) R(10,23)] => [R(109,621)]
138
+ ; [R(28,-3) R(30,-12)] => [R(-11,6)]
139
+ ; [R(-29,21) R(19,-23)] => [R(-268,483)]
140
+ ratmod: :_rathead mod :_rattail ret
141
+
142
+ ; returns the sign of the rational number R
143
+ ; [R] => [-1 | 0 | 1]
144
+ ;
145
+ ; [R(4,3)] => [1]
146
+ ; [R(4,-3)] => [-1]
147
+ ; [R(0,10)] => [0]
148
+ ; [R(-3,4)] => [-1]
149
+ ; [R(-3,-4)] => [1]
150
+ ratsign:
151
+ dup $RAT push 2 mul push 2 add sub jn _ratsign_zero
152
+ push 2 mod push 2 mul push 1 sub ret
153
+ _ratsign_zero: pop push 0 ret