// Template-level lexer.
//
// Splits a template string into Text / Variable / Tag tokens. Variable
// expressions (`{{ }}`), block tags (`{% %}`), and comments (`{# #}`) are
// recognized; everything else is literal text. The expression/tag contents are
// returned verbatim (trimmed); parsing them into structured AST happens later.

///|
/// Push the accumulated text buffer as a `Text` token and reset it, unless empty.
fn flush_text(text : StringBuilder, tokens : Array[Token]) -> Unit {
  if !text.is_empty() {
    tokens.push(Text(text.to_string()))
    text.reset()
  }
}

///|
/// Lex a template string into top-level tokens.
pub fn lex(src : String) -> Array[Token] {
  let chars = src.to_array()
  let n = chars.length()
  let tokens : Array[Token] = []
  let mut i = 0
  let text = StringBuilder::new()
  while i < n {
    if chars[i] == '{' && i + 1 < n {
      match chars[i + 1] {
        '{' => {
          // variable: scan until }}
          flush_text(text, tokens)
          let buf = StringBuilder::new()
          i = i + 2
          while i + 1 < n && !(chars[i] == '}' && chars[i + 1] == '}') {
            buf.write_char(chars[i])
            i = i + 1
          }
          tokens.push(Variable(buf.to_string().trim().to_owned()))
          i = i + 2
        }
        '%' => {
          // tag: scan until %}
          flush_text(text, tokens)
          let buf = StringBuilder::new()
          i = i + 2
          while i + 1 < n && !(chars[i] == '%' && chars[i + 1] == '}') {
            buf.write_char(chars[i])
            i = i + 1
          }
          tokens.push(Tag(buf.to_string().trim().to_owned()))
          i = i + 2
        }
        '#' => {
          // comment: skip until #}
          flush_text(text, tokens)
          i = i + 2
          while i + 1 < n && !(chars[i] == '#' && chars[i + 1] == '}') {
            i = i + 1
          }
          i = i + 2
        }
        _ => {
          text.write_char(chars[i])
          i = i + 1
        }
      }
    } else {
      text.write_char(chars[i])
      i = i + 1
    }
  }
  flush_text(text, tokens)
  tokens
}