pub fn expand_builtin(name : String, args : String) -> String? {
  match name {
    "assert" => Some("if !(" + args + ") { println(\"assertion failed: " + args + "\"); abort(\"\") }")
    "stringify" => Some("\"" + args + "\"")
    "matches" => {
      let split = split_at_top_level_comma(args)
      match split {
        Some((expr, pat)) => Some("match " + expr + " { " + pat + " => true; _ => false }")
        None => None
      }
    }
    "todo" => {
      let msg = args.trim().to_owned()
      if msg.length() > 0 { Some("println(" + msg + "); abort(\"\")") }
      else { Some("println(\"not implemented\"); abort(\"\")") }
    }
    "dbg" => Some("{ let __dbg_val = " + args + "; println(\"[dbg] \" + stringify!(" + args + ") + \" = \" + __dbg_val.to_string()); __dbg_val }")
    "let_array" => expand_let_array(args)
    "call_each" => expand_call_each(args)
    "repeat" => expand_repeat(args)
    "inspect" => {
      match split_at_top_level_comma(args) {
        Some((expr, expected)) => {
          Some("@debug.debug_inspect(" + expr + ", content=" + expected.trim().to_owned() + ")")
        }
        None => None
      }
    }
    "echo" => expand_echo(args)
    "logger" => expand_logger(args)
    "timeit" => expand_timeit(args)
    "tryn" => expand_tryn(args)
    _ => None
  }
}

fn expand_let_array(args : String) -> String? {
  let eq_pos = find_top_level_eq(args)
  if eq_pos < 0 { return None }
  let vars_part = args[0:eq_pos].trim().to_owned()
  let rest = args[eq_pos + 1:].trim().to_owned()
  if vars_part.length() == 0 || rest.length() == 0 { return None }

  let start = 0
  let expr = rest
  let comma_start = find_last_top_level_prefix(rest, ", start:")
  let actual_start = if comma_start >= 0 {
    let start_str = rest[comma_start + ", start:".length():].trim().to_owned()
    parse_int_safe(start_str)
  } else { start }
  let actual_expr = if comma_start >= 0 {
    rest[0:comma_start].trim().to_owned()
  } else { expr }

  let vars : Array[String] = []
  let is_mut : Array[Bool] = []
  let mut pos = 0
  let vlen = vars_part.length()
  while pos < vlen {
    let trimmed_start = skip_ws_from(vars_part, pos)
    if trimmed_start >= vlen { break }
    let comma_pos = find_top_level_comma(vars_part, trimmed_start)
    let raw = if comma_pos < 0 {
      vars_part[trimmed_start:].trim().to_owned()
    } else {
      vars_part[trimmed_start:comma_pos].trim().to_owned()
    }
    if raw.length() > 0 {
      if raw.has_prefix("mut ") {
        vars.push(raw[4:].trim().to_owned())
        is_mut.push(true)
      } else {
        vars.push(raw)
        is_mut.push(false)
      }
    }
    pos = if comma_pos < 0 { vlen } else { comma_pos + 1 }
  }

  if vars.length() == 0 { return None }

  let result = StringBuilder()
  for i in 0.. 0 { result.write_string("; ") }
    if is_mut[i] { result.write_string("let mut ") } else { result.write_string("let ") }
    result.write_string(vars[i])
    result.write_string(" = ")
    result.write_string(actual_expr)
    result.write_string("[")
    result.write_string((actual_start + i).to_string())
    result.write_string("]")
  }
  Some(result.to_string())
}

fn parse_int_safe(s : String) -> Int {
  let mut result = 0
  let mut i = 0
  let len = s.length()
  while i < len {
    let c = s[i]
    if c >= ('0' : UInt16) && c <= ('9' : UInt16) {
      result = result * 10 + (c.to_int() - ('0' : UInt16).to_int())
    } else { break }
    i = i + 1
  }
  result
}

fn find_top_level_eq(s : String) -> Int {
  let mut depth = 0
  let mut pos = 0
  let len = s.length()
  while pos < len {
    let c = s[pos]
    if c == ('(' : UInt16) || c == ('<' : UInt16) || c == ('[' : UInt16) || c == ('{' : UInt16) {
      depth = depth + 1
    } else if c == (')' : UInt16) || c == ('>' : UInt16) || c == (']' : UInt16) || c == ('}' : UInt16) {
      depth = depth - 1
    } else if c == ('=' : UInt16) && depth == 0 {
      return pos
    }
    pos = pos + 1
  }
  -1
}

fn find_last_top_level_prefix(s : String, prefix : String) -> Int {
  let mut depth = 0
  let mut pos = s.length() - 1
  let plen = prefix.length()
  while pos >= 0 {
    let c = s[pos]
    if c == (')' : UInt16) || c == ('>' : UInt16) || c == (']' : UInt16) || c == ('}' : UInt16) {
      depth = depth + 1
    } else if c == ('(' : UInt16) || c == ('<' : UInt16) || c == ('[' : UInt16) || c == ('{' : UInt16) {
      depth = depth - 1
    } else if depth == 0 && pos + plen <= s.length() && s[pos:pos + plen] == prefix {
      return pos
    }
    pos = pos - 1
  }
  -1
}

fn find_top_level_comma(s : String, from : Int) -> Int {
  let mut depth = 0
  let mut pos = from
  let len = s.length()
  while pos < len {
    let c = s[pos]
    if c == ('(' : UInt16) || c == ('<' : UInt16) || c == ('[' : UInt16) || c == ('{' : UInt16) {
      depth = depth + 1
    } else if c == (')' : UInt16) || c == ('>' : UInt16) || c == (']' : UInt16) || c == ('}' : UInt16) {
      depth = depth - 1
    } else if c == (',' : UInt16) && depth == 0 {
      return pos
    }
    pos = pos + 1
  }
  -1
}

fn skip_ws_from(s : String, from : Int) -> Int {
  let mut pos = from
  let len = s.length()
  while pos < len && (s[pos] == (' ' : UInt16) || s[pos] == ('\t' : UInt16) || s[pos] == ('\n' : UInt16)) {
    pos = pos + 1
  }
  pos
}

fn expand_call_each(args : String) -> String? {
  let split = split_at_top_level_comma(args)
  match split {
    None => None
    Some((prefix, rest)) => {
      let p = prefix
      let r = rest
      if p.length() == 0 || r.length() == 0 { return None }
      let arg_parts = split_all_top_level_commas(r)
      let mut has_tuple = false
      let mut has_single = false
      for i in 0.. 0 { result.write_string("; ") }
        result.write_string(p)
        result.write_string("(")
        if a.has_prefix("(") && ends_with_paren(a) {
          result.write_string(a[1:a.length() - 1].to_owned())
        } else {
          result.write_string(a)
        }
        result.write_string(")")
        count = count + 1
      }
      if count == 0 { None } else { Some(result.to_string()) }
    }
  }
}

fn expand_repeat(args : String) -> String? {
  let parts = split_all_top_level_commas(args)
  if parts.length() < 2 { return None }
  let expr = parts[0].trim().to_owned()
  if expr.length() == 0 { return None }
  let count_str = parts[1].trim().to_owned()
  let count = parse_int_safe(count_str)
  if count <= 0 { return None }
  let sep = if parts.length() >= 3 {
    parts[2].trim().to_owned()
  } else { ", " }
  let result = StringBuilder()
  for i in 0.. 0 { result.write_string(sep) }
    result.write_string(expr)
  }
  Some(result.to_string())
}

fn expand_echo(args : String) -> String? {
  let parts = split_all_top_level_commas(args)
  let result = StringBuilder()
  result.write_string("println(\"")
  let mut count = 0
  for p in parts {
    let name = p.trim().to_owned()
    if name.length() == 0 { continue }
    if count > 0 { result.write_string(", ") }
    result.write_string(name)
    result.write_string(" = \\{")
    result.write_string(name)
    result.write_string("}")
    count = count + 1
  }
  if count == 0 { return None }
  result.write_string("\")")
  Some(result.to_string())
}

fn expand_logger(args : String) -> String? {
  let s = args.trim().to_owned()
  if !s.has_prefix("fn ") { return None }

  let len = s.length()
  let name_start = 3
  let mut name_end = name_start
  while name_end < len {
    let c = s[name_end]
    let valid = (c >= ('a' : UInt16) && c <= ('z' : UInt16)) ||
      (c >= ('A' : UInt16) && c <= ('Z' : UInt16)) ||
      c == ('_' : UInt16) ||
      (name_end > name_start && c >= ('0' : UInt16) && c <= ('9' : UInt16))
    if valid { name_end = name_end + 1 } else { break }
  }
  if name_end == name_start { return None }
  let func_name = s[name_start:name_end].to_owned()

  let mut depth = 0
  let mut brace_open = -1
  let mut pos = 0
  while pos < len {
    let c = s[pos]
    if c == ('"' : UInt16) { pos = skip_string_from(s, pos); pos = pos - 1 }
    else if c == ('(' : UInt16) || c == ('<' : UInt16) || c == ('[' : UInt16) || c == ('{' : UInt16) {
      if c == ('{' : UInt16) && depth == 0 { brace_open = pos; break }
      depth = depth + 1
    } else if c == (')' : UInt16) || c == (']' : UInt16) || c == ('}' : UInt16) {
      depth = depth - 1
    } else if c == ('>' : UInt16) && depth > 0 {
      depth = depth - 1
    } else if c == ('-' : UInt16) && pos + 1 < len && s[pos + 1] == ('>' : UInt16) {
      pos = pos + 1
    }
    pos = pos + 1
  }
  if brace_open < 0 { return None }

  let mut d = 1
  let mut brace_close = brace_open + 1
  while brace_close < len && d > 0 {
    let c = s[brace_close]
    if c == ('"' : UInt16) { brace_close = skip_string_from(s, brace_close); brace_close = brace_close - 1 }
    else if c == ('{' : UInt16) { d = d + 1 }
    else if c == ('}' : UInt16) { d = d - 1 }
    brace_close = brace_close + 1
  }
  if d != 0 { return None }
  brace_close = brace_close - 1

  let before = s[0:brace_open + 1].to_owned()
  let body = s[brace_open + 1:brace_close].to_owned()
  let after = s[brace_close:].to_owned()

  let result = StringBuilder()
  result.write_string(before)
  result.write_string("\n  defer { println(\"leaving ")
  result.write_string(func_name)
  result.write_string("\") }")
  result.write_string("\n  println(\"entering ")
  result.write_string(func_name)
  result.write_string("\")")
  let trimmed_body = body.trim().to_owned()
  if trimmed_body.length() > 0 {
    result.write_string("\n  ")
    result.write_string(trimmed_body)
  }
  result.write_string("\n")
  result.write_string(after)
  Some(result.to_string())
}

fn expand_timeit(args : String) -> String? {
  let s = args.trim().to_owned()
  if !s.has_prefix("fn ") { return None }

  let len = s.length()
  let name_start = 3
  let mut name_end = name_start
  while name_end < len {
    let c = s[name_end]
    let valid = (c >= ('a' : UInt16) && c <= ('z' : UInt16)) ||
      (c >= ('A' : UInt16) && c <= ('Z' : UInt16)) ||
      c == ('_' : UInt16) ||
      (name_end > name_start && c >= ('0' : UInt16) && c <= ('9' : UInt16))
    if valid { name_end = name_end + 1 } else { break }
  }
  if name_end == name_start { return None }
  let func_name = s[name_start:name_end].to_owned()

  let mut depth = 0
  let mut brace_open = -1
  let mut pos = 0
  while pos < len {
    let c = s[pos]
    if c == ('"' : UInt16) { pos = skip_string_from(s, pos); pos = pos - 1 }
    else if c == ('(' : UInt16) || c == ('<' : UInt16) || c == ('[' : UInt16) || c == ('{' : UInt16) {
      if c == ('{' : UInt16) && depth == 0 { brace_open = pos; break }
      depth = depth + 1
    } else if c == (')' : UInt16) || c == (']' : UInt16) || c == ('}' : UInt16) {
      depth = depth - 1
    } else if c == ('>' : UInt16) && depth > 0 {
      depth = depth - 1
    } else if c == ('-' : UInt16) && pos + 1 < len && s[pos + 1] == ('>' : UInt16) {
      pos = pos + 1
    }
    pos = pos + 1
  }
  if brace_open < 0 { return None }

  let mut d = 1
  let mut brace_close = brace_open + 1
  while brace_close < len && d > 0 {
    let c = s[brace_close]
    if c == ('"' : UInt16) { brace_close = skip_string_from(s, brace_close); brace_close = brace_close - 1 }
    else if c == ('{' : UInt16) { d = d + 1 }
    else if c == ('}' : UInt16) { d = d - 1 }
    brace_close = brace_close + 1
  }
  if d != 0 { return None }
  brace_close = brace_close - 1

  let before = s[0:brace_open + 1].to_owned()
  let body = s[brace_open + 1:brace_close].to_owned()
  let after = s[brace_close:].to_owned()

  let result = StringBuilder()
  result.write_string(before)
  result.write_string("\n  let __timeit_start = @env.now()")
  result.write_string("\n  defer { println(\"")
  result.write_string(func_name)
  result.write_string(" took \" + (@env.now() - __timeit_start).to_string() + \"ms\") }")
  let trimmed_body = body.trim().to_owned()
  if trimmed_body.length() > 0 {
    result.write_string("\n  ")
    result.write_string(trimmed_body)
  }
  result.write_string("\n")
  result.write_string(after)
  Some(result.to_string())
}

fn expand_tryn(args : String) -> String? {
  let parts = split_all_top_level_commas(args)
  if parts.length() != 2 { return None }
  let expr = parts[0].trim().to_owned()
  let n = parts[1].trim().to_owned()
  let result = StringBuilder()
  result.write_string("{\n  let __tryn_max = ")
  result.write_string(n)
  result.write_string("\n  let mut __tryn_i = 0")
  result.write_string("\n  let __tryn_val = loop {")
  result.write_string("\n    if __tryn_i >= __tryn_max { break None }")
  result.write_string("\n    __tryn_i = __tryn_i + 1")
  result.write_string("\n    let __tryn_cur = ")
  result.write_string(expr)
  result.write_string("\n    if __tryn_cur is Some(_) { break __tryn_cur }")
  result.write_string("\n  }")
  result.write_string("\n  __tryn_val")
  result.write_string("\n}")
  Some(result.to_string())
}

fn skip_string_from(s : String, start : Int) -> Int {
  let mut pos = start + 1
  let len = s.length()
  while pos < len {
    let c = s[pos]
    if c == ('\\' : UInt16) { pos = pos + 2 }
    else if c == ('"' : UInt16) { return pos + 1 }
    else { pos = pos + 1 }
  }
  pos
}

fn ends_with_paren(s : String) -> Bool {
  let len = s.length()
  len > 0 && s[len - 1] == (')' : UInt16)
}

fn split_all_top_level_commas(s : String) -> Array[String] {
  let parts : Array[String] = []
  let mut depth = 0
  let mut start = 0
  let mut pos = 0
  let len = s.length()
  while pos < len {
    let c = s[pos]
    if c == ('(' : UInt16) || c == ('<' : UInt16) || c == ('[' : UInt16) || c == ('{' : UInt16) {
      depth = depth + 1
    } else if c == (')' : UInt16) || c == ('>' : UInt16) || c == (']' : UInt16) || c == ('}' : UInt16) {
      depth = depth - 1
    } else if c == (',' : UInt16) && depth == 0 {
      parts.push(s[start:pos].to_owned())
      start = pos + 1
    }
    pos = pos + 1
  }
  if start < len { parts.push(s[start:].to_owned()) }
  parts
}

fn split_at_top_level_comma(s : String) -> (String, String)? {
  let mut depth = 0
  let mut pos = 0
  let len = s.length()
  while pos < len {
    let c = s[pos]
    if c == ('(' : UInt16) || c == ('<' : UInt16) || c == ('[' : UInt16) || c == ('{' : UInt16) {
      depth = depth + 1
    } else if c == (')' : UInt16) || c == ('>' : UInt16) || c == (']' : UInt16) || c == ('}' : UInt16) {
      depth = depth - 1
    } else if c == (',' : UInt16) && depth == 0 {
      let expr = s[0:pos].trim().to_owned()
      let pat = s[pos + 1:].trim().to_owned()
      return Some((expr, pat))
    }
    pos = pos + 1
  }
  None
}