java.lang.Object
com.darkcollective.relix.semantic.internal.SemanticAnalyzer

public final class SemanticAnalyzer extends Object
Entry point for semantic analysis — the engine's front door.

The contract is Script → SemanticResult: the engine analyses an AST, and text is not part of that contract. How a Script is produced — the .relix grammar, a serialized query, a programmatic builder — is a frontend concern, and every frontend enters here.

Usage — AST-first (the primary entry point)

   SemanticAnalyzer analyzer = new SemanticAnalyzer(loader);
   SemanticResult result = analyzer.analyze(script);
 
The root is attributed to the synthetic path AST_PATH ("<ast>"); pass a real one with analyze(Script, String) when the caller knows where the AST came from.

Usage — loader-based

   SemanticAnalyzer analyzer = new SemanticAnalyzer(loader);
   SemanticResult result = analyzer.analyze("./weather.relix");
 
The root is fetched through the configured ScriptLoader, which is also what resolves every import the script reaches — so how a path becomes a Script stays a frontend decision. Reading .relix text is one such frontend and lives outside the engine: see Relix.parse in relix-embed, and FileSystemScriptLoader in relix-console.

Built-in symbols

Pass a BuiltinProvider to register functions or relations that should be available in every script without an explicit import:
   SemanticAnalyzer analyzer = new SemanticAnalyzer(loader, myBuiltins);
 
Use BuiltinProvider.none() (the default) when no built-ins are needed.

Functions

The scalar functions a script may call come from the installed function libraries, discovered once per analyser and carried on to every later phase through SemanticModel.functions(). A library function enters the symbol table when a script first mentions it, so an analysed script's symbols are the functions it calls rather than the whole library. Supply a FunctionCatalog explicitly to control what is callable.

Analysis phases

  1. Obtain the root script (supplied directly as a Script, or loaded from a path via the ScriptLoader).
  2. Load all transitively imported files via the ScriptLoader; detect and report import cycles.
  3. Register built-in symbols via the BuiltinProvider.
  4. Collect all declarations (sources, assignments, defs) as typed symbols in the symbol table, processing files in topological import order.
  5. Resolve import statements — pull exported symbols from imported files into the importing file's scope.
  6. Infer schemas for relational-algebra expression bodies using a SchemaInferenceVisitor; annotate the per-node schema map.
  7. Assemble and validate the schema graph from relate statements and references: blocks, merging any supplemental session-learned edges (see withSupplementalRelationships(com.darkcollective.relix.symbol.graph.SchemaGraph)).
  8. Validate all names, arities, schema compatibility, and source-config completeness; accumulate all errors.

Instances are stateless after construction and may be reused across multiple analyze calls.

  • Field Details

    • STDIN_PATH

      public static final String STDIN_PATH
      Synthetic file path used in SemanticError.filePath() when the root script came from a stream — piped stdin, or any other unnamed source — rather than a named file.

      Like AST_PATH it is not a file name, and is never passed to the file system. A caller analysing text it read from standard input uses it as the root path.

      See Also:
    • AST_PATH

      public static final String AST_PATH
      Synthetic path the root script is attributed to when it was supplied as an already-built Script with no path of its own — see analyze(Script).

      Like STDIN_PATH it is not a file name, and is never passed to the file system.

      See Also:
  • Constructor Details

    • SemanticAnalyzer

      public SemanticAnalyzer(ScriptLoader loader, BuiltinProvider builtins, CatalogProvider catalog)
      Creates an analyser with a custom built-in provider and catalog provider, and no generator catalog.
      Parameters:
      loader - the script loader used to resolve imports; must not be null
      builtins - the hook for registering built-in symbols; must not be null
      catalog - supplies schemas for connection-backed tables; must not be null
    • SemanticAnalyzer

      public SemanticAnalyzer(ScriptLoader loader, BuiltinProvider builtins, CatalogProvider catalog, GeneratorCatalog generators)
      Creates an analyser with a custom built-in provider, catalog provider, and generator catalog.
      Parameters:
      loader - the script loader used to resolve imports; must not be null
      builtins - the hook for registering built-in symbols; must not be null
      catalog - supplies schemas for connection-backed tables; must not be null
      generators - supplies schemas for generator sources; must not be null
    • SemanticAnalyzer

      public SemanticAnalyzer(ScriptLoader loader, BuiltinProvider builtins, CatalogProvider catalog, GeneratorCatalog generators, FunctionCatalog functions)
      Creates an analyser that resolves function calls against functions rather than against the installed libraries.

      Every other constructor discovers the installed libraries — one catalogue, built once and passed on through the SemanticModel — which is what an embedder wants unless it is deliberately controlling which functions a script may call. Passing FunctionCatalog.empty() makes every function call unknown.

      Parameters:
      loader - the script loader used to resolve imports; must not be null
      builtins - the hook for registering built-in symbols; must not be null
      catalog - supplies schemas for connection-backed tables; must not be null
      generators - supplies schemas for generator sources; must not be null
      functions - the functions scripts may call; must not be null
    • SemanticAnalyzer

      public SemanticAnalyzer(ScriptLoader loader, BuiltinProvider builtins)
      Creates an analyser with a custom built-in provider and no catalog (connection tables must declare their schema).
      Parameters:
      loader - the script loader used to resolve imports; must not be null
      builtins - the hook for registering built-in symbols; must not be null
    • SemanticAnalyzer

      public SemanticAnalyzer(ScriptLoader loader, CatalogProvider catalog)
      Creates an analyser with no built-in symbols and the given catalog provider.
      Parameters:
      loader - the script loader used to resolve imports; must not be null
      catalog - supplies schemas for connection-backed tables; must not be null
    • SemanticAnalyzer

      public SemanticAnalyzer(ScriptLoader loader, CatalogProvider catalog, GeneratorCatalog generators)
      Creates an analyser with no built-in symbols, the given catalog provider, and the given generator catalog.
      Parameters:
      loader - the script loader used to resolve imports; must not be null
      catalog - supplies schemas for connection-backed tables; must not be null
      generators - supplies schemas for generator sources; must not be null
    • SemanticAnalyzer

      public SemanticAnalyzer(ScriptLoader loader)
      Creates an analyser with no built-in symbols and no catalog.
      Parameters:
      loader - the script loader used to resolve imports; must not be null
  • Method Details

    • withSupplementalRelationships

      public SemanticAnalyzer withSupplementalRelationships(SchemaGraph supplemental)
      Returns an analyser that merges the given supplemental relationships into the schema graph on every analysis.

      This is the seam for edges acquired outside the source text — conversational acquisition ("how do orders relate to customers?") and relationships captured from the joins a user actually wrote. A session host (the REPL) keeps its learned edges and re-supplies them on each accumulate-and-reanalyze pass. Supplemental edges are re-validated against the current symbol table each time; a stale edge (its relation or column no longer exists) is dropped with a warning, never a hard error — unlike a declared edge in the file, it is not a claim the current source makes.

      Parameters:
      supplemental - the session-learned edges; must not be null
      Returns:
      a new analyser instance; this one is unchanged
    • withComponents

      public SemanticAnalyzer withComponents(ComponentInventory components)
      Returns an analyser whose relix.version additionally reports the components the host can see.

      The engine reports itself and the function libraries it holds; connectors, solvers and JDBC drivers live above it — a driver behind a java.sql dependency no engine module may have — so whoever assembled the process supplies them here.

      Parameters:
      components - the host's inventory; must not be null
      Returns:
      a new analyser instance; this one is unchanged
    • withSessionEvents

      public SemanticAnalyzer withSessionEvents(List<QueryEvent> events)
    • analyze

      public SemanticResult analyze(Script script)
      Analyses an already-built Script — the engine's primary entry point.

      The script is analysed as the root of the import graph; any import statements it contains are still resolved through the configured ScriptLoader. The root carries no path of its own, so its relative import paths are normalised as-is ("./lib.relix" → "lib.relix") and it is attributed to AST_PATH in the diagnostics the analyser raises about the root file. Diagnostics about the tree's contents carry whatever SourceLocation the AST itself holds. Use analyze(Script, String) to supply a real path.

      Parameters:
      script - the root script; must not be null
      Returns:
      the analysis result; never null
      Throws:
      NullPointerException - if script is null
    • analyze

      public SemanticResult analyze(Script script, String rootPath)
      Analyses an already-built Script that came from rootPath.

      Identical to analyze(Script) except that rootPath is the root node's key in the import graph — so it is the base the root's relative import paths resolve against, and the file path the analyser names the root by. A frontend that read the script from a file (or from stdin, using STDIN_PATH) should use this overload.

      Parameters:
      script - the root script; must not be null
      rootPath - the path to attribute the script to; must not be null
      Returns:
      the analysis result; never null
      Throws:
      NullPointerException - if either argument is null
    • analyze

      public SemanticResult analyze(String rootPath)
      Analyses the script at rootPath and returns a result containing the semantic model (if analysis produced one) and all diagnostics collected.
      Parameters:
      rootPath - the path of the root .relix file, interpreted by the configured ScriptLoader
      Returns:
      the analysis result; never null
      Throws:
      NullPointerException - if rootPath is null