diff --git a/README.md b/README.md index 6ffde9b..01cc0f0 100644 --- a/README.md +++ b/README.md @@ -150,7 +150,8 @@ and raise catchable runtime errors, so `pcall` recovers from them in-band: iex> message =~ "instruction budget exceeded" true -See the [Sandboxing guide](guides/examples/sandboxing.livemd) for details. +See the runnable [Sandboxing example](guides/examples/sandboxing.livemd), or +the [Security and sandboxing](guides/sandboxing.md) guide, for details. ### Metatables and metamethods @@ -188,21 +189,21 @@ deliberate non-goal rather than a missing feature: For the live Lua 5.3 official test-suite pass count and the rationale behind each deferral, see the -[`ROADMAP.md`](https://github.com/tv-labs/lua/blob/main/ROADMAP.md). This -release is `1.0.0-rc.0`. +[`ROADMAP.md`](https://github.com/tv-labs/lua/blob/main/ROADMAP.md). ## Examples -Runnable, end-to-end scripts live in -[`examples/`](https://github.com/tv-labs/lua/blob/main/examples/README.md). Run -any of them with `mix run examples/.exs`: - -- [`examples/01_quickstart.exs`](https://github.com/tv-labs/lua/blob/main/examples/01_quickstart.exs) — eval some Lua and get the result. -- [`examples/02_userdata.exs`](https://github.com/tv-labs/lua/blob/main/examples/02_userdata.exs) — pass an Elixir struct as userdata and call methods on it from Lua. -- [`examples/03_custom_stdlib.exs`](https://github.com/tv-labs/lua/blob/main/examples/03_custom_stdlib.exs) — add an Elixir-defined function to the state and call it from Lua. -- [`examples/04_sandboxing.exs`](https://github.com/tv-labs/lua/blob/main/examples/04_sandboxing.exs) — the default sandbox plus allowing specific `os.*` ops explicitly. -- [`examples/05_chunks.exs`](https://github.com/tv-labs/lua/blob/main/examples/05_chunks.exs) — compile once, eval many times. -- [`examples/06_error_handling.exs`](https://github.com/tv-labs/lua/blob/main/examples/06_error_handling.exs) — `pcall`, structured exception fields, source/line attribution. +End-to-end, runnable [Livebook](https://livebook.dev) notebooks live in +[`guides/examples/`](https://github.com/tv-labs/lua/tree/main/guides/examples). +Open any of them in Livebook, or read them rendered under **Examples** on +[HexDocs](https://hexdocs.pm/lua): + +- [`quickstart.livemd`](https://github.com/tv-labs/lua/blob/main/guides/examples/quickstart.livemd) — eval some Lua and get the result. +- [`userdata.livemd`](https://github.com/tv-labs/lua/blob/main/guides/examples/userdata.livemd) — pass an Elixir struct as userdata and call methods on it from Lua. +- [`custom_stdlib.livemd`](https://github.com/tv-labs/lua/blob/main/guides/examples/custom_stdlib.livemd) — add an Elixir-defined function to the state and call it from Lua. +- [`sandboxing.livemd`](https://github.com/tv-labs/lua/blob/main/guides/examples/sandboxing.livemd) — the default sandbox plus allowing specific `os.*` ops explicitly. +- [`chunks.livemd`](https://github.com/tv-labs/lua/blob/main/guides/examples/chunks.livemd) — compile once, eval many times. +- [`error_handling.livemd`](https://github.com/tv-labs/lua/blob/main/guides/examples/error_handling.livemd) — `pcall`, structured exception fields, source/line attribution. ## Documentation diff --git a/guides/sandboxing.md b/guides/sandboxing.md index c2a1574..a4d76e3 100644 --- a/guides/sandboxing.md +++ b/guides/sandboxing.md @@ -31,7 +31,7 @@ the standard library: `loadstring`, `dofile` Everything else — `string`, `table`, `math`, `utf8`, the `debug` -library, metatables, coroutity-free control flow — remains available. +library, metatables, coroutine-free control flow — remains available. ```elixir # os.exit is sandboxed by default @@ -72,7 +72,7 @@ lua = Lua.new(sandboxed: []) ### Sandboxing a single path `Lua.sandbox/2` sandboxes one path on an existing VM, which is handy when -building a configuration up in instruction_count: +building a configuration up in stages: ```elixir lua = diff --git a/guides/working-with-lua.livemd b/guides/working-with-lua.livemd index b8fba86..bae759f 100644 --- a/guides/working-with-lua.livemd +++ b/guides/working-with-lua.livemd @@ -4,7 +4,7 @@ ```elixir Mix.install([ - {:lua, "~> 0.4.0"} + {:lua, "~> 1.0.0-rc"} ]) ``` @@ -24,7 +24,7 @@ Lua ```elixir code = ~LUA""" - +return 2 + 2 """ {value, %Lua{}} = Lua.eval!(code) @@ -33,7 +33,7 @@ code = ~LUA""" ``` -{[], #Lua<>} +{[4], #Lua<>} ``` ## Getting and Setting values diff --git a/lib/lua/api.ex b/lib/lua/api.ex index 9cb262d..558ab55 100644 --- a/lib/lua/api.ex +++ b/lib/lua/api.ex @@ -60,7 +60,7 @@ defmodule Lua.API do end If you don't need to write Elixir, but want to execute some Lua - to setup global variables, modify state, or expose some additonal + to setup global variables, modify state, or expose some additional APIs, you can simply return a Lua chunk directly using the `c` modifier on `Lua.sigil_LUA/2` @@ -190,27 +190,27 @@ defmodule Lua.API do deflua get_value(key), state do # Access the Lua environment - Lua.get!(lua, [key]) + Lua.get!(state, [key]) end To modify and return new state, return a tuple deflua set_value(key, value), state do # Return nothing but modify the state - {[], Lua.set!(lua, [key])} + {[], Lua.set!(state, [key], value)} end ## Using guards Since `deflua` uses non-conventional syntax to receive the current state, make sure - you specifiy the `when` clause and guards first, e.g. + you specify the `when` clause and guards first, e.g. deflua set_int(key, value) when is_integer(value), state do # Return nothing but modify the state {[], Lua.set!(state, [key])} end - Specifyiing the `when` clause and guards last will result in a confusing error message. + Specifying the `when` clause and guards last will result in a confusing error message. ## Variadic functions @@ -226,7 +226,7 @@ defmodule Lua.API do IO.puts(Enum.join(args, " ")) end - > #### @variadic behavior {: .neutal} + > #### @variadic behavior {: .neutral} > When using the `@variadic` attribute, note that it is per-function. `Lua` will > reset this attribute after every function definition, so there is no need to > manually reset it yourself diff --git a/lib/lua/parser/error.ex b/lib/lua/parser/error.ex index ab17934..e84c45d 100644 --- a/lib/lua/parser/error.ex +++ b/lib/lua/parser/error.ex @@ -1,17 +1,23 @@ defmodule Lua.Parser.Error do @moduledoc """ - Beautiful error reporting for the Lua parser. + Structured, wire-safe error reporting for the Lua parser. - Provides detailed error messages with: + Each error carries: - Source code context with line numbers - - Visual indicators pointing to the error location - - Helpful suggestions for common mistakes - - Multiple error reporting - """ + - Position information pointing to the error location + - A human-readable message and, where possible, a suggested fix + - Related errors, for multi-error reporting - alias Lua.AST.Meta + Produced when parsing fails: the parser's parse_structured/1 entry point + returns `{:error, [t()]}`. Call `to_map/2` for a JSON-serializable shape + suitable for editors, LSPs, and web frontends. + """ - @type position :: Meta.position() + @type position :: %{ + line: pos_integer(), + column: pos_integer(), + byte_offset: non_neg_integer() + } @type t :: %__MODULE__{ type: error_type(), @@ -37,9 +43,9 @@ defmodule Lua.Parser.Error do } @typedoc """ - Wire-safe representation produced by `to_map/2`. Mirrors the shape of - `Lua.VM.ErrorFormatter.to_map/3` so runtime and parse errors render - through one path. `source`, `call_stack`, and `error_kind` are constant + Wire-safe representation produced by `to_map/2`. Mirrors the shape used + for runtime errors so runtime and parse errors render through one path. + `source`, `call_stack`, and `error_kind` are constant for parse errors (no file name, stack, or error kind) and exist only for shape parity. """ @@ -146,8 +152,8 @@ defmodule Lua.Parser.Error do @doc """ Returns a wire-safe structured representation of a parse error. - The shape is identical to `Lua.VM.ErrorFormatter.to_map/3`, so runtime and - parse errors can flow through a single renderer (HTML, JSON, structured + The shape is identical to the map produced for runtime errors, so runtime + and parse errors can flow through a single renderer (HTML, JSON, structured logs). No ANSI escapes appear in any string field, and leading/trailing whitespace from the internal message/suggestion templates is trimmed. diff --git a/lib/lua/runtime_exception.ex b/lib/lua/runtime_exception.ex index ac632e0..2c9bae6 100644 --- a/lib/lua/runtime_exception.ex +++ b/lib/lua/runtime_exception.ex @@ -8,7 +8,7 @@ defmodule Lua.RuntimeException do * `:message` — formatted error string * `:original` — the underlying VM error term - * `:state` — `Lua.VM.State` at the point of failure + * `:state` — the internal VM state at the point of failure * `:line` — line number where the error was raised * `:source` — source name (filename or ``) * `:call_stack` — list of Lua frames at failure diff --git a/mix.exs b/mix.exs index 3a3bb3a..f2f33e2 100644 --- a/mix.exs +++ b/mix.exs @@ -1,9 +1,26 @@ defmodule Lua.MixProject do use Mix.Project + alias Lua.Parser.Error + alias Mix.Tasks.Lua.Eval + @url "https://github.com/tv-labs/lua" @version "1.0.0-rc.2" + # The curated public API surface rendered on HexDocs. Everything else is an + # implementation detail: its @moduledoc stays intact for source readers and + # `h Mod` in IEx, but `filter_modules/2` keeps it out of the published sidebar. + @public_modules [ + Lua, + Lua.API, + Lua.Table, + Lua.Chunk, + Lua.RuntimeException, + Lua.CompilerException, + Error, + Eval + ] + def project do [ app: :lua, @@ -34,18 +51,31 @@ defmodule Lua.MixProject do main: "Lua", source_url: @url, source_ref: "v#{@version}", + # Render only the curated public surface; keep internals in source/IEx. + filter_modules: fn module, _meta -> module in @public_modules end, + groups_for_modules: [ + Core: [Lua, Lua.API, Lua.Table, Lua.Chunk], + Errors: [Lua.RuntimeException, Lua.CompilerException, Error], + "Mix Tasks": [Eval] + ], extras: [ - "CHANGELOG.md", - "guides/working-with-lua.livemd", - "guides/sandboxing.md", - "guides/examples/quickstart.livemd", - "guides/examples/userdata.livemd", - "guides/examples/custom_stdlib.livemd", - "guides/examples/sandboxing.livemd", - "guides/examples/chunks.livemd", - "guides/examples/error_handling.livemd" + "guides/working-with-lua.livemd": [title: "Working with Lua"], + "guides/sandboxing.md": [title: "Security & Sandboxing"], + "guides/mix_tasks.md": [title: "Mix Tasks & the ~LUA sigil"], + "guides/examples/quickstart.livemd": [title: "Quickstart"], + "guides/examples/userdata.livemd": [title: "Userdata"], + "guides/examples/custom_stdlib.livemd": [title: "Custom stdlib"], + "guides/examples/sandboxing.livemd": [title: "Sandboxing"], + "guides/examples/chunks.livemd": [title: "Chunks"], + "guides/examples/error_handling.livemd": [title: "Error handling"], + "CHANGELOG.md": [] ], groups_for_extras: [ + Guides: [ + "guides/working-with-lua.livemd", + "guides/sandboxing.md", + "guides/mix_tasks.md" + ], Examples: ~r{guides/examples/} ] ]