/* * Copyright (C) 2026 Peter Mikkelsen * * This file is part of aplwc. * * Aplwc is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Aplwc is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with aplwc. If not, see . */ #include "aplwc_internal.h" static struct module_member * add_member(struct aplwc *aplwc, struct aplwc_module *module, symbol_t name, struct module_member *real_member, int export) { struct module_member *m; MEMPTRS(aplwc, 2); MEMPTR(0, module); MEMPTR(1, real_member); for(m = module->members; m; m = m->link){ if(m->name == name){ if(real_member) assert(m->real_member == NULL || m->real_member == real_member); m->real_member = real_member; m->exported |= export; goto end; } } if((m = memory_alloc_module_member(aplwc)) == NULL) goto end; m->module = module; m->exported = export; m->name = name; m->real_member = real_member; m->link = module->members; module->members = m; end: MEMPTRS_EXIT(aplwc); return m; } static int read_keyword_decl(struct aplwc *aplwc, struct aplwc_module *module, size_t offset) { int len; size_t max; struct module_member *m; MEMPTRS(aplwc, 2); MEMPTR(0, module); m = NULL; MEMPTR(1, m); #define t(n) module->tokens->tokens[offset+(n)] max = module->tokens->length - offset - 1; len = -1; assert(t(0).tag == TOKEN_TAG_KEYWORD); switch(t(0).data.keyword){ case KW_MODULE: if((max < 1) || (t(1).tag != TOKEN_TAG_IDENTIFIER)) goto fail_syntax; len = 1; break; case KW_SYMBOL: if((max < 1) || (t(1).tag != TOKEN_TAG_IDENTIFIER)) goto fail_syntax; if((max < 2) || (t(2).tag != TOKEN_TAG_UNKNOWN_SYM)) goto fail_syntax; len = 2; if((m = add_member(aplwc, module, t(1).data.symbol, NULL, 0)) == NULL) goto fail_other; if(add_member(aplwc, module, t(2).data.symbol, m, 0) == NULL) goto fail_other; break; case KW_EXPORT: for(len = 1; (size_t)len < max; len++){ switch(t(len).tag){ case TOKEN_TAG_UNKNOWN_SYM: case TOKEN_TAG_IDENTIFIER: if(add_member(aplwc, module, t(len).data.symbol, NULL, 1) == NULL) goto fail_other; break; case TOKEN_TAG_NEWLINE: goto end; default: fail_syntax: aplwc->last_error = APLWC_INVALID_SYNTAX; fail_other: len = -1; goto end; } } break; } if(ALWAYS(len >= 0) && (max > (size_t)len)) if(t(len+1).tag != TOKEN_TAG_NEWLINE) goto fail_syntax; len++; /* For the keyword itself */ end: MEMPTRS_EXIT(aplwc); return len; #undef t } static int read_function_decl(struct aplwc *aplwc, struct aplwc_module *module, size_t offset) { int len, ok; ok = 0; for(len = 1; (offset + len) < module->tokens->length; len++){ if(module->tokens->tokens[offset+len].tag == TOKEN_TAG_DEL){ ok = 1; break; } } if(!ok){ len = -1; aplwc->last_error = APLWC_INVALID_SYNTAX; } return len; } static int read_decl(struct aplwc *aplwc, struct aplwc_module *module, size_t offset) { switch(module->tokens->tokens[offset].tag){ case TOKEN_TAG_COMMENT: case TOKEN_TAG_NEWLINE: return 0; case TOKEN_TAG_KEYWORD: return read_keyword_decl(aplwc, module, offset); case TOKEN_TAG_DEL: return read_function_decl(aplwc, module, offset); default: aplwc->last_error = APLWC_INVALID_SYNTAX; return -1; } } static enum aplwc_rc read_decls(struct aplwc *aplwc, struct aplwc_module *module) { size_t offset; int skip; MEMPTRS(aplwc, 1); MEMPTR(0, module); for(offset = 0; offset < module->tokens->length; offset++){ skip = read_decl(aplwc, module, offset); if(skip == -1) goto end; offset += skip; } end: MEMPTRS_EXIT(aplwc); return aplwc->last_error; } struct aplwc_module * module_lookup(struct aplwc *aplwc, struct string *name) { struct aplwc_module *m; for(m = aplwc->modules; m; m = m->link){ if(string_compare(m->name, name) == 0) break; } return m; } static enum aplwc_rc resolve_imports(struct aplwc *aplwc, struct aplwc_module *module) { struct aplwc_module *imported; struct string *name; struct module_member *m; enum aplwc_rc rc; MEMPTRS(aplwc, 3); MEMPTR(0, module); rc = APLWC_OK; if((name = chars_to_string(aplwc, "Boot", -1)) == NULL){ rc = aplwc->last_error; goto end; } if((imported = module_lookup(aplwc, name)) == NULL) goto end; /* only because it is Boot for now... */ MEMPTR(1, imported); m = NULL; MEMPTR(2, m); for(m = imported->members; m; m = m->link){ if(m->exported == 0) continue; if(add_member(aplwc, module, m->name, m, 0) == NULL){ rc = aplwc->last_error; goto end; } } end: MEMPTRS_EXIT(aplwc); return rc; } enum aplwc_rc load_module(struct aplwc *aplwc, struct string *name, struct token_list *tokens) { struct aplwc_module *module; MEMPTRS(aplwc, 3); MEMPTR(0, name); MEMPTR(1, tokens); if((module = memory_alloc_aplwc_module(aplwc)) == NULL) goto end; MEMPTR(2, module); module->name = name; module->tokens = tokens; module->imports = NULL; module->members = NULL; module->link = NULL; if((aplwc->last_error = read_decls(aplwc, module)) != APLWC_OK) goto end; if((aplwc->last_error = resolve_imports(aplwc, module)) != APLWC_OK) goto end; if((aplwc->last_error = check_tokens(aplwc, module, tokens)) != APLWC_OK) goto end; module->link = aplwc->modules; aplwc->modules = module; end: MEMPTRS_EXIT(aplwc); return aplwc->last_error; }