use ascii; use strconv; use strings; // TODO icon_theme references an "integer" data type. support that here. // Parses a string value. It may contain all ASCII characters except for control // characters. The memory must be freed by the caller. // Specification: §4 export fn parse_string(in: str) (str | error) = { for (let char .. strings::toutf8(in)) { if (!ascii::valid(char: rune)) return invalid_ascii; }; return parse_localestring(in); }; // Parses a localestring value. It is user displayable, and is encoded in UTF-8. // The memory must be freed by the caller. // Specification: §4 export fn parse_localestring(in: str) (str | error) = { let escaper = escape_string(in); let string = match(string_escaper_next(&escaper)?) { case let string: str => yield string; case done => return ""; }; if (len(escaper) > 0) { free(string); return expected_single; }; return string; }; // Parses an iconstring value. It is the name of an icon; it may be an absolute // path, or a symbolic name for an icon located using the algorithm described in // the Icon Theme Specification. Such values are not user-displayable, and are // encoded in UTF-8. The memory must be freed by the caller. // Specification: §4 export fn parse_iconstring(in: str) (str | error) = parse_localestring(in); // Parses a boolean value. It must either be the string "true" or "false". // Specification: §4 export fn parse_boolean(in: str) (bool | error) = { if (strings::contains(in, ";")) return expected_single; if (in == "true" ) return true; if (in == "false") return false; return invalid_boolean; }; // Parses a numeric value. It must be a valid floating point number as // recognized by the %f specifier for scanf in the C locale. // Specification: §4 export fn parse_numeric(in: str) (f64 | error) = match (strconv::stof64(in)) { case let float: f64 => yield float; case => if (strings::contains(in, ";")) return expected_single; yield invalid_numeric; }; // Parses multiple string values. See [[parse_string]] for more information. The // memory must be freed by the caller. // Specification: §4 export fn parse_strings(in: str) ([]str | error) = { for (let char .. strings::toutf8(in)) { if (!ascii::valid(char: rune)) return invalid_ascii; }; return parse_localestrings(in); }; // Parses multiple localestring values. See [[parse_localestring]] for more // information. The memory must be freed by the caller. // Specification: §4 export fn parse_localestrings(in: str) ([]str | error) = { let escaper = escape_string(in); let result: []str = alloc([], 0); for (let in => string_escaper_next(&escaper)) { match (in) { case let string: str => append(result, string); case let err: error => strings::freeall(result); return err; }; }; return result; }; // Parses multiple iconstring values. See [[parse_iconstring]] for more // information. The memory must be freed by the caller. // Specification: §4 export fn parse_iconstrings(in: str) ([]str | error) = parse_localestrings(in); // Parses multiple boolean values. See [[parse_boolean]] for more information. // The memory must be freed by the caller. // Specification: §4 export fn parse_booleans(in: str) ([]bool | error) = { let splitte = split(in); let result: []bool = alloc([], 0); for (let in => splitter_next(&splitte)) { match (parse_boolean(in)) { case let boolean: bool => append(result, boolean); case let err: error => free(result); return err; }; }; return result; }; // Parses multiple numeric values. See [[parse_numeric]] for more information. // The memory must be freed by the caller. // Specification: §4 export fn parse_numerics(in: str) ([]f64 | error) = { let splitte = split(in); let result: []f64 = alloc([], 0); for (let in => splitter_next(&splitte)) { match (parse_numeric(in)) { case let number: f64 => append(result, number); case let err: error => free(result); return err; }; }; return result; }; type string_escaper = []u8; fn escape_string(in: str) string_escaper = strings::toutf8(in): string_escaper; fn string_escaper_next(this: *string_escaper) (str | done | error) = { if (len(*this) < 1) return done; let result: []u8 = alloc([], 0); let end = 0z; let saw_escape = false; for (let byte .. *this) { end += 1; if (saw_escape) { switch (byte) { case ';' => append(result, ';'); case 's' => append(result, ' '); case 'n' => append(result, '\n'); case 't' => append(result, '\t'); case 'r' => append(result, '\r'); case '\\' => append(result, '\\'); case => free(result); return invalid_escape; }; saw_escape = false; } else { switch (byte) { case ';' => break; case '\\' => saw_escape = true; case => append(result, byte); }; }; }; *this = (*this)[end..]; return strings::fromutf8_unsafe(result); }; type splitter = []u8; fn split(in: str) splitter = strings::toutf8(in): splitter; fn splitter_next(this: *splitter) (str | done) = { if (len(*this) < 1) return done; let end = 0z; let knife = 0z; for (let byte .. *this) { knife += 1; if (byte == ';') { break; } else { end += 1; }; }; let result = this[..end]; *this = (*this)[knife..]; return strings::fromutf8_unsafe(result); };