use super::lexer::{
Token, Keyword, TimeUnit, TokenWithLocation, SourceLocation, SourceMap,
};
use super::ast::*;
use crate::types::Duration;
use std::collections::HashMap;
pub struct Parser {
tokens: Vec<TokenWithLocation>,
source_map: Option<SourceMap>,
current: usize,
errors: Vec<ParseError>,
recovery_points: Vec<usize>,
}
#[derive(Debug, Clone)]
pub struct ParseError {
pub message: String,
pub location: Option<SourceLocation>,
pub token_index: usize,
pub expected: Option<String>,
pub found: String,
pub context: String,
}
impl std::fmt::Display for ParseError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.message)?;
if let Some(expected) = &self.expected {
write!(f, " (expected: {}, found: {})", expected, self.found)?;
}
if !self.context.is_empty() {
write!(f, " in {}", self.context)?;
}
Ok(())
}
}
impl std::error::Error for ParseError {}
impl ParseError {
pub fn format_with_source(&self, source: &str, tokens: &[Token]) -> String {
let mut line = 1;
let mut col = 1;
for (i, ch) in source.chars().enumerate() {
if i >= self.token_index {
break;
}
if ch == '\n' {
line += 1;
col = 1;
} else {
col += 1;
}
}
let lines: Vec<&str> = source.lines().collect();
let error_line = if line > 0 && line <= lines.len() {
lines[line - 1]
} else {
""
};
let mut result = format!("Error at line {}, column {}:\n", line, col);
result.push_str(&format!(" {}\n", error_line));
result.push_str(&format!(" {}^\n", " ".repeat(col - 1)));
let token_info = if self.token_index < tokens.len() {
format!("{:?}", tokens[self.token_index])
} else {
"<EOF>".to_string()
};
if let Some(expected) = &self.expected {
result
.push_str(
&format!("Expected {}, found token {}\n", expected, token_info),
);
} else {
result.push_str(&format!("{}\n", self.message));
}
if !self.context.is_empty() {
result.push_str(&format!("Context: {}\n", self.context));
}
result
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
#[allow(dead_code)]
enum Precedence {
Lowest = 0,
Pipeline = 1,
Logical = 2,
Equality = 3,
Comparison = 4,
Addition = 5,
Multiplication = 6,
Unary = 7,
Call = 8,
Index = 9,
Highest = 10,
}
impl Parser {
pub fn new(tokens: Vec<Token>) -> Self {
let tokens_with_location = tokens
.into_iter()
.enumerate()
.map(|(i, token)| {
TokenWithLocation {
token,
location: SourceLocation {
line: 1,
column: i + 1,
position: i,
},
}
})
.collect();
Parser {
tokens: tokens_with_location,
source_map: None,
current: 0,
errors: Vec::new(),
recovery_points: Vec::new(),
}
}
pub fn new_enhanced(tokens: Vec<TokenWithLocation>) -> Self {
Parser {
tokens,
source_map: None,
current: 0,
errors: Vec::new(),
recovery_points: Vec::new(),
}
}
pub fn new_with_source_map(source_map: SourceMap) -> Self {
let tokens = source_map.tokens.clone();
Parser {
tokens,
source_map: Some(source_map),
current: 0,
errors: Vec::new(),
recovery_points: Vec::new(),
}
}
fn add_error(&mut self, message: String, expected: Option<String>) {
let error = ParseError {
message,
location: self.current_location(),
token_index: self.current,
expected,
found: format!("{:?}", self.current_token()),
context: self.get_enhanced_context(),
};
self.errors.push(error);
}
fn get_context(&self) -> String {
if self.recovery_points.is_empty() {
"top-level".to_string()
} else {
match self.recovery_points.last() {
Some(_) => "inside declaration".to_string(),
None => "unknown".to_string(),
}
}
}
fn get_enhanced_context(&self) -> String {
let basic_context = self.get_context();
if let (Some(source_map), Some(location)) = (
&self.source_map,
&self.current_location(),
) {
let source_context = source_map.get_context(location, 2);
format!("{} - Source context:\n{}", basic_context, source_context)
} else {
basic_context
}
}
fn recover_to_next_declaration(&mut self) {
while self.current_token() != &Token::Eof {
match self.current_token() {
Token::Keyword(k) => {
match k {
Keyword::Agent
| Keyword::Workflow
| Keyword::Memory
| Keyword::Context
| Keyword::Crew
| Keyword::Project
| Keyword::Pipeline
| Keyword::Load => {
break;
}
_ => {}
}
}
_ => {}
}
self.advance();
}
}
#[allow(dead_code)]
fn recover_to_closing_brace(&mut self) {
let mut brace_depth = 1;
while self.current_token() != &Token::Eof && brace_depth > 0 {
match self.current_token() {
Token::LeftBrace => brace_depth += 1,
Token::RightBrace => brace_depth -= 1,
_ => {}
}
if brace_depth > 0 {
self.advance();
}
}
}
fn current_token(&self) -> &Token {
self.tokens
.get(self.current)
.map(|token_with_loc| &token_with_loc.token)
.unwrap_or(&Token::Eof)
}
fn current_location(&self) -> Option<SourceLocation> {
self.tokens
.get(self.current)
.map(|token_with_loc| token_with_loc.location.clone())
}
fn peek_token(&self) -> &Token {
self.tokens
.get(self.current + 1)
.map(|token_with_loc| &token_with_loc.token)
.unwrap_or(&Token::Eof)
}
fn parse_enhanced_expression(&mut self) -> Result<Expression, String> {
let next_token = self.peek_token().clone();
match (self.current_token().clone(), &next_token) {
(Token::Identifier(name), Token::Assign) => {
self.advance();
Ok(Expression::Identifier(name))
}
(Token::LeftParen, _) => {
self.advance();
let expr = self.parse_enhanced_expression()?;
self.expect(Token::RightParen)?;
Ok(expr)
}
(Token::LeftBracket, _) => {
self.advance();
let mut elements = Vec::new();
while self.current_token() != &Token::RightBracket
&& self.current_token() != &Token::Eof
{
self.skip_newlines();
if self.current_token() == &Token::RightBracket {
break;
}
elements.push(self.parse_enhanced_expression()?);
self.skip_newlines();
if self.current_token() == &Token::Comma {
self.advance();
}
}
self.expect(Token::RightBracket)?;
Ok(Expression::Array(elements))
}
_ => self.parse_primary_expression(),
}
}
fn advance(&mut self) -> Token {
let token = self.current_token().clone();
if self.current < self.tokens.len() {
self.current += 1;
}
token
}
fn expect(&mut self, expected: Token) -> Result<(), String> {
let token = self.current_token().clone();
if token == expected {
self.advance();
Ok(())
} else {
Err(format!("Expected {:?}, found {:?}", expected, token))
}
}
fn expect_identifier(&mut self) -> Result<String, String> {
match self.current_token() {
Token::Identifier(name) => {
let name = name.clone();
self.advance();
Ok(name)
}
Token::String(name) => {
let name = name.clone();
self.advance();
Ok(name)
}
_ => Err(format!("Expected identifier, found {:?}", self.current_token())),
}
}
fn skip_newlines(&mut self) {
while self.current_token() == &Token::Newline {
self.advance();
}
}
pub fn parse(&mut self) -> Result<HelixAst, String> {
let mut ast = HelixAst::new();
while self.current_token() != &Token::Eof {
self.skip_newlines();
if let Token::Keyword(keyword) = self.current_token().clone() {
self.recovery_points.push(self.current);
match self.parse_declaration(keyword.clone()) {
Ok(decl) => {
ast.add_declaration(decl);
self.recovery_points.pop();
}
Err(err) => {
self.add_error(
err.clone(),
Some(format!("valid {:?} declaration", keyword)),
);
self.recover_to_next_declaration();
self.recovery_points.pop();
}
}
} else {
match self.current_token() {
Token::Eof => break,
_ => {
self.add_error(
format!("Unexpected token: {:?}", self.current_token()),
Some("declaration keyword".to_string()),
);
self.recover_to_next_declaration();
}
}
}
self.skip_newlines();
}
if !self.errors.is_empty() {
let error_summary = self
.errors
.iter()
.map(|e| format!("{} at token {}", e.message, e.token_index))
.collect::<Vec<_>>()
.join("; ");
Err(format!("Parse errors: {}", error_summary))
} else {
Ok(ast)
}
}
fn parse_declaration(&mut self, keyword: Keyword) -> Result<Declaration, String> {
match keyword {
Keyword::Project => {
self.advance();
let name = self.expect_identifier()?;
self.expect(Token::LeftBrace)?;
let properties = self.parse_properties()?;
self.expect(Token::RightBrace)?;
Ok(Declaration::Project(ProjectDecl { name, properties }))
}
Keyword::Agent => {
self.advance();
let name = self.expect_identifier()?;
self.expect(Token::LeftBrace)?;
let mut properties = HashMap::new();
let mut capabilities = None;
let mut backstory = None;
let tools = None;
while self.current_token() != &Token::RightBrace {
self.skip_newlines();
match self.current_token() {
Token::Keyword(Keyword::Capabilities) => {
self.advance();
capabilities = Some(self.parse_string_array()?);
}
Token::Keyword(Keyword::Backstory) => {
self.advance();
backstory = Some(self.parse_backstory_block()?);
}
Token::Identifier(key) => {
let key = key.clone();
self.advance();
self.expect(Token::Assign)?;
let value = self.parse_expression()?;
properties.insert(key, value);
}
Token::Keyword(keyword) => {
match keyword {
Keyword::Capabilities | Keyword::Backstory => {
return Err(
format!(
"Unexpected token in agent: {:?}", self.current_token()
),
);
}
_ => {
let key = format!("{:?}", keyword).to_lowercase();
self.advance();
self.expect(Token::Assign)?;
let value = self.parse_expression()?;
properties.insert(key, value);
}
}
}
Token::RightBrace => break,
_ => {
return Err(
format!(
"Unexpected token in agent: {:?}", self.current_token()
),
);
}
}
self.skip_newlines();
}
self.expect(Token::RightBrace)?;
Ok(
Declaration::Agent(AgentDecl {
name,
properties,
capabilities,
backstory,
tools,
}),
)
}
Keyword::Workflow => {
self.advance();
let name = self.expect_identifier()?;
self.expect(Token::LeftBrace)?;
let mut trigger = None;
let mut steps = Vec::new();
let mut pipeline = None;
let mut properties = HashMap::new();
while self.current_token() != &Token::RightBrace {
self.skip_newlines();
match self.current_token() {
Token::Keyword(Keyword::Trigger) => {
self.advance();
self.expect(Token::Assign)?;
trigger = Some(self.parse_trigger_config()?);
}
Token::Keyword(Keyword::Step) => {
steps.push(self.parse_step()?);
}
Token::Keyword(Keyword::Pipeline) => {
self.advance();
pipeline = Some(self.parse_pipeline_block()?);
}
Token::Keyword(Keyword::Timeout) => {
self.advance();
self.expect(Token::Assign)?;
let timeout_value = self.parse_expression()?;
properties.insert("timeout".to_string(), timeout_value);
}
Token::Identifier(key) => {
let key = key.clone();
self.advance();
self.expect(Token::Assign)?;
let value = self.parse_expression()?;
properties.insert(key, value);
}
Token::RightBrace => break,
_ => {
return Err(
format!(
"Unexpected token in workflow: {:?}", self.current_token()
),
);
}
}
self.skip_newlines();
}
self.expect(Token::RightBrace)?;
Ok(
Declaration::Workflow(WorkflowDecl {
name,
trigger,
steps,
pipeline,
properties,
}),
)
}
Keyword::Memory => {
self.advance();
self.expect(Token::LeftBrace)?;
let mut provider = String::new();
let mut connection = String::new();
let mut embeddings = None;
let mut properties = HashMap::new();
while self.current_token() != &Token::RightBrace {
self.skip_newlines();
match self.current_token() {
Token::Keyword(Keyword::Embeddings) => {
self.advance();
embeddings = Some(self.parse_embeddings_block()?);
}
Token::Identifier(key) => {
let key = key.clone();
self.advance();
self.expect(Token::Assign)?;
let value = self.parse_expression()?;
match key.as_str() {
"provider" => {
provider = value.as_string().unwrap_or_default();
}
"connection" => {
connection = value.as_string().unwrap_or_default();
}
_ => {
properties.insert(key, value);
}
}
}
Token::RightBrace => break,
_ => {
return Err(
format!(
"Unexpected token in memory: {:?}", self.current_token()
),
);
}
}
self.skip_newlines();
}
self.expect(Token::RightBrace)?;
Ok(
Declaration::Memory(MemoryDecl {
provider,
connection,
embeddings,
properties,
}),
)
}
Keyword::Context => {
self.advance();
let name = self.expect_identifier()?;
self.expect(Token::LeftBrace)?;
let mut environment = String::new();
let mut secrets = None;
let mut variables = None;
let mut properties = HashMap::new();
while self.current_token() != &Token::RightBrace {
self.skip_newlines();
match self.current_token() {
Token::Keyword(Keyword::Secrets) => {
self.advance();
secrets = Some(self.parse_secrets_block()?);
}
Token::Keyword(Keyword::Variables) => {
self.advance();
variables = Some(self.parse_variables_block()?);
}
Token::Identifier(key) => {
let key = key.clone();
self.advance();
self.expect(Token::Assign)?;
let value = self.parse_expression()?;
if key == "environment" {
environment = value.as_string().unwrap_or_default();
} else {
properties.insert(key, value);
}
}
Token::RightBrace => break,
_ => {
return Err(
format!(
"Unexpected token in context: {:?}", self.current_token()
),
);
}
}
self.skip_newlines();
}
self.expect(Token::RightBrace)?;
Ok(
Declaration::Context(ContextDecl {
name,
environment,
secrets,
variables,
properties,
}),
)
}
Keyword::Crew => {
self.advance();
let name = self.expect_identifier()?;
self.expect(Token::LeftBrace)?;
let mut agents = Vec::new();
let mut process_type = None;
let mut properties = HashMap::new();
while self.current_token() != &Token::RightBrace {
self.skip_newlines();
match self.current_token() {
Token::Identifier(key) => {
let key = key.clone();
self.advance();
if key == "agents" {
agents = self.parse_string_array()?;
} else {
self.expect(Token::Assign)?;
let value = self.parse_expression()?;
if key == "process" {
process_type = value.as_string();
} else {
properties.insert(key, value);
}
}
}
Token::RightBrace => break,
_ => {
return Err(
format!(
"Unexpected token in crew: {:?}", self.current_token()
),
);
}
}
self.skip_newlines();
}
self.expect(Token::RightBrace)?;
Ok(
Declaration::Crew(CrewDecl {
name,
agents,
process_type,
properties,
}),
)
}
Keyword::Pipeline => {
self.advance();
self.expect(Token::LeftBrace)?;
let pipeline = self.parse_pipeline_block()?;
self.expect(Token::RightBrace)?;
Ok(Declaration::Pipeline(pipeline))
}
Keyword::Load => {
self.advance();
let file_name = self.expect_identifier()?;
self.expect(Token::LeftBrace)?;
let properties = self.parse_properties()?;
self.expect(Token::RightBrace)?;
Ok(Declaration::Load(LoadDecl { file_name, properties }))
}
_ => Err(format!("Unexpected keyword: {:?}", keyword)),
}
}
fn parse_step(&mut self) -> Result<StepDecl, String> {
self.advance();
let name = self.expect_identifier()?;
self.expect(Token::LeftBrace)?;
let mut agent = None;
let mut crew = None;
let mut task = None;
let mut properties = HashMap::new();
while self.current_token() != &Token::RightBrace {
self.skip_newlines();
match self.current_token() {
Token::Keyword(Keyword::Timeout) => {
self.advance();
self.expect(Token::Assign)?;
let timeout_value = self.parse_expression()?;
properties.insert("timeout".to_string(), timeout_value);
}
Token::Identifier(key) => {
let key = key.clone();
self.advance();
match key.as_str() {
"agent" => {
self.expect(Token::Assign)?;
agent = self.parse_expression()?.as_string();
}
"crew" => {
self.expect(Token::Assign)?;
if self.current_token() == &Token::LeftBracket {
crew = Some(self.parse_string_array()?);
}
}
"task" => {
self.expect(Token::Assign)?;
task = self.parse_expression()?.as_string();
}
"retry" => {
let retry_config = self.parse_retry_block()?;
properties
.insert(
"retry".to_string(),
Expression::Object(retry_config),
);
}
_ => {
self.expect(Token::Assign)?;
let value = self.parse_expression()?;
properties.insert(key, value);
}
}
}
Token::Keyword(keyword) => {
let key = format!("{:?}", keyword).to_lowercase();
self.advance();
self.expect(Token::Assign)?;
match key.as_str() {
"agent" => {
agent = self.parse_expression()?.as_string();
}
_ => {
let value = self.parse_expression()?;
properties.insert(key, value);
}
}
}
Token::RightBrace => break,
_ => {
return Err(
format!("Unexpected token in step: {:?}", self.current_token()),
);
}
}
self.skip_newlines();
}
self.expect(Token::RightBrace)?;
Ok(StepDecl {
name,
agent,
crew,
task,
properties,
})
}
fn parse_retry_block(&mut self) -> Result<HashMap<String, Expression>, String> {
self.expect(Token::LeftBrace)?;
let mut retry_config = HashMap::new();
while self.current_token() != &Token::RightBrace {
self.skip_newlines();
if self.current_token() == &Token::RightBrace {
break;
}
let key = self.expect_identifier()?;
if self.peek_token() != &Token::Assign
&& self.current_token() != &Token::Assign
{
return Err(
format!(
"Expected '=' after property key '{}', found {:?}", key, self
.current_token()
),
);
}
self.expect(Token::Assign)?;
let value = self.parse_expression()?;
retry_config.insert(key, value);
self.skip_newlines();
}
self.expect(Token::RightBrace)?;
Ok(retry_config)
}
fn parse_trigger_config(&mut self) -> Result<Expression, String> {
if self.current_token() == &Token::LeftBrace {
self.advance();
let trigger_obj = self.parse_object()?;
Ok(Expression::Object(trigger_obj))
} else {
self.parse_expression()
}
}
fn parse_expression(&mut self) -> Result<Expression, String> {
match self.current_token() {
Token::LeftParen | Token::LeftBracket => self.parse_enhanced_expression(),
_ => self.parse_expression_with_precedence(Precedence::Lowest),
}
}
fn parse_expression_with_precedence(
&mut self,
min_precedence: Precedence,
) -> Result<Expression, String> {
let mut left = self.parse_primary_expression()?;
while !self.is_at_end() {
let precedence = self.get_token_precedence(self.current_token());
if precedence < min_precedence {
break;
}
match self.current_token() {
Token::Arrow => {
self.advance();
let mut pipeline = vec![];
if let Expression::Identifier(id) = left {
pipeline.push(id);
} else if let Expression::Pipeline(mut p) = left {
pipeline.append(&mut p);
} else {
return Err(format!("Invalid left side of pipeline: {:?}", left));
}
let right = self
.parse_expression_with_precedence(Precedence::Pipeline)?;
if let Expression::Identifier(id) = right {
pipeline.push(id);
} else if let Expression::Pipeline(mut p) = right {
pipeline.append(&mut p);
} else {
return Err(
format!("Invalid right side of pipeline: {:?}", right),
);
}
left = Expression::Pipeline(pipeline);
}
_ => {
break;
}
}
}
Ok(left)
}
fn parse_primary_expression(&mut self) -> Result<Expression, String> {
match self.current_token() {
Token::String(s) => {
let s = s.clone();
self.advance();
Ok(Expression::String(s))
}
Token::Number(n) => {
let n = *n;
self.advance();
Ok(Expression::Number(n))
}
Token::Bool(b) => {
let b = *b;
self.advance();
Ok(Expression::Bool(b))
}
Token::Duration(value, unit) => {
let duration = Duration {
value: *value,
unit: match unit {
TimeUnit::Seconds => crate::types::TimeUnit::Seconds,
TimeUnit::Minutes => crate::types::TimeUnit::Minutes,
TimeUnit::Hours => crate::types::TimeUnit::Hours,
TimeUnit::Days => crate::types::TimeUnit::Days,
},
};
self.advance();
Ok(Expression::Duration(duration))
}
Token::Variable(v) => {
let v = v.clone();
self.advance();
Ok(Expression::Variable(v))
}
Token::Reference(r) => {
let r = r.clone();
self.advance();
if self.current_token() == &Token::LeftBracket {
self.advance();
let key = self.expect_identifier()?;
self.expect(Token::RightBracket)?;
Ok(Expression::IndexedReference(r, key))
} else {
Ok(Expression::Reference(r))
}
}
Token::Identifier(i) => {
let i = i.clone();
self.advance();
Ok(Expression::Identifier(i))
}
Token::LeftBracket => {
self.advance();
let array = self.parse_array()?;
Ok(Expression::Array(array))
}
Token::LeftBrace => {
self.advance();
let object = self.parse_object()?;
Ok(Expression::Object(object))
}
Token::LeftParen => {
self.advance();
let expr = self.parse_expression()?;
self.expect(Token::RightParen)?;
Ok(expr)
}
_ => {
Err(
format!("Unexpected token in expression: {:?}", self.current_token()),
)
}
}
}
fn get_token_precedence(&self, token: &Token) -> Precedence {
match token {
Token::Arrow => Precedence::Pipeline,
_ => Precedence::Lowest,
}
}
fn is_at_end(&self) -> bool {
self.current_token() == &Token::Eof
}
fn parse_array(&mut self) -> Result<Vec<Expression>, String> {
let mut elements = Vec::new();
while self.current_token() != &Token::RightBracket {
self.skip_newlines();
if self.current_token() == &Token::RightBracket {
break;
}
elements.push(self.parse_expression()?);
if self.current_token() == &Token::Comma {
self.advance();
}
self.skip_newlines();
}
self.expect(Token::RightBracket)?;
Ok(elements)
}
fn parse_object(&mut self) -> Result<HashMap<String, Expression>, String> {
let mut object = HashMap::new();
while self.current_token() != &Token::RightBrace {
self.skip_newlines();
if self.current_token() == &Token::RightBrace {
break;
}
let key = self.expect_identifier()?;
if self.peek_token() != &Token::Assign
&& self.current_token() != &Token::Assign
{
return Err(
format!(
"Expected '=' after property key '{}', found {:?}", key, self
.current_token()
),
);
}
self.expect(Token::Assign)?;
let value = self.parse_expression()?;
object.insert(key, value);
if self.current_token() == &Token::Comma {
self.advance();
}
self.skip_newlines();
}
self.expect(Token::RightBrace)?;
Ok(object)
}
fn parse_string_array(&mut self) -> Result<Vec<String>, String> {
self.expect(Token::LeftBracket)?;
let mut items = Vec::new();
while self.current_token() != &Token::RightBracket {
self.skip_newlines();
if self.current_token() == &Token::RightBracket {
break;
}
items.push(self.expect_identifier()?);
if self.current_token() == &Token::Comma {
self.advance();
}
self.skip_newlines();
}
self.expect(Token::RightBracket)?;
Ok(items)
}
fn parse_properties(&mut self) -> Result<HashMap<String, Expression>, String> {
let mut properties = HashMap::new();
while self.current_token() != &Token::RightBrace {
self.skip_newlines();
if self.current_token() == &Token::RightBrace {
break;
}
let key = self.expect_identifier()?;
if self.peek_token() != &Token::Assign
&& self.current_token() != &Token::Assign
{
return Err(
format!(
"Expected '=' after property key '{}', found {:?}", key, self
.current_token()
),
);
}
self.expect(Token::Assign)?;
let value = self.parse_expression()?;
properties.insert(key, value);
self.skip_newlines();
}
Ok(properties)
}
fn parse_backstory_block(&mut self) -> Result<BackstoryBlock, String> {
self.expect(Token::LeftBrace)?;
let mut lines = Vec::new();
while self.current_token() != &Token::RightBrace {
self.skip_newlines();
match self.current_token() {
Token::Identifier(text) | Token::String(text) => {
lines.push(text.clone());
self.advance();
}
Token::RightBrace => break,
_ => {
self.advance();
}
}
}
self.expect(Token::RightBrace)?;
Ok(BackstoryBlock { lines })
}
fn parse_pipeline_block(&mut self) -> Result<PipelineDecl, String> {
self.expect(Token::LeftBrace)?;
let mut flow = Vec::new();
while self.current_token() != &Token::RightBrace {
self.skip_newlines();
if let Token::Identifier(step) = self.current_token() {
flow.push(PipelineNode::Step(step.clone()));
self.advance();
if self.current_token() == &Token::Arrow {
self.advance();
}
} else if self.current_token() == &Token::RightBrace {
break;
} else {
self.advance();
}
self.skip_newlines();
}
self.expect(Token::RightBrace)?;
Ok(PipelineDecl { flow })
}
fn parse_embeddings_block(&mut self) -> Result<EmbeddingsDecl, String> {
self.expect(Token::LeftBrace)?;
let mut model = String::new();
let mut dimensions = 0;
let mut properties = HashMap::new();
while self.current_token() != &Token::RightBrace {
self.skip_newlines();
let key = self.expect_identifier()?;
if self.peek_token() != &Token::Assign
&& self.current_token() != &Token::Assign
{
return Err(
format!(
"Expected '=' after property key '{}', found {:?}", key, self
.current_token()
),
);
}
self.expect(Token::Assign)?;
let value = self.parse_expression()?;
match key.as_str() {
"model" => model = value.as_string().unwrap_or_default(),
"dimensions" => dimensions = value.as_number().unwrap_or(0.0) as u32,
_ => {
properties.insert(key, value);
}
}
self.skip_newlines();
}
self.expect(Token::RightBrace)?;
Ok(EmbeddingsDecl {
model,
dimensions,
properties,
})
}
fn parse_variables_block(&mut self) -> Result<HashMap<String, Expression>, String> {
self.expect(Token::LeftBrace)?;
let mut variables = HashMap::new();
while self.current_token() != &Token::RightBrace {
self.skip_newlines();
if self.current_token() == &Token::RightBrace {
break;
}
let key = match self.current_token().clone() {
Token::Identifier(id) => {
self.advance();
id.clone()
}
Token::Keyword(kw) => {
self.advance();
format!("{:?}", kw).to_lowercase()
}
_ => {
return Err(
format!(
"Expected identifier or keyword for variable name, found {:?}",
self.current_token()
),
);
}
};
self.expect(Token::Assign)?;
let value = self.parse_expression()?;
variables.insert(key, value);
self.skip_newlines();
}
self.expect(Token::RightBrace)?;
Ok(variables)
}
fn parse_secrets_block(&mut self) -> Result<HashMap<String, SecretRef>, String> {
self.expect(Token::LeftBrace)?;
let mut secrets = HashMap::new();
while self.current_token() != &Token::RightBrace {
self.skip_newlines();
let key = self.expect_identifier()?;
self.expect(Token::Assign)?;
let secret_ref = match self.current_token() {
Token::Variable(var) => {
let var = var.clone();
self.advance();
SecretRef::Environment(var)
}
Token::String(path) if path.starts_with("vault:") => {
let path = path.clone();
self.advance();
SecretRef::Vault(path.trim_start_matches("vault:").to_string())
}
Token::String(path) if path.starts_with("file:") => {
let path = path.clone();
self.advance();
SecretRef::File(path.trim_start_matches("file:").to_string())
}
_ => {
return Err(
format!("Invalid secret reference: {:?}", self.current_token()),
);
}
};
secrets.insert(key, secret_ref);
self.skip_newlines();
}
self.expect(Token::RightBrace)?;
Ok(secrets)
}
}
pub fn parse(tokens: Vec<Token>) -> Result<HelixAst, ParseError> {
let mut parser = Parser::new(tokens);
parser
.parse()
.map_err(|msg| ParseError {
message: msg,
location: None,
token_index: 0,
expected: None,
found: String::new(),
context: String::new(),
})
}