Slang Solidity Compiler APIs

repository·main·Indexed 42 days ago

https://github.com/nomicfoundation/slang

A modular set of Solidity compiler APIs written in Rust and distributed in multiple languages, including TypeScript. Slang provides high-fidelity Concrete Syntax Trees (CST) for building advanced developer tools, code analysis, and IDE extensions. It includes specialized libraries for parsing (slang_solidity_v2_parser), CST support (slang_solidity_v2_cst), and semantic analysis (slang_solidity_v2_semantic), featuring abstractions like Compilation Units, Binding Graphs, and a declarative Query API.

Tokens
86.4K
Snippets
249
Records
377
Agent score
51%

What's inside Slang

  1. What is Slang and how is it used?

    main

    Slang is a modular Solidity compiler designed specifically for code analysis and developer tooling. Unlike the standard solc compiler, Slang does not aim to emit optimized EVM bytecode for production. Instead, it provides domain-specific APIs to facilitate tasks such as:

    • IDE integration (syntax highlighting, autocomplete)
    • Linting and validation
    • Static analysis
    • Building formal verification tools

    Slang is written in Rust and distributed as an npm package (@nomicfoundation/slang) with a TypeScript interface, though Rust crate and Python library distributions are planned for the future.

  2. Understand breaking changes in Slang v2

    main

    The Slang v2 lexer/parser system introduces significant changes to the language grammar and manifest. Key architectural shifts include:

    • Versioning: Support for Solidity versions before 0.8.0 has been removed.
    • Lexical Contexts: Section entries are now grouped under a top-level Language::contexts field. Each LexicalContext defines its own identifier_token for keywords, replacing the global Keyword::identifier.
    • Trivia: Trivia items (like whitespace or comments) are now defined per lexical context with context-prefixed names (e.g., PragmaWhitespace, YulEndOfLine) instead of being shared globally. The Language::leading_trivia and Language::trailing_trivia fields, and the TriviaParser type, have been removed.
    • Terminals: The new lexer uses a priority-based system to resolve ambiguities instead of backtracking. Priority is determined by kind (Trivia < Tokens < Keywords) and then by declaration order. Scanner has been renamed to TokenScanner and KeywordValue to KeywordScanner.
    • Nonterminals: FieldsErrorRecovery and FieldDelimiters types from the v1 parser have been removed.
  3. Use the Parser to generate a CST

    main

    The Parser API produces a Concrete Syntax Tree (CST) from Solidity source code.

    Key requirements:

    • You must initialize a Parser with a specific Solidity version. Providing an accurate version is critical as it determines the shape of the syntax tree and error reporting.
    • The parser returns a ParseOutput object, which contains:
      • The root of the CST.
      • Any syntax errors encountered during parsing.

    Refer to the supported versions list in the Solidity Grammar documentation to ensure compatibility.

  4. Important notes on Slang memory behavior

    main

    When working with Slang memory, keep the following behaviors in mind:

    • Non-deterministic Cleanup: Garbage collection and finalizers are not deterministic. Behavior and timing will vary between different JavaScript engines and machines.
    • Wasm Memory Growth: Once Wasm memory is allocated, it never shrinks back after being cleaned. This is a design characteristic of WebAssembly. You should expect memory usage to grow initially and then stabilize.
    • Interactive Environments: In browsers or IDEs, you generally do not need to manually add pauses, as the engine has frequent idle periods to perform cleanup.
  5. Understand the Binding Graph concept

    main

    The binding graph is a graph structure that represents the relationships between identifiers across all source files within a CompilationUnit. It stores cursors to all definitions and references and provides the logic to resolve the edges between them.

    Key Characteristics:

    • Lazy Construction: Building the graph is an expensive operation. It is constructed lazily upon the first access and is cached thereafter.
    • Identifier Identification: To identify if a terminal in the CST is an identifier (e.g., a Solidity Identifier or a Yul YulIdentifier), use the TerminalKindExtensions.isIdentifier() API.
    • Resolution: Any identifier in the tree can be resolved to either a Definition or a Reference via the binding graph.
  6. Iterate over node patterns using match roots

    main
    Instead of manual iteration via visitors, queries allow you to iterate over all node patterns that match a query. Each match provides a cursor pointing to the node matched by the outermost pattern in the query, referred to as the match root. This is useful for finding all instances of a specific node type, such as contract definitions.
  7. Resolve identifiers with the Binding Graph

    main

    The BindingGraph is a graph structure within a CompilationUnit that maps relationships between identifiers across different source files.

    Core Functionality:

    • Identifier Roles: It identifies whether an identifier acts as a Definition or a Reference (e.g., an alias).
    • Resolution: Provides an API to resolve references to their definitions.
    • Relationship Queries:
      • Find all references to a specific definition.
      • Find all definitions bound by a specific reference.
  8. Understand Slang memory management (Internal vs External)

    main

    Slang's core is implemented in Rust and compiled to WebAssembly (Wasm), then imported into JavaScript/TypeScript. This creates two distinct memory spaces:

    1. Internal Memory (Heap Space): This is the memory used by JavaScript objects. Slang objects you interact with (like a Cursor) reside here. These objects are lightweight because they primarily contain a handle (a reference number).
    2. External Memory (Wasm Memory): This is the memory used by the Wasm component. A single lightweight JavaScript object (like a Cursor) can be associated with a significant amount of external memory (e.g., the entire parsing tree).

    Because the JavaScript engine only sees the lightweight handle in internal memory, it may not realize how much external Wasm memory is actually being consumed, which can lead to high memory usage or out-of-memory errors.