System Architecture - source-academy/pie-slang GitHub Wiki

System Architecture

This page provides a detailed overview of the Pie interpreter's architecture, explaining the key components and their interactions.

High-Level Overview

The Pie interpreter follows a three-level architecture that separates syntax, elaborated expressions, and runtime values:

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”      β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”      β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚    Source   │─────▢│    Core     │─────▢│    Value    β”‚
β”‚ (User Code) β”‚      β”‚(Elaborated) β”‚      β”‚ (Runtime)   β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜      β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜      β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                                                 β”‚
                                                 β–Ό
                                          β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
                                          β”‚    Core     β”‚
                                          β”‚ (Normalized)β”‚
                                          β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

The system processes Pie code through several stages:

  1. Parsing: Convert text to Source AST
  2. Type Checking: Transform Source to Core while checking types
  3. Evaluation: Execute Core to produce Values
  4. Normalization: Convert Values back to Core for comparison

Key Components

Parser System

The parser converts input strings to abstract syntax trees (ASTs) in two stages:

  1. Scheme Parser: Converts the input into a Scheme-like S-expression
  2. Pie Parser: Converts the S-expression into a Pie-specific AST

Key files:

  • src/pie_interpreter/parser/parser.ts

Parser Flow

Input String β†’ Scheme Lexer β†’ Scheme Parser β†’ Pie Parser β†’ Source AST

Main Components

Source

Source represents the original syntax as written by the programmer:

  • Contains location information for error reporting
  • Defines type checking methods (check, synth, isType)
  • Describes the syntactic structure before elaboration

Key files:

  • src/pie_interpreter/types/source.ts

Core

Core represents the elaborated, fully type-checked expressions:

  • Focused on evaluation and normalization
  • Serves as the intermediary between source syntax and runtime values

Key files:

  • src/pie_interpreter/types/core.ts

Value

Value represents the semantic meaning of expressions during evaluation:

  • Captures runtime values during type checking and execution
  • Has methods for normalization (converting back to Core)
  • Includes lazy evaluation support through the now() method

Key files:

  • src/pie_interpreter/types/value.ts
  • src/pie_interpreter/types/neutral.ts

Type Checker

The type checker verifies that Pie programs are well-typed, following bidirectional type checking with three primary operations:

Type Checking Flow

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Expression β”‚
β””β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”˜
      β”‚
   β”Œβ”€β”€β–Όβ”€β”€β” No   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
   β”‚Synthβ”œβ”€β”€β”€β”€β”€β”€β–Ά Check     β”‚
   β””β”€β”€β”¬β”€β”€β”˜      β”‚ Against T β”‚
      β”‚ Yes     β””β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”˜
      β”‚               β”‚
β”Œβ”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”
β”‚  Type T +   β”‚  β”‚Elaboratedβ”‚
β”‚Elaborated E β”‚  β”‚    E     β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
  • isType: Verifies that an expression is a valid type
  • synth: Synthesizes the type of an expression without a type annotation
  • check: Verifies that an expression has a given type

Key files:

  • src/pie_interpreter/typechecker/represent.ts
  • src/pie_interpreter/typechecker/synthesizer.ts
  • src/pie_interpreter/typechecker/utils.ts

Evaluator

The evaluator implements the operational semantics of Pie expressions through a call-by-need evaluation strategy:

Evaluation Process

β”Œβ”€β”€β”€β”€β”€β”€β”    β”Œβ”€β”€β”€β”€β”€β”€β”    β”Œβ”€β”€β”€β”€β”€β”€β”
β”‚ Core │───▢│Value │───▢│ Core β”‚
β””β”€β”€β”€β”€β”€β”€β”˜    β””β”€β”€β”€β”€β”€β”€β”˜    β””β”€β”€β”€β”€β”€β”€β”˜
  valOf       now/     readBack
             readBack
  • Value Evaluation: Each Core expression has a valOf method that evaluates it to a Value
  • Specialized Evaluators: Functions like doApp, doIndNat, etc. handle different expression types
  • Normalization by Evaluation: The readBack functions convert Values back to Core expressions
  • Call-by-need Evaluation: Uses lazy evaluation through the Delay type and now() method

Key files:

  • src/pie_interpreter/evaluator/evaluator.ts
  • src/pie_interpreter/evaluator/utils.ts

Error Handling

The error handling system uses a monadic approach with Perhaps<T>:

Error Handling Flow

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”   success   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Operation1 β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Ά Operation2 β”‚
β””β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”˜             β””β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”˜
      β”‚ failure                  β”‚ failure
      β–Ό                          β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”            β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  Error     β”‚            β”‚  Error     β”‚
β”‚  Message   β”‚            β”‚  Message   β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜            β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
  • Perhaps Type: Represents computations that might succeed or fail
    • go<T>: Contains a successful result of type T
    • stop: Contains an error message and location
  • PerhapsM Type: A mutable container for passing results from computations
  • goOn Function: Sequences computations that may fail

Key files:

  • src/pie_interpreter/types/utils.ts

Tactics System

The tactics system enables structured proof development:

Tactics System Architecture

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ ProofState│◄────   Tactic  │◄────    User   β”‚
β””β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”˜    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
      β”‚
      β”‚
β”Œβ”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”
β”‚ ProofStateβ”‚
β”‚  Updated  β”‚
β””β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”˜
      β”‚
      β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  Final    β”‚
β”‚  Proof    β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
  • ProofState: Tracks current goals and proof environment
  • Tactics: Operate on the ProofState to transform goals
  • ProofManager: Manages the application of tactics to proofs

Key files:

  • src/pie_interpreter/tactics/proofstate.ts
  • src/pie_interpreter/tactics/tactics.ts
  • src/pie_interpreter/tactics/proofmanager.ts

Directory Structure

src/pie_interpreter/
β”œβ”€β”€ parser/                # Parsing functionality
β”‚   └── parser.ts
β”œβ”€β”€ types/                 # Core type definitions
β”‚   β”œβ”€β”€ source.ts          # Source AST
β”‚   β”œβ”€β”€ core.ts            # Core expressions
β”‚   β”œβ”€β”€ value.ts           # Runtime values
β”‚   β”œβ”€β”€ neutral.ts         # Neutral expressions
β”‚   └── utils.ts           # Utilities and monads
β”œβ”€β”€ typechecker/           # Type checking
β”‚   β”œβ”€β”€ represent.ts
β”‚   β”œβ”€β”€ synthesizer.ts
β”‚   └── utils.ts
β”œβ”€β”€ evaluator/             # Evaluation and normalization
β”‚   β”œβ”€β”€ evaluator.ts
β”‚   └── utils.ts
β”œβ”€β”€ tactics/               # Tactical theorem proving
β”‚   β”œβ”€β”€ proofstate.ts
β”‚   β”œβ”€β”€ tactics.ts
β”‚   └── proofmanager.ts
β”œβ”€β”€ utils/                 # Utility functions
β”‚   β”œβ”€β”€ environment.ts
β”‚   β”œβ”€β”€ locations.ts
β”‚   β”œβ”€β”€ context.ts
β”‚   β”œβ”€β”€ fresh.ts
β”‚   └── alphaeqv.ts
└── __tests__/             # Test files