Skip to content

Commit

Permalink
Fix local references and add NOFUNC
Browse files Browse the repository at this point in the history
  • Loading branch information
szapp committed Apr 21, 2024
1 parent 74044d5 commit e449a9b
Show file tree
Hide file tree
Showing 15 changed files with 33,358 additions and 141,628 deletions.
10 changes: 9 additions & 1 deletion __tests__/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,16 @@ describe('index', () => {
})

it('calls run when imported', async () => {
jest.replaceProperty(process, 'env', { ...process.env, GITHUB_WORKSPACE: '' })
require('../src/index.js')

expect(runMock).toHaveBeenCalled()
expect(runMock).toHaveBeenCalledWith(true)
})

it('does not call run when imported out side of GitHub actions', async () => {
jest.replaceProperty(process, 'env', { ...process.env, GITHUB_WORKSPACE: undefined })
require('../src/index.js')

expect(runMock).not.toHaveBeenCalled()
})
})
5 changes: 0 additions & 5 deletions __tests__/main.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,8 @@ import { Parser } from '../src/parser.ts'
import * as inputs from '../src/inputs.ts'
import write from '../src/write.ts'
import * as cleanup from '../src/cleanup.ts'
import { DefaultArtifactClient } from '@actions/artifact'
import fs from 'fs'

let runMock: jest.SpiedFunction<typeof main.run>
DefaultArtifactClient.prototype.uploadArtifact = jest.fn()

describe('run', () => {
beforeEach(() => {
Expand All @@ -23,8 +20,6 @@ describe('run', () => {
jest.spyOn(cleanup, 'workflow').mockResolvedValue(false)
jest.spyOn(core, 'setFailed').mockImplementation()
jest.spyOn(core, 'error').mockImplementation()
jest.spyOn(fs, 'writeFileSync').mockImplementation()
jest.spyOn(fs, 'unlinkSync').mockImplementation()
runMock = jest.spyOn(main, 'run')
})

Expand Down
255 changes: 186 additions & 69 deletions __tests__/parser.test.ts

Large diffs are not rendered by default.

169,315 changes: 32,458 additions & 136,857 deletions dist/index.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion dist/index.js.map

Large diffs are not rendered by default.

3,082 changes: 154 additions & 2,928 deletions dist/licenses.txt

Large diffs are not rendered by default.

3 changes: 3 additions & 0 deletions g4/Daedalus.g4
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ Float: F L O A T;
Prototype: P R O T O T Y P E;
Instance: I N S T A N C E;
Null: N U L L;
NoFunc: N O F U N C;
LeftParen: '(';
RightParen: ')';
Expand Down Expand Up @@ -194,6 +195,7 @@ value:
| FloatLiteral # floatLiteralValue
| StringLiteral # stringLiteralValue
| Null # nullLiteralValue
| NoFunc # nofuncLiteralValue
| funcCall # funcCallValue
| reference # referenceValue;

Expand All @@ -220,6 +222,7 @@ anyIdentifier: (
| Class
| Prototype
| Null
| NoFunc
| Identifier
);

Expand Down
1,980 changes: 292 additions & 1,688 deletions package-lock.json

Large diffs are not rendered by default.

1 change: 0 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,6 @@
]
},
"dependencies": {
"@actions/artifact": "^2.1.5",
"@actions/core": "^1.10.1",
"@actions/github": "^6.0.0",
"@actions/glob": "^0.4.0",
Expand Down
6 changes: 3 additions & 3 deletions src/class.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ export type Tables = { symbols: SymbolTable; references: SymbolTable }
export class SymbolVisitor extends DaedalusVisitor<Tables> {
constructor(
protected readonly file: string,
protected readonly symbolTable: SymbolTable = [],
protected readonly symbolTable: SymbolTable,
protected readonly referenceTable: SymbolTable = [],
protected scope: string = '',
protected type: string = ''
Expand Down Expand Up @@ -82,8 +82,7 @@ export class SymbolVisitor extends DaedalusVisitor<Tables> {
.join('.')
.toUpperCase()
if (name) {
const scopedName = (this.getScope() + name).toUpperCase()
if (this.symbolTable.find((s) => s.name === scopedName)) name = scopedName
name = this.getScope() + name
// istanbul ignore next: Unnecessary to test emty line
this.referenceTable.push({ name, file: this.file, line: ctx.start?.line ?? 0 })
}
Expand All @@ -95,6 +94,7 @@ export class SymbolVisitor extends DaedalusVisitor<Tables> {
const symbol = ctx.Identifier().getSymbol()
const refName = symbol.text?.toUpperCase()
if (refName) {
this.referenceTable.push({ name: refName, file: this.file, line: symbol.line })
this.symbolTable
.filter((s) => s.name.startsWith(refName + '.'))
.forEach((s) => {
Expand Down
38 changes: 17 additions & 21 deletions src/externals.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,3 @@
export interface ExternalList {
[key: string]: Record<string, string[]>
}

const basic = {
CONTENT: [
'INTTOSTRING',
Expand Down Expand Up @@ -307,13 +303,13 @@ const basic = {
}

const G1 = {
...basic,
CONTENT: basic['CONTENT'].concat(['AI_LOOKFORITEM']),
CONTENT: [...basic.CONTENT, 'AI_LOOKFORITEM'],
MENU: basic.MENU,
}

const G112 = {
...basic,
CONTENT: basic['CONTENT'].concat([
CONTENT: [
...basic.CONTENT,
'PRINTSCREENCOLORED',
'AI_PRINTSCREEN',
'WLD_ISFPAVAILINRANGE',
Expand All @@ -332,12 +328,13 @@ const G112 = {
'REMOVEITEMFROMSLOT',
'AI_CREATEITEMINSLOT',
'AI_REMOVEITEMFROMSLOT',
]),
],
MENU: basic.MENU,
}

const G130 = {
...basic,
CONTENT: basic['CONTENT'].concat([
CONTENT: [
...basic.CONTENT,
'AI_PRINTSCREEN',
'EXITSESSION',
'PLAYVIDEOEX',
Expand All @@ -358,12 +355,13 @@ const G130 = {
'NPC_GETPORTALGUILD',
'GAME_INITGERMAN',
'GAME_INITENGLISH',
]),
],
MENU: basic.MENU,
}

const G2 = {
...basic,
CONTENT: basic['CONTENT'].concat([
CONTENT: [
...basic.CONTENT,
'AI_PRINTSCREEN',
'EXITSESSION',
'PLAYVIDEOEX',
Expand Down Expand Up @@ -391,14 +389,12 @@ const G2 = {
'GAME_INITGERMAN',
'GAME_INITENGLISH',
'GAME_INITENGINTL',
]),
],
MENU: basic.MENU,
}

const list: ExternalList = {
G1,
G112,
G130,
G2,
export interface ExternalList {
[key: string]: Record<string, string[]>
}

const list: ExternalList = { G1, G112, G130, G2 }
export default list
7 changes: 6 additions & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
import { run } from './main.js'

run(true)
// Running in GitHub Actions
if (typeof process.env['GITHUB_WORKSPACE'] === 'string') {
run(true)
}

export { run }
17 changes: 1 addition & 16 deletions src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@ import { workflow } from './cleanup.js'
import { Parser } from './parser.js'
import { loadInputs, formatFilters } from './inputs.js'
import write, { Annotation } from './write.js'
import { DefaultArtifactClient } from '@actions/artifact'
import fs from 'fs'

export async function run(github: boolean = false): Promise<{ summary: string; annotations: Annotation[] } | void> {
try {
Expand All @@ -31,20 +29,6 @@ export async function run(github: boolean = false): Promise<{ summary: string; a
parser.validateOverwrites()
}

// Save symbol table and reference table as workflow artifacts
if (github) {
const artifactFiles: string[] = []
for (const parser of parsers) {
fs.writeFileSync(`./${parser.filename}-symbol-table.json`, JSON.stringify(parser.symbolTable, null, 2))
fs.writeFileSync(`./${parser.filename}-reference-table.json`, JSON.stringify(parser.referenceTable, null, 2))
artifactFiles.push(`./${parser.filename}-symbol-table.json`)
artifactFiles.push(`./${parser.filename}-reference-table.json`)
}
const artifact = new DefaultArtifactClient()
await artifact.uploadArtifact('symbol-tables', artifactFiles, '.', { retentionDays: 3 })
artifactFiles.forEach((file) => fs.unlinkSync(file))
}

// Initialize check run
const { details_url, check_id } = await write.createCheckRun(startedAt, github)

Expand All @@ -59,5 +43,6 @@ export async function run(github: boolean = false): Promise<{ summary: string; a
const msg: string = error instanceof Error ? error.message : String(error)
if (github) core.setFailed(msg)
else console.error(msg)
await Parser.clearTmpDir()
}
}
92 changes: 55 additions & 37 deletions src/parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { DaedalusParser } from './generated/DaedalusParser.js'
import { SymbolVisitor, SymbolTable } from './class.js'
import { normalizePath } from './utils.js'
import externals from './externals.js'
import symbols from './symbols.js'
import * as io from '@actions/io'
import * as tc from '@actions/tool-cache'
import * as glob from '@actions/glob'
Expand Down Expand Up @@ -117,40 +118,25 @@ export class Parser {
* Parses the basic symbols for content and menu parsers.
*/
protected parseRequired(): void {
let symbols: string[] = []
switch (this.type) {
case 'CONTENT':
symbols = ['C_NPC', 'C_ITEM', 'SELF', 'OTHER', 'VICTIM', 'ITEM', 'HERO']
switch (this.version) {
case 130:
case 2:
symbols.push('INIT_GLOBAL')
// eslint-disable-next-line no-fallthrough
case 1:
symbols.push('STARTUP_GLOBAL')
}
break
case 'MENU':
symbols = ['MENU_MAIN']
break
case 'CAMERA':
symbols = ['CAMMODNORMAL']
break
}
let basicSymbols: string[] = []

// Add minimal symbols for all parser types
const requiredSymbols = symbols?.[`G${this.version}`]?.[this.type]
if (requiredSymbols) basicSymbols = requiredSymbols

// Add Ninja helper symbols (to all parser types)
symbols = symbols.concat([
'NINJA_SYMBOLS_START',
`NINJA_SYMBOLS_START_${this.patchName}`,
basicSymbols = basicSymbols.concat([
'NINJA_VERSION',
'NINJA_PATCHES',
`NINJA_ID_${this.patchName}`,
'NINJA_MODNAME',
`NINJA_ID_${this.patchName}`,
'NINJA_SYMBOLS_START',
`NINJA_SYMBOLS_START_${this.patchName}`,
])

// Add symbols to the symbol table
if (symbols.length > 0) {
symbols.forEach((symbol) => {
// Add symbols to the symbol table(helperSymbols)
if (basicSymbols.length > 0) {
basicSymbols.forEach((symbol) => {
this.symbolTable.push({ name: symbol.toUpperCase(), file: '', line: 0 })
})
}
Expand Down Expand Up @@ -180,7 +166,7 @@ export class Parser {
let symbols: string[] = []
let repoUrl: string = ''
let srcPath: string = ''
const tmpPath = '.patch-validator-special'
const tmpPath = posix.join(process.env['RUNNER_TEMP'] ?? '', '.patch-validator-special')

switch (pattern.toLowerCase()) {
case 'ikarus':
Expand Down Expand Up @@ -287,7 +273,9 @@ export class Parser {

/**
* Parses the specified file and collects symbol tables.
*
* @param filepath - The path of the file to parse.
* @param exclude - Indicates whether the file is not part of the patch.
* @throws Error if wildcards are used in the filepath.
*/
protected parseD(filepath: string, exclude: boolean = false): void {
Expand All @@ -297,19 +285,34 @@ export class Parser {
if (this.filelist.includes(relPath)) return
this.filelist.push(relPath)

// Parse file
const input = fs.readFileSync(fullPath, 'ascii')
this.parseStr(input, exclude ? '' : relPath)
}

/**
* Parses a string input and collects symbol tables.
*
* @param input - The string input to parse.
* @param filename - The name of the file being parsed (blank for non-patch parsing).
*/
protected parseStr(input: string, filename: string = ''): void {
const inputStream = CharStream.fromString(input)
const lexer = new DaedalusLexer(inputStream)
const tokenStream = new CommonTokenStream(lexer)
const parser = new DaedalusParser(tokenStream)
const tree = parser.daedalusFile()

// Collect symbol tables
const visitor = new SymbolVisitor(exclude ? '' : relPath)
const { symbols, references } = visitor.visit(tree) as { symbols: SymbolTable; references: SymbolTable }
this.symbolTable.push(...symbols)
if (!exclude) this.referenceTable.push(...references)
const visitor = new SymbolVisitor(filename, this.symbolTable, filename ? this.referenceTable : undefined)
visitor.visit(tree)
}

/**
* Clears the temporary directory.
*/
public static async clearTmpDir(): Promise<void> {
const tmpPath = posix.join(process.env['RUNNER_TEMP'] ?? '', '.patch-validator-special')
await io.rmRF(tmpPath)
}

/**
Expand All @@ -330,12 +333,27 @@ export class Parser {

/**
* Validates the references in the reference table against the symbol table.
* This function also corrects the unscoped names in the reference table.
*/
public validateReferences(): void {
this.referenceViolations = this.referenceTable.filter((symbol) => {
const fromPatch = symbol.file !== ''
const isDefined = this.symbolTable.some((s) => s.name === symbol.name)
return fromPatch && !isDefined
this.referenceViolations.length = 0
this.referenceTable.forEach((symbol, idx) => {
// Skip base symbols
if (symbol.file === '') return

// Check if the symbol is defined
let isDefined = this.symbolTable.some((s) => s.name === symbol.name)

// Check if symbol is defined without scope
const scope = symbol.name.indexOf('.')
if (!isDefined && scope !== -1) {
const unscopedName = symbol.name.substring(scope + 1)
isDefined = this.symbolTable.some((s) => s.name === unscopedName)
this.referenceTable[idx].name = unscopedName // Fix name
}

// Add violation
if (!isDefined) this.referenceViolations.push(symbol)
})
}

Expand Down
Loading

0 comments on commit e449a9b

Please sign in to comment.