Merge worktree-agent: add struct literals, generics, print/log builtins
This commit is contained in:
@@ -59,6 +59,8 @@ pub enum TypeExpr {
|
||||
Result { ok: Box<TypeExpr>, err: Box<TypeExpr> },
|
||||
/// `Map<K, V>` — built-in key-value map type
|
||||
Map { key: Box<TypeExpr>, value: Box<TypeExpr> },
|
||||
/// A generic type parameter: `T`, `E` — used inside generic function signatures.
|
||||
TypeParam(String),
|
||||
}
|
||||
|
||||
// ── Patterns (for match arms) ─────────────────────────────────────────────────
|
||||
@@ -118,6 +120,12 @@ pub enum Expr {
|
||||
Try(Box<Expr>),
|
||||
/// Map literal: `{"key": value, ...}`
|
||||
MapLiteral(Vec<(Expr, Expr)>),
|
||||
/// Struct literal: `Point { x: 10, y: 20 }`
|
||||
StructLit {
|
||||
type_name: String,
|
||||
fields: Vec<(String, Expr)>,
|
||||
span: Span,
|
||||
},
|
||||
}
|
||||
|
||||
// ── Match arm ─────────────────────────────────────────────────────────────────
|
||||
@@ -190,10 +198,12 @@ pub enum Stmt {
|
||||
Return(Expr, Span),
|
||||
/// A bare expression used as a statement (usually a call).
|
||||
Expr(Expr, Span),
|
||||
/// `fn name(params) -> ReturnType { body }` (with optional decorators)
|
||||
/// `fn name<T, E>(params) -> ReturnType { body }` (with optional decorators)
|
||||
FnDef {
|
||||
name: String,
|
||||
decorators: Vec<Decorator>,
|
||||
/// Generic type parameters, e.g. `["T", "E"]` for `fn foo<T, E>`.
|
||||
type_params: Vec<String>,
|
||||
params: Vec<Param>,
|
||||
return_type: TypeExpr,
|
||||
body: Vec<Stmt>,
|
||||
|
||||
@@ -11,8 +11,8 @@ mod error;
|
||||
mod parser;
|
||||
|
||||
pub use ast::{
|
||||
BinOp, Expr, Field, Literal, MatchArm, Param, Pattern, Program, SeedStmt, Stmt, TestTarget,
|
||||
TypeExpr, Variant,
|
||||
BinOp, Decorator, Expr, Field, Literal, MatchArm, Param, Pattern, Program, ProtocolMethod,
|
||||
SeedStmt, Stmt, TestTarget, TypeExpr, Variant,
|
||||
};
|
||||
pub use error::{ParseError, ParseErrorKind};
|
||||
pub use parser::parse;
|
||||
|
||||
@@ -333,15 +333,28 @@ impl Parser {
|
||||
fn parse_fn_def(&mut self, start: Span, decorators: Vec<Decorator>) -> Result<Stmt, ParseError> {
|
||||
self.expect(&Token::Fn)?;
|
||||
let (name, _) = self.expect_ident()?;
|
||||
// Optional generic type parameters: `<T, E>`
|
||||
let type_params = if self.eat(&Token::Lt) {
|
||||
let mut tps = Vec::new();
|
||||
while !matches!(self.peek(), Token::Gt | Token::Eof) {
|
||||
let (tp, _) = self.expect_ident()?;
|
||||
tps.push(tp);
|
||||
if !self.eat(&Token::Comma) { break; }
|
||||
}
|
||||
self.expect(&Token::Gt)?;
|
||||
tps
|
||||
} else {
|
||||
Vec::new()
|
||||
};
|
||||
self.expect(&Token::LParen)?;
|
||||
let params = self.parse_param_list()?;
|
||||
let params = self.parse_param_list_with_type_params(&type_params)?;
|
||||
self.expect(&Token::RParen)?;
|
||||
self.expect(&Token::Arrow)?;
|
||||
let return_type = self.parse_type_expr()?;
|
||||
let return_type = self.parse_type_expr_with_params(&type_params)?;
|
||||
self.expect(&Token::LBrace)?;
|
||||
let body = self.parse_block_body()?;
|
||||
self.expect(&Token::RBrace)?;
|
||||
Ok(Stmt::FnDef { name, decorators, params, return_type, body, span: start })
|
||||
Ok(Stmt::FnDef { name, decorators, type_params, params, return_type, body, span: start })
|
||||
}
|
||||
|
||||
/// Parse one or more `@decorator` annotations, then the `fn` definition.
|
||||
@@ -484,13 +497,18 @@ impl Parser {
|
||||
Ok(Stmt::ImplDef { protocol_name, type_name, methods, span: start })
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
fn parse_param_list(&mut self) -> Result<Vec<Param>, ParseError> {
|
||||
self.parse_param_list_with_type_params(&[])
|
||||
}
|
||||
|
||||
fn parse_param_list_with_type_params(&mut self, type_params: &[String]) -> Result<Vec<Param>, ParseError> {
|
||||
let mut params = Vec::new();
|
||||
while !matches!(self.peek(), Token::RParen | Token::Eof | Token::Pipe) {
|
||||
let span = self.peek_span();
|
||||
let (name, _) = self.expect_ident()?;
|
||||
self.expect(&Token::Colon)?;
|
||||
let type_ann = self.parse_type_expr()?;
|
||||
let type_ann = self.parse_type_expr_with_params(type_params)?;
|
||||
params.push(Param { name, type_ann, span });
|
||||
if !self.eat(&Token::Comma) {
|
||||
break;
|
||||
@@ -555,10 +573,14 @@ impl Parser {
|
||||
// ── Type expressions ──────────────────────────────────────────────────────
|
||||
|
||||
fn parse_type_expr(&mut self) -> Result<TypeExpr, ParseError> {
|
||||
self.parse_type_expr_with_params(&[])
|
||||
}
|
||||
|
||||
fn parse_type_expr_with_params(&mut self, type_params: &[String]) -> Result<TypeExpr, ParseError> {
|
||||
let span = self.peek_span();
|
||||
// Array type: [T]
|
||||
if self.eat(&Token::LBracket) {
|
||||
let inner = self.parse_type_expr()?;
|
||||
let inner = self.parse_type_expr_with_params(type_params)?;
|
||||
self.expect(&Token::RBracket)?;
|
||||
let mut te = TypeExpr::Array(Box::new(inner));
|
||||
// Optional array: [T]?
|
||||
@@ -580,12 +602,12 @@ impl Parser {
|
||||
self.expect(&Token::LParen)?;
|
||||
let mut params = Vec::new();
|
||||
while !matches!(self.peek(), Token::RParen | Token::Eof) {
|
||||
params.push(self.parse_type_expr()?);
|
||||
params.push(self.parse_type_expr_with_params(type_params)?);
|
||||
if !self.eat(&Token::Comma) { break; }
|
||||
}
|
||||
self.expect(&Token::RParen)?;
|
||||
self.expect(&Token::Arrow)?;
|
||||
let ret = self.parse_type_expr()?;
|
||||
let ret = self.parse_type_expr_with_params(type_params)?;
|
||||
return Ok(TypeExpr::Fn { params, return_type: Box::new(ret) });
|
||||
}
|
||||
// Result<T, E> — built-in generic result type
|
||||
@@ -612,6 +634,10 @@ impl Parser {
|
||||
}
|
||||
return Ok(te);
|
||||
}
|
||||
// If the name is in the current generic type params list, emit TypeParam
|
||||
if type_params.contains(&name) {
|
||||
return Ok(TypeExpr::TypeParam(name));
|
||||
}
|
||||
// Named type with optional ? suffix
|
||||
let mut te = TypeExpr::Named(name);
|
||||
if self.eat(&Token::QuestionMark) {
|
||||
@@ -881,7 +907,7 @@ impl Parser {
|
||||
Ok(Expr::If { cond: Box::new(cond), then: Box::new(then), else_ })
|
||||
}
|
||||
|
||||
// Identifier — could be plain name or path (Foo::Bar)
|
||||
// Identifier — could be plain name, path (Foo::Bar), or struct literal (Foo { ... })
|
||||
Token::Ident(name) => {
|
||||
self.advance();
|
||||
// Check for path: Foo::Bar or Foo::Bar::Baz
|
||||
@@ -892,6 +918,21 @@ impl Parser {
|
||||
segments.push(seg);
|
||||
}
|
||||
Ok(Expr::Path { segments })
|
||||
} else if matches!(self.peek(), Token::LBrace)
|
||||
&& name.chars().next().map(|c| c.is_uppercase()).unwrap_or(false)
|
||||
{
|
||||
// Struct literal: TypeName { field: expr, ... }
|
||||
self.advance(); // consume `{`
|
||||
let mut fields = Vec::new();
|
||||
while !matches!(self.peek(), Token::RBrace | Token::Eof) {
|
||||
let (field_name, _) = self.expect_ident()?;
|
||||
self.expect(&Token::Colon)?;
|
||||
let field_expr = self.parse_expr()?;
|
||||
fields.push((field_name, field_expr));
|
||||
if !self.eat(&Token::Comma) { break; }
|
||||
}
|
||||
self.expect(&Token::RBrace)?;
|
||||
Ok(Expr::StructLit { type_name: name, fields, span })
|
||||
} else {
|
||||
Ok(Expr::Ident(name))
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user