Class: SpitewasteCLI

Inherits:
Thor
  • Object
show all
Defined in:
lib/spitewaste/cli.rb,
lib/spitewaste/cli/asm.rb,
lib/spitewaste/cli/exec.rb,
lib/spitewaste/cli/image.rb,
lib/spitewaste/cli/compile.rb,
lib/spitewaste/cli/convert.rb

Constant Summary collapse

VALID_FORMATS =
i[spw spitewaste ws whitespace wsa wsassembly asm assembly]
CACHE_DIR =
File.expand_path '~/.cache/spitewaste'

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.exit_on_failure?Boolean

Returns:

  • (Boolean)


10
11
12
# File 'lib/spitewaste/cli.rb', line 10

def self.exit_on_failure?
  exit 1
end

.handle_no_command_error(*args) ⇒ Object

TODO: Figure out how to invoke a command from this class method.



15
16
17
# File 'lib/spitewaste/cli.rb', line 15

def self.handle_no_command_error *args
  exec $0, 'exec', *args
end

.make_cache_path(name) ⇒ Object



46
47
48
49
50
# File 'lib/spitewaste/cli.rb', line 46

def self.make_cache_path name
  FileUtils.mkpath CACHE_DIR
  base = File.basename File.expand_path(name).tr(?/, ?-), '.*'
  File.join(CACHE_DIR, base[1..]) + '.json'
end

.validate_format(options) ⇒ Object



38
39
40
41
42
43
44
# File 'lib/spitewaste/cli.rb', line 38

def self.validate_format options
  if fmt = options[:format]&.to_sym and !VALID_FORMATS.include?(fmt)
    raise ArgumentError, "invalid format '#{fmt}'", []
  end

  Hash[*VALID_FORMATS][fmt] || fmt # expand short names
end

Instance Method Details

#asm(input, output = '/dev/stdout') ⇒ Object



6
7
8
9
# File 'lib/spitewaste/cli/asm.rb', line 6

def asm input, output = '/dev/stdout'
  as = Spitewaste::Assembler.new File.read input
  File.open(output, ?w) { |of| as.assemble! format: :assembly, io: of }
end

#compile(file = nil) ⇒ Object



42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
# File 'lib/spitewaste/cli/compile.rb', line 42

def compile file = nil
  fmt = SpitewasteCLI.validate_format options

  out = options[:output]
  out ||= file ? File.basename(file, '.*') : 'a.out'
  file ||= '/dev/stdin'

  as = Spitewaste::Assembler.new File.read(file), format: fmt
  as.assemble! format: :codegen, io: cpp = StringIO.new

  cmd = "#{options[:compiler]} -x c++ -o #{out} #{options[:argv]} -"
  Open3.capture2 cmd, stdin_data: cpp.string

  if options[:run]
    print `./#{out}`
    File.delete out unless options[:keep]
  end
end

#convert(input = '/dev/stdin', output = '/dev/stdout') ⇒ Object

Raises:

  • (ArgumentError)


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
# File 'lib/spitewaste/cli/convert.rb', line 24

def convert input = '/dev/stdin', output = '/dev/stdout'
  ext = File.extname output
  Kernel.exec 'spw', 'image', input, output if ext == '.png'
  fmt = SpitewasteCLI.validate_format options

  valid_outputs = i[ws whitespace
                     wsa wsassembly
                     asm assembly
                     cpp codegen]
  if out_fmt = options[:output_format]&.to_sym and !valid_outputs.include?(out_fmt)
    raise ArgumentError, "invalid output format '#{out_fmt}'", []
  end

  ext_map = {
    '.ws'  => :whitespace,
    '.wsa' => :wsassembly,
    '.asm' => :assembly,
    '.cpp' => :codegen}
  out_fmt = Hash[*valid_outputs][out_fmt] || ext_map[ext]
  raise ArgumentError, "can't determine output format", [] unless out_fmt

  opts = options.dup # options is frozen
  if opts[:symbol_file] == '%'
    opts[:symbol_file] = SpitewasteCLI.make_cache_path input
  end

  as = Spitewaste::Assembler.new File.read(input), format: fmt, **opts
  File.open(output, ?w) { |of| as.assemble! format: out_fmt, io: of }
end

#exec(file = '/dev/stdin') ⇒ Object

Raises:

  • (LoadError)


19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
# File 'lib/spitewaste/cli/exec.rb', line 19

def exec file = '/dev/stdin'
  fmt = SpitewasteCLI.validate_format options

  raise LoadError, "No such file '#{file}'", [] unless File.exists? file

  opts = options.dup # options is frozen
  if opts[:symbol_file] == '%'
    opts[:symbol_file] = SpitewasteCLI.make_cache_path file
  end

  path =
    if File.extname(file) != '.ws'
      io = Tempfile.new
      as = Spitewaste::Assembler.new File.read(file), format: fmt, **opts
      as.assemble! format: :whitespace, io: io
      io.tap(&:close).path
    else
      file
    end

  cmd = options[:interpreter].split
  cmd.map! { |c| c == '%' ? path : c }
  Kernel.exec *cmd
end

#image(input, output = nil) ⇒ Object



44
45
46
47
48
49
50
51
52
# File 'lib/spitewaste/cli/image.rb', line 44

def image input, output = nil
  fmt = SpitewasteCLI.validate_format options
  output ||= Pathname.new(input).sub_ext '.png'

  as = Spitewaste::Assembler.new File.read(input), format: fmt
  File.open(output, ?w) { |of|
    as.assemble! format: :image, io: of, **options.transform_keys(&:to_sym)
  }
end