sweet-moon 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +2 -0
- data/.rspec +1 -0
- data/.rubocop.yml +40 -0
- data/Gemfile +12 -0
- data/Gemfile.lock +61 -0
- data/README.md +1149 -0
- data/components/api.rb +83 -0
- data/components/injections/injections_503.rb +21 -0
- data/components/injections/injections_514.rb +29 -0
- data/components/injections/injections_542.rb +49 -0
- data/components/injections.rb +11 -0
- data/components/interpreters/50/function.rb +52 -0
- data/components/interpreters/50/interpreter.rb +105 -0
- data/components/interpreters/50/reader.rb +65 -0
- data/components/interpreters/50/table.rb +99 -0
- data/components/interpreters/50/writer.rb +45 -0
- data/components/interpreters/51/function.rb +52 -0
- data/components/interpreters/51/interpreter.rb +104 -0
- data/components/interpreters/51/reader.rb +65 -0
- data/components/interpreters/51/table.rb +60 -0
- data/components/interpreters/51/writer.rb +45 -0
- data/components/interpreters/54/function.rb +52 -0
- data/components/interpreters/54/interpreter.rb +100 -0
- data/components/interpreters/54/reader.rb +65 -0
- data/components/interpreters/54/table.rb +60 -0
- data/components/interpreters/54/writer.rb +45 -0
- data/components/interpreters.rb +11 -0
- data/components/io.rb +11 -0
- data/config/tests.sample.yml +15 -0
- data/controllers/api.rb +143 -0
- data/controllers/cli/cli.rb +32 -0
- data/controllers/cli/help.rb +24 -0
- data/controllers/cli/signatures.rb +179 -0
- data/controllers/cli/version.rb +14 -0
- data/controllers/interpreter.rb +68 -0
- data/controllers/state.rb +74 -0
- data/dsl/api.rb +31 -0
- data/dsl/cache.rb +118 -0
- data/dsl/concerns/fennel.rb +13 -0
- data/dsl/concerns/packages.rb +37 -0
- data/dsl/errors.rb +28 -0
- data/dsl/fennel.rb +47 -0
- data/dsl/global.rb +42 -0
- data/dsl/state.rb +104 -0
- data/dsl/sweet_moon.rb +53 -0
- data/logic/api.rb +17 -0
- data/logic/interpreter.rb +84 -0
- data/logic/interpreters/interpreter_50.rb +34 -0
- data/logic/interpreters/interpreter_51.rb +37 -0
- data/logic/interpreters/interpreter_54.rb +41 -0
- data/logic/io.rb +6 -0
- data/logic/options.rb +14 -0
- data/logic/shared_object.rb +52 -0
- data/logic/signature.rb +258 -0
- data/logic/signatures/ffi_types.rb +27 -0
- data/logic/signatures/signatures_322.rb +418 -0
- data/logic/signatures/signatures_401.rb +243 -0
- data/logic/signatures/signatures_503.rb +575 -0
- data/logic/signatures/signatures_514.rb +460 -0
- data/logic/signatures/signatures_542.rb +591 -0
- data/logic/spec.rb +13 -0
- data/logic/tables.rb +32 -0
- data/ports/in/dsl/sweet-moon/errors.rb +3 -0
- data/ports/in/dsl/sweet-moon.rb +1 -0
- data/ports/in/shell/sweet-moon +5 -0
- data/ports/in/shell.rb +21 -0
- data/ports/out/shell.rb +9 -0
- data/sweet-moon.gemspec +35 -0
- metadata +137 -0
    
        data/controllers/api.rb
    ADDED
    
    | @@ -0,0 +1,143 @@ | |
| 1 | 
            +
            require_relative '../components/injections'
         | 
| 2 | 
            +
            require_relative '../components/api'
         | 
| 3 | 
            +
            require_relative '../components/io'
         | 
| 4 | 
            +
            require_relative '../dsl/errors'
         | 
| 5 | 
            +
             | 
| 6 | 
            +
            require_relative '../logic/api'
         | 
| 7 | 
            +
            require_relative '../logic/shared_object'
         | 
| 8 | 
            +
             | 
| 9 | 
            +
            module Controller
         | 
| 10 | 
            +
              API = {
         | 
| 11 | 
            +
                handle!: ->(options) {
         | 
| 12 | 
            +
                  shared_objects = API[:elect_shared_objects!].(options[:shared_objects])
         | 
| 13 | 
            +
             | 
| 14 | 
            +
                  api = Component::API[:open!].(shared_objects)
         | 
| 15 | 
            +
             | 
| 16 | 
            +
                  api_reference = API[:elect_api_reference!].(
         | 
| 17 | 
            +
                    api.ffi_libraries, options[:api_reference]
         | 
| 18 | 
            +
                  )
         | 
| 19 | 
            +
             | 
| 20 | 
            +
                  injections = Component::Injections[api_reference[:version]]
         | 
| 21 | 
            +
                  injections = if injections
         | 
| 22 | 
            +
                                 injections[:injections]
         | 
| 23 | 
            +
                               else
         | 
| 24 | 
            +
                                 { macros: {},
         | 
| 25 | 
            +
                                   callbacks: [] }
         | 
| 26 | 
            +
                               end
         | 
| 27 | 
            +
             | 
| 28 | 
            +
                  component = Component::API[:attach!].(api, api_reference, injections)
         | 
| 29 | 
            +
             | 
| 30 | 
            +
                  component[:meta] = {
         | 
| 31 | 
            +
                    options: options,
         | 
| 32 | 
            +
                    elected: {
         | 
| 33 | 
            +
                      shared_objects: shared_objects.map { |o| o[:path] },
         | 
| 34 | 
            +
                      api_reference: api_reference[:version]
         | 
| 35 | 
            +
                    }
         | 
| 36 | 
            +
                  }
         | 
| 37 | 
            +
             | 
| 38 | 
            +
                  component
         | 
| 39 | 
            +
                },
         | 
| 40 | 
            +
             | 
| 41 | 
            +
                elect_shared_objects!: ->(paths) {
         | 
| 42 | 
            +
                  candidates = []
         | 
| 43 | 
            +
                  shared_objects = []
         | 
| 44 | 
            +
             | 
| 45 | 
            +
                  if paths&.size&.positive?
         | 
| 46 | 
            +
                    candidates = paths
         | 
| 47 | 
            +
             | 
| 48 | 
            +
                    shared_objects = Component::IO[:reject_non_existent!].(paths).map do |path|
         | 
| 49 | 
            +
                      { path: path }
         | 
| 50 | 
            +
                    end
         | 
| 51 | 
            +
                  else
         | 
| 52 | 
            +
                    bases = %w[/usr/lib /usr/lib/* /usr/local/lib /opt/local/lib]
         | 
| 53 | 
            +
             | 
| 54 | 
            +
                    # XDG
         | 
| 55 | 
            +
                    if ENV['HOME']
         | 
| 56 | 
            +
                      bases << "#{ENV['HOME']}/.local/lib"
         | 
| 57 | 
            +
                      bases << "#{ENV['HOME']}/.local/lib/*"
         | 
| 58 | 
            +
                    end
         | 
| 59 | 
            +
             | 
| 60 | 
            +
                    bases.each do |base|
         | 
| 61 | 
            +
                      candidates.concat Component::IO[:find_by_pattern!].("#{base}/liblua*so*")
         | 
| 62 | 
            +
                      candidates.concat Component::IO[:find_by_pattern!].("#{base}/liblua*dylib*")
         | 
| 63 | 
            +
                    end
         | 
| 64 | 
            +
             | 
| 65 | 
            +
                    candidates = Component::IO[:reject_non_existent!].(candidates)
         | 
| 66 | 
            +
             | 
| 67 | 
            +
                    shared_objects = Logic::SharedObject[:choose].(candidates)
         | 
| 68 | 
            +
                  end
         | 
| 69 | 
            +
             | 
| 70 | 
            +
                  if shared_objects.size.zero?
         | 
| 71 | 
            +
                    raise SweetMoon::Errors::SweetMoonError,
         | 
| 72 | 
            +
                          "Lua shared object (liblua.so) not found: #{candidates}"
         | 
| 73 | 
            +
                  end
         | 
| 74 | 
            +
             | 
| 75 | 
            +
                  shared_objects
         | 
| 76 | 
            +
                },
         | 
| 77 | 
            +
             | 
| 78 | 
            +
                elect_api_reference!: ->(ffi_libraries, api_reference) {
         | 
| 79 | 
            +
                  availabe_candidates = Logic::API[:candidates].values
         | 
| 80 | 
            +
             | 
| 81 | 
            +
                  if api_reference
         | 
| 82 | 
            +
                    availabe_candidates = availabe_candidates.select do |candidate|
         | 
| 83 | 
            +
                      candidate[:version] == api_reference
         | 
| 84 | 
            +
                    end
         | 
| 85 | 
            +
             | 
| 86 | 
            +
                    if availabe_candidates.size.zero?
         | 
| 87 | 
            +
                      raise SweetMoon::Errors::SweetMoonError,
         | 
| 88 | 
            +
                            "API Reference #{api_reference} not available."
         | 
| 89 | 
            +
                    end
         | 
| 90 | 
            +
                  end
         | 
| 91 | 
            +
             | 
| 92 | 
            +
                  candidates = API[:calculate_compatibility!].(
         | 
| 93 | 
            +
                    availabe_candidates, ffi_libraries
         | 
| 94 | 
            +
                  )
         | 
| 95 | 
            +
             | 
| 96 | 
            +
                  candidates.sort_by do |_, functions|
         | 
| 97 | 
            +
                    functions[:found]
         | 
| 98 | 
            +
                  end.reverse
         | 
| 99 | 
            +
             | 
| 100 | 
            +
                  # TODO: This is the best strategy?
         | 
| 101 | 
            +
                  # version = candidates.sort_by do |_, functions|
         | 
| 102 | 
            +
                  #   functions[:proportion]
         | 
| 103 | 
            +
                  # end.reverse.first.first
         | 
| 104 | 
            +
             | 
| 105 | 
            +
                  version = candidates.sort_by do |_, functions|
         | 
| 106 | 
            +
                    functions[:found] * functions[:proportion]
         | 
| 107 | 
            +
                  end.reverse.first.first
         | 
| 108 | 
            +
             | 
| 109 | 
            +
                  Logic::API[:candidates][version]
         | 
| 110 | 
            +
                },
         | 
| 111 | 
            +
             | 
| 112 | 
            +
                calculate_compatibility!: ->(availabe_candidates, ffi_libraries) {
         | 
| 113 | 
            +
                  candidates = {}
         | 
| 114 | 
            +
             | 
| 115 | 
            +
                  availabe_candidates.each do |candidate|
         | 
| 116 | 
            +
                    candidates[candidate[:version]] = {
         | 
| 117 | 
            +
                      found: 0,
         | 
| 118 | 
            +
                      expected: (
         | 
| 119 | 
            +
                        candidate[:signatures][:functions].size +
         | 
| 120 | 
            +
                        candidate[:signatures][:macros].size
         | 
| 121 | 
            +
                      )
         | 
| 122 | 
            +
                    }
         | 
| 123 | 
            +
                    candidate[:signatures][:functions].each do |signature|
         | 
| 124 | 
            +
                      function = signature[:ffi].first
         | 
| 125 | 
            +
             | 
| 126 | 
            +
                      ffi_libraries.each do |ffi_library|
         | 
| 127 | 
            +
                        if ffi_library.find_function(function.to_s)
         | 
| 128 | 
            +
                          candidates[candidate[:version]][:found] += 1
         | 
| 129 | 
            +
                          break
         | 
| 130 | 
            +
                        end
         | 
| 131 | 
            +
                      end
         | 
| 132 | 
            +
                    end
         | 
| 133 | 
            +
             | 
| 134 | 
            +
                    candidates[candidate[:version]][:proportion] = (
         | 
| 135 | 
            +
                      candidates[candidate[:version]][:found].to_f /
         | 
| 136 | 
            +
                      candidates[candidate[:version]][:expected]
         | 
| 137 | 
            +
                    )
         | 
| 138 | 
            +
                  end
         | 
| 139 | 
            +
             | 
| 140 | 
            +
                  candidates
         | 
| 141 | 
            +
                }
         | 
| 142 | 
            +
              }
         | 
| 143 | 
            +
            end
         | 
| @@ -0,0 +1,32 @@ | |
| 1 | 
            +
            require 'yaml'
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            require_relative '../../ports/out/shell'
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            module Controller
         | 
| 6 | 
            +
              module CLI
         | 
| 7 | 
            +
                CLI = {
         | 
| 8 | 
            +
                  handle!: ->(arguments, _fennel = false) {
         | 
| 9 | 
            +
                    options = {}
         | 
| 10 | 
            +
             | 
| 11 | 
            +
                    arguments = arguments.select do |argument|
         | 
| 12 | 
            +
                      if argument[/^-/]
         | 
| 13 | 
            +
                        options[argument] = true
         | 
| 14 | 
            +
                        false
         | 
| 15 | 
            +
                      else
         | 
| 16 | 
            +
                        true
         | 
| 17 | 
            +
                      end
         | 
| 18 | 
            +
                    end
         | 
| 19 | 
            +
             | 
| 20 | 
            +
                    if options['-i']
         | 
| 21 | 
            +
                      return Port::Out::Shell[:dispatch!].(YAML.dump({ TODO: true }))
         | 
| 22 | 
            +
                    end
         | 
| 23 | 
            +
             | 
| 24 | 
            +
                    input = arguments.first
         | 
| 25 | 
            +
             | 
| 26 | 
            +
                    output = options['-e'] ? "TODO eval #{input}" : "TODO file #{input}"
         | 
| 27 | 
            +
             | 
| 28 | 
            +
                    Port::Out::Shell[:dispatch!].(output) if options['-o']
         | 
| 29 | 
            +
                  }
         | 
| 30 | 
            +
                }
         | 
| 31 | 
            +
              end
         | 
| 32 | 
            +
            end
         | 
| @@ -0,0 +1,24 @@ | |
| 1 | 
            +
            require_relative '../../ports/out/shell'
         | 
| 2 | 
            +
            require_relative '../../logic/spec'
         | 
| 3 | 
            +
             | 
| 4 | 
            +
            module Controller
         | 
| 5 | 
            +
              module CLI
         | 
| 6 | 
            +
                Help = {
         | 
| 7 | 
            +
                  handle!: -> {
         | 
| 8 | 
            +
                    Port::Out::Shell[:dispatch!].(
         | 
| 9 | 
            +
                      "\n#{Logic::Spec[:command]} #{Logic::Spec[:version]}\n\n" \
         | 
| 10 | 
            +
                      "usage:\n" \
         | 
| 11 | 
            +
                      "  #{Logic::Spec[:command]} version\n"\
         | 
| 12 | 
            +
                      "  #{Logic::Spec[:command]} signatures /lua/source [output.rb]\n"\
         | 
| 13 | 
            +
                      "  #{Logic::Spec[:command]} lua -i\n"\
         | 
| 14 | 
            +
                      "  #{Logic::Spec[:command]} lua file.lua [-o]\n"\
         | 
| 15 | 
            +
                      "  #{Logic::Spec[:command]} lua -e \"print(1 + 2);\" [-o]\n"\
         | 
| 16 | 
            +
                      "  #{Logic::Spec[:command]} fennel -i\n"\
         | 
| 17 | 
            +
                      "  #{Logic::Spec[:command]} fennel file.fnl [-o]\n"\
         | 
| 18 | 
            +
                      "  #{Logic::Spec[:command]} fennel -e \"(+ 1 2)\" [-o]"\
         | 
| 19 | 
            +
                      "\n\n"
         | 
| 20 | 
            +
                    )
         | 
| 21 | 
            +
                  }
         | 
| 22 | 
            +
                }
         | 
| 23 | 
            +
              end
         | 
| 24 | 
            +
            end
         | 
| @@ -0,0 +1,179 @@ | |
| 1 | 
            +
            require 'pp'
         | 
| 2 | 
            +
            require 'yaml'
         | 
| 3 | 
            +
             | 
| 4 | 
            +
            require_relative '../../components/io'
         | 
| 5 | 
            +
            require_relative '../../logic/io'
         | 
| 6 | 
            +
            require_relative '../../logic/signature'
         | 
| 7 | 
            +
            require_relative '../../ports/out/shell'
         | 
| 8 | 
            +
             | 
| 9 | 
            +
            module Controller
         | 
| 10 | 
            +
              module CLI
         | 
| 11 | 
            +
                Signatures = {
         | 
| 12 | 
            +
                  handle!: ->(source_path, output_path = nil) {
         | 
| 13 | 
            +
                    functions = Signatures[:functions_from_shared_objects!].(source_path)
         | 
| 14 | 
            +
                    signatures, types = Signatures[:signatures_from_headers!].(source_path)
         | 
| 15 | 
            +
             | 
| 16 | 
            +
                    result = Signatures[:match_functions_and_signatures!].(
         | 
| 17 | 
            +
                      functions, signatures, types
         | 
| 18 | 
            +
                    )
         | 
| 19 | 
            +
             | 
| 20 | 
            +
                    primitives = Signatures[:collect_primitives!].(result[:attachables])
         | 
| 21 | 
            +
             | 
| 22 | 
            +
                    Signatures[:print_samples!].(result, functions, signatures, primitives)
         | 
| 23 | 
            +
             | 
| 24 | 
            +
                    return if output_path.nil?
         | 
| 25 | 
            +
             | 
| 26 | 
            +
                    output = {
         | 
| 27 | 
            +
                      functions: result[:attachables].values.map do |a|
         | 
| 28 | 
            +
                                   { source: a[:source], ffi: a[:ffi] }
         | 
| 29 | 
            +
                                 end,
         | 
| 30 | 
            +
                      macros: result[:macros].values.map do |a|
         | 
| 31 | 
            +
                                { source: a[:source], name: a[:name],
         | 
| 32 | 
            +
                                  input: a[:input].map { |i| i[:name] } }
         | 
| 33 | 
            +
                              end
         | 
| 34 | 
            +
                    }
         | 
| 35 | 
            +
             | 
| 36 | 
            +
                    output[:functions] = output[:functions].sort_by { |a| a[:source] }
         | 
| 37 | 
            +
                    output[:macros] = output[:macros].sort_by { |a| a[:source] }
         | 
| 38 | 
            +
             | 
| 39 | 
            +
                    Component::IO[:write!].(output_path, output.pretty_inspect)
         | 
| 40 | 
            +
             | 
| 41 | 
            +
                    Port::Out::Shell[:dispatch!].(
         | 
| 42 | 
            +
                      "\n > attachables dumped to: \"#{output_path}\""
         | 
| 43 | 
            +
                    )
         | 
| 44 | 
            +
                  },
         | 
| 45 | 
            +
             | 
| 46 | 
            +
                  print_samples!: ->(result, functions, signatures, primitives) {
         | 
| 47 | 
            +
                    Port::Out::Shell[:dispatch!].("#{functions.size} functions:\n")
         | 
| 48 | 
            +
                    2.times { Port::Out::Shell[:dispatch!].(" #{functions.sample}") }
         | 
| 49 | 
            +
             | 
| 50 | 
            +
                    Port::Out::Shell[:dispatch!].("\n#{signatures.size} signatures:\n")
         | 
| 51 | 
            +
                    2.times { Port::Out::Shell[:dispatch!].(" #{signatures.sample}") }
         | 
| 52 | 
            +
             | 
| 53 | 
            +
                    Port::Out::Shell[:dispatch!].("\n#{result[:attachables].size} attachables:\n")
         | 
| 54 | 
            +
                    2.times do
         | 
| 55 | 
            +
                      Port::Out::Shell[:dispatch!].(
         | 
| 56 | 
            +
                        " #{result[:attachables].values.sample[:name]}"
         | 
| 57 | 
            +
                      )
         | 
| 58 | 
            +
                    end
         | 
| 59 | 
            +
             | 
| 60 | 
            +
                    Port::Out::Shell[:dispatch!].("\n#{result[:macros].size} macros:\n")
         | 
| 61 | 
            +
                    2.times do
         | 
| 62 | 
            +
                      Port::Out::Shell[:dispatch!].(" #{result[:macros].values.sample[:name]}")
         | 
| 63 | 
            +
                    end
         | 
| 64 | 
            +
             | 
| 65 | 
            +
                    Port::Out::Shell[:dispatch!].("\n#{result[:missing].size} missing:\n")
         | 
| 66 | 
            +
                    (result[:missing].size < 3 ? 1 : 3).times do
         | 
| 67 | 
            +
                      Port::Out::Shell[:dispatch!].(" #{result[:missing].sample}")
         | 
| 68 | 
            +
                    end
         | 
| 69 | 
            +
             | 
| 70 | 
            +
                    Port::Out::Shell[:dispatch!].("\n#{primitives.size} primitives:\n")
         | 
| 71 | 
            +
                    Port::Out::Shell[:dispatch!].(
         | 
| 72 | 
            +
                      YAML.dump(primitives).lines[1..-1].map { |line| "  #{line}" }
         | 
| 73 | 
            +
                    )
         | 
| 74 | 
            +
                  },
         | 
| 75 | 
            +
             | 
| 76 | 
            +
                  collect_primitives!: ->(attachables) {
         | 
| 77 | 
            +
                    types = {}
         | 
| 78 | 
            +
             | 
| 79 | 
            +
                    attachables.each_value do |attachable|
         | 
| 80 | 
            +
                      unless attachable[:output][:pointer]
         | 
| 81 | 
            +
                        if types[attachable[:output][:primitive]].nil?
         | 
| 82 | 
            +
                          types[attachable[:output][:primitive]] = 0
         | 
| 83 | 
            +
                        end
         | 
| 84 | 
            +
                        types[attachable[:output][:primitive]] += 1
         | 
| 85 | 
            +
                      end
         | 
| 86 | 
            +
             | 
| 87 | 
            +
                      attachable[:input].each do |input|
         | 
| 88 | 
            +
                        unless input[:pointer]
         | 
| 89 | 
            +
                          types[input[:primitive]] = 0 if types[input[:primitive]].nil?
         | 
| 90 | 
            +
                          types[input[:primitive]] += 1
         | 
| 91 | 
            +
                        end
         | 
| 92 | 
            +
                      end
         | 
| 93 | 
            +
                    end
         | 
| 94 | 
            +
             | 
| 95 | 
            +
                    types
         | 
| 96 | 
            +
                  },
         | 
| 97 | 
            +
             | 
| 98 | 
            +
                  match_functions_and_signatures!: ->(functions, signatures, types) {
         | 
| 99 | 
            +
                    exists = {}
         | 
| 100 | 
            +
             | 
| 101 | 
            +
                    functions.each { |function| exists[function] = true }
         | 
| 102 | 
            +
             | 
| 103 | 
            +
                    attachables = {}
         | 
| 104 | 
            +
                    macros = {}
         | 
| 105 | 
            +
                    missing = []
         | 
| 106 | 
            +
             | 
| 107 | 
            +
                    signatures = signatures.map do |signature|
         | 
| 108 | 
            +
                      Logic::Signature[:extract_from_source].(signature, types)
         | 
| 109 | 
            +
                    end.compact
         | 
| 110 | 
            +
             | 
| 111 | 
            +
                    signatures.each do |signature|
         | 
| 112 | 
            +
                      next unless signature[:name][/^lua/i] # TODO: Is it true for Lua < 5?
         | 
| 113 | 
            +
             | 
| 114 | 
            +
                      if signature[:macro]
         | 
| 115 | 
            +
                        macros[signature[:name].to_sym] = signature
         | 
| 116 | 
            +
                      elsif exists[signature[:name]]
         | 
| 117 | 
            +
                        attachables[signature[:name].to_sym] = signature
         | 
| 118 | 
            +
                      end
         | 
| 119 | 
            +
                    end
         | 
| 120 | 
            +
             | 
| 121 | 
            +
                    attachables.values.map do |attachable|
         | 
| 122 | 
            +
                      attachable[:ffi] = Logic::Signature[:to_ffi].(attachable)
         | 
| 123 | 
            +
                    end
         | 
| 124 | 
            +
             | 
| 125 | 
            +
                    functions.each do |function|
         | 
| 126 | 
            +
                      missing << function unless attachables[function.to_sym]
         | 
| 127 | 
            +
                    end
         | 
| 128 | 
            +
             | 
| 129 | 
            +
                    { attachables: attachables, macros: macros, missing: missing }
         | 
| 130 | 
            +
                  },
         | 
| 131 | 
            +
             | 
| 132 | 
            +
                  functions_from_shared_objects!: ->(path) {
         | 
| 133 | 
            +
                    shared_objects = Component::IO[:find_recursively!].(
         | 
| 134 | 
            +
                      path
         | 
| 135 | 
            +
                    ).select do |candidate|
         | 
| 136 | 
            +
                      Logic::IO[:extension].(candidate) == '.so'
         | 
| 137 | 
            +
                    end
         | 
| 138 | 
            +
             | 
| 139 | 
            +
                    puts shared_objects
         | 
| 140 | 
            +
             | 
| 141 | 
            +
                    functions = []
         | 
| 142 | 
            +
             | 
| 143 | 
            +
                    command = 'nm --demangle --dynamic --defined-only --extern-only'
         | 
| 144 | 
            +
             | 
| 145 | 
            +
                    shared_objects.each do |shared_object_path|
         | 
| 146 | 
            +
                      functions.concat(
         | 
| 147 | 
            +
                        Logic::Signature[:extract_from_nm].(`#{command} #{shared_object_path}`)
         | 
| 148 | 
            +
                      )
         | 
| 149 | 
            +
                    end
         | 
| 150 | 
            +
             | 
| 151 | 
            +
                    functions.uniq.sort
         | 
| 152 | 
            +
                  },
         | 
| 153 | 
            +
             | 
| 154 | 
            +
                  signatures_from_headers!: ->(path) {
         | 
| 155 | 
            +
                    headers = Component::IO[:find_recursively!].(path).select do |candidate|
         | 
| 156 | 
            +
                      Logic::IO[:extension].(candidate) == '.h'
         | 
| 157 | 
            +
                    end
         | 
| 158 | 
            +
             | 
| 159 | 
            +
                    sources = headers.map { |header_path| Component::IO[:read!].(header_path) }
         | 
| 160 | 
            +
             | 
| 161 | 
            +
                    types = {}
         | 
| 162 | 
            +
             | 
| 163 | 
            +
                    sources.each do |source|
         | 
| 164 | 
            +
                      types = Logic::Signature[:extract_types_from_header].(source, types)
         | 
| 165 | 
            +
                    end
         | 
| 166 | 
            +
             | 
| 167 | 
            +
                    signatures = []
         | 
| 168 | 
            +
             | 
| 169 | 
            +
                    sources.each do |source|
         | 
| 170 | 
            +
                      signatures.concat(
         | 
| 171 | 
            +
                        Logic::Signature[:extract_from_header].(source)
         | 
| 172 | 
            +
                      )
         | 
| 173 | 
            +
                    end
         | 
| 174 | 
            +
             | 
| 175 | 
            +
                    [signatures.uniq.sort, types]
         | 
| 176 | 
            +
                  }
         | 
| 177 | 
            +
                }
         | 
| 178 | 
            +
              end
         | 
| 179 | 
            +
            end
         | 
| @@ -0,0 +1,14 @@ | |
| 1 | 
            +
            require_relative '../../ports/out/shell'
         | 
| 2 | 
            +
            require_relative '../../logic/spec'
         | 
| 3 | 
            +
             | 
| 4 | 
            +
            module Controller
         | 
| 5 | 
            +
              module CLI
         | 
| 6 | 
            +
                Version = {
         | 
| 7 | 
            +
                  handle!: -> {
         | 
| 8 | 
            +
                    Port::Out::Shell[:dispatch!].(
         | 
| 9 | 
            +
                      "\n#{Logic::Spec[:command]} #{Logic::Spec[:version]}\n\n"
         | 
| 10 | 
            +
                    )
         | 
| 11 | 
            +
                  }
         | 
| 12 | 
            +
                }
         | 
| 13 | 
            +
              end
         | 
| 14 | 
            +
            end
         | 
| @@ -0,0 +1,68 @@ | |
| 1 | 
            +
            require_relative '../components/interpreters'
         | 
| 2 | 
            +
            require_relative '../logic/interpreter'
         | 
| 3 | 
            +
            require_relative 'state'
         | 
| 4 | 
            +
            require_relative '../dsl/errors'
         | 
| 5 | 
            +
             | 
| 6 | 
            +
            module Controller
         | 
| 7 | 
            +
              Interpreter = {
         | 
| 8 | 
            +
                handle!: ->(api, options = {}) {
         | 
| 9 | 
            +
                  component = {}
         | 
| 10 | 
            +
             | 
| 11 | 
            +
                  component[:interpreter] = Interpreter[:elect_interpreter!].(
         | 
| 12 | 
            +
                    api, options
         | 
| 13 | 
            +
                  )
         | 
| 14 | 
            +
             | 
| 15 | 
            +
                  component = Interpreter[:build_meta!].(component, options)
         | 
| 16 | 
            +
                  component = Interpreter[:build_runtime!].(api, component, options)
         | 
| 17 | 
            +
             | 
| 18 | 
            +
                  component
         | 
| 19 | 
            +
                },
         | 
| 20 | 
            +
             | 
| 21 | 
            +
                build_meta!: ->(component, options) {
         | 
| 22 | 
            +
                  component[:meta] = {
         | 
| 23 | 
            +
                    options: options,
         | 
| 24 | 
            +
                    elected: {
         | 
| 25 | 
            +
                      interpreter: component[:interpreter][:version]
         | 
| 26 | 
            +
                    },
         | 
| 27 | 
            +
                    runtime: {}
         | 
| 28 | 
            +
                  }
         | 
| 29 | 
            +
             | 
| 30 | 
            +
                  component
         | 
| 31 | 
            +
                },
         | 
| 32 | 
            +
             | 
| 33 | 
            +
                build_runtime!: ->(api, component, _options) {
         | 
| 34 | 
            +
                  state = State[:create!].(api[:api], component[:interpreter])[:state]
         | 
| 35 | 
            +
             | 
| 36 | 
            +
                  result = State[:eval!].(
         | 
| 37 | 
            +
                    api[:api], component[:interpreter], state, 'return _VERSION;'
         | 
| 38 | 
            +
                  )
         | 
| 39 | 
            +
             | 
| 40 | 
            +
                  is_jit = State[:get!].(
         | 
| 41 | 
            +
                    api[:api], component[:interpreter], state, 'jit', 'version'
         | 
| 42 | 
            +
                  )[:output]
         | 
| 43 | 
            +
             | 
| 44 | 
            +
                  State[:destroy!].(api[:api], component[:interpreter], state)
         | 
| 45 | 
            +
             | 
| 46 | 
            +
                  component[:meta][:runtime][:lua] = if is_jit
         | 
| 47 | 
            +
                                                       "#{is_jit} (#{result[:output]})"
         | 
| 48 | 
            +
                                                     else
         | 
| 49 | 
            +
                                                       result[:output]
         | 
| 50 | 
            +
                                                     end
         | 
| 51 | 
            +
             | 
| 52 | 
            +
                  component
         | 
| 53 | 
            +
                },
         | 
| 54 | 
            +
             | 
| 55 | 
            +
                elect_interpreter!: ->(api, options) {
         | 
| 56 | 
            +
                  result = Logic::Interpreter[:elect].(
         | 
| 57 | 
            +
                    api[:signatures], api[:meta][:elected][:api_reference], options
         | 
| 58 | 
            +
                  )
         | 
| 59 | 
            +
             | 
| 60 | 
            +
                  unless result[:compatible]
         | 
| 61 | 
            +
                    raise SweetMoon::Errors::SweetMoonError,
         | 
| 62 | 
            +
                          result[:error]
         | 
| 63 | 
            +
                  end
         | 
| 64 | 
            +
             | 
| 65 | 
            +
                  return Component::Interpreters[result[:version]][:interpreter]
         | 
| 66 | 
            +
                }
         | 
| 67 | 
            +
              }
         | 
| 68 | 
            +
            end
         | 
| @@ -0,0 +1,74 @@ | |
| 1 | 
            +
            require_relative '../dsl/errors'
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            module Controller
         | 
| 4 | 
            +
              State = {
         | 
| 5 | 
            +
                create!: ->(api, interpreter) {
         | 
| 6 | 
            +
                  result = State[:_check!].(interpreter[:create_state!].(api))
         | 
| 7 | 
            +
                  result = State[:_check!].(
         | 
| 8 | 
            +
                    interpreter[:open_standard_libraries!].(api, result[:state])
         | 
| 9 | 
            +
                  )
         | 
| 10 | 
            +
             | 
| 11 | 
            +
                  { state: result[:state] }
         | 
| 12 | 
            +
                },
         | 
| 13 | 
            +
             | 
| 14 | 
            +
                eval!: ->(api, interpreter, state, input, outputs = 1) {
         | 
| 15 | 
            +
                  result = State[:_check!].(interpreter[:push_chunk!].(api, state, input))
         | 
| 16 | 
            +
             | 
| 17 | 
            +
                  State[:_call_and_read!].(api, interpreter, result[:state], outputs)
         | 
| 18 | 
            +
                },
         | 
| 19 | 
            +
             | 
| 20 | 
            +
                load!: ->(api, interpreter, state, path, outputs = 1) {
         | 
| 21 | 
            +
                  result = State[:_check!].(
         | 
| 22 | 
            +
                    interpreter[:load_file_and_push_chunck!].(api, state, path)
         | 
| 23 | 
            +
                  )
         | 
| 24 | 
            +
             | 
| 25 | 
            +
                  State[:_call_and_read!].(api, interpreter, result[:state], outputs)
         | 
| 26 | 
            +
                },
         | 
| 27 | 
            +
             | 
| 28 | 
            +
                get!: ->(api, interpreter, state, variable, key = nil) {
         | 
| 29 | 
            +
                  result = State[:_check!].(
         | 
| 30 | 
            +
                    interpreter[:get_variable_and_push!].(api, state, variable, key)
         | 
| 31 | 
            +
                  )
         | 
| 32 | 
            +
             | 
| 33 | 
            +
                  result = State[:_check!].(interpreter[:read_and_pop!].(
         | 
| 34 | 
            +
                                              api, result[:state], -1, extra_pop: !key.nil?
         | 
| 35 | 
            +
                                            ))
         | 
| 36 | 
            +
             | 
| 37 | 
            +
                  { state: result[:state], output: result[:output] }
         | 
| 38 | 
            +
                },
         | 
| 39 | 
            +
             | 
| 40 | 
            +
                set!: ->(api, interpreter, state, variable, value) {
         | 
| 41 | 
            +
                  result = State[:_check!].(interpreter[:push_value!].(api, state, value))
         | 
| 42 | 
            +
             | 
| 43 | 
            +
                  result = State[:_check!].(
         | 
| 44 | 
            +
                    interpreter[:pop_and_set_as!].(api, result[:state], variable.to_s)
         | 
| 45 | 
            +
                  )
         | 
| 46 | 
            +
             | 
| 47 | 
            +
                  { state: result[:state], output: result[:output] }
         | 
| 48 | 
            +
                },
         | 
| 49 | 
            +
             | 
| 50 | 
            +
                destroy!: ->(api, interpreter, state) {
         | 
| 51 | 
            +
                  State[:_check!].(interpreter[:destroy_state!].(api, state))
         | 
| 52 | 
            +
             | 
| 53 | 
            +
                  { state: nil }
         | 
| 54 | 
            +
                },
         | 
| 55 | 
            +
             | 
| 56 | 
            +
                _call_and_read!: ->(api, interpreter, state, outputs = 1) {
         | 
| 57 | 
            +
                  result = State[:_check!].(interpreter[:call!].(api, state, 0, outputs))
         | 
| 58 | 
            +
                  result = State[:_check!].(interpreter[:read_all!].(api, result[:state]))
         | 
| 59 | 
            +
             | 
| 60 | 
            +
                  { state: result[:state],
         | 
| 61 | 
            +
                    output: outputs <= 1 ? result[:output].first : result[:output] }
         | 
| 62 | 
            +
                },
         | 
| 63 | 
            +
             | 
| 64 | 
            +
                _check!: ->(result) {
         | 
| 65 | 
            +
                  if result[:error]
         | 
| 66 | 
            +
                    raise SweetMoon::Errors::SweetMoonErrorHelper.for(
         | 
| 67 | 
            +
                      result[:error][:status]
         | 
| 68 | 
            +
                    ), result[:error][:value]
         | 
| 69 | 
            +
                  end
         | 
| 70 | 
            +
             | 
| 71 | 
            +
                  result
         | 
| 72 | 
            +
                }
         | 
| 73 | 
            +
              }
         | 
| 74 | 
            +
            end
         | 
    
        data/dsl/api.rb
    ADDED
    
    | @@ -0,0 +1,31 @@ | |
| 1 | 
            +
            module DSL
         | 
| 2 | 
            +
              class Api
         | 
| 3 | 
            +
                attr_reader :functions, :meta
         | 
| 4 | 
            +
             | 
| 5 | 
            +
                def initialize(component)
         | 
| 6 | 
            +
                  @component = component
         | 
| 7 | 
            +
             | 
| 8 | 
            +
                  @functions = @component[:signatures].keys
         | 
| 9 | 
            +
             | 
| 10 | 
            +
                  @meta = Struct.new(
         | 
| 11 | 
            +
                    *@component[:meta][:elected].keys
         | 
| 12 | 
            +
                  ).new(*@component[:meta][:elected].values)
         | 
| 13 | 
            +
             | 
| 14 | 
            +
                  extend @component[:api]
         | 
| 15 | 
            +
                end
         | 
| 16 | 
            +
             | 
| 17 | 
            +
                def signature_for(function)
         | 
| 18 | 
            +
                  @component[:signatures][function.to_sym]
         | 
| 19 | 
            +
                end
         | 
| 20 | 
            +
             | 
| 21 | 
            +
                def inspect
         | 
| 22 | 
            +
                  output = "#<#{self.class}:0x#{format('%016x', object_id)}"
         | 
| 23 | 
            +
             | 
| 24 | 
            +
                  variables = ['@meta'].map do |struct_name|
         | 
| 25 | 
            +
                    "#{struct_name}=#{instance_variable_get(struct_name).inspect}"
         | 
| 26 | 
            +
                  end
         | 
| 27 | 
            +
             | 
| 28 | 
            +
                  "#{output} #{variables.join(' ')}>"
         | 
| 29 | 
            +
                end
         | 
| 30 | 
            +
              end
         | 
| 31 | 
            +
            end
         | 
    
        data/dsl/cache.rb
    ADDED
    
    | @@ -0,0 +1,118 @@ | |
| 1 | 
            +
            require 'singleton'
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            require_relative '../controllers/api'
         | 
| 4 | 
            +
            require_relative '../controllers/interpreter'
         | 
| 5 | 
            +
            require_relative '../controllers/state'
         | 
| 6 | 
            +
             | 
| 7 | 
            +
            require_relative 'api'
         | 
| 8 | 
            +
            require_relative 'sweet_moon'
         | 
| 9 | 
            +
             | 
| 10 | 
            +
            class Cache
         | 
| 11 | 
            +
              include Singleton
         | 
| 12 | 
            +
             | 
| 13 | 
            +
              def clear_global!
         | 
| 14 | 
            +
                @cache[:global_state]&._unsafely_destroy
         | 
| 15 | 
            +
             | 
| 16 | 
            +
                @cache.each_key { |key| @cache.delete(key) if key[/^global/] }
         | 
| 17 | 
            +
              end
         | 
| 18 | 
            +
             | 
| 19 | 
            +
              def keys
         | 
| 20 | 
            +
                @cache.keys
         | 
| 21 | 
            +
              end
         | 
| 22 | 
            +
             | 
| 23 | 
            +
              def initialize
         | 
| 24 | 
            +
                @cache = {}
         | 
| 25 | 
            +
              end
         | 
| 26 | 
            +
             | 
| 27 | 
            +
              def global_state(options = {}, recreate: false)
         | 
| 28 | 
            +
                key = :global_state
         | 
| 29 | 
            +
             | 
| 30 | 
            +
                clear_global_state_cache!(options) if recreate
         | 
| 31 | 
            +
             | 
| 32 | 
            +
                return @cache[key] if @cache[key]
         | 
| 33 | 
            +
             | 
| 34 | 
            +
                api = Cache.instance.api_module(options, :global_api_module)
         | 
| 35 | 
            +
                interpreter = Cache.instance.interpreter_module(
         | 
| 36 | 
            +
                  api, options, :global_interpreter_module
         | 
| 37 | 
            +
                )
         | 
| 38 | 
            +
             | 
| 39 | 
            +
                @cache[key] = DSL::State.new(api, interpreter, Controller::State, options)
         | 
| 40 | 
            +
                @cache[key].instance_eval('undef :destroy', __FILE__, __LINE__)
         | 
| 41 | 
            +
             | 
| 42 | 
            +
                @cache[key]
         | 
| 43 | 
            +
              end
         | 
| 44 | 
            +
             | 
| 45 | 
            +
              def global_api(options = {}, recreate: false)
         | 
| 46 | 
            +
                key = :global_api
         | 
| 47 | 
            +
             | 
| 48 | 
            +
                clear_global_api_cache! if recreate
         | 
| 49 | 
            +
             | 
| 50 | 
            +
                @cache[key] ||= api(options, :global_api, :global_api_module)
         | 
| 51 | 
            +
              end
         | 
| 52 | 
            +
             | 
| 53 | 
            +
              def api(options = {}, key = nil, api_module_key = nil)
         | 
| 54 | 
            +
                key ||= cache_key_for(:api, options, %i[shared_objects api_reference])
         | 
| 55 | 
            +
                @cache[key] ||= DSL::Api.new(api_module(options, api_module_key))
         | 
| 56 | 
            +
              end
         | 
| 57 | 
            +
             | 
| 58 | 
            +
              def api_module(options = {}, key = nil)
         | 
| 59 | 
            +
                key ||= cache_key_for(:api_module, options, %i[shared_objects api_reference])
         | 
| 60 | 
            +
                @cache[key] ||= Controller::API[:handle!].(options)
         | 
| 61 | 
            +
              end
         | 
| 62 | 
            +
             | 
| 63 | 
            +
              def interpreter_module(api, options = {}, key = nil)
         | 
| 64 | 
            +
                key ||= cache_key_for(
         | 
| 65 | 
            +
                  :interpreter_module,
         | 
| 66 | 
            +
                  { shared_objects: api[:meta][:elected][:shared_objects],
         | 
| 67 | 
            +
                    api_reference: api[:meta][:elected][:api_reference],
         | 
| 68 | 
            +
                    interpreter: options[:interpreter], package_path: options[:package_path],
         | 
| 69 | 
            +
                    package_cpath: options[:package_cpath] },
         | 
| 70 | 
            +
                  %i[shared_objects api_reference interpreter package_path package_cpath]
         | 
| 71 | 
            +
                )
         | 
| 72 | 
            +
             | 
| 73 | 
            +
                @cache[key] ||= Controller::Interpreter[:handle!].(api, options)
         | 
| 74 | 
            +
              end
         | 
| 75 | 
            +
             | 
| 76 | 
            +
              private
         | 
| 77 | 
            +
             | 
| 78 | 
            +
              def clear_global_api_cache!
         | 
| 79 | 
            +
                @cache[:global_state]&._unsafely_destroy
         | 
| 80 | 
            +
             | 
| 81 | 
            +
                %i[global_api global_api_module
         | 
| 82 | 
            +
                   global_interpreter_module
         | 
| 83 | 
            +
                   global_state].each do |key|
         | 
| 84 | 
            +
                  @cache.delete(key)
         | 
| 85 | 
            +
                end
         | 
| 86 | 
            +
              end
         | 
| 87 | 
            +
             | 
| 88 | 
            +
              def clear_global_state_cache!(options)
         | 
| 89 | 
            +
                @cache[:global_state]&._unsafely_destroy
         | 
| 90 | 
            +
             | 
| 91 | 
            +
                %i[global_interpreter_module global_state].each do |key|
         | 
| 92 | 
            +
                  @cache.delete(key)
         | 
| 93 | 
            +
                end
         | 
| 94 | 
            +
             | 
| 95 | 
            +
                return unless options.key?(:shared_objects) || options.key?(:api_reference)
         | 
| 96 | 
            +
             | 
| 97 | 
            +
                %i[global_api global_api_module].each do |key|
         | 
| 98 | 
            +
                  @cache.delete(key)
         | 
| 99 | 
            +
                end
         | 
| 100 | 
            +
             | 
| 101 | 
            +
                global_api(options, recreate: true)
         | 
| 102 | 
            +
              end
         | 
| 103 | 
            +
             | 
| 104 | 
            +
              def cache_key_for(prefix, options = {}, relevant_keys = [])
         | 
| 105 | 
            +
                values = [prefix]
         | 
| 106 | 
            +
             | 
| 107 | 
            +
                relevant_keys.each do |key|
         | 
| 108 | 
            +
                  value = options[key] || options[key.to_s]
         | 
| 109 | 
            +
                  if value.is_a?(Array)
         | 
| 110 | 
            +
                    values << value.sort.join(':')
         | 
| 111 | 
            +
                  elsif value
         | 
| 112 | 
            +
                    values << value
         | 
| 113 | 
            +
                  end
         | 
| 114 | 
            +
                end
         | 
| 115 | 
            +
             | 
| 116 | 
            +
                values.join('|')
         | 
| 117 | 
            +
              end
         | 
| 118 | 
            +
            end
         |