hare

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

context.ha (3326B)


      1 // License: MPL-2.0
      2 // (c) 2022 Alexey Yerin <yyp@disroot.org>
      3 // (c) 2021-2022 Drew DeVault <sir@cmpwn.com>
      4 // (c) 2021 Ember Sawady <ecs@d2evs.net>
      5 use dirs;
      6 use fmt;
      7 use fs;
      8 use glob;
      9 use hare::ast;
     10 use memio;
     11 use os;
     12 use path;
     13 use strings;
     14 
     15 export type context = struct {
     16 	// Filesystem to use for the cache and source files.
     17 	fs: *fs::fs,
     18 	// List of paths to search, generally populated from HAREPATH plus some
     19 	// baked-in defaults.
     20 	paths: []str,
     21 	// Path to the Hare cache, generally populated from HARECACHE and
     22 	// defaulting to $XDG_CACHE_HOME/hare.
     23 	cache: str,
     24 	// Build tags to apply to this context.
     25 	tags: []tag,
     26 	// List of -D arguments passed to harec
     27 	defines: []str,
     28 };
     29 
     30 // Initializes a new context with the system default configuration. The tag list
     31 // and list of defines (arguments passed with harec -D) is borrowed from the
     32 // caller. The harepath parameter is not borrowed, but it is ignored if HAREPATH
     33 // is set in the process environment.
     34 export fn context_init(tags: []tag, defs: []str, harepath: str) context = {
     35 	let ctx = context {
     36 		fs = os::cwd,
     37 		tags = tags,
     38 		defines = defs,
     39 		paths = {
     40 			let harepath = match (os::getenv("HAREPATH")) {
     41 			case void =>
     42 				yield harepath;
     43 			case let s: str =>
     44 				yield s;
     45 			};
     46 
     47 			let path: []str = [];
     48 			let tok = strings::tokenize(harepath, ":");
     49 			for (true) match (strings::next_token(&tok)) {
     50 			case void =>
     51 				break;
     52 			case let s: str =>
     53 				append(path, strings::dup(s));
     54 			};
     55 
     56 			let vendor = glob::glob("vendor/*");
     57 			defer glob::finish(&vendor);
     58 			for (true) match (glob::next(&vendor)) {
     59 			case void =>
     60 				break;
     61 			case glob::failure =>
     62 				void; // XXX: Anything else?
     63 			case let s: str =>
     64 				append(path, strings::dup(s));
     65 			};
     66 
     67 			append(path, strings::dup("."));
     68 			yield path;
     69 		},
     70 		cache: str = match (os::getenv("HARECACHE")) {
     71 		case void =>
     72 			yield strings::dup(dirs::cache("hare"));
     73 		case let s: str =>
     74 			yield strings::dup(s);
     75 		},
     76 		...
     77 	};
     78 	return ctx;
     79 };
     80 
     81 // Frees resources associated with this context.
     82 export fn context_finish(ctx: *context) void = {
     83 	for (let i = 0z; i < len(ctx.paths); i += 1) {
     84 		free(ctx.paths[i]);
     85 	};
     86 	free(ctx.paths);
     87 	free(ctx.cache);
     88 };
     89 
     90 // Converts an identifier to a partial path (e.g. foo::bar becomes foo/bar). The
     91 // return value must be freed by the caller.
     92 export fn identpath(name: ast::ident) str = {
     93 	if (len(name) == 0) {
     94 		return strings::dup(".");
     95 	};
     96 	let buf = path::init()!;
     97 	for (let i = 0z; i < len(name); i += 1) {
     98 		path::push(&buf, name[i])!;
     99 	};
    100 	return strings::dup(path::string(&buf));
    101 };
    102 
    103 @test fn identpath() void = {
    104 	let ident: ast::ident = ["foo", "bar", "baz"];
    105 	let p = identpath(ident);
    106 	defer free(p);
    107 	assert(p == "foo/bar/baz");
    108 };
    109 
    110 // Joins an ident string with underscores instead of double colons. The return
    111 // value must be freed by the caller.
    112 //
    113 // This is used for module names in environment variables and some file names.
    114 export fn identuscore(ident: ast::ident) str = {
    115 	let buf = memio::dynamic();
    116 	for (let i = 0z; i < len(ident); i += 1) {
    117 		fmt::fprintf(&buf, "{}{}", ident[i],
    118 			if (i + 1 < len(ident)) "_"
    119 			else "") as size;
    120 	};
    121 	return memio::string(&buf)!;
    122 };
    123 
    124 @test fn identuscore() void = {
    125 	let ident: ast::ident = ["foo", "bar", "baz"];
    126 	let p = identuscore(ident);
    127 	defer free(p);
    128 	assert(p == "foo_bar_baz");
    129 };