@@[target("rust")]

// ContextParser — FSM for parsing all @@ context constructs.
//
// Dispatches on the character after @@ to parse:
//   @@:return [= expr] → ContextReturn (kind=2)
//   @@:event           → ContextEvent (kind=3)
//   @@:data.key [= e]  → ContextData (kind=4) or ContextDataAssign (kind=5)
//   @@:params.key      → ContextParams (kind=6)
//   @@SystemName()     → SystemInstantiation (kind=7), Factory call
//   @@!SystemName()    → SystemInstantiation (kind=7), NoInitialization (RFC-0015 D7)
//   @@:(expr)          → ContextReturnExpr (kind=8)
//   @@:return(expr)    → ReturnCall (kind=9)
//   @@:self.method()   → ContextSelfCall (kind=10)
//   @@:self            → ContextSelf (kind=11)
//   @@:system.state.name → ContextSystemState (kind=12), current state name
//   @@:system.state    → ContextSystemStateReserved (kind=14), reserved (RFC-0045) → E608
//   other              → no match (has_result=false)
//
// For SystemInstantiation, the FSM sets `result_no_init = true` if the source
// had `@@!SystemName(...)` (the user's no-initialization sigil). The caller
// reads that flag to populate `InstantiationKind::{Factory, NoInitialization}`
// in the segment metadata.
//
// Demonstrates hierarchical composition: $ParseReturn and $ParseData
// create ExprScannerFsm sub-machines when they detect assignment `=`.

include!("expr_scanner.gen.rs");

@@system ContextParserFsm {
    interface:
        do_parse()

    machine:
        $Init {
            do_parse() { -> $Dispatching }
        }

        $Dispatching {
            $>() {
                let i = self.pos;
                let end = self.end;
                let bytes = &self.bytes;

                if i >= end {
                    self.has_result = false;
                    -> $Done
                }

                let b = bytes[i];

                if b == b':' {
                    self.pos = i + 1;
                    -> $DispatchColon
                } else if b == b'!' {
                    // @@! — RFC-0015 D7 no-initialization sigil. Must be
                    // followed immediately by an uppercase identifier
                    // (the system name). If not, no match — the user
                    // wrote something like `@@! foo` which is meaningless.
                    let j = i + 1;
                    if j < end && bytes[j].is_ascii_uppercase() {
                        self.pos = j;
                        self.result_no_init = true;
                        -> $ParseInstantiation
                    } else {
                        self.result_end = i;
                        self.has_result = false;
                        -> $Done
                    }
                } else if b.is_ascii_uppercase() {
                    // @@SystemName — pos stays at start of name
                    -> $ParseInstantiation
                } else {
                    // Just @@ without . or : or uppercase or !
                    self.result_end = i;
                    self.has_result = false;
                    -> $Done
                }
            }
        }

        $DispatchColon {
            $>() {
                // @@: — dispatch on the keyword after ':'
                let i = self.pos;
                let end = self.end;
                let bytes = &self.bytes;

                if i + 5 < end && &bytes[i..i + 6] == b"return" {
                    self.pos = i + 6;
                    -> $ParseReturn
                } else if i + 4 < end && &bytes[i..i + 5] == b"event" {
                    self.result_end = i + 5;
                    self.result_kind = 3; // ContextEvent
                    self.has_result = true;
                    -> $Done
                } else if i + 3 < end && &bytes[i..i + 4] == b"data" {
                    self.pos = i + 4;
                    -> $ParseData
                } else if i + 5 < end && &bytes[i..i + 6] == b"params" {
                    self.pos = i + 6;
                    -> $ParseParams
                } else if i + 3 < end && &bytes[i..i + 4] == b"self" {
                    self.pos = i + 4;
                    -> $ParseSelf
                } else if i + 5 < end && &bytes[i..i + 6] == b"system" {
                    self.pos = i + 6;
                    -> $ParseSystem
                } else if i < end && bytes[i] == b'(' {
                    // @@:(expr) — context return expression
                    self.pos = i;
                    -> $ParseContextReturnExpr
                } else {
                    // Unknown @@: variant
                    self.result_end = i;
                    self.has_result = false;
                    -> $Done
                }
            }
        }

        $ParseReturn {
            $>() {
                // @@:return — check for assignment, call form, or bare read
                let mut i = self.pos;
                let end = self.end;
                let bytes = &self.bytes;

                // Skip whitespace
                while i < end && (bytes[i] == b' ' || bytes[i] == b'\t') {
                    i += 1;
                }

                if i < end && bytes[i] == b'(' {
                    // @@:return(expr) — set return value AND exit handler.
                    // Scan balanced parens to find matching ')'.
                    let mut depth: usize = 1;
                    i += 1; // Skip opening '('
                    while i < end && depth > 0 {
                        if bytes[i] == b'(' { depth += 1; }
                        if bytes[i] == b')' { depth -= 1; }
                        if depth > 0 { i += 1; }
                    }
                    if depth == 0 { i += 1; } // Skip closing ')'
                    self.result_end = i;
                    self.result_kind = 9; // ReturnCall
                    self.has_result = true;
                    -> $Done
                } else if i < end && bytes[i] == b'=' && (i + 1 >= end || bytes[i + 1] != b'=') {
                    // @@:return = <expr> — create ExprScanner sub-machine
                    i += 1; // Skip '='
                    let mut expr = ExprScannerFsm::new();
                    expr.bytes = bytes.to_vec();
                    expr.pos = i;
                    expr.end = end;
                    expr.do_scan();
                    i = expr.result_end;
                    // expr is destroyed here (state manager pattern)
                    self.result_end = i;
                    self.result_kind = 2; // ContextReturn
                    self.has_result = true;
                    -> $Done
                } else {
                    // @@:return (bare read) — rvalue access to return slot
                    self.result_end = i;
                    self.result_kind = 2; // ContextReturn (read mode)
                    self.has_result = true;
                    -> $Done
                }
            }
        }

        $ParseContextReturnExpr {
            $>() {
                // @@:(expr) — scan balanced parens to find matching ')'
                let mut i = self.pos;
                let end = self.end;
                let bytes = &self.bytes;

                if i < end && bytes[i] == b'(' {
                    let mut depth: usize = 1;
                    i += 1; // Skip opening '('
                    while i < end && depth > 0 {
                        let b = bytes[i];
                        if b == b'(' {
                            depth += 1;
                        } else if b == b')' {
                            depth -= 1;
                        } else if b == b'"' || b == b'\'' {
                            // Skip string literals
                            let q = b;
                            i += 1;
                            while i < end {
                                if bytes[i] == b'\\' && i + 1 < end {
                                    i += 2;
                                    continue;
                                }
                                if bytes[i] == q {
                                    break;
                                }
                                i += 1;
                            }
                        }
                        i += 1;
                    }
                }

                self.result_end = i;
                self.result_kind = 8; // ContextReturnExpr
                self.has_result = true;
                -> $Done
            }
        }

        $ParseData {
            $>() {
                // @@:data.key or @@:data.key = expr
                let mut i = self.pos;
                let end = self.end;
                let bytes = &self.bytes;

                // Scan .key (dot + identifier)
                if i < end && bytes[i] == b'.' {
                    i += 1; // Skip '.'
                    while i < end && (bytes[i].is_ascii_alphanumeric() || bytes[i] == b'_') {
                        i += 1;
                    }
                }

                // Check for assignment
                let mut j = i;
                while j < end && (bytes[j] == b' ' || bytes[j] == b'\t') {
                    j += 1;
                }

                if j < end && bytes[j] == b'=' && (j + 1 >= end || bytes[j + 1] != b'=') {
                    // @@:data[key] = expr — create ExprScanner sub-machine
                    j += 1; // Skip '='
                    let mut expr = ExprScannerFsm::new();
                    expr.bytes = bytes.to_vec();
                    expr.pos = j;
                    expr.end = end;
                    expr.do_scan();
                    self.result_end = expr.result_end;
                    // expr is destroyed here (state manager pattern)
                    self.result_kind = 5; // ContextDataAssign
                } else {
                    self.result_end = i;
                    self.result_kind = 4; // ContextData
                }

                self.has_result = true;
                -> $Done
            }
        }

        $ParseParams {
            $>() {
                // @@:params.key — dot-accessor for interface parameter
                let mut i = self.pos;
                let end = self.end;
                let bytes = &self.bytes;

                if i < end && bytes[i] == b'.' {
                    i += 1; // Skip '.'
                    // Scan identifier
                    while i < end && (bytes[i].is_ascii_alphanumeric() || bytes[i] == b'_') {
                        i += 1;
                    }
                }

                self.result_end = i;
                self.result_kind = 6; // ContextParams
                self.has_result = true;
                -> $Done
            }
        }

        $ParseSelf {
            $>() {
                // @@:self — bare reference or @@:self.method(args) call
                let mut i = self.pos;
                let end = self.end;
                let bytes = &self.bytes;

                if i < end && bytes[i] == b'.' {
                    i += 1; // Skip '.'
                    // Scan identifier (method or property name)
                    let name_start = i;
                    while i < end && (bytes[i].is_ascii_alphanumeric() || bytes[i] == b'_') {
                        i += 1;
                    }
                    if i < end && bytes[i] == b'(' {
                        // @@:self.method(args) — scan balanced parens
                        let mut depth: usize = 1;
                        i += 1; // Skip '('
                        while i < end && depth > 0 {
                            if bytes[i] == b'(' { depth += 1; }
                            if bytes[i] == b')' { depth -= 1; }
                            if bytes[i] == b'"' || bytes[i] == b'\'' {
                                let q = bytes[i];
                                i += 1;
                                while i < end {
                                    if bytes[i] == b'\\' && i + 1 < end { i += 2; continue; }
                                    if bytes[i] == q { break; }
                                    i += 1;
                                }
                            }
                            if depth > 0 { i += 1; }
                        }
                        if depth == 0 { i += 1; } // Skip closing ')'
                        self.result_end = i;
                        self.result_kind = 10; // ContextSelfCall
                        self.has_result = true;
                    } else {
                        // @@:self.property — bare accessor
                        self.result_end = i;
                        self.result_kind = 11; // ContextSelf
                        self.has_result = true;
                    }
                } else {
                    // bare @@:self
                    self.result_end = i;
                    self.result_kind = 11; // ContextSelf
                    self.has_result = true;
                }

                -> $Done
            }
        }

        $ParseSystem {
            $>() {
                // @@:system — `.state.name` is the current-state name accessor.
                // Bare `@@:system.state` is RESERVED for future use (RFC-0045)
                // and rejected with E608; anything else is E604.
                let i = self.pos;
                let end = self.end;
                let bytes = &self.bytes;

                if i + 5 < end && &bytes[i..i + 6] == b".state"
                    && (i + 6 >= end || !(bytes[i + 6].is_ascii_alphanumeric() || bytes[i + 6] == b'_'))
                {
                    // Matched `.state` at a word boundary — now require `.name`.
                    let j = i + 6;
                    if j + 4 < end && &bytes[j..j + 5] == b".name"
                        && (j + 5 >= end || !(bytes[j + 5].is_ascii_alphanumeric() || bytes[j + 5] == b'_'))
                    {
                        // @@:system.state.name — read-only state name accessor
                        self.result_end = j + 5;
                        self.result_kind = 12; // ContextSystemState
                        self.has_result = true;
                    } else {
                        // @@:system.state (no `.name`) — reserved (RFC-0045) → E608
                        self.result_end = j;
                        self.result_kind = 14; // ContextSystemStateReserved
                        self.has_result = true;
                    }
                } else {
                    // Bare @@:system or unknown variant — emit for validation (E604)
                    self.result_end = i;
                    self.result_kind = 13; // ContextSystemBare
                    self.has_result = true;
                }

                -> $Done
            }
        }

        $ParseInstantiation {
            $>() {
                // @@SystemName() — scan name, find balanced parens
                let mut i = self.pos;
                let end = self.end;
                let bytes = &self.bytes;

                // Scan identifier
                while i < end && (bytes[i].is_ascii_alphanumeric() || bytes[i] == b'_') {
                    i += 1;
                }

                // Must be followed by (
                if i < end && bytes[i] == b'(' {
                    // Use the pre-computed paren_end if available
                    if self.paren_end > 0 {
                        i = self.paren_end;
                        self.result_end = i;
                        self.result_kind = 7; // SystemInstantiation
                        self.has_result = true;
                    } else {
                        // No paren_end provided — caller must handle
                        self.result_end = i;
                        self.has_result = false;
                    }
                } else {
                    // @@SomeName without () — treat as native
                    self.result_end = i;
                    self.has_result = false;
                }

                -> $Done
            }
        }

        $Done {
            $>() {
                // Terminal state — results are in domain vars
            }
        }

    domain:
        bytes: Vec<u8> = Vec::new()
        pos: usize = 0
        end: usize = 0
        result_end: usize = 0
        result_kind: usize = 0
        has_result: bool = false
        paren_end: usize = 0
        // RFC-0015 D7: set true when $Dispatching saw `@@!` and routed to
        // $ParseInstantiation. Caller uses this to map result_kind=7
        // to InstantiationKind::NoInitialization (else Factory).
        result_no_init: bool = false
}
