"""
CSSL Parser - Lexer and Parser for CSO Service Script Language

Features:
- Complete tokenization of CSSL syntax
- AST (Abstract Syntax Tree) generation
- Enhanced error reporting with line/column info
- Support for service files and standalone programs
- Special operators: <== (inject), ==> (receive), -> <- (flow)
- Module references (@Module) and self-references (s@Struct)
"""

import re
from enum import Enum, auto
from dataclasses import dataclass, field
from typing import List, Dict, Any, Optional, Union


class CSSLSyntaxError(Exception):
    """Syntax error with detailed location information"""

    def __init__(self, message: str, line: int = 0, column: int = 0, source_line: str = ""):
        self.line = line
        self.column = column
        self.source_line = source_line

        # Build detailed error message
        location = f" at line {line}" if line else ""
        if column:
            location += f", column {column}"

        full_message = f"CSSL Syntax Error{location}: {message}"

        if source_line:
            full_message += f"\n  {source_line}"
            if column > 0:
                full_message += f"\n  {' ' * (column - 1)}^"

        super().__init__(full_message)


class TokenType(Enum):
    KEYWORD = auto()
    IDENTIFIER = auto()
    STRING = auto()
    STRING_INTERP = auto()  # <variable> in strings
    NUMBER = auto()
    BOOLEAN = auto()
    NULL = auto()
    TYPE_LITERAL = auto()  # list, dict as type literals
    TYPE_GENERIC = auto()  # datastruct<T>, shuffled<T>, iterator<T>, combo<T>
    OPERATOR = auto()
    # Basic injection operators
    INJECT_LEFT = auto()        # <==
    INJECT_RIGHT = auto()       # ==>
    # BruteForce Injection operators - Copy & Add
    INJECT_PLUS_LEFT = auto()   # +<==
    INJECT_PLUS_RIGHT = auto()  # ==>+
    # BruteForce Injection operators - Move & Remove
    INJECT_MINUS_LEFT = auto()  # -<==
    INJECT_MINUS_RIGHT = auto() # ===>-
    # BruteForce Injection operators - Code Infusion
    INFUSE_LEFT = auto()        # <<==
    INFUSE_RIGHT = auto()       # ==>>
    INFUSE_PLUS_LEFT = auto()   # +<<==
    INFUSE_PLUS_RIGHT = auto()  # ==>>+
    INFUSE_MINUS_LEFT = auto()  # -<<==
    INFUSE_MINUS_RIGHT = auto() # ==>>-
    # Flow operators
    FLOW_RIGHT = auto()
    FLOW_LEFT = auto()
    EQUALS = auto()
    COMPARE_EQ = auto()
    COMPARE_NE = auto()
    COMPARE_LT = auto()
    COMPARE_GT = auto()
    COMPARE_LE = auto()
    COMPARE_GE = auto()
    PLUS = auto()
    MINUS = auto()
    MULTIPLY = auto()
    DIVIDE = auto()
    MODULO = auto()
    AND = auto()
    OR = auto()
    NOT = auto()
    AMPERSAND = auto()  # & for references
    BLOCK_START = auto()
    BLOCK_END = auto()
    PAREN_START = auto()
    PAREN_END = auto()
    BRACKET_START = auto()
    BRACKET_END = auto()
    SEMICOLON = auto()
    COLON = auto()
    DOUBLE_COLON = auto()  # :: for injection helpers (string::where, json::key, etc)
    COMMA = auto()
    DOT = auto()
    AT = auto()
    GLOBAL_REF = auto()  # r@<name> global variable declaration
    SELF_REF = auto()    # s@<name> self-reference to global struct
    PACKAGE = auto()
    PACKAGE_INCLUDES = auto()
    AS = auto()
    COMMENT = auto()
    NEWLINE = auto()
    EOF = auto()


KEYWORDS = {
    # Service structure
    'service-init', 'service-run', 'service-include', 'struct', 'define', 'main',
    # Control flow
    'if', 'else', 'elif', 'while', 'for', 'foreach', 'in', 'range',
    'switch', 'case', 'default', 'break', 'continue', 'return',
    'try', 'catch', 'finally', 'throw',
    # Literals
    'True', 'False', 'null', 'None', 'true', 'false',
    # Logical operators
    'and', 'or', 'not',
    # Async/Events
    'start', 'stop', 'wait_for', 'on_event', 'emit_event', 'await',
    # Package system
    'package', 'package-includes', 'exec', 'as', 'global',
    # CSSL Type Keywords
    'int', 'string', 'float', 'bool', 'void', 'json', 'array', 'vector', 'stack',
    'dynamic',      # No type declaration (slow but flexible)
    'undefined',    # Function errors ignored
    'open',         # Accept any parameter type
    'datastruct',   # Universal container (lazy declarator)
    'dataspace',    # SQL/data storage container
    'shuffled',     # Unorganized fast storage (multiple returns)
    'iterator',     # Advanced iterator with tasks
    'combo',        # Filter/search spaces
    'structure',    # Advanced C++/Py Class
    'openquote',    # SQL openquote container
    # CSSL Function Modifiers
    'meta',         # Source function (must return)
    'super',        # Force execution (no exceptions)
    'closed',       # Protect from external injection
    'private',      # Disable all injections
    'virtual',      # Import cycle safe
    'sqlbased',     # SQL-based function
    # CSSL Include Keywords
    'include', 'get',
}

# Type literals that create empty instances
TYPE_LITERALS = {'list', 'dict'}

# Generic type keywords that use <T> syntax
TYPE_GENERICS = {
    'datastruct', 'dataspace', 'shuffled', 'iterator', 'combo',
    'vector', 'stack', 'array', 'openquote'
}

# Injection helper prefixes (type::helper=value)
INJECTION_HELPERS = {
    'string', 'integer', 'json', 'array', 'vector', 'combo', 'dynamic', 'sql'
}


@dataclass
class Token:
    type: TokenType
    value: Any
    line: int
    column: int


class CSSLLexer:
    """Tokenizes CSSL source code into a stream of tokens."""

    def __init__(self, source: str):
        self.source = source
        self.pos = 0
        self.line = 1
        self.column = 1
        self.tokens: List[Token] = []
        # Store source lines for error messages
        self.source_lines = source.split('\n')

    def get_source_line(self, line_num: int) -> str:
        """Get a specific source line for error reporting"""
        if 0 < line_num <= len(self.source_lines):
            return self.source_lines[line_num - 1]
        return ""

    def error(self, message: str):
        """Raise a syntax error with location info"""
        raise CSSLSyntaxError(
            message,
            line=self.line,
            column=self.column,
            source_line=self.get_source_line(self.line)
        )

    def tokenize(self) -> List[Token]:
        while self.pos < len(self.source):
            self._skip_whitespace()
            if self.pos >= len(self.source):
                break

            char = self.source[self.pos]

            # Comments: both # and // style
            if char == '#':
                self._skip_comment()
            elif char == '/' and self._peek(1) == '/':
                # C-style // comment - NEW
                self._skip_comment()
            elif char == '\n':
                self._add_token(TokenType.NEWLINE, '\n')
                self._advance()
                self.line += 1
                self.column = 1
            elif char in '"\'':
                self._read_string(char)
            elif char.isdigit() or (char == '-' and self._peek(1).isdigit()):
                self._read_number()
            elif char == 'r' and self._peek(1) == '@':
                # r@<name> global variable declaration (same as 'global')
                self._read_global_ref()
            elif char == 's' and self._peek(1) == '@':
                # s@<name> self-reference to global struct
                self._read_self_ref()
            elif char.isalpha() or char == '_' or char == '-':
                self._read_identifier()
            elif char == '@':
                self._add_token(TokenType.AT, '@')
                self._advance()
            elif char == '&':
                # & for references
                if self._peek(1) == '&':
                    self._add_token(TokenType.AND, '&&')
                    self._advance()
                    self._advance()
                else:
                    self._add_token(TokenType.AMPERSAND, '&')
                    self._advance()
            elif char == '{':
                self._add_token(TokenType.BLOCK_START, '{')
                self._advance()
            elif char == '}':
                self._add_token(TokenType.BLOCK_END, '}')
                self._advance()
            elif char == '(':
                self._add_token(TokenType.PAREN_START, '(')
                self._advance()
            elif char == ')':
                self._add_token(TokenType.PAREN_END, ')')
                self._advance()
            elif char == '[':
                self._add_token(TokenType.BRACKET_START, '[')
                self._advance()
            elif char == ']':
                self._add_token(TokenType.BRACKET_END, ']')
                self._advance()
            elif char == ';':
                self._add_token(TokenType.SEMICOLON, ';')
                self._advance()
            elif char == ':':
                # Check for :: (double colon for injection helpers)
                if self._peek(1) == ':':
                    self._add_token(TokenType.DOUBLE_COLON, '::')
                    self._advance()
                    self._advance()
                else:
                    self._add_token(TokenType.COLON, ':')
                    self._advance()
            elif char == ',':
                self._add_token(TokenType.COMMA, ',')
                self._advance()
            elif char == '.':
                self._add_token(TokenType.DOT, '.')
                self._advance()
            elif char == '+':
                # Check for BruteForce Injection: +<== or +<<==
                if self._peek(1) == '<' and self._peek(2) == '<' and self._peek(3) == '=' and self._peek(4) == '=':
                    self._add_token(TokenType.INFUSE_PLUS_LEFT, '+<<==')
                    for _ in range(5): self._advance()
                elif self._peek(1) == '<' and self._peek(2) == '=' and self._peek(3) == '=':
                    self._add_token(TokenType.INJECT_PLUS_LEFT, '+<==')
                    for _ in range(4): self._advance()
                else:
                    self._add_token(TokenType.PLUS, '+')
                    self._advance()
            elif char == '*':
                self._add_token(TokenType.MULTIPLY, '*')
                self._advance()
            elif char == '/':
                # Check if this is a // comment (handled above) or division
                if self._peek(1) != '/':
                    self._add_token(TokenType.DIVIDE, '/')
                    self._advance()
                else:
                    # Already handled by // comment check above, but just in case
                    self._skip_comment()
            elif char == '%':
                self._add_token(TokenType.MODULO, '%')
                self._advance()
            elif char == '<':
                self._read_less_than()
            elif char == '>':
                self._read_greater_than()
            elif char == '=':
                self._read_equals()
            elif char == '!':
                self._read_not()
            elif char == '-':
                self._read_minus()
            elif char == '|':
                if self._peek(1) == '|':
                    self._add_token(TokenType.OR, '||')
                    self._advance()
                    self._advance()
                else:
                    self._advance()
            else:
                self._advance()

        self._add_token(TokenType.EOF, '')
        return self.tokens

    def _advance(self):
        self.pos += 1
        self.column += 1

    def _peek(self, offset=0) -> str:
        pos = self.pos + offset
        if pos < len(self.source):
            return self.source[pos]
        return ''

    def _add_token(self, token_type: TokenType, value: Any):
        self.tokens.append(Token(token_type, value, self.line, self.column))

    def _skip_whitespace(self):
        while self.pos < len(self.source) and self.source[self.pos] in ' \t\r':
            self._advance()

    def _skip_comment(self):
        while self.pos < len(self.source) and self.source[self.pos] != '\n':
            self._advance()

    def _read_string(self, quote_char: str):
        self._advance()
        start = self.pos
        while self.pos < len(self.source) and self.source[self.pos] != quote_char:
            if self.source[self.pos] == '\\':
                self._advance()
            self._advance()
        value = self.source[start:self.pos]
        self._add_token(TokenType.STRING, value)
        self._advance()

    def _read_number(self):
        start = self.pos
        if self.source[self.pos] == '-':
            self._advance()
        while self.pos < len(self.source) and (self.source[self.pos].isdigit() or self.source[self.pos] == '.'):
            self._advance()
        value = self.source[start:self.pos]
        if '.' in value:
            self._add_token(TokenType.NUMBER, float(value))
        else:
            self._add_token(TokenType.NUMBER, int(value))

    def _read_identifier(self):
        start = self.pos
        while self.pos < len(self.source) and (self.source[self.pos].isalnum() or self.source[self.pos] in '_-'):
            self._advance()
        value = self.source[start:self.pos]

        if value in ('True', 'true'):
            self._add_token(TokenType.BOOLEAN, True)
        elif value in ('False', 'false'):
            self._add_token(TokenType.BOOLEAN, False)
        elif value in ('null', 'None', 'none'):
            self._add_token(TokenType.NULL, None)
        elif value in TYPE_LITERALS:
            # NEW: list and dict as type literals (e.g., cache = list;)
            self._add_token(TokenType.TYPE_LITERAL, value)
        elif value == 'as':
            # NEW: 'as' keyword for foreach ... as ... syntax
            self._add_token(TokenType.AS, value)
        elif value in KEYWORDS:
            self._add_token(TokenType.KEYWORD, value)
        else:
            self._add_token(TokenType.IDENTIFIER, value)

    def _read_self_ref(self):
        """Read s@<name> or s@<name>.<member>... self-reference"""
        start = self.pos
        self._advance()  # skip 's'
        self._advance()  # skip '@'

        # Read the identifier path (Name.Member.SubMember)
        path_parts = []
        while self.pos < len(self.source):
            # Read identifier part
            part_start = self.pos
            while self.pos < len(self.source) and (self.source[self.pos].isalnum() or self.source[self.pos] == '_'):
                self._advance()
            if self.pos > part_start:
                path_parts.append(self.source[part_start:self.pos])

            # Check for dot to continue path
            if self.pos < len(self.source) and self.source[self.pos] == '.':
                self._advance()  # skip '.'
            else:
                break

        value = '.'.join(path_parts)
        self._add_token(TokenType.SELF_REF, value)

    def _read_global_ref(self):
        """Read r@<name> global variable declaration (equivalent to 'global')"""
        start = self.pos
        self._advance()  # skip 'r'
        self._advance()  # skip '@'

        # Read the identifier
        name_start = self.pos
        while self.pos < len(self.source) and (self.source[self.pos].isalnum() or self.source[self.pos] == '_'):
            self._advance()

        value = self.source[name_start:self.pos]
        self._add_token(TokenType.GLOBAL_REF, value)

    def _read_less_than(self):
        # Check for <<== (code infusion left)
        if self._peek(1) == '<' and self._peek(2) == '=' and self._peek(3) == '=':
            self._add_token(TokenType.INFUSE_LEFT, '<<==')
            for _ in range(4): self._advance()
        # Check for <== (basic injection left)
        elif self._peek(1) == '=' and self._peek(2) == '=':
            self._add_token(TokenType.INJECT_LEFT, '<==')
            for _ in range(3): self._advance()
        elif self._peek(1) == '=':
            self._add_token(TokenType.COMPARE_LE, '<=')
            self._advance()
            self._advance()
        elif self._peek(1) == '-':
            self._add_token(TokenType.FLOW_LEFT, '<-')
            self._advance()
            self._advance()
        else:
            self._add_token(TokenType.COMPARE_LT, '<')
            self._advance()

    def _read_greater_than(self):
        if self._peek(1) == '=':
            self._add_token(TokenType.COMPARE_GE, '>=')
            self._advance()
            self._advance()
        else:
            self._add_token(TokenType.COMPARE_GT, '>')
            self._advance()

    def _read_equals(self):
        # Check for ==>>+ (code infusion right plus)
        if self._peek(1) == '=' and self._peek(2) == '>' and self._peek(3) == '>' and self._peek(4) == '+':
            self._add_token(TokenType.INFUSE_PLUS_RIGHT, '==>>+')
            for _ in range(5): self._advance()
        # Check for ==>>- (code infusion right minus)
        elif self._peek(1) == '=' and self._peek(2) == '>' and self._peek(3) == '>' and self._peek(4) == '-':
            self._add_token(TokenType.INFUSE_MINUS_RIGHT, '==>>-')
            for _ in range(5): self._advance()
        # Check for ==>> (code infusion right)
        elif self._peek(1) == '=' and self._peek(2) == '>' and self._peek(3) == '>':
            self._add_token(TokenType.INFUSE_RIGHT, '==>>')
            for _ in range(4): self._advance()
        # Check for ==>+ (injection right plus)
        elif self._peek(1) == '=' and self._peek(2) == '>' and self._peek(3) == '+':
            self._add_token(TokenType.INJECT_PLUS_RIGHT, '==>+')
            for _ in range(4): self._advance()
        # Check for ===>- (injection right minus - moves & removes)
        elif self._peek(1) == '=' and self._peek(2) == '=' and self._peek(3) == '>' and self._peek(4) == '-':
            self._add_token(TokenType.INJECT_MINUS_RIGHT, '===>')
            for _ in range(5): self._advance()
        # Check for ==> (basic injection right)
        elif self._peek(1) == '=' and self._peek(2) == '>':
            self._add_token(TokenType.INJECT_RIGHT, '==>')
            for _ in range(3): self._advance()
        elif self._peek(1) == '=':
            self._add_token(TokenType.COMPARE_EQ, '==')
            self._advance()
            self._advance()
        else:
            self._add_token(TokenType.EQUALS, '=')
            self._advance()

    def _read_not(self):
        if self._peek(1) == '=':
            self._add_token(TokenType.COMPARE_NE, '!=')
            self._advance()
            self._advance()
        else:
            self._add_token(TokenType.NOT, '!')
            self._advance()

    def _read_minus(self):
        # Check for -<<== (code infusion minus left)
        if self._peek(1) == '<' and self._peek(2) == '<' and self._peek(3) == '=' and self._peek(4) == '=':
            self._add_token(TokenType.INFUSE_MINUS_LEFT, '-<<==')
            for _ in range(5): self._advance()
        # Check for -<== (injection minus left - move & remove)
        elif self._peek(1) == '<' and self._peek(2) == '=' and self._peek(3) == '=':
            self._add_token(TokenType.INJECT_MINUS_LEFT, '-<==')
            for _ in range(4): self._advance()
        # Check for -==> (injection right minus)
        elif self._peek(1) == '=' and self._peek(2) == '=' and self._peek(3) == '>':
            self._add_token(TokenType.INJECT_MINUS_RIGHT, '-==>')
            for _ in range(4): self._advance()
        elif self._peek(1) == '>':
            self._add_token(TokenType.FLOW_RIGHT, '->')
            self._advance()
            self._advance()
        else:
            self._add_token(TokenType.MINUS, '-')
            self._advance()


@dataclass
class ASTNode:
    type: str
    value: Any = None
    children: List['ASTNode'] = field(default_factory=list)
    line: int = 0
    column: int = 0


class CSSLParser:
    """Parses CSSL tokens into an Abstract Syntax Tree."""

    def __init__(self, tokens: List[Token], source_lines: List[str] = None):
        self.tokens = [t for t in tokens if t.type != TokenType.NEWLINE]
        self.pos = 0
        self.source_lines = source_lines or []

    def get_source_line(self, line_num: int) -> str:
        """Get a specific source line for error reporting"""
        if 0 < line_num <= len(self.source_lines):
            return self.source_lines[line_num - 1]
        return ""

    def error(self, message: str, token: Token = None):
        """Raise a syntax error with location info"""
        if token is None:
            token = self._current()
        raise CSSLSyntaxError(
            message,
            line=token.line,
            column=token.column,
            source_line=self.get_source_line(token.line)
        )

    def parse(self) -> ASTNode:
        """Parse a service file (wrapped in braces)"""
        root = ASTNode('service', children=[])

        if not self._match(TokenType.BLOCK_START):
            self.error(f"Expected '{{' at start of service, got {self._current().type.name}")

        while not self._check(TokenType.BLOCK_END) and not self._is_at_end():
            if self._match_keyword('service-init'):
                root.children.append(self._parse_service_init())
            elif self._match_keyword('service-include'):
                root.children.append(self._parse_service_include())
            elif self._match_keyword('service-run'):
                root.children.append(self._parse_service_run())
            # NEW: package block support
            elif self._match_keyword('package'):
                root.children.append(self._parse_package())
            # NEW: package-includes block support
            elif self._match_keyword('package-includes'):
                root.children.append(self._parse_package_includes())
            # NEW: struct at top level
            elif self._match_keyword('struct'):
                root.children.append(self._parse_struct())
            # NEW: define at top level
            elif self._match_keyword('define'):
                root.children.append(self._parse_define())
            else:
                self._advance()

        self._match(TokenType.BLOCK_END)
        return root

    def _is_function_modifier(self, value: str) -> bool:
        """Check if a keyword is a function modifier"""
        return value in ('undefined', 'open', 'meta', 'super', 'closed', 'private', 'virtual', 'sqlbased')

    def _is_type_keyword(self, value: str) -> bool:
        """Check if a keyword is a type declaration"""
        return value in ('int', 'string', 'float', 'bool', 'void', 'json', 'array', 'vector', 'stack',
                        'dynamic', 'datastruct', 'dataspace', 'shuffled', 'iterator', 'combo', 'structure')

    def _looks_like_function_declaration(self) -> bool:
        """Check if current position looks like a C-style function declaration.

        Patterns:
        - int funcName(...)
        - undefined int funcName(...)
        - vector<string> funcName(...)
        - undefined void funcName(...)
        """
        saved_pos = self.pos

        # Skip modifiers (undefined, open, meta, super, closed, private, virtual)
        while self._check(TokenType.KEYWORD) and self._is_function_modifier(self._current().value):
            self._advance()

        # Check for type keyword
        if self._check(TokenType.KEYWORD) and self._is_type_keyword(self._current().value):
            self._advance()

            # Skip generic type parameters <T>
            if self._check(TokenType.COMPARE_LT):
                depth = 1
                self._advance()
                while depth > 0 and not self._is_at_end():
                    if self._check(TokenType.COMPARE_LT):
                        depth += 1
                    elif self._check(TokenType.COMPARE_GT):
                        depth -= 1
                    self._advance()

            # Check for identifier followed by (
            if self._check(TokenType.IDENTIFIER):
                self._advance()
                is_func = self._check(TokenType.PAREN_START)
                self.pos = saved_pos
                return is_func

        self.pos = saved_pos
        return False

    def _parse_typed_function(self) -> ASTNode:
        """Parse C-style typed function declaration.

        Patterns:
        - int Add(int a, int b) { }
        - undefined int Func() { }
        - open void Handler(open Params) { }
        - vector<string> GetNames() { }
        """
        modifiers = []
        return_type = None
        generic_type = None

        # Collect modifiers (undefined, open, meta, super, closed, private, virtual)
        while self._check(TokenType.KEYWORD) and self._is_function_modifier(self._current().value):
            modifiers.append(self._advance().value)

        # Get return type
        if self._check(TokenType.KEYWORD) and self._is_type_keyword(self._current().value):
            return_type = self._advance().value

            # Check for generic type <T>
            if self._check(TokenType.COMPARE_LT):
                self._advance()  # skip <
                generic_parts = []
                depth = 1
                while depth > 0 and not self._is_at_end():
                    if self._check(TokenType.COMPARE_LT):
                        depth += 1
                        generic_parts.append('<')
                    elif self._check(TokenType.COMPARE_GT):
                        depth -= 1
                        if depth > 0:
                            generic_parts.append('>')
                    elif self._check(TokenType.COMMA):
                        generic_parts.append(',')
                    else:
                        generic_parts.append(self._current().value)
                    self._advance()
                generic_type = ''.join(generic_parts)

        # Get function name
        name = self._advance().value

        # Parse parameters
        params = []
        self._expect(TokenType.PAREN_START)

        while not self._check(TokenType.PAREN_END) and not self._is_at_end():
            param_info = {}

            # Handle 'open' keyword for open parameters
            if self._match_keyword('open'):
                param_info['open'] = True

            # Handle type annotations
            if self._check(TokenType.KEYWORD) and self._is_type_keyword(self._current().value):
                param_info['type'] = self._advance().value

                # Check for generic type parameter <T>
                if self._check(TokenType.COMPARE_LT):
                    self._advance()
                    generic_parts = []
                    depth = 1
                    while depth > 0 and not self._is_at_end():
                        if self._check(TokenType.COMPARE_LT):
                            depth += 1
                            generic_parts.append('<')
                        elif self._check(TokenType.COMPARE_GT):
                            depth -= 1
                            if depth > 0:
                                generic_parts.append('>')
                        elif self._check(TokenType.COMMA):
                            generic_parts.append(',')
                        else:
                            generic_parts.append(self._current().value)
                        self._advance()
                    param_info['generic'] = ''.join(generic_parts)

            # Handle reference operator &
            if self._match(TokenType.AMPERSAND):
                param_info['ref'] = True

            # Get parameter name
            if self._check(TokenType.IDENTIFIER):
                param_name = self._advance().value
                if param_info:
                    params.append({'name': param_name, **param_info})
                else:
                    params.append(param_name)
            elif self._check(TokenType.KEYWORD):
                # Parameter name could be a keyword
                param_name = self._advance().value
                if param_info:
                    params.append({'name': param_name, **param_info})
                else:
                    params.append(param_name)

            self._match(TokenType.COMMA)

        self._expect(TokenType.PAREN_END)

        # Parse function body
        node = ASTNode('function', value={
            'name': name,
            'params': params,
            'return_type': return_type,
            'generic_type': generic_type,
            'modifiers': modifiers
        }, children=[])

        self._expect(TokenType.BLOCK_START)

        while not self._check(TokenType.BLOCK_END) and not self._is_at_end():
            stmt = self._parse_statement()
            if stmt:
                node.children.append(stmt)

        self._expect(TokenType.BLOCK_END)
        return node

    def parse_program(self) -> ASTNode:
        """Parse a standalone program (no service wrapper)"""
        root = ASTNode('program', children=[])

        while not self._is_at_end():
            if self._match_keyword('struct'):
                root.children.append(self._parse_struct())
            elif self._match_keyword('define'):
                root.children.append(self._parse_define())
            # Check for C-style typed function declarations
            elif self._looks_like_function_declaration():
                root.children.append(self._parse_typed_function())
            # Handle service blocks
            elif self._match_keyword('service-init'):
                root.children.append(self._parse_service_init())
            elif self._match_keyword('service-include'):
                root.children.append(self._parse_service_include())
            elif self._match_keyword('service-run'):
                root.children.append(self._parse_service_run())
            elif self._match_keyword('package'):
                root.children.append(self._parse_package())
            elif self._match_keyword('package-includes'):
                root.children.append(self._parse_package_includes())
            # Handle global declarations
            elif self._match_keyword('global'):
                stmt = self._parse_expression_statement()
                if stmt:
                    root.children.append(stmt)
            elif self._check(TokenType.GLOBAL_REF):
                stmt = self._parse_expression_statement()
                if stmt:
                    root.children.append(stmt)
            # Handle statements
            elif self._check(TokenType.IDENTIFIER) or self._check(TokenType.AT) or self._check(TokenType.SELF_REF):
                stmt = self._parse_expression_statement()
                if stmt:
                    root.children.append(stmt)
            elif self._match_keyword('if'):
                root.children.append(self._parse_if())
            elif self._match_keyword('while'):
                root.children.append(self._parse_while())
            elif self._match_keyword('for'):
                root.children.append(self._parse_for())
            elif self._match_keyword('foreach'):
                root.children.append(self._parse_foreach())
            # Skip comments and newlines
            elif self._check(TokenType.COMMENT) or self._check(TokenType.NEWLINE):
                self._advance()
            else:
                self._advance()

        return root

    def _current(self) -> Token:
        if self.pos < len(self.tokens):
            return self.tokens[self.pos]
        return Token(TokenType.EOF, '', 0, 0)

    def _peek(self, offset=0) -> Token:
        pos = self.pos + offset
        if pos < len(self.tokens):
            return self.tokens[pos]
        return Token(TokenType.EOF, '', 0, 0)

    def _advance(self) -> Token:
        token = self._current()
        self.pos += 1
        return token

    def _is_at_end(self) -> bool:
        return self._current().type == TokenType.EOF

    def _check(self, token_type: TokenType) -> bool:
        return self._current().type == token_type

    def _match(self, token_type: TokenType) -> bool:
        if self._check(token_type):
            self._advance()
            return True
        return False

    def _match_keyword(self, keyword: str) -> bool:
        if self._current().type == TokenType.KEYWORD and self._current().value == keyword:
            self._advance()
            return True
        return False

    def _expect(self, token_type: TokenType, message: str = None):
        if not self._match(token_type):
            msg = message or f"Expected {token_type.name}, got {self._current().type.name}"
            self.error(msg)
        return self.tokens[self.pos - 1]

    def _parse_service_init(self) -> ASTNode:
        node = ASTNode('service-init', children=[])
        self._expect(TokenType.BLOCK_START)

        while not self._check(TokenType.BLOCK_END) and not self._is_at_end():
            if self._check(TokenType.IDENTIFIER) or self._check(TokenType.KEYWORD):
                key = self._advance().value
                self._expect(TokenType.COLON)
                value = self._parse_value()
                node.children.append(ASTNode('property', value={'key': key, 'value': value}))
                self._match(TokenType.SEMICOLON)
            else:
                self._advance()

        self._expect(TokenType.BLOCK_END)
        return node

    def _parse_service_include(self) -> ASTNode:
        """Parse service-include block for importing modules and files

        Syntax:
        service-include {
            @KernelClient <== get(include(cso_root('/root32/etc/tasks/kernel.cssl')));
            @Time <== get('time');
            @Secrets <== get('secrets');
        }
        """
        node = ASTNode('service-include', children=[])
        self._expect(TokenType.BLOCK_START)

        while not self._check(TokenType.BLOCK_END) and not self._is_at_end():
            # Parse module injection statements like @ModuleName <== get(...);
            if self._check(TokenType.AT):
                stmt = self._parse_expression_statement()
                if stmt:
                    node.children.append(stmt)
            elif self._check(TokenType.IDENTIFIER):
                # Also support identifier-based assignments: moduleName <== get(...);
                stmt = self._parse_expression_statement()
                if stmt:
                    node.children.append(stmt)
            else:
                self._advance()

        self._expect(TokenType.BLOCK_END)
        return node

    def _parse_service_run(self) -> ASTNode:
        node = ASTNode('service-run', children=[])
        self._expect(TokenType.BLOCK_START)

        while not self._check(TokenType.BLOCK_END) and not self._is_at_end():
            if self._match_keyword('struct'):
                node.children.append(self._parse_struct())
            elif self._match_keyword('define'):
                node.children.append(self._parse_define())
            else:
                self._advance()

        self._expect(TokenType.BLOCK_END)
        return node

    def _parse_package(self) -> ASTNode:
        """Parse package {} block for service metadata - NEW

        Syntax:
        package {
            service = "ServiceName";
            exec = @Start();
            version = "1.0.0";
            description = "Beschreibung";
        }
        """
        node = ASTNode('package', children=[])
        self._expect(TokenType.BLOCK_START)

        while not self._check(TokenType.BLOCK_END) and not self._is_at_end():
            if self._check(TokenType.IDENTIFIER) or self._check(TokenType.KEYWORD):
                key = self._advance().value
                self._expect(TokenType.EQUALS)
                value = self._parse_expression()
                node.children.append(ASTNode('package_property', value={'key': key, 'value': value}))
                self._match(TokenType.SEMICOLON)
            else:
                self._advance()

        self._expect(TokenType.BLOCK_END)
        return node

    def _parse_package_includes(self) -> ASTNode:
        """Parse package-includes {} block for imports - NEW

        Syntax:
        package-includes {
            @Lists = get('list');
            @OS = get('os');
            @Time = get('time');
            @VSRam = get('vsramsdk');
        }
        """
        node = ASTNode('package-includes', children=[])
        self._expect(TokenType.BLOCK_START)

        while not self._check(TokenType.BLOCK_END) and not self._is_at_end():
            # Parse module injection statements like @ModuleName = get(...);
            if self._check(TokenType.AT):
                stmt = self._parse_expression_statement()
                if stmt:
                    node.children.append(stmt)
            elif self._check(TokenType.IDENTIFIER):
                # Also support identifier-based assignments
                stmt = self._parse_expression_statement()
                if stmt:
                    node.children.append(stmt)
            else:
                self._advance()

        self._expect(TokenType.BLOCK_END)
        return node

    def _parse_struct(self) -> ASTNode:
        name = self._advance().value
        is_global = False

        # Check for (@) decorator: struct Name(@) { ... }
        if self._match(TokenType.PAREN_START):
            if self._check(TokenType.AT):
                self._advance()  # skip @
                is_global = True
            self._expect(TokenType.PAREN_END)

        node = ASTNode('struct', value={'name': name, 'global': is_global}, children=[])
        self._expect(TokenType.BLOCK_START)

        while not self._check(TokenType.BLOCK_END) and not self._is_at_end():
            if self._match_keyword('define'):
                node.children.append(self._parse_define())
            elif self._check(TokenType.IDENTIFIER):
                # Look ahead to determine what kind of statement this is
                saved_pos = self.pos
                var_name = self._advance().value

                if self._match(TokenType.INJECT_LEFT):
                    # Injection: var <== expr
                    value = self._parse_expression()
                    node.children.append(ASTNode('injection', value={'name': var_name, 'source': value}))
                    self._match(TokenType.SEMICOLON)
                elif self._match(TokenType.EQUALS):
                    # Assignment: var = expr
                    value = self._parse_expression()
                    node.children.append(ASTNode('assignment', value={'name': var_name, 'value': value}))
                    self._match(TokenType.SEMICOLON)
                elif self._check(TokenType.PAREN_START):
                    # Function call: func(args)
                    self.pos = saved_pos  # Go back to parse full expression
                    stmt = self._parse_expression_statement()
                    if stmt:
                        node.children.append(stmt)
                elif self._match(TokenType.DOT):
                    # Method call: obj.method(args)
                    self.pos = saved_pos  # Go back to parse full expression
                    stmt = self._parse_expression_statement()
                    if stmt:
                        node.children.append(stmt)
                else:
                    self._match(TokenType.SEMICOLON)
            elif self._check(TokenType.AT):
                # Module reference statement
                stmt = self._parse_expression_statement()
                if stmt:
                    node.children.append(stmt)
            else:
                self._advance()

        self._expect(TokenType.BLOCK_END)
        return node

    def _parse_define(self) -> ASTNode:
        name = self._advance().value
        params = []

        if self._match(TokenType.PAREN_START):
            while not self._check(TokenType.PAREN_END):
                param_info = {}
                # Handle 'open' keyword for open parameters
                if self._match_keyword('open'):
                    param_info['open'] = True
                # Handle type annotations (e.g., string, int, dynamic, etc.)
                if self._check(TokenType.KEYWORD):
                    param_info['type'] = self._advance().value
                # Handle reference operator &
                if self._match(TokenType.AMPERSAND):
                    param_info['ref'] = True
                # Get parameter name
                if self._check(TokenType.IDENTIFIER):
                    param_name = self._advance().value
                    if param_info:
                        params.append({'name': param_name, **param_info})
                    else:
                        params.append(param_name)
                    self._match(TokenType.COMMA)
                elif self._check(TokenType.KEYWORD):
                    # Parameter name could be a keyword like 'Params'
                    param_name = self._advance().value
                    if param_info:
                        params.append({'name': param_name, **param_info})
                    else:
                        params.append(param_name)
                    self._match(TokenType.COMMA)
                else:
                    break
            self._expect(TokenType.PAREN_END)

        node = ASTNode('function', value={'name': name, 'params': params}, children=[])
        self._expect(TokenType.BLOCK_START)

        while not self._check(TokenType.BLOCK_END) and not self._is_at_end():
            stmt = self._parse_statement()
            if stmt:
                node.children.append(stmt)

        self._expect(TokenType.BLOCK_END)
        return node

    def _parse_statement(self) -> Optional[ASTNode]:
        if self._match_keyword('if'):
            return self._parse_if()
        elif self._match_keyword('while'):
            return self._parse_while()
        elif self._match_keyword('for'):
            return self._parse_for()
        elif self._match_keyword('foreach'):
            return self._parse_foreach()
        elif self._match_keyword('switch'):
            return self._parse_switch()
        elif self._match_keyword('return'):
            return self._parse_return()
        elif self._match_keyword('break'):
            self._match(TokenType.SEMICOLON)
            return ASTNode('break')
        elif self._match_keyword('continue'):
            self._match(TokenType.SEMICOLON)
            return ASTNode('continue')
        elif self._match_keyword('try'):
            return self._parse_try()
        elif self._match_keyword('await'):
            return self._parse_await()
        elif self._check(TokenType.IDENTIFIER) or self._check(TokenType.AT):
            return self._parse_expression_statement()
        else:
            self._advance()
            return None

    def _parse_if(self) -> ASTNode:
        self._expect(TokenType.PAREN_START)
        condition = self._parse_expression()
        self._expect(TokenType.PAREN_END)

        node = ASTNode('if', value={'condition': condition}, children=[])

        self._expect(TokenType.BLOCK_START)
        then_block = ASTNode('then', children=[])
        while not self._check(TokenType.BLOCK_END) and not self._is_at_end():
            stmt = self._parse_statement()
            if stmt:
                then_block.children.append(stmt)
        self._expect(TokenType.BLOCK_END)
        node.children.append(then_block)

        if self._match_keyword('else'):
            else_block = ASTNode('else', children=[])
            if self._match_keyword('if'):
                else_block.children.append(self._parse_if())
            else:
                self._expect(TokenType.BLOCK_START)
                while not self._check(TokenType.BLOCK_END) and not self._is_at_end():
                    stmt = self._parse_statement()
                    if stmt:
                        else_block.children.append(stmt)
                self._expect(TokenType.BLOCK_END)
            node.children.append(else_block)

        return node

    def _parse_while(self) -> ASTNode:
        self._expect(TokenType.PAREN_START)
        condition = self._parse_expression()
        self._expect(TokenType.PAREN_END)

        node = ASTNode('while', value={'condition': condition}, children=[])
        self._expect(TokenType.BLOCK_START)

        while not self._check(TokenType.BLOCK_END) and not self._is_at_end():
            stmt = self._parse_statement()
            if stmt:
                node.children.append(stmt)

        self._expect(TokenType.BLOCK_END)
        return node

    def _parse_for(self) -> ASTNode:
        self._expect(TokenType.PAREN_START)
        var_name = self._advance().value
        self._expect(TokenType.KEYWORD)
        self._expect(TokenType.KEYWORD)
        self._expect(TokenType.PAREN_START)
        start = self._parse_expression()
        self._expect(TokenType.COMMA)
        end = self._parse_expression()
        self._expect(TokenType.PAREN_END)
        self._expect(TokenType.PAREN_END)

        node = ASTNode('for', value={'var': var_name, 'start': start, 'end': end}, children=[])
        self._expect(TokenType.BLOCK_START)

        while not self._check(TokenType.BLOCK_END) and not self._is_at_end():
            stmt = self._parse_statement()
            if stmt:
                node.children.append(stmt)

        self._expect(TokenType.BLOCK_END)
        return node

    def _parse_foreach(self) -> ASTNode:
        """Parse foreach loop - supports both syntaxes:

        Traditional: foreach (var in iterable) { }
        New 'as' syntax: foreach iterable as var { }
        """
        # Check if this is the new 'as' syntax or traditional syntax
        if self._check(TokenType.PAREN_START):
            # Traditional syntax: foreach (var in iterable) { }
            self._expect(TokenType.PAREN_START)
            var_name = self._advance().value
            self._match_keyword('in')
            iterable = self._parse_expression()
            self._expect(TokenType.PAREN_END)
        else:
            # NEW: 'as' syntax: foreach iterable as var { }
            iterable = self._parse_expression()
            if self._check(TokenType.AS):
                self._advance()  # consume 'as'
            else:
                self._match_keyword('as')  # try keyword match as fallback
            var_name = self._advance().value

        node = ASTNode('foreach', value={'var': var_name, 'iterable': iterable}, children=[])
        self._expect(TokenType.BLOCK_START)

        while not self._check(TokenType.BLOCK_END) and not self._is_at_end():
            stmt = self._parse_statement()
            if stmt:
                node.children.append(stmt)

        self._expect(TokenType.BLOCK_END)
        return node

    def _parse_switch(self) -> ASTNode:
        self._expect(TokenType.PAREN_START)
        value = self._parse_expression()
        self._expect(TokenType.PAREN_END)

        node = ASTNode('switch', value={'value': value}, children=[])
        self._expect(TokenType.BLOCK_START)

        while not self._check(TokenType.BLOCK_END) and not self._is_at_end():
            if self._match_keyword('case'):
                case_value = self._parse_expression()
                self._expect(TokenType.COLON)
                case_node = ASTNode('case', value={'value': case_value}, children=[])

                while not self._check_keyword('case') and not self._check_keyword('default') and not self._check(TokenType.BLOCK_END):
                    stmt = self._parse_statement()
                    if stmt:
                        case_node.children.append(stmt)
                    if self._check_keyword('break'):
                        break

                node.children.append(case_node)
            elif self._match_keyword('default'):
                self._expect(TokenType.COLON)
                default_node = ASTNode('default', children=[])

                while not self._check(TokenType.BLOCK_END):
                    stmt = self._parse_statement()
                    if stmt:
                        default_node.children.append(stmt)

                node.children.append(default_node)
            else:
                self._advance()

        self._expect(TokenType.BLOCK_END)
        return node

    def _parse_return(self) -> ASTNode:
        value = None
        if not self._check(TokenType.SEMICOLON) and not self._check(TokenType.BLOCK_END):
            value = self._parse_expression()
        self._match(TokenType.SEMICOLON)
        return ASTNode('return', value=value)

    def _parse_try(self) -> ASTNode:
        node = ASTNode('try', children=[])

        try_block = ASTNode('try-block', children=[])
        self._expect(TokenType.BLOCK_START)
        while not self._check(TokenType.BLOCK_END) and not self._is_at_end():
            stmt = self._parse_statement()
            if stmt:
                try_block.children.append(stmt)
        self._expect(TokenType.BLOCK_END)
        node.children.append(try_block)

        if self._match_keyword('catch'):
            error_var = None
            if self._match(TokenType.PAREN_START):
                error_var = self._advance().value
                self._expect(TokenType.PAREN_END)

            catch_block = ASTNode('catch-block', value={'error_var': error_var}, children=[])
            self._expect(TokenType.BLOCK_START)
            while not self._check(TokenType.BLOCK_END) and not self._is_at_end():
                stmt = self._parse_statement()
                if stmt:
                    catch_block.children.append(stmt)
            self._expect(TokenType.BLOCK_END)
            node.children.append(catch_block)

        return node

    def _parse_await(self) -> ASTNode:
        """Parse await statement: await expression;"""
        expr = self._parse_expression()
        self._match(TokenType.SEMICOLON)
        return ASTNode('await', value=expr)

    def _parse_action_block(self) -> ASTNode:
        """Parse an action block { ... } containing statements for createcmd"""
        node = ASTNode('action_block', children=[])
        self._expect(TokenType.BLOCK_START)

        while not self._check(TokenType.BLOCK_END) and not self._is_at_end():
            # Check for define statements inside action block
            if self._match_keyword('define'):
                node.children.append(self._parse_define())
            else:
                stmt = self._parse_statement()
                if stmt:
                    node.children.append(stmt)

        self._expect(TokenType.BLOCK_END)
        return node

    def _parse_injection_filter(self) -> Optional[dict]:
        """Parse injection filter: [type::helper=value]"""
        if not self._match(TokenType.BRACKET_START):
            return None

        filter_info = {}
        # Parse type::helper=value patterns
        while not self._check(TokenType.BRACKET_END) and not self._is_at_end():
            if self._check(TokenType.IDENTIFIER) or self._check(TokenType.KEYWORD):
                filter_type = self._advance().value
                if self._match(TokenType.DOUBLE_COLON):
                    helper = self._advance().value
                    if self._match(TokenType.EQUALS):
                        value = self._parse_expression()
                        filter_info[f'{filter_type}::{helper}'] = value
                    else:
                        filter_info[f'{filter_type}::{helper}'] = True
                else:
                    filter_info['type'] = filter_type
            elif self._check(TokenType.COMMA):
                self._advance()
            else:
                break

        self._expect(TokenType.BRACKET_END)
        return filter_info if filter_info else None

    def _parse_expression_statement(self) -> Optional[ASTNode]:
        expr = self._parse_expression()

        # === BASIC INJECTION: <== (replace target with source) ===
        if self._match(TokenType.INJECT_LEFT):
            # Check if this is a createcmd injection with a code block
            is_createcmd = (
                expr.type == 'call' and
                expr.value.get('callee') and
                expr.value.get('callee').type == 'identifier' and
                expr.value.get('callee').value == 'createcmd'
            )

            if is_createcmd and self._check(TokenType.BLOCK_START):
                action_block = self._parse_action_block()
                self._match(TokenType.SEMICOLON)
                return ASTNode('createcmd_inject', value={'command_call': expr, 'action': action_block})
            else:
                # Check for injection filter [type::helper=value]
                filter_info = self._parse_injection_filter()
                source = self._parse_expression()
                self._match(TokenType.SEMICOLON)
                return ASTNode('inject', value={'target': expr, 'source': source, 'mode': 'replace', 'filter': filter_info})

        # === PLUS INJECTION: +<== (copy & add to target) ===
        if self._match(TokenType.INJECT_PLUS_LEFT):
            filter_info = self._parse_injection_filter()
            source = self._parse_expression()
            self._match(TokenType.SEMICOLON)
            return ASTNode('inject', value={'target': expr, 'source': source, 'mode': 'add', 'filter': filter_info})

        # === MINUS INJECTION: -<== (move & remove from source) ===
        if self._match(TokenType.INJECT_MINUS_LEFT):
            filter_info = self._parse_injection_filter()
            source = self._parse_expression()
            self._match(TokenType.SEMICOLON)
            return ASTNode('inject', value={'target': expr, 'source': source, 'mode': 'move', 'filter': filter_info})

        # === CODE INFUSION: <<== (inject code into function) ===
        if self._match(TokenType.INFUSE_LEFT):
            if self._check(TokenType.BLOCK_START):
                code_block = self._parse_action_block()
                self._match(TokenType.SEMICOLON)
                return ASTNode('infuse', value={'target': expr, 'code': code_block, 'mode': 'replace'})
            else:
                source = self._parse_expression()
                self._match(TokenType.SEMICOLON)
                return ASTNode('infuse', value={'target': expr, 'source': source, 'mode': 'replace'})

        # === CODE INFUSION PLUS: +<<== (add code to function) ===
        if self._match(TokenType.INFUSE_PLUS_LEFT):
            if self._check(TokenType.BLOCK_START):
                code_block = self._parse_action_block()
                self._match(TokenType.SEMICOLON)
                return ASTNode('infuse', value={'target': expr, 'code': code_block, 'mode': 'add'})
            else:
                source = self._parse_expression()
                self._match(TokenType.SEMICOLON)
                return ASTNode('infuse', value={'target': expr, 'source': source, 'mode': 'add'})

        # === CODE INFUSION MINUS: -<<== (remove code from function) ===
        if self._match(TokenType.INFUSE_MINUS_LEFT):
            if self._check(TokenType.BLOCK_START):
                code_block = self._parse_action_block()
                self._match(TokenType.SEMICOLON)
                return ASTNode('infuse', value={'target': expr, 'code': code_block, 'mode': 'remove'})
            else:
                source = self._parse_expression()
                self._match(TokenType.SEMICOLON)
                return ASTNode('infuse', value={'target': expr, 'source': source, 'mode': 'remove'})

        # === RIGHT-SIDE OPERATORS ===

        # === BASIC RECEIVE: ==> (move source to target) ===
        if self._match(TokenType.INJECT_RIGHT):
            filter_info = self._parse_injection_filter()
            target = self._parse_expression()
            self._match(TokenType.SEMICOLON)
            return ASTNode('receive', value={'source': expr, 'target': target, 'mode': 'replace', 'filter': filter_info})

        # === PLUS RECEIVE: ==>+ (copy source to target) ===
        if self._match(TokenType.INJECT_PLUS_RIGHT):
            filter_info = self._parse_injection_filter()
            target = self._parse_expression()
            self._match(TokenType.SEMICOLON)
            return ASTNode('receive', value={'source': expr, 'target': target, 'mode': 'add', 'filter': filter_info})

        # === MINUS RECEIVE: -==> (move & remove from source) ===
        if self._match(TokenType.INJECT_MINUS_RIGHT):
            filter_info = self._parse_injection_filter()
            target = self._parse_expression()
            self._match(TokenType.SEMICOLON)
            return ASTNode('receive', value={'source': expr, 'target': target, 'mode': 'move', 'filter': filter_info})

        # === CODE INFUSION RIGHT: ==>> ===
        if self._match(TokenType.INFUSE_RIGHT):
            target = self._parse_expression()
            self._match(TokenType.SEMICOLON)
            return ASTNode('infuse_right', value={'source': expr, 'target': target, 'mode': 'replace'})

        # === FLOW OPERATORS ===
        if self._match(TokenType.FLOW_RIGHT):
            target = self._parse_expression()
            self._match(TokenType.SEMICOLON)
            return ASTNode('flow', value={'source': expr, 'target': target})

        if self._match(TokenType.FLOW_LEFT):
            source = self._parse_expression()
            self._match(TokenType.SEMICOLON)
            return ASTNode('flow', value={'source': source, 'target': expr})

        # === BASIC ASSIGNMENT ===
        if self._match(TokenType.EQUALS):
            value = self._parse_expression()
            self._match(TokenType.SEMICOLON)
            return ASTNode('assignment', value={'target': expr, 'value': value})

        self._match(TokenType.SEMICOLON)
        return ASTNode('expression', value=expr)

    def _parse_expression(self) -> ASTNode:
        return self._parse_or()

    def _parse_or(self) -> ASTNode:
        left = self._parse_and()

        while self._match(TokenType.OR) or self._match_keyword('or'):
            right = self._parse_and()
            left = ASTNode('binary', value={'op': 'or', 'left': left, 'right': right})

        return left

    def _parse_and(self) -> ASTNode:
        left = self._parse_comparison()

        while self._match(TokenType.AND) or self._match_keyword('and'):
            right = self._parse_comparison()
            left = ASTNode('binary', value={'op': 'and', 'left': left, 'right': right})

        return left

    def _parse_comparison(self) -> ASTNode:
        left = self._parse_term()

        while True:
            if self._match(TokenType.COMPARE_EQ):
                right = self._parse_term()
                left = ASTNode('binary', value={'op': '==', 'left': left, 'right': right})
            elif self._match(TokenType.COMPARE_NE):
                right = self._parse_term()
                left = ASTNode('binary', value={'op': '!=', 'left': left, 'right': right})
            elif self._match(TokenType.COMPARE_LT):
                right = self._parse_term()
                left = ASTNode('binary', value={'op': '<', 'left': left, 'right': right})
            elif self._match(TokenType.COMPARE_GT):
                right = self._parse_term()
                left = ASTNode('binary', value={'op': '>', 'left': left, 'right': right})
            elif self._match(TokenType.COMPARE_LE):
                right = self._parse_term()
                left = ASTNode('binary', value={'op': '<=', 'left': left, 'right': right})
            elif self._match(TokenType.COMPARE_GE):
                right = self._parse_term()
                left = ASTNode('binary', value={'op': '>=', 'left': left, 'right': right})
            else:
                break

        return left

    def _parse_term(self) -> ASTNode:
        left = self._parse_factor()

        while True:
            if self._match(TokenType.PLUS):
                right = self._parse_factor()
                left = ASTNode('binary', value={'op': '+', 'left': left, 'right': right})
            elif self._match(TokenType.MINUS):
                right = self._parse_factor()
                left = ASTNode('binary', value={'op': '-', 'left': left, 'right': right})
            else:
                break

        return left

    def _parse_factor(self) -> ASTNode:
        left = self._parse_unary()

        while True:
            if self._match(TokenType.MULTIPLY):
                right = self._parse_unary()
                left = ASTNode('binary', value={'op': '*', 'left': left, 'right': right})
            elif self._match(TokenType.DIVIDE):
                right = self._parse_unary()
                left = ASTNode('binary', value={'op': '/', 'left': left, 'right': right})
            elif self._match(TokenType.MODULO):
                right = self._parse_unary()
                left = ASTNode('binary', value={'op': '%', 'left': left, 'right': right})
            else:
                break

        return left

    def _parse_unary(self) -> ASTNode:
        if self._match(TokenType.NOT) or self._match_keyword('not'):
            operand = self._parse_unary()
            return ASTNode('unary', value={'op': 'not', 'operand': operand})
        if self._match(TokenType.MINUS):
            operand = self._parse_unary()
            return ASTNode('unary', value={'op': '-', 'operand': operand})

        return self._parse_primary()

    def _parse_primary(self) -> ASTNode:
        if self._match(TokenType.AT):
            return self._parse_module_reference()

        if self._check(TokenType.SELF_REF):
            # s@<name> self-reference to global struct
            token = self._advance()
            node = ASTNode('self_ref', value=token.value, line=token.line, column=token.column)
            # Check for function call: s@Backend.Loop.Start()
            if self._match(TokenType.PAREN_START):
                args = []
                while not self._check(TokenType.PAREN_END):
                    args.append(self._parse_expression())
                    if not self._check(TokenType.PAREN_END):
                        self._expect(TokenType.COMMA)
                self._expect(TokenType.PAREN_END)
                node = ASTNode('call', value={'callee': node, 'args': args})
            return node

        if self._check(TokenType.NUMBER):
            return ASTNode('literal', value=self._advance().value)

        if self._check(TokenType.STRING):
            return ASTNode('literal', value=self._advance().value)

        if self._check(TokenType.BOOLEAN):
            return ASTNode('literal', value=self._advance().value)

        if self._check(TokenType.NULL):
            self._advance()
            return ASTNode('literal', value=None)

        # NEW: Type literals (list, dict) - create empty instances
        if self._check(TokenType.TYPE_LITERAL):
            type_name = self._advance().value
            return ASTNode('type_literal', value=type_name)

        if self._match(TokenType.PAREN_START):
            expr = self._parse_expression()
            self._expect(TokenType.PAREN_END)
            return expr

        if self._match(TokenType.BLOCK_START):
            return self._parse_object()

        if self._match(TokenType.BRACKET_START):
            return self._parse_array()

        if self._check(TokenType.IDENTIFIER) or self._check(TokenType.KEYWORD):
            return self._parse_identifier_or_call()

        return ASTNode('literal', value=None)

    def _parse_module_reference(self) -> ASTNode:
        parts = []
        parts.append(self._advance().value)

        while self._match(TokenType.DOT):
            parts.append(self._advance().value)

        return ASTNode('module_ref', value='.'.join(parts))

    def _parse_identifier_or_call(self) -> ASTNode:
        name = self._advance().value
        node = ASTNode('identifier', value=name)

        while True:
            if self._match(TokenType.DOT):
                member = self._advance().value
                node = ASTNode('member_access', value={'object': node, 'member': member})
            elif self._match(TokenType.PAREN_START):
                args = []
                while not self._check(TokenType.PAREN_END):
                    args.append(self._parse_expression())
                    if not self._check(TokenType.PAREN_END):
                        self._expect(TokenType.COMMA)
                self._expect(TokenType.PAREN_END)
                node = ASTNode('call', value={'callee': node, 'args': args})
            elif self._match(TokenType.BRACKET_START):
                index = self._parse_expression()
                self._expect(TokenType.BRACKET_END)
                node = ASTNode('index_access', value={'object': node, 'index': index})
            else:
                break

        return node

    def _parse_object(self) -> ASTNode:
        properties = {}

        while not self._check(TokenType.BLOCK_END) and not self._is_at_end():
            if self._check(TokenType.IDENTIFIER) or self._check(TokenType.STRING):
                key = self._advance().value
                self._expect(TokenType.EQUALS)
                value = self._parse_expression()
                properties[key] = value
                self._match(TokenType.SEMICOLON)
                self._match(TokenType.COMMA)
            else:
                self._advance()

        self._expect(TokenType.BLOCK_END)
        return ASTNode('object', value=properties)

    def _parse_array(self) -> ASTNode:
        elements = []

        while not self._check(TokenType.BRACKET_END) and not self._is_at_end():
            elements.append(self._parse_expression())
            if not self._check(TokenType.BRACKET_END):
                self._expect(TokenType.COMMA)

        self._expect(TokenType.BRACKET_END)
        return ASTNode('array', value=elements)

    def _parse_value(self) -> Any:
        if self._check(TokenType.STRING):
            return self._advance().value
        if self._check(TokenType.NUMBER):
            return self._advance().value
        if self._check(TokenType.BOOLEAN):
            return self._advance().value
        if self._check(TokenType.NULL):
            self._advance()
            return None
        if self._check(TokenType.IDENTIFIER) or self._check(TokenType.KEYWORD):
            return self._advance().value
        return None

    def _check_keyword(self, keyword: str) -> bool:
        return self._current().type == TokenType.KEYWORD and self._current().value == keyword


def parse_cssl(source: str) -> ASTNode:
    """Parse CSSL source code into an AST - auto-detects service vs program format"""
    lexer = CSSLLexer(source)
    tokens = lexer.tokenize()
    parser = CSSLParser(tokens, lexer.source_lines)

    # Auto-detect: if first token is '{', it's a service file
    # Otherwise treat as standalone program (whitespace is already filtered by lexer)
    if tokens and tokens[0].type == TokenType.BLOCK_START:
        return parser.parse()  # Service file format
    else:
        return parser.parse_program()  # Standalone program format


def parse_cssl_program(source: str) -> ASTNode:
    """Parse standalone CSSL program (no service wrapper) into an AST"""
    lexer = CSSLLexer(source)
    tokens = lexer.tokenize()
    parser = CSSLParser(tokens, lexer.source_lines)
    return parser.parse_program()


def tokenize_cssl(source: str) -> List[Token]:
    """Tokenize CSSL source code (useful for syntax highlighting)"""
    lexer = CSSLLexer(source)
    return lexer.tokenize()


# Export public API
__all__ = [
    'TokenType', 'Token', 'ASTNode',
    'CSSLLexer', 'CSSLParser', 'CSSLSyntaxError',
    'parse_cssl', 'parse_cssl_program', 'tokenize_cssl',
    'KEYWORDS', 'TYPE_LITERALS'
]
