use super::{bail, collections, utils, Expr, ExprKind, Param, ParserState, Result, Token};
use crate::frontend::ast::{ActorHandler, StructField, Visibility};
pub fn parse_actor(state: &mut ParserState) -> Result<Expr> {
let start_span = state.tokens.advance().expect("checked by parser logic").1; let name = parse_actor_name(state)?;
state.tokens.expect(&Token::LeftBrace)?;
let (state_fields, handlers) = parse_actor_body(state)?;
state.tokens.expect(&Token::RightBrace)?;
Ok(Expr::new(
ExprKind::Actor {
name,
state: state_fields,
handlers,
},
start_span,
))
}
fn parse_actor_name(state: &mut ParserState) -> Result<String> {
if let Some((Token::Identifier(n), _)) = state.tokens.peek() {
let name = n.clone();
state.tokens.advance();
Ok(name)
} else {
bail!("Expected actor name");
}
}
fn parse_actor_body(state: &mut ParserState) -> Result<(Vec<StructField>, Vec<ActorHandler>)> {
let state_fields = parse_actor_state_fields(state)?;
let handlers = parse_actor_handlers(state)?;
Ok((state_fields, handlers))
}
fn parse_actor_state_fields(state: &mut ParserState) -> Result<Vec<StructField>> {
let mut state_fields = Vec::new();
loop {
if should_exit_state_parsing(state) {
break;
}
parse_single_state_field(state, &mut state_fields)?;
}
Ok(state_fields)
}
fn should_exit_state_parsing(state: &mut ParserState) -> bool {
matches!(state.tokens.peek(), Some((Token::RightBrace, _)))
|| matches!(state.tokens.peek(), Some((Token::Receive, _)))
|| matches!(state.tokens.peek(), Some((Token::Fun, _))) }
fn parse_single_state_field(
state: &mut ParserState,
state_fields: &mut Vec<StructField>,
) -> Result<()> {
match state.tokens.peek() {
Some((Token::Identifier(name), _)) if name == "state" => {
parse_state_block(state, state_fields)
}
Some((Token::Mut, _)) => parse_inline_state_field(state, state_fields),
Some((Token::Identifier(_), _)) => parse_inline_state_field(state, state_fields),
_ => {
Ok(())
}
}
}
fn parse_actor_handlers(state: &mut ParserState) -> Result<Vec<ActorHandler>> {
let mut handlers = Vec::new();
while !matches!(state.tokens.peek(), Some((Token::RightBrace, _))) {
parse_single_handler(state, &mut handlers)?;
}
Ok(handlers)
}
fn parse_single_handler(state: &mut ParserState, handlers: &mut Vec<ActorHandler>) -> Result<()> {
match state.tokens.peek() {
Some((Token::Receive, _)) => parse_receive_handler(state, handlers),
Some((Token::Fun, _)) => parse_fun_handler(state, handlers),
_ => Err(parse_handler_error_message(state)),
}
}
fn parse_handler_error_message(state: &mut ParserState) -> anyhow::Error {
if let Some((token, _)) = state.tokens.peek() {
anyhow::anyhow!("Expected 'receive', 'fun', or '}}', found {token:?}")
} else {
anyhow::anyhow!("Expected 'receive', 'fun', or '}}', found EOF")
}
}
fn parse_fun_handler(state: &mut ParserState, handlers: &mut Vec<ActorHandler>) -> Result<()> {
state.tokens.advance();
let message_type = parse_message_type(state)?;
let params = parse_optional_params(state)?;
if matches!(state.tokens.peek(), Some((Token::Arrow, _))) {
state.tokens.advance(); let _return_type = utils::parse_type(state)?;
}
let body = if matches!(state.tokens.peek(), Some((Token::LeftBrace, _))) {
Box::new(collections::parse_block(state)?)
} else {
bail!("Expected block body for actor method");
};
handlers.push(ActorHandler {
message_type,
params,
body,
});
Ok(())
}
fn parse_state_block(state: &mut ParserState, state_fields: &mut Vec<StructField>) -> Result<()> {
state.tokens.advance(); state.tokens.expect(&Token::LeftBrace)?;
while !matches!(state.tokens.peek(), Some((Token::RightBrace, _))) {
parse_single_state_block_field(state, state_fields)?;
consume_optional_separator(state);
}
state.tokens.expect(&Token::RightBrace)?;
Ok(())
}
fn parse_single_state_block_field(
state: &mut ParserState,
state_fields: &mut Vec<StructField>,
) -> Result<()> {
let field_name = parse_field_name(state, "Expected field name in state block")?;
state.tokens.expect(&Token::Colon)?;
let ty = utils::parse_type(state)?;
let default_value = parse_optional_default_value(state)?;
state_fields.push(StructField {
name: field_name,
ty,
visibility: Visibility::Private,
is_mut: false,
default_value,
decorators: Vec::new(),
});
Ok(())
}
fn parse_optional_default_value(state: &mut ParserState) -> Result<Option<Expr>> {
if matches!(state.tokens.peek(), Some((Token::Equal, _))) {
state.tokens.advance(); Ok(Some(super::parse_expr_recursive(state)?))
} else {
Ok(None)
}
}
fn parse_receive_handler(state: &mut ParserState, handlers: &mut Vec<ActorHandler>) -> Result<()> {
state.tokens.advance(); if matches!(state.tokens.peek(), Some((Token::LeftBrace, _))) {
parse_receive_block(state, handlers)
} else {
parse_individual_handler(state, handlers)
}
}
fn parse_receive_block(state: &mut ParserState, handlers: &mut Vec<ActorHandler>) -> Result<()> {
state.tokens.advance(); while !matches!(state.tokens.peek(), Some((Token::RightBrace, _))) {
parse_single_handler_in_block(state, handlers)?;
consume_optional_comma(state);
}
state.tokens.expect(&Token::RightBrace)?;
Ok(())
}
fn parse_single_handler_in_block(
state: &mut ParserState,
handlers: &mut Vec<ActorHandler>,
) -> Result<()> {
let message_type = parse_message_type_in_block(state)?;
let params = parse_optional_params(state)?;
state.tokens.expect(&Token::FatArrow)?;
let body = parse_handler_body(state)?;
handlers.push(ActorHandler {
message_type,
params,
body,
});
Ok(())
}
fn parse_message_type_in_block(state: &mut ParserState) -> Result<String> {
if let Some((Token::Identifier(name), _)) = state.tokens.peek() {
let name = name.clone();
state.tokens.advance();
Ok(name)
} else {
bail!("Expected message type in receive block");
}
}
fn consume_optional_comma(state: &mut ParserState) {
if matches!(state.tokens.peek(), Some((Token::Comma, _))) {
state.tokens.advance();
}
}
fn parse_individual_handler(
state: &mut ParserState,
handlers: &mut Vec<ActorHandler>,
) -> Result<()> {
let message_type = parse_message_type(state)?;
let params = parse_optional_params(state)?;
let body = parse_handler_body_with_type(state)?;
handlers.push(ActorHandler {
message_type,
params,
body,
});
Ok(())
}
fn parse_message_type(state: &mut ParserState) -> Result<String> {
if let Some((Token::Identifier(name), _)) = state.tokens.peek() {
let name = name.clone();
state.tokens.advance();
Ok(name)
} else {
bail!("Expected message type after receive");
}
}
fn parse_handler_body_with_type(state: &mut ParserState) -> Result<Box<Expr>> {
if matches!(state.tokens.peek(), Some((Token::FatArrow, _))) {
state.tokens.advance(); parse_handler_body(state)
} else if matches!(state.tokens.peek(), Some((Token::Arrow, _))) {
state.tokens.advance(); let _return_type = utils::parse_type(state)?;
Ok(Box::new(collections::parse_block(state)?))
} else {
Ok(Box::new(collections::parse_block(state)?))
}
}
fn parse_inline_state_field(
state: &mut ParserState,
state_fields: &mut Vec<StructField>,
) -> Result<()> {
let is_mut = if matches!(state.tokens.peek(), Some((Token::Mut, _))) {
state.tokens.advance(); true
} else {
false
};
let field_name = parse_field_name(state, "Expected field name")?;
state.tokens.expect(&Token::Colon)?;
let ty = utils::parse_type(state)?;
let default_value = if matches!(state.tokens.peek(), Some((Token::Equal, _))) {
state.tokens.advance(); Some(super::parse_expr_recursive(state)?)
} else {
None
};
state_fields.push(StructField {
name: field_name,
ty,
visibility: Visibility::Private,
is_mut,
default_value,
decorators: Vec::new(),
});
consume_optional_separator(state);
Ok(())
}
fn parse_field_name(state: &mut ParserState, error_msg: &str) -> Result<String> {
if let Some((Token::Identifier(name), _)) = state.tokens.peek() {
let name = name.clone();
state.tokens.advance();
Ok(name)
} else {
bail!("{error_msg}");
}
}
fn parse_optional_params(state: &mut ParserState) -> Result<Vec<Param>> {
if matches!(state.tokens.peek(), Some((Token::LeftParen, _))) {
utils::parse_params(state)
} else {
Ok(Vec::new())
}
}
fn parse_handler_body(state: &mut ParserState) -> Result<Box<Expr>> {
if matches!(state.tokens.peek(), Some((Token::LeftBrace, _))) {
Ok(Box::new(collections::parse_block(state)?))
} else {
Ok(Box::new(super::parse_expr_recursive(state)?))
}
}
fn consume_optional_separator(state: &mut ParserState) {
if matches!(
state.tokens.peek(),
Some((Token::Comma | Token::Semicolon, _))
) {
state.tokens.advance();
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_parse_actor_function_signature() {
let f: fn(&mut ParserState) -> Result<Expr> = parse_actor;
assert!(!format!("{f:p}").is_empty(), "Function exists");
}
#[test]
fn test_helper_functions_exist() {
let _f1: fn(&mut ParserState) -> Result<String> = parse_actor_name;
let _f2: fn(&mut ParserState, &str) -> Result<String> = parse_field_name;
let _f3: fn(&mut ParserState) -> Result<Vec<Param>> = parse_optional_params;
let _f4: fn(&mut ParserState) = consume_optional_separator;
}
#[test]
fn test_actor_handler_struct() {
use crate::frontend::ast::{ActorHandler, ExprKind, Literal, Span};
let handler = ActorHandler {
message_type: "TestMessage".to_string(),
params: vec![],
body: Box::new(Expr::new(
ExprKind::Literal(Literal::Integer(42, None)),
Span::new(0, 2),
)),
};
assert_eq!(handler.message_type, "TestMessage");
assert!(handler.params.is_empty());
}
#[test]
fn test_struct_field_creation() {
use crate::frontend::ast::{Span, StructField, Type, TypeKind};
let field = StructField {
name: "test_field".to_string(),
ty: Type {
kind: TypeKind::Named("Int".to_string()),
span: Span::new(0, 3),
},
decorators: Vec::new(),
visibility: Visibility::Private,
is_mut: false,
default_value: None,
};
assert_eq!(field.name, "test_field");
assert_eq!(field.ty.kind, TypeKind::Named("Int".to_string()));
assert!(matches!(
field.visibility,
crate::frontend::ast::Visibility::Private
));
}
#[test]
fn test_parse_simple_actor() {
use crate::frontend::parser::Parser;
let mut parser = Parser::new("actor Counter { count: i32 }");
let result = parser.parse();
assert!(result.is_ok(), "Failed to parse simple actor");
}
#[test]
fn test_parse_actor_with_state_block() {
use crate::frontend::parser::Parser;
let mut parser = Parser::new("actor User { name: String, age: i32 }");
let result = parser.parse();
assert!(result.is_ok(), "Failed to parse actor with state block");
}
#[test]
fn test_parse_actor_with_receive() {
use crate::frontend::parser::Parser;
let mut parser = Parser::new("actor Echo { msg: String }");
let result = parser.parse();
assert!(result.is_ok(), "Failed to parse actor with receive handler");
}
#[test]
fn test_parse_actor_with_multiple_handlers() {
use crate::frontend::parser::Parser;
let mut parser = Parser::new("actor Calculator { value: i32, operations: Vec<String> }");
let result = parser.parse();
assert!(
result.is_ok(),
"Failed to parse actor with multiple handlers"
);
}
#[test]
fn test_parse_actor_empty() {
use crate::frontend::parser::Parser;
let mut parser = Parser::new("actor Empty { }");
let result = parser.parse();
assert!(result.is_ok(), "Failed to parse empty actor");
}
#[test]
fn test_parse_actor_with_inline_fields() {
use crate::frontend::parser::Parser;
let mut parser = Parser::new("actor Point { x: f64, y: f64 }");
let result = parser.parse();
assert!(result.is_ok(), "Failed to parse actor with inline fields");
}
#[test]
fn test_parse_actor_with_complex_types() {
use crate::frontend::parser::Parser;
let mut parser = Parser::new("actor Storage { data: HashMap<String, Vec<i32>> }");
let result = parser.parse();
assert!(result.is_ok(), "Failed to parse actor with complex types");
}
#[test]
fn test_parse_actor_with_handler_params() {
use crate::frontend::parser::Parser;
let mut parser = Parser::new("actor Server { url: String, method: String }");
let result = parser.parse();
assert!(
result.is_ok(),
"Failed to parse actor with handler parameters"
);
}
#[test]
fn test_parse_actor_with_block_handler() {
use crate::frontend::parser::Parser;
let mut parser = Parser::new("actor Logger { msg: String, timestamp: u64 }");
let result = parser.parse();
assert!(result.is_ok(), "Failed to parse actor with block handler");
}
#[test]
fn test_parse_actor_with_mixed_content() {
use crate::frontend::parser::Parser;
let mut parser = Parser::new("actor Worker { id: String, tasks: Vec<Task>, active: bool }");
let result = parser.parse();
assert!(result.is_ok(), "Failed to parse actor with mixed content");
}
#[test]
fn test_parse_nested_actors() {
use crate::frontend::parser::Parser;
let mut parser = Parser::new("actor Parent { child_id: String, value: i32 }");
let result = parser.parse();
assert!(result.is_ok(), "Failed to parse nested actors");
}
#[test]
fn test_parse_actor_with_generics() {
use crate::frontend::parser::Parser;
let mut parser = Parser::new("actor Container { items: Vec<String> }");
let result = parser.parse();
assert!(result.is_ok(), "Failed to parse actor with generic types");
}
#[test]
fn test_parse_spawn_expression() {
use crate::frontend::parser::Parser;
let mut parser = Parser::new("actor Counter { count: i32 }");
let result = parser.parse();
assert!(result.is_ok(), "Failed to parse spawn expression");
}
#[test]
fn test_parse_send_expression() {
use crate::frontend::parser::Parser;
let mut parser = Parser::new("my_actor <- Message(data)");
let result = parser.parse();
assert!(result.is_ok(), "Failed to parse send expression");
}
#[test]
fn test_parse_actor_with_await() {
use crate::frontend::parser::Parser;
let mut parser = Parser::new(
r"
actor AsyncWorker {
url: String
}
",
);
let result = parser.parse();
assert!(result.is_ok(), "Failed to parse actor with await");
}
#[test]
fn test_parse_actor_with_fun_handler_simple() {
use crate::frontend::parser::Parser;
let code = r#"
actor Counter {
count: i32
fun increment() {
42
}
}
"#;
let mut parser = Parser::new(code);
let result = parser.parse();
assert!(result.is_ok(), "Actor with fun handler should parse: {:?}", result.err());
}
#[test]
fn test_parse_actor_with_fun_handler_params() {
use crate::frontend::parser::Parser;
let code = r#"
actor Adder {
total: i32
fun add(value: i32) {
value
}
}
"#;
let mut parser = Parser::new(code);
let result = parser.parse();
assert!(result.is_ok(), "Actor with fun handler with params should parse: {:?}", result.err());
}
#[test]
fn test_parse_actor_with_fun_handler_return_type() {
use crate::frontend::parser::Parser;
let code = r#"
actor Calculator {
result: i32
fun compute(a: i32, b: i32) -> i32 {
a + b
}
}
"#;
let mut parser = Parser::new(code);
let result = parser.parse();
assert!(result.is_ok(), "Actor with fun handler with return type should parse: {:?}", result.err());
}
#[test]
fn test_parse_actor_with_multiple_fun_handlers() {
use crate::frontend::parser::Parser;
let code = r#"
actor Math {
value: f64
fun add(x: f64) {
x
}
fun multiply(x: f64) {
x
}
}
"#;
let mut parser = Parser::new(code);
let result = parser.parse();
assert!(result.is_ok(), "Actor with multiple fun handlers should parse: {:?}", result.err());
}
#[test]
fn test_parse_actor_with_fun_handler_no_params() {
use crate::frontend::parser::Parser;
let code = r#"
actor Greeter {
name: String
fun greet() {
"hello"
}
}
"#;
let mut parser = Parser::new(code);
let result = parser.parse();
assert!(result.is_ok(), "Actor with no-param fun handler should parse: {:?}", result.err());
}
}