///|
/// Python's `_get_ttype_class` (html) / `_get_ttype_name` (latex): the short
/// name of the nearest standard ancestor followed by `-` for the
/// remaining components (`Name.Builtin.Magic` gives `nb-Magic`).
pub fn ttype_class(t : @token.TokenType) -> String {
let names = @token.standard_names()
let mut suffix = ""
let mut cur = t
while true {
match names.get(cur) {
Some(short) => return short + suffix
None => ()
}
suffix = "-" + cur.last() + suffix
match cur.parent() {
Some(p) => cur = p
None => break
}
}
suffix
}
///|
/// Whether `t` is one of Python's `STANDARD_TYPES`.
fn is_standard(t : @token.TokenType) -> Bool {
@token.standard_names().contains(t)
}
///|
/// The path components of `t` below the root (Python's tuple value of the
/// token type: `Token.Name.Builtin` is `('Name', 'Builtin')`).
fn ttype_tuple(t : @token.TokenType) -> Array[String] {
let out = []
let mut cur = t
while true {
match cur.parent() {
Some(p) => {
out.push(cur.last())
cur = p
}
None => break
}
}
out.rev_in_place()
out
}
///|
/// Python's `str(ttype)` minus the leading `Token.` (`''` for `Token`).
fn ttype_repr_tail(t : @token.TokenType) -> String {
ttype_tuple(t).join(".")
}
///|
/// Python string ordering (by code point; code units are equivalent for
/// the ASCII names compared here).
fn py_str_compare(a : String, b : String) -> Int {
let n = if a.length() < b.length() { a.length() } else { b.length() }
for i in 0.. Int {
let x = ttype_tuple(a)
let y = ttype_tuple(b)
let n = if x.length() < y.length() { x.length() } else { y.length() }
for i in 0.. String {
if color == "transparent" ||
color.has_prefix("calc") ||
color.has_prefix("var") {
return color
}
let color = @pystr.upper(color)
let c = color.to_array()
if c.length() == 6 && c[0] == c[1] && c[2] == c[3] && c[4] == c[5] {
"#\{c[0]}\{c[2]}\{c[4]}"
} else {
"#\{color}"
}
}
///|
/// Python's `int(s)` for decimal strings (surrounding whitespace, an
/// optional sign and single underscores between digits are accepted).
fn py_int(s : String) -> Int? {
let t = s.trim(chars=" \t\n\r\u{0b}\u{0c}").to_owned()
let chars = t.to_array()
if chars.length() == 0 {
return None
}
let mut i = 0
let mut neg = false
if chars[0] == '+' || chars[0] == '-' {
neg = chars[0] == '-'
i = 1
}
if i >= chars.length() {
return None
}
let mut n = 0
let mut prev_digit = false
while i < chars.length() {
let c = chars[i]
if c >= '0' && c <= '9' {
n = n * 10 + (c.to_int() - '0'.to_int())
prev_digit = true
} else if c == '_' && prev_digit && i + 1 < chars.length() {
prev_digit = false
} else {
return None
}
i += 1
}
if !prev_digit {
return None
}
Some(if neg { -n } else { n })
}
///|
/// Python's `a % b` (result has the sign of `b`); raises Python's
/// `ZeroDivisionError` for `b == 0`.
fn py_mod(a : Int, b : Int) -> Int raise FormatterError {
if b == 0 {
raise FormatterError("ZeroDivisionError: integer modulo by zero")
}
let r = a % b
if r != 0 && (r < 0) != (b < 0) {
r + b
} else {
r
}
}
///|
/// Python's `abs(get_int_opt(options, name, default))`.
fn abs_int_opt(
options : @lexer.Options,
name : String,
default : Int,
) -> Int raise {
let v = @lexer.get_int_opt(options, name, default)
if v < 0 {
-v
} else {
v
}
}
///|
/// Python truthiness of an option given as a string (`options.get(name,
/// False)` used as a Boolean): present and non-empty.
fn truthy_opt(options : @lexer.Options, name : String) -> Bool {
match options.get(name) {
Some(v) => v != ""
None => false
}
}
///|
/// Python's `int(hex, 16)` for up to 8 hex digits (`None` if invalid).
fn parse_hex(s : String) -> Int? {
let t = s.trim(chars=" \t\n\r\u{0b}\u{0c}").to_owned()
if t.length() == 0 {
return None
}
let mut n = 0
let mut prev_digit = false
let chars = t.to_array()
for i, c in chars {
let d = if c >= '0' && c <= '9' {
c.to_int() - '0'.to_int()
} else if c >= 'a' && c <= 'f' {
c.to_int() - 'a'.to_int() + 10
} else if c >= 'A' && c <= 'F' {
c.to_int() - 'A'.to_int() + 10
} else if c == '_' && prev_digit && i + 1 < chars.length() {
prev_digit = false
continue
} else {
return None
}
n = n * 16 + d
prev_digit = true
}
if prev_digit {
Some(n)
} else {
None
}
}