spitewaste 0.1.004 → 0.1.009
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +2 -2
- data/Rakefile +4 -3
- data/bin/spw +1 -0
- data/lib/spitewaste.rb +2 -0
- data/lib/spitewaste/assembler.rb +51 -6
- 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 +2 -0
- data/lib/spitewaste/cli/image.rb +2 -0
- data/lib/spitewaste/libspw/array.spw +69 -18
- data/lib/spitewaste/libspw/bits.spw +70 -70
- data/lib/spitewaste/libspw/case.spw +34 -28
- data/lib/spitewaste/libspw/docs.json +1 -0
- data/lib/spitewaste/libspw/fun.spw +34 -22
- data/lib/spitewaste/libspw/math.spw +7 -3
- data/lib/spitewaste/libspw/prime.spw +33 -33
- data/lib/spitewaste/libspw/rational.spw +23 -0
- data/lib/spitewaste/libspw/string.spw +20 -19
- data/lib/spitewaste/libspw/test.spw +2 -3
- data/lib/spitewaste/libspw/util.spw +66 -20
- data/lib/spitewaste/parsers/spitewaste.rb +17 -3
- data/lib/spitewaste/version.rb +1 -1
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 660c0022e893f3656cb3851b0bb3bddebe1df750555138a58dcd81339f11effd
|
4
|
+
data.tar.gz: d5d539641ca545525a26a6d357831baa1dcb95189a4023955d15b45142bf2dad
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 28be3283bde45056b240eaba41f4b783a4b4e7bdacf8cfce02e052bf11f002c4e19beb9b101cf2c42638a1406de56f0b466503289a2596e457a4817442fa59f1
|
7
|
+
data.tar.gz: 57591a541274bef66a01828a2b719b4ec5b8091cb83e1cb488adf61041590cb84344db411cddbdd9d9f0dc16395b30cb59f6be397633c996a448262162d2c8c2
|
data/README.md
CHANGED
@@ -35,8 +35,8 @@ See `spw help` for guidance on invoking the command-line interface, and [this tu
|
|
35
35
|
* {ws, wsa, asm} to {ws, wsa, asm}
|
36
36
|
* all supported formats to C++ and PNG
|
37
37
|
- [x] Execute all supported formats by converting to Whitespace and passing the results to a user-specified interpreter
|
38
|
-
- [
|
39
|
-
- - [
|
38
|
+
- [x] Thoroughly document the standard library and make it searchable (via `spw docs`)
|
39
|
+
- - [x] using a doctest-like approach for free specs
|
40
40
|
- [ ] Write a proper Spitewaste parser for better error reporting (and performance?)
|
41
41
|
- [ ] Support user-specified aliases of the builtin mnemonics in case you wanna say `discard` instead of `pop`, etc.
|
42
42
|
- [ ] Resolve missing identifiers by auto-importing the necessary standard library?
|
data/Rakefile
CHANGED
@@ -14,7 +14,8 @@ task :docs do |t|
|
|
14
14
|
docs = {}
|
15
15
|
|
16
16
|
Dir.chdir('lib/spitewaste/libspw') do |d|
|
17
|
-
|
17
|
+
Dir['*.spw'].each do |path|
|
18
|
+
next if path['random']
|
18
19
|
lib = File.basename path, '.spw'
|
19
20
|
docs[lib] = extract_docs path
|
20
21
|
end
|
@@ -64,8 +65,8 @@ end
|
|
64
65
|
StackRx = /(?<=\[)[^\]]*(?=\])/ # match [.*], but don't capture the brackets
|
65
66
|
|
66
67
|
def parse_doc doc
|
67
|
-
# strip comment
|
68
|
-
doc.gsub!(
|
68
|
+
# strip leading comment to margin and any implementation details (!)
|
69
|
+
doc.gsub!(/^; */, '').gsub!(/^!.+\n/, '')
|
69
70
|
|
70
71
|
head, specs = doc.split "\n\n"
|
71
72
|
*desc, effect = head.split ?\n
|
data/bin/spw
CHANGED
data/lib/spitewaste.rb
CHANGED
data/lib/spitewaste/assembler.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
module Spitewaste
|
2
2
|
class Assembler
|
3
|
-
attr_reader :
|
3
|
+
attr_reader :parser
|
4
4
|
|
5
5
|
def initialize program, **options
|
6
6
|
format = options[:format]
|
@@ -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
|
-
@
|
24
|
-
@
|
22
|
+
@parser = parser.new(program, **options).tap &:parse
|
23
|
+
@src = @parser.src if format == :spitewaste
|
24
|
+
@instructions = @parser.instructions
|
25
25
|
end
|
26
26
|
|
27
27
|
def assemble! format:, io: STDOUT, **options
|
@@ -42,7 +42,52 @@ module Spitewaste
|
|
42
42
|
else
|
43
43
|
AssemblyEmitter
|
44
44
|
end
|
45
|
-
|
45
|
+
|
46
|
+
# Worthwhile optimizations can only be done if we have a symbol table.
|
47
|
+
optimize! if parser.respond_to? :symbol_table
|
48
|
+
|
49
|
+
src = format == :wsassembly ? @src : @instructions
|
50
|
+
emitter.new(src, **options).emit io: io
|
51
|
+
end
|
52
|
+
|
53
|
+
def optimize_constant_pow
|
54
|
+
pow = parser.symbol_table['pow']
|
55
|
+
|
56
|
+
case @instructions[@ip - 2, 3]
|
57
|
+
in [[:push, base], [:push, exp], [:call, ^pow]]
|
58
|
+
@instructions[@ip - 2, 3] = [[:push, base ** exp]]
|
59
|
+
|
60
|
+
in [[:push, base], [:dup, _], [:call, ^pow]]
|
61
|
+
@instructions[@ip - 2, 3] = [[:push, base ** base]]
|
62
|
+
|
63
|
+
else nil
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def optimize_constant_strpack
|
68
|
+
strpack = parser.symbol_table['strpack']
|
69
|
+
return if @instructions[@ip] != [:call, strpack]
|
70
|
+
|
71
|
+
# grab all the instructions between `push 0` and this `call strpack`
|
72
|
+
start = @instructions[0, @ip].rindex [:push, 0]
|
73
|
+
between = @instructions[start + 1...@ip]
|
74
|
+
|
75
|
+
bytes = []
|
76
|
+
# optimization only applies if all of the intervening ops are pushes
|
77
|
+
return unless between.all? { |op, arg| op == :push && bytes << arg }
|
78
|
+
|
79
|
+
packed = bytes.reverse.zip(0..).sum { |b, e| b * 128 ** e }
|
80
|
+
@instructions[start..@ip] = [[:push, packed]]
|
81
|
+
@ip = start + 1
|
82
|
+
end
|
83
|
+
|
84
|
+
def optimize!
|
85
|
+
@ip = 0
|
86
|
+
while @instructions[@ip]
|
87
|
+
next @ip -= 1 if optimize_constant_pow
|
88
|
+
next if optimize_constant_strpack
|
89
|
+
@ip += 1
|
90
|
+
end
|
46
91
|
end
|
47
92
|
|
48
93
|
def try_elide_main
|
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 = "#{lib}/#{bold under fn} #{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#{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
data/lib/spitewaste/cli/image.rb
CHANGED
@@ -1,21 +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
|
+
|
1
7
|
import math ; divmod, max, min, pos?
|
2
8
|
import stack ; bury, dig, ncopy, nslide, roll
|
3
9
|
import util ; dec, die!, eq
|
4
10
|
|
5
11
|
$amax = 1000
|
6
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]
|
7
19
|
arysum: reduce (add) ret
|
8
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]
|
9
26
|
arydup:
|
10
27
|
dup $++ push -2 copy 1 store
|
11
28
|
times (push -2 load $-- :ncopy) ret
|
12
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]
|
13
36
|
arypop: slide 1 $-- ret
|
14
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]
|
15
44
|
aryshift: dup :dig $-- ret
|
16
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]
|
17
51
|
arycat: dup $++ :roll add ret
|
18
52
|
|
53
|
+
;;;
|
54
|
+
|
19
55
|
arypack:
|
20
56
|
:arydup reduce (:max) $++ push -1 swap store
|
21
57
|
$-- times (push -1 load mul add)
|
@@ -30,36 +66,43 @@ aryunpack:
|
|
30
66
|
|
31
67
|
arylen: push $amax mod ret
|
32
68
|
|
33
|
-
|
34
|
-
|
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]
|
35
75
|
aryindex: copy 1 ; two copies of length, one gets decremented, index is diff
|
36
|
-
_aryindex_loop:
|
37
|
-
dup jn _aryindex_notfound
|
76
|
+
_aryindex_loop: dup jn _aryindex_notfound
|
38
77
|
dup push 2 add :roll copy 2 :eq jz _aryindex_no
|
39
78
|
copy 2 copy 1 sub swap $++ :nslide ret
|
40
|
-
|
41
79
|
_aryindex_no: $-- jump _aryindex_loop
|
42
|
-
|
43
80
|
_aryindex_notfound: slide 1 ret
|
44
81
|
|
45
|
-
|
46
|
-
|
47
|
-
;
|
48
|
-
|
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:
|
49
89
|
dup jn _aryat_neg
|
50
90
|
copy 1 swap sub dup :pos? jz _aryat_oob
|
51
91
|
:roll swap $-- :nslide ret
|
52
|
-
|
53
92
|
_aryat_neg: copy 1 add dup jn _aryat_oob jump aryat
|
54
|
-
|
55
93
|
_aryat_oob: push "(aryat) index out of bounds!" :die!
|
56
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]
|
57
101
|
minmax:
|
58
|
-
:arydup reduce (:max)
|
59
|
-
push -1 swap store
|
102
|
+
:arydup reduce (:max) push -1 swap store
|
60
103
|
reduce (:min) push -1 load ret
|
61
104
|
|
62
|
-
;;;
|
105
|
+
;;; TODO: make sort not atrociously inefficient
|
63
106
|
|
64
107
|
sort: push -3 copy 1 store ; preserve length
|
65
108
|
_sort_loop:
|
@@ -70,17 +113,25 @@ _sort_loop:
|
|
70
113
|
push 0 copy 1 sub jn _sort_loop
|
71
114
|
push 3 sub load ret
|
72
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]
|
73
122
|
aryrev: push -3 copy 1 store
|
74
123
|
_aryrev_loop:
|
75
124
|
swap copy 1 :bury $--
|
76
125
|
push 0 copy 1 sub jn _aryrev_loop
|
77
126
|
push 3 sub load ret
|
78
127
|
|
79
|
-
|
80
|
-
|
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]
|
81
133
|
aryrep: push -1 swap push -3 copy 1 store store
|
82
134
|
_aryrep_loop:
|
83
135
|
push -1 dup :dec load jz _aryrep_done
|
84
136
|
:arydup jump _aryrep_loop
|
85
|
-
|
86
137
|
_aryrep_done: push -3 load $-- times (:arycat) ret
|
@@ -1,74 +1,74 @@
|
|
1
|
-
import math
|
2
|
-
import
|
3
|
-
|
4
|
-
|
5
|
-
;
|
6
|
-
|
7
|
-
;
|
8
|
-
;
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
copy
|
15
|
-
|
16
|
-
|
17
|
-
push
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
;
|
45
|
-
;
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
_bitwise_xor_update: :_bitwise_update jump _bitwise_xor_loop
|
58
|
-
|
59
|
-
; flip all bits in n ; [n]
|
60
|
-
bitwise_not:
|
1
|
+
import math ; ilog, pow
|
2
|
+
import util ; digits
|
3
|
+
|
4
|
+
; The bitwise operators all work very similarly, differing only in how they
|
5
|
+
; map pairs of bits to 0 or 1. For AND, we simply multiply them. For OR, we
|
6
|
+
; sum them, add 1, then divide by 2. For XOR, we only want 1 if they're neq.
|
7
|
+
; We repeatedly halve the inputs while consuming bits and stop when at least
|
8
|
+
; one of them becomes zero. For AND, the answer is in the accumulator and we
|
9
|
+
; are done. For OR and XOR, it's likely the nonzero input still has bits to
|
10
|
+
; contribute, so we also accumulate its product with the current power of 2.
|
11
|
+
$bitfun(op, bit, done) {
|
12
|
+
push -2,1,-1,0 store store
|
13
|
+
_`op`_loop: push -1
|
14
|
+
copy 2 push 2 mod copy 2 push 2 mod `bit`
|
15
|
+
push -1 load swap push -2 load dup push 2 mul
|
16
|
+
push -2 swap store mul add store
|
17
|
+
push 2 div swap push 2 div
|
18
|
+
dup copy 2 mul jz _`op`_done swap jump _`op`_loop
|
19
|
+
_`op`_done: `done` ret
|
20
|
+
}
|
21
|
+
|
22
|
+
; returns the bitwise AND of A and B
|
23
|
+
; [A B] => [A & B]
|
24
|
+
;
|
25
|
+
; [65 47] => [1] [83 25] => [17]
|
26
|
+
; [40 64] => [0] [74 59] => [10]
|
27
|
+
; [31 18] => [18] [86 32] => [0]
|
28
|
+
; [93 79] => [77] [11 79] => [11]
|
29
|
+
band: $bitfun(band, mul, mul $-- load)
|
30
|
+
|
31
|
+
; returns the bitwise OR of A and B
|
32
|
+
; [A B] => [A | B]
|
33
|
+
;
|
34
|
+
; [66 51] => [115] [64 4] => [68]
|
35
|
+
; [61 77] => [125] [93 65] => [93]
|
36
|
+
; [14 87] => [95] [71 37] => [103]
|
37
|
+
; [7 19] => [23] [38 92] => [126]
|
38
|
+
bor: $bitfun(bor, add $++ push 2 div, add push -2 load mul push -1 load add)
|
39
|
+
|
40
|
+
; returns the bitwise XOR of A and B
|
41
|
+
; [A B] => [A ^ B]
|
42
|
+
;
|
43
|
+
; [4 14] => [10] [0 80] => [80]
|
44
|
+
; [51 5] => [54] [97 77] => [44]
|
45
|
+
; [39 65] => [102] [12 26] => [22]
|
46
|
+
; [44 36] => [8] [6 21] => [19]
|
47
|
+
bxor: $bitfun(bxor, :neq, add push -2 load mul push -1 load add)
|
48
|
+
|
49
|
+
; returns the infinite-precision bitwise NOT of N by inverting all of its bits
|
50
|
+
; [N] => [~N]
|
51
|
+
;
|
52
|
+
; [58] => [5] [46] => [17]
|
53
|
+
; [48] => [15] [87] => [40]
|
54
|
+
; [98] => [29] [51] => [12]
|
55
|
+
; [3] => [0] [42] => [21]
|
56
|
+
bnot:
|
61
57
|
dup push 0 swap push 1 add sub
|
62
58
|
swap push 2 :ilog push 2 swap :pow mod ret
|
63
59
|
|
64
|
-
|
65
|
-
|
66
|
-
;
|
60
|
+
; returns the number of bits in the binary representation of N
|
61
|
+
; [N] => [# of bits]
|
62
|
+
;
|
63
|
+
; [0] => [0] [1] => [1]
|
64
|
+
; [7] => [3] [8] => [4]
|
65
|
+
; [255] => [8]
|
66
|
+
blength: dup jz _blength_zero push 2 :ilog $++ ret
|
67
|
+
_blength_zero: ret
|
68
|
+
|
69
|
+
; returns the number of set bits in N
|
70
|
+
; [N] => [popcount]
|
71
|
+
;
|
72
|
+
; [0] => [0] [1] => [1]
|
73
|
+
; [7] => [3] [8] => [1]
|
67
74
|
popcount: push 2 :digits reduce (add) ret
|
68
|
-
|
69
|
-
;;;
|
70
|
-
|
71
|
-
; most significant set bit in n, as a power of 2 ; [n]
|
72
|
-
msb: dup jz _msb_zero
|
73
|
-
push 2 :itos :strlen $-- push 2 swap :pow ret
|
74
|
-
_msb_zero: ret
|