{"id":16278243,"url":"https://github.com/gbaptista/sweet-moon","last_synced_at":"2025-08-03T04:32:58.004Z","repository":{"id":42673845,"uuid":"472107901","full_name":"gbaptista/sweet-moon","owner":"gbaptista","description":"Lua / Fennel from Ruby and vice versa. Support to LuaJIT, Lua 5.0, and 5.1. Lua C API for Lua 5, 4, and 3. LuaRocks and fnx integration.","archived":false,"fork":false,"pushed_at":"2024-06-22T17:12:07.000Z","size":146,"stargazers_count":15,"open_issues_count":0,"forks_count":0,"subscribers_count":3,"default_branch":"main","last_synced_at":"2025-07-28T17:56:17.766Z","etag":null,"topics":["fennel","fnx","lua","luajit","ruby"],"latest_commit_sha":null,"homepage":"https://rubygems.org/gems/sweet-moon","language":"Ruby","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/gbaptista.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2022-03-20T21:32:17.000Z","updated_at":"2025-04-17T13:26:01.000Z","dependencies_parsed_at":"2024-06-22T23:39:52.302Z","dependency_job_id":"1875492a-bc5c-48dd-ba28-63630c48ab46","html_url":"https://github.com/gbaptista/sweet-moon","commit_stats":{"total_commits":51,"total_committers":1,"mean_commits":51.0,"dds":0.0,"last_synced_commit":"6e343360de1651abbf67181d851bb76fd96fdd97"},"previous_names":[],"tags_count":7,"template":false,"template_full_name":null,"purl":"pkg:github/gbaptista/sweet-moon","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gbaptista%2Fsweet-moon","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gbaptista%2Fsweet-moon/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gbaptista%2Fsweet-moon/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gbaptista%2Fsweet-moon/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/gbaptista","download_url":"https://codeload.github.com/gbaptista/sweet-moon/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gbaptista%2Fsweet-moon/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":268495882,"owners_count":24259397,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-08-03T02:00:12.545Z","response_time":2577,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["fennel","fnx","lua","luajit","ruby"],"created_at":"2024-10-10T18:57:54.851Z","updated_at":"2025-08-03T04:32:57.726Z","avatar_url":"https://github.com/gbaptista.png","language":"Ruby","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Sweet Moon\n[![Gem Version](https://badge.fury.io/rb/sweet-moon.svg)](https://badge.fury.io/rb/sweet-moon)\n\n_Sweet Moon_ is a resilient solution that makes working with [Lua](https://www.lua.org) / [Fennel](https://fennel-lang.org) from [Ruby](https://www.ruby-lang.org) and vice versa a delightful experience.\n\n![Image with Lua, Fennel, and Ruby source code examples.](https://raw.githubusercontent.com/gbaptista/assets/main/sweet-moon/sweet-moon.png)\n\n- [Supported Versions](#supported-versions)\n- [Setup and TLDR](#setup-and-tldr)\n- [Loading Configuration Files](#loading-configuration-files)\n  - [Lua Configuration Files](#lua-configuration-files)\n  - [Fennel Configuration Files](#fennel-configuration-files)\n- [Performance and Benchmarks](#performance-and-benchmarks)\n  - [Fennel and Lua Versions](#fennel-and-lua-versions)\n  - [Comparison with other Gems](comparison-with-other-gems)\n- [Interacting with a Lua State](#interacting-with-a-lua-state)\n  - [Setup](#setup)\n  - [Exchanging Data](#exchanging-data)\n    - [_eval_ and _load_](#eval-and-load)\n    - [Primitives](#primitives)\n    - [Tables, Arrays, and Hashes](#tables-arrays-and-hashes)\n    - [Functions](#functions)\n    - [Other Types](#other-types)\n    - [Lua Global vs Local Variables](#lua-global-vs-local-variables)\n  - [_destroy_ and _clear_](#destroy-and-clear)\n- [Modules, Packages and LuaRocks](#modules-packages-and-luarocks)\n  - [Integration with LuaRocks](#integration-with-luarocks)\n- [Fennel](#fennel)\n  - [Fennel Usage](#fennel-usage)\n  - [Fennel Global vs Local Variables](#fennel-global-vs-local-variables)\n  - [allowedGlobals and options](#allowedglobals-and-options)\n  - [Fennel Setup](#fennel-setup)\n  - [Integration with fnx](#integration-with-fnx)\n  - [Fennel REPL](#fennel-repl)\n- [Global vs Isolated](#global-vs-isolated)\n- [Global FFI](#global-ffi)\n- [Error Handling](#error-handling)\n  - [Ruby feat. Lua Errors](#ruby-feat-lua-errors)\n- [Where can I find .so files?](#where-can-i-find-so-files)\n- [Low-Level C API](#low-level-c-api)\n  - [The API](#the-api)\n  - [Custom Shared Objects](#custom-shared-objects)\n  - [Custom API References](#custom-api-references)\n  - [Functions, Macros and Signatures](#functions-macros-and-signatures)\n  - [Low-Level C API Example](#low-level-c-api-example)\n    - [Lua 5.4](#lua-54)\n    - [Lua 4.0](#lua-40)\n- [Development](#development)\n  - [Tests Setup](#tests-setup)\n  - [Running](#running)\n  - [Publish to RubyGems](#publish-to-rubygems)\n  - [Supporting New Versions](#supporting-new-versions)\n\n## Supported Versions\n\n_Sweet Moon_ was created to be resilient and adaptable. So it doesn't have a dependency on specific versions, and it will always try to create a working environment with whatever you have available.\n\nThat said, these are the officially tested versions:\n\nC API:\n- Lua: `3.2.2`, `4.0.1`, `5.0.3`, `5.1.4`, and `5.4.2`\n\nInterpreter:\n- Lua: `5.0`, `5.1`, and `5.4`\n\nInterpreters' Compatibility:\n- Lua: `5.0.3`, `5.1.4`, `5.1.5`, `5.2.4`, `5.3.3`, `5.4.2`, and `5.4.4`\n- LuaJIT: `2.0.5`\n\n## Setup and TLDR\n\n```sh\ngem install sweet-moon\n```\n\n\u003e **Disclaimer:** It's an early-stage project, and you should expect breaking changes.\n\n```ruby\ngem 'sweet-moon', '~\u003e 1.0.0'\n```\n\n```ruby\nrequire 'sweet-moon'\n\n# State\n\nSweetMoon.global.state.eval('return 1 + 2') # =\u003e 3\nSweetMoon.global.state.fennel.eval('(+ 2 3)') # =\u003e 5\n\nstate = SweetMoon::State.new\n\nstate.eval('return 3 + 4') # =\u003e 7\nstate.load('file.lua') # =\u003e {...}\n\nstate.fennel.eval('(+ 3 7)') # =\u003e 10\nstate.fennel.load('file.fnl') # =\u003e {...}\n\n# API\n\nSweetMoon.global.api.luaL_newstate\n\napi = SweetMoon::API.new\n\nstate = api.luaL_newstate\napi.luaL_openlibs(state)\n```\n\n## Loading Configuration Files\n\nLua as a _Configuration Language_ is a robust approach widely used in the industry for [decades](https://www.lua.org/about.html). It's a powerful alternative to _[YAML](https://yaml.org)_ or _[TOML](https://toml.io)_ and way more spread and battle-tested than _[edn](https://github.com/edn-format/edn)_.\n\n### Lua Configuration Files\n\nCreate a `.lua` file:\n\n```lua\nreturn {\n  color = \"red\",\n  dimensions = { width = 200, height = 2 * 80 },\n  values = {4, 6} }\n```\n\nLoad it:\n```ruby\nrequire 'sweet-moon'\n\nSweetMoon.global.state.load('config.lua')\n\n# =\u003e { 'color' =\u003e 'red',\n#      'dimensions' =\u003e { 'width' =\u003e 200, 'height' =\u003e 160 },\n#      'values' =\u003e [4, 6] }\n```\n\nAlternatively:\n```ruby\nrequire 'sweet-moon'\n\nstate = SweetMoon::State.new\n\nstate.load('config.lua')\n```\n\n### Fennel Configuration Files\n\nCreate a `.fnl` file:\n\n```fnl\n{:color \"red\"\n :dimensions {:width 200 :height (* 2 80)}\n :values [4 6]}\n```\n\nLoad it:\n```ruby\nrequire 'sweet-moon'\n\nSweetMoon.global.state.fennel.load('config.fnl')\n\n# =\u003e { 'color' =\u003e 'red',\n#      'dimensions' =\u003e { 'width' =\u003e 200, 'height' =\u003e 160 },\n#      'values' =\u003e [4, 6] }\n```\n\nAlternatively:\n```ruby\nrequire 'sweet-moon'\n\nstate = SweetMoon::State.new\n\nstate.fennel.load('config.fnl')\n```\n\n## Performance and Benchmarks\n\nBenchmarks created through [benchmark-ips](https://github.com/evanphx/benchmark-ips).\n\nThe task is to get a file with a source code equivalent to:\n```lua\nreturn {\n  color = \"red\",\n  dimensions = { width = 200, height = 2 * 80 },\n  values = {4, 6} }\n```\n\nAnd then bring the final Ruby representation:\n```ruby\n{ 'color' =\u003e 'red',\n  'dimensions' =\u003e { 'width' =\u003e 200, 'height' =\u003e 160 },\n  'values' =\u003e [4, 6] }\n```\n\nIt is important to note that only Lua and Fennel natively support expressions like `2 * 80`, and the other solutions have only a static number in their source.\n\n### Fennel and Lua Versions\n\nHigher is better:\n\n![Image of a chart with Benchmarks between different Lua and Fennel versions.](https://raw.githubusercontent.com/gbaptista/assets/main/sweet-moon/lua-versions.png)\n\n### Comparison with other Gems\n\nHigher is better:\n\n![Image of a chart with Benchmarks between Sweet Moon and other Gems.](https://raw.githubusercontent.com/gbaptista/assets/main/sweet-moon/other-gems.png)\n\nCompared to: [rufus-lua](https://github.com/jmettraux/rufus-lua), [YAML](https://ruby-doc.org/stdlib-3.0.1/libdoc/yaml/rdoc/YAML.html), [edn-ruby](https://github.com/relevance/edn-ruby), [toml-rb](https://github.com/emancu/toml-rb), and [toml](https://github.com/jm/toml).\n\n## Interacting with a Lua State\n\n\u003e Lua is a fast language engine with small footprint that you can [embed](https://www.lua.org/about.html) easily into your application.\n\n- [Setup](#setup)\n- [Exchanging Data](#exchanging-data)\n  - [_eval_ and _load_](#eval-and-load)\n  - [Primitives](#primitives)\n  - [Tables, Arrays, and Hashes](#tables-arrays-and-hashes)\n  - [Functions](#functions)\n  - [Other Types](#other-types)\n  - [Lua Global vs Local Variables](#lua-global-vs-local-variables)\n- [_destroy_ and _clear_](#destroy-and-clear)\n\n### Setup\n\nA state is composed of three key elements: `shared_object`, `api_reference`, and `interpreter`.\n\nFor the global state:\n```ruby\nrequire 'sweet-moon'\n\nSweetMoon.global.config(\n  shared_object: '/usr/lib/liblua.so.5.4.4',\n  api_reference: '5.4.2',\n  interpreter: '5.4'\n)\n\nSweetMoon.global.state.eval('return 1 + 1') # =\u003e 2\n```\n\nFor a new isolated state:\n```ruby\nrequire 'sweet-moon'\n\nstate = SweetMoon::State.new(\n  shared_object: '/usr/lib/liblua.so.5.4.4',\n  api_reference: '5.4.2',\n  interpreter: '5.4'\n)\n\nstate.eval('return 1 + 1') # =\u003e 2\n```\nBy default, _Sweet Moon_ will automatically identify all these elements and find the best possible combination for them. Usually, the only parameter you might want to set manually is the `shared_object`. To understand `shared_object` and `api_reference`, check [_Custom Shared Objects_](#custom-shared-objects).\n\nThe `interpreter` describes which version of _Sweet Moon's_ internal Interpreter will handle the interactions with the Lua state. The internal interpreter abstracts the Lua C API to provide methods like `state.eval`, `state.get`, etc.\n\n_Sweet Moon_ may not have an interpreter for all Lua versions, especially the too old or very specific ones. For this scenario, an error will be raised:\n\n```ruby\nrequire 'sweet-moon'\n\nSweetMoon::State.new(shared_object: '/usr/lib/liblua3.so')\n\n# =\u003e SweetMoon::Errors::SweetMoonError\n#    No compatible interpreter found for Lua C API 3.2.2\n```\n\nTo check all available Interpreters, you can:\n\n```ruby\nrequire 'sweet-moon'\n\nSweetMoon.meta.interpreters\n# =\u003e ['5.0', '5.1', '5.4']\n```\n\nYou can also check information about a state with:\n\n```ruby\nrequire 'sweet-moon'\n\nstate = SweetMoon::State.new\n\nstate.meta.shared_objects # =\u003e ['/usr/lib/liblua.so.5.4.4']\nstate.meta.api_reference # =\u003e 5.4.2\nstate.meta.interpreter   # =\u003e 5.4\nstate.meta.runtime       # =\u003e Lua 5.4\n\nstate.meta.to_h\n# =\u003e { shared_objects: ['/usr/lib/liblua.so.5.4.4'],\n#      api_reference: '5.4.2',\n#      interpreter: '5.4',\n#      runtime: 'Lua 5.4' }\n```\n\nThe same is true for the global state with `SweetMoon.global.state.meta`.\n\n### Exchanging Data\n\n#### _eval_ and _load_\n\nThe `eval` method evaluates a Lua source code, and the `load` method loads a file and evaluates its content. Both return the output of the evaluation if it exists.\n\nCaveat: The data exchange works through Lua [_global_](https://www.lua.org/pil/1.2.html) variables only.\n\n```ruby\nrequire 'sweet-moon'\n\nstate = SweetMoon::State.new\n\nstate.eval('return 2 + 2') # =\u003e 4\n```\n\n```lua\n-- source.lua\n\nfrom_lua = \"Lua Text\"\n\nreturn { data = from_ruby }\n```\n\n```ruby\nrequire 'sweet-moon'\n\nstate = SweetMoon::State.new\n\nstate.set('from_ruby', 'Ruby Text')\n\nstate.load('source.lua') # =\u003e { 'data' =\u003e 'Ruby Text' }\n\nstate.get('from_lua') # =\u003e 'Lua Text'\n```\n\n#### Primitives\n\nWith `get` and `set`, you can exchange between Lua and Ruby the following primitive types:\n- Lua: `string`, `integer`, `number`, `boolean`, and `nil`.\n- Ruby: `String`, `Symbol`, `Integer`, `Float`, `TrueClass` (`true`), `FalseClass` (`false`), and `NilClass` (`nil`).\n\n```ruby\nrequire 'sweet-moon'\n\nstate = SweetMoon::State.new\n\nstate.eval('lua_value = \"Lua Text\"') # =\u003e nil\n\nstate.get('lua_value') # =\u003e 'Lua Text'\n\nstate.set(:ruby_value, 'Ruby Text') # =\u003e nil\n\nstate.eval('return ruby_value') # =\u003e 'Ruby Text'\n```\n\nCaveats:\n\n- Ruby `Symbol` (e.g. `:value`) is converted to Lua `string`.\n- [_Floating-point arithmetic_](https://en.wikipedia.org/wiki/Floating-point_arithmetic) may be tricky when exchanging numbers between two different environments.\n\n#### Tables, Arrays, and Hashes\n\nYou can exchange `Array`, `Hash` and `table` with `get` and `set`.\n\n```ruby\nrequire 'sweet-moon'\n\nstate = SweetMoon::State.new\n\nstate.eval('lua_value = {a = \"text\", b = 1.5, c = true}') # =\u003e nil\n\nstate.get(:lua_value) # =\u003e { 'a' =\u003e 'text', 'b' =\u003e 1.5, 'c' =\u003e true }\n\nstate.eval('list = {\"a\", \"b\", \"c\"}') # =\u003e nil\n\nstate.get('list') # =\u003e ['a', 'b', 'c']\n\nstate.eval('empty = {}') # =\u003e nil\n\nstate.get(:empty) # =\u003e { }\n\nstate.set('ruby_array', [3, 'a', true]) # =\u003e nil\n\nstate.eval('return ruby_array[1]') # =\u003e 3\nstate.eval('return ruby_array[2]') # =\u003e 'a'\n\nstate.set('ruby_hash', { a: 'b', values: ['c', 'd'] }) # =\u003e nil\n\nstate.eval('return ruby_hash[\"values\"][2]') # =\u003e 'd'\n```\n\nWith `get`, you can use a second parameter to read a field:\n\n```ruby\nrequire 'sweet-moon'\n\nstate = SweetMoon::State.new\n\nstate.eval('lua_value = {a = \"text\", b = 1.5, c = true}') # =\u003e nil\n\nstate.get(:lua_value, :b) # =\u003e 1.5\n```\n\nWith `set`, you can use a second parameter to set a field:\n\n```ruby\nrequire 'sweet-moon'\n\nstate = SweetMoon::State.new\n\nstate.set(:myTable, {}) # =\u003e nil\n\nstate.set(:myTable, :a, 3) # =\u003e nil\n\nstate.eval('return myTable[\"a\"]') # =\u003e 3\n```\n\nCaveats:\n\n- Ruby `Symbol` (e.g. `:value`) is converted to Lua `string`.\n- Ruby `Hash` is converted to Lua `table`.\n- Ruby `Array` is converted to a _sequential_ Lua `table`.\n- Lua _sequential_ `table` is converted to Ruby `Array`.\n- Lua _non-sequential_ `table` is converted to Ruby `Hash`.\n- Lua **empty** `table` is converted to `Hash` (`{}`).\n- Lua _sequential_ `table` (_array_) [starts](https://www.lua.org/pil/2.5.html) at index `1`.\n\n#### Functions\n\nLua [_Functions_](https://www.lua.org/pil/5.html) are converted to Ruby [_Lambdas_](https://docs.ruby-lang.org/en/3.1/Proc.html#class-Proc-label-Lambda+and+non-lambda+semantics), where the first parameter is an array of parameters, and the second is an optional expected number of results that default to 1 (Lua _Functions_ can return [multiple results](https://www.lua.org/pil/5.1.html)).\n\n```ruby\nrequire 'sweet-moon'\n\nstate = SweetMoon::State.new\n\nstate.eval('lua_fn = function(a, b) return \"ok\", a + b; end') # =\u003e nil\n\nlua_fn = state.get(:lua_fn)\n\nlua_fn.call([1, 2]) # =\u003e 'ok'\nlua_fn.call([1, 2], 2) # =\u003e ['ok', 3]\n\nlua_fn.([1, 2]) # =\u003e 'ok'\nlua_fn.([1, 2], 2) # =\u003e ['ok', 3]\n\nstate.eval('second = function(list) return list[2]; end') # =\u003e nil\n\nsecond = state.get(:second)\n\nsecond.([%w[a b c]]) # =\u003e 'b'\n```\n\nAlternatively, you can send the `outputs` parameter:\n\n```ruby\nrequire 'sweet-moon'\n\nstate = SweetMoon::State.new\n\nstate.eval('return \"a\", \"b\"', { outputs: 2 }) # =\u003e ['a', 'b']\n```\n\nYou can call Ruby _Lambdas_ from _Lua_ as well:\n\n```ruby\nrequire 'sweet-moon'\n\nstate = SweetMoon::State.new\n\nruby_fn = lambda do |a, b|\n  return a + b\nend\n\nstate.set(:rubyFn, ruby_fn) # =\u003e nil\n\nstate.eval('return rubyFn(2, 2)') # =\u003e 4\n\nsum_list = -\u003e (list) { list.sum }\n\nstate.set('sumList', sum_list) # =\u003e nil\n\nstate.eval('return sumList({2, 3, 5})') # =\u003e 10\n```\n\n#### Other Types\n\nWe encourage you to keep a clean and simple exchange between Lua and Ruby, avoiding complex data types and bloated data structures.\n\nAnytime you try to exchange an unsupported data type, you won't get an error, but it will be converted to a string representation:\n\n```ruby\nrequire 'sweet-moon'\n\nstate = SweetMoon::State.new\n\nstate.eval('return coroutine.create(function() end)')\n# =\u003e 'thread: 0x93924850822056'\n\nstate.set('ruby_thread', Thread.new { 1 + 1 })\n\nstate.eval('return ruby_thread') # =\u003e '#\u003cThread:0x0000000000000d0c\u003e'\n```\n\nAlso, avoid exchanging complex things unnecessarily, e.g., modules:\n\n```ruby\nrequire 'sweet-moon'\n\nstate = SweetMoon::State.new\n\nstate.require_module('fennel')\n\nstate.get(:fennel) # =\u003e {...}\n# =\u003e It returns a huge chunk of data with\n#    a complex structure and mixed data types.\n#    It will work, but we encourage you to\n#    avoid that.\n```\n\nPrefer instead to extract what you need only:\n\n```ruby\nrequire 'sweet-moon'\n\nstate = SweetMoon::State.new\n\nstate.require_module('fennel')\n\nfennel_eval = state.get(:fennel, :eval)\n\nfennel_eval.(['(+ 1 1)']) # =\u003e 2\n```\n\nYou can also abstract what you need into global variables:\n\n```ruby\nrequire 'sweet-moon'\n\nstate = SweetMoon::State.new\n\nstate.require_module('fennel')\n\nstate.eval('fennel_eval = fennel.eval')\n\nfennel_eval = state.get(:fennel_eval)\n\nfennel_eval.(['(+ 1 1)']) # =\u003e 2\n```\n\n#### Lua Global vs Local Variables\n\nYou can't exchange _local_ variables, only [_global_](https://www.lua.org/pil/1.2.html) ones:\n\n```ruby\nrequire 'sweet-moon'\n\nstate = SweetMoon::State.new\n\nstate.eval('lua_value = \"Lua Text\"') # =\u003e nil\n\nstate.get('lua_value') # =\u003e 'Lua Text'\n\nstate.eval('local lua_b = \"b\"') # =\u003e nil\n\nstate.get('lua_b') # =\u003e nil\n```\n\n## _destroy_ and _clear_\n\nYou can destroy a state:\n\n```ruby\nrequire 'sweet-moon'\n\nstate = SweetMoon::State.new\n\nstate.set(:a, 1)\nstate.get(:a) # =\u003e 1\n\nstate.destroy\n\nstate.get(:a)\n# =\u003e SweetMoon::Errors::SweetMoonError\n#    The state no longer exists.\n```\n\nYou can also clear a state:\n\n```ruby\nrequire 'sweet-moon'\n\nstate = SweetMoon::State.new\n\nstate.set(:a, 1)\nstate.get(:a) # =\u003e 1\n\nstate.clear\n\nstate.get(:a) # =\u003e nil\n```\n\n## Modules, Packages and LuaRocks\n\n\u003e Check the [Modules](https://www.lua.org/manual/5.4/manual.html#6.3) documentation at the _Lua Manual_ to understand the essentials.\n\nYou can achieve everything through eval:\n\n```ruby\nrequire 'sweet-moon'\n\nstate = SweetMoon::State.new\n\nstate.eval('package.path = \"/my-modules/?.lua;\" .. package.path')\nstate.eval('package.cpath = \"/my-modules/?.so;\" .. package.cpath')\n\nstate.eval('some_package = require(\"my_module\")')\n```\n\nRegardless, we offer some helpers that you can use.\n\nAdding a path to the Lua [`package.path`](https://www.lua.org/manual/5.4/manual.html#pdf-package.path):\n\n```ruby\nrequire 'sweet-moon'\n\nstate = SweetMoon::State.new\n\nstate.add_package_path('/home/me/my-lua-modules/?.lua')\nstate.add_package_path('/home/me/my-lua-modules/?/init.lua')\n\nstate.add_package_cpath('/home/me/my-lua-modules/?.so')\n\nstate.add_package_path('/home/me/fennel/?.lua')\n\nstate.add_package_cpath('/home/me/?.so')\n\nstate.package_path\n# =\u003e ['./?.lua',\n #    './?/init.lua',\n #    '/home/me/my-lua-modules/?.lua',\n #    '/home/me/my-lua-modules/?/init.lua',\n #    '/home/me/fennel/?.lua']\n\nstate.package_cpath\n# =\u003e ['./?.so',\n#     '/home/me/my-lua-modules/?.so',\n#     '/home/me/?.so']\n```\n\nRequiring a module:\n```ruby\nrequire 'sweet-moon'\n\nstate = SweetMoon::State.new\n\nstate.require_module('supernova')\n\nstate.require_module_as('fennel', 'f')\n```\n\nYou can set packages in State constructors:\n\n```ruby\nrequire 'sweet-moon'\n\nSweetMoon::State.new(\n  package_path: '/folder/lib/?.lua',\n  package_cpath: '/lib/lib/?.so',\n)\n```\n\nAlso, you can add packages through the global config:\n\n```ruby\nrequire 'sweet-moon'\n\nSweetMoon.global.config(\n  package_path: '/folder/lib/?.lua',\n  package_cpath: '/lib/lib/?.so',\n)\n```\n\n### Integration with LuaRocks:\n\n\u003e Read more about how to use LuaRocks in the official documentation: [_Using LuaRocks_](https://github.com/luarocks/luarocks/wiki/Using-LuaRocks)\n\n[LuaRocks](https://luarocks.org) is a popular package manager for the Lua language.\n\nYou can install modules like [_supernova_](https://github.com/gbaptista/supernova#lua) with:\n\n```sh\nluarocks install supernova --local\n```\n\nYou can figure out the path for LuaRocks modules with:\n```sh\nluarocks path\n# =\u003e export LUA_PATH='.../home/me/.luarocks/share...\n```\n\nIf you set the `LUA_PATH` and `LUA_CPATH` environment variable on your system, modules installed through LuaRocks will just work.\n\nAlternatively, you can add it manually to the `package`:\n\n```ruby\nrequire 'sweet-moon'\n\nstate = SweetMoon::State.new\n\nstate.add_package_path('/home/me/.luarocks/share/lua/5.4/?.lua')\nstate.add_package_path('/home/me/.luarocks/share/lua/5.4/?/init.lua')\nstate.add_package_cpath('/home/me/.luarocks/lib/lua/5.4/?.so')\n\nstate.require_module('supernova')\n\nstate.eval('return supernova.enabled') # =\u003e true\n\nstate.require_module_as('supernova', 'sn')\n\nstate.eval('return sn.active_theme') # =\u003e 'default'\n\nputs state.eval('return sn.red(\"hello\")')  # =\u003e \"\\e[31mhello\\e[0m\"\nputs state.eval('return sn.blue(\"hello\")') # =\u003e \"\\e[34mhello\\e[0m\"\n```\n\nYou can also use the constructor:\n```ruby\nrequire 'sweet-moon'\n\nstate = SweetMoon::State.new(\n  package_path: [\n    '/home/me/.luarocks/share/lua/5.4/?.lua',\n    '/home/me/.luarocks/share/lua/5.4/?/init.lua'\n  ],\n  package_cpath: '/home/me/.luarocks/lib/lua/5.4/?.so'\n)\n```\n\nFor global:\n```ruby\nrequire 'sweet-moon'\n\nSweetMoon.global.config(\n  package_path: [\n    '/home/me/.luarocks/share/lua/5.4/?.lua',\n    '/home/me/.luarocks/share/lua/5.4/?/init.lua'\n  ],\n  package_cpath: '/home/me/.luarocks/lib/lua/5.4/?.so'\n)\n```\n\n\n## Fennel\n\n\u003e _[Fennel](https://fennel-lang.org) is a programming language that brings together the speed, simplicity, and reach of [Lua](https://www.lua.org) with the flexibility of a [lisp syntax and macro system.](https://en.wikipedia.org/wiki/Lisp_(programming_language))_\n\n### Fennel Usage\n\nEverything described for Lua is equivalent to Fennel, and you have the same capabilities, methods, and data exchanging.\n\nThe only thing needed is to prefix your calls with `.fennel` and ensure that the Fennel module is available:\n\n```ruby\nrequire 'sweet-moon'\n\nstate = SweetMoon::State.new\n\nstate.fennel.eval('(+ 1 2)') # =\u003e 3\n\nstate.fennel.eval('(set _G.mySum (fn [a b] (+ a b)))')\nstate.fennel.eval('(_G.mySum 2 3)') # =\u003e 5\n\nmySum = state.fennel.get(:mySum)\n\nmySum.([4, 5]) # =\u003e 9\n\nsum_list = -\u003e (list) { list.sum }\n\nstate.set('sumList', sum_list) # =\u003e nil\n\nstate.fennel.eval('(_G.sumList [2 3 5])') # =\u003e 10\n\nstate.fennel.load('file.fnl')\n```\n\nAlternatively:\n\n```ruby\nrequire 'sweet-moon'\n\nstate = SweetMoon::State.new.fennel\n\nstate.eval('(+ 1 2)') # =\u003e 3\n```\n\n### Fennel Global vs Local Variables\n\nFennel encourages you to explicitly use the [_`_G`_](https://www.lua.org/manual/5.4/manual.html#pdf-_G) table to access global variables:\n\n```ruby\nrequire 'sweet-moon'\n\nfennel = SweetMoon::State.new.fennel\n\nfennel.eval('(set _G.a? 2)')\n\nfennel.get('a?') # =\u003e 2\nfennel.get('_G', 'a?') # =\u003e 2\n\nfennel.set('b', 3)\n\nfennel.eval('(print _G.b)') # =\u003e 3\n```\n\nAlthough older versions have the expression `(global name \"value\")`, it's deprecated, and you should avoid using that. _Sweet Moon_ has no commitments in supporting this deprecated expression, and you should prefer the `_G` way.\n\nAs is [true for Lua](#lua-global-vs-local-variables), you can't exchange _local_ variables, only [_global_](https://www.lua.org/pil/1.2.html) ones:\n\n```ruby\nrequire 'sweet-moon'\n\nfennel = SweetMoon::State.new.fennel\n\nfennel.eval('(local name \"value\")')\n\nfennel.get('name') # =\u003e nil\n\nfennel.eval('(set _G.name \"value\")')\n\nfennel.get('name') # =\u003e \"value\"\n\nfennel.set('var-b', 35) # =\u003e nil\n\nfennel.eval('var-b') # =\u003e nil\n\nfennel.eval('_G.var-b') # =\u003e 35\n```\n\n### allowedGlobals and options\n\nAs Lua, Fennel functions may return [multiple results](https://www.lua.org/pil/5.1.html), so `eval` and `load` accept a second parameter to indicate the expected number of outputs:\n\n```fnl\n; source.fnl\n\n(fn multi [] (values \"c\" \"d\"))\n\n(multi)\n```\n\n```ruby\nrequire 'sweet-moon'\n\nfennel = SweetMoon::State.new.fennel\n\nfennel.eval('(values \"a\" \"b\")', 2) # =\u003e ['a', 'b']\nfennel.load('source.fnl', 2) # =\u003e ['c', 'd']\n```\n\nThe Fennel API offers [some options](https://fennel-lang.org/api) that `eval` and `load` accept as a third parameter:\n\n```ruby\nrequire 'sweet-moon'\n\nfennel = SweetMoon::State.new.fennel\n\nfennel.eval('(print (+ 2 3))', 1, { allowedGlobals: ['print'] }) # =\u003e 5\n\nfennel.eval('(print (+ 2 3))', 1, { allowedGlobals: [] })\n# Compile error in unknown:1 (SweetMoon::Errors::LuaRuntimeError)\n#   unknown identifier in strict mode: print\n\n# (print (+ 2 3))\n#  ^^^^^\n# * Try looking to see if there's a typo.\n# * Try using the _G table instead, eg. _G.print if you really want a global.\n# * Try moving this code to somewhere that print is in scope.\n# * Try binding print as a local in the scope of this code.\n```\n\nAlternatively, you can use the second parameter for options as well:\n\n```ruby\nrequire 'sweet-moon'\n\nfennel = SweetMoon::State.new.fennel\n\nfennel.eval('(print (+ 2 3))', { allowedGlobals: ['print'] }) # =\u003e 5\n```\n\nYou can also specify the expected outputs in the options parameter (it will be removed and not forwarded to Fennel):\n\n```ruby\nrequire 'sweet-moon'\n\nfennel = SweetMoon::State.new.fennel\n\nfennel.eval(\n  '(values \"a\" \"b\")',\n  { allowedGlobals: ['values'], outputs: 2 }\n) # =\u003e ['a', 'b']\n```\n\n### Fennel Setup\n\nTo ensure that the Fennel module is available, you can set up the [_LuaRocks_](#integration-withluarocks) integration or manually add the `package_path` for the module.\n\nYou can download the `fennel.lua` file on the [Fennel's website](https://fennel-lang.org/setup#embedding-the-fennel-compiler-in-a-lua-application).\n\nManually:\n\n```ruby\nrequire 'sweet-moon'\n\nstate = SweetMoon::State.new\n\nstate.add_package_path('/folder/fennel/?.lua')\n\nstate.fennel.eval('(+ 1 1)') # =\u003e 2\n```\n\nWith the constructor:\n\n```ruby\nrequire 'sweet-moon'\n\nfennel = SweetMoon::State.new(package_path: '/folder/fennel/?.lua').fennel\n\nfennel.eval('(+ 1 1)') # =\u003e 2\n```\n\nWith global:\n\n```ruby\nrequire 'sweet-moon'\n\nSweetMoon.global.state.add_package_path('/folder/fennel/?.lua')\n\nSweetMoon.global.state.fennel.eval('(+ 1 1)') # =\u003e 2\n```\n\nAlternatively:\n\n```ruby\nrequire 'sweet-moon'\n\nSweetMoon.global.config(package_path: '/folder/fennel/?.lua')\n\nSweetMoon.global.state.fennel.eval('(+ 1 1)') # =\u003e 2\n```\n\n### Integration with fnx\n\n[fnx](https://github.com/gbaptista/fnx) is a package manager for the Fennel language.\n\nAfter installing `fnx` and configuring it for [_Embedding_](https://github.com/gbaptista/fnx#embedding), you can:\n\n```ruby\nrequire 'sweet-moon'\n\nfennel = SweetMoon::State.new.fennel\n\nfennel.eval('(let [fnx (require :fnx)] (fnx.bootstrap!))')\n```\n\nDone. It will automatically inject all your dependencies according to your `.fnx.fnl` file, similar to using the `fnx` command.\n\nTo enforce the path for the `.fnx.fnl` file:\n\n```ruby\nfennel.eval('(let [fnx (require :fnx)] (fnx.bootstrap! \"/project/.fnx.fnl\"))')\n```\n\n### Fennel REPL\n\nIn Ruby, you can start a [REPL](https://en.wikipedia.org/wiki/Read%E2%80%93eval%E2%80%93print_loop) at any time somewhere in your code with [_pry_](https://github.com/pry/pry):\n\n```ruby\nrequire 'pry'\n\nbinding.pry\n```\n\nThe same is true for Fennel, you just need to:\n```fnl\n(let [fennel (require :fennel)]\n  (fennel.repl {}))\n```\n\nFennel's REPL won't have your _local_ values. But, you can tweak it to receive values to be checked inside the REPL:\n\n```fnl\n(fn my-repl [to-expose]\n  (let [fennel (require :fennel) env _G]\n    (each [key value (pairs to-expose)] (tset env key value))\n    (fennel.repl {:env env})))\n\n(local value \"some value\")\n\n(my-repl {:value value})\n\n; Inside the REPL:\n\n; \u003e\u003e value\n; \"some value\"\n```\n\nYou can install [_readline_](https://luarocks.org/modules/peterbillam/readline) for a better experience, e.g., autocompleting.\n\n\u003e _Check [Fennel's documentation](https://fennel-lang.org/api#start-a-configurable-repl) to learn more about the REPL._\n\n## Global vs Isolated\n\nYou can use the **global** helper that provides an _API_ and a _State_ for quick-and-dirty coding. It uses internally a Ruby [_Singleton_](https://docs.ruby-lang.org/en/3.1/Singleton.html):\n\n```ruby\nrequire 'sweet-moon'\n\nSweetMoon.global.state.eval('return 1 + 1')\n\nSweetMoon.global.api.luaL_newstate\n```\n\nYou can configure **global** with:\n```ruby\nrequire 'sweet-moon'\n\nSweetMoon.global.config(\n  shared_object: '/usr/lib/liblua.so.5.4.4',\n  api_reference: '5.4.2',\n  interpreter: '5.4'\n)\n```\n\nTo clean up, you can:\n\n```ruby\nrequire 'sweet-moon'\n\nSweetMoon.global.clear\n```\n\nAs the API is just a stateless binding to the Lua C API, you can use it without worries.\n\nYou may want to use an isolated API for scenarios like interacting with two Lua versions simultaneously:\n\n```ruby\nrequire 'sweet-moon'\n\napi_5 = SweetMoon::API.new(shared_object: '/usr/lib/liblua5.s')\napi_3 = SweetMoon::API.new(shared_object: '/usr/lib/liblua3.so')\n\napi_5.luaL_newstate\n\napi_3.luaH_new\n```\n\n\u003e _Check the caveats related to [_Global FFI_](#global-ffi) when working with multiple versions._\n\nOn the other hand, using the **global** _State_ may lead to a lot of issues. You need to consider from simple things – _\"If I load two different files, the first file may impact the state of the second one?\"_ – to more complex ones like multithreading, concurrency, etc.\n\nSo, you can at any time create a new isolated _State_ and destroy it when you don't need it anymore:\n\n```ruby\nrequire 'sweet-moon'\n\nstate = SweetMoon::State.new\n\nstate.eval('return 3 + 4') # =\u003e 7\nstate.load('file.lua') # =\u003e {...}\n\nstate.destroy\n```\n\nIt's possible to empty a state with [_clear_](#destroy-and-clear).\n\nLike the _API_, you may want to use an isolated _State_ to run Lua code in different Lua Versions simultaneously:\n\n```ruby\nrequire 'sweet-moon'\n\nstate_5 = SweetMoon::State.new(shared_object: '/usr/lib/liblua5.s')\nstate_3 = SweetMoon::State.new(shared_object: '/usr/lib/liblua3.so')\n\nstate_5.eval('return _VERSION') # =\u003e Lua 5.4\nstate_3.eval('return _VERSION') # =\u003e Lua 3.2\n```\n\n\u003e _Check the caveats related to [_Global FFI_](#global-ffi) when working with multiple versions._\n\n## Global FFI\n\nSome Lua libraries (e.g., [_readline_](https://pjb.com.au/comp/lua/readline.html) and [_luafilesystem_](https://keplerproject.github.io/luafilesystem/)) require the Lua C API functions available in the global C environment.\n\nBy default, _Sweet Moon_ enables [_Global FFI_](https://github.com/ffi/ffi/wiki/Loading-Libraries#function-visibility) to reduce friction when using popular libraries.\n\nUsing distinct Lua versions simultaneously with multiple _Shared Objects_ may be dangerous in this setup: Two APIs with the same name functions could be an issue because something will be overwritten.\n\nAlso, libraries that need Lua C API functions are compiled for a specific Lua version. If you are, e.g., using _LuaJIT_ and your library expects the _Standard Lua_, you may face issues.\n\nYou can disable _Global FFI_ at any time with:\n\n```ruby\nrequire 'sweet-moon'\n\nSweetMoon.global.config(global_ffi: false)\n\nSweetMoon::State.new(global_ffi: false)\n\nSweetMoon::API.new(global_ffi: false)\n```\n\nTo check if it's enabled or not:\n\n```ruby\nrequire 'sweet-moon'\n\nSweetMoon.global.api.meta.global_ffi # =\u003e true\nSweetMoon.global.state.meta.global_ffi # =\u003e true\n\nSweetMoon::API.new.meta.global_ffi # =\u003e true\n\nSweetMoon::State.new.meta.global_ffi # =\u003e true\n```\n\n**Caveats:**\n\nBinding globally a C API is irreversible, so if you start something with `global_ffi: true` and then change to `global_ffi: false`, it won't make the global one disappear. If you need _local_, ensure that you do it from the first line and never put anything as global throughout the entire program life cycle.\n\nAlso, the simple action of accessing `meta.global_ff` will bind the API, so you need to set your desired configuration before checking.\n\n## Error Handling\n\nThese are – hopefully – all the possible errors:\n\n```ruby\nSweetMoonError # inherits from StandardError\nLuaError # inherits from SweetMoonError\n\n# inherits from LuaError:\nLuaRuntimeError \nLuaMemoryAllocationError\nLuaMessageHandlerError\nLuaSyntaxError\nLuaFileError\n```\n\nYou can handle the errors from the `SweetMoon::Errors` namespace:\n\n```ruby\nrequire 'sweet-moon'\n\nbegin\n  SweetMoon.global.state.eval('return 1 + true')\nrescue SweetMoon::Errors::LuaRuntimeError =\u003e error\n  puts error.message\n  # =\u003e [string \"return 1 + true\"]:1: attempt to perform arithmetic on a boolean value\nend\n```\n\nOr you can _include_ the errors for a cleaner version with `sweet-moon/errors`:\n\n```ruby\nrequire 'sweet-moon'\nrequire 'sweet-moon/errors'\n\nbegin\n  SweetMoon.global.state.eval('return 1 + true')\nrescue LuaRuntimeError =\u003e error\n  puts error.message\n  # =\u003e [string \"return 1 + true\"]:1: attempt to perform arithmetic on a boolean value\nend\n```\n\n### Ruby feat. Lua Errors\n\nLua errors can be rescued inside Ruby:\n\n```lua\n-- source.lua\nerror('error from lua')\n```\n\n```ruby\nrequire 'sweet-moon'\nrequire 'sweet-moon/errors'\n\nstate = SweetMoon::State.new\n\nbegin\n  state.load('source.lua')\nrescue LuaRuntimeError =\u003e e\n  puts e.message\n  # =\u003e source.lua:2: error from lua\nend\n```\n\nRuby errors can be handled inside Lua:\n\n```ruby\nrequire 'sweet-moon'\n\nstate = SweetMoon::State.new\n\nstate.set(:rubyFn, -\u003e { raise 'error from ruby' })\n\nstate.load('source.lua')\n```\n\n```lua\n-- source.lua\nlocal status, err = pcall(rubyFn)\n\nprint(status) -- =\u003e false\n\nprint(err)\n-- [string \"    return function (...)...\"]:5: RuntimeError: error from ruby stack traceback:\n--         [string \"    return function (...)...\"]:5: in function 'rubyFn'\n--         [C]: in function 'pcall'\n--         source.lua:2: in main chunk\n```\n\nRuby errors not handled inside Lua can be rescued inside Ruby again, with an additional Lua backtrace:\n\n```lua\n-- source.lua\na = 1\n\nrubyFn()\n```\n\n```ruby\nrequire 'sweet-moon'\n\nstate = SweetMoon::State.new\n\nstate.set(:rubyFn, -\u003e { raise 'error from ruby' })\n\nbegin\n  state.load('source.lua')\nrescue RuntimeError =\u003e e\n  puts e.message # =\u003e error from ruby\n\n  puts e.backtrace.last\n  # =\u003e source.lua:4: in main chunk\nend\n```\n\nLua errors inside Lua functions can be rescued inside Ruby:\n\n```lua\n-- source.lua\nfunction luaFn()\n  error('lua function error')\nend\n```\n\n```ruby\nrequire 'sweet-moon'\nrequire 'sweet-moon/errors'\n\nstate = SweetMoon::State.new\n\nstate.load('source.lua')\n\nlua_fn = state.get(:luaFn)\n\nbegin\n  lua_fn.()\nrescue LuaRuntimeError =\u003e e\n  puts e.message # =\u003e \"source.lua:3: lua function error\"\nend\n```\n\nFor Fennel, all the examples above are equally true, with additional stack traceback as well.\n\n## Where can I find .so files?\n\nDue to the Lua's popularity, you likely have it already on your system, and _Sweet Moon_ will be able to find the files by itself.\n\nEither way, you can download it from:\n- [Lua Binaries](http://luabinaries.sourceforge.net)\n- [LuaJIT releases](http://luajit.org/download.html)\n\n## Low-Level C API\n\n- [The API](#the-api)\n- [Custom Shared Objects](#custom-shared-objects)\n- [Custom API References](#custom-api-references)\n- [Functions, Macros and Signatures](#functions-macros-and-signatures)\n- [Low-Level C API Example](#low-level-c-api-example)\n  - [Lua 5.4](#lua-54)\n  - [Lua 4.0](#lua-40)\n\n### The API\n\nYou can access a global instance of the low-level C API with:\n\n```ruby\nrequire 'sweet-moon'\n\nSweetMoon.global.api\n```\n\nFor a fresh new non-global instance:\n\n```ruby\nrequire 'sweet-moon'\n\napi = SweetMoon::API.new\n```\n\nInformations about the API:\n\n```ruby\napi.meta.shared_objects # =\u003e ['/usr/lib/liblua.so.5.4.4']\napi.meta.api_reference  # =\u003e '5.4.2'\n\napi.meta.to_h\n\n# =\u003e { shared_objects: ['/usr/lib/liblua.so.5.4.4'],\n#      api_reference: '5.4.2' }\n```\n\n### Custom Shared Objects\n\n\u003e To learn more about _Shared Objects_ and `.so` files: [_Dynamic linking_](https://en.wikipedia.org/wiki/Library_(computing)#Dynamic_linking), [_Dynamic linker_](https://en.wikipedia.org/wiki/Dynamic_linker) and [_Executable and Linkable Format_](https://en.wikipedia.org/wiki/Executable_and_Linkable_Format).\n\nBy default, _Sweet Moon_ will try to find and identify the Shared Object with the highest version available. You can customize it through:\n\n```ruby\nrequire 'sweet-moon'\n\napi = SweetMoon::API.new(shared_object: '/usr/lib/liblua.so.5.4.4')\n```\n\nFor the global instance:\n\n```ruby\nrequire 'sweet-moon'\n\nSweetMoon.global.config(shared_object: '/usr/lib/liblua.so.5.4.4')\n\nSweetMoon.global.api\n```\n\nImportant to notice that the API Reference will not always be the same version of the Shared Object:\n\n```ruby\nrequire 'sweet-moon'\n\napi = SweetMoon::API.new(shared_object: '/usr/lib/liblua.so.5.4.4')\n\napi.meta.api_reference # =\u003e \"5.4.2\"\n```\n\nThe Shared Object is from Lua **5.4.4**, and the API Reference is from Lua **5.4.2**.\n\nThis happens because it is impossible to extract function signatures from Shared Objects. So, _Sweet Moon_ will use an API Reference with the highest proportion of expected functions detected in the Shared Object as a reference.\n\nA difference in versions, for practical purposes, is not a problem, given that _Sweet Moon_ has several relevant versions to choose from.\n\n### Custom API References\n\nYou can force an specific API Reference for your Shared Object:\n\n```ruby\nrequire 'sweet-moon'\n\napi = SweetMoon::API.new(\n  shared_object: '/usr/lib/liblua.so.5.4.4',\n  api_refence: '3.2.2'\n)\napi.meta.shared_objects # =\u003e ['/usr/lib/liblua.so.5.4.4']\napi.meta.api_reference # =\u003e '3.2.2'\n```\n\nTo check all available API References you can:\n\n```ruby\nrequire 'sweet-moon'\n\nSweetMoon.meta.api_references\n# =\u003e ['3.2.2', '4.0.1', '5.0.3', '5.1.4', '5.4.2']\n```\n\n_Sweet Moon_ won't raise errors by you trying to use an API Reference different from the Shared Object, but it will only attach valid functions, so you need to know what you are doing:\n\n```ruby\nrequire 'sweet-moon'\n\napi_5 = SweetMoon::API.new(shared_object: '/usr/lib/liblua.so.5.4.4')\n\napi_3 = SweetMoon::API.new(shared_object: '/usr/lib/liblua.so.3.2.2')\n\napi_5_with_3 = SweetMoon::API.new(\n  shared_object: '/usr/lib/liblua.so.5.4.4',\n  api_reference: '3.2.2'\n)\n\napi_5.functions.size # =\u003e 159\napi_3.functions.size # =\u003e 162\napi_5_with_3.functions.size # =\u003e 20\n```\n\n### Functions, Macros, and Signatures\n\n_Sweet Moon_ will provide the available Lua-related functions for a Shared Object:\n\n```ruby\nrequire 'sweet-moon'\n\napi = SweetMoon::API.new(shared_object: '/usr/lib/liblua.so.5.4.4')\n\napi.functions.size # 159\n\napi.functions[0] # =\u003e :luaL_buffinitsize\napi.functions[1] # =\u003e :luaL_prepbuffsize\napi.functions[2] # =\u003e :luaL_checklstring\n```\n\nTo check the signature of a function you can:\n\n```ruby\napi.signature_for(:luaL_checklstring)\n# =\u003e { source: 'LUALIB_API const char *(luaL_checklstring) (lua_State *L, int arg, size_t *l);',\n#      input: %i[pointer int pointer],\n#      output: :pointer }\n\napi.signature_for(:luaL_newstate)\n# =\u003e { source: 'LUALIB_API lua_State *(luaL_newstate) (void);',\n#      input: [],\n#      output: :pointer }\n\napi.signature_for(:lua_pop)\n# =\u003e { source: '#define lua_pop(L,n) lua_settop(L, -(n)-1)',\n#      macro: true,\n#      requires: [\n#        { source: 'LUA_API void (lua_settop) (lua_State *L, int idx);',\n#          input: %i[pointer int],\n#          output: :void }\n#      ] }\n```\n\nNotice that `lua_pop` is a [macro](https://en.wikipedia.org/wiki/C_preprocessor), so the information about its signature is described differently.\n\n### Low-Level C API Example\n\nWorking at a low-level with Lua will differ from version to version, and I recommend the book [_Programming in Lua_](https://www.lua.org/pil/) according to your target version. Chapters related to _\"C API\"_ are what you will probably search for, and the [_Lua Reference Manual_](https://www.lua.org/manual/) is [also](https://www.lua.org/manual/5.4/manual.html#4) a great [source](https://www.lua.org/manual/5.4/contents.html#index) of information.\n\n#### Lua 5.4\n\nAs an example, following-ish [this reference](https://www.lua.org/pil/24.1.html), to get the result of the expression `math.pow(2, 3)`, you would do something like:\n\n```ruby\nrequire 'sweet-moon'\n\napi = SweetMoon::API.new(shared_object: '/usr/lib/liblua.so.5.4.4')\n\nstate = api.luaL_newstate\napi.luaL_openlibs(state)\n\napi.luaL_loadstring(state, 'return math.pow(2, 3);')\napi.lua_pcall(state, 0, 1, 0)\n\nresult = api.lua_tonumber(state, -1)\n\napi.lua_pop(state)\napi.lua_close(state)\n\nputs result # =\u003e 8.0\n```\n\nThis is a minimal example and does not consider things you probably should for production-ready purposes, like error handling, available stack space, type checking, etc.\n\n#### Lua 4.0\n\nAs an example, following the [manual](https://www.lua.org/manual/4.0/manual.html#5.), to get the result of the expression `2 ^ 3`, you would do something like:\n\n```ruby\nrequire 'sweet-moon'\n\napi = SweetMoon::API.new(\n  shared_objects: ['/usr/lib/liblua4.so', '/usr/lib/liblualib4.so']\n)\n\nstate = api.lua_open(0)\n\napi.lua_mathlibopen(state)\n\napi.lua_dostring(state, 'return 2 ^ 3')\n\nresult = api.lua_tonumber(state, -1)\n\napi.lua_settop(state, -2)\napi.lua_close(state)\n\nputs result # =\u003e 8.0\n```\n\nNotice that two _Shared Objects_ were necessary for this Lua version, one for the _Standard API_ and another for the _Standard Libraries_.\n\nThis is a minimal example and does not consider things you probably should for production-ready purposes, like error handling, available stack space, type checking, etc.\n\n## Development\n\n```sh\nbundle\nrubocop -a\nrspec\n```\n\n### Tests Setup\n\nTo setup tests:\n\n```\ncp config/tests.sample.yml config/tests.yml\n```\n\nClone the [sweet-moon-test](https://github.com/gbaptista/sweet-moon-test) repo somewhere:\n\n```sh\ngit clone git@github.com:gbaptista/sweet-moon-test.git\n```\n\nUpdate the [`config/tests.yml`](https://github.com/gbaptista/sweet-moon/blob/main/config/tests.sample.yml) accordingly.\n\nAlternatively: Find or build the _Shared Objects_ for your Operating System on your own.\n\nInstall the expected Lua _rocks_ described in `config/tests.yml`.\n\n### Running\n\n```sh\n./ports/in/shell/sweet-moon version\n\nbundle exec sweet-moon version\n\nbundle exec sweet-moon signatures /lua/lib/542 542.rb\n\nbundle exec ruby some/file.rb\n```\n\n### Publish to RubyGems\n\n```bash\ngem build sweet-moon.gemspec\n\ngem signin\n\ngem push sweet-moon-1.0.0.gem\n```\n\n### Supporting New Versions\n\nDownload both the source code and the libraries.\n\nExample: For [Lua 5.4.2](https://sourceforge.net/projects/luabinaries/files/5.4.2/), you would download _\"Linux Libraries\"_ and _\"Docs and Sources.\"_\n\nExtract everything to a folder, e.g., `lua-542-source-libs`.\n\nRun the command to extract the signatures:\n\n```shell\nbundle exec sweet-moon signatures /home/me/lua-542-source-libs 542.rb\n```\n\nCheck the `542.rb` file for the output and then start coding.\n\nYou can use the [`logic/signatures`](https://github.com/gbaptista/sweet-moon/tree/main/logic/signatures) folder as a reference starting point.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgbaptista%2Fsweet-moon","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fgbaptista%2Fsweet-moon","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgbaptista%2Fsweet-moon/lists"}