hare

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

load.ha (3396B)


      1 use bufio;
      2 use io;
      3 use strings;
      4 use types;
      5 
      6 // Options for [[load]].
      7 export type load_option = nestlimit;
      8 
      9 // The maximum number of nested objects or arrays that can be entered before
     10 // erroring out.
     11 export type nestlimit = uint;
     12 
     13 // Parses a JSON value from the given [[io::handle]], returning the value or an
     14 // error. The return value is allocated on the heap; use [[finish]] to free it
     15 // up when you're done using it.
     16 //
     17 // By default, this function assumes non-antagonistic inputs, and does not limit
     18 // recursion depth or memory usage. You may want to set a custom [[nestlimit]],
     19 // or incorporate an [[io::limitreader]] or similar. Alternatively, you can use
     20 // the JSON lexer ([[lex]]) directly if dealing with potentially malicious
     21 // inputs.
     22 export fn load(src: io::handle, opts: load_option...) (value | error) = {
     23 	let limit = types::UINT_MAX;
     24 	for (let i = 0z; i < len(opts); i += 1) {
     25 		limit = opts[i]: nestlimit: uint;
     26 	};
     27 	const lex = newlexer(src);
     28 	defer close(&lex);
     29 	return _load(&lex, 0, limit);
     30 };
     31 
     32 // Parses a JSON value from the given string, returning the value or an error.
     33 // The return value is allocated on the heap; use [[finish]] to free it up when
     34 // you're done using it.
     35 //
     36 // See the documentation for [[load]] for information on dealing with
     37 // potentially malicious inputs.
     38 export fn loadstr(input: str, opts: load_option...) (value | error) = {
     39 	let src = bufio::fixed(strings::toutf8(input), io::mode::READ);
     40 	return load(&src, opts...);
     41 };
     42 
     43 fn _load(lexer: *lexer, level: uint, limit: uint) (value | error) = {
     44 	const tok = mustscan(lexer)?;
     45 	match (tok) {
     46 	case _null =>
     47 		return _null;
     48 	case let b: bool =>
     49 		return b;
     50 	case let f: f64 =>
     51 		return f;
     52 	case let s: str =>
     53 		return strings::dup(s);
     54 	case arraystart =>
     55 		if (level == limit) {
     56 			return limitreached;
     57 		};
     58 		return _load_array(lexer, level + 1, limit);
     59 	case objstart =>
     60 		if (level == limit) {
     61 			return limitreached;
     62 		};
     63 		return _load_obj(lexer, level + 1, limit);
     64 	case (arrayend | objend | colon | comma) =>
     65 		return lexer.loc: invalid;
     66 	};
     67 };
     68 
     69 fn _load_array(lexer: *lexer, level: uint, limit: uint) (value | error) = {
     70 	let array: []value = [];
     71 	let tok = mustscan(lexer)?;
     72 	match (tok) {
     73 	case arrayend =>
     74 		return array;
     75 	case =>
     76 		unlex(lexer, tok);
     77 	};
     78 
     79 	for (true) {
     80 		append(array, _load(lexer, level, limit)?);
     81 
     82 		tok = mustscan(lexer)?;
     83 		match (tok) {
     84 		case comma => void;
     85 		case arrayend => break;
     86 		case =>
     87 			return lexer.loc: invalid;
     88 		};
     89 	};
     90 	return array;
     91 };
     92 
     93 fn _load_obj(lexer: *lexer, level: uint, limit: uint) (value | error) = {
     94 	let obj = newobject();
     95 	let tok = mustscan(lexer)?;
     96 	match (tok) {
     97 	case objend =>
     98 		return obj;
     99 	case =>
    100 		unlex(lexer, tok);
    101 	};
    102 
    103 	for (true) {
    104 		let tok = mustscan(lexer)?;
    105 		const key = match (tok) {
    106 		case let s: str =>
    107 			yield strings::dup(s);
    108 		case =>
    109 			return lexer.loc: invalid;
    110 		};
    111 		defer free(key);
    112 
    113 		tok = mustscan(lexer)?;
    114 		if (!(tok is colon)) {
    115 			return lexer.loc: invalid;
    116 		};
    117 
    118 		const val = _load(lexer, level, limit)?;
    119 		defer finish(val);
    120 		set(&obj, key, val);
    121 
    122 		tok = mustscan(lexer)?;
    123 		match (tok) {
    124 		case comma => void;
    125 		case objend => break;
    126 		case =>
    127 			return lexer.loc: invalid;
    128 		};
    129 	};
    130 
    131 	return obj;
    132 };
    133 
    134 fn mustscan(lexer: *lexer) (token | error) = {
    135 	match (lex(lexer)?) {
    136 	case io::EOF =>
    137 		lexer.loc.1 += 1;
    138 		return lexer.loc: invalid;
    139 	case let tok: token =>
    140 		return tok;
    141 	};
    142 };