Metastatic.Builder
(Metastatic v0.10.4)
View Source
High-level API for building MetaAST documents from source code.
This module provides the primary interface for users to work with Metastatic:
from_source/2- Parse source code to MetaAST (Source → M1 → M2)to_source/1- Convert MetaAST back to source (M2 → M1 → Source)
Usage
# Parse Python code to MetaAST
{:ok, doc} = Metastatic.Builder.from_source("x + 5", :python)
# Doc now contains M2 representation (3-tuple format):
doc.ast
# => {:binary_op, [category: :arithmetic, operator: :+],
# [{:variable, [], "x"}, {:literal, [subtype: :integer], 5}]}
# Convert back to source
{:ok, source} = Metastatic.Builder.to_source(doc)
# => "x + 5"Round-Trip Example
source = "x + 5"
{:ok, doc} = Metastatic.Builder.from_source(source, :python)
{:ok, result} = Metastatic.Builder.to_source(doc)
# Should be semantically equivalent (may have normalized formatting)
assert normalize(result) == normalize(source)Cross-Language Transformation
# Parse from Python
{:ok, doc} = Metastatic.Builder.from_source("x + 5", :python)
# The M2 AST is language-independent!
# Could theoretically transform to JavaScript, Elixir, etc.
# (Once those adapters are implemented)
Summary
Functions
Build a MetaAST document from a file.
Build a MetaAST document from source code.
Round-trip test: Source → M1 → M2 → M1 → Source.
Get information about available language adapters.
Write a MetaAST document to a file.
Convert a MetaAST document back to source code.
Validate that source code can be parsed and abstracted.
Functions
@spec from_file(Path.t(), atom() | nil) :: {:ok, Metastatic.Document.t()} | {:error, term()}
Build a MetaAST document from a file.
Reads the file, detects language from extension, and parses to MetaAST.
Parameters
file_path- Path to source filelanguage- Optional language override (auto-detected if not provided)
Examples
iex> Metastatic.Builder.from_file("script.py")
{:ok, %Metastatic.Document{...}}
iex> Metastatic.Builder.from_file("module.js")
{:ok, %Metastatic.Document{language: :javascript, ...}}
iex> Metastatic.Builder.from_file("nonexistent.py")
{:error, :enoent}
@spec from_source(String.t(), atom()) :: {:ok, Metastatic.Document.t()} | {:error, term()}
Build a MetaAST document from source code.
Performs the full Source → M1 → M2 pipeline:
- Detects or uses specified language
- Parses source to M1 (native AST)
- Abstracts M1 to M2 (MetaAST)
- Returns Document with M2 AST and metadata
Parameters
source- Source code stringlanguage- Language atom (:python,:javascript,:elixir, etc.) If not provided, attempts to detect from source (not yet implemented)
Returns
{:ok, document}- Successfully parsed and abstracted{:error, reason}- Parsing or abstraction failed
Examples
# Example result (not runnable - depends on Python adapter)
# Metastatic.Builder.from_source("x + 5", :python)
# => {:ok, %Metastatic.Document{
# ast: {:binary_op, [category: :arithmetic, operator: :+],
# [{:variable, [], "x"}, {:literal, [subtype: :integer], 5}]},
# language: :python,
# metadata: %{...},
# original_source: "x + 5"
# }}
Round-trip test: Source → M1 → M2 → M1 → Source.
Useful for validating adapter fidelity. The result may have normalized formatting but should be semantically equivalent to the input.
Examples
iex> Metastatic.Builder.round_trip("x + 5", :python)
{:ok, "x + 5"}
iex> Metastatic.Builder.round_trip("x + 5", :python)
{:ok, "x + 5"} # Normalized spacing
iex> Metastatic.Builder.round_trip("invalid!", :python)
{:error, "SyntaxError: ..."}
@spec supported_languages() :: [map()]
Get information about available language adapters.
Returns a list of supported languages with their adapters.
Examples
iex> Metastatic.Builder.supported_languages()
[
%{language: :python, adapter: Metastatic.Adapters.Python, extensions: [".py"]},
%{language: :javascript, adapter: Metastatic.Adapters.JavaScript, extensions: [".js", ".jsx"]}
]
@spec to_file(Metastatic.Document.t(), Path.t(), atom() | nil) :: :ok | {:error, term()}
Write a MetaAST document to a file.
Converts to source and writes to the specified path.
Examples
iex> doc = %Metastatic.Document{...}
iex> Metastatic.Builder.to_file(doc, "output.py")
:ok
iex> Metastatic.Builder.to_file(doc, "/invalid/path/file.py")
{:error, :enoent}
@spec to_source(Metastatic.Document.t(), atom() | nil) :: {:ok, String.t()} | {:error, term()}
Convert a MetaAST document back to source code.
Performs the full M2 → M1 → Source pipeline:
- Gets adapter for document's language
- Reifies M2 to M1 (native AST)
- Unparses M1 to source
- Returns source string
Parameters
document- MetaAST documenttarget_language- Optional target language (defaults to document's language)
Returns
{:ok, source}- Successfully unparsed{:error, reason}- Unparsing failed
Examples
iex> doc = %Metastatic.Document{
...> ast: {:literal, [subtype: :integer], 42},
...> language: :elixir,
...> metadata: %{}
...> }
iex> Metastatic.Builder.to_source(doc)
{:ok, "42"}Cross-Language Translation (Future)
# This will be possible once multiple adapters are implemented
iex> doc = %Metastatic.Document{ast: ..., language: :python, ...}
iex> Metastatic.Builder.to_source(doc, :javascript)
{:ok, "// JavaScript equivalent"}
Validate that source code can be parsed and abstracted.
Returns true if the source is valid, false otherwise.
Examples
iex> Metastatic.Builder.valid_source?("x + 5", :python)
true
iex> Metastatic.Builder.valid_source?("x +", :python)
false
iex> Metastatic.Builder.valid_source?("code", :unknown)
false