Module: Fusion::CLI

Extended by:
CLI
Included in:
CLI
Defined in:
lib/fusion/cli.rb,
lib/fusion/cli/repl.rb,
lib/fusion/cli/parser.rb,
lib/fusion/cli/decoder.rb,
lib/fusion/cli/encoder.rb,
lib/fusion/cli/options.rb,
lib/fusion/cli/serializer.rb

Defined Under Namespace

Modules: Decoder, Encoder, Parser, Serializer Classes: Options, Repl

Instance Method Summary collapse

Instance Method Details

#apply(input, function, environment:) ⇒ Object

runtime value + runtime value -> runtime value input | function -> output Confines @-resolution to the environment's jail. Ignores the environment's bindings. The function carries its own closure.



122
123
124
# File 'lib/fusion/cli.rb', line 122

def apply(input, function, environment:)
  Interpreter.safe_apply(function, input, environment)
end

#decode(string, mode:) ⇒ Object

String (treated as stdin) -> WirePair Doesn't support mode :unix



86
87
88
# File 'lib/fusion/cli.rb', line 86

def decode(string, mode:)
  Decoder.decode(string, mode:)
end

#encode(wire_pair, mode:) ⇒ Object

WirePair -> String Doesn't support mode :unix



151
152
153
# File 'lib/fusion/cli.rb', line 151

def encode(wire_pair, mode:)
  Encoder.encode(wire_pair, mode:)
end

#evaluate(ast, environment) ⇒ Object

expression (AST) -> runtime value Mutates environment if given an assignment statement. Confines @-resolution to the environment's jail. Has access to the environment's bindings.



130
131
132
133
134
135
136
137
138
139
140
141
# File 'lib/fusion/cli.rb', line 130

def evaluate(ast, environment)
  case ast
  when AST::Statement::Assignment
    value = Interpreter.safe_evaluate(ast.expression, environment)
    environment.bind(ast.name, value, checked: false)
    value
  when AST::Expression
    Interpreter.safe_evaluate(ast, environment)
  else
    raise Unreachable, "Unhandled AST node #{ast.class}"
  end
end

#load_file(rel_path, root_env) ⇒ Object

relative path -> runtime_value



110
111
112
113
114
115
116
# File 'lib/fusion/cli.rb', line 110

def load_file(rel_path, root_env)
  interp = Fusion::Interpreter.new(root_env)
  abspath = File.expand_path(rel_path)
  # The top-level program file is loaded by the runtime, not via an @-reference:
  # the operation is "loading code", with the path as `input` (which file).
  interp.load_file(abspath).force(operation: "loading code", input: interp.display_path(abspath), site: { origin: "code", file: nil })
end

#load_source(inline_source, root_env) ⇒ Object

String (treated as inline source) -> runtime value



101
102
103
104
105
106
107
# File 'lib/fusion/cli.rb', line 101

def load_source(inline_source, root_env)
  ast = Fusion::Parser.parse_file(inline_source, site: { origin: "code", file: "<inline>" })
  return ast if ast.is_a?(Fusion::Interpreter::ErrorVal) # a parse error

  inline_env = root_env.child.set_context(:dir, Dir.pwd)
  Fusion::Interpreter.new(inline_env).evaluate_unit(ast)
end

#parse(wire_pair) ⇒ Object

WirePair -> runtime value



91
92
93
# File 'lib/fusion/cli.rb', line 91

def parse(wire_pair)
  Parser.parse(wire_pair)
end

#prepare!Object



24
25
26
27
28
29
30
# File 'lib/fusion/cli.rb', line 24

def prepare!
  $stdout.sync = true
  $stderr.sync = true
  $stdin.set_encoding(Encoding::UTF_8)
  $stdout.set_encoding(Encoding::UTF_8)
  $stderr.set_encoding(Encoding::UTF_8)
end

#root_environment(jail: Dir.pwd) ⇒ Object

A binding-free root environment



96
97
98
# File 'lib/fusion/cli.rb', line 96

def root_environment(jail: Dir.pwd)
  Interpreter::Env.new.set_context(:jail, jail)
end

#run(options) ⇒ Object



32
33
34
35
36
37
38
39
40
41
42
43
# File 'lib/fusion/cli.rb', line 32

def run(options)
  case options.use_case
  when :pipe
    run_pipe(options)
  when :stream
    run_stream(options)
  when :repl
    run_repl(options)
  else
    raise Unreachable, "Unknown use case #{options.use_case}"
  end
end

#run_pipe(options) ⇒ Object



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

def run_pipe(options)
  prepare!

  root = root_environment(jail: jail_root(options))
  program = load_program(options, root)

  input = load_input(options)
  output = if input.nil?
    program
  else
    apply(parse(input), program, environment: root)
  end

  emit_output(serialize(output), output_mode: options.output_mode)
end

#run_repl(options) ⇒ Object



80
81
82
# File 'lib/fusion/cli.rb', line 80

def run_repl(options)
  Repl.new(root_env: root_environment(jail: jail_root(options))).run
end

#run_stream(options) ⇒ Object



61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
# File 'lib/fusion/cli.rb', line 61

def run_stream(options)
  prepare!

  root = root_environment(jail: jail_root(options))
  program = load_program(options, root)

  $stdin.each_line do |line|
    record = line.chomp

    if record.strip.empty?
      $stdout.puts unless options.skip_blank_lines?
    else
      input = decode(record, mode: options.input_mode)
      output = apply(parse(input), program, environment: root)
      $stdout.puts(encode(serialize(output), mode: options.output_mode))
    end
  end
end

#serialize(runtime_value) ⇒ Object

runtime value -> WirePair CAUTION: resolves "internal" error status, only use for final output



145
146
147
# File 'lib/fusion/cli.rb', line 145

def serialize(runtime_value)
  Serializer.serialize(runtime_value)
end