devalang_core/core/parser/handler/
dot.rs

1use crate::core::{
2    lexer::token::TokenKind,
3    parser::{driver::Parser, statement::{Statement, StatementKind}},
4    shared::{duration::Duration, value::Value},
5};
6
7pub fn parse_dot_token(
8    parser: &mut Parser,
9    _global_store: &mut crate::core::store::global::GlobalStore
10) -> Statement {
11    parser.advance(); // consume '.'
12
13    let Some(dot_token) = parser.previous_clone() else {
14        return Statement::unknown();
15    };
16
17    // Parse a single entity (namespace-friendly, stops at newline)
18    let mut parts = Vec::new();
19    let current_line = dot_token.line;
20
21    while let Some(token) = parser.peek_clone() {
22        // Never cross a newline
23        if token.line != current_line {
24            break;
25        }
26        match token.kind {
27            TokenKind::Identifier | TokenKind::Number => {
28                parts.push(token.lexeme.clone());
29                parser.advance();
30                // The separator must be a '.' on the same line, otherwise stop
31                if let Some(next) = parser.peek_clone() {
32                    if next.line != current_line || next.kind != TokenKind::Dot {
33                        break;
34                    }
35                } else {
36                    break;
37                }
38            }
39            TokenKind::Dot => {
40                parser.advance();
41            }
42            TokenKind::Newline | TokenKind::EOF | TokenKind::Indent | TokenKind::Dedent => {
43                break; // Stop at newline or dedent
44            }
45            _ => {
46                break;
47            }
48        }
49    }
50
51    // Build entity name properly
52    let entity = if !parts.is_empty() {
53        parts.join(".") // only join within the same line
54    } else {
55        eprintln!("⚠️ Empty entity after '.' at line {}", dot_token.line);
56        String::new()
57    };
58
59    // Optional duration and effects map
60    let mut duration = Duration::Auto;
61    let mut value = Value::Null;
62
63    if let Some(token) = parser.peek_clone() {
64    // Duration and effects map are only valid on the same line
65        if token.line == current_line {
66            match token.kind {
67            TokenKind::Number => {
68                let numerator = token.lexeme.clone();
69                parser.advance();
70                    if let Some(peek) = parser.peek_clone() {
71                        if peek.line == current_line {
72                            if let Some(TokenKind::Slash) = parser.peek_kind() {
73                                parser.advance();
74                                if let Some(denominator_token) = parser.peek_clone() {
75                                    if denominator_token.line == current_line && denominator_token.kind == TokenKind::Number {
76                                        let denominator = denominator_token.lexeme.clone();
77                                        parser.advance();
78                                        duration = Duration::Beat(format!("{}/{}", numerator, denominator));
79                                    }
80                                }
81                            } else {
82                                duration = parse_duration(numerator);
83                            }
84                        } else {
85                            duration = parse_duration(numerator);
86                        }
87                    } else {
88                        duration = parse_duration(numerator);
89                    }
90                    if let Some(next) = parser.peek_clone() {
91                        if next.line == current_line && next.kind == TokenKind::LBrace {
92                            value = parser.parse_map_value().unwrap_or(Value::Null);
93                        }
94                    }
95            }
96            TokenKind::Identifier => {
97                let id = token.lexeme.clone();
98                parser.advance();
99                duration = parse_duration(id);
100                    if let Some(next) = parser.peek_clone() {
101                        if next.line == current_line && next.kind == TokenKind::LBrace {
102                            value = parser.parse_map_value().unwrap_or(Value::Null);
103                        }
104                    }
105            }
106            TokenKind::LBrace => {
107                    value = parser.parse_map_value().unwrap_or(Value::Null);
108            }
109                _ => {}
110            }
111        }
112    }
113
114    Statement {
115        kind: StatementKind::Trigger {
116            entity,
117            duration,
118            effects: Some(value.clone()),
119        },
120        value: Value::Null,
121        indent: dot_token.indent,
122        line: dot_token.line,
123        column: dot_token.column,
124    }
125}
126
127fn parse_duration(s: String) -> Duration {
128    if s == "auto" {
129        Duration::Auto
130    } else if let Ok(num) = s.parse::<f32>() {
131        Duration::Number(num)
132    } else {
133        Duration::Identifier(s)
134    }
135}