import { Range , DiagnosticSeverity , Diagnostic } from 'vscode-languageserver/node' ; import { TextDocument } from 'vscode-languageserver-textdocument' ; import { CharStreams , CommonTokenStream } from 'antlr4ts' ; import { SystemVerilogLexer } from './ANTLR/grammar/build/SystemVerilogLexer' ; import { SystemVerilogParser } from './ANTLR/grammar/build/SystemVerilogParser' ; import { SyntaxErrorListener } from './ANTLR/SyntaxErrorListener' ; import { isSystemVerilogDocument , isVerilogDocument , getLineRange } from '../utils/server' ; export class ANTLRBackend { param document returns public async getDiagnostics ( document ) { return new Promise ( ( resolve , reject ) => { if ( ! document ) { reject ( new Error ( 'SystemVerilog: Invalid document.' ) ) ; return ; } if ( ! isSystemVerilogDocument ( document ) && ! isVerilogDocument ( document ) ) { reject ( new Error ( 'The document is not a SystemVerilog/Verilog file.' ) ) ; return ; } const diagnosticCollection = new Map ( ) ; const text = document . getText ( ) ; const new_text = this . macroReplace ( text ) ; const inputStream = CharStreams . fromString ( new_text ) ; const lexer = new SystemVerilogLexer ( inputStream ) ; const tokenStream = new CommonTokenStream ( lexer ) ; const parser = new SystemVerilogParser ( tokenStream ) ; const syntaxError = new SyntaxErrorListener ( ) ; parser . addErrorListener ( syntaxError ) ; parser . system_verilog_text ( ) ; const diagnosticList = new Array < Diagnostic > ( ) ; for ( let i = 0 ; i < syntaxError . error_list . length ; i ++ ) { const range = getLineRange ( syntaxError . error_list [ i ] . line , syntaxError . error_list [ i ] . offendingSymbol . text , syntaxError . error_list [ i ] . charPositionInLine ) ; const diagnostic = { severity : DiagnosticSeverity . Error , range , message : this . getImprovedMessage ( syntaxError . error_list [ i ] , document . uri , syntaxError . error_list . length ) , source : 'systemverilog' } ; if ( diagnostic . message !== '' ) diagnosticList . push ( diagnostic ) ; } diagnosticCollection . set ( document . uri , diagnosticList ) ; resolve ( diagnosticCollection ) ; } ) ; } param parser_error param uri param error_count returns public getImprovedMessage ( parser_error , uri , error_count ) { let out = parser_error . msg ; if ( parser_error . msg . startsWith ( 'extraneous input' ) ) { out = `extraneous input "${ parser_error . offendingSymbol . text }"` ; } if ( parser_error . msg . startsWith ( 'mismatched input' ) ) { if ( error_count > 1 ) out = '' ; else out = `mismatched input "${ parser_error . offendingSymbol . text }"` ; } return out ; } param text returns public macroReplace ( text ) { const defines_with_text = this . extract_defines ( text . replace ( /\r\n/g , '\n' ) ) ; const defines = defines_with_text [ 0 ] ; const new_text = defines_with_text [ 1 ] ; const newer_text = this . remove_ifdef_ifndef ( new_text ) ; return this . replace_defines ( newer_text , defines ) ; } param text returns private remove_ifdef_ifndef ( text ) { return text . replace ( /(`ifdef|`ifndef)[\s\S]*?`endif/gm , '' ) ; } param text returns private extract_defines ( text ) { let current_index = text . indexOf ( '`define' ) ; const defines = [ ] ; let new_text ; if ( current_index === - 1 ) { new_text = text ; } else { new_text = text . slice ( 0 , current_index ) ; } while ( current_index !== - 1 ) { const label = text . slice ( current_index ) . split ( ' ' , 2 ) [ 1 ] ; let temp_index = text . indexOf ( '\n' , current_index ) ; while ( temp_index !== - 1 && text . charAt ( temp_index - 1 ) === '\\' ) { temp_index = text . indexOf ( '\n' , temp_index + 1 ) ; } let value = text . slice ( text . indexOf ( label , current_index ) + label . length + 1 , temp_index ) ; value = value . replace ( '\\\n' , '\n' ) ; defines . push ( [ label , value ] ) ; current_index = text . indexOf ( '`define' , current_index + 1 ) ; if ( current_index === - 1 ) { new_text = new_text . concat ( text . slice ( temp_index + 1 ) ) ; } else { new_text = new_text . concat ( text . slice ( temp_index + 1 , current_index ) ) ; } } return [ defines , new_text ] ; } param text param defines returns private replace_defines ( text , defines ) { let new_text = text ; defines . forEach ( ( define ) => { while ( new_text . indexOf ( `\`${ define [ 0 ] }` ) !== - 1 ) { new_text = new_text . replace ( `\`${ define [ 0 ] }` , define [ 1 ] ) ; } } ) ; return new_text ; } }