webamp/experiments/maki-interpreter
Jordan Eldredge 4a15cde301 Start writing MakiFile to use a Uint8Array under the hood
For now we do both and assert that they behave the same
2019-11-30 13:42:53 -08:00
..
fixtures Add sketch of .maki parser 2019-01-16 07:15:52 -08:00
reference Setup multi version test 2019-11-30 13:42:53 -08:00
runtime Don't share subscriptions between instances 2019-11-30 13:42:53 -08:00
tools Progress on maki stuff 2019-05-28 21:55:27 -07:00
.gitignore Add sketch of .maki parser 2019-01-16 07:15:52 -08:00
cli.js Add first integration test 2019-11-30 13:42:53 -08:00
constants.js Add sketch of .maki parser 2019-01-16 07:15:52 -08:00
index.test.js Move parser into its own file 2019-11-30 13:42:53 -08:00
interpreter.js Add first integration test 2019-11-30 13:42:53 -08:00
interpreter.test.js Setup multi version test 2019-11-30 13:42:53 -08:00
objects.js Attach function objects to function names 2019-11-30 13:42:53 -08:00
package.json Separate runtime 2019-11-30 13:42:53 -08:00
parser.js Start writing MakiFile to use a Uint8Array under the hood 2019-11-30 13:42:53 -08:00
prettyPrinter.js Improve code organization and document it 2019-11-30 13:42:53 -08:00
readme.md Improve code organization and document it 2019-11-30 13:42:53 -08:00
variable.js Separate runtime 2019-11-30 13:42:53 -08:00
virtualMachine.js Add first integration test 2019-11-30 13:42:53 -08:00
yarn.lock Separate runtime 2019-11-30 13:42:53 -08:00

Maki Interpreter

One possible future for Webamp would be to support "modern" skins. The biggest blocker to this is that modern skins suport a custom scripting language called Maki.

This project is an investigation into how feasible it would be to write a .maki interpreter in JavaScript. Maki is a compiled language, so this interpreter plans to parse/evaluate the compiled byte code. Most of the hard work of figuring out how to write the parser has already been done as part of Ralf Engels' Maki Decompiler. The first stage of this project is just to reimplement the parser portion of the decompiler in JavaScript.

Architecture

  • Parser: Given a Buffer containing the binary data of a .maki file, returns a JSON serializeable representation of the script.
  • Runtime: A JavaScript object mapping class unique ids to JavaScript implementations of those classes.
  • Virtual Machine: Given a parsed .maki file, and a command offset, exectues the .maki script starting at that command, and returing the value returned from executing that function.
  • Interpreter: This ties all of the above pieces together. Given a Buffer containing a .maki script, a Runtime, and an instance of System, it parses the .maki file, and then binds the runtime into the parsed .maki program. This consists of populating the System variable, and binding the class ids defined in the script to the actual JavaScript implementations of those classes. Finally it kicks off execution by triggering System.onScriptLoaded().
  • Cli: A command line script that, given the path to a .maki file will run it through the Interpreter.

Roadmap

  • Parse top level bytecode (constants, types, variables)
  • Evaluate function code
  • Implement core of standard library
  • ???

Tools

  • yarn parse Run the parser on the fixture .maki file. This is useful for console.log debugging.
  • yarn decompile Run the Perl decompiler on the fixture .maki file. This is useful for sticking print(); statements in the Perl and trying to figure out what exactly it does.
  • yarn tdd Run the tests in "watch" mode. Great for TDD
  • yarn interpret Interpret the test script. Currently this just logs the commands and stack as they get executed.

Structure of a .maki file

The bytecode contained in a .maki file takes the following form (I think. I'm still trying to gock it). These are my notes trying to write down what I understand so far.

  1. The "magic" string "FG". This might be some kind attempt to validate that this is realy a .maki file?
  2. A version number (which we currently ignore)
  3. Some 32 bit something. We ignore this.
  4. Types
    1. A 32 bit number defines how many types there are.
    2. Each type consists of four 32 bit numbers.
  5. Function names
    1. A 32 bit number defines how many function names there are.
    2. Each funtion name consists of a 16 bit class code
    3. A 16 bit "dummy" (not sure what this is) and a name.
    4. The name is defined by a 16 bit number showing how long the name is, followed by that many ascii bytes.
  6. Variables
    1. A 32 bit number defines how many variables there are.
    2. Each variable consists of:
    3. A byte of what "type" it is.
    4. A byte of what object it refers to.
    5. 16 bits of what subclass it refers to.
    6. Four uinits (what are those?) each 16 bits long.
    7. A byte representing "global" (what? Maybe a boolean?)
    8. A byte representing "syste" (What? Maybe a boolean?)
  7. Constants
    1. A 32 bit number defines how many constants there are.
    2. Each constant consists of:
    3. A 32 bit number representing its number (is this just an ID?)
    4. The value is defined by a 16 bit number showing how long the name is, followed by that many ascii bytes.
  8. Functions
    1. A 32 bit number defines how many functions there are.
    2. Each constant consists of:
    3. A 32 bit number representing its variable number (is this just an ID?)
    4. A 32 bit number representing its function number (is this just an ID?)
    5. A 32 bit number representing its offset (offset into what?)
  9. Function Code
    1. A 32 bit number defines how many commands there are.
    2. Each command consists of:
    3. A byte representing that command's opcode