Class: Maxima::Command

Inherits:
Object
  • Object
show all
Defined in:
lib/maxima/command.rb

Constant Summary collapse

OPTIONS =
{
  float:           -> (enabled) { "float: #{enabled}" },
  use_fast_arrays: -> (enabled) { "use_fast_arrays: #{enabled}" },
  real_only:       -> (enabled) { "realonly: #{enabled}" }
}
MATCH_REGEX =
-> (eol_maxima_characters) { /(?<=\(%i#{eol_maxima_characters}\)).*(?=\$|\Z)/m.freeze }
GSUB_REGEX =
Regexp.union(/\s+/, /\(%(i|o)\d\)|done/)

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initializeCommand

Returns a new instance of Command.



5
6
7
8
9
10
# File 'lib/maxima/command.rb', line 5

def initialize
  @dependencies = []
  @assigned_variables = Set.new()
  @commands = []
  @options = {}
end

Instance Attribute Details

#assigned_variablesObject

Returns the value of attribute assigned_variables.



3
4
5
# File 'lib/maxima/command.rb', line 3

def assigned_variables
  @assigned_variables
end

#commandsObject

Returns the value of attribute commands.



3
4
5
# File 'lib/maxima/command.rb', line 3

def commands
  @commands
end

#dependenciesObject

Returns the value of attribute dependencies.



3
4
5
# File 'lib/maxima/command.rb', line 3

def dependencies
  @dependencies
end

#optionsObject

Returns the value of attribute options.



3
4
5
# File 'lib/maxima/command.rb', line 3

def options
  @options
end

Class Method Details

.convert_output_to_variables(output_variable_map, raw_output) ⇒ Object



129
130
131
132
133
134
135
136
137
# File 'lib/maxima/command.rb', line 129

def self.convert_output_to_variables(output_variable_map, raw_output)
  {}.tap do |result|
    output_variable_map.each_with_index do |(variable, klazz), index|
      output = raw_output[index]
      output = klazz.respond_to?(:parse) ? klazz.parse(output) : klazz.new(output)
      result[variable] = output
    end
  end
end

.eol_maxima_characters(input) ⇒ Object



162
163
164
# File 'lib/maxima/command.rb', line 162

def self.eol_maxima_characters(input)
  input.count("$") + input.count(";")
end

.expand_output_variable_map(output_variable_map) ⇒ Object



139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
# File 'lib/maxima/command.rb', line 139

def self.expand_output_variable_map(output_variable_map)
  {}.tap do |expanded_output_variable_map|
    add_key = -> (k,v) {
      if expanded_output_variable_map.has_key?(k)
        throw :key_used_twice
      else
        expanded_output_variable_map[k] = v
      end
    }

    output_variable_map.each do |output_key, parsed_into_class|
      case output_key
      when Array
        output_key.each do |output_subkey|
          add_key.call(output_subkey, parsed_into_class)
        end
      else
        add_key.call(output_key, parsed_into_class)
      end
    end
  end
end

.extract_outputs(output, eol_maxima_characters) ⇒ Object



122
123
124
125
126
127
# File 'lib/maxima/command.rb', line 122

def self.extract_outputs(output, eol_maxima_characters)
  MATCH_REGEX.call(eol_maxima_characters)
    .match(output)[0]
    .gsub(GSUB_REGEX, "")
    .split("$")
end

.output(*v) ⇒ Object



12
13
14
15
16
17
18
19
20
# File 'lib/maxima/command.rb', line 12

def self.output(*v)
  Command.new()
    .with_options(
      use_fast_arrays: true,
      float: true
    ).tap do |c|
    yield c
  end.output_variables(*v)
end

Instance Method Details

#<<(expression) ⇒ Object



60
61
62
# File 'lib/maxima/command.rb', line 60

def <<(expression)
  @commands << expression
end

#_apply_unary_operations(expression, *unary_operations, **unary_operations_options) ⇒ Object



64
65
66
67
68
69
70
71
72
73
74
75
# File 'lib/maxima/command.rb', line 64

def _apply_unary_operations(expression, *unary_operations, **unary_operations_options)
  unary_operations = Set.new(unary_operations)
  unary_operations_options.map do |option, is_enabled|
    unary_operations.add(option) if is_enabled
  end

  [
    unary_operations.map { |unary_operation| "#{unary_operation}(" },
    Maxima.mformat(expression),
    ")" * unary_operations.count
  ].join()
end

#_let(variable, expression) ⇒ Object



56
57
58
# File 'lib/maxima/command.rb', line 56

def _let(variable, expression)
  @commands << "#{variable} : #{expression}"
end

#add_variable(variable) ⇒ Object



26
27
28
29
30
31
32
33
# File 'lib/maxima/command.rb', line 26

def add_variable(variable)
  case variable
  when Enumerable
    @assigned_variables.merge(variable)
  else
    @assigned_variables.add(variable)
  end
end

#dependencies_inputObject



177
178
179
# File 'lib/maxima/command.rb', line 177

def dependencies_input
  @dependencies.map { |s| "load(#{s})" }
end

#let(variable_or_variables, expression, *unary_operations, **unary_operations_options) ⇒ Object



35
36
37
38
39
40
41
42
43
# File 'lib/maxima/command.rb', line 35

def let(variable_or_variables, expression, *unary_operations, **unary_operations_options)
  add_variable(variable_or_variables)


  variable   = Maxima.mformat(variable_or_variables)
  expression = _apply_unary_operations(expression, *unary_operations, **unary_operations_options)

  _let(variable, expression)
end

#let_simplified(variable, expression, *unary_operations, **unary_operations_options) ⇒ Object



45
46
47
48
49
50
51
52
53
54
# File 'lib/maxima/command.rb', line 45

def let_simplified(variable, expression, *unary_operations, **unary_operations_options)
  unary_operations_options[:expand] = true

  let(
    variable,
    expression,
    *unary_operations,
    **unary_operations_options
  )
end

#options_commandsObject



83
84
85
86
87
88
89
90
91
# File 'lib/maxima/command.rb', line 83

def options_commands()
  [].tap do |commands|
    @options.each do |option, configuration|
      # warn that option is not applicable
      next unless OPTIONS[option]
      commands << OPTIONS[option].call(configuration)
    end
  end
end

#output_variables(output_variable_map) ⇒ Object



166
167
168
169
170
171
172
173
174
175
# File 'lib/maxima/command.rb', line 166

def output_variables(output_variable_map)
  output_variable_map = Command.expand_output_variable_map(output_variable_map)

  input, output = run_shell(output_variable_map.keys).values_at(:input, :output)

  eol_maxima_characters = Command.eol_maxima_characters(input)
  extracted_outputs = Command.extract_outputs(output, eol_maxima_characters)

  Command.convert_output_to_variables(output_variable_map, extracted_outputs)
end

#run_shell(extract_variables = nil, debug: ) ⇒ Object



93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
# File 'lib/maxima/command.rb', line 93

def run_shell(extract_variables = nil, debug: ENV["DEBUG_RB_MAXIMA"])
  inputs = [*dependencies_input, *options_commands(), *@commands]

  inputs << "grind(#{extract_variables.join(', ')})" if extract_variables
  input = inputs.join("$\n") + "$\n"

  output = with_debug(debug, input) do
    Helper.spawn_silenced_shell_process("maxima --quiet --run-string '#{input}'")
  end

  {
    input:  input,
    output: output
  }
end

#with_debug(debug, input) ⇒ Object



109
110
111
112
113
114
115
116
117
118
# File 'lib/maxima/command.rb', line 109

def with_debug(debug, input)
  return yield unless debug

  uuid = SecureRandom.uuid[0..6]
  puts input.lines.map { |s| "#{uuid}>>>\t#{s}" }.join

  yield.tap do |output|
    puts output.lines.map { |s| "#{uuid}<<<\t#{s}" }.join
  end
end

#with_options(options) ⇒ Object



22
23
24
# File 'lib/maxima/command.rb', line 22

def with_options(options)
  self.tap { @options.merge!(options) }
end