Starlark in Go

repository·master·Indexed 25 days ago

https://github.com/google/starlark-go

An interpreter for Starlark, a deterministic, untyped dialect of Python designed as a configuration language. It is intended to be embedded in Go applications via the go.starlark.net/starlark package to provide safe, parallelizable scripting. The project includes a CLI for executing .star files and an interactive REPL, as well as support for profiling CPU and memory usage.

Tokens
11.4K
Snippets
34
Records
77
Agent score
78%

What's inside starlark-go

  1. Overview of Starlark Language

    master

    Starlark is an untyped, dynamic, and deterministic dialect of Python designed for configuration. It is intended for short-lived programs with no external side effects, primarily used to express structured data or interact with a host application. Key characteristics include:

    • Deterministic Execution: All core functions and operators produce the same results every time, with no sources of randomness, clocks, or unspecified iterators.
    • Subset of Python: It uses Python-like syntax and data types but excludes classes, exceptions, reflection, and concurrency.
    • Memory Management: Features automatic garbage collection.
    • Module-based: A program consists of one or more modules, each defined by a single UTF-8-encoded text file.
  2. Understand Starlark module execution and scoping

    master

    Each Starlark file defines a module, which is a mapping of global variable names to their values. When a file is executed (directly or via load), a new Starlark thread is created to execute all top-level statements.

    Key execution rules:

    • Top-to-bottom flow: Control flow is strictly top-to-bottom because if statements and for loops cannot exist outside of functions.
    • Immutability: Once a module reaches the end of the file, initialization is successful and all global variables are frozen. Subsequent mutation of these globals is impossible.
    • Thread Isolation: Module initialization always occurs in a new thread. Therefore, thread state from a higher-level module is never carried into a lower-level module during initialization. This ensures module initialization is independent of the module that triggered it.
    • Error Handling: Starlark does not provide a mechanism for handling errors within the language itself. If an error occurs, execution stops and the error is reported to the host application with a backtrace.
  3. Understand dialect differences from Bazel's Starlark (Java)

    master

    The Go implementation of Starlark has several differences compared to the Java implementation used by Bazel. While some of these can be controlled via global options to mimic the Bazel dialect, the following features are currently distinct in the Go implementation:

    • String Interpolation: Supports [ioxXc] conversions.
    • String Representation: String elements are bytes, and non-ASCII strings are encoded using UTF-8.
    • String Escaping: Supports hex byte escapes.
    • String Methods: Includes additional methods elem_ords, codepoint_ords, and codepoints.
    • Built-in Functions: Supports chr, ord, and set (the latter via the -set option).
    • Set Operations: Supports set & set (intersection) and set | set (union).
    • Identifiers: assert is a valid identifier.
    • Top-level Execution: if, for, and while are permitted at the top level, and top-level rebindings are permitted (both via the -globalreassign option).
  4. Understand Starlark value types and behaviors

    master

    Starlark has eleven core data types. All values, whether core or application-defined, support three basic behaviors:

    • str(x): returns a string representation of x.
    • type(x): returns a string describing the type of x.
    • bool(x): converts x to a Boolean truth value.
  5. Identify Starlark built-in data types

    master

    Starlark provides several built-in data types. Understanding these is essential for working with the interpreter:

    • NoneType: The type of None.
    • bool: Boolean values (True or False).
    • int: Signed integers of arbitrary magnitude.
    • float: IEEE 754 double-precision floating-point numbers.
    • string: Immutable byte strings.
    • list: Modifiable sequences of values.
    • tuple: Unmodifiable sequences of values.
    • dict: Mappings from values to values.
    • set: A set of values.
    • function: Functions implemented in Starlark.
    • builtin_function_or_method: Functions or methods implemented by the interpreter or host application.

    Note: Some functions (like range) return special-purpose types not listed here. Host applications can also define additional data types.

  6. Freeze mutable values to make them immutable

    master

    Starlark allows you to freeze a mutable value. Once frozen, all subsequent attempts to mutate that value (and any values reachable from it) will result in a dynamic error.

    Note: Immediately after the execution of a Starlark module, all values in its top-level environment are automatically frozen. This allows modules to be used safely in parallel programs without locks.

  7. Control flow with if, elif, and else

    master

    Use if statements to execute a block of code based on a condition. Use elif to check multiple conditions and else for a fallback block.

    Restriction: In standard Starlark, if statements are permitted only within a function definition. However, the Go implementation allows them at the top level if the -globalreassign flag is enabled.

    if score >= 100:
        print("You win!")
        return
    elif x < 0:
        result = -1
    else:
        result = 0
  8. Understand Starlark name resolution and scoping

    master

    Starlark uses static name resolution to map names to variable bindings before execution. A name's scope is the region of text where it refers to the same binding.

    Key scoping rules:

    • Predeclared block: Contains universal constants (None, True, False) and immutable built-in functions (e.g., len, list). These cannot be reassigned.
    • Module block: Contains global bindings. These may be visible to other modules.
    • File block: Contains local bindings created by load statements. File block names and module block names cannot overlap.
    • Function/Comprehension blocks: Contain local bindings (e.g., function parameters, variables assigned within a loop or comprehension).

    If a name is bound anywhere within a block, all uses of that name within the block refer to that binding, even if the use appears before the binding (unlike Python).

  9. Use pass statements for empty blocks

    master

    A pass statement is a null operation. Use it when the syntax requires a statement (such as the body of a function or a loop) but no actual behavior is required.

    def noop():
       pass
    
    def list_to_dict(items):
      # Convert list of tuples to dict
      m = {}
      for k, m[k] in items:
        pass
      return m
  10. Iterate with for loops

    master

    A for loop iterates over an iterable value. For each element, it assigns the value to one or more variables and executes the loop body.

    Guaranteed Termination: Starlark loops always iterate over a finite sequence, ensuring they terminate.

    Loop Control: Use break to terminate the entire loop or continue to skip to the next iteration.

    Restriction: Standard Starlark requires for loops to be inside a function. The Go implementation allows them at top level if the -globalreassign flag is enabled.

    for x in range(10):
       print(x)
    
    for a, i in [["a", 1], ["b", 2]]:
      print(a, i)
    
    for x in range(10):
        if x%2 == 1:
            continue
        if x > 7:
            break
        print(x)