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.
Files changed (3) hide show
  1. checksums.yaml +7 -0
  2. data/lib/sym.rb +117 -0
  3. 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: []