lualike 0.2.2 copy "lualike: ^0.2.2" to clipboard
lualike: ^0.2.2 copied to clipboard

A Lua-like scripting language for Dart, designed to be easy to embed and extend. It provides a simple REPL, supports Lua-like syntax, and allows for seamless integration with Dart code.

LuaLike #

GitHub release Pub Version GitHub Actions Workflow Status License issues - lualike

LuaLike is an embeddable Lua-like runtime and tooling package for Dart.

It includes a high-level bridge for running scripts from Dart, AST parsing APIs, low-level parser utilities, and the same standard-library registration surface used by the built-in string, table, math, and debug libraries.

What this package exposes #

  • package:lualike/lualike.dart Main entrypoint for embedding LuaLike, running code, selecting an engine, parsing source into ASTs, and working with values and errors.
  • package:lualike/parsers.dart Lower-level parsers for Lua format strings, binary pack formats, string parsing helpers, and Lua patterns.
  • package:lualike/library_builder.dart Public extension surface for registering libraries and builder-style native APIs from Dart.

Install #

dependencies:
  lualike: ^0.0.1-alpha.2

Then run:

dart pub get

Quick start #

import 'package:lualike/lualike.dart';

Future<void> main() async {
  final lua = LuaLike();

  lua.expose('greet', (List<Object?> args) {
    final name = args.isEmpty ? 'world' : Value.wrap(args.first).unwrap();
    return Value('Hello, $name!');
  });

  final result = await lua.execute('''
    local total = 0
    for i = 1, 4 do
      total = total + i
    end
    return greet(total)
  ''');

  print((result as Value).unwrap());
}

Features #

Dart ↔ Lua interop #

Expose Dart functions and call them from Lua, or call Lua functions from Dart. Share complex data structures like maps, lists, and tables between the two languages.

lualike.expose('getCurrentTime', () => DateTime.now().toString());
lualike.expose('pow', (num x, num y) => x * y);

await lualike.execute('''
  print("2^8 =", pow(2, 8))
  print("Time:", getCurrentTime())
''');

See example/interop_example.dart.

Lua tables as Dart maps #

Lua tables are exposed as Dart Map objects, so you can read, write, and pass them back and forth seamlessly.

// Send a config map to Lua
lua.setGlobal('config', {
  'debug': true,
  'maxRetries': 3,
});

// Read tables back from Lua
final summary = lua.getGlobal('summary')?.unwrap() as Map;
print(summary['playerLevel']);

This works for complex nested structures too — see example/dart_library_example.dart.

Error handling with pcall / xpcall #

Both synchronous and asynchronous Dart functions work with Lua's pcall and xpcall error handling, including nested protected calls.

await bridge.execute('''
  local status, result = pcall(function()
    local inner, err = pcall(function()
      error("inner error")
    end)
    return inner
  end)
''');

See example/error_handling_example.dart.

Virtual file system and modules #

Register virtual files so Lua's require can load them without a physical filesystem:

lua.fileManager.registerVirtualFile('mathutils.lua', '''
  local M = {}
  function M.factorial(n)
    if n <= 1 then return 1 end
    return n * M.factorial(n - 1)
  end
  return M
''');

await lua.execute('local m = require("mathutils"); print(m.factorial(5))');

See moduleExample() in example/dart_library_example.dart.

Custom I/O backend #

For web platforms or testing, the io library's physical file backend can be replaced with an in-memory adapter. The web/main.dart demo does this with IOLib.fileSystemProvider.setIODeviceFactory(). Check the FileSystemProvider and InMemoryIODevice source for the pattern.

AST parsing #

Parse Lua source into an AST tree without executing it, then traverse or transform the tree.

final program = parse('''
  local answer = 42
  return answer
''', url: 'example.lua');

final expression = parseExpression('a + b * c');
print(program.statements.length);
print(expression.runtimeType);

See example/lualike_example.dart.

Choose an engine #

LuaLike currently ships three execution modes:

  • EngineMode.ast Parses source and runs it directly with the AST interpreter.
  • EngineMode.luaBytecode Emits Lua-compatible bytecode and runs it through the bytecode VM. It currently passes the Lua compatibility suite, but it is still slower than EngineMode.ast.
  • EngineMode.ir Runs the experimental IR pipeline.

You can select an engine per call:

import 'package:lualike/lualike.dart';

Future<void> main() async {
  final astResult = await executeCode('return 20 + 22');
  final bytecodeResult = await executeCode(
    'return 20 + 22',
    mode: EngineMode.luaBytecode,
  );

  print((astResult as Value).unwrap());
  print((bytecodeResult as Value).unwrap());
}

Or set a process-wide default:

import 'package:lualike/lualike.dart';

void main() {
  LuaLikeConfig().defaultEngineMode = EngineMode.luaBytecode;
}

Parse without executing #

If you only need the syntax tree, use the exported parsing helpers instead of the runtime bridge:

import 'package:lualike/lualike.dart';

void main() {
  final program = parse('''
    local answer = 42
    return answer
  ''', url: 'example.lua');

  final expression = parseExpression('a + b * c');

  print(program.statements.length);
  print(expression.runtimeType);
  print(luaChunkId('example.lua'));
}

Use package:lualike/parsers.dart when you want the reusable parser implementations behind helpers such as string.format, string.pack, or Lua pattern handling.

Extend LuaLike from Dart #

The simplest extension point is LuaLike.expose():

import 'package:lualike/lualike.dart';

Future<void> main() async {
  final lua = LuaLike();

  lua.expose('double', (List<Object?> args) {
    final value = Value.wrap(args.first).unwrap() as num;
    return Value(value * 2);
  });

  final result = await lua.execute('return double(21)');
  print((result as Value).unwrap());
}

For reusable namespaced libraries, use package:lualike/library_builder.dart:

import 'package:lualike/library_builder.dart';
import 'package:lualike/lualike.dart';

class GreetingLibrary extends Library {
  @override
  String get name => 'greeting';

  @override
  void registerFunctions(LibraryRegistrationContext context) {
    final builder = BuiltinFunctionBuilder(context);

    context.define('hello', builder.create((args) {
      final who = args.isEmpty ? 'world' : Value.wrap(args.first).unwrap();
      return Value('hello, $who');
    }));

    // Inline documentation for IDE completion and doc generation:
    context.describe('hello', FunctionDoc(
      summary: 'Returns a greeting.',
      params: [DocParam('who', 'string', 'Name to greet.', optional: true)],
      returns: 'string',
      category: 'greeting',
    ));
  }
}

Future<void> main() async {
  final lua = LuaLike();
  lua.register(GreetingLibrary()); // shorthand for register + initialize

  final result = await lua.execute('return greeting.hello("LuaLike")');
  print((result as Value).unwrap());
}

This is the same registration path used by the built-in libraries in the repository. Add inline FunctionDoc metadata via context.describe() to power IDE completions and documentation generation (see doc/lsp.md).

Generate table schema docs from Dart annotations #

Annotate your Dart classes with @TableSchema() / @SchemaField() and use the table_schema builder to auto-generate TableDoc constants — no manual FieldDoc boilerplate.

1. Annotate a class

import 'package:lualike/annotations.dart';

@TableSchema(description: 'Metadata table every plugin must export.')
class PluginManifest {
  @SchemaField(description: 'Unique plugin identifier.', required: true)
  final String id;

  @SchemaField(description: 'Semantic version string.', required: true)
  final String version;

  @SchemaField(
    description: 'Runtime capabilities required.',
    type: 'string[]',
    defaultValue: [],
  )
  final List<String> capabilities;
}

Type inference is automatic — the List<String> above maps to array unless you pass an explicit type: override. Supported: Stringstring, intinteger, double/numnumber, boolboolean, Listarray, Maptable.

2. Configure build.yaml

targets:
  $default:
    builders:
      lualike|table_schema:
        enabled: true
        generate_for:
          - "lib/**_schema.dart"

3. Run the builder

dart run build_runner build

This produces a .table_schema.g.dart file next to each matching source file:

// GENERATED CODE — DO NOT MODIFY BY HAND.
final pluginManifest = TableDoc(
  name: 'PluginManifest',
  description: 'Metadata table every plugin must export.',
  fields: [
    FieldDoc(key: 'id', type: 'string',
        description: 'Unique plugin identifier.', required: true),
    FieldDoc(key: 'version', type: 'string',
        description: 'Semantic version string.', required: true),
    FieldDoc(key: 'capabilities', type: 'string[]',
        description: 'Runtime capabilities required.',
        defaultValue: [], required: false),
  ],
);

4. Register in your library

class MyLibrary extends Library {
  @override
  String get name => 'my_library';

  @override
  void registerFunctions(LibraryRegistrationContext context) {
    context.describeTable('PluginManifest', pluginManifest);
  }
}

The annotated fields, types, defaults, and constraints are all included in the generated output and appear in both LuaLS annotations and JSON manifests. See example/builder_demo for a complete walkthrough covering annotations, functions, ValueClass, constants, and build_runner automation.

Examples #

Run any example directly:

dart run example/interop_example.dart
dart run example/error_handling_example.dart
dart run example/dart_library_example.dart
dart run example/lualike_example.dart
File Demonstrates
interop_example.dart Exposing Dart functions, calling Lua from Dart, sharing data
error_handling_example.dart pcall, xpcall, async error handling, nested protected calls
dart_library_example.dart Full-featured: basic usage, value exchange, tables, modules, config, custom functions
lualike_example.dart AST parsing: method syntax, varargs, complex source snippets
builder_demo Build runner integration, @TableSchema annotations, generated docs, and library registration

LSP support for your scripts #

lualike generates LuaLS-compatible annotation stubs so your editor can provide completion, hover docs, and signature help for lualike scripts — including custom libraries you register from Dart.

Built-in stdlib only #

dart run bin/main.dart --emit-docs luals --emit-docs-output annotations.lua

Your own libraries #

Create tool/generate_metadata.dart:

import 'package:lualike/lualike.dart';
import 'package:lualike/docs.dart';
import 'package:your_project/your_project.dart';

Future<void> main() async {
  final lua = LuaLike();
  lua.vm.libraryRegistry.register(MyGameLibrary());

  await generateMetadata(
    lua,
    outputDir: 'doc/api',
    formats: {MetadataFormat.luals},
  );
}

Then point your LuaLS workspace library at the generated file. See doc/lsp.md for editor-specific configuration (Neovim, VS Code, .luarc.json).

Guides and reference material #

Repository guides:

Examples and source:

Notes on public API stability #

The supported API surface is the set of symbols exported by package:lualike/lualike.dart, package:lualike/parsers.dart, and package:lualike/library_builder.dart.

The lib/src/ tree is still visible in the repository for people who want to study or borrow implementations, but those files should be treated as internal details unless they are re-exported through one of the public libraries above.

5
likes
40
points
335
downloads

Publisher

verified publisherglenfordwilliams.com

Weekly Downloads

A Lua-like scripting language for Dart, designed to be easy to embed and extend. It provides a simple REPL, supports Lua-like syntax, and allows for seamless integration with Dart code.

Repository (GitHub)
View/report issues

Topics

#lua #scripting #interop #interpreter #repl

Funding

Consider supporting this project:

www.buymeacoffee.com

License

MIT (license)

Dependencies

analyzer, archive, args, artisanal, build, characters, collection, contextual, convert, crypto, glob, http, meta, path, petitparser, pointycastle, source_span, web, xrandom, yaml

More

Packages that depend on lualike