OR-1 dataflow CPU sketch
at main 120 lines 3.6 kB view raw
1"""Progressive pipeline runner for dfasm assembly. 2 3Runs assembler passes individually, capturing the deepest successful IRGraph 4even when later passes fail. This enables partial graph visualisation. 5""" 6 7from __future__ import annotations 8 9from dataclasses import dataclass 10from enum import Enum 11from typing import Optional 12 13from lark import Lark 14from lark.exceptions import UnexpectedInput 15from pathlib import Path 16 17from asm.ir import IRGraph 18from asm.lower import lower 19from asm.resolve import resolve 20from asm.place import place 21from asm.allocate import allocate 22from asm.errors import AssemblyError 23 24 25_GRAMMAR_PATH = Path(__file__).parent.parent / "dfasm.lark" 26_parser: Optional[Lark] = None 27 28 29def _get_parser() -> Lark: 30 """Lazily initialize and cache the Lark parser.""" 31 global _parser 32 if _parser is None: 33 _parser = Lark( 34 _GRAMMAR_PATH.read_text(), 35 parser="earley", 36 propagate_positions=True, 37 ) 38 return _parser 39 40 41class PipelineStage(Enum): 42 """Enumeration of pipeline stages.""" 43 PARSE_ERROR = "parse_error" 44 LOWER = "lower" 45 RESOLVE = "resolve" 46 PLACE = "place" 47 ALLOCATE = "allocate" 48 49 50@dataclass(frozen=True) 51class PipelineResult: 52 """Result of running the progressive pipeline. 53 54 Attributes: 55 graph: The IRGraph at the deepest successful stage, or None if parse failed 56 stage: The stage where progress stopped 57 errors: All AssemblyErrors accumulated from the pipeline 58 parse_error: String representation of parse error, if stage is PARSE_ERROR 59 """ 60 graph: Optional[IRGraph] 61 stage: PipelineStage 62 errors: list[AssemblyError] 63 parse_error: Optional[str] = None 64 65 66def run_progressive(source: str) -> PipelineResult: 67 """Run the assembly pipeline progressively, capturing errors at each stage. 68 69 Unlike asm._run_pipeline() which raises on first error, this function runs 70 each pass independently and accumulates errors, allowing partial graphs to 71 be captured for visualization. 72 73 Pipeline stages: 74 1. Parse: Convert source to Lark CST (may raise lark.exceptions.UnexpectedInput) 75 2. Lower: CST to IRGraph (may accumulate errors but doesn't stop) 76 3. Resolve: Validate edge endpoints and scope (may accumulate errors) 77 4. Place: Validate/auto-place nodes on PEs (stops before this if resolve failed) 78 5. Allocate: Assign IRAM offsets and context slots (stops before this if place failed) 79 80 Args: 81 source: dfasm source code as a string 82 83 Returns: 84 PipelineResult containing the deepest graph, stage reached, and all errors 85 """ 86 # Stage 1: Parse 87 try: 88 tree = _get_parser().parse(source) 89 except UnexpectedInput as exc: 90 return PipelineResult( 91 graph=None, 92 stage=PipelineStage.PARSE_ERROR, 93 errors=[], 94 parse_error=str(exc), 95 ) 96 97 # Stage 2: Lower 98 graph = lower(tree) 99 stage = PipelineStage.LOWER 100 101 # Stage 3: Resolve 102 # Note: resolve() always runs after lower (matches asm._run_pipeline behaviour) 103 graph = resolve(graph) 104 stage = PipelineStage.RESOLVE 105 106 # Stage 4: Place (skip if resolve accumulated errors) 107 if not graph.errors: 108 graph = place(graph) 109 stage = PipelineStage.PLACE 110 111 # Stage 5: Allocate (skip if place accumulated errors) 112 if not graph.errors: 113 graph = allocate(graph) 114 stage = PipelineStage.ALLOCATE 115 116 return PipelineResult( 117 graph=graph, 118 stage=stage, 119 errors=list(graph.errors), 120 )