import { DiagnosticSeverity , Diagnostic } from 'vscode-languageserver/node' ; import { TextDocument } from 'vscode-languageserver-textdocument' ; import * as child from 'child_process' ; import { DocumentCompiler } from './DocumentCompiler' ; import { DiagnosticData , isDiagnosticDataUndefined } from './DiagnosticData' ; /**
 * CannotFindModule states: Handles the states of parsing `Cannot find` errors.
 */ enum CannotFindModuleState { CannotFindModule = 1 , SearchPathNotFound = 2 , LookedIn = 3 , FilesSearched = 4 , End = 5 } /**
 * Verilator Compiler class contains functionality for compiling SystemVerilog/Verilog files using Verilator simulator.
 * Generates and takes in predefined runtime arguments,
 * and eventually parses the errors/warnings in `stderr` into `Diagnostic` array mapped to each unique document's uri.
 */ export class VerilatorCompiler extends DocumentCompiler { regexError = new RegExp ( '%Error: ([^:]*):([0-9]+)(:[0-9]+)?: (.*)' ) ; regexErrorWarning = new RegExp ( '%Error-(.*): ([^:]*):([0-9]+)(:[0-9]+)?: (.*)' ) ; regexWarning = new RegExp ( '%Warning-(.*): ([^:]*):([0-9]+)(:[0-9]+)?: (.*)' ) ; regexWarningSuggest = new RegExp ( '%Warning-(.*): (.*)' ) ; regexCannotFindModule = new RegExp ( '%Error: ([^:]*):([0-9]+)(:[0-9]+)?: Cannot find(.*): (.*)' ) ; regexSearchPathNotFound = new RegExp ( "%Error: ([^:]*):([0-9]+)(:[0-9]+)?: This may be because there's no search path specified with -I<dir>." ) ; regexLookedIn = new RegExp ( '%Error: ([^:]*):([0-9]+)(:[0-9]+)?: Looked in:' ) ; regexFilesSearchedSource = '%Error: ([^:]*):([0-9]+)(:[0-9]+)?:       (.*)notFoundModulePlaceHolder(.v|.sv|.vh|.svh|)$' ; regexOffendPart = new RegExp ( ".*'(.*)'.*" ) ; param _error param _stdout param stderr param compiledDocument param documentFilePath param collection returns public parseDiagnostics ( _error , _stdout , stderr , compiledDocument , documentFilePath , collection ) { if ( stderr === undefined || stderr == null || ! compiledDocument ) { return ; } stderr = stderr . replace ( /\r\n|\n|\r/g , '\n' ) . trim ( ) ; const errors = stderr . split ( /\r|\n/g ) ; const visitedDocuments = new Map < string , boolean > ( ) ; let previousLine ; for ( let i = 0 ; i < errors . length ; i ++ ) { const error = errors [ i ] . trim ( ) ; const diagnosticData = new DiagnosticData ( ) ; let matches ; if ( ( matches = this . regexError . exec ( error ) ) ) { if ( matches && matches . length > 4 ) { diagnosticData . filePath = matches [ 1 ] ; diagnosticData . line = parseInt ( matches [ 2 ] , 10 ) - 1 ; if ( matches [ 3 ] !== undefined ) { diagnosticData . charPosition = parseInt ( matches [ 3 ] . substr ( 1 ) , 10 ) - 1 ; const symbol = this . regexOffendPart . exec ( error ) ; diagnosticData . offendingSymbol = symbol ? symbol [ 1 ] : null ; } diagnosticData . problem = matches [ 4 ] . trim ( ) ; diagnosticData . diagnosticSeverity = DiagnosticSeverity . Error ; } } else if ( ( matches = this . regexErrorWarning . exec ( error . trim ( ) ) ) ) { if ( matches && matches . length > 4 ) { diagnosticData . filePath = matches [ 2 ] ; diagnosticData . line = parseInt ( matches [ 3 ] , 10 ) - 1 ; if ( matches [ 4 ] !== undefined ) { diagnosticData . charPosition = parseInt ( matches [ 4 ] . substr ( 1 ) , 10 ) - 1 ; const symbol = this . regexOffendPart . exec ( error ) ; diagnosticData . offendingSymbol = symbol ? symbol [ 1 ] : null ; } diagnosticData . problem = `${ matches [ 1 ] }: ${ matches [ 3 ] }${ matches [ 4 ] }` ; diagnosticData . problem = diagnosticData . problem . trim ( ) ; diagnosticData . diagnosticSeverity = DiagnosticSeverity . Error ; } } else if ( ( matches = this . regexWarningSuggest . exec ( error . trim ( ) ) ) ) { if ( ( matches = this . regexWarning . exec ( error . trim ( ) ) ) ) { if ( matches && matches . length > 5 ) { diagnosticData . filePath = matches [ 2 ] ; diagnosticData . line = parseInt ( matches [ 3 ] , 10 ) - 1 ; if ( matches [ 4 ] !== undefined ) { diagnosticData . charPosition = parseInt ( matches [ 4 ] . substr ( 1 ) , 10 ) - 1 ; const symbol = this . regexOffendPart . exec ( error ) ; diagnosticData . offendingSymbol = symbol ? symbol [ 1 ] : null ; } diagnosticData . problem = `${ matches [ 1 ] }: ${ matches [ 5 ] }` ; diagnosticData . problem = diagnosticData . problem . trim ( ) ; diagnosticData . diagnosticSeverity = DiagnosticSeverity . Warning ; } } else if ( previousLine !== undefined ) { matches = this . regexWarningSuggest . exec ( error . trim ( ) ) ; if ( matches && matches . length > 2 ) { diagnosticData . filePath = documentFilePath ; diagnosticData . problem = `${ matches [ 1 ] }: ${ matches [ 2 ] }` ; diagnosticData . diagnosticSeverity = DiagnosticSeverity . Information ; } } } if ( ( matches = this . regexCannotFindModule . exec ( error ) ) ) { if ( matches && matches . length > 5 ) { i = this . skipCannotFindModuleTrailingErrors ( errors , i , matches [ 5 ] ) ; } } if ( diagnosticData . line !== undefined ) { previousLine = diagnosticData . line ; } else { diagnosticData . line = previousLine ; } if ( ! isDiagnosticDataUndefined ( diagnosticData ) ) { if ( visitedDocuments . has ( diagnosticData . filePath ) ) { this . publishDiagnosticForDocument ( compiledDocument , false , diagnosticData , collection ) ; } else { this . publishDiagnosticForDocument ( compiledDocument , true , diagnosticData , collection ) ; visitedDocuments . set ( diagnosticData . filePath , true ) ; } } } } param errors param i param notFoundModule returns skipCannotFindModuleTrailingErrors ( errors , i , notFoundModule ) { let state = CannotFindModuleState . CannotFindModule ; const regexFilesSearched = new RegExp ( this . regexFilesSearchedSource . replace ( 'notFoundModulePlaceHolder' , notFoundModule ) ) ; while ( i < errors . length && state !== CannotFindModuleState . End ) { i += 1 ; const error = errors [ i ] . trim ( ) ; let matches ; switch ( state ) { case CannotFindModuleState . CannotFindModule : if ( ( matches = this . regexSearchPathNotFound . exec ( error ) ) ) { state = CannotFindModuleState . SearchPathNotFound ; } else if ( ( matches = this . regexLookedIn . exec ( error ) ) ) { state = CannotFindModuleState . LookedIn ; } else { state = CannotFindModuleState . End ; } break ; case CannotFindModuleState . SearchPathNotFound : if ( ( matches = this . regexLookedIn . exec ( error ) ) ) { state = CannotFindModuleState . LookedIn ; } else if ( ( matches = regexFilesSearched . exec ( error ) ) ) { state = CannotFindModuleState . FilesSearched ; } else { state = CannotFindModuleState . End ; } break ; case CannotFindModuleState . LookedIn : if ( ( matches = regexFilesSearched . exec ( error ) ) ) { state = CannotFindModuleState . FilesSearched ; } else { state = CannotFindModuleState . End ; } break ; case CannotFindModuleState . FilesSearched : if ( ! ( matches = regexFilesSearched . exec ( error ) ) ) { state = CannotFindModuleState . End ; } break ; default : state = CannotFindModuleState . End ; } } if ( state === CannotFindModuleState . End ) { i -= 1 ; } return i ; } }