Erythro/src/Importer.c
2023-12-08 03:01:56 +00:00

135 lines
4.4 KiB
C

/*************/
/*GEMWIRE */
/* ERYTHRO*/
/*************/
#include <Defs.h>
#include <Data.h>
#include <string.h>
#include <unistd.h>
#include <stdio.h>
#include <limits.h>
#include <sys/stat.h>
#if defined(__GNUC__) || defined(APPLE)
#include <errno.h>
#endif
#ifdef WIN32
#define realpath(N,R) _fullpath((R),(N),PATH_MAX)
#endif
/**
* The function of the importer is to read in definitions from a file, and store
* them into the symbol tables.
*
* The file to be imported is called a "module", which is Erythro terminology for C-like "headers".
* They contain extra metadata that allows for Erythro's enhanced debugging and error logging.
*
* Modules may also contain metadata about the contents within - allowing for multiple compile-time
* sourcesets with different arguments, all parsed at the same time as the source code.
*
* This allows Erythro to have first-class support for multiple-build-single-link situations,
* that would require the use of a build system like CMake in other languages.
*
*/
/**
* Read in the information of a module, check that it is valid, and then read the module itself.
* Import syntax looks like:
*
* > import "file"
*
* The string is appended to the current working directory and is checked.
* If the resulting path exists and resolves to a file, then the file's declarations are added to the symbol tables.
*
* Modules may not contain definitions. Only declarations
* TODO: Module metadata as described above.
*/
void ImportModule() {
// Skip the import keyword
Tokenise();
// Make sure there's a string after the import.
if (CurrentFile->CurrentSymbol.type != LI_STR)
Die("Import statement must be followed by a compile-time constant string.");
// Read in the string that we know must be there.
char* Module = strdup(CurrentIdentifier);
// Two strategies for finding the module; either it's relative to cwd, or it's relative to source.
bool FoundImport = false;
// Check the cwd first.
// Figure out the working directory
char CWD[PATH_MAX];
if (getcwd(CWD, sizeof(CWD)) == NULL)
DieMessage("Unable to find cwd when importing module", Module);
// Append the module name to the current working directory
char* ModulePath = malloc(strlen(CWD) + strlen(Module) + 1);
strcpy(ModulePath, CWD);
strcpy(ModulePath + strlen(CWD), "/");
strcpy(ModulePath + strlen(CWD) + 1, Module);
printf("Scanning %s for module definitions.\n", ModulePath);
// Stat the file to see if it exists
struct stat FileInfo;
if (stat(ModulePath, &FileInfo) != 0) {
free(ModulePath);
char SourcePath[PATH_MAX + 1];
realpath(CurrentFile->SourceName, SourcePath);
// Deal with windows being windows
char* SourceFolderLength = strrchr(SourcePath, '/');
if (SourceFolderLength == NULL) {
SourceFolderLength = strrchr(SourcePath, '\\');
}
*(SourceFolderLength + 1) = '\0';
size_t SourcePathLength = strlen(SourcePath);
ModulePath = malloc(SourcePathLength + sizeof(Module) + 1);
strcpy(ModulePath, SourcePath);
strcpy(ModulePath + SourcePathLength, Module);
printf("Scanning %s for module definitions.\n", ModulePath);
if (stat(ModulePath, &FileInfo) != 0)
DieMessage("Unable to access the imported module", ModulePath);
}
// At this point, the file exists and we have the path.
// Save the current file, so that we can restore it later
struct FileData* SavedFile = CurrentFile;
// Create a new file with the module name
struct FileData* ModuleData = malloc(sizeof(struct FileData));
memset(ModuleData, 0, sizeof(struct FileData));
ModuleData->AllowDefinitions = false;
ModuleData->SourceName = ModulePath;
printf("Swapping to module %s..\n\n", ModulePath);
// Parse all relevant data from the module file...
if ((ModuleData->Stream = fopen(ModuleData->SourceName, "r")) == NULL) {
fprintf(stderr, "Unable to open %s: %s\n", ModuleData->SourceName, strerror(errno));
exit(1);
}
CurrentFile = ModuleData;
CurrentFile->CurrentLine = 1;
Tokenise();
ParseGlobals();
fclose(CurrentFile->Stream);
printf("\n\nSwapping back to file %s..\n", SavedFile->SourceName);
// Reinstate the saved file
CurrentFile = SavedFile;
// Tokenise past the string we just parsed
Tokenise();
}