1
0
mirror of https://github.com/sasjs/lint.git synced 2025-12-11 01:44:36 +00:00
Files
lint/src/utils/parseMacros.ts
2021-05-22 00:08:24 +05:00

152 lines
4.9 KiB
TypeScript

import { LintConfig, Macro } from '../types'
import { LineEndings } from '../types/LineEndings'
import { trimComments } from './trimComments'
export const parseMacros = (text: string, config?: LintConfig): Macro[] => {
const lineEnding = config?.lineEndings === LineEndings.CRLF ? '\r\n' : '\n'
const lines: string[] = text ? text.split(lineEnding) : []
const macros: Macro[] = []
let isCommentStarted = false
let macroStack: Macro[] = []
let isReadingMacroDefinition = false
let isStatementContinues = true
let tempMacroDeclaration = ''
let tempMacroDeclarationLines: string[] = []
let tempStartLineNumbers: number[] = []
lines.forEach((line, lineIndex) => {
const { statement: trimmedLine, commentStarted } = trimComments(
line,
isCommentStarted
)
isCommentStarted = commentStarted
isStatementContinues = !trimmedLine.endsWith(';')
const statements: string[] = trimmedLine.split(';')
if (isReadingMacroDefinition) {
// checking if code is split into statements based on `;` is a part of HTML Encoded Character
// if it happened, merges two statements into one
statements.forEach((statement, statementIndex) => {
if (/&[^\s]{1,5}$/.test(statement)) {
const next = statements[statementIndex]
const updatedStatement = `${statement};${
statements[statementIndex + 1]
}`
statements.splice(statementIndex, 1, updatedStatement)
statements.splice(statementIndex + 1, 1)
}
})
}
statements.forEach((statement, statementIndex) => {
const { statement: trimmedStatement, commentStarted } = trimComments(
statement,
isCommentStarted
)
isCommentStarted = commentStarted
if (isReadingMacroDefinition) {
tempMacroDeclaration =
tempMacroDeclaration +
(trimmedStatement ? ' ' + trimmedStatement : '')
tempMacroDeclarationLines.push(line)
tempStartLineNumbers.push(lineIndex + 1)
if (!Object.is(statements.length - 1, statementIndex)) {
isReadingMacroDefinition = false
const name = tempMacroDeclaration
.slice(7, tempMacroDeclaration.length)
.trim()
.split('/')[0]
.split('(')[0]
.trim()
macroStack.push({
name,
startLineNumbers: tempStartLineNumbers,
endLineNumber: null,
parentMacro: macroStack.length
? macroStack[macroStack.length - 1].name
: '',
hasMacroNameInMend: false,
mismatchedMendMacroName: '',
declarationLines: tempMacroDeclarationLines,
terminationLine: '',
declaration: tempMacroDeclaration,
termination: ''
})
}
}
if (trimmedStatement.startsWith('%macro')) {
const startLineNumber = lineIndex + 1
if (
isStatementContinues &&
Object.is(statements.length - 1, statementIndex)
) {
tempMacroDeclaration = trimmedStatement
tempMacroDeclarationLines = [line]
tempStartLineNumbers = [startLineNumber]
isReadingMacroDefinition = true
return
}
const name = trimmedStatement
.slice(7, trimmedStatement.length)
.trim()
.split('/')[0]
.split('(')[0]
.trim()
macroStack.push({
name,
startLineNumbers: [startLineNumber],
endLineNumber: null,
parentMacro: macroStack.length
? macroStack[macroStack.length - 1].name
: '',
hasMacroNameInMend: false,
mismatchedMendMacroName: '',
declarationLines: [line],
terminationLine: '',
declaration: trimmedStatement,
termination: ''
})
} else if (trimmedStatement.startsWith('%mend')) {
if (macroStack.length) {
const macro = macroStack.pop() as Macro
const mendMacroName =
trimmedStatement.split(' ').filter((s: string) => !!s)[1] || ''
macro.endLineNumber = lineIndex + 1
macro.hasMacroNameInMend = mendMacroName === macro.name
macro.mismatchedMendMacroName = macro.hasMacroNameInMend
? ''
: mendMacroName
macro.terminationLine = line
macro.termination = trimmedStatement
macros.push(macro)
} else {
macros.push({
name: '',
startLineNumbers: [],
endLineNumber: lineIndex + 1,
parentMacro: '',
hasMacroNameInMend: false,
mismatchedMendMacroName: '',
declarationLines: [],
terminationLine: line,
declaration: '',
termination: trimmedStatement
})
}
}
})
})
macros.push(...macroStack)
return macros
}