refactor: interpreter and type system improvements

- Parser and typechecker updates for new features
- Schema evolution refinements
- Type system enhancements

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-02-15 03:54:23 -05:00
parent 634f665b1b
commit c6d7f5cffb
5 changed files with 400 additions and 64 deletions

View File

@@ -479,8 +479,11 @@ impl Parser {
self.skip_newlines();
if self.check(TokenKind::Pipe) {
// Enum type - requires leading | for variants
// Enum type - with leading | for variants
(TypeDef::Enum(self.parse_variants()?), Vec::new())
} else if self.is_single_line_enum() {
// Single-line enum without leading pipe: type Status = Ok | Error
(TypeDef::Enum(self.parse_single_line_variants()?), Vec::new())
} else {
// Type alias - any type expression
(TypeDef::Alias(self.parse_type()?), Vec::new())
@@ -1253,6 +1256,85 @@ impl Parser {
Ok(migrations)
}
/// Check if we're looking at a single-line enum (no leading pipe)
/// e.g., type Status = Ok | Error
fn is_single_line_enum(&self) -> bool {
// Look for pattern: Ident (LParen|LBrace)? Pipe
// We need to peek ahead without consuming tokens
if !matches!(&self.peek().kind, TokenKind::Ident(_)) {
return false;
}
// Check if there's a | somewhere on this logical line
// This is a simple heuristic - look at the next few tokens
let mut pos = self.pos;
let mut found_ident = false;
let mut depth = 0;
while pos < self.tokens.len() {
match &self.tokens[pos].kind {
TokenKind::Ident(_) if !found_ident => found_ident = true,
TokenKind::LParen | TokenKind::LBrace => depth += 1,
TokenKind::RParen | TokenKind::RBrace => {
if depth > 0 {
depth -= 1;
}
}
TokenKind::Pipe if depth == 0 => return true,
TokenKind::Newline | TokenKind::Eof => return false,
_ => {}
}
pos += 1;
}
false
}
/// Parse single-line variants without leading pipe
fn parse_single_line_variants(&mut self) -> Result<Vec<Variant>, ParseError> {
let mut variants = Vec::new();
loop {
let start = self.current_span();
let name = self.parse_ident()?;
let fields = if self.check(TokenKind::LParen) {
// Tuple variant
self.advance();
let mut types = Vec::new();
while !self.check(TokenKind::RParen) {
types.push(self.parse_type()?);
if !self.check(TokenKind::RParen) {
self.expect(TokenKind::Comma)?;
}
}
self.expect(TokenKind::RParen)?;
VariantFields::Tuple(types)
} else if self.check(TokenKind::LBrace) {
// Record variant
self.advance();
self.skip_newlines();
let fields = self.parse_record_fields()?;
self.expect(TokenKind::RBrace)?;
VariantFields::Record(fields)
} else {
VariantFields::Unit
};
let span = start.merge(self.previous_span());
variants.push(Variant { name, fields, span });
// Check for more variants
if self.check(TokenKind::Pipe) {
self.advance();
self.skip_newlines();
} else {
break;
}
}
Ok(variants)
}
fn parse_variants(&mut self) -> Result<Vec<Variant>, ParseError> {
let mut variants = Vec::new();
@@ -1660,9 +1742,19 @@ impl Parser {
let then_branch = Box::new(self.parse_expr()?);
self.skip_newlines();
self.expect(TokenKind::Else)?;
self.skip_newlines();
let else_branch = Box::new(self.parse_expr()?);
// Else is optional - if missing, synthesize a Unit value
let else_branch = if self.check(TokenKind::Else) {
self.expect(TokenKind::Else)?;
self.skip_newlines();
Box::new(self.parse_expr()?)
} else {
// No else clause - use Unit as the else branch
Box::new(Expr::Literal(Literal {
kind: LiteralKind::Unit,
span: then_branch.span(),
}))
};
let span = start.merge(else_branch.span());
Ok(Expr::If {