hare

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

sort.ha (3216B)


      1 // SPDX-License-Identifier: GPL-3.0-only
      2 // (c) Hare authors <https://harelang.org>
      3 
      4 use hare::ast;
      5 use sort;
      6 use strings;
      7 
      8 // Sorts declarations by:
      9 // - removing unexported declarations,
     10 // - setting the "exported" field of all remaining declarations to false, so the
     11 //   "export" keyword isn't unparsed,
     12 // - moving undocumented declarations to the end,
     13 // - sorting by identifier,
     14 // - removing the initializer from globals and the body from functions,
     15 // - ensuring that only one member is present in each declaration:
     16 //   "let x: int, y: int;" becomes two declarations: "let x: int; let y: int;".
     17 export fn sort_decls(decls: []ast::decl) summary = {
     18 	let sorted = summary { ... };
     19 
     20 	for (let decl .. decls) {
     21 		if (!decl.exported) {
     22 			continue;
     23 		};
     24 
     25 		match (decl.decl) {
     26 		case let f: ast::decl_func =>
     27 			append(sorted.funcs, ast::decl {
     28 				exported = false,
     29 				start = decl.start,
     30 				end = decl.end,
     31 				decl = ast::decl_func {
     32 					symbol = f.symbol,
     33 					ident = f.ident,
     34 					prototype = f.prototype,
     35 					body = null,
     36 					attrs = f.attrs,
     37 				},
     38 				docs = decl.docs,
     39 			});
     40 		case let types: []ast::decl_type =>
     41 			for (let t .. types) {
     42 				let bucket = &sorted.types;
     43 				if (t._type.flags & ast::type_flag::ERROR == ast::type_flag::ERROR) {
     44 					bucket = &sorted.errors;
     45 				};
     46 				append(bucket, ast::decl {
     47 					exported = false,
     48 					start = decl.start,
     49 					end = decl.end,
     50 					decl = alloc([t]),
     51 					docs = decl.docs,
     52 				});
     53 			};
     54 		case let consts: []ast::decl_const =>
     55 			for (let c .. consts) {
     56 				append(sorted.constants, ast::decl {
     57 					exported = false,
     58 					start = decl.start,
     59 					end = decl.end,
     60 					decl = alloc([c]),
     61 					docs = decl.docs,
     62 				});
     63 			};
     64 		case let globals: []ast::decl_global =>
     65 			for (let g .. globals) {
     66 				append(sorted.globals, ast::decl {
     67 					exported = false,
     68 					start = decl.start,
     69 					end = decl.end,
     70 					decl = alloc([ast::decl_global {
     71 						is_const = g.is_const,
     72 						is_threadlocal = g.is_threadlocal,
     73 						symbol = g.symbol,
     74 						ident = g.ident,
     75 						_type = g._type,
     76 						init = null,
     77 					}]),
     78 					docs = decl.docs,
     79 				});
     80 			};
     81 		case ast::assert_expr => void;
     82 		};
     83 	};
     84 
     85 	sort::sort(sorted.constants, size(ast::decl), &decl_cmp);
     86 	sort::sort(sorted.errors, size(ast::decl), &decl_cmp);
     87 	sort::sort(sorted.types, size(ast::decl), &decl_cmp);
     88 	sort::sort(sorted.globals, size(ast::decl), &decl_cmp);
     89 	sort::sort(sorted.funcs, size(ast::decl), &decl_cmp);
     90 	return sorted;
     91 };
     92 
     93 fn decl_cmp(a: const *opaque, b: const *opaque) int = {
     94 	const a = a: const *ast::decl;
     95 	const b = b: const *ast::decl;
     96 	if (a.docs == "" && b.docs != "") {
     97 		return 1;
     98 	} else if (a.docs != "" && b.docs == "") {
     99 		return -1;
    100 	};
    101 	const id_a = decl_ident(a), id_b = decl_ident(b);
    102 	return strings::compare(id_a[len(id_a) - 1], id_b[len(id_b) - 1]);
    103 };
    104 
    105 fn decl_ident(decl: *ast::decl) ast::ident = {
    106 	match (decl.decl) {
    107 	case let f: ast::decl_func =>
    108 		return f.ident;
    109 	case let t: []ast::decl_type =>
    110 		assert(len(t) == 1);
    111 		return t[0].ident;
    112 	case let c: []ast::decl_const =>
    113 		assert(len(c) == 1);
    114 		return c[0].ident;
    115 	case let g: []ast::decl_global =>
    116 		assert(len(g) == 1);
    117 		return g[0].ident;
    118 	case ast::assert_expr => abort();
    119 	};
    120 };