use dist_agent_lang::lexer::tokens::{Keyword, Literal, Token};
use dist_agent_lang::parser::ast::{Expression, Statement};
use dist_agent_lang::{execute_source, parse_source, Lexer};
#[test]
fn test_lexer_basic_tokens() {
let code = "let x = 42;";
let mut lexer = Lexer::new(code);
let tokens = lexer.tokenize().unwrap();
assert!(!tokens.is_empty());
assert!(matches!(tokens[0], Token::Keyword(Keyword::Let)));
}
#[test]
fn test_lexer_string_literals() {
let code = r#"let name = "dist_agent_lang";"#;
let mut lexer = Lexer::new(code);
let tokens = lexer.tokenize().unwrap();
assert!(tokens
.iter()
.any(|t| matches!(t, Token::Literal(Literal::String(_)))));
}
#[test]
fn test_lexer_numbers() {
let code = "let x = 42; let y = 3.14;";
let mut lexer = Lexer::new(code);
let tokens = lexer.tokenize().unwrap();
let has_numbers = tokens.iter().any(|t| {
matches!(
t,
Token::Literal(Literal::Int(_)) | Token::Literal(Literal::Float(_))
)
});
assert!(has_numbers, "Should tokenize numbers");
}
#[test]
fn test_lexer_boolean_literals() {
let code = "let is_active = true; let is_inactive = false;";
let mut lexer = Lexer::new(code);
let tokens = lexer.tokenize().unwrap();
let has_bools = tokens
.iter()
.any(|t| matches!(t, Token::Literal(Literal::Bool(_))));
assert!(has_bools, "Should tokenize boolean literals");
}
#[test]
fn test_lexer_null_literal() {
let code = "let data = null;";
let mut lexer = Lexer::new(code);
let tokens = lexer.tokenize().unwrap();
assert!(tokens
.iter()
.any(|t| matches!(t, Token::Literal(Literal::Null))));
}
#[test]
fn test_lexer_attributes() {
let code = r#"@trust("hybrid") service Test {}"#;
let mut lexer = Lexer::new(code);
let tokens = lexer.tokenize().unwrap();
assert!(tokens.iter().any(|t| matches!(t, Token::Punctuation(_))));
}
#[test]
fn test_lexer_keywords() {
let code = "fn service let if else return try catch spawn agent event";
let mut lexer = Lexer::new(code);
let tokens = lexer.tokenize().unwrap();
assert!(tokens
.iter()
.any(|t| matches!(t, Token::Keyword(Keyword::Fn))));
assert!(tokens
.iter()
.any(|t| matches!(t, Token::Keyword(Keyword::Service))));
assert!(tokens
.iter()
.any(|t| matches!(t, Token::Keyword(Keyword::Let))));
assert!(tokens
.iter()
.any(|t| matches!(t, Token::Keyword(Keyword::If))));
assert!(tokens
.iter()
.any(|t| matches!(t, Token::Keyword(Keyword::Return))));
assert!(tokens
.iter()
.any(|t| matches!(t, Token::Keyword(Keyword::Try))));
assert!(tokens
.iter()
.any(|t| matches!(t, Token::Keyword(Keyword::Agent))));
assert!(tokens
.iter()
.any(|t| matches!(t, Token::Keyword(Keyword::Event))));
}
#[test]
fn test_lexer_empty_file() {
let code = "";
let mut lexer = Lexer::new(code);
let tokens = lexer.tokenize().unwrap();
assert!(tokens.len() <= 1);
}
#[test]
fn test_lexer_whitespace_handling() {
let code = "let x = 42 ;";
let mut lexer = Lexer::new(code);
let tokens = lexer.tokenize().unwrap();
assert!(tokens
.iter()
.any(|t| matches!(t, Token::Keyword(Keyword::Let))));
assert!(tokens
.iter()
.any(|t| matches!(t, Token::Literal(Literal::Int(_)))));
}
#[test]
fn test_lexer_unicode_support() {
let code = r#"let name = "测试";"#;
let mut lexer = Lexer::new(code);
let tokens = lexer.tokenize().unwrap();
assert!(tokens
.iter()
.any(|t| matches!(t, Token::Literal(Literal::String(_)))));
}
#[test]
fn test_lexer_comments() {
let code = r#"
// This is a comment
let x = 42; // Inline comment
"#;
let mut lexer = Lexer::new(code);
let tokens = lexer.tokenize().unwrap();
assert!(tokens
.iter()
.any(|t| matches!(t, Token::Keyword(Keyword::Let))));
}
#[test]
fn test_lexer_operators() {
let code = "let x = 10 + 20 - 5 * 2 / 3;";
let mut lexer = Lexer::new(code);
let tokens = lexer.tokenize().unwrap();
assert!(!tokens.is_empty());
}
#[test]
fn test_lexer_namespace_operator() {
let code = "let x = ai::create_agent({});";
let mut lexer = Lexer::new(code);
let tokens = lexer.tokenize().unwrap();
assert!(tokens.iter().any(|t| matches!(t, Token::Punctuation(_))));
}
#[test]
fn test_parser_variable_declaration() {
let code = "let x = 42;";
let program = parse_source(code).unwrap();
assert!(!program.statements.is_empty());
assert!(matches!(program.statements[0], Statement::Let(_)));
}
#[test]
fn test_parser_variable_with_keyword_name() {
let code = "let agent = 10; let ai = 20; let chain = 30;";
let program = parse_source(code).unwrap();
assert!(program.statements.len() >= 3);
assert!(matches!(program.statements[0], Statement::Let(_)));
assert!(matches!(program.statements[1], Statement::Let(_)));
assert!(matches!(program.statements[2], Statement::Let(_)));
}
#[test]
fn test_parser_multiple_variables() {
let code = "let x = 10; let y = 20; let z = x + y;";
let program = parse_source(code).unwrap();
assert!(program.statements.len() >= 3);
}
#[test]
fn test_parser_import_stdlib_path() {
let code = "import stdlib::chain;";
let program = parse_source(code).unwrap();
assert_eq!(program.statements.len(), 1);
if let Statement::Import(imp) = &program.statements[0] {
assert_eq!(imp.path, "stdlib::chain");
assert!(imp.alias.is_none());
} else {
panic!("expected Statement::Import");
}
}
#[test]
fn test_parser_import_string_path_with_alias() {
let code = r#"import "./mymod.dal" as mymod;"#;
let program = parse_source(code).unwrap();
assert_eq!(program.statements.len(), 1);
if let Statement::Import(imp) = &program.statements[0] {
assert_eq!(imp.path, "./mymod.dal");
assert_eq!(imp.alias.as_deref(), Some("mymod"));
} else {
panic!("expected Statement::Import");
}
}
#[test]
fn test_parser_import_identifier_path_with_alias() {
let code = "import stdlib::chain as ch;";
let program = parse_source(code).unwrap();
assert_eq!(program.statements.len(), 1);
if let Statement::Import(imp) = &program.statements[0] {
assert_eq!(imp.path, "stdlib::chain");
assert_eq!(imp.alias.as_deref(), Some("ch"));
} else {
panic!("expected Statement::Import");
}
}
#[test]
fn test_parser_import_inside_block_rejected() {
let code = r#"fn f() { import "x"; }"#;
let result = parse_source(code);
if let Ok(program) = result {
assert_eq!(program.statements.len(), 1);
assert!(matches!(program.statements[0], Statement::Function(_)));
}
}
#[test]
fn test_runtime_import_noop() {
let code = "import stdlib::chain;";
let result = execute_source(code);
assert!(
result.is_ok(),
"script with only import should run (no-op): {:?}",
result
);
}
#[test]
fn test_runtime_import_then_statement() {
let code = "import stdlib::chain;\nlet x = 1;";
let result = execute_source(code);
assert!(result.is_ok(), "import + let should run: {:?}", result);
}
#[test]
fn test_parser_type_annotations() {
let code = "let x: int = 42;";
let result = parse_source(code);
assert!(result.is_ok() || result.is_err());
}
#[test]
fn test_parser_function_declaration() {
let code = "fn add(a: int, b: int) -> int { return a + b; }";
let program = parse_source(code).unwrap();
assert!(!program.statements.is_empty());
assert!(matches!(program.statements[0], Statement::Function(_)));
}
#[test]
fn test_parser_function_without_return_type() {
let code = "fn greet(name: string) { return \"Hello \" + name; }";
let program = parse_source(code).unwrap();
assert!(!program.statements.is_empty());
}
#[test]
fn test_parser_function_without_parameters() {
let code = "fn main() { return 42; }";
let program = parse_source(code).unwrap();
assert!(!program.statements.is_empty());
}
#[test]
fn test_parser_nested_functions() {
let code = r#"
fn outer() {
fn inner() {
return 42;
}
return inner();
}
"#;
let program = parse_source(code).unwrap();
assert!(!program.statements.is_empty());
}
#[test]
fn test_parser_function_call() {
let code = r#"
fn add(a: int, b: int) -> int {
return a + b;
}
let result = add(10, 20);
"#;
let program = parse_source(code).unwrap();
assert!(program.statements.len() >= 2);
assert!(matches!(program.statements[0], Statement::Function(_)));
}
#[test]
fn test_parser_service_declaration() {
let code = r#"@trust("hybrid") @chain("ethereum") service TestService { value: int; }"#;
let program = parse_source(code).unwrap();
assert!(!program.statements.is_empty());
assert!(matches!(program.statements[0], Statement::Service(_)));
}
#[test]
fn test_service_empty_not_parsed_as_expression() {
let code = "service Foo { }";
let program = parse_source(code).unwrap();
assert!(
!program.statements.is_empty(),
"should parse at least one statement"
);
match &program.statements[0] {
Statement::Service(s) => {
assert_eq!(s.name, "Foo");
assert!(s.fields.is_empty());
assert!(s.methods.is_empty());
}
other => panic!(
"service Foo {{ }} must be parsed as Statement::Service, got {:?}",
other
),
}
}
#[test]
fn test_parser_service_with_fields() {
let code = r#"
service TestService {
balance: int = 1000;
owner: string;
active: bool = true;
}
"#;
let program = parse_source(code).unwrap();
assert!(!program.statements.is_empty());
}
#[test]
fn test_parser_service_with_methods() {
let code = r#"
service TestService {
balance: int = 0;
fn deposit(amount: int) {
balance = balance + amount;
}
fn withdraw(amount: int) -> string {
if (balance < amount) {
return "insufficient";
}
balance = balance - amount;
return "success";
}
}
"#;
let program = parse_source(code).unwrap();
assert!(!program.statements.is_empty());
}
#[test]
fn test_parser_service_with_events() {
let code = r#"
service TestService {
event Transfer(from: string, to: string, amount: int);
event Mint(to: string, amount: int);
}
"#;
let program = parse_source(code).unwrap();
assert!(!program.statements.is_empty());
}
#[test]
fn test_parser_service_with_attributes() {
let code = r#"
@trust("hybrid")
@chain("ethereum")
@secure
service SecureService {
fn execute() {}
}
"#;
let program = parse_source(code).unwrap();
assert!(!program.statements.is_empty());
}
#[test]
fn test_parser_if_statement() {
let code = r#"
let x = 10;
if (x > 5) {
let y = 20;
}
"#;
let program = parse_source(code).unwrap();
assert!(!program.statements.is_empty());
}
#[test]
fn test_parser_if_else_statement() {
let code = r#"
let x = 10;
if (x > 5) {
return "greater";
} else {
return "less";
}
"#;
let program = parse_source(code).unwrap();
assert!(!program.statements.is_empty());
}
#[test]
fn test_parser_nested_if_statements() {
let code = r#"
let x = 10;
let y = 20;
if (x > 5) {
if (y > 15) {
return "both";
}
}
"#;
let program = parse_source(code).unwrap();
assert!(!program.statements.is_empty());
}
#[test]
fn test_parser_if_without_parentheses_rejected() {
let code = r#"
let x = 10;
if x > 5 {
return "greater";
}
"#;
let result = parse_source(code);
assert!(
result.is_err(),
"if statement without parentheses should be rejected"
);
if let Err(e) = result {
let error_msg = format!("{}", e);
assert!(
error_msg.contains("(")
|| error_msg.contains("parentheses")
|| error_msg.contains("Expected")
|| error_msg.contains("Punctuation"),
"Error message should mention parentheses or expected token. Got: {}",
error_msg
);
}
}
#[test]
fn test_parser_try_catch() {
let code = r#"
try {
let x = 1 / 0;
} catch {
return "error";
}
"#;
let program = parse_source(code).unwrap();
assert!(!program.statements.is_empty());
assert!(matches!(program.statements[0], Statement::Try(_)));
}
#[test]
fn test_parser_arithmetic_expressions() {
let code = "let result = 10 + 20 * 2 - 5;";
let program = parse_source(code).unwrap();
assert!(!program.statements.is_empty());
}
#[test]
fn test_parser_complex_expressions() {
let code = "let result = (10 + 20) * 2 - 5;";
let program = parse_source(code).unwrap();
assert!(!program.statements.is_empty());
}
#[test]
fn test_parser_string_concatenation() {
let code = r#"let result = "Hello" + " " + "World";"#;
let program = parse_source(code).unwrap();
assert!(!program.statements.is_empty());
}
#[test]
fn test_parser_comparison_operators() {
let code = "let result = 10 > 5; let result2 = 10 == 10; let result3 = 10 != 5;";
let program = parse_source(code).unwrap();
assert!(program.statements.len() >= 3);
}
#[test]
fn test_parser_logical_operators() {
let code = "let result = true && false; let result2 = true || false; let result3 = !true;";
let program = parse_source(code).unwrap();
assert!(program.statements.len() >= 3);
}
#[test]
fn test_parser_array_literals() {
let code = "let arr = [1, 2, 3, 4, 5];";
let program = parse_source(code).unwrap();
assert!(!program.statements.is_empty());
if let Statement::Let(let_stmt) = &program.statements[0] {
assert_eq!(let_stmt.name, "arr");
match &let_stmt.value {
Expression::ArrayLiteral(elements) => {
assert_eq!(elements.len(), 5);
}
_ => panic!("Expected ArrayLiteral, got {:?}", let_stmt.value),
}
} else {
panic!("Expected Let statement");
}
}
#[test]
fn test_parser_empty_array() {
let code = "let arr = [];";
let program = parse_source(code).unwrap();
assert!(!program.statements.is_empty());
if let Statement::Let(let_stmt) = &program.statements[0] {
assert_eq!(let_stmt.name, "arr");
match &let_stmt.value {
Expression::ArrayLiteral(elements) => {
assert_eq!(elements.len(), 0);
}
_ => panic!("Expected ArrayLiteral, got {:?}", let_stmt.value),
}
} else {
panic!("Expected Let statement");
}
}
#[test]
fn test_parser_mixed_array() {
let code = r#"let mixed = [1, "two", true, null];"#;
let program = parse_source(code).unwrap();
assert!(!program.statements.is_empty());
if let Statement::Let(let_stmt) = &program.statements[0] {
match &let_stmt.value {
Expression::ArrayLiteral(elements) => {
assert_eq!(elements.len(), 4);
}
_ => panic!("Expected ArrayLiteral"),
}
}
}
#[test]
fn test_parser_map_literals() {
let code = r#"let map = {"key": "value", "num": 42};"#;
let program = parse_source(code).unwrap();
assert!(!program.statements.is_empty());
}
#[test]
fn test_parser_nested_collections() {
let code = r#"let data = {"nested": {"key": "value"}};"#;
let program = parse_source(code).unwrap();
assert!(!program.statements.is_empty());
}
#[test]
fn test_parser_nested_maps() {
let code = r#"let data = {"outer": {"inner": "value"}};"#;
let program = parse_source(code).unwrap();
assert!(!program.statements.is_empty());
}
#[test]
fn test_parser_ai_namespace() {
let code = r#"
service AIService {
fn create() {
return ai::create_agent({});
}
}
"#;
let program = parse_source(code).unwrap();
assert!(!program.statements.is_empty());
}
#[test]
fn test_parser_chain_namespace() {
let code = r#"
service ChainService {
fn deploy() {
return chain::deploy(1, "Contract", {});
}
}
"#;
let program = parse_source(code).unwrap();
assert!(!program.statements.is_empty());
}
#[test]
fn test_parser_crypto_namespace() {
let code = r#"
service CryptoService {
fn hash() {
return crypto::sha256("data");
}
}
"#;
let program = parse_source(code).unwrap();
assert!(!program.statements.is_empty());
}
#[test]
fn test_parser_log_namespace() {
let code = r#"
service LogService {
fn log() {
log::info("test", "message");
}
}
"#;
let program = parse_source(code).unwrap();
assert!(!program.statements.is_empty());
}
#[test]
fn test_parser_auth_namespace() {
let code = r#"
service AuthService {
fn authenticate() {
return auth::generate_token("user");
}
}
"#;
let program = parse_source(code).unwrap();
assert!(!program.statements.is_empty());
}
#[test]
fn test_parser_namespace_in_let_statement() {
let code = r#"
service TestService {
fn test() {
let agent = ai::create_agent({});
let address = chain::deploy(1, "Contract", {});
return agent;
}
}
"#;
let program = parse_source(code).unwrap();
assert!(!program.statements.is_empty());
}
#[test]
fn test_parser_trust_attribute() {
let code = r#"@trust("hybrid") @chain("ethereum") service Test {}"#;
let program = parse_source(code).unwrap();
assert!(!program.statements.is_empty());
}
#[test]
fn test_parser_secure_attribute() {
let code = r#"@secure service Test {}"#;
let program = parse_source(code).unwrap();
assert!(!program.statements.is_empty());
}
#[test]
fn test_parser_multiple_attributes() {
let code = r#"@trust("hybrid") @secure @limit(1000) fn test() {}"#;
let program = parse_source(code).unwrap();
assert!(!program.statements.is_empty());
}
#[test]
fn test_parser_chain_attributes() {
let code = r#"@chain("ethereum") @chain("polygon") service Test {}"#;
let program = parse_source(code).unwrap();
assert!(!program.statements.is_empty());
}
#[test]
fn test_parser_block_statements() {
let code = r#"
let x = 10;
{
let y = 20;
}
"#;
let program = parse_source(code).unwrap();
assert!(!program.statements.is_empty());
}
#[test]
fn test_parser_nested_blocks() {
let code = r#"
{
let x = 10;
{
let y = 20;
}
}
"#;
let program = parse_source(code).unwrap();
assert!(!program.statements.is_empty());
}
#[test]
fn test_runtime_variable_assignment() {
let code = "let x = 42;";
let program = parse_source(code).unwrap();
let result = execute_source(code);
assert!(result.is_ok() || !program.statements.is_empty());
}
#[test]
fn test_runtime_function_call() {
let code = r#"
fn add(a: int, b: int) -> int {
return a + b;
}
let result = add(10, 20);
"#;
let program = parse_source(code).unwrap();
let result = execute_source(code);
assert!(result.is_ok() || !program.statements.is_empty());
}
#[test]
fn test_runtime_control_flow() {
let code = r#"
let x = 10;
if (x > 5) {
let y = 20;
}
"#;
let program = parse_source(code).unwrap();
let result = execute_source(code);
assert!(result.is_ok() || !program.statements.is_empty());
}
#[test]
fn test_runtime_scope_management() {
let code = r#"
let x = 10;
{
let x = 20;
}
"#;
let program = parse_source(code).unwrap();
let result = execute_source(code);
assert!(result.is_ok() || !program.statements.is_empty());
}
#[test]
fn test_runtime_error_handling() {
let code = r#"
try {
let x = 1 / 0;
} catch {
return "error";
}
"#;
let program = parse_source(code).unwrap();
let result = execute_source(code);
assert!(result.is_ok() || !program.statements.is_empty());
}
#[test]
fn test_complete_service_pattern() {
let code = r#"
@trust("hybrid")
@chain("ethereum")
service CompleteService {
balance: int = 1000;
owner: string;
fn transfer(to: string, amount: int) -> string {
if (balance < amount) {
return "insufficient";
}
balance = balance - amount;
return "success";
}
event Transfer(from: string, to: string, amount: int);
}
"#;
let program = parse_source(code).unwrap();
assert!(!program.statements.is_empty());
assert!(matches!(program.statements[0], Statement::Service(_)));
}
#[test]
fn test_ai_agent_pattern() {
let code = r#"
service AIService {
fn create_system() {
let coordinator = ai::create_agent_coordinator();
let agent_config = {
"name": "worker",
"role": "data_processor"
};
let agent = ai::create_agent(agent_config);
return agent;
}
}
"#;
let program = parse_source(code).unwrap();
assert!(!program.statements.is_empty());
}
#[test]
fn test_multi_chain_pattern() {
let code = r#"
@chain("ethereum")
@chain("polygon")
service MultiChainService {
fn deploy() {
chain::deploy(1, "Contract", {});
chain::deploy(137, "Contract", {});
}
}
"#;
let program = parse_source(code).unwrap();
assert!(!program.statements.is_empty());
}
#[test]
fn test_complex_expression_pattern() {
let code = r#"
fn calculate(a: int, b: int, c: int) -> int {
let result = (a + b) * c - (a * b);
if (result > 100) {
return result / 2;
}
return result;
}
"#;
let program = parse_source(code).unwrap();
assert!(!program.statements.is_empty());
}
#[test]
fn test_error_recovery_pattern() {
let invalid_codes = vec![
"let x = ;", "fn test() {", "service Test {", ];
for code in invalid_codes {
let result = parse_source(code);
assert!(result.is_ok() || result.is_err());
}
}
#[test]
fn test_execute_dal_file_returns_err_on_parse_error() {
let dir = tempfile::tempdir().unwrap();
let path = dir.path().join("bad.dal");
std::fs::write(&path, "let = 1;").unwrap();
let result = dist_agent_lang::execute_dal_file(path.to_str().unwrap());
assert!(
result.is_err(),
"execute_dal_file on invalid syntax should return Err; got: {:?}",
result
);
let err = result.unwrap_err();
assert!(
err.contains("Parse error") || err.contains("parse"),
"error message should mention parse; got: {}",
err
);
}