@@[target("rust")]

// Dogfooded body closer — Lua language brace matcher.
// Lua uses -- for line comments, --[[ ]] for block comments,
// "..." and '...' for strings, and [[ ]] for long strings.
//
// State machine flow:
//   $Init.scan() → $Scanning.$>() ↔ $InString/$InLineComment/$InBlockComment

@@system LuaBodyCloserFsm {
    interface:
        scan()

    machine:
        $Init {
            scan() {
                -> $Scanning
            }
        }

        $Scanning {
            $>() {
                let n = self.bytes.len();
                while self.pos < n {
                    let b = self.bytes[self.pos];
                    if b == b'\n' {
                        self.pos += 1;
                    } else if b == b'-' && self.pos + 1 < n && self.bytes[self.pos + 1] == b'-' {
                        // Check for block comment --[[ ]]
                        if self.pos + 3 < n && self.bytes[self.pos + 2] == b'[' && self.bytes[self.pos + 3] == b'[' {
                            self.pos += 4;
                            -> $InBlockComment
                        }
                        self.pos += 2;
                        -> $InLineComment
                    } else if b == b'"' || b == b'\'' {
                        self.string_char = b;
                        self.pos += 1;
                        -> $InString
                    } else if b == b'[' && self.pos + 1 < n && self.bytes[self.pos + 1] == b'[' {
                        // Long string [[ ]]
                        self.pos += 2;
                        -> $InLongString
                    } else if b == b'{' {
                        self.depth += 1;
                        self.pos += 1;
                    } else if b == b'}' {
                        self.depth -= 1;
                        if self.depth == 0 {
                            self.result = self.pos as i32;
                            return
                        }
                        self.pos += 1;
                    } else {
                        self.pos += 1;
                    }
                }
                self.result = -1;
            }
        }

        $InString {
            $>() {
                let n = self.bytes.len();
                while self.pos < n {
                    let b = self.bytes[self.pos];
                    if b == b'\\' {
                        self.pos += 2;
                    } else if b == self.string_char {
                        self.pos += 1;
                        -> $Scanning
                    } else {
                        self.pos += 1;
                    }
                }
                self.result = -1;
            }
        }

        $InLongString {
            $>() {
                let n = self.bytes.len();
                while self.pos + 1 < n {
                    if self.bytes[self.pos] == b']' && self.bytes[self.pos + 1] == b']' {
                        self.pos += 2;
                        -> $Scanning
                    }
                    self.pos += 1;
                }
                self.result = -1;
            }
        }

        $InLineComment {
            $>() {
                let n = self.bytes.len();
                while self.pos < n && self.bytes[self.pos] != b'\n' {
                    self.pos += 1;
                }
                -> $Scanning
            }
        }

        $InBlockComment {
            $>() {
                let n = self.bytes.len();
                while self.pos + 1 < n {
                    if self.bytes[self.pos] == b']' && self.bytes[self.pos + 1] == b']' {
                        self.pos += 2;
                        -> $Scanning
                    }
                    self.pos += 1;
                }
                self.result = -1;
            }
        }

    domain:
        bytes: Vec<u8> = Vec::new()
        pos: usize = 0
        depth: i32 = 1
        result: i32 = -1
        string_char: u8 = 0
}
