spitewaste 0.1.001

Sign up to get free protection for your applications and to get access to all the features.
Files changed (46) hide show
  1. checksums.yaml +7 -0
  2. data/Gemfile +8 -0
  3. data/README.md +55 -0
  4. data/Rakefile +10 -0
  5. data/TUTORIAL.md +125 -0
  6. data/bin/spw +10 -0
  7. data/demo/factorial-nicespace.png +0 -0
  8. data/demo/factorial.asm +47 -0
  9. data/demo/factorial.png +0 -0
  10. data/demo/factorial.wsa +5 -0
  11. data/lib/spitewaste.rb +35 -0
  12. data/lib/spitewaste/assembler.rb +56 -0
  13. data/lib/spitewaste/cli.rb +51 -0
  14. data/lib/spitewaste/cli/asm.rb +10 -0
  15. data/lib/spitewaste/cli/compile.rb +60 -0
  16. data/lib/spitewaste/cli/convert.rb +53 -0
  17. data/lib/spitewaste/cli/exec.rb +43 -0
  18. data/lib/spitewaste/cli/image.rb +53 -0
  19. data/lib/spitewaste/emitter.rb +10 -0
  20. data/lib/spitewaste/emitters/assembly.rb +7 -0
  21. data/lib/spitewaste/emitters/codegen.rb +72 -0
  22. data/lib/spitewaste/emitters/image.rb +135 -0
  23. data/lib/spitewaste/emitters/linefeed.png +0 -0
  24. data/lib/spitewaste/emitters/schemes.yaml +1143 -0
  25. data/lib/spitewaste/emitters/whitespace.rb +14 -0
  26. data/lib/spitewaste/emitters/wsassembly.rb +7 -0
  27. data/lib/spitewaste/libspw/array.spw +82 -0
  28. data/lib/spitewaste/libspw/bits.spw +72 -0
  29. data/lib/spitewaste/libspw/case.spw +32 -0
  30. data/lib/spitewaste/libspw/fun.spw +42 -0
  31. data/lib/spitewaste/libspw/io.spw +39 -0
  32. data/lib/spitewaste/libspw/math.spw +117 -0
  33. data/lib/spitewaste/libspw/prime.spw +46 -0
  34. data/lib/spitewaste/libspw/random.spw +10 -0
  35. data/lib/spitewaste/libspw/stack.spw +84 -0
  36. data/lib/spitewaste/libspw/string.spw +233 -0
  37. data/lib/spitewaste/libspw/syntax.spw +2 -0
  38. data/lib/spitewaste/libspw/test.spw +8 -0
  39. data/lib/spitewaste/libspw/util.spw +98 -0
  40. data/lib/spitewaste/parsers/assembly.rb +35 -0
  41. data/lib/spitewaste/parsers/fucktional.rb +72 -0
  42. data/lib/spitewaste/parsers/spitewaste.rb +192 -0
  43. data/lib/spitewaste/parsers/whitespace.rb +60 -0
  44. data/lib/spitewaste/version.rb +3 -0
  45. data/spitewaste.gemspec +17 -0
  46. metadata +88 -0
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: c2124fde880a571c835efe1e74e397a0dea698786c69a62852c4ad8bd7e3ffe0
4
+ data.tar.gz: 0d40e036ca0d7f51ea617be600274088160a8159dc7e04f87af8429d29154fc4
5
+ SHA512:
6
+ metadata.gz: 8ca420c6896dba0743f70f27a6558178bfe6201fd821afd45594e11796a975ebd042eb6d65fda6b20221f40831d183e417aca2e7dd7c19edff16c4e99ea15135
7
+ data.tar.gz: 1699a938795047284088697c7c3d7f444e626b033f888080618d2d7350635a69dab37b63455cd6544740e747b2665f30df5c15d14c64bdb1a2f2755387cd4ff5
data/Gemfile ADDED
@@ -0,0 +1,8 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in spitewaste.gemspec
4
+
5
+ gem 'rake', '~> 12.0'
6
+ gem 'minitest', '~> 5.0'
7
+ gem 'thor', '~> 1.0'
8
+ gem 'oily_png' # TODO: optionalize?
@@ -0,0 +1,55 @@
1
+ <p align="center">
2
+ <img src="abc" /><br >
3
+ "Code should be invisible." — someone, surely
4
+ </p>
5
+
6
+ # Spitewaste
7
+
8
+ **Spitewaste** is a collection of tools with the noble goal of making your code disappear. Far too many CPU cycles are spent on displaying visible source code, and much too much ink has been spilled in the service of program printouts. We need a language whose only syntactic elements are space, tab, and newline; we need [Whitespace](https://en.wikipedia.org/wiki/Whitespace_(programming_language)).
9
+
10
+ Of course, writing programs in *pure* Whitespace is a task beyond mere mortals, but it'd be great if we could at least transpile to that most ephemerally beautiful of languages. To that end, I've endeavored to make spiting the aforementioned wastefulness (get it?<b id="a1">[*](#f1)</b>) a little more tractable by laying a thin veneer of syntax and additional semantics over the Whitespace instruction set.
11
+
12
+ ~~Many~~ [Some](https://rosettacode.org/wiki/Category:Whitespace) programs have been written in "Whitespace assembly", but this is *Spitewaste* assembly: <p align="center"><img src="demo/factorial.png" /></p>
13
+
14
+ Granted, that gets translated to [all this](demo/factorial.wsa) (which is just a nicer way of saying [this](demo/factorial.asm), though not nearly as nice as [*this*](demo/factorial-nicespace.png)), but that's the price paid for convenience. Besides, we're much less interested in the size of the code than we are in how many printable characters it ultimately contains, and the best possible score on that metric is zero.
15
+
16
+ <b id="f1">*</b> <sup>Kidding, of course; it's so named because I wrote it in **spite** of it being a huge **waste** of... all the things.</sup> [↩](#a1)
17
+
18
+ ## What <sub>is it?</sub>
19
+
20
+ Spitewaste is the pseudo-assembly language referred to throughout this document, as well as its accompanying standard library of useful subroutines ([`libspw`](lib/spitewaste/libspw)). The assembler that converts programs written in this language to pure Whitespace is also called Spitewaste, but the executable (and conventional extension) is `spw`.
21
+
22
+ **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
+
24
+ Spitewaste is not [a Whitespace interpreter](collidedscope/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
+
26
+ ## Where <sub>can I get it?</sub>
27
+
28
+ The project is distributed as a Ruby gem, so you're just a `gem install spitewaste` away from being able to run Spitewaste programs and/or turn them into executable nothingness. The package includes the assembler (a Ruby module), the CLI that drives it (**`spw`**), as well as the standard library (`libspw`).
29
+
30
+ ## How <sub>do I use it?</sub>
31
+
32
+ See `spw help` for guidance on invoking the command-line interface, and [this tutorial](TUTORIAL.md) for information regarding what Spitewaste offers over plain-old Whitespace assembly.
33
+
34
+ ## When <sub>will it do X?</sub>
35
+
36
+ - [x] Convert from
37
+ * Spitewaste to: Whitespace (ws), Whitespace assembly (wsa; just preprocessed spw), straight-line opcodes (asm)
38
+ * {ws, wsa, asm} to {ws, wsa, asm}
39
+ * all supported formats to C++ and PNG
40
+ - [x] Execute all supported formats by converting to Whitespace and passing the results to a user-specified interpreter
41
+ - [ ] Thoroughly document the standard library and make it searchable (eventually `spw docs`)
42
+ - - [ ] using a doctest-like approach for free specs
43
+ - [ ] Write a proper Spitewaste parser for better error reporting (and performance?)
44
+ - [ ] Support user-specified aliases of the builtin mnemonics in case you wanna say `discard` instead of `pop`, etc.
45
+ - [ ] Resolve missing identifiers by auto-importing the necessary standard library?
46
+ - [ ] Run a program with several interpreters to compare output and performance?
47
+ - [ ] Show inline opcodes with `spw image`?
48
+
49
+ ## Why <sub>is this a thing?</sub>
50
+
51
+ Because it's fun, of course! Any useful knowledge gained along the way is just icing on the cake.
52
+
53
+ ## Who <sub>wants to help?</sub>
54
+
55
+ The standard library currently sports just over a hundred "functions". A lot of them are pretty neat, but there's certainly room for many more. If you're up to it, I encourage you to learn just enough Spitewaste to be dangerous and contribute anything you feel is missing.
@@ -0,0 +1,10 @@
1
+ require 'bundler/gem_tasks'
2
+ require 'rake/testtask'
3
+
4
+ Rake::TestTask.new(:test) do |t|
5
+ t.libs << 'test'
6
+ t.libs << 'lib'
7
+ t.test_files = FileList['test/*_test.rb']
8
+ end
9
+
10
+ task :default => :test
@@ -0,0 +1,125 @@
1
+ For starters, note that Spitewaste is a strict superset of Whitespace (assembly) insofar as it doesn't add anything to the instruction set. That is, what we ultimately have to work with are a stack, a "heap" (hash, dictionary) of integer key-value pairs, and these 24 instructions:
2
+
3
+ <table>
4
+ <tr>
5
+ <td colspan="2" align="center"><b>stack manipulation</b></td>
6
+ </tr>
7
+ <tr>
8
+ <td><code>push <em>n</em></code></td>
9
+ <td>Push the value <em>n</em> onto the stack.</td>
10
+ </tr>
11
+ <tr>
12
+ <td><code>pop</code></td>
13
+ <td>Remove the value at the top of the stack.</td>
14
+ </tr>
15
+ <tr>
16
+ <td><code>dup</code></td>
17
+ <td>Duplicate the value at the top of the stack.</td>
18
+ </tr>
19
+ <tr>
20
+ <td><code>swap</code></td>
21
+ <td>Swap the top two values of the stack.</td>
22
+ </tr>
23
+ <tr>
24
+ <td><code>copy <em>n</em></code></td>
25
+ <td>Place a copy of the <em>n</em>th value at the top of the stack.<br><code>copy 0</code> is a funny way to write <code>dup</code></td>
26
+ </tr>
27
+ <tr>
28
+ <td><code>slide <em>n</em></code></td>
29
+ <td>Remove <em>n</em> values from the top of the stack, but preserve the top.</td>
30
+ </tr>
31
+ <tr>
32
+ <td colspan="2" align="center"><b>control flow</b></td>
33
+ </tr>
34
+ <tr>
35
+ <td><code>label <em>n</em></code></td>
36
+ <td>Mark a position in the program and name it <em>n</em>.</td>
37
+ </tr>
38
+ <tr>
39
+ <td><code>jump <em>n</em></code></td>
40
+ <td>Unconditionally jump to label <em>n</em>.
41
+ </tr>
42
+ <tr>
43
+ <td><code>jz <em>n</em></code></td>
44
+ <td>Pop the stack and jump to label <em>n</em> if the popped value was zero.
45
+ </tr>
46
+ <tr>
47
+ <td><code>jn <em>n</em></code></td>
48
+ <td>Pop the stack and jump to label <em>n</em> if the popped value was negative.
49
+ </tr>
50
+ <tr>
51
+ <td><code>call <em>n</em></code></td>
52
+ <td>Jump to label <em>n</em>, expecting it to <code>ret</code> in order to return to this call site.
53
+ </tr>
54
+ <tr>
55
+ <td><code>ret</code></td>
56
+ <td>Return to the most recent call site.</td>
57
+ </tr>
58
+ <tr>
59
+ <td><code>exit</code></td>
60
+ <td>Halt execution immediately.</td>
61
+ </tr>
62
+ <tr>
63
+ <td colspan="2" align="center"><b>arithmetic</b></td>
64
+ </tr>
65
+ <tr>
66
+ <td><code>add</code></td>
67
+ <td>Pop the top two values of the stack and push their sum.</td>
68
+ </tr>
69
+ <tr>
70
+ <td><code>sub</code></td>
71
+ <td>Pop the top two values of the stack and push their difference.<br>The top gets subtracted, so <code>push 3 push 5 sub</code> leaves -2 on the stack.</td>
72
+ </tr>
73
+ <tr>
74
+ <td><code>mul</code></td>
75
+ <td>Pop the top two values of the stack and push their product.</td>
76
+ </tr>
77
+ <tr>
78
+ <td><code>div</code></td>
79
+ <td>Pop the top two values of the stack and push their quotient.<br><b>Note:</b> floored, truncating division so -5 / 2 == -3</td>
80
+ </tr>
81
+ <tr>
82
+ <td><code>mod</code></td>
83
+ <td>Pop the top two values of the stack and push their modulus.<br><b>Note:</b>-1 % 4 == 3</td>
84
+ </tr>
85
+ <tr>
86
+ <td colspan="2" align="center"><b>heap access</b></td>
87
+ </tr>
88
+ <tr>
89
+ <td><code>store</code></td>
90
+ <td>Pop the value then the key and store the pair in the heap.<br><code>push 4 push 2 store</code> => <code>{4: 2}</code></td>
91
+ </tr>
92
+ <tr>
93
+ <td><code>load</code></td>
94
+ <td>Pop the key and push the associated heap value.<br><b>Note:</b> Undefined heap slots have an implicit value of 0.</td>
95
+ </tr>
96
+ <tr>
97
+ <td colspan="2" align="center"><b>input/output</b></td>
98
+ </tr>
99
+ <tr>
100
+ <td><code>ichr</code></td>
101
+ <td>Read a character (byte) from <code>stdin</code>, pop a key from the top of the stack, <br>and store the byte in the heap at that key.</td>
102
+ </tr>
103
+ <tr>
104
+ <td><code>inum</code></td>
105
+ <td>Read a number from <code>stdin</code>, pop a key from the top of the stack, <br>and store the byte in the heap at that key.</td>
106
+ </tr>
107
+ <tr>
108
+ <td><code>ochr</code></td>
109
+ <td>Pop the top of the stack and print it (as a character) to <code>stdout</code>.</td>
110
+ </tr>
111
+ <tr>
112
+ <td><code>onum</code></td>
113
+ <td>Pop the top of the stack and print it (as a number) to <code>stdout</code>.</td>
114
+ </tr>
115
+ </table>
116
+
117
+ ## What Spitewaste adds
118
+
119
+ A standard library! Writing a Whitespace program from scratch can be a daunting endeavor. TODO: more here.
120
+
121
+ ### Strings
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>. Strings in Spitewaste are similarly encoded, except backwards (as an implementation detail) and in base-128 rather than decimal.
124
+
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 ADDED
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'spitewaste/cli'
4
+ require 'spitewaste/cli/asm'
5
+ require 'spitewaste/cli/exec'
6
+ require 'spitewaste/cli/compile'
7
+ require 'spitewaste/cli/convert'
8
+ require 'spitewaste/cli/image'
9
+
10
+ SpitewasteCLI.start ARGV
@@ -0,0 +1,47 @@
1
+ label 0
2
+ push 6
3
+ call 1
4
+ onum
5
+ exit
6
+ label 1
7
+ push 1
8
+ swap
9
+ call 4
10
+ dup
11
+ dup
12
+ dup
13
+ push 2
14
+ sub
15
+ jn 3
16
+ pop
17
+ push -9
18
+ swap
19
+ push 1
20
+ sub
21
+ store
22
+ label 2
23
+ mul
24
+ push -9
25
+ dup
26
+ load
27
+ push 1
28
+ sub
29
+ dup
30
+ jz 3
31
+ store
32
+ jump 2
33
+ label 3
34
+ pop
35
+ pop
36
+ ret
37
+ label 4
38
+ copy 1
39
+ push 1
40
+ add
41
+ swap
42
+ copy 1
43
+ copy 1
44
+ sub
45
+ jn 4
46
+ pop
47
+ ret
Binary file
@@ -0,0 +1,5 @@
1
+ main: push 6 call factorial onum exit
2
+ factorial: push 1 swap call range dup dup dup push 2 sub jn reduce_done_a pop push -9 swap push 1 sub store
3
+ reduce_loop_a: mul push -9 dup load push 1 sub dup jz reduce_done_a store jump reduce_loop_a
4
+ reduce_done_a: pop pop ret
5
+ range: copy 1 push 1 add swap copy 1 copy 1 sub jn range pop ret
@@ -0,0 +1,35 @@
1
+ module Spitewaste
2
+ OPERATORS_M2T = { # mnemonic to tokens
3
+ push: " ", copy: " \t ", slide: " \t\n", label: "\n ",
4
+ call: "\n \t", jump: "\n \n", jz: "\n\t ", jn: "\n\t\t",
5
+ pop: " \n\n", dup: " \n ", swap: " \n\t", add: "\t ",
6
+ sub: "\t \t", mul: "\t \n", div: "\t \t ", mod: "\t \t\t",
7
+ store: "\t\t ", load: "\t\t\t", ret: "\n\t\n", ichr: "\t\n\t ",
8
+ inum: "\t\n\t\t", ochr: "\t\n ", onum: "\t\n \t", exit: "\n\n\n",
9
+ shell: "\t\n\n", eval: "\n\n\t"
10
+ }
11
+ OPERATORS_T2M = OPERATORS_M2T.invert # tokens to mnemonic
12
+
13
+ module_function
14
+
15
+ def guess_format program
16
+ white = program.count "\s\t\n"
17
+ black = program.size - white
18
+
19
+ return :whitespace if white > black
20
+ program[/[^-\w\s]/] ? :spitewaste : :assembly # TODO: something more robust?
21
+ end
22
+ end
23
+
24
+ require 'spitewaste/assembler'
25
+
26
+ require 'spitewaste/parsers/spitewaste'
27
+ require 'spitewaste/parsers/whitespace'
28
+ require 'spitewaste/parsers/assembly'
29
+
30
+ require 'spitewaste/emitter'
31
+ require 'spitewaste/emitters/whitespace'
32
+ require 'spitewaste/emitters/assembly'
33
+ require 'spitewaste/emitters/wsassembly'
34
+ require 'spitewaste/emitters/codegen'
35
+ require 'spitewaste/emitters/image'
@@ -0,0 +1,56 @@
1
+ module Spitewaste
2
+ class Assembler
3
+ attr_reader :src, :parser, :instructions
4
+
5
+ def initialize program, **options
6
+ format = options[:format]
7
+ format ||= Spitewaste.guess_format program
8
+ unless %i[whitespace wsassembly assembly spitewaste].include? format
9
+ raise ArgumentError, "unknown format for parse: #{format}"
10
+ end
11
+
12
+ @parser =
13
+ case format
14
+ when :whitespace
15
+ WhitespaceParser
16
+ when :assembly
17
+ AssemblyParser
18
+ else # used for both Spitewaste and Whitespace assembly
19
+ SpitewasteParser
20
+ end
21
+
22
+ parser = @parser.new(program, **options).tap &:parse
23
+ @instructions = parser.instructions
24
+ @src = parser.src if format == :spitewaste
25
+ end
26
+
27
+ def assemble! format:, io: STDOUT, **options
28
+ unless %i[whitespace wsassembly assembly codegen image].include? format
29
+ raise ArgumentError, "unknown format for emit: #{format}"
30
+ end
31
+
32
+ emitter =
33
+ case format
34
+ when :whitespace
35
+ WhitespaceEmitter
36
+ when :wsassembly
37
+ WSAssemblyEmitter
38
+ when :codegen
39
+ CodegenEmitter
40
+ when :image
41
+ ImageEmitter
42
+ else
43
+ AssemblyEmitter
44
+ end
45
+ emitter.new(format == :wsassembly ? @src : @instructions, **options).emit io: io
46
+ end
47
+
48
+ def try_elide_main
49
+ if @instructions.first == [:label, 0]
50
+ @instructions.shift unless @instructions.any? { |op, arg|
51
+ arg == 0 && %i[jump jz jn call].include?(op)
52
+ }
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,51 @@
1
+ require 'spitewaste'
2
+ require 'thor'
3
+ require 'fileutils'
4
+ require 'pathname'
5
+
6
+ class SpitewasteCLI < Thor
7
+ VALID_FORMATS = %i[spw spitewaste ws whitespace wsa wsassembly asm assembly]
8
+ CACHE_DIR = File.expand_path '~/.cache/spitewaste'
9
+
10
+ def self.exit_on_failure?
11
+ exit 1
12
+ end
13
+
14
+ # TODO: Figure out how to invoke a command from this class method.
15
+ def self.handle_no_command_error *args
16
+ exec $0, 'exec', *args
17
+ end
18
+
19
+ class_option :format,
20
+ desc: 'input format (in case auto-detection misguesses)',
21
+ aliases: '-f'
22
+
23
+ class_option :aliases,
24
+ desc: 'augment or override one or more of the default instruction mnemonics [WIP]',
25
+ banner: 'pop:drop...',
26
+ type: :array,
27
+ aliases: '-a'
28
+
29
+ class_option :coexist,
30
+ desc: <<DESC,
31
+ allow multiple mnemonics to refer to the same instruction where possible [WIP]
32
+
33
+ \e[1mNOTE: \e[0mIf --no-coexist (the default), aliases take precedence and render the original mnemonics invalid.
34
+ DESC
35
+ type: :boolean,
36
+ aliases: '-x'
37
+
38
+ def self.validate_format options
39
+ if fmt = options[:format]&.to_sym and !VALID_FORMATS.include?(fmt)
40
+ raise ArgumentError, "invalid format '#{fmt}'", []
41
+ end
42
+
43
+ Hash[*VALID_FORMATS][fmt] || fmt # expand short names
44
+ end
45
+
46
+ def self.make_cache_path name
47
+ FileUtils.mkpath CACHE_DIR
48
+ base = File.basename File.expand_path(name).tr(?/, ?-), '.*'
49
+ File.join(CACHE_DIR, base[1..]) + '.json'
50
+ end
51
+ end
@@ -0,0 +1,10 @@
1
+ class SpitewasteCLI
2
+ desc 'asm INPUT [OUTPUT]',
3
+ 'Generate plain Whitespace assembly from INPUT and write it to OUTPUT.'
4
+ long_desc 'Mostly just a convenience wrapper around `spw convert in -o asm out`'
5
+
6
+ def asm input, output = '/dev/stdout'
7
+ as = Spitewaste::Assembler.new File.read input
8
+ File.open(output, ?w) { |of| as.assemble! format: :assembly, io: of }
9
+ end
10
+ end
@@ -0,0 +1,60 @@
1
+ require 'open3'
2
+ require 'stringio'
3
+
4
+ class SpitewasteCLI
5
+ desc 'compile [OPTIONS] FILE',
6
+ 'Compile a Whitespace program to native machine instructions.'
7
+
8
+ long_desc <<-DESC
9
+ Under the hood, this command simply converts the input program's Whitespace
10
+ instructions to functionally equivalent C++ code and lets a modern compiler
11
+ do all the heavy lifting. The results are still quite good, but you'll need
12
+ a compiler and libgmpxx (arbitrary precision) for this to do the right thing.
13
+ DESC
14
+
15
+ option :compiler,
16
+ banner: 'PROG',
17
+ desc: 'a C++ compiler',
18
+ default: 'c++',
19
+ aliases: '-c'
20
+
21
+ option :output,
22
+ banner: 'FILE',
23
+ desc: 'the name of the executable to produce
24
+ (defaults to input without extension, or a.out if input is from stdin)',
25
+ aliases: '-o'
26
+
27
+ option :argv,
28
+ desc: 'additional flags to provide to the C++ compiler',
29
+ default: '-O3 -lgmp -lgmpxx',
30
+ aliases: '-v'
31
+
32
+ option :run,
33
+ desc: 'immediately invoke the resultant executable, then remove it',
34
+ type: :boolean,
35
+ aliases: '-r'
36
+
37
+ option :keep,
38
+ desc: "with --run, don't remove the executable",
39
+ type: :boolean,
40
+ aliases: '-k'
41
+
42
+ def compile file = nil
43
+ fmt = SpitewasteCLI.validate_format options
44
+
45
+ out = options[:output]
46
+ out ||= file ? File.basename(file, '.*') : 'a.out'
47
+ file ||= '/dev/stdin'
48
+
49
+ as = Spitewaste::Assembler.new File.read(file), format: fmt
50
+ as.assemble! format: :codegen, io: cpp = StringIO.new
51
+
52
+ cmd = "#{options[:compiler]} -x c++ -o #{out} #{options[:argv]} -"
53
+ Open3.capture2 cmd, stdin_data: cpp.string
54
+
55
+ if options[:run]
56
+ print `./#{out}`
57
+ File.delete out unless options[:keep]
58
+ end
59
+ end
60
+ end