spitewaste 0.1.003 → 0.1.004

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