import { readFileSync } from 'fs' ; import { Utility } from '../misc/utility' ; import { UnifiedPath } from '../misc/unifiedpath' ; import { SourceFileEntry , ListFileLine } from './labels' ; import { AsmConfigBase } from '../settings' ; import * as minimatch from 'minimatch' ; /**
 * This class is the base class for the assembler list file parsers.
 */ export class LabelParserBase { protected fileLineNrs : Map < number , SourceFileEntry > ; protected lineArrays : Map < string , Array < number > > ; protected labelsForNumber64k : Array < any > ; protected labelsForLongAddress = new Map < number , Array < string > > ( ) ; protected numberForLabel = new Map < string , number > ( ) ; protected labelLocations : Map < string , { file : string ; lineNr : number ; address : number ; } > ; protected watchPointLines : Array < { address : number ; line : string ; } > ; protected assertionLines : Array < { address : number ; line : string ; } > ; protected logPointLines : Array < { address : number ; line : string ; } > ; protected config : AsmConfigBase ; protected listFile : Array < ListFileLine > ; protected modulePrefixStack : Array < string > ; protected modulePrefix : string ; protected lastLabel : string ; protected labelSeparator = '.' ; protected currentFileEntry : ListFileLine ; protected includeFileStack : Array < { fileName : string ; lineNr : number ; } > ; protected excludedFileStackIndex : number ; protected warnings : string ; public constructor ( fileLineNrs , lineArrays , labelsForNumber64k , labelsForLongAddress , numberForLabel , labelLocations , watchPointLines , assertionLines , logPointLines ) { this . fileLineNrs = fileLineNrs ; this . lineArrays = lineArrays ; this . labelsForNumber64k = labelsForNumber64k ; this . labelsForLongAddress = labelsForLongAddress ; this . numberForLabel = numberForLabel ; this . labelLocations = labelLocations ; this . watchPointLines = watchPointLines ; this . assertionLines = assertionLines ; this . logPointLines = logPointLines ; this . warnings = '' ; } /**
     * Reads the given file (an assembler .list file) and extracts all PC
     * values (the first 4 digits), so that each line can be associated with a
     * PC value.
     */ public loadAsmListFile ( config ) { this . config = config ; this . excludedFileStackIndex = - 1 ; this . includeFileStack = new Array < { fileName : string ; lineNr : number ; } > ( ) ; this . listFile = new Array < ListFileLine > ( ) ; this . modulePrefixStack = new Array < string > ( ) ; this . modulePrefix = undefined as any ; this . lastLabel = undefined as any ; this . parseAllLabelsAndAddresses ( ) ; if ( config . srcDirs . length == 0 ) { this . listFileModeFinish ( ) ; return ; } this . parseAllFilesAndLineNumbers ( ) ; this . sourcesModeFinish ( ) ; } /**
     * Loops all lines of the list file and parses for labels and the addresses
     * for each line.
     */ protected parseAllLabelsAndAddresses ( ) { const fileName = Utility . getRelFilePath ( this . config . path ) ; const listLinesFull = readFileSync ( this . config . path ) . toString ( ) . split ( '\n' ) ; const listLines = listLinesFull . map ( line => line . trimRight ( ) ) ; let lineNr = 0 ; for ( let line of listLines ) { this . currentFileEntry = { fileName , lineNr , addr : undefined , size : 0 , line , modulePrefix : this . modulePrefix , lastLabel : this . lastLabel } ; this . listFile . push ( this . currentFileEntry ) ; this . parseLabelAndAddress ( line ) ; const address = this . currentFileEntry . addr ; this . findWpmemAssertionLogpoint ( address , line ) ; lineNr ++ ; } } param startLineNr protected parseAllFilesAndLineNumbers ( startLineNr = 0 ) { const count = this . listFile . length ; for ( let listFileNumber = startLineNr ; listFileNumber < count ; listFileNumber ++ ) { const entry = this . listFile [ listFileNumber ] ; const line = entry . line ; if ( line . length == 0 ) continue ; this . currentFileEntry = entry ; this . parseFileAndLineNumber ( line ) ; const index = this . includeFileStack . length - 1 ; if ( index < 0 ) continue ; this . associateSourceFileName ( ) ; } } param address param fullLine protected findWpmemAssertionLogpoint ( address , fullLine ) { const comment = this . getComment ( fullLine ) ; let match = /.*(\bWPMEM([\s,]|$).*)/ . exec ( comment ) ; if ( match ) { this . watchPointLines . push ( { address : address ! , line : match [ 1 ] } ) ; } if ( address == undefined ) return ; match = /.*(\bASSERTION([\s,]|$).*)/ . exec ( comment ) ; if ( match ) { this . assertionLines . push ( { address , line : match [ 1 ] } ) ; } match = /.*(\bLOGPOINT([\s,]|$).*)/ . exec ( comment ) ; if ( match ) { this . logPointLines . push ( { address , line : match [ 1 ] } ) ; } } param line returns protected getComment ( line ) { const i = line . indexOf ( ";" ) ; if ( i < 0 ) return "" ; const comment = line . substr ( i + 1 ) ; return comment ; } /**
     * Will check if the name is excluded (excludedFiles).
     * If so the source filename is not set to the source file name so that it
     * is "" and will be ignored.
     */ protected associateSourceFileName ( ) { let fName = "" ; if ( this . excludedFileStackIndex == - 1 ) { const index = this . includeFileStack . length - 1 ; if ( index >= 0 ) fName = this . includeFileStack [ index ] . fileName ; } this . currentFileEntry . fileName = fName ; } /**
     * Finishes the list file mode.
     * Puts filename (the list file name) and line numbers into the
     * this.fileLineNrs and this.lineArrays structures.
     */ protected listFileModeFinish ( ) { const lineArray = new Array < number > ( ) ; const fileName = Utility . getRelFilePath ( this . config . path ) ; this . lineArrays . set ( fileName , lineArray ) ; for ( const entry of this . listFile ) { const lastLabel = entry . lastLabel ; if ( lastLabel ) { const fullLabel = this . getFullLabel ( entry . modulePrefix , lastLabel ) ; let fileLoc = this . labelLocations . get ( fullLabel ) ; if ( ! fileLoc ) { const address = entry . addr ! ; fileLoc = { file : entry . fileName , lineNr : entry . lineNr , address } ; this . labelLocations . set ( fullLabel , fileLoc ) ; } } if ( entry . addr == undefined ) continue ; const prevFileLine = this . fileLineNrs . get ( entry . addr ) ; if ( ! prevFileLine || entry . size > 0 ) { this . fileLineNrs . set ( entry . addr , { fileName : entry . fileName , lineNr : entry . lineNr , modulePrefix : entry . modulePrefix , lastLabel : entry . lastLabel } ) ; } if ( ! lineArray [ entry . lineNr ] ) { lineArray [ entry . lineNr ] = entry . addr ; } } } /**
     * Finishes the sources mode.
     * Puts filename (the list file name) and line numbers into the
     * this.labelLocations, this.fileLineNrs and this.lineArrays structures.
     */ protected sourcesModeFinish ( ) { for ( const entry of this . listFile ) { if ( entry . fileName . length == 0 ) continue ; const lastLabel = entry . lastLabel ; if ( lastLabel ) { const fullLabel = this . getFullLabel ( entry . modulePrefix , lastLabel ) ; let fileLoc = this . labelLocations . get ( fullLabel ) ; if ( ! fileLoc ) { const address = entry . addr ! ; fileLoc = { file : entry . fileName , lineNr : entry . lineNr , address } ; this . labelLocations . set ( fullLabel , fileLoc ) ; } } if ( ! entry . addr ) continue ; for ( let i = 0 ; i < entry . size ; i ++ ) { const addr = ( i == 0 ) ? entry . addr : ( entry . addr + i ) & 0xFFFF ; this . fileLineNrs . set ( addr , { fileName : entry . fileName , lineNr : entry . lineNr , modulePrefix : entry . modulePrefix , lastLabel : entry . lastLabel } ) ; } if ( ! this . lineArrays . get ( entry . fileName ) ) { this . lineArrays . set ( entry . fileName , new Array < number > ( ) ) ; } const lineArray = this . lineArrays . get ( entry . fileName ) ! ; if ( ! lineArray [ entry . lineNr ] ) { lineArray [ entry . lineNr ] = entry . addr ; } } } param line protected parseLabelAndAddress ( line ) { Utility . assert ( false , "Override parseLabelAndAddress" ) ; } param line protected parseFileAndLineNumber ( line ) { Utility . assert ( false , "Override parseFileAndLineNumber" ) ; } param moduleName protected moduleStart ( moduleName ) { this . modulePrefixStack . push ( moduleName ) ; this . modulePrefix = this . modulePrefixStack . join ( this . labelSeparator ) + this . labelSeparator ; this . currentFileEntry . modulePrefix = this . modulePrefix ; this . lastLabel = undefined as any ; this . currentFileEntry . lastLabel = this . lastLabel ; } /**
     * Called by the parser if a module end is found.
     */ protected moduleEnd ( ) { this . modulePrefixStack . pop ( ) ; if ( this . modulePrefixStack . length > 0 ) this . modulePrefix = this . modulePrefixStack . join ( this . labelSeparator ) + this . labelSeparator ; else this . modulePrefix = undefined as any ; this . currentFileEntry . modulePrefix = this . modulePrefix ; this . lastLabel = undefined as any ; this . currentFileEntry . lastLabel = this . lastLabel ; } param value param label protected addLabelForNumber ( value , label ) { this . lastLabel = label ; this . currentFileEntry . lastLabel = this . lastLabel ; this . currentFileEntry . modulePrefix = undefined ; this . addLabelForNumberRaw ( value , label ) ; } param value param label param labelType protected addLabelForNumberRaw ( value , label ) { this . numberForLabel . set ( label , value ) ; const value64k = value & 0xFFFF ; let labelsArray = this . labelsForNumber64k [ value64k ] ; if ( labelsArray === undefined ) { labelsArray = new Array < string > ( ) ; this . labelsForNumber64k [ value64k ] = labelsArray ; } if ( labelsArray . indexOf ( label ) < 0 ) labelsArray . push ( label ) ; labelsArray = this . labelsForLongAddress . get ( value ) ; if ( labelsArray === undefined ) { labelsArray = new Array < string > ( ) ; this . labelsForLongAddress . set ( value , labelsArray ) ; } if ( labelsArray . indexOf ( label ) < 0 ) labelsArray . push ( label ) ; } param address param size protected addAddressLine ( address , size ) { this . currentFileEntry . addr = address ; this . currentFileEntry . size = size ; } param modulePrefix param label protected getFullLabel ( modulePrefix , label ) { let result = modulePrefix || '' ; if ( result . length == 0 ) return label ; result += label ; return result ; } param includeFileName protected includeStart ( includeFileName ) { includeFileName = UnifiedPath . getUnifiedPath ( includeFileName ) ; const index = this . includeFileStack . length - 1 ; let fileName ; if ( index >= 0 ) { const parentFileName = this . includeFileStack [ this . includeFileStack . length - 1 ] . fileName ; const dirName = UnifiedPath . dirname ( parentFileName ) ; fileName = Utility . getRelSourceFilePath ( includeFileName , [ dirName , ... this . config . srcDirs ] ) ; } else { fileName = Utility . getRelSourceFilePath ( includeFileName , this . config . srcDirs ) ; } this . includeFileStack . push ( { fileName , lineNr : 0 } ) ; if ( this . excludedFileStackIndex == - 1 ) { for ( const exclGlob of this . config . excludeFiles ) { const found = minimatch ( fileName , exclGlob ) ; if ( found ) { this . excludedFileStackIndex = index + 1 ; break ; } } } } /**
     * Called by the parser if the end of an include file is found.
     */ protected includeEnd ( ) { if ( this . includeFileStack . length == 0 ) throw Error ( "File parsing error: include file stacking." ) ; this . includeFileStack . pop ( ) ; const index = this . includeFileStack . length ; if ( this . excludedFileStackIndex == index ) { this . excludedFileStackIndex = - 1 ; } } param lineNr protected setLineNumber ( lineNr ) { this . currentFileEntry . lineNr = lineNr ; } param address param bank returns protected createLongAddress ( address , bank ) { return address ; } /**
     * Returns the collected warnings.
     * undefined if no warnings.
     */ public getWarnings ( ) { return this . warnings ; } }