Presentamos Achronyme — un lenguaje para pruebas zero-knowledge. Lee el anuncio arrow_right_alt

Referencia del AST

Árbol sintáctico abstracto de Achronyme: enums, campos y rastreo de spans.

Visión general

  • AST con propiedad, construido a mano. Sin rowan, sin Rc, sin interning en esta capa.
  • Archivo: crates/achronyme-parser/src/ast.rs
  • Cada nodo lleva un Span. Re-exportado desde crates/diagnostics/src/span.rs.
  • Cada Expr lleva un ExprId(u32) — denso, único dentro de un Program. Usado por el resolver para adjuntar SymbolId vía un HashMap<ExprId, SymbolId> paralelo en lugar de mutar el AST.
  • Las expresiones sintéticas generadas por el compilador usan ExprId::SYNTHETIC = 0. Los IDs reales asignados por el parser comienzan en 1.
  • El AST es inmutable después del parseo. Los pases de lowering (ProveIR, bytecode) leen el árbol y emiten nuevos IRs; nunca reescriben nodos.

Span / posición en el código fuente

pub struct Span {
    pub byte_start: usize,
    pub byte_end: usize,
    pub line_start: usize,
    pub col_start: usize,
    pub line_end: usize,
    pub col_end: usize,
}

pub struct SpanRange {
    pub start: Span,
    pub end: Span,
}

Los spans están basados en byte-offset para hacer slicing del código fuente original y basados en (line, col) para diagnósticos visibles al usuario. SpanRange se usa cuando el span de un nodo realmente necesita dos anclas (p.ej., un if/else cuya rama else vive lejos del encabezado).

Tope: Program

pub struct Program {
    pub stmts: Vec<Stmt>,
}

Un Program es la unidad de parseo. Cada archivo de entrada se convierte en un Program; las importaciones de módulos se resuelven por separado recorriendo Stmt::Import y re-parseando cada archivo referenciado en su propio Program.

Enum Stmt

De crates/achronyme-parser/src/ast.rs:52-141:

VarianteCamposPropósito
LetDeclname: String, type_ann: Option<TypeAnnotation>, value: Expr, span: SpanEnlace inmutable. let x = 1;
MutDeclname: String, type_ann: Option<TypeAnnotation>, value: Expr, span: SpanEnlace mutable. mut x = 1;
Assignmenttarget: Expr, value: Expr, span: Spantarget debe ser un lvalue (Ident / Index / DotAccess); validado en tiempo de resolución.
PublicDeclnames: Vec<InputDecl>, span: Spanpublic root, leaf; — visibilidad de inputs de circuito / prove.
WitnessDeclnames: Vec<InputDecl>, span: Spanwitness path; — misma forma que PublicDecl, visibilidad opuesta.
FnDeclname: String, params: Vec<TypedParam>, return_type: Option<TypeAnnotation>, body: Block, span: SpanFunción de nivel superior.
CircuitDeclname: String, params: Vec<TypedParam>, body: Block, span: Spancircuit MerkleProof(...) { ... }.
Printvalue: Expr, span: SpanEfecto colateral solo de VM.
Returnvalue: Option<Expr>, span: Spanreturn; puro está permitido en funciones que retornan nil.
Breakspan: SpanControl de loop.
Continuespan: SpanControl de loop.
Importpath: String, alias: Option<String>, span: Spanimport "./foo.ach" as foo;
SelectiveImportnames: Vec<String>, path: String, span: Spanimport { a, b } from "./foo.ach";
Exportinner: Box<Stmt>, span: Spanexport fn foo() { ... }.
ExportListnames: Vec<String>, span: Spanexport { a, b };
ImportCircuitpath: String, alias: Option<String>, span: Spanimport circuit "./foo.circom" as Foo;
ExprExprExpresión pura como sentencia (foo();).
Errorspan: SpanPlaceholder de recuperación; los pases de lowering los saltan silenciosamente.

Enum Expr

De crates/achronyme-parser/src/ast.rs:188-331. Cada variante lleva id: ExprId y span: Span además de los campos listados.

Literales

VarianteCampos extraNotas
Numbervalue: StringAlmacenado como string; parseado perezosamente por lowering. Permite enteros de longitud arbitraria sin comprometerse a un tipo numérico en tiempo de parseo.
FieldLitvalue: String, radix: FieldRadixradix ∈ {Decimal, Hex, Bin}.
BigIntLitvalue: String, width: u16, radix: BigIntRadixwidth es el ancho de bits: 0i256_…width = 256.
Boolvalue: bool
StringLitvalue: StringUTF-8 sin escapar.
NilSolo VM; rechazado por el lowering de ProveIR.

Nombres + acceso

VarianteCampos extraNotas
Identname: String
StaticAccesstype_name: String, member: StringType::MEMBER, p.ej., Field::ZERO, Int::MAX, BigInt::from_bits.
Indexobject: Box<Expr>, index: Box<Expr>arr[i].
DotAccessobject: Box<Expr>, field: StringSe desugara a dispatch de método cuando va seguido de (...) (manejado en postfix).
Callcallee: Box<Expr>, args: Vec<CallArg>Args posicionales y de palabra clave mezcladas en args.

Operadores

VarianteCampos extra
BinOpop: BinOp, lhs: Box<Expr>, rhs: Box<Expr>
UnaryOpop: UnaryOp, operand: Box<Expr>

Control + estructura

VarianteCampos extraNotas
Ifcondition: Box<Expr>, then_block: Block, else_branch: Option<ElseBranch>else_branch es o un bloque u otro If, soportando cadenas else if.
Forvar: String, iterable: ForIterable, body: BlockVer ForIterable abajo.
Whilecondition: Box<Expr>, body: BlockDesenrollado por la capa de lowering cuando se usa dentro del cuerpo de un circuito.
Foreverbody: Blockforever { ... } — solo VM; el lowering de circuito lo rechaza.
Blockblock: BlockBloque-como-expresión; el valor de la última expresión es el valor del bloque si no hay ; final.
FnExprname: Option<String>, params: Vec<TypedParam>, return_type: Option<TypeAnnotation>, body: BlockFunción-como-valor. name es para auto-recursión.
Provename: Option<String>, body: Block, params: Vec<TypedParam>La nueva forma de parámetros tipados; la forma legacy de lista pública se reescribe a esta forma durante el parseo.

Compuestas

VarianteCampos extraNotas
Arrayelements: Vec<Expr>Arrays de tamaño estático en modo circuito.
Mappairs: Vec<(MapKey, Expr)>Solo VM; rechazado por ProveIR.

Recuperación

VarianteCampos extraNotas
ErrorEmitido por puntos de sincronización del parser (synchronize() en parser/core.rs).

Operadores

pub enum BinOp {
    Add, Sub, Mul, Div, Mod, Pow,
    Eq, Neq, Lt, Le, Gt, Ge,
    And, Or,
}

pub enum UnaryOp {
    Neg, Not,
}

BinOp cubre todos los operadores infijos en la tabla de precedencia. No hay nodo separado para el ternario — se desugara a If en tiempo de parseo, así los pases posteriores solo ven una forma de condicional.

Tipos auxiliares

pub struct CallArg {
    pub name: Option<String>,   // None = positional, Some("x") = keyword
    pub value: Expr,
}

pub enum ElseBranch {
    Block(Block),
    If(Box<Expr>),    // chained else-if
}

pub enum ForIterable {
    Range { start: u64, end: u64 },              // 0..10
    ExprRange { start: u64, end: Box<Expr> },    // 0..n  (dynamic upper bound, circuit mode)
    Expr(Box<Expr>),                             // for x in arr
}

pub enum MapKey {
    Ident(String),
    StringLit(String),
}

pub enum FieldRadix {
    Decimal,
    Hex,
    Bin,
}

pub enum BigIntRadix {
    Decimal,
    Hex,
    Bin,
}

pub struct InputDecl {
    pub name: String,
    pub type_ann: Option<TypeAnnotation>,
}

pub struct TypedParam {
    pub name: String,
    pub type_ann: TypeAnnotation,
}

pub struct TypeAnnotation {
    pub visibility: Option<Visibility>,
    pub base: BaseType,
    pub array_size: Option<usize>,
}

pub enum Visibility {
    Public,
    Witness,
}

pub enum BaseType {
    Field,
    Bool,
    Int,
    String,
}

ForIterable::ExprRange es la variante introducida para límites de loop paramétricos en modo circuito: for i in 0..n donde n es un parámetro de plantilla o un var conocido en tiempo de compilación. El pase de lowering evalúa end a un u64 antes de desenrollar.

TypeAnnotation::array_size es Some(n) para arrays de tamaño fijo (Field[8]) y None para Field[] en parámetros de prove(...) donde el tamaño se captura desde el alcance llamante.

Asignación de ExprId

  • IDs u32 densos, asignados por el parser a medida que recorre el código fuente.
  • El Program completo es la arena de IDs; las comparaciones cross-program no son significativas (p.ej., ExprId(42) en foo.ach y ExprId(42) en bar.ach no están relacionados).
  • ExprId::SYNTHETIC = 0 está reservado para nodos generados por el compilador (principalmente dentro del lowering de Circom y el compilador de ProveIR cuando sintetiza expresiones auxiliares durante la instanciación de plantillas).
  • El pase del resolver anota cada ExprId con un SymbolId en SymbolTable. Ver crates/resolve/src/annotate.rs. El resolver no muta el AST — construye una tabla lateral indexada por ExprId.
  • Esta forma (AST inmutable + anotaciones de tabla lateral) es lo que hace barato al LSP: el resolver corre en microsegundos y descarta sus tablas cuando el AST es invalidado.

Archivos fuente

ComponenteArchivo
Tipos del ASTcrates/achronyme-parser/src/ast.rs
Spancrates/diagnostics/src/span.rs
Entrada del parsercrates/achronyme-parser/src/lib.rs
Anotación del resolvercrates/resolve/src/annotate.rs
Tabla de símboloscrates/resolve/src/table.rs

Ver Gramática y Lexer para la sintaxis de superficie. Ver Visión General del Pipeline para qué pasa después del parseo.

Navigation