ApolloVM

pub package Null Safety Codecov Dart CI GitHub Tag New Commits Last Commits Pull Requests Code size License

ApolloVM is a portable VM (native, JS/Web, Flutter) that can parse, translate, and execute multiple languages such as Dart, Java, Kotlin, C#, JavaScript, TypeScript, Lua, and Python. It also provides on-the-fly compilation to Wasm.


Use Cases

  • ๐Ÿค– MCP tool for LLMs โ€” expose ApolloVM over MCP so an LLM can use it as a sandboxed reasoning scratchpad and validate generated code (parse, run, check output).
  • ๐Ÿ”„ Cross-language translation / porting โ€” translate and regenerate source between any supported languages to prototype, migrate, or share logic across stacks.
  • ๐Ÿ“ฑ Embedded scripting in Dart/Flutter โ€” ship logic, UIs, and behavior that update live at runtime, with no rebuild or app-store redeploy.
  • ๐Ÿ“ Business rules & formulas โ€” define pricing, discounts, validation, and eligibility logic as editable rules in a real, familiar language, evaluated safely at runtime โ€” no hardcoding or custom expression engine to build and maintain.
  • ๐Ÿ”’ Sandboxed execution โ€” run untrusted, user-supplied snippets isolated from the host, via the interpreter or compiled Wasm.
  • โš™๏ธ On-the-fly WebAssembly โ€” compile loaded source to portable Wasm modules at runtime (browser or native) without any external toolchain.
  • ๐ŸŽ“ Playgrounds & education โ€” build multi-language runners/REPLs that show the same program across languages and its translated output.
  • ๐Ÿงช Reference implementations / test oracles โ€” write an algorithm once, then run or emit it across language targets to cross-check behavior.
  • ๐ŸŒณ Polyglot AST tooling โ€” parse multiple languages into one shared AST for analysis, transformation, and code generation.
  • ๐Ÿชถ Small, multi-platform VM โ€” runs on native, web/JS, and Flutter; the CLI compiles to a self-contained native binary under 10 MB with all features included.

Live Example

Experience ApolloVM in action right from your browser:

If you prefer to run the demo on your local machine:


Supported Features

ApolloVM can parse, execute (interpret), and translate (cross-compile) source code between all supported languages, and compile to WebAssembly on the fly.

Languages

Dart, Java 11, Kotlin, C#, JavaScript, TypeScript, Lua and Python.

Any supported language can be translated to any other (e.g. Java โ†’ Dart, C# โ†’ Python, Kotlin โ†’ JavaScript), and code can be regenerated back to its original language.

Core capabilities

  • Parsing of each language into a shared AST.
  • Execution: a tree-walking interpreter runs the AST directly.
  • Translation: regenerate the AST as source in any supported language.
  • Wasm compilation: compile to WebAssembly, including async/await (via Asyncify), classes (fields, constructors, instance & static methods, toString() dispatch, Object/dynamic boxing), exceptions, closures, lists/maps and GC types.

Control flow & operators

Legend: โœ… supported ยท ๐Ÿงฉ supported via the language's idiom ยท ๐Ÿšง not yet supported (exists in the language but not implemented yet) ยท ๐Ÿšซ not applicable (the language has no such construct).

The Wasm column shows what the on-the-fly WebAssembly compiler currently supports (any source language is compiled through the same shared AST).

Feature Dart Java Kotlin C# JS TS Lua Python Wasm
if / else if / else โœ… โœ… โœ… โœ… โœ… โœ… โœ… โœ… โœ…
for (C-style) โœ… โœ… ๐Ÿšซ โœ… โœ… โœ… ๐Ÿงฉยน ๐Ÿšซ โœ…
for-each / for-in โœ… โœ… โœ… โœ… โœ… โœ… โœ… โœ… โœ…
while โœ… โœ… โœ… โœ… โœ… โœ… โœ… โœ… โœ…
do / while โœ… โœ… โœ… โœ… โœ… โœ… ๐Ÿงฉยฒ ๐Ÿšซ โœ…
switch / case โœ… โœ… ๐Ÿงฉยณ โœ… โœ… โœ… ๐Ÿšซ ๐Ÿงฉโด โœ…
break โœ… โœ… โœ… โœ… โœ… โœ… โœ… โœ… โœ…
continue โœ… โœ… โœ… โœ… โœ… โœ… ๐Ÿšซ โœ… โœ…
try / catch / finally โœ… โœ… โœ… โœ… โœ… โœ… ๐Ÿšซ โœ… โœ…
throw / raise โœ… โœ… โœ… โœ… โœ… โœ… ๐Ÿšซ โœ… โœ…
async / await โœ… ๐Ÿšซ ๐Ÿงฉโท โœ… โœ… โœ… ๐Ÿšซ โœ… โœ…
Ternary (? :) โœ… โœ… โœ… โœ… โœ… โœ… ๐Ÿšซ โœ… โœ…
Arithmetic (+ - * / %) โœ… โœ… โœ… โœ… โœ… โœ… โœ… โœ… โœ…
Comparison / logical โœ… โœ… โœ… โœ… โœ… โœ… โœ… โœ… โœ…
Bitwise (& | ^ << >> ~) โœ… โœ… ๐Ÿงฉโต โœ… โœ… โœ… ๐Ÿงฉโถ โœ… โœ…
++ / --, compound assign โœ… โœ… โœ… โœ… โœ… โœ… โœ… โœ… โœ…
Lambdas / closures โœ… โœ… โœ… โœ… โœ… โœ… โœ… โœ… โœ…
Named / keyword arguments โœ… ๐Ÿšซ โœ… โœ… ๐Ÿšซ ๐Ÿšซ ๐Ÿšซ โœ… โœ…
Parameter default values โœ… ๐Ÿšซ โœ… โœ… ๐Ÿšซ ๐Ÿšซ ๐Ÿšซ โœ… โœ…
String interpolation / concat โœ… โœ… โœ… โœ… โœ… โœ… โœ… โœ… โœ…
List & map / dict literals โœ… โœ… โœ… โœ… โœ… โœ… โœ… โœ… โœ…
null / None / nil โœ… โœ… โœ… โœ… โœ… โœ… โœ… โœ… โœ…

ยน Lua numeric-for (for i = a, b do).   ยฒ Lua repeat โ€ฆ until.   ยณ Kotlin when.   โด Python match / case.   โต Kotlin bitwise are infix functions: and/or/xor/shl/shr and .inv().   โถ Lua bitwise use &/|/~ (xor)/<</>> and unary ~ (Lua 5.3).   โท Kotlin's async idiom is suspend / coroutines: ApolloVM generates suspend fun (dropping await) when translating to Kotlin, but does not yet parse Kotlin suspend. await unwraps the awaitable (Future<T> / Promise<T> / Task<T>) to T.

Classes, types & OOP

Legend: โœ… supported ยท ๐Ÿงฉ supported via the language's idiom ยท ๐Ÿšง not yet supported (exists in the language but not implemented yet) ยท ๐Ÿšซ not applicable (the language has no such construct).

The Wasm column shows what the on-the-fly WebAssembly compiler currently supports (any source language is compiled through the same shared AST).

Feature Dart Java Kotlin C# JS TS Lua Python Wasm
Classes โœ… โœ… โœ… โœ… โœ… โœ… ๐Ÿšซ โœ… โœ…
Fields (with initializers) โœ… โœ… โœ… โœ… โœ… โœ… ๐Ÿงฉยน โœ… โœ…
Constructors & instantiation (new Foo(...) / Foo(...)) โœ… โœ… โœ… โœ… โœ… โœ… ๐Ÿงฉยน โœ… โœ…
Methods โœ… โœ… โœ… โœ… โœ… โœ… โœ… โœ… โœ…
Static / visibility modifiers โœ… โœ… ๐Ÿงฉยณ โœ… ๐Ÿงฉยฒ โœ… ๐Ÿšซ ๐Ÿšซ ๐Ÿงฉโด
Inheritance (extends) / interfaces โœ… โœ… โœ… โœ… โœ… โœ… ๐Ÿšซ โœ… ๐Ÿšง
Enums (rich: ctor args, fields, methods)โถ โœ… โœ… โœ… โœ… ๐Ÿšซ โœ… ๐Ÿšซ โœ… โœ…โถ
Generics (generic classes + instantiation + type erasure) โœ… โœ… โœ… โœ… ๐Ÿšซ โœ… ๐Ÿšซ ๐Ÿšซ ๐Ÿšง
Type inference (var / val / auto) โœ… โœ… โœ… โœ… ๐Ÿšซ โœ… ๐Ÿšซ ๐Ÿšซ โœ…โต

ยน Lua is table-based: "fields" are table entries (obj.x), "constructors" are factory/setmetatable idioms, methods are function Obj:method.   ยฒ JavaScript has static but no visibility keywords (privacy is by convention/closures).   ยณ Kotlin private/public visibility round-trips; internal/protected are parsed but not preserved yet; no static (uses companion object, not yet supported).   โด Wasm compiles static methods (exported as Class.method) and instance methods (called with a receiver); only static methods are callable as entry points, with no source-level visibility.   โต Wasm consumes the already type-resolved AST, so var/val-typed code compiles unchanged.   โถ Each enum entry is a const instance of the enum's class (Dart enhanced enums): .index, .name, EnumName.values, identity ==, plus rich-enum constructor args, fields and methods. Java/Kotlin emit native rich enums; C#/TS/Python use a class + static-const-instances idiom; Wasm compiles entries to heap instances (a method chained directly on an entry needs a variable first). Breaking change: an entry is no longer an int โ€” use .index (and .value for = N entries).   Generics are ๐Ÿšซ for JS/Lua/Python: no static type syntax to parameterize.

Per-language behavior is normalized to a shared AST, so types and constructs map cleanly when translating between languages (e.g. C# string โ‡„ Dart String, Java List<Integer> โ‡„ Dart List<int>).


Command Line Usage

You can use the executable apollovm to run or translate source codes.

First you should activate the package globally:

$> dart pub global activate apollovm

Now you can use the apollovm Dart executable:

$> apollovm help

ApolloVM - A compact VM for Dart, Java, Kotlin, C#, JavaScript, TypeScript, Lua and Python.

Usage: apollovm <command> [arguments]

Global options:
-h, --help       Print this usage information.
-v, --version    Show ApolloVM version.

Available commands:
  run         Run a source file.
  translate   Translate a source file.

Run "apollovm help <command>" for more information about a command.

To run a Java file:

$> apollovm run -v test/hello-world.java foo
## [RUN]        File: 'test/hello-world.java' ; language: java11 > main( [foo] )
Hello World!
- args: [foo]
- a0: foo

To translate a Java file to Dart:

$> apollovm translate -v --target dart test/hello-world.java
## [TRANSLATE]  File: 'test/hello-world.java' ; language: java11 > targetLanguage: dart
<<<< [SOURCES_BEGIN] >>>>
<<<< NAMESPACE="" >>>>
<<<< CODE_UNIT_START="/test/hello-world.java" >>>>
class Hello {

  static void main(List<String> args) {
    var a0 = args[0];
    print('Hello World!');
    print('- args: $args');
    print('- a0: $a0');
  }

}
<<<< CODE_UNIT_END="/test/hello-world.java" >>>>
<<<< [SOURCES_END] >>>>

The same commands work for JavaScript (.js) files. To run a JavaScript file:

$> apollovm run -v test/hello_world.js foo
## [RUN]        File: 'test/hello_world.js' ; language: javascript > main( [foo] )
Hello World!
- name: foo

To translate a JavaScript file to Dart:

$> apollovm translate -v --target dart test/hello_world.js
## [TRANSLATE]  File: 'test/hello_world.js' ; language: javascript > targetLanguage: dart
<<<< [SOURCES_BEGIN] >>>>
<<<< NAMESPACE="" >>>>
<<<< CODE_UNIT_START="/test/hello_world.js" >>>>
class Hello {

  static void main(dynamic name) {
    print('Hello World!');
    print('- name: ${name}');
  }

}
<<<< CODE_UNIT_END="/test/hello_world.js" >>>>
<<<< [SOURCES_END] >>>>

The same commands work for TypeScript (.ts) files. To translate a TypeScript file to Dart:

$> apollovm translate -v --target dart test/hello_world.ts
## [TRANSLATE]  File: 'test/hello_world.ts' ; language: typescript > targetLanguage: dart
<<<< [SOURCES_BEGIN] >>>>
<<<< NAMESPACE="" >>>>
<<<< CODE_UNIT_START="/test/hello_world.ts" >>>>
class Hello {

  static void main(String name) {
    print('Hello World!');
    print('- name: ${name}');
  }

}
<<<< CODE_UNIT_END="/test/hello_world.ts" >>>>
<<<< [SOURCES_END] >>>>

Compiling ApolloVM executable.

Dart supports compilation to native self-contained executables.

To have a fast and small executable of ApolloVM, just clone the project and compile it:


## Go to a directory to clone the project (usually a workspace):
$> cd ./some-workspace/

## Git clone the project:
$> git clone https://github.com/ApolloVM/apollovm_dart.git

## Enter the project:
$> cd ./apollovm_dart

## Compile ApolloVM executable:
$> dart compile exe bin/apollovm.dart

## Copy the binary to your preferred PATH:
$> cp bin/apollovm.exe /usr/bin/apollovm

Now you can use apollovm as a self-executable, even if you don't have Dart installed.


Package Usage

The ApolloVM is still in alpha stage. Below, we can see simple usage examples in Dart, Java, Kotlin, C#, JavaScript, TypeScript, Lua and Python.

Language: Dart

Loading Dart source code, executing it, and then converting it to Java 11:

import 'package:apollovm/apollovm.dart';

void main() async {
  var vm = ApolloVM();

  var codeUnit = SourceCodeUnit(
          'dart',
          r'''
      
      class Foo {
      
          static int main(List<Object> args) {
            var title = args[0];
            var a = args[1];
            var b = args[2] ~/ 2;
            var c = args[3] * 3;
            
            if (c > 120) {
              c = 120 ;
            }
            
            var str = 'variables> a: $a ; b: $b ; c: $c' ;
            var sumAB = a + b ;
            var sumABC = a + b + c;
            
            print(str);
            print(title);
            print(sumAB);
            print(sumABC);
            
            // Map:
            var map = <String,int>{
            'a': a,
            'b': b,
            'c': c,
            'sumAB': sumAB,
            "sumABC": sumABC,
            };
            
            print('Map: $map');
            print('Map `b`: ${map['b']}');
            
            return map['sumABC'];
          }
          
      }
      
      ''',
          id: 'test');

  var loadOK = await vm.loadCodeUnit(codeUnit);

  if (!loadOK) {
    print("Can't load source!");
    return;
  }

  print('---------------------------------------');

  var dartRunner = vm.createRunner('dart')!;

  // Map the `print` function in the VM:
  dartRunner.externalPrintFunction = (o) => print("ยป $o");

  var astValue = await dartRunner.executeClassMethod(
    '',
    'Foo',
    'main',
    positionalParameters: [
      ['Sums:', 10, 30, 50]
    ],
  );

  var result = astValue.getValueNoContext();
  print('Result: $result');

  print('---------------------------------------');

  // Regenerate code in Java11:
  var codeStorageJava = vm.generateAllCodeIn('java11');
  var allSourcesJava = await codeStorageJava.writeAllSources();
  print(allSourcesJava);
}

Note: the parsed function print was mapped as an external function.

Output:

---------------------------------------
ยป variables> a: 10 ; b: 15 ; c: 120
ยป Sums:
ยป 25
ยป 145
ยป Map: {a: 10, b: 15, c: 120, sumAB: 25, sumABC: 145}
ยป Map `b`: 15
Result: 145
---------------------------------------
<<<< [SOURCES_BEGIN] >>>>
<<<< NAMESPACE="" >>>>
<<<< CODE_UNIT_START="/test" >>>>
class Foo {

  static int main(Object[] args) {
    var title = args[0];
    var a = args[1];
    var b = args[2] / 2;
    var c = args[3] * 3;
    if (c > 120) {
        c = 120;
    }

    var str = "variables> a: " + a + " ; b: " + b + " ; c: " + c;
    var sumAB = a + b;
    var sumABC = a + b + c;
    print(str);
    print(title);
    print(sumAB);
    print(sumABC);
    var map = new HashMap<String,int>(){{
      put("a", a);
      put("b", b);
      put("c", c);
      put("sumAB", sumAB);
      put("sumABC", sumABC);
    }};
    print("Map: " + map);
    print("Map `b`: " + String.valueOf( map["b"] ));
    return map["sumABC"];
  }

}
<<<< CODE_UNIT_END="/test" >>>>
<<<< [SOURCES_END] >>>>

Language: Java 11

Loading Java 11 source code, executing it, and then converting it to Dart:

import 'package:apollovm/apollovm.dart';

void main() async {
  var vm = ApolloVM();

  var codeUnit = SourceCodeUnit(
          'java11',
          r'''
            class Foo {
               static public void main(Object[] args) {
                 var title = args[0];
                 var a = args[1];
                 var b = args[2];
                 var c = args[3];
                 var sumAB = a + b ;
                 var sumABC = a + b + c;
                 print(title);
                 print(sumAB);
                 print(sumABC);
                 
                 // Map:
                 var map = new HashMap<String,int>(){{
                  put("a", a);
                  put("b", b);
                  put("c", c);
                  put("sumAB", sumAB);
                  put("sumABC", sumABC);
                }};
                 
                print("Map: " + map);
               }
            }
          ''',
          id: 'test');

  var loadOK = await vm.loadCodeUnit(codeUnit);

  if (!loadOK) {
    throw StateError('Error parsing Java11 code!');
  }

  var javaRunner = vm.createRunner('java11')!;

  // Map the `print` function in the VM:
  javaRunner.externalPrintFunction = (o) => print("ยป $o");

  await javaRunner.executeClassMethod('', 'Foo', 'main', positionalParameters: [
    ['Sums:', 10, 20, 30]
  ]);

  print('---------------------------------------');

  // Regenerate code:
  var codeStorageDart = vm.generateAllCodeIn('dart');
  var allSourcesDart = await codeStorageDart.writeAllSources();
  print(allSourcesDart.toString());
}

Note: the parsed function print was mapped as an external function.

Output:

ยป Sums:
ยป 30
ยป 60
ยป Map: {a: 10, b: 20, c: 30, sumAB: 30, sumABC: 60}
---------------------------------------
<<<< [SOURCES_BEGIN] >>>>
<<<< NAMESPACE="" >>>>
<<<< CODE_UNIT_START="/test" >>>>
class Foo {

  static void main(List<Object> args) {
    var title = args[0];
    var a = args[1];
    var b = args[2];
    var c = args[3];
    var sumAB = a + b;
    var sumABC = a + b + c;
    print(title);
    print(sumAB);
    print(sumABC);
    var map = <String,int>{'a': a, 'b': b, 'c': c, 'sumAB': sumAB, 'sumABC': sumABC};
    print('Map: $map');
  }

}
<<<< CODE_UNIT_END="/test" >>>>
<<<< [SOURCES_END] >>>>

Language: Kotlin

Loading Kotlin source code, executing it, and then converting it to Dart:

import 'package:apollovm/apollovm.dart';

void main() async {
  var vm = ApolloVM();

  var codeUnit = SourceCodeUnit(
          'kotlin',
          r'''
            class Foo {
              fun greet(name: String, count: Int) {
                val msg = "Hello $name, you have $count messages."
                println(msg)
              }
            }
          ''',
          id: 'test');

  var loadOK = await vm.loadCodeUnit(codeUnit);

  if (!loadOK) {
    throw StateError('Error parsing Kotlin code!');
  }

  var kotlinRunner = vm.createRunner('kotlin')!;

  // Map the `print` function in the VM:
  kotlinRunner.externalPrintFunction = (o) => print("ยป $o");

  // `greet` is an instance method (not `static`), so it needs a class instance;
  // `classInstanceFields` provides one (here with no fields).
  await kotlinRunner.executeClassMethod('', 'Foo', 'greet',
      positionalParameters: ['World', 3], classInstanceFields: const {});

  print('---------------------------------------');

  // Regenerate code in Dart:
  var codeStorageDart = vm.generateAllCodeIn('dart');
  var allSourcesDart = await codeStorageDart.writeAllSources();
  print(allSourcesDart.toString());
}

Note: the parsed function println is normalized to the VM's print (mapped as an external function).

Output:

ยป Hello World, you have 3 messages.
---------------------------------------
<<<< [SOURCES_BEGIN] >>>>
<<<< NAMESPACE="" >>>>
<<<< CODE_UNIT_START="/test" >>>>
class Foo {

  void greet(String name, int count) {
    final msg = 'Hello $name, you have $count messages.';
    print(msg);
  }

}
<<<< CODE_UNIT_END="/test" >>>>
<<<< [SOURCES_END] >>>>

Kotlin support reaches parity with the Java feature set: top-level and class fun declarations, val/var with type inference, if/else, for (x in โ€ฆ), while, listOf/mapOf literals, and "$x" / "${expr}" string templates โ€” all translatable to Dart, Java or back to Kotlin.

Language: JavaScript

Loading JavaScript (modern ES) source code, executing it, and then converting it to Dart:

import 'package:apollovm/apollovm.dart';

void main() async {
  var vm = ApolloVM();

  var codeUnit = SourceCodeUnit(
          'javascript',
          r'''
            class Foo {
              static greet(name, count) {
                let msg = `Hello ${name}, you have ${count} messages.`;
                print(msg);
              }
            }
          ''',
          id: 'test');

  var loadOK = await vm.loadCodeUnit(codeUnit);

  if (!loadOK) {
    throw StateError('Error parsing JavaScript code!');
  }

  var jsRunner = vm.createRunner('javascript')!;

  // Map the `print` function in the VM:
  jsRunner.externalPrintFunction = (o) => print("ยป $o");

  await jsRunner.executeClassMethod('', 'Foo', 'greet',
      positionalParameters: ['World', 3]);

  print('---------------------------------------');

  // Regenerate code in Dart:
  var codeStorageDart = vm.generateAllCodeIn('dart');
  var allSourcesDart = await codeStorageDart.writeAllSources();
  print(allSourcesDart.toString());
}

Note: the parsed function print was mapped as an external function.

Output:

ยป Hello World, you have 3 messages.
---------------------------------------
<<<< [SOURCES_BEGIN] >>>>
<<<< NAMESPACE="" >>>>
<<<< CODE_UNIT_START="/test" >>>>
class Foo {

  static void greet(dynamic name, dynamic count) {
    var msg = 'Hello ${name}, you have ${count} messages.';
    print(msg);
  }

}
<<<< CODE_UNIT_END="/test" >>>>
<<<< [SOURCES_END] >>>>

JavaScript is fully bidirectional: ApolloVM can parse .js/javascript source into the AST, execute it, and generate idiomatic modern ES (let/const, template literals, for...of, top-level functions, ===/!==) from any loaded AST (Dart, Java, or JavaScript).

Language: TypeScript

TypeScript is supported as a superset of JavaScript: everything JavaScript supports, plus type annotations (variables, parameters, return types, fields), interfaces, enums, and access modifiers (public/private/protected/readonly/static/ abstract).

Loading TypeScript source code, executing it, and then converting it to Dart:

import 'package:apollovm/apollovm.dart';

void main() async {
  var vm = ApolloVM();

  var codeUnit = SourceCodeUnit(
          'typescript',
          r'''
            class Foo {
              static greet(name: string, count: number): void {
                let msg: string = `Hello ${name}, you have ${count} messages.`;
                print(msg);
              }
            }
          ''',
          id: 'test');

  var loadOK = await vm.loadCodeUnit(codeUnit);

  if (!loadOK) {
    throw StateError('Error parsing TypeScript code!');
  }

  var tsRunner = vm.createRunner('typescript')!;

  // Map the `print` function in the VM:
  tsRunner.externalPrintFunction = (o) => print("ยป $o");

  await tsRunner.executeClassMethod('', 'Foo', 'greet',
      positionalParameters: ['World', 3]);

  print('---------------------------------------');

  // Regenerate code in Dart:
  var codeStorageDart = vm.generateAllCodeIn('dart');
  var allSourcesDart = await codeStorageDart.writeAllSources();
  print(allSourcesDart.toString());
}

Note: the parsed function print was mapped as an external function.

Output:

ยป Hello World, you have 3 messages.
---------------------------------------
<<<< [SOURCES_BEGIN] >>>>
<<<< NAMESPACE="" >>>>
<<<< CODE_UNIT_START="/test" >>>>
class Foo {

  static void greet(String name, num count) {
    String msg = 'Hello ${name}, you have ${count} messages.';
    print(msg);
  }

}
<<<< CODE_UNIT_END="/test" >>>>
<<<< [SOURCES_END] >>>>

TypeScript is bidirectional: ApolloVM parses .ts/typescript source (including interface, enum, and member modifiers) into the AST, executes it, and generates idiomatic TypeScript with type annotations. The same interface/enum/modifier constructs are also supported in Dart (abstract class, enum, static/final), so they cross-translate between Dart, TypeScript and JavaScript.

Language: Lua

Loading Lua source code, executing it, and then converting it to Dart:

import 'package:apollovm/apollovm.dart';

void main() async {
  var vm = ApolloVM();

  var codeUnit = SourceCodeUnit(
          'lua',
          r'''
            Foo = {}
            Foo.__index = Foo

            function Foo:main(title, a, b, c)
              local sumAB = a + b
              local sumABC = a + b + c
              print(title)
              print(sumAB)
              print(sumABC)
            end
          ''',
          id: 'test');

  var loadOK = await vm.loadCodeUnit(codeUnit);

  if (!loadOK) {
    throw StateError('Error parsing Lua code!');
  }

  var luaRunner = vm.createRunner('lua')!;

  // Map the `print` function in the VM:
  luaRunner.externalPrintFunction = (o) => print("ยป $o");

  // `Foo:main` is an instance method (uses `self`), so it needs a class
  // instance; `classInstanceFields` provides one (here with no fields).
  await luaRunner.executeClassMethod('', 'Foo', 'main',
      positionalParameters: ['Sums:', 10, 20, 30], classInstanceFields: const {});

  print('---------------------------------------');

  // Regenerate code in Dart:
  var codeStorageDart = vm.generateAllCodeIn('dart');
  var allSourcesDart = await codeStorageDart.writeAllSources();
  print(allSourcesDart.toString());
}

Note: the parsed function print was mapped as an external function.

Output:

ยป Sums:
ยป 30
ยป 60
---------------------------------------
<<<< [SOURCES_BEGIN] >>>>
<<<< NAMESPACE="" >>>>
<<<< CODE_UNIT_START="/test" >>>>
class Foo {

  void main(dynamic title, dynamic a, dynamic b, dynamic c) {
    var sumAB = a + b;
    var sumABC = (a + b) + c;
    print(title);
    print(sumAB);
    print(sumABC);
  }

}
<<<< CODE_UNIT_END="/test" >>>>
<<<< [SOURCES_END] >>>>

Lua is bidirectional: ApolloVM parses .lua source into the AST, executes it, and generates idiomatic Lua (keyword-delimited blocks with end, local variables, .. concatenation, and/or/not/~=, and generic for ... in ipairs(...)) from any loaded AST. Object-oriented code uses the conventional table + metatable form (Name = {}, Name.__index = Name, function Name:method(...)), so classes round-trip to and from Dart, Java, Kotlin, JavaScript and TypeScript.

Language: Python

Loading Python source code, executing it, and then converting it to Dart:

import 'package:apollovm/apollovm.dart';

void main() async {
  var vm = ApolloVM();

  var codeUnit = SourceCodeUnit(
          'python',
          r'''
            class Foo:
                def greet(self, name, count):
                    msg = f'Hello {name}, you have {count} messages.'
                    print(msg)
          ''',
          id: 'test');

  var loadOK = await vm.loadCodeUnit(codeUnit);

  if (!loadOK) {
    throw StateError('Error parsing Python code!');
  }

  var pyRunner = vm.createRunner('python')!;

  // Map the `print` function in the VM:
  pyRunner.externalPrintFunction = (o) => print("ยป $o");

  // `greet` is an instance method (takes `self`), so it needs a class instance;
  // `classInstanceFields` provides one (here with no fields).
  await pyRunner.executeClassMethod('', 'Foo', 'greet',
      positionalParameters: ['World', 3], classInstanceFields: const {});

  print('---------------------------------------');

  // Regenerate code in Dart:
  var codeStorageDart = vm.generateAllCodeIn('dart');
  var allSourcesDart = await codeStorageDart.writeAllSources();
  print(allSourcesDart.toString());
}

Note: the parsed function print was mapped as an external function.

Output:

ยป Hello World, you have 3 messages.
---------------------------------------
<<<< [SOURCES_BEGIN] >>>>
<<<< NAMESPACE="" >>>>
<<<< CODE_UNIT_START="/test" >>>>
class Foo {

  void greet(dynamic name, dynamic count) {
    var msg = 'Hello ${name}, you have ${count} messages.';
    print(msg);
  }

}
<<<< CODE_UNIT_END="/test" >>>>
<<<< [SOURCES_END] >>>>

Python is bidirectional: ApolloVM parses .py/python source into the AST, executes it, and generates strict, idiomatic Python 3. Indentation-significant blocks are handled by an INDENT/DEDENT/NEWLINE pre-tokenizer, and generation emits PEP-484 type hints when the AST type is statically known (def f(x: int) -> int:, x: str = ..., List[T]/Dict[K, V]) with a dynamic fallback. Supported constructs include functions, self-based methods and class declarations, if/elif/else, while, for ... in, try/except/finally with raise, lists & dicts, f-string interpolation, // integer division, and/or/not, True/False/None, and import/from ... import โ€” all cross-translatable with Dart, Java, Kotlin, JavaScript, TypeScript and Lua.

Wasm Support

ApolloVM can compile its AST tree to WebAssembly (Wasm). This means that parsed code loaded into the VM can be compiled on the fly, without the need for any third-party tools.

  • Status: Wasm support is under active development. It already compiles a broad subset of the AST โ€” functions, full control flow (if/for/for-each/while/do-while/switch/ break/continue/ternary), arithmetic/comparison/logical/bitwise operators, try/catch/throw, classes, closures, lists/maps, and async/await (via Asyncify); see the feature table. Constructs not yet compiled to Wasm are limited to a few higher-level features (e.g. non-integer switch).

Example of Dart code compiled to Wasm:

int main( int a , double b ) {
  var x = (a + b) / 2 ;
  if (x > 1000) {
    return -1;
  }
  return x ;
}

Example code to compile to WebAssembly (Wasm):

import 'dart:typed_data';
import 'package:apollovm/apollovm.dart';

void main() async {
  var wasmBytes = await compileToWasm('dart', '''
    
    int main( int a , double b ) {
      var x = (a + b) / 2 ;
      if (x > 1000) {
        return -1;
      }
      return x ;
    }
    
  ''');

  // Execute or save the compiled Wasm...
}

Future<Uint8List> compileToWasm(String codeLanguage, String code) async {
  var vm = ApolloVM();

  var codeUnit = SourceCodeUnit(codeLanguage, code, id: 'test');

  Object? loadError;
  var loadOK = false;
  try {
    loadOK = await vm.loadCodeUnit(codeUnit);
  } catch (e, s) {
    loadError = e;
  }

  if (!loadOK) {
    throw StateError(
            "Can't load source! Language: $codeLanguage\n\n$loadError");
  }

  var storageWasm = vm.generateAllIn<BytesOutput>('wasm');
  var wasmModules = await storageWasm.allEntries();

  var namespace0 = wasmModules.values.first;

  var wasmModule = namespace0.entries.first;
  var wasmOutput = wasmModule.value; // BytesOutput

  print(wasmOutput.toString()); // Show bytes description.

  var wasmBytes = wasmOutput.output();
  return wasmBytes;
}

Generated Wasm bytes with description:


  ## Wasm Magic:
  [0 97 115 109]
  ## Version 1:
  [1 0 0 0]
  ## Section: Type:
      ## Section Type ID:
      [1]
      ## Bytes block length:
      [7]
      ## Functions signatures:
        ## Types count:
        [1]
          ## Type: function:
          [96]
          ## Parameters types:
          [2 126 124]
          ## Return value:
          [1 126]
  ## Section: Function:
      ## Section Function ID:
      [3]
      ## Bytes block length:
      [2]
      ## Functions type indexes:
      [1 0]
  ## Section: Export:
      ## Section Export ID:
      [7]
      ## Bytes block length:
      [8]
      ## Exported types:
        ## Exported types count:
        [1]
        ## Export function:
          ## Function name(`main`):
          [4 109 97 105 110]
          ## Export type(function):
          [0]
          ## Type index(0):
          [0]
  ## Section: Code:
      ## Section Code ID:
      [10]
      ## Bytes block length:
      [35]
      ## Functions bodies:
        ## Bodies count:
        [1]
          ## Bytes block length:
          [33]
          ## Function body:
              ## Local variables count:
              [1]
              ## Declared variable count:
              [1]
              ## Declared variable type(f64):
              [124]
                  ## [OP] local get: 0 $a:
                  [32 0]
                ## [OP] convert i64 to f64 signed:
                [185]
                  ## [OP] local get: 1 $b:
                  [32 1]
                ## [OP] operator: add(f64):
                [160]
                ## [OP] push constant(i64): 2:
                [66 2]
              ## [OP] convert i64 to f64 signed:
              [185]
              ## [OP] operator: divide(f64):
              [163]
              ## [OP] local set: 2 $x:
              [33 2]
                ## [OP] local get: 2 $x:
                [32 2]
                ## [OP] push constant(i64): 1000:
                [66 232 7]
              ## [OP] convert i64 to f64 signed:
              [185]
              ## [OP] operator: greaterThan(f64):
              [100]
              ## [OP] if ( x > (int) 1000 ):
              [4 64]
              ## [OP] push constant(i64): -1:
              [66 127]
              ## [OP] return value: (int) -1:
              [15]
              ## [OP] if end:
              [11]
              ## [OP] local get: 2 $x (return):
              [32 2]
              ## f64TruncateToI64Signed:
              [176]
              ## [OP] return variable: 2 $x:
              [15]
              ## Code body end:
              [11]

  • NOTE: When compiling to WebAssembly, ApolloVM keeps track of the stack and performs automatic type casting to facilitate operations between different types or return values.

See Also

ApolloVM uses PetitParser for Dart to define the grammars of the languages and to analyze the source codes.

Features and bugs

Please file feature requests and bugs at the issue tracker.

Contribution

Any help from the open-source community is always welcome and needed:

  • Have an issue? Please fill a bug report ๐Ÿ‘.
  • Feature? Request with use cases ๐Ÿค.
  • Like the project? Promote, post, or donate ๐Ÿ˜„.
  • Are you a developer? Fix a bug, add a feature, or improve tests ๐Ÿš€.
  • Already helped? Many thanks from me, the contributors and all project users ๐Ÿ‘๐Ÿ‘๐Ÿ‘!

Contribute an hour and inspire others to do the same.

TODO

  • JavaScript: extended support (destructuring, spread, this.x constructor parameters, full ESM modules). Named arrow functions (const f = (a, b) => a + b;), anonymous arrow callbacks/closures ((x) => x * 2), the ternary operator (c ? a : b), and async/await are already supported.

  • TypeScript: extended support (union/intersection types, type aliases, parameter properties, decorators). Type annotations, interface, enum (incl. member access at runtime), generic classes/instantiation, and access modifiers (public/private/protected/readonly/static/abstract) are already supported.

  • Lua: extended support (multiple assignment/returns, varargs, non-ipairs numeric-for round-tripping, and hand-written metatable styles beyond the Name = {} / function Name:method convention).

  • Python: extended support (comprehensions, decorators, with statements, *args/**kwargs, multiple assignment/returns). Functions, classes/self-methods, if/elif/else, while, for ... in, try/except/finally + raise, lists & dicts, f-strings, keyword arguments, lambda expressions, conditional expressions (a if c else b), and import/from ... import are already supported.

  • Package Importer.

  • Full Wasm support:

Author

Graciliano M. Passos: gmpassos@GitHub.

Don't be shy, show some love, and become our GitHub Sponsor. Your support means the world to us, and it keeps the code caffeinated! โ˜•โœจ

Thanks a million! ๐Ÿš€๐Ÿ˜„

License

Apache License - Version 2.0

Libraries

apollovm
A portable virtual machine for Dart, web (JavaScript), and Flutter.