sym-lang 0.2.0
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/lib/sym.rb +117 -0
- metadata +43 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 5f5501d5e9fe41654d0bc6bc55ae07e292f1e1570952af45c6b22664c625d3ad
|
|
4
|
+
data.tar.gz: 85d9c18ffdd2fb8e611bf7a894b6998ad032e374774ffaf05b60d05212e63ded
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 786a988a512ff6a097b711020ae654b88a3928a0f886c50bb29e1f66dc3f36ceca60afaab8090bf8b991c0390095a53d9171904c94c5b4f2b6f52e04a56c332f
|
|
7
|
+
data.tar.gz: 947365ae1ed80fb9e17cf1427ed7b1a4ddeaea01fd61642f89231b872846e1028dba84ac3542e315eb04c6a99b2fa74cb31490ccf264ef27841e62d6c00d4711
|
data/lib/sym.rb
ADDED
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
# sym — the SymBridge client for Ruby.
|
|
2
|
+
#
|
|
3
|
+
# require_relative "sym"
|
|
4
|
+
# mean = Sym.call("python", "statistics.mean", [[1, 2, 3]])
|
|
5
|
+
# lst = Sym.new_obj("java", "java.util.ArrayList")
|
|
6
|
+
# lst.add("from ruby")
|
|
7
|
+
# puts lst.size
|
|
8
|
+
#
|
|
9
|
+
# Ruby importing Python libs. Java objects living in Ruby variables.
|
|
10
|
+
# The hub is a Sym stdio host this client spawns and owns.
|
|
11
|
+
require "json"
|
|
12
|
+
require "open3"
|
|
13
|
+
|
|
14
|
+
module Sym
|
|
15
|
+
@stdin = @stdout = @wait = nil
|
|
16
|
+
@next_id = 0
|
|
17
|
+
|
|
18
|
+
def self.root
|
|
19
|
+
return ENV["SYM_HOME"] if ENV["SYM_HOME"] && File.directory?(File.join(ENV["SYM_HOME"], "bridge"))
|
|
20
|
+
home = File.join(Dir.home, ".sym")
|
|
21
|
+
return home if File.directory?(File.join(home, "bridge"))
|
|
22
|
+
d = File.expand_path(File.dirname(__FILE__))
|
|
23
|
+
5.times do
|
|
24
|
+
return d if File.directory?(File.join(d, "bridge"))
|
|
25
|
+
d = File.dirname(d)
|
|
26
|
+
end
|
|
27
|
+
core = `python3 -c "import sym_lang, os; print(os.path.join(os.path.dirname(sym_lang.__file__), 'core'))" 2>/dev/null`.strip
|
|
28
|
+
return core if !core.empty? && File.directory?(File.join(core, "bridge"))
|
|
29
|
+
raise "sym: no Sym found (pip install sym-lang, or set SYM_HOME)"
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def self.ensure_host
|
|
33
|
+
return if @stdin
|
|
34
|
+
@stdin, @stdout, @wait = Open3.popen2("python3", File.join(root, "bridge", "stdio_host.py"))
|
|
35
|
+
at_exit { close }
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def self.request(msg)
|
|
39
|
+
ensure_host
|
|
40
|
+
@next_id += 1
|
|
41
|
+
msg["id"] = @next_id
|
|
42
|
+
@stdin.puts(JSON.generate(msg))
|
|
43
|
+
@stdin.flush
|
|
44
|
+
loop do
|
|
45
|
+
line = @stdout.gets or raise "sym host died"
|
|
46
|
+
resp = JSON.parse(line) rescue next
|
|
47
|
+
next unless resp["id"] == @next_id
|
|
48
|
+
raise "sym: #{resp["error"]}" unless resp["ok"]
|
|
49
|
+
return wrap(resp["value"])
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def self.wrap(v)
|
|
54
|
+
if v.is_a?(Hash) && v["__sym__"] == "handle"
|
|
55
|
+
Handle.new(v)
|
|
56
|
+
elsif v.is_a?(Array)
|
|
57
|
+
v.map { |x| wrap(x) }
|
|
58
|
+
else
|
|
59
|
+
v
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def self.unwrap(args)
|
|
64
|
+
args.map { |a| a.is_a?(Handle) ? a.tag : a }
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def self.import(lang, target) = request({ "op" => "import", "lang" => lang, "target" => target })
|
|
68
|
+
def self.call(lang, target, args = []) = request({ "op" => "call", "lang" => lang, "target" => target, "args" => unwrap(args) })
|
|
69
|
+
def self.new_obj(lang, target, args = []) = request({ "op" => "new", "lang" => lang, "target" => target, "args" => unwrap(args) })
|
|
70
|
+
def self.block(lang, code) = request({ "op" => "exec", "lang" => lang, "code" => code })
|
|
71
|
+
def self.get(key) = request({ "op" => "get", "key" => key })
|
|
72
|
+
def self.set(key, value) = request({ "op" => "set", "key" => key, "value" => value })
|
|
73
|
+
def self.c_call(lib_fn, args, ret = "int", argtypes = []) = request({ "op" => "call", "lang" => "c", "target" => lib_fn, "args" => args, "ret" => ret, "argtypes" => argtypes })
|
|
74
|
+
|
|
75
|
+
def self.close
|
|
76
|
+
return unless @stdin
|
|
77
|
+
begin; request({ "op" => "shutdown" }); rescue; end
|
|
78
|
+
begin; @stdin.close; rescue; end
|
|
79
|
+
@stdin = nil
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
class Handle
|
|
83
|
+
attr_reader :tag
|
|
84
|
+
def initialize(tag); @tag = tag; end
|
|
85
|
+
def method_missing(name, *args)
|
|
86
|
+
Sym.request({ "op" => "hcall", "lang" => @tag["runtime"],
|
|
87
|
+
"handle" => @tag["id"], "method" => name.to_s,
|
|
88
|
+
"args" => Sym.unwrap(args) })
|
|
89
|
+
end
|
|
90
|
+
def respond_to_missing?(*); true; end
|
|
91
|
+
def [](key)
|
|
92
|
+
Sym.request({ "op" => "index", "lang" => @tag["runtime"],
|
|
93
|
+
"handle" => @tag["id"], "args" => [key] })
|
|
94
|
+
end
|
|
95
|
+
def to_s; "#<Sym::Handle #{@tag["runtime"]}##{@tag["id"]} #{@tag["type"]}>"; end
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
# ── selftest: this consumer against every provider ──────────
|
|
100
|
+
if __FILE__ == $0
|
|
101
|
+
checks = {
|
|
102
|
+
"java" => -> { Sym.call("java", "java.lang.Math.pow", [2, 5]) == 32.0 },
|
|
103
|
+
"js" => -> { Sym.call("js", "Math.max", [3, 9, 2]) == 9 },
|
|
104
|
+
"python" => -> { Sym.call("python", "math.sqrt", [81]) == 9.0 },
|
|
105
|
+
"php" => -> { Sym.call("php", "strtoupper", ["sym"]) == "SYM" },
|
|
106
|
+
"ruby" => -> { Sym.call("ruby", "Math.sqrt", [144]) == 12.0 },
|
|
107
|
+
"r" => -> { Sym.call("r", "mean", [[1, 2, 3, 4, 5]]) == 3 },
|
|
108
|
+
"perl" => -> { Sym.import("perl", "POSIX"); Sym.call("perl", "POSIX.floor", [3.7]) == 3 },
|
|
109
|
+
"c" => -> { (Sym.c_call("m.sqrt", [2.0], "double") - 1.41421).abs < 0.001 },
|
|
110
|
+
}
|
|
111
|
+
checks.each { |name, fn| puts " #{fn.call ? "✅" : "❌"} ruby → #{name}" }
|
|
112
|
+
lst = Sym.new_obj("java", "java.util.ArrayList")
|
|
113
|
+
lst.add("from ruby")
|
|
114
|
+
puts " #{lst.size == 1 ? "✅" : "❌"} ruby → java live object"
|
|
115
|
+
Sym.close
|
|
116
|
+
puts "MATRIX_ROW_OK ruby"
|
|
117
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: sym-lang
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.2.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Ric Kanjilal
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: bin
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2026-08-02 00:00:00.000000000 Z
|
|
12
|
+
dependencies: []
|
|
13
|
+
description:
|
|
14
|
+
email:
|
|
15
|
+
executables: []
|
|
16
|
+
extensions: []
|
|
17
|
+
extra_rdoc_files: []
|
|
18
|
+
files:
|
|
19
|
+
- lib/sym.rb
|
|
20
|
+
homepage: https://github.com/RicKanjilal/sym
|
|
21
|
+
licenses:
|
|
22
|
+
- MIT
|
|
23
|
+
metadata: {}
|
|
24
|
+
post_install_message: 'Sym core needed: pip install sym-lang'
|
|
25
|
+
rdoc_options: []
|
|
26
|
+
require_paths:
|
|
27
|
+
- lib
|
|
28
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
29
|
+
requirements:
|
|
30
|
+
- - ">="
|
|
31
|
+
- !ruby/object:Gem::Version
|
|
32
|
+
version: '0'
|
|
33
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
34
|
+
requirements:
|
|
35
|
+
- - ">="
|
|
36
|
+
- !ruby/object:Gem::Version
|
|
37
|
+
version: '0'
|
|
38
|
+
requirements: []
|
|
39
|
+
rubygems_version: 3.4.20
|
|
40
|
+
signing_key:
|
|
41
|
+
specification_version: 4
|
|
42
|
+
summary: Sym client for Ruby - use any language's libraries from Ruby
|
|
43
|
+
test_files: []
|