# calc_terms.ludic - calc()'s products and its terms: a number with its unit, a bracket, a nested # calc(), a minus in front; a product needs a plain number on one side, and a quotient a plain number # or a length on its right (a length over a length is a plain number, as CSS's typed division makes) function ca_product(ui_st: mut UiState) -> CaVal { var a = ca_factor(ui_st) while ui_st.ca_bad == "" { ca_ws(ui_st) if ui_st.ca_i >= len(ui_st.ca_s) or (ui_st.ca_s[ui_st.ca_i] != 42 and ui_st.ca_s[ui_st.ca_i] != 47) { return a } let div = ui_st.ca_s[ui_st.ca_i] == 47 ui_st.ca_i += 1 let b = ca_factor(ui_st) if div and not b.bare and not a.bare and a.pct == 0.0 and b.pct == 0.0 and b.px != 0.0 { a = ca_ratio(ui_st, a.px / b.px) continue } if div and not b.bare { ui_st.ca_bad = "a plain number, or a length under a length, after /" return a } if div and b.n == 0.0 { ui_st.ca_bad = "a number that is not 0 after /" return a } if div { a = ca_scale(ui_st, a, 1.0 / b.n) } else if a.bare { a = ca_scale(ui_st, b, a.n) } else if b.bare { a = ca_scale(ui_st, a, b.n) } else { ui_st.ca_bad = "a plain number on one side of *" return a } } return a } # a length over a length is a plain number (100vw / 1800px) function ca_ratio(ui_st: mut UiState, k: float) -> CaVal { let r = ca_new(ui_st) r.n = k return r } function ca_scale(ui_st: mut UiState, a: CaVal, k: float) -> CaVal { let r = ca_new(ui_st) r.bare = a.bare r.n = a.n * k r.px = a.px * k r.pct = a.pct * k return r } function ca_factor(ui_st: mut UiState) -> CaVal { ca_ws(ui_st) if ui_st.ca_i >= len(ui_st.ca_s) { ui_st.ca_bad = "a value" return ca_new(ui_st) } let c = ui_st.ca_s[ui_st.ca_i] if c == 45 { ui_st.ca_i += 1 return ca_scale(ui_st, ca_factor(ui_st), -1.0) } let f = ca_fn_at(ui_st.ca_s, ui_st.ca_i) if f == "min" or f == "max" or f == "clamp" { return ca_fn(ui_st, f) } if c == 40 or f == "calc" { while ui_st.ca_s[ui_st.ca_i] != 40 { ui_st.ca_i += 1 } ui_st.ca_i += 1 let v = ca_sum(ui_st) ca_ws(ui_st) if ui_st.ca_i < len(ui_st.ca_s) and ui_st.ca_s[ui_st.ca_i] == 41 { ui_st.ca_i += 1 } else if ui_st.ca_bad == "" { ui_st.ca_bad = "a closing )" } return v } return ca_number(ui_st) } # 12, 1.5em, 50%, 20vw function ca_number(ui_st: mut UiState) -> CaVal { let at = ui_st.ca_i while ui_st.ca_i < len(ui_st.ca_s) and ((ui_st.ca_s[ui_st.ca_i] >= 48 and ui_st.ca_s[ui_st.ca_i] <= 57) or ui_st.ca_s[ui_st.ca_i] == 46) { ui_st.ca_i += 1 } let num_end = ui_st.ca_i while ui_st.ca_i < len(ui_st.ca_s) and ((ui_st.ca_s[ui_st.ca_i] >= 97 and ui_st.ca_s[ui_st.ca_i] <= 122) or ui_st.ca_s[ui_st.ca_i] == 37) { ui_st.ca_i += 1 } let r = ca_new(ui_st) if num_end == at { ui_st.ca_bad = "a number" return r } let s = ui_st.ca_s let end = ui_st.ca_i let f = el_num_in(s, at, num_end) if end == num_end { r.n = f return r } r.bare = false @alloc_ok("only when a template is wrong: a broken screen, never a working one") if ca_unit(s, num_end, end, "%") { r.pct = f } else if ca_unit(s, num_end, end, "px") { r.px = el_px(ui_st, f) } else if ca_unit(s, num_end, end, "rem") { r.px = f * el_px(ui_st, 16.0) } else if ca_unit(s, num_end, end, "em") { r.px = f * el_em(ui_st) } else if ca_unit(s, num_end, end, "vw") { r.px = f * ui_st.ly_vp_w / 100.0 } else if ca_unit(s, num_end, end, "vh") { r.px = f * ui_st.ly_vp_h / 100.0 } else { ui_st.ca_bad = `a unit calc() knows, not {s[num_end..end]}` } return r } # the unit between `from` and `to` is `u`, compared in place function ca_unit(s: string, from: int, to: int, u: string) -> bool { return to - from == len(u) and sat(s, from, u) }