Metastatic - a MetaAST for Elixir, Erlang and technically every language

Metastatic is a library that provides a unified MetaAST (Meta-level Abstract Syntax Tree) intermediate representation for parsing, transforming, and analyzing code across multiple programming languages using a three-layer meta-model architecture.

“Build tools once, apply them everywhere,” they Java-evangelists said. I tried my own path to create a universal meta-model for program syntax that enables cross-language code analysis, transformation, and tooling.

Metastatic provides the foundation for that—the MetaAST meta-model and language adapters. Tools that leverage this foundation (mutation testing, purity analysis, complexity metrics) are built separately.

It’s already used in Ragex v0.13.0 — Documentation , https://cure-lang.org, and some WIP pets.

Now it’s probably a good time to release it to the wild.

iex|🌢|1 ▶ Metastatic.quote("defmodule M, do: def forty_two, do: 42", :elixir)

{:ok,
 {:container,
  [container_type: :module, name: "M", module: "M", language: :elixir, line: 1],
  [
    {:function_def,
     [
       name: "forty_two",
       params: [],
       visibility: :public,
       arity: 0,
       function: "forty_two",
       language: :elixir,
       line: 1
     ], [{:literal, [subtype: :integer], 42}]}
  ]}}

Metastatic v0.21.0 — Documentation

6 Likes

How does it compare to TreeSitter ASTs?

1 Like

It does not compare to TreeSitter, if I put it straight.

TreeSitter does not build AST, in the first place. It builds CST (for concrete syntax tree.)

TreeSitter aims to build CST for each language based on its grammar, AFAIK. MetaAST is claimed to be the same for all the languages.

Wait until the Java devs find you with the pitchforks and torches for daring not to use “class” but “module” instead.

2 Likes

They’ll find {:container, [container_type: :class … there. I write all my code in an assumption there are many violent java-coders knowing where I live.

3 Likes

CST is an AST with information about location of every node. The only meaningful difference between Metastatic and TreeSitter trees is that Metastatic has aliases like container, function_def, etc. and TreeSitter parses into more precise things like class, module, etc. Aside from that, these are similar trees

Lately I had some more dealings with TreeSitter. It can produce quite different trees depending on the language and the parser. It’s a pretty good and even revolutionary piece of software but it definitely can be better. Almost no structure is enforced. Compare what a Rust and Elixir program yield and you might get a little bit horrified.

Of course. TreeSitter has never been about generalization nor about building a language-agnostic tree.

Metastatic was designed strictly for that.

If you say so.

v0.22.0 All the analysis-related stuff has been extracted to MetaCredo. Metastatic remains a pure MetaAST handling.

1 Like

they also love ifs more than anything else (especially the 5+ times nested ones starting closer to the right side of the editor window given their default 4 char indentations), so you better take special measures of precaution!

1 Like

I spent the whole morning figuring out do I need to introduce if only construct to Cure.

As in

if only payment.succeeded?, dont: goods.buy
1 Like

@mudasobwa - I’m looking at security modeling for the BEAM.
Thinking Elixir podcast #300 referenced a library called Reach:

This call-graph approach seems like it could meld into the metastatic compiler. Fun to add to a codebase and play around; less useful as-is than I had hoped. Could you imagine any chance of melding your packages together?

Copying from their README, they handle these languages:

Elixir, Erlang, Gleam, JavaScript, and TypeScript

1 Like

BEAM languages do provide their own native AST which metastatic uses. JS/TS is planned by me, but not yet even taught.