hare

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

type.ha (12644B)


      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 ltok::NOMEM =>
    136 		yield builtin_type::NOMEM;
    137 	case =>
    138 		return syntaxerr(lex::mkloc(lexer),
    139 			"Unexpected {}, was expecting primitive type",
    140 			lex::tokstr(tok));
    141 	};
    142 	return ast::_type {
    143 		start = tok.2,
    144 		end = lex::prevloc(lexer),
    145 		flags = ast::type_flag::NONE,
    146 		repr = builtin,
    147 	};
    148 };
    149 
    150 fn alias_type(lexer: *lex::lexer) (ast::_type | error) = {
    151 	const start = lex::mkloc(lexer);
    152 	let unwrap = try(lexer, ltok::ELLIPSIS)? is lex::token;
    153 	let ident = ident(lexer)?;
    154 	return ast::_type {
    155 		start = start,
    156 		end = lex::prevloc(lexer),
    157 		flags = 0,
    158 		repr = ast::alias_type {
    159 			unwrap = unwrap,
    160 			ident = ident,
    161 		},
    162 	};
    163 };
    164 
    165 fn pointer_type(lexer: *lex::lexer) (ast::_type | error) = {
    166 	const start = lex::mkloc(lexer);
    167 	let flags = match (try(lexer, ltok::NULLABLE)?) {
    168 	case void =>
    169 		yield ast::pointer_flag::NONE;
    170 	case =>
    171 		yield ast::pointer_flag::NULLABLE;
    172 	};
    173 	want(lexer, ltok::TIMES)?;
    174 	let _type = _type(lexer)?;
    175 	return ast::_type {
    176 		start = start,
    177 		end = lex::prevloc(lexer),
    178 		flags = ast::type_flag::NONE,
    179 		repr = ast::pointer_type {
    180 			referent = alloc(_type)!,
    181 			flags = flags,
    182 		},
    183 	};
    184 };
    185 
    186 fn tagged_type(
    187 	lexer: *lex::lexer,
    188 	first: ast::_type,
    189 	start: lex::location
    190 ) (ast::_type | error) = {
    191 	let tagged: ast::tagged_type = [];
    192 	append(tagged, alloc(first)!)!;
    193 	for (true) {
    194 		append(tagged, alloc(_type(lexer)?)!)!;
    195 
    196 		match (try(lexer, ltok::BOR)?) {
    197 		case lex::token =>
    198 			match (try(lexer, ltok::RPAREN)) {
    199 			case lex::token => break;
    200 			case => void;
    201 			};
    202 		case void =>
    203 			want(lexer, ltok::RPAREN)?;
    204 			break;
    205 		};
    206 	};
    207 	return ast::_type {
    208 		start = start,
    209 		end = lex::prevloc(lexer),
    210 		flags = ast::type_flag::NONE,
    211 		repr = tagged,
    212 	};
    213 };
    214 
    215 fn tuple_type(
    216 	lexer: *lex::lexer,
    217 	first: ast::_type,
    218 	start: lex::location
    219 ) (ast::_type | error) = {
    220 	let tuple: ast::tuple_type = [];
    221 	append(tuple, alloc(first)!)!;
    222 	for (true) {
    223 		append(tuple, alloc(_type(lexer)?)!)!;
    224 		match (try(lexer, ltok::COMMA)?) {
    225 		case lex::token =>
    226 			match (try(lexer, ltok::RPAREN)) {
    227 			case lex::token => break;
    228 			case => void;
    229 			};
    230 		case void =>
    231 			want(lexer, ltok::RPAREN)?;
    232 			break;
    233 		};
    234 	};
    235 	return ast::_type {
    236 		start = start,
    237 		end = lex::prevloc(lexer),
    238 		flags = ast::type_flag::NONE,
    239 		repr = tuple,
    240 	};
    241 };
    242 
    243 fn fn_type(lexer: *lex::lexer) (ast::_type | error) = {
    244 	const start = lex::mkloc(lexer);
    245 	want(lexer, ltok::FN)?;
    246 	let proto = prototype(lexer)?;
    247 	return ast::_type {
    248 		start = start,
    249 		end = lex::prevloc(lexer),
    250 		flags = 0,
    251 		repr = proto,
    252 	};
    253 };
    254 
    255 fn struct_union_type(lexer: *lex::lexer) (ast::_type | error) = {
    256 	let membs: []ast::struct_member = [];
    257 	let kind = want(lexer, ltok::STRUCT, ltok::UNION)?;
    258 	let packed = false;
    259 
    260 	if (kind.0 == ltok::STRUCT && try(lexer, ltok::ATTR_PACKED)? is lex::token) {
    261 		packed = true;
    262 	};
    263 
    264 	want(lexer, ltok::LBRACE)?;
    265 
    266 	for (true) {
    267 		if (try(lexer, ltok::RBRACE)? is lex::token) {
    268 			synassert(lex::mkloc(lexer), len(membs) != 0,
    269 				"Expected field list")?;
    270 			break;
    271 		};
    272 
    273 		let comment = "";
    274 
    275 		let offs: nullable *ast::expr = match (try(lexer, ltok::ATTR_OFFSET)?) {
    276 		case void =>
    277 			yield null;
    278 		case lex::token =>
    279 			comment = strings::dup(lex::comment(lexer));
    280 			want(lexer, ltok::LPAREN)?;
    281 			let ex = expr(lexer)?;
    282 			want(lexer, ltok::RPAREN)?;
    283 			yield alloc(ex)!;
    284 		};
    285 
    286 		let tok = want(lexer, ltok::NAME, ltok::STRUCT, ltok::UNION)?;
    287 		if (comment == "") {
    288 			comment = strings::dup(lex::comment(lexer));
    289 		};
    290 		switch (tok.0) {
    291 		case ltok::NAME =>
    292 			lex::unlex(lexer, tok);
    293 			let memb = struct_embed_or_field(lexer, offs, comment)?;
    294 			append(membs, memb)!;
    295 		case ltok::STRUCT, ltok::UNION =>
    296 			lex::unlex(lexer, tok);
    297 			let subtype = struct_union_type(lexer)?;
    298 			append(membs, ast::struct_member {
    299 				_offset = offs,
    300 				member = alloc(subtype)!,
    301 				docs = comment,
    302 			})!;
    303 		case => abort();
    304 		};
    305 
    306 		switch (want(lexer, ltok::RBRACE, ltok::COMMA)?.0) {
    307 		case ltok::RBRACE => break;
    308 		case ltok::COMMA =>
    309 			const linecomment = lex::comment(lexer);
    310 			const docs = &membs[len(membs) - 1].docs;
    311 			if (linecomment != "" && *docs == "") {
    312 				*docs = strings::dup(linecomment);
    313 				free(lexer.comment);
    314 				lexer.comment = "";
    315 			};
    316 		case => abort();
    317 		};
    318 	};
    319 
    320 	return ast::_type {
    321 		start = kind.2,
    322 		end = lex::prevloc(lexer),
    323 		flags = ast::type_flag::NONE,
    324 		repr = switch (kind.0) {
    325 		case ltok::STRUCT =>
    326 			yield ast::struct_type { members = membs, packed = packed, ...};
    327 		case ltok::UNION =>
    328 			yield membs: ast::union_type;
    329 		case => abort();
    330 		},
    331 	};
    332 };
    333 
    334 fn struct_embed_or_field(
    335 	lexer: *lex::lexer,
    336 	offs: nullable *ast::expr,
    337 	comment: str,
    338 ) (ast::struct_member | error) = {
    339 	// Disambiguates between `name: type` and `identifier`
    340 	//
    341 	// struct-union-field
    342 	// 	name : type
    343 	// 	identifier
    344 	//
    345 	// identifier
    346 	// 	name
    347 	// 	name :: identifier
    348 	let name = want(lexer, ltok::NAME)?;
    349 
    350 	let id: ast::ident = match (try(lexer, ltok::COLON, ltok::DOUBLE_COLON)?) {
    351 	case void =>
    352 		yield alloc([name.1 as str])!;
    353 	case let tok: lex::token =>
    354 		yield switch (tok.0) {
    355 		case ltok::COLON =>
    356 			let field = ast::struct_field {
    357 				name = name.1 as str,
    358 				_type = alloc(_type(lexer)?)!,
    359 			};
    360 			return ast::struct_member {
    361 				_offset = offs,
    362 				member = field,
    363 				docs = comment,
    364 			};
    365 		case ltok::DOUBLE_COLON =>
    366 			let id = ident(lexer)?;
    367 			insert(id[0], name.1 as str)!;
    368 			yield id;
    369 		case => abort();
    370 		};
    371 	};
    372 
    373 	return ast::struct_member {
    374 		_offset = offs,
    375 		member = id: ast::struct_alias,
    376 		docs = comment,
    377 	};
    378 };
    379 
    380 fn array_slice_type(lexer: *lex::lexer) (ast::_type | error) = {
    381 	let start = want(lexer, ltok::LBRACKET)?;
    382 
    383 	let length = match (try(lexer, ltok::UNDERSCORE,
    384 		ltok::TIMES, ltok::RBRACKET)?) {
    385 	case void =>
    386 		yield alloc(expr(lexer)?)!;
    387 	case let tok: lex::token =>
    388 		yield switch (tok.0) {
    389 		case ltok::UNDERSCORE =>
    390 			yield ast::len_contextual;
    391 		case ltok::TIMES =>
    392 			yield ast::len_unbounded;
    393 		case ltok::RBRACKET =>
    394 			yield ast::len_slice;
    395 		case => abort();
    396 		};
    397 	};
    398 
    399 	if (!(length is ast::len_slice)) {
    400 		want(lexer, ltok::RBRACKET)?;
    401 	};
    402 
    403 	let _type = _type(lexer)?;
    404 	return ast::_type {
    405 		start = start.2,
    406 		end = lex::prevloc(lexer),
    407 		flags = ast::type_flag::NONE,
    408 		repr = ast::list_type {
    409 			length = length,
    410 			members = alloc(_type)!,
    411 		},
    412 	};
    413 };
    414 
    415 fn enum_type(lexer: *lex::lexer) (ast::_type | error) = {
    416 	let start = want(lexer, ltok::ENUM)?;
    417 
    418 	const storage = match (try(lexer, ltok::LBRACE, ltok::RUNE)?) {
    419 	case void =>
    420 		let storage = integer_type(lexer)?;
    421 		want(lexer, ltok::LBRACE)?;
    422 		yield storage;
    423 	case let tok: lex::token =>
    424 		yield switch (tok.0) {
    425 		case ltok::LBRACE =>
    426 			yield builtin_type::INT;
    427 		case ltok::RUNE =>
    428 			want(lexer, ltok::LBRACE)?;
    429 			yield builtin_type::RUNE;
    430 		case => abort(); // unreachable
    431 		};
    432 	};
    433 
    434 	let membs: []ast::enum_field = [];
    435 	for (true) {
    436 		if (try(lexer, ltok::RBRACE)? is lex::token) {
    437 			synassert(lex::mkloc(lexer), len(membs) != 0,
    438 				"Expected member list")?;
    439 			break;
    440 		};
    441 
    442 		const loc = lex::mkloc(lexer);
    443 		let name = want(lexer, ltok::NAME)?;
    444 		let comment = strings::dup(lex::comment(lexer));
    445 		let value: nullable *ast::expr =
    446 			if (try(lexer, ltok::EQUAL)? is lex::token)
    447 				alloc(expr(lexer)?)!
    448 			else null;
    449 
    450 		defer append(membs, ast::enum_field {
    451 			name = name.1 as str,
    452 			value = value,
    453 			loc = loc,
    454 			docs = comment,
    455 		})!;
    456 
    457 		switch (want(lexer, ltok::COMMA, ltok::RBRACE)?.0) {
    458 		case ltok::COMMA =>
    459 			const linecomment = lex::comment(lexer);
    460 			if (linecomment != "" && comment == "") {
    461 				free(comment);
    462 				comment = strings::dup(linecomment);
    463 				free(lexer.comment);
    464 				lexer.comment = "";
    465 			};
    466 		case ltok::RBRACE => break;
    467 		case => abort();
    468 		};
    469 	};
    470 
    471 	return ast::_type {
    472 		start = start.2,
    473 		end = lex::prevloc(lexer),
    474 		flags = ast::type_flag::NONE,
    475 		repr = ast::enum_type {
    476 			storage = storage,
    477 			values = membs,
    478 		},
    479 	};
    480 };
    481 
    482 // Parses a type, e.g. '[]int'.
    483 export fn _type(lexer: *lex::lexer) (ast::_type | error) = {
    484 	let flags = ast::type_flag::NONE;
    485 	if (try(lexer, ltok::CONST)? is lex::token) {
    486 		flags |= ast::type_flag::CONST;
    487 	};
    488 
    489 	if (try(lexer, ltok::LNOT)? is lex::token) {
    490 		flags |= ast::type_flag::ERROR;
    491 	};
    492 
    493 	let tok = peek(lexer)? as lex::token;
    494 	let typ: ast::_type = switch (tok.0) {
    495 	case ltok::RUNE, ltok::STR, ltok::BOOL, ltok::DONE, ltok::I8, ltok::I16,
    496 			ltok::I32, ltok::I64, ltok::U8, ltok::U16, ltok::U32,
    497 			ltok::U64, ltok::INT, ltok::UINT, ltok::UINTPTR,
    498 			ltok::SIZE, ltok::F32, ltok::F64, ltok::VALIST,
    499 			ltok::VOID, ltok::OPAQUE, ltok::NEVER, ltok::NOMEM =>
    500 		yield primitive_type(lexer)?;
    501 	case ltok::ENUM =>
    502 		yield enum_type(lexer)?;
    503 	case ltok::NULLABLE, ltok::TIMES =>
    504 		yield pointer_type(lexer)?;
    505 	case ltok::STRUCT, ltok::UNION =>
    506 		yield struct_union_type(lexer)?;
    507 	case ltok::LBRACKET =>
    508 		yield array_slice_type(lexer)?;
    509 	case ltok::LPAREN =>
    510 		want(lexer, ltok::LPAREN)?;
    511 		let t = _type(lexer)?;
    512 		yield switch (want(lexer, ltok::BOR, ltok::COMMA)?.0) {
    513 		case ltok::BOR =>
    514 			yield tagged_type(lexer, t, tok.2)?;
    515 		case ltok::COMMA =>
    516 			yield tuple_type(lexer, t, tok.2)?;
    517 		case => abort("unreachable");
    518 		};
    519 	case ltok::FN =>
    520 		yield fn_type(lexer)?;
    521 	case ltok::ELLIPSIS, ltok::NAME =>
    522 		yield alias_type(lexer)?;
    523 	case =>
    524 		return syntaxerr(lex::mkloc(lexer),
    525 			"Unexpected {}, was expecting type",
    526 			lex::tokstr(tok));
    527 	};
    528 
    529 	typ.flags |= flags;
    530 	return typ;
    531 };