Skip to main content

openscad_lalr_parser

A fast LALR(1) parser for the OpenSCAD language with AST generation, powered by Lark.

Tests PyPI version License: MIT

Overview

openscad_lalr_parser parses OpenSCAD source code using a Lark-based LALR(1) parser and produces an abstract syntax tree (AST). The AST node classes are identical to those in the openscad_parser library, making it a drop-in replacement with significantly better performance.

Why LALR?

  • Speed: LALR(1) parsing is O(n) in the input size — no PEG backtracking overhead.
  • Predictability: Parse time is always proportional to input size.
  • Compatibility: Produces the same AST node types as openscad_parser.

Installation

pip install openscad-lalr-parser

For YAML output support:

pip install openscad-lalr-parser[yaml]

From Source

git clone https://github.com/BelfrySCAD/openscad_lalr_parser.git
cd openscad_lalr_parser
pip install -e ".[dev]"

Quick Start

Parse a String

from openscad_lalr_parser import getASTfromString

ast = getASTfromString("cube([1, 2, 3]);")
for node in ast:
    print(node)

Parse a File

from openscad_lalr_parser import getASTfromFile

ast = getASTfromFile("model.scad")
for node in ast:
    print(node)

Files are cached by modification time — repeated calls to getASTfromFile() with the same unchanged file return instantly. A disk cache is also maintained for persistence across interpreter sessions.

Include Comments

ast = getASTfromString("""
    // A comment
    x = 42; /* inline */
""", include_comments=True)

Comments are returned as CommentLine, CommentSpan, and BlankLine nodes at the top level. Inline comments adjacent to expressions are wrapped in CommentedExpr nodes with leading_comments and trailing_comments fields.

Scope Analysis

from openscad_lalr_parser import getASTfromString, build_scopes

ast = getASTfromString('''
    x = 10;
    function double(n) = n * 2;
    module box(size) { cube(size); }
''')

root_scope = build_scopes(ast)
print(root_scope.lookup_variable("x"))
print(root_scope.lookup_function("double"))
print(root_scope.lookup_module("box"))

Serialization

from openscad_lalr_parser import getASTfromString, ast_to_json, ast_from_json

ast = getASTfromString("cube(10);")

# Serialize to JSON
json_str = ast_to_json(ast, indent=2)

# Deserialize back to AST nodes
ast2 = ast_from_json(json_str)

YAML serialization is available when PyYAML is installed:

from openscad_lalr_parser import ast_to_yaml, ast_from_yaml

yaml_str = ast_to_yaml(ast)
ast2 = ast_from_yaml(yaml_str)

Pretty-Printing

from openscad_lalr_parser import getASTfromString, to_openscad

ast = getASTfromString("module box(w,h){cube([w,h,1]);}")
print(to_openscad(ast, indent_width=4))

Output:

module box(w, h) {
    cube([w, h, 1]);
}

CLI

The openscad-lalr command-line tool parses OpenSCAD files and outputs JSON, YAML, or reformatted source.

# Parse to JSON (default)
openscad-lalr model.scad

# Pretty-print / reformat
openscad-lalr --format model.scad

# YAML output
openscad-lalr --yaml model.scad

# Read from stdin
echo 'cube(10);' | openscad-lalr -

# Include comments in output
openscad-lalr --with-comments model.scad

# Custom indentation
openscad-lalr --format --indent 2 model.scad

# Skip include resolution
openscad-lalr --no-includes model.scad

API Reference

Parsing Functions

Function Description
getASTfromString(code, include_comments=False, origin="<string>") Parse OpenSCAD source code and return its AST.
getASTfromFile(file, include_comments=False, process_includes=True) Parse a file with mtime-based caching and optional include resolution.
getASTfromLibraryFile(currfile, libfile, ...) Find and parse a library file using OpenSCAD's search path rules.
parse_ast(code, origin="<string>") Low-level parse returning AST nodes (no comment processing).
findLibraryFile(currfile, libfile) Find a library file path without parsing it.
librarySearchDirs(currfile) The directories searched, in order: the including file's, each OPENSCADPATH entry, then the user's OpenSCAD libraries folder (as OpenSCAD orders them).
clear_ast_cache() Clear the in-memory and on-disk AST caches.

Serialization Functions

Function Description
ast_to_json(ast, indent=None) Serialize AST nodes to a JSON string.
ast_from_json(json_str) Deserialize AST nodes from a JSON string.
ast_to_dict(ast) Convert AST nodes to plain dicts.
ast_from_dict(data) Reconstruct AST nodes from dicts.
ast_to_yaml(ast) Serialize AST nodes to YAML (requires PyYAML).
ast_from_yaml(yaml_str) Deserialize AST nodes from YAML.
to_openscad(ast, indent_width=4) Pretty-print AST back to OpenSCAD source.

Scope Analysis

Function / Class Description
build_scopes(ast) Build scope tree for top-level AST nodes. Returns root Scope.
Scope Lexical scope with lookup_variable(), lookup_function(), lookup_module().

AST Node Classes

All AST nodes inherit from ASTNode. The main categories are:

Literals: Identifier, StringLiteral, NumberLiteral, BooleanLiteral, UndefinedLiteral, RangeLiteral (its implicit_step is true for [a:b], whose step node is a synthesized 1)

Operators: AdditionOp, SubtractionOp, MultiplicationOp, DivisionOp, ModuloOp, ExponentOp, UnaryMinusOp, LogicalAndOp, LogicalOrOp, LogicalNotOp, BitwiseAndOp, BitwiseOrOp, BitwiseNotOp, BitwiseShiftLeftOp, BitwiseShiftRightOp, EqualityOp, InequalityOp, GreaterThanOp, GreaterThanOrEqualOp, LessThanOp, LessThanOrEqualOp, TernaryOp

Expressions: PrimaryCall, PrimaryIndex, PrimaryMember, LetOp, EchoOp, AssertOp, FunctionLiteral, ListComprehension, RenderExpression

RenderExpression is render() in expression position, obj = render() { cube(1); };, a language extension of openscad_cpp_evaluator/BelfrySCAD: its children's geometry as a value. The braces are required. To make it parseable, render is a reserved word wherever an expression can start, so it can't be a variable or an argument name; render() cube(1); as a statement is still a ModularCall.

List Comprehensions: ListCompFor, ListCompCFor, ListCompIf, ListCompIfElse, ListCompLet, ListCompEach

Declarations: Assignment, FunctionDeclaration, ModuleDeclaration, ParameterDeclaration

Module Instantiations: ModularCall, ModularFor, ModularIntersectionFor, ModularLet, ModularEcho, ModularAssert, ModularIf, ModularIfElse

Modifiers: ModularModifierShowOnly, ModularModifierHighlight, ModularModifierBackground, ModularModifierDisable

Imports: UseStatement, IncludeStatement

Arguments: PositionalArgument, NamedArgument

Comments: CommentLine, CommentSpan, CommentedExpr, BlankLine

Comparison with openscad_parser

Feature openscad_parser openscad_lalr_parser
Parser type PEG (Arpeggio) LALR(1) (Lark)
AST nodes ✓ ✓ (identical)
Scope analysis ✓ ✓
Comment preservation ✓ ✓
Inline comment attachment ✓ ✓
Serialization (JSON/YAML) ✓ ✓
Pretty-printing ✓ ✓
Source maps ✓ ✓
CLI tool ✓ (openscad-parser) ✓ (openscad-lalr)
Disk caching ✓ ✓
Performance Baseline Faster (LALR, no backtracking)

Development

Running Tests

pytest tests/ -v

Running Tests with Coverage

pytest tests/ --cov=src/openscad_lalr_parser --cov-report=term-missing

License

MIT License - Copyright (c) 2025 Belfry OpenSCAD Libraries

See LICENSE for full text.

Release files for openscad-lalr-parser 1.2.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for openscad-lalr-parser 1.2.0
File Size Uploaded
openscad_lalr_parser-1.2.0.tar.gz 35.8 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for openscad-lalr-parser 1.2.0
File Interpreter ABI Platform
openscad_lalr_parser-1.2.0-py3-none-any.whl Python 3 none any Details

Total release size: 76.2 kB

Release files / openscad_lalr_parser-1.2.0.tar.gz

Download URL openscad_lalr_parser-1.2.0.tar.gz
Size 35.8 kB
Tags Source
SHA-256 checksum
How to use checksums
a500b9c0b79df99802d47e88e45fae6b9b4ca261cdc98151edd13b9512c5043b
BLAKE2b-256 checksum
How to use checksums
f5f29736afd930a43b8d4cdd1d859bc95f066cd5019e77a6c009641129ad5031
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 25, 2026.

Transparency log

Release files / openscad_lalr_parser-1.2.0-py3-none-any.whl

Download URL openscad_lalr_parser-1.2.0-py3-none-any.whl
Size 40.4 kB
Tags Python 3
SHA-256 checksum
How to use checksums
32f56d8a1a7259aab70b7740e44825765c40f1febcd27ff9e4f141b8cb213a83
BLAKE2b-256 checksum
How to use checksums
f3279657520cf15375bf4dabd464269d05a58e395c0c4c75cc6066e44f936bef
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 25, 2026.

Transparency log

Release history Release notifications | RSS feed

1.3.1

2 release files

1.3.0

2 release files

1.2.1

2 release files

This release

1.2.0 This release

2 release files

1.1.0

2 release files

1.0.2

2 release files

1.0.1

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page