@specs-feup/clava
Version:
A C/C++ source-to-source compiler written in Typescript
185 lines (150 loc) • 5.33 kB
text/typescript
import Io from "@specs-feup/lara/api/lara/Io.js";
import Check from "@specs-feup/lara/api/lara/Check.js";
import System from "@specs-feup/lara/api/lara/System.js";
import Strings from "@specs-feup/lara/api/lara/Strings.js";
import Clava from "../Clava.js";
import ClavaJoinPoints from "../ClavaJoinPoints.js";
import { JavaClasses } from "@specs-feup/lara/api/lara/util/JavaTypes.js";
import { debug } from "@specs-feup/lara/api/lara/core/LaraCore.js";
import { ClavaException, FileJp } from "../../Joinpoints.js";
/**
* Parses C/C++ files.
*/
export default class BatchParser {
private basePath: string;
/**
* The source files found on the given path
*/
private sourceFiles: JavaClasses.File[];
/**
* Maps header file names to the corresponding File objects
*/
private headerFilesMap: Map<string, JavaClasses.File> = new Map();
private static _IMPLEMENTATION_PATTERNS = ["*.c", "*.cpp"];
private static _HEADER_PATTERNS = ["*.h", "*.hpp"];
constructor(srcPath: string) {
this.basePath = srcPath;
this.sourceFiles = Io.getFiles(
srcPath,
BatchParser._IMPLEMENTATION_PATTERNS,
true
);
const headerFiles = Io.getFiles(
srcPath,
BatchParser._HEADER_PATTERNS,
true
);
for (const headerFile of headerFiles) {
this.headerFilesMap.set(headerFile.getName() as string, headerFile);
}
}
getSourceFiles() {
return this.sourceFiles;
}
parse(sourceFile: string | JavaClasses.File) {
debug("Parsing " + sourceFile.toString() + "...");
const parsingStart = System.nanos();
const $literalFile = Clava.getProgram().addFileFromPath(
sourceFile as object
) as FileJp;
// Rebuild tree
const $parsedFile = this.rebuildFile($literalFile);
const parsingTime = System.toc(parsingStart);
debug("Parsing took " + parsingTime);
return $parsedFile;
}
/**
* Tries to rebuild the current tree, using several methods to fix any problem it finds
*/
private rebuildFile($literalFile: FileJp) {
let parsing: boolean | undefined = true;
while (parsing) {
const $parsedFile = $literalFile.rebuildTry() as FileJp | ClavaException;
// Check if it is a file
if ($parsedFile instanceof FileJp) {
return $parsedFile;
}
// It is an exception
parsing = this.solveRebuildFile($parsedFile, $literalFile);
}
return undefined;
}
private solveRebuildFile($exception: ClavaException, $literalFile: FileJp) {
// Get error message
const message = $exception.message;
// Check if correct type
if ($exception.exceptionType !== "ClavaParserException") {
throw $exception.exception;
}
const lines = Strings.asLines(message);
// Check first line
if (lines === undefined || lines.length === 0) {
throw new Error("Could not parse error message: " + message);
}
Check.strings(lines[0], "There are errors in the source code:");
// Parse first error
return this.parseError(lines.subList(1, lines.length), $literalFile);
}
private parseError(lines: JavaClasses.List<string>, $literalFile: FileJp) {
const errorLine: string = lines[0];
const filename = $literalFile.name;
// Find name of file in line
const nameIndex = errorLine.indexOf(filename);
if (nameIndex === -1) {
throw "Could not find filename '" + filename + "' in " + errorLine;
}
// Remove filename
let parsedLine = errorLine
.substring(nameIndex + filename.length, errorLine.length)
.trim();
// Remove location
const locationSep = parsedLine.indexOf(" ");
if (locationSep !== -1) {
parsedLine = parsedLine
.substring(locationSep + 1, parsedLine.length)
.trim();
}
// Check if fatal error
if (parsedLine.startsWith("fatal error:")) {
const fatalError = "fatal error:";
parsedLine = parsedLine
.substring(fatalError.length, parsedLine.length)
.trim();
return this.parseFatalError(parsedLine);
}
console.log("Line 0: " + lines[0]);
console.log("Parsed line: " + parsedLine);
return false;
}
private parseFatalError(error: string) {
if (error.endsWith("' file not found")) {
const fileNotFound = "' file not found";
const endIndex = error.length - fileNotFound.length;
let parsedError = error.substring(0, endIndex).trim();
if (!parsedError.startsWith("'")) {
throw "Expected file not found string to start with ':" + parsedError;
}
parsedError = parsedError.substring(1, parsedError.length).trim();
// Normalize
parsedError.replace("\\\\", "/");
let filename = parsedError;
let path = undefined;
// Extract path
const slashIndex = parsedError.lastIndexOf("/");
if (slashIndex !== -1) {
filename = parsedError.substring(slashIndex + 1);
path = parsedError.substring(0, slashIndex);
}
console.log("File: " + filename);
console.log("Path: " + path);
let pathname = filename;
if (path !== undefined) {
pathname = path + "/" + filename;
}
debug("Adding file " + pathname);
const newFile = ClavaJoinPoints.file(filename, path);
Clava.getProgram().addFile(newFile);
return true;
}
}
}