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 };