class SpitewasteCLI

Constants

CACHE_DIR
VALID_FORMATS

Public Class Methods

exit_on_failure?() click to toggle source
# File lib/spitewaste/cli.rb, line 10
def self.exit_on_failure?
  exit 1
end
handle_no_command_error(*args) click to toggle source

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

# File lib/spitewaste/cli.rb, line 36
def self.handle_no_command_error *args
  exec $0, 'exec', *args
end
make_cache_path(name) click to toggle source
# File lib/spitewaste/cli.rb, line 48
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
shared_options() click to toggle source
# File lib/spitewaste/cli.rb, line 14
  def self.shared_options
    option :format,
      desc: 'input format (in case auto-detection misguesses)',
      aliases: '-f'

    option :aliases,
      desc: 'augment or override one or more of the default instruction mnemonics [WIP]',
      banner: 'pop:drop...',
      type: :array,
      aliases: '-a'

    option :coexist,
      desc: <<DESC,
allow multiple mnemonics to refer to the same instruction where possible [WIP]

\e[1mNOTE: \e[0mIf --no-coexist (the default), aliases take precedence and render the original mnemonics invalid.
DESC
      type: :boolean,
      aliases: '-x'
  end
validate_format(options) click to toggle source
# File lib/spitewaste/cli.rb, line 40
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

Public Instance Methods

asm(input, output = '/dev/stdout') click to toggle source
# File lib/spitewaste/cli/asm.rb, line 8
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) click to toggle source
# File lib/spitewaste/cli/compile.rb, line 44
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') click to toggle source
# File lib/spitewaste/cli/convert.rb, line 26
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
docs(*terms) click to toggle source
# File lib/spitewaste/cli/docs.rb, line 23
def docs *terms
  abort "need at least one search term" if terms.empty?

  docs = JSON.load File.read DOCS
  found = []
  min = options[:match_all] ? terms.size : 1

  docs.each do |lib, fns|
    fns.each do |fn, data|
      full = "#{lib}/#{bold under fn} #{data['full']}"
      ms = terms.count { |t| full[/#{t}/i] }
      found << [lib, fn, full, ms] if ms >= min
    end
  end

  abort "no matches for terms: #{terms}" if found.empty?

  found.sort_by! { |v| -v[-1] }

  IO.popen('less -R', 'w') do |io|
    found.each do |lib, fn, full|
      full.gsub! /#{terms * ?|}/i, &method(:hi)
      desc, specs = full.split "\n\n"
      io.puts "#{?- * 10}\n#{desc}\n\n"
      io.puts specs if options[:show_specs]
    end
  end
end
exec(input = '/dev/stdin') click to toggle source
# File lib/spitewaste/cli/exec.rb, line 21
def exec input = '/dev/stdin'
  fmt = SpitewasteCLI.validate_format options

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

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

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

  cmd = options[:interpreter].split
  cmd.map! { |c| c == '%' ? input : c }
  Kernel.exec *cmd
end
image(input, output = nil) click to toggle source
# File lib/spitewaste/cli/image.rb, line 46
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
version() click to toggle source
# File lib/spitewaste/cli/version.rb, line 6
def version
  puts "spw version #{Spitewaste::VERSION}"
end