sweet-moon 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|