Skip to content

arcgolabs/plano

Repository files navigation

plano

plano is an embeddable, schema-driven DSL runtime written in Go.

Current baseline:

  • release: v0.8.0
  • public API generation: v1
  • artifact schema: plano.artifact/v2

This repository currently contains a first usable implementation with:

  • hand-written lexer and parser
  • AST and diagnostics with compiler-provided quick-fix suggestions
  • a Go workspace (go.work) with separate core, CLI, and example modules
  • schema registration for forms and functions
  • a Cobra-based CLI under cmd/plano for core parsing, analysis, and embedded sample display
  • a public bind API for declaration and symbol collection
  • a public check API for static type analysis
  • a public HIR phase for typed compiler-internal lowering input
  • a compiler that produces a typed document
  • script-body execution with lexical scope, filtered loops, conditional expressions, membership expressions, and user-defined functions
  • script control flow with else if, break, and continue
  • expr-lang backed expression evaluation with host-registered variables and functions
  • bounded parse and expr-lang program caches for repeated compile requests
  • bundled example host DSLs under examples/
  • validated action registry for call statements
  • glob imports via **
  • a Taskfile.yml for common local commands
  • unit tests for parsing, compilation, imports, script execution, and lowering

Packages

  • cmd/plano: CLI for parsing, checking, compiling, and displaying embedded .plano sample files
  • frontend/plano: ParseFile API for .plano source to AST
  • compiler: structured compile API from source bytes, strings, or files to typed documents
  • lsp: workspace analysis plus a basic go.lsp.dev/protocol LSP server with hover, definition, diagnostics, folding ranges, code actions, and expr-lang host binding hints
  • schema: form specs, field specs, types, refs, and builtin scalar types
  • ast: parser output nodes
  • diag: diagnostics model

Examples:

  • examples/builddsl: build graph lowering
  • examples/pipelinedsl: CI pipeline lowering
  • examples/servicedsl: service topology lowering

The example modules are documentation and host-integration examples. The CLI intentionally does not import them; it embeds a few sample .plano files for quick inspection.

The implementation also uses:

  • collectionx for ordered compiler outputs, object values, and host-side IR structures
  • expr-lang/expr for opt-in dynamic expression evaluation through expr(...)
  • mo for optional values in lowered IR
  • lo for concise lowering transforms
  • oops for internal error wrapping on loader and lowering boundaries

Quick Example

package main

import (
    "context"

    "github.com/arcgolabs/plano/compiler"
    "github.com/arcgolabs/plano/schema"
)

func main() {
    c := compiler.New(compiler.Options{})

    _ = c.RegisterForm(schema.FormSpec{
        Name:      "workspace",
        LabelKind: schema.LabelNone,
        BodyMode:  schema.BodyFieldOnly,
        Fields: schema.Fields(
            schema.FieldSpec{
                Name:     "name",
                Type:     schema.TypeString,
                Required: true,
            },
        ),
    })

    _, _ = c.CompileSource(context.Background(), "build.plano", []byte(`
workspace {
  name = "demo"
}
`))
}

You can also inspect the declaration-binding phase directly:

binding, diags := c.BindSource(context.Background(), "build.plano", src)
_ = binding
_ = diags

The compiler also exposes string helpers when you already have in-memory source text:

result := c.CompileStringDetailed(ctx, "build.plano", src)
_ = result.Document

Hosts can expose additional variables and functions to expr-lang expressions:

_ = c.RegisterExprVar("branch", "main")
_ = c.RegisterExprFunc("slug", func(params ...any) (any, error) {
    return strings.ReplaceAll(params[0].(string), "/", "-"), nil
}, func(string) string { return "" })

Those values are available from expr(...) and expr_eval(...) in .plano scripts, together with current script locals and top-level constants. Repeated expr-lang program compilation is cached by default. Set compiler.Options.ExprCacheEntries to a positive entry count to tune the bounded cache, or -1 to disable it.

And the static typecheck phase:

checks, diags := c.CheckSource(context.Background(), "build.plano", src)
_ = checks
_ = diags

And the typed HIR phase:

result := c.CompileSourceDetailed(ctx, "build.plano", src)
_ = result.HIR

For editor integrations, the lsp module can either analyze in-memory documents directly or expose a basic LSP server:

server := lsp.NewServer(lsp.ServerOptions{
    Compiler: configuredCompiler,
})
_ = lsp.ServeStdio(context.Background(), lsp.ServerOptions{
    Compiler: configuredCompiler,
})

The LSP module also exposes folding ranges through Snapshot.FoldingRanges(...) and compiler-suggested quick fixes through Snapshot.CodeActions(...), with matching protocol handlers.

Docs

CLI

Build and run:

go run ./cmd/plano examples
go run ./cmd/plano examples build
go run ./cmd/plano version
go run ./cmd/plano parse ./cmd/plano/samples/basic.plano
go run ./cmd/plano bind ./cmd/plano/samples/basic.plano
go run ./cmd/plano check ./cmd/plano/samples/basic.plano
go run ./cmd/plano hir ./cmd/plano/samples/basic.plano
go run ./cmd/plano compile ./cmd/plano/samples/basic.plano
go run ./cmd/plano validate ./cmd/plano/samples/basic.plano
go run ./cmd/plano diag ./cmd/plano/samples/basic.plano
go run ./cmd/plano compile --format yaml --out ./document.yaml ./cmd/plano/samples/basic.plano

parse prints AST JSON. bind prints the declaration and symbol binding result JSON. check prints the binding plus static typecheck result JSON. hir prints the typed HIR JSON. compile prints the typed document JSON. validate checks whether the file compiles successfully. diag prints diagnostics without failing the command on warnings. examples lists embedded sample files, or prints one sample when passed a name. version prints the release version, public API generation, and artifact schema generation.

Output controls:

  • --format json|yaml for parse, bind, check, hir, and compile
  • --format text|json|yaml for validate and diag
  • --out <path> to write command output to a file instead of stdout
  • --strict on compiler-backed commands to fail on any diagnostics, not only errors

CLI releases:

  • pushing a tag like cmd/plano/v0.8.1 runs the cli-release GitHub Actions workflow
  • the workflow builds cross-platform plano archives with GoReleaser and publishes them to a GitHub Release for that tag
  • workspace-local modules are resolved through go.work; child go.mod files should not declare local github.com/arcgolabs/plano dependencies

Taskfile

The repository also ships a small Taskfile.yml for common local workflows:

task fmt
task test
task lint
task bench
task bench:compiler
task bench:lsp
task work:sync
task examples
task sample SAMPLE=build
task parse FILE=./cmd/plano/samples/basic.plano FORMAT=yaml
task bind FILE=./cmd/plano/samples/basic.plano FORMAT=yaml
task check FILE=./cmd/plano/samples/basic.plano FORMAT=yaml
task hir FILE=./cmd/plano/samples/basic.plano FORMAT=yaml
task compile FILE=./cmd/plano/samples/basic.plano FORMAT=yaml OUT=./document.yaml

Repo Shape

The repository now runs as a small Go workspace rather than a single module.

  • Root module github.com/arcgolabs/plano: compiler core, AST, diagnostics, schema, and frontend packages
  • cmd/plano: standalone CLI module
  • lsp: standalone LSP helper module
  • examples/builddsl: example build DSL module
  • examples/pipelinedsl: example pipeline DSL module
  • examples/servicedsl: example service DSL module

That split gives us cleaner boundaries:

  • core can evolve without dragging CLI/example dependencies into every consumer build
  • example DSLs are now visibly host-side modules instead of looking like core packages
  • future modules such as lsp or plugin/runtime adapters can be added without reshaping the core again

Workspace note:

  • sibling workspace modules are resolved through go.work
  • child go.mod files only declare external dependencies, not other local workspace modules

Current Scope

The implementation is still narrower than the full language draft, but the main compiler path is now usable:

  • imports
  • glob imports such as import "tasks/**/*.plano"
  • top-level const
  • top-level user-defined fn
  • builtins such as len, keys, values, range, get, slice, has, append, concat, and merge
  • expr-lang backed expr(...) and expr_eval(...) calls with host-registered variables and functions
  • conditional expressions with condition ? then : else
  • membership expressions with item in list and key in map
  • LSP completion and hover for expr-lang host variables and functions inside expr(...) strings
  • static typechecking for expressions, fields, returns, and registered function/action signatures
  • validated call statements through host-registered actions
  • typed HIR output for stable lowering
  • form declarations
  • script-body execution with let, local reassignment, if, else if, single- and dual-variable for, for ... where, conditional expressions, membership expressions, break, and continue
  • field assignments, nested forms, and call statements
  • expression evaluation with registered and user-defined functions
  • lowering from HIR to sample IRs through documentation example modules under examples/

Plugin packaging and richer module/runtime integration are still pending.

Near-Term Direction

The repository is now past the "parser prototype" stage and behaves more like a real compiler core. The next useful iterations are mostly semantic and tooling work:

  • keep extending collection and script ergonomics without collapsing host DSL boundaries into core
  • tighten diagnostics, especially related spans and richer import/reference errors
  • stabilize the HIR and example-lowering contracts before introducing a formal plugin packaging API
  • add more real-world example DSL flows so language changes are exercised against multiple host shapes

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Packages

 
 
 

Contributors

Languages