Add untested value parsing
This commit is contained in:
		
							parent
							
								
									5ec91331d3
								
							
						
					
					
						commit
						ecbbccbaa6
					
				
							
								
								
									
										178
									
								
								format/desktop_entry/value.ha
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										178
									
								
								format/desktop_entry/value.ha
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,178 @@ | ||||
| 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 (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                => 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); | ||||
| }; | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user