Something went wrong. Try again.
The Monad language. Dependent types, functional programming compiled with LLVM. Hobby project. monad-lang.org
dependent-types language compiler programming-language functional-programming
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253// Standard library: ANSI terminal color and style codes.//// `colors_enabled` only checks NO_COLOR / FORCE_COLOR / TERM via IO.get_env —// there is no isatty/TTY check, so piped output is not auto-disabled unless// NO_COLOR is set. This is a deliberate scope cut (see// implementations/ansi-colors.md in the plans repo), not an oversight.use lib::io {}open IO {get_env}pub type Color { black, red, green, yellow, blue, magenta, cyan, white, bright_black, bright_red, bright_green, bright_yellow, bright_blue, bright_magenta, bright_cyan, bright_white, color256 (code : U8), true_color (r : U8) (g : U8) (b : U8)}/// Structural equality on `Color`, variant by variant; `color256` compares/// its code and `true_color` all three channels.pub def Color.beq (a b : Color) : Bool := match a { black => match b { black => true, _ => false }, red => match b { red => true, _ => false }, green => match b { green => true, _ => false }, yellow => match b { yellow => true, _ => false }, blue => match b { blue => true, _ => false }, magenta => match b { magenta => true, _ => false }, cyan => match b { cyan => true, _ => false }, white => match b { white => true, _ => false }, bright_black => match b { bright_black => true, _ => false }, bright_red => match b { bright_red => true, _ => false }, bright_green => match b { bright_green => true, _ => false }, bright_yellow => match b { bright_yellow => true, _ => false }, bright_blue => match b { bright_blue => true, _ => false }, bright_magenta => match b { bright_magenta => true, _ => false }, bright_cyan => match b { bright_cyan => true, _ => false }, bright_white => match b { bright_white => true, _ => false }, color256 ca => match b { color256 cb => ca == cb, _ => false }, true_color ra ga ba => match b { true_color rb gb bb => ra == rb && ga == gb && ba == bb, _ => false } }instance BEq Color { def beq (a b : Color) : Bool := Color.beq a b}type Style { bold, dim, italic, underline, blink, reverse, hidden, strikethrough}type Modifier { fg (color : Color), bg (color : Color), style (s : Style), reset}pub def color_fg_code (c : Color) : String := match c { black => "30", red => "31", green => "32", yellow => "33", blue => "34", magenta => "35", cyan => "36", white => "37", bright_black => "90", bright_red => "91", bright_green => "92", bright_yellow => "93", bright_blue => "94", bright_magenta => "95", bright_cyan => "96", bright_white => "97", color256 code => "38;5;" ++ U8.to_string code, true_color r g b => "38;2;" ++ U8.to_string r ++ ";" ++ U8.to_string g ++ ";" ++ U8.to_string b }pub def color_bg_code (c : Color) : String := match c { black => "40", red => "41", green => "42", yellow => "43", blue => "44", magenta => "45", cyan => "46", white => "47", bright_black => "100", bright_red => "101", bright_green => "102", bright_yellow => "103", bright_blue => "104", bright_magenta => "105", bright_cyan => "106", bright_white => "107", color256 code => "48;5;" ++ U8.to_string code, true_color r g b => "48;2;" ++ U8.to_string r ++ ";" ++ U8.to_string g ++ ";" ++ U8.to_string b }def style_code (s : Style) : String := match s { bold => "1", dim => "2", italic => "3", underline => "4", blink => "5", reverse => "7", hidden => "8", strikethrough => "9" }/// Constructs an ANSI escape sequence for a single modifier.def escape (m : Modifier) : String := match m { fg c => "[" ++ color_fg_code c ++ "m", bg c => "[" ++ color_bg_code c ++ "m", style s => "[" ++ style_code s ++ "m", reset => "[0m" }def red (s : String) : String := escape (Modifier.fg Color.red) ++ s ++ escape Modifier.resetdef green (s : String) : String := escape (Modifier.fg Color.green) ++ s ++ escape Modifier.resetdef yellow (s : String) : String := escape (Modifier.fg Color.yellow) ++ s ++ escape Modifier.resetdef bold (s : String) : String := escape (Modifier.style Style.bold) ++ s ++ escape Modifier.resetdef dim (s : String) : String := escape (Modifier.style Style.dim) ++ s ++ escape Modifier.reset/// Bold red — for failure output.////// Dotted on purpose (`Ansi.fail`, the `Bench.now` convention): a BARE/// `def fail` here poisons every program that loads this module./// `qualify_modules`'s `resolve_owner` rule 3 resolves a bare reference/// to "exactly one module [that] declares it" -- the only DECLARER it/// knows is defs, not `open`d constructors, so once a bare `def fail`/// entered the loaded set, `lang/parser.mo`'s own bare `fail`/// (`open ParseResult {fail, success}` -- a CONSTRUCTOR, invisible to/// rule 3) was rewritten to `std.ansi::fail` in the self-compiled/// binary and SIGSEGV'd in `String.concat` on the first parse (a/// `fail`-of-the-parser call became a String style-wrap). `pass` is/// renamed for the same reason: it is exactly as common a word, and the/// next `open X {pass}` would walk into the same trap.def Ansi.fail (s : String) : String := bold (red s)/// Bold green — for success output. See `Ansi.fail` for the dotted name.def Ansi.pass (s : String) : String := bold (green s)/// Yellow — for warning output.pub def warn (s : String) : String := escape (Modifier.fg Color.yellow) ++ s ++ escape Modifier.resetdef env_flag_set (v : Option String) : Bool := match v { some _ => true, none => false }def term_is_dumb (v : Option String) : Bool := match v { some t => t == "dumb", none => false }/// Whether ANSI colors should be used, per NO_COLOR / FORCE_COLOR / TERM./// NO_COLOR (https://no-color.org) always wins; FORCE_COLOR forces colors on/// unless NO_COLOR is set; otherwise colors are on unless TERM=dumb.def colors_enabled : IO Bool := do { let no_color <- get_env "NO_COLOR"; if env_flag_set no_color then return false else do { let force_color <- get_env "FORCE_COLOR"; if env_flag_set force_color then return true else do { let term <- get_env "TERM"; return (Bool.not (term_is_dumb term)) } }}/// Wraps `s` in `color` if colors are enabled, otherwise returns `s` unchanged.pub def colored (s : String) (color : Color) : IO String := do { let enabled <- colors_enabled; if enabled then return (escape (Modifier.fg color) ++ s ++ escape Modifier.reset) else return s}// ---------- Tests ----------#[test]def test_color_beq_same_variant : Bool := Color.red == Color.red#[test]def test_color_beq_different_variant : Bool := Bool.not (Color.red == Color.green)#[test]def test_color_beq_color256_compares_code : Bool := Color.color256 42u8 == Color.color256 42u8 && Bool.not (Color.color256 1u8 == Color.color256 2u8)#[test]def test_color_beq_true_color_compares_all_fields : Bool := Color.true_color 1u8 2u8 3u8 == Color.true_color 1u8 2u8 3u8 && Bool.not (Color.true_color 1u8 2u8 3u8 == Color.true_color 1u8 2u8 9u8)#[test]def test_escape_reset : Bool := escape Modifier.reset == "[0m"#[test]def test_escape_fg_red : Bool := escape (Modifier.fg Color.red) == "[31m"#[test]def test_escape_bg_blue : Bool := escape (Modifier.bg Color.blue) == "[44m"#[test]def test_escape_style_bold : Bool := escape (Modifier.style Style.bold) == "[1m"#[test]def test_escape_color256 : Bool := escape (Modifier.fg (Color.color256 42u8)) == "[38;5;42m"#[test]def test_escape_true_color : Bool := escape (Modifier.fg (Color.true_color 1u8 2u8 3u8)) == "[38;2;1;2;3m"#[test]def test_red_wraps_and_resets : Bool := red "x" == "[31mx[0m"#[test]def test_green_wraps_and_resets : Bool := green "x" == "[32mx[0m"#[test]def test_bold_wraps_and_resets : Bool := bold "x" == "[1mx[0m"#[test]def test_fail_is_bold_red : Bool := Ansi.fail "x" == bold (red "x")#[test]def test_pass_is_bold_green : Bool := Ansi.pass "x" == bold (green "x")#[test]def test_env_flag_set_some : Bool := env_flag_set (Option.some "1")#[test]def test_env_flag_set_none : Bool := Bool.not (env_flag_set Option.none)#[test]def test_term_is_dumb_true : Bool := term_is_dumb (Option.some "dumb")#[test]def test_term_is_dumb_false_for_other_term : Bool := Bool.not (term_is_dumb (Option.some "xterm-256color"))#[test]def test_term_is_dumb_false_when_unset : Bool := Bool.not (term_is_dumb Option.none)