hare

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

type.ha (12555B)


      1 // SPDX-License-Identifier: MPL-2.0
      2 // (c) Hare authors <https://harelang.org>
      3 
      4 use hare::ast;
      5 use hare::ast::{builtin_type};
      6 use hare::lex;
      7 use hare::lex::{ltok};
      8 use strings;
      9 
     10 fn prototype(lexer: *lex::lexer) (ast::func_type | error) = {
     11 	let variadism = ast::variadism::NONE;
     12 	let params: []ast::func_param = [];
     13 	want(lexer, ltok::LPAREN)?;
     14 	for (try(lexer, ltok::RPAREN)? is void) {
     15 		let loc = lex::mkloc(lexer);
     16 		match (try(lexer, ltok::ELLIPSIS)?) {
     17 		case lex::token =>
     18 			variadism = ast::variadism::C;
     19 			want(lexer, ltok::RPAREN)?;
     20 			break;
     21 		case void => void;
     22 		};
     23 
     24 		let name_or_type = _type(lexer)?;
     25 		match (try(lexer, ltok::COLON)?) {
     26 		case void =>
     27 			append(params, ast::func_param {
     28 				loc = loc,
     29 				name = "",
     30 				_type = alloc(name_or_type),
     31 				default_value = void,
     32 			});
     33 		case lex::token =>
     34 			// Bit of a hack because we can't unlex twice.
     35 			synassert(loc, name_or_type.repr is ast::alias_type,
     36 				"Invalid parameter name")?;
     37 			let ns = (name_or_type.repr as ast::alias_type).ident;
     38 			synassert(loc, len(ns) == 1, "Invalid parameter name")?;
     39 			append(params, ast::func_param {
     40 				loc = loc,
     41 				name = ns[0],
     42 				_type = alloc(_type(lexer)?),
     43 				default_value = void,
     44 			});
     45 		};
     46 		match (try(lexer, ltok::EQUAL)?) {
     47 		case void =>
     48 			yield void;
     49 		case lex::token =>
     50 			params[len(params) - 1].default_value = expr(lexer)?;
     51 		};
     52 		match (try(lexer, ltok::ELLIPSIS)?) {
     53 		case lex::token =>
     54 			variadism = ast::variadism::HARE;
     55 			want(lexer, ltok::RPAREN)?;
     56 			break;
     57 		case void => void;
     58 		};
     59 		match (try(lexer, ltok::COMMA)?) {
     60 		case void =>
     61 			want(lexer, ltok::RPAREN)?;
     62 			break;
     63 		case lex::token => void;
     64 		};
     65 	};
     66 	let t = _type(lexer)?;
     67 	return ast::func_type {
     68 		result = alloc(t),
     69 		variadism = variadism,
     70 		params = params,
     71 	};
     72 };
     73 
     74 fn integer_type(
     75 	lexer: *lex::lexer,
     76 ) (builtin_type | error) = {
     77 	switch (want(lexer)?.0) {
     78 	case ltok::INT =>
     79 		return builtin_type::INT;
     80 	case ltok::I8 =>
     81 		return builtin_type::I8;
     82 	case ltok::I16 =>
     83 		return builtin_type::I16;
     84 	case ltok::I32 =>
     85 		return builtin_type::I32;
     86 	case ltok::I64 =>
     87 		return builtin_type::I64;
     88 	case ltok::SIZE =>
     89 		return builtin_type::SIZE;
     90 	case ltok::UINT =>
     91 		return builtin_type::UINT;
     92 	case ltok::UINTPTR =>
     93 		return builtin_type::UINTPTR;
     94 	case ltok::U8 =>
     95 		return builtin_type::U8;
     96 	case ltok::U16 =>
     97 		return builtin_type::U16;
     98 	case ltok::U32 =>
     99 		return builtin_type::U32;
    100 	case ltok::U64 =>
    101 		return builtin_type::U64;
    102 	case =>
    103 		return syntaxerr(lex::mkloc(lexer), "Expected integer type");
    104 	};
    105 };
    106 
    107 fn primitive_type(lexer: *lex::lexer) (ast::_type | error) = {
    108 	let tok = want(lexer)?;
    109 	let builtin = switch (tok.0) {
    110 	case ltok::I8, ltok::I16, ltok::I32, ltok::I64,
    111 			ltok::INT, ltok::UINT, ltok::UINTPTR, ltok::SIZE,
    112 			ltok::U8, ltok::U16, ltok::U32, ltok::U64 =>
    113 		lex::unlex(lexer, tok);
    114 		yield integer_type(lexer)?;
    115 	case ltok::RUNE =>
    116 		yield builtin_type::RUNE;
    117 	case ltok::STR =>
    118 		yield builtin_type::STR;
    119 	case ltok::F32 =>
    120 		yield builtin_type::F32;
    121 	case ltok::F64 =>
    122 		yield builtin_type::F64;
    123 	case ltok::BOOL =>
    124 		yield builtin_type::BOOL;
    125 	case ltok::DONE =>
    126 		yield builtin_type::DONE;
    127 	case ltok::VALIST =>
    128 		yield builtin_type::VALIST;
    129 	case ltok::VOID =>
    130 		yield builtin_type::VOID;
    131 	case ltok::OPAQUE =>
    132 		yield builtin_type::OPAQUE;
    133 	case ltok::NEVER =>
    134 		yield builtin_type::NEVER;
    135 	case =>
    136 		return syntaxerr(lex::mkloc(lexer),
    137 			"Unexected {}, was expecting primitive type",
    138 			lex::tokstr(tok));
    139 	};
    140 	return ast::_type {
    141 		start = tok.2,
    142 		end = lex::prevloc(lexer),
    143 		flags = ast::type_flag::NONE,
    144 		repr = builtin,
    145 	};
    146 };
    147 
    148 fn alias_type(lexer: *lex::lexer) (ast::_type | error) = {
    149 	const start = lex::mkloc(lexer);
    150 	let unwrap = try(lexer, ltok::ELLIPSIS)? is lex::token;
    151 	let ident = ident(lexer)?;
    152 	return ast::_type {
    153 		start = start,
    154 		end = lex::prevloc(lexer),
    155 		flags = 0,
    156 		repr = ast::alias_type {
    157 			unwrap = unwrap,
    158 			ident = ident,
    159 		},
    160 	};
    161 };
    162 
    163 fn pointer_type(lexer: *lex::lexer) (ast::_type | error) = {
    164 	const start = lex::mkloc(lexer);
    165 	let flags = match (try(lexer, ltok::NULLABLE)?) {
    166 	case void =>
    167 		yield ast::pointer_flag::NONE;
    168 	case =>
    169 		yield ast::pointer_flag::NULLABLE;
    170 	};
    171 	want(lexer, ltok::TIMES)?;
    172 	let _type = _type(lexer)?;
    173 	return ast::_type {
    174 		start = start,
    175 		end = lex::prevloc(lexer),
    176 		flags = ast::type_flag::NONE,
    177 		repr = ast::pointer_type {
    178 			referent = alloc(_type),
    179 			flags = flags,
    180 		},
    181 	};
    182 };
    183 
    184 fn tagged_type(
    185 	lexer: *lex::lexer,
    186 	first: ast::_type,
    187 	start: lex::location
    188 ) (ast::_type | error) = {
    189 	let tagged: ast::tagged_type = [];
    190 	append(tagged, alloc(first));
    191 	for (true) {
    192 		append(tagged, alloc(_type(lexer)?));
    193 
    194 		match (try(lexer, ltok::BOR)?) {
    195 		case lex::token =>
    196 			match (try(lexer, ltok::RPAREN)) {
    197 			case lex::token => break;
    198 			case => void;
    199 			};
    200 		case void =>
    201 			want(lexer, ltok::RPAREN)?;
    202 			break;
    203 		};
    204 	};
    205 	return ast::_type {
    206 		start = start,
    207 		end = lex::prevloc(lexer),
    208 		flags = ast::type_flag::NONE,
    209 		repr = tagged,
    210 	};
    211 };
    212 
    213 fn tuple_type(
    214 	lexer: *lex::lexer,
    215 	first: ast::_type,
    216 	start: lex::location
    217 ) (ast::_type | error) = {
    218 	let tuple: ast::tuple_type = [];
    219 	append(tuple, alloc(first));
    220 	for (true) {
    221 		append(tuple, alloc(_type(lexer)?));
    222 		match (try(lexer, ltok::COMMA)?) {
    223 		case lex::token =>
    224 			match (try(lexer, ltok::RPAREN)) {
    225 			case lex::token => break;
    226 			case => void;
    227 			};
    228 		case void =>
    229 			want(lexer, ltok::RPAREN)?;
    230 			break;
    231 		};
    232 	};
    233 	return ast::_type {
    234 		start = start,
    235 		end = lex::prevloc(lexer),
    236 		flags = ast::type_flag::NONE,
    237 		repr = tuple,
    238 	};
    239 };
    240 
    241 fn fn_type(lexer: *lex::lexer) (ast::_type | error) = {
    242 	const start = lex::mkloc(lexer);
    243 	want(lexer, ltok::FN)?;
    244 	let proto = prototype(lexer)?;
    245 	return ast::_type {
    246 		start = start,
    247 		end = lex::prevloc(lexer),
    248 		flags = 0,
    249 		repr = proto,
    250 	};
    251 };
    252 
    253 fn struct_union_type(lexer: *lex::lexer) (ast::_type | error) = {
    254 	let membs: []ast::struct_member = [];
    255 	let kind = want(lexer, ltok::STRUCT, ltok::UNION)?;
    256 	let packed = false;
    257 
    258 	if (kind.0 == ltok::STRUCT && try(lexer, ltok::ATTR_PACKED)? is lex::token) {
    259 		packed = true;
    260 	};
    261 
    262 	want(lexer, ltok::LBRACE)?;
    263 
    264 	for (true) {
    265 		if (try(lexer, ltok::RBRACE)? is lex::token) {
    266 			synassert(lex::mkloc(lexer), len(membs) != 0,
    267 				"Expected field list")?;
    268 			break;
    269 		};
    270 
    271 		let comment = "";
    272 
    273 		let offs: nullable *ast::expr = match (try(lexer, ltok::ATTR_OFFSET)?) {
    274 		case void =>
    275 			yield null;
    276 		case lex::token =>
    277 			comment = strings::dup(lex::comment(lexer));
    278 			want(lexer, ltok::LPAREN)?;
    279 			let ex = expr(lexer)?;
    280 			want(lexer, ltok::RPAREN)?;
    281 			yield alloc(ex);
    282 		};
    283 
    284 		let tok = want(lexer, ltok::NAME, ltok::STRUCT, ltok::UNION)?;
    285 		if (comment == "") {
    286 			comment = strings::dup(lex::comment(lexer));
    287 		};
    288 		switch (tok.0) {
    289 		case ltok::NAME =>
    290 			lex::unlex(lexer, tok);
    291 			let memb = struct_embed_or_field(lexer, offs, comment)?;
    292 			append(membs, memb);
    293 		case ltok::STRUCT, ltok::UNION =>
    294 			lex::unlex(lexer, tok);
    295 			let subtype = struct_union_type(lexer)?;
    296 			append(membs, ast::struct_member {
    297 				_offset = offs,
    298 				member = alloc(subtype),
    299 				docs = comment,
    300 			});
    301 		case => abort();
    302 		};
    303 
    304 		switch (want(lexer, ltok::RBRACE, ltok::COMMA)?.0) {
    305 		case ltok::RBRACE => break;
    306 		case ltok::COMMA =>
    307 			const linecomment = lex::comment(lexer);
    308 			const docs = &membs[len(membs) - 1].docs;
    309 			if (linecomment != "" && *docs == "") {
    310 				*docs = strings::dup(linecomment);
    311 				free(lexer.comment);
    312 				lexer.comment = "";
    313 			};
    314 		case => abort();
    315 		};
    316 	};
    317 
    318 	return ast::_type {
    319 		start = kind.2,
    320 		end = lex::prevloc(lexer),
    321 		flags = ast::type_flag::NONE,
    322 		repr = switch (kind.0) {
    323 		case ltok::STRUCT =>
    324 			yield ast::struct_type { members = membs, packed = packed, ...};
    325 		case ltok::UNION =>
    326 			yield membs: ast::union_type;
    327 		case => abort();
    328 		},
    329 	};
    330 };
    331 
    332 fn struct_embed_or_field(
    333 	lexer: *lex::lexer,
    334 	offs: nullable *ast::expr,
    335 	comment: str,
    336 ) (ast::struct_member | error) = {
    337 	// Disambiguates between `name: type` and `identifier`
    338 	//
    339 	// struct-union-field
    340 	// 	name : type
    341 	// 	identifier
    342 	//
    343 	// identifier
    344 	// 	name
    345 	// 	name :: identifier
    346 	let name = want(lexer, ltok::NAME)?;
    347 
    348 	let id: ast::ident = match (try(lexer, ltok::COLON, ltok::DOUBLE_COLON)?) {
    349 	case void =>
    350 		yield alloc([name.1 as str]);
    351 	case let tok: lex::token =>
    352 		yield switch (tok.0) {
    353 		case ltok::COLON =>
    354 			let field = ast::struct_field {
    355 				name = name.1 as str,
    356 				_type = alloc(_type(lexer)?),
    357 			};
    358 			return ast::struct_member {
    359 				_offset = offs,
    360 				member = field,
    361 				docs = comment,
    362 			};
    363 		case ltok::DOUBLE_COLON =>
    364 			let id = ident(lexer)?;
    365 			insert(id[0], name.1 as str);
    366 			yield id;
    367 		case => abort();
    368 		};
    369 	};
    370 
    371 	return ast::struct_member {
    372 		_offset = offs,
    373 		member = id: ast::struct_alias,
    374 		docs = comment,
    375 	};
    376 };
    377 
    378 fn array_slice_type(lexer: *lex::lexer) (ast::_type | error) = {
    379 	let start = want(lexer, ltok::LBRACKET)?;
    380 
    381 	let length = match (try(lexer, ltok::UNDERSCORE,
    382 		ltok::TIMES, ltok::RBRACKET)?) {
    383 	case void =>
    384 		yield alloc(expr(lexer)?);
    385 	case let tok: lex::token =>
    386 		yield switch (tok.0) {
    387 		case ltok::UNDERSCORE =>
    388 			yield ast::len_contextual;
    389 		case ltok::TIMES =>
    390 			yield ast::len_unbounded;
    391 		case ltok::RBRACKET =>
    392 			yield ast::len_slice;
    393 		case => abort();
    394 		};
    395 	};
    396 
    397 	if (!(length is ast::len_slice)) {
    398 		want(lexer, ltok::RBRACKET)?;
    399 	};
    400 
    401 	let _type = _type(lexer)?;
    402 	return ast::_type {
    403 		start = start.2,
    404 		end = lex::prevloc(lexer),
    405 		flags = ast::type_flag::NONE,
    406 		repr = ast::list_type {
    407 			length = length,
    408 			members = alloc(_type),
    409 		},
    410 	};
    411 };
    412 
    413 fn enum_type(lexer: *lex::lexer) (ast::_type | error) = {
    414 	let start = want(lexer, ltok::ENUM)?;
    415 
    416 	const storage = match (try(lexer, ltok::LBRACE, ltok::RUNE)?) {
    417 	case void =>
    418 		let storage = integer_type(lexer)?;
    419 		want(lexer, ltok::LBRACE)?;
    420 		yield storage;
    421 	case let tok: lex::token =>
    422 		yield switch (tok.0) {
    423 		case ltok::LBRACE =>
    424 			yield builtin_type::INT;
    425 		case ltok::RUNE =>
    426 			want(lexer, ltok::LBRACE)?;
    427 			yield builtin_type::RUNE;
    428 		case => abort(); // unreachable
    429 		};
    430 	};
    431 
    432 	let membs: []ast::enum_field = [];
    433 	for (true) {
    434 		if (try(lexer, ltok::RBRACE)? is lex::token) {
    435 			synassert(lex::mkloc(lexer), len(membs) != 0,
    436 				"Expected member list")?;
    437 			break;
    438 		};
    439 
    440 		const loc = lex::mkloc(lexer);
    441 		let name = want(lexer, ltok::NAME)?;
    442 		let comment = strings::dup(lex::comment(lexer));
    443 		let value: nullable *ast::expr =
    444 			if (try(lexer, ltok::EQUAL)? is lex::token)
    445 				alloc(expr(lexer)?)
    446 			else null;
    447 
    448 		defer append(membs, ast::enum_field {
    449 			name = name.1 as str,
    450 			value = value,
    451 			loc = loc,
    452 			docs = comment,
    453 		});
    454 
    455 		switch (want(lexer, ltok::COMMA, ltok::RBRACE)?.0) {
    456 		case ltok::COMMA =>
    457 			const linecomment = lex::comment(lexer);
    458 			if (linecomment != "" && comment == "") {
    459 				free(comment);
    460 				comment = strings::dup(linecomment);
    461 				free(lexer.comment);
    462 				lexer.comment = "";
    463 			};
    464 		case ltok::RBRACE => break;
    465 		case => abort();
    466 		};
    467 	};
    468 
    469 	return ast::_type {
    470 		start = start.2,
    471 		end = lex::prevloc(lexer),
    472 		flags = ast::type_flag::NONE,
    473 		repr = ast::enum_type {
    474 			storage = storage,
    475 			values = membs,
    476 		},
    477 	};
    478 };
    479 
    480 // Parses a type, e.g. '[]int'.
    481 export fn _type(lexer: *lex::lexer) (ast::_type | error) = {
    482 	let flags = ast::type_flag::NONE;
    483 	if (try(lexer, ltok::CONST)? is lex::token) {
    484 		flags |= ast::type_flag::CONST;
    485 	};
    486 
    487 	if (try(lexer, ltok::LNOT)? is lex::token) {
    488 		flags |= ast::type_flag::ERROR;
    489 	};
    490 
    491 	let tok = peek(lexer)? as lex::token;
    492 	let typ: ast::_type = switch (tok.0) {
    493 	case ltok::RUNE, ltok::STR, ltok::BOOL, ltok::DONE, ltok::I8, ltok::I16,
    494 			ltok::I32, ltok::I64, ltok::U8, ltok::U16, ltok::U32,
    495 			ltok::U64, ltok::INT, ltok::UINT, ltok::UINTPTR,
    496 			ltok::SIZE, ltok::F32, ltok::F64, ltok::VALIST,
    497 			ltok::VOID, ltok::OPAQUE, ltok::NEVER =>
    498 		yield primitive_type(lexer)?;
    499 	case ltok::ENUM =>
    500 		yield enum_type(lexer)?;
    501 	case ltok::NULLABLE, ltok::TIMES =>
    502 		yield pointer_type(lexer)?;
    503 	case ltok::STRUCT, ltok::UNION =>
    504 		yield struct_union_type(lexer)?;
    505 	case ltok::LBRACKET =>
    506 		yield array_slice_type(lexer)?;
    507 	case ltok::LPAREN =>
    508 		want(lexer, ltok::LPAREN)?;
    509 		let t = _type(lexer)?;
    510 		yield switch (want(lexer, ltok::BOR, ltok::COMMA)?.0) {
    511 		case ltok::BOR =>
    512 			yield tagged_type(lexer, t, tok.2)?;
    513 		case ltok::COMMA =>
    514 			yield tuple_type(lexer, t, tok.2)?;
    515 		case => abort("unreachable");
    516 		};
    517 	case ltok::FN =>
    518 		yield fn_type(lexer)?;
    519 	case ltok::ELLIPSIS, ltok::NAME =>
    520 		yield alias_type(lexer)?;
    521 	case =>
    522 		return syntaxerr(lex::mkloc(lexer),
    523 			"Unexpected {}, was expecting type",
    524 			lex::tokstr(tok));
    525 	};
    526 
    527 	typ.flags |= flags;
    528 	return typ;
    529 };