Skip to content

Commit eea4cf4

Browse files
authored
Add multiple file compilation for SVML (#1375)
* Add multiple file program support to SVML compiler * Add multiple file program tests for SVML compiler
1 parent efafb0c commit eea4cf4

File tree

2 files changed

+103
-4
lines changed

2 files changed

+103
-4
lines changed

src/index.ts

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import * as es from 'estree'
3030
import { ECEResultPromise, resumeEvaluate } from './ec-evaluator/interpreter'
3131
import { CannotFindModuleError } from './errors/localImportErrors'
3232
import { validateFilePath } from './localImports/filePaths'
33+
import preprocessFileImports from './localImports/preprocessor'
3334
import { getKeywords, getProgramNames, NameDeclaration } from './name-extractor'
3435
import { parse } from './parser/parser'
3536
import { parseWithComments } from './parser/utils'
@@ -371,13 +372,39 @@ export function compile(
371372
context: Context,
372373
vmInternalFunctions?: string[]
373374
): SVMProgram | undefined {
374-
const astProgram = parse(code, context)
375-
if (!astProgram) {
375+
const defaultFilePath = '/default.js'
376+
const files: Partial<Record<string, string>> = {}
377+
files[defaultFilePath] = code
378+
return compileFiles(files, defaultFilePath, context, vmInternalFunctions)
379+
}
380+
381+
export function compileFiles(
382+
files: Partial<Record<string, string>>,
383+
entrypointFilePath: string,
384+
context: Context,
385+
vmInternalFunctions?: string[]
386+
): SVMProgram | undefined {
387+
for (const filePath in files) {
388+
const filePathError = validateFilePath(filePath)
389+
if (filePathError !== null) {
390+
context.errors.push(filePathError)
391+
return undefined
392+
}
393+
}
394+
395+
const entrypointCode = files[entrypointFilePath]
396+
if (entrypointCode === undefined) {
397+
context.errors.push(new CannotFindModuleError(entrypointFilePath))
398+
return undefined
399+
}
400+
401+
const preprocessedProgram = preprocessFileImports(files, entrypointFilePath, context)
402+
if (!preprocessedProgram) {
376403
return undefined
377404
}
378405

379406
try {
380-
return compileToIns(astProgram, undefined, vmInternalFunctions)
407+
return compileToIns(preprocessedProgram, undefined, vmInternalFunctions)
381408
} catch (error) {
382409
context.errors.push(error)
383410
return undefined

src/runner/__tests__/files.ts

Lines changed: 73 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { parseError, runFilesInContext } from '../../index'
1+
import { compileFiles, parseError, runFilesInContext } from '../../index'
22
import { mockContext } from '../../mocks/context'
33
import { Chapter } from '../../types'
44

@@ -73,3 +73,75 @@ describe('runFilesInContext', () => {
7373
`)
7474
})
7575
})
76+
77+
describe('compileFiles', () => {
78+
let context = mockContext(Chapter.SOURCE_4)
79+
80+
beforeEach(() => {
81+
context = mockContext(Chapter.SOURCE_4)
82+
})
83+
84+
it('returns IllegalCharInFilePathError if any file path contains invalid characters', () => {
85+
const files: Record<string, string> = {
86+
'/a.js': '1 + 2;',
87+
'/+-.js': '"hello world";'
88+
}
89+
compileFiles(files, '/a.js', context)
90+
expect(parseError(context.errors)).toMatchInlineSnapshot(
91+
`"File path '/+-.js' must only contain alphanumeric chars and/or '_', '/', '.', '-'."`
92+
)
93+
})
94+
95+
it('returns IllegalCharInFilePathError if any file path contains invalid characters - verbose', () => {
96+
const files: Record<string, string> = {
97+
'/a.js': '1 + 2;',
98+
'/+-.js': '"hello world";'
99+
}
100+
compileFiles(files, '/a.js', context)
101+
expect(parseError(context.errors, true)).toMatchInlineSnapshot(`
102+
"File path '/+-.js' must only contain alphanumeric chars and/or '_', '/', '.', '-'.
103+
Rename the offending file path to only use valid chars.
104+
"
105+
`)
106+
})
107+
108+
it('returns ConsecutiveSlashesInFilePathError if any file path contains consecutive slash characters', () => {
109+
const files: Record<string, string> = {
110+
'/a.js': '1 + 2;',
111+
'/dir//dir2/b.js': '"hello world";'
112+
}
113+
compileFiles(files, '/a.js', context)
114+
expect(parseError(context.errors)).toMatchInlineSnapshot(
115+
`"File path '/dir//dir2/b.js' cannot contain consecutive slashes '//'."`
116+
)
117+
})
118+
119+
it('returns ConsecutiveSlashesInFilePathError if any file path contains consecutive slash characters - verbose', () => {
120+
const files: Record<string, string> = {
121+
'/a.js': '1 + 2;',
122+
'/dir//dir2/b.js': '"hello world";'
123+
}
124+
compileFiles(files, '/a.js', context)
125+
expect(parseError(context.errors, true)).toMatchInlineSnapshot(`
126+
"File path '/dir//dir2/b.js' cannot contain consecutive slashes '//'.
127+
Remove consecutive slashes from the offending file path.
128+
"
129+
`)
130+
})
131+
132+
it('returns CannotFindModuleError if entrypoint file does not exist', () => {
133+
const files: Record<string, string> = {}
134+
compileFiles(files, '/a.js', context)
135+
expect(parseError(context.errors)).toMatchInlineSnapshot(`"Cannot find module '/a.js'."`)
136+
})
137+
138+
it('returns CannotFindModuleError if entrypoint file does not exist - verbose', () => {
139+
const files: Record<string, string> = {}
140+
compileFiles(files, '/a.js', context)
141+
expect(parseError(context.errors, true)).toMatchInlineSnapshot(`
142+
"Cannot find module '/a.js'.
143+
Check that the module file path resolves to an existing file.
144+
"
145+
`)
146+
})
147+
})

0 commit comments

Comments
 (0)