Compare commits
No commits in common. "9e4d7e4ae49654e3e0e64da5b3485f3d3e503141" and "5ec91331d31d818fb71e0ba7a1e81d2faddf0dd4" have entirely different histories.
9e4d7e4ae4
...
5ec91331d3
@ -1,183 +0,0 @@
|
|||||||
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);
|
|
||||||
};
|
|
@ -1,202 +0,0 @@
|
|||||||
use fmt;
|
|
||||||
use math;
|
|
||||||
use strings;
|
|
||||||
|
|
||||||
@test fn parse_string() void = {
|
|
||||||
assert(parse_string("hello")! == "hello");
|
|
||||||
assert(parse_string("hel\\s\\n\\t\\r\\\\\\;lo")! == "hel \n\t\r\\;lo");
|
|
||||||
assert(parse_string("hello☠") is invalid_ascii);
|
|
||||||
assert(parse_string("hello;world") is expected_single);
|
|
||||||
assert(parse_string("hello\\d") is invalid_escape);
|
|
||||||
};
|
|
||||||
|
|
||||||
@test fn parse_localestring() void = {
|
|
||||||
assert(parse_localestring("hello")! == "hello");
|
|
||||||
assert(parse_localestring("hel\\s\\n\\t\\r\\\\\\;lo")! == "hel \n\t\r\\;lo");
|
|
||||||
assert(parse_localestring("hello☠")! == "hello☠");
|
|
||||||
assert(parse_localestring("hello;world") is expected_single);
|
|
||||||
assert(parse_localestring("hello\\d") is invalid_escape);
|
|
||||||
};
|
|
||||||
|
|
||||||
@test fn parse_iconstring() void = {
|
|
||||||
assert(parse_iconstring("hello")! == "hello");
|
|
||||||
assert(parse_iconstring("hel\\s\\n\\t\\r\\\\\\;lo")! == "hel \n\t\r\\;lo");
|
|
||||||
assert(parse_iconstring("hello☠")! == "hello☠");
|
|
||||||
assert(parse_iconstring("hello;world") is expected_single);
|
|
||||||
assert(parse_iconstring("hello\\d") is invalid_escape);
|
|
||||||
};
|
|
||||||
|
|
||||||
@test fn parse_boolean() void = {
|
|
||||||
assert(parse_boolean("true")! == true);
|
|
||||||
assert(parse_boolean("false")! == false);
|
|
||||||
assert(parse_boolean("hello") is invalid_boolean);
|
|
||||||
assert(parse_boolean("ttrue") is invalid_boolean);
|
|
||||||
assert(parse_boolean("falsee") is invalid_boolean);
|
|
||||||
assert(parse_boolean("") is invalid_boolean);
|
|
||||||
assert(parse_boolean("1") is invalid_boolean);
|
|
||||||
assert(parse_boolean("0") is invalid_boolean);
|
|
||||||
assert(parse_boolean("true;false") is expected_single);
|
|
||||||
};
|
|
||||||
|
|
||||||
@test fn parse_numeric() void = {
|
|
||||||
assert(parse_numeric("9")! == 9.0);
|
|
||||||
assert(parse_numeric("9.0")! == 9.0);
|
|
||||||
assert(parse_numeric("34.93")! == 34.93);
|
|
||||||
assert(parse_numeric("-100.895")! == -100.895);
|
|
||||||
assert(math::isnan(parse_numeric("NaN")!));
|
|
||||||
assert(parse_numeric("Infinity")! == math::INF);
|
|
||||||
assert(parse_numeric("hello") is invalid_numeric);
|
|
||||||
assert(parse_numeric("--") is invalid_numeric);
|
|
||||||
assert(parse_numeric("....") is invalid_numeric);
|
|
||||||
assert(parse_numeric("234;7.4") is expected_single);
|
|
||||||
};
|
|
||||||
|
|
||||||
@test fn parse_strings() void = {
|
|
||||||
let correct: []str = [
|
|
||||||
"b\r\tird",
|
|
||||||
"wa;ter",
|
|
||||||
"",
|
|
||||||
"riv\ner",
|
|
||||||
"nuh uh",
|
|
||||||
];
|
|
||||||
let got = parse_strings("b\\r\\tird;wa\\;ter;;riv\\ner;nuh uh")!;
|
|
||||||
defer strings::freeall(got);
|
|
||||||
for (let index = 0z; index < len(correct); index += 1) {
|
|
||||||
assert(index < len(got), "ran out");
|
|
||||||
assert(compare_strings(correct[index], got[index]));
|
|
||||||
};
|
|
||||||
assert(len(got) == len(correct), "not done");
|
|
||||||
assert(parse_strings("hello☠;world") is invalid_ascii);
|
|
||||||
assert(parse_strings("hello\\d;world") is invalid_escape);
|
|
||||||
};
|
|
||||||
|
|
||||||
@test fn parse_localestrings() void = {
|
|
||||||
let correct: []str = [
|
|
||||||
"b\r\tir☠d",
|
|
||||||
"wa;ter",
|
|
||||||
"",
|
|
||||||
"ri☠v\ner",
|
|
||||||
"nuh uh",
|
|
||||||
];
|
|
||||||
let got = parse_localestrings("b\\r\\tir☠d;wa\\;ter;;ri☠v\\ner;nuh uh")!;
|
|
||||||
defer strings::freeall(got);
|
|
||||||
for (let index = 0z; index < len(correct); index += 1) {
|
|
||||||
assert(index < len(got), "ran out");
|
|
||||||
assert(compare_strings(correct[index], got[index]));
|
|
||||||
};
|
|
||||||
assert(len(got) == len(correct), "not done");
|
|
||||||
assert(parse_strings("hello\\d;world") is invalid_escape);
|
|
||||||
};
|
|
||||||
|
|
||||||
@test fn parse_booleans() void = {
|
|
||||||
let correct: []bool = [
|
|
||||||
true,
|
|
||||||
true,
|
|
||||||
false,
|
|
||||||
true,
|
|
||||||
false,
|
|
||||||
true,
|
|
||||||
];
|
|
||||||
let got = parse_booleans("true;true;false;true;false;true")!;
|
|
||||||
defer free(got);
|
|
||||||
for (let index = 0z; index < len(correct); index += 1) {
|
|
||||||
assert(index < len(got), "ran out");
|
|
||||||
let correct = correct[index];
|
|
||||||
let got = got[index];
|
|
||||||
fmt::printf("[{}]\t[{}]\n", correct, got)!;
|
|
||||||
assert(correct == got);
|
|
||||||
};
|
|
||||||
assert(len(got) == len(correct), "not done");
|
|
||||||
assert(parse_booleans("hello;world") is invalid_boolean);
|
|
||||||
assert(parse_booleans("true;;") is invalid_boolean);
|
|
||||||
assert(parse_booleans(";false;") is invalid_boolean);
|
|
||||||
};
|
|
||||||
|
|
||||||
@test fn parse_numerics() void = {
|
|
||||||
let correct: []f64 = [
|
|
||||||
5.0,
|
|
||||||
34.9,
|
|
||||||
29.0,
|
|
||||||
32498.23784,
|
|
||||||
];
|
|
||||||
let got = parse_numerics("5;34.9;29;32498.23784;")!;
|
|
||||||
defer free(got);
|
|
||||||
for (let index = 0z; index < len(correct); index += 1) {
|
|
||||||
assert(index < len(got), "ran out");
|
|
||||||
let correct = correct[index];
|
|
||||||
let got = got[index];
|
|
||||||
fmt::printf("[{}]\t[{}]\n", correct, got)!;
|
|
||||||
assert(correct == got);
|
|
||||||
};
|
|
||||||
assert(len(got) == len(correct), "not done");
|
|
||||||
assert(parse_numerics("hello;world") is invalid_numeric);
|
|
||||||
assert(parse_numerics("5;;") is invalid_numeric);
|
|
||||||
assert(parse_numerics(";5;") is invalid_numeric);
|
|
||||||
};
|
|
||||||
|
|
||||||
@test fn string_escaper_next_a() void = {
|
|
||||||
let escaper = escape_string("bird;water;;river;");
|
|
||||||
let correct: []str = [
|
|
||||||
"bird",
|
|
||||||
"water",
|
|
||||||
"",
|
|
||||||
"river",
|
|
||||||
];
|
|
||||||
for (let correct .. correct) {
|
|
||||||
let got = string_escaper_next(&escaper) as str;
|
|
||||||
assert(compare_strings(correct, got));
|
|
||||||
};
|
|
||||||
assert(string_escaper_next(&escaper) is done, "not done");
|
|
||||||
};
|
|
||||||
|
|
||||||
@test fn string_escaper_next_b() void = {
|
|
||||||
let escaper = escape_string("b\\r\\tird;wa\\;ter;;riv\\ner;nuh uh");
|
|
||||||
let correct: []str = [
|
|
||||||
"b\r\tird",
|
|
||||||
"wa;ter",
|
|
||||||
"",
|
|
||||||
"riv\ner",
|
|
||||||
"nuh uh",
|
|
||||||
];
|
|
||||||
for (let correct .. correct) {
|
|
||||||
let got = string_escaper_next(&escaper) as str;
|
|
||||||
assert(compare_strings(correct, got));
|
|
||||||
};
|
|
||||||
assert(string_escaper_next(&escaper) is done, "not done");
|
|
||||||
};
|
|
||||||
|
|
||||||
@test fn splitter_next_a() void = {
|
|
||||||
let splitte = split("bird;water;;river;");
|
|
||||||
let correct: []str = [
|
|
||||||
"bird",
|
|
||||||
"water",
|
|
||||||
"",
|
|
||||||
"river",
|
|
||||||
];
|
|
||||||
for (let correct .. correct) {
|
|
||||||
let got = splitter_next(&splitte) as str;
|
|
||||||
assert(compare_strings(correct, got));
|
|
||||||
};
|
|
||||||
assert(splitter_next(&splitte) is done, "not done");
|
|
||||||
};
|
|
||||||
|
|
||||||
@test fn splitter_next_b() void = {
|
|
||||||
let splitte = split("bird;water;;river");
|
|
||||||
let correct: []str = [
|
|
||||||
"bird",
|
|
||||||
"water",
|
|
||||||
"",
|
|
||||||
"river",
|
|
||||||
];
|
|
||||||
for (let correct .. correct) {
|
|
||||||
let got = splitter_next(&splitte) as str;
|
|
||||||
fmt::printf("[{}]\t[{}]\n", correct, got)!;
|
|
||||||
assert(compare_strings(correct, got));
|
|
||||||
};
|
|
||||||
assert(splitter_next(&splitte) is done, "not done");
|
|
||||||
};
|
|
||||||
|
|
||||||
fn compare_strings(a: str, b: str) bool = {
|
|
||||||
fmt::printf("[{}]\t[{}]\n", a, b)!;
|
|
||||||
return a == b;
|
|
||||||
};
|
|
Loading…
Reference in New Issue
Block a user