hare

[hare] The Hare programming language
git clone https://git.torresjrjr.com/hare.git
Log | Files | Refs | README | LICENSE

scan.ha (2157B)


      1 // SPDX-License-Identifier: MPL-2.0
      2 // (c) Hare authors <https://harelang.org>
      3 
      4 use bufio;
      5 use encoding::utf8;
      6 use fmt;
      7 use io;
      8 use strings;
      9 
     10 export type scanner = struct {
     11 	in: io::handle,
     12 	line: str,
     13 	lineno: size,
     14 	section: str,
     15 };
     16 
     17 // Creates an INI file scanner. Use [[next]] to read entries. The caller must
     18 // call [[finish]] once they're done with this object.
     19 export fn scan(in: io::handle) scanner = scanner {
     20 	in = in,
     21 	lineno = 1,
     22 	...
     23 };
     24 
     25 // Frees resources associated with a [[scanner]].
     26 export fn finish(sc: *scanner) void = {
     27 	free(sc.line);
     28 	free(sc.section);
     29 };
     30 
     31 // An entry in an INI file: (section, key, value).
     32 export type entry = (const str, const str, const str);
     33 
     34 // Duplicates an [[entry]]. Use [[entry_finish]] to get rid of it.
     35 export fn entry_dup(ent: entry) entry = (
     36 	strings::dup(ent.0),
     37 	strings::dup(ent.1),
     38 	strings::dup(ent.2),
     39 );
     40 
     41 // Frees an [[entry]] previously duplicated with [[entry_dup]].
     42 export fn entry_finish(ent: entry) void = {
     43 	free(ent.0);
     44 	free(ent.1);
     45 	free(ent.2);
     46 };
     47 
     48 // Returns the next entry from an INI file. The return value is overwritten on
     49 // subsequent calls, use [[entry_dup]] or [[strings::dup]] to extend the
     50 // lifetime of the entry or its fields respectively.
     51 export fn next(sc: *scanner) (entry | io::EOF | error) = {
     52 	for (true) {
     53 		const line = match (bufio::read_line(sc.in)?) {
     54 		case let b: []u8 =>
     55 			yield strings::fromutf8(b)?;
     56 		case io::EOF =>
     57 			return io::EOF;
     58 		};
     59 		defer sc.lineno += 1;
     60 
     61 		free(sc.line);
     62 		sc.line = line;
     63 
     64 		const line = strings::trim(sc.line);
     65 
     66 		if (len(line) == 0 || strings::hasprefix(line, "#")) {
     67 			continue;
     68 		};
     69 
     70 		if (strings::hasprefix(line, "[")) {
     71 			const end = match (strings::index(line, ']')) {
     72 			case let idx: size =>
     73 				yield idx;
     74 			case void =>
     75 				return sc.lineno: syntaxerr;
     76 			};
     77 			free(sc.section);
     78 			sc.section = strings::dup(strings::sub(line, 1, end));
     79 			continue;
     80 		};
     81 
     82 		const eq = match (strings::index(line, '=')) {
     83 		case let idx: size =>
     84 			yield idx;
     85 		case void =>
     86 			return sc.lineno: syntaxerr;
     87 		};
     88 		return (
     89 			sc.section,
     90 			strings::sub(line, 0, eq),
     91 			strings::sub(line, eq + 1, strings::end),
     92 		);
     93 	};
     94 };