Sasha Koshka
0f5dd78aea
The specification defines numerics to be anything accepted by the %f specifier for scanf, which implies a 32 bit float.
208 lines
5.9 KiB
Hare
208 lines
5.9 KiB
Hare
use ascii;
|
|
use strconv;
|
|
use strings;
|
|
|
|
// 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) (f32 | error) = match (strconv::stof32(in)) {
|
|
case let float: f32 => yield float;
|
|
case =>
|
|
if (strings::contains(in, ";")) return expected_single;
|
|
yield invalid_numeric;
|
|
};
|
|
|
|
// Parses an integer value. While there is no documentation on this data type,
|
|
// it is referred to by the Icon Theme Specification, §4, tables 2-3.
|
|
export fn parse_integer(in: str) (int | error) = match(strconv::stoi(in)) {
|
|
case let integer: int => yield integer;
|
|
case =>
|
|
if (strings::contains(in, ";")) return expected_single;
|
|
yield invalid_integer;
|
|
};
|
|
|
|
// 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) ([]f32 | error) = {
|
|
let splitte = split(in);
|
|
let result: []f32 = alloc([], 0);
|
|
for (let in => splitter_next(&splitte)) {
|
|
match (parse_numeric(in)) {
|
|
case let number: f32 =>
|
|
append(result, number);
|
|
case let err: error =>
|
|
free(result);
|
|
return err;
|
|
};
|
|
};
|
|
return result;
|
|
};
|
|
|
|
// Parses multiple integer values. See [[parse_integer]] for more information.
|
|
// The memory must be freed by the caller.
|
|
export fn parse_integers(in: str) ([]int | error) = {
|
|
let splitte = split(in);
|
|
let result: []int = alloc([], 0);
|
|
for (let in => splitter_next(&splitte)) {
|
|
match (parse_integer(in)) {
|
|
case let number: int =>
|
|
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);
|
|
};
|