-
Notifications
You must be signed in to change notification settings - Fork 37
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #1163 from informalsystems/gabriela/fix-shadowing-…
…different-modules Rename shadowed names to unique names
- Loading branch information
Showing
5 changed files
with
158 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
/* ---------------------------------------------------------------------------------- | ||
* Copyright (c) Informal Systems 2023. All rights reserved. | ||
* Licensed under the Apache 2.0. | ||
* See License.txt in the project root for license information. | ||
* --------------------------------------------------------------------------------- */ | ||
|
||
/** | ||
* Renamer for shadowed names. | ||
* | ||
* @author Gabriela Moreira | ||
* | ||
* @module | ||
*/ | ||
|
||
import { IRTransformer, transformModule } from '../ir/IRTransformer' | ||
import { LookupTable } from './base' | ||
import { QuintApp, QuintLambda, QuintLambdaParameter, QuintLet, QuintModule, QuintName } from '../ir/quintIr' | ||
|
||
/** | ||
* Replace all names with unique names, to avoid shadowing. | ||
* - Lambda parameters are renamed to `<name>_<lambda-id>` | ||
* - Nested definitions (from let expressions) are renamed to `<name>_<let-id>` | ||
* | ||
* @param module The module to unshadow | ||
* @param lookupTable The lookup table with the module's name references | ||
* | ||
* @returns The module with no shadowed names | ||
*/ | ||
export function unshadowNames(module: QuintModule, lookupTable: LookupTable): QuintModule { | ||
const transformer = new Unshadower(lookupTable) | ||
return transformModule(transformer, module) | ||
} | ||
|
||
class Unshadower implements IRTransformer { | ||
private nestedNames = new Map<bigint, string>() | ||
private lookupTable: LookupTable | ||
|
||
constructor(lookupTable: LookupTable) { | ||
this.lookupTable = lookupTable | ||
} | ||
|
||
enterLambda(lambda: QuintLambda): QuintLambda { | ||
const newParams: QuintLambdaParameter[] = lambda.params.map(p => { | ||
const newName = `${p.name}_${lambda.id}` | ||
this.nestedNames.set(p.id, newName) | ||
|
||
return { ...p, name: newName } | ||
}) | ||
return { ...lambda, params: newParams } | ||
} | ||
|
||
enterLet(expr: QuintLet): QuintLet { | ||
const newName = `${expr.opdef.name}_${expr.id}` | ||
this.nestedNames.set(expr.opdef.id, newName) | ||
|
||
return { ...expr, opdef: { ...expr.opdef, name: newName } } | ||
} | ||
|
||
enterName(expr: QuintName): QuintName { | ||
const def = this.lookupTable.get(expr.id) | ||
if (!def) { | ||
return expr | ||
} | ||
|
||
const newName = this.nestedNames.get(def.id) | ||
if (newName) { | ||
this.lookupTable.set(expr.id, { ...def, name: newName }) | ||
return { ...expr, name: newName } | ||
} | ||
|
||
return expr | ||
} | ||
|
||
enterApp(expr: QuintApp): QuintApp { | ||
const def = this.lookupTable.get(expr.id) | ||
if (!def) { | ||
return expr | ||
} | ||
|
||
const newName = this.nestedNames.get(def.id) | ||
if (newName) { | ||
this.lookupTable.set(expr.id, { ...def, name: newName }) | ||
return { ...expr, opcode: newName } | ||
} | ||
|
||
return expr | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
import { describe, it } from 'mocha' | ||
import { assert } from 'chai' | ||
import { newIdGenerator } from '../../src/idGenerator' | ||
import { SourceLookupPath } from '../../src/parsing/sourceResolver' | ||
import { parse } from '../../src/parsing/quintParserFrontend' | ||
import { unshadowNames } from '../../src/names/unshadower' | ||
import { moduleToString } from '../../src' | ||
import { dedent } from '../textUtils' | ||
|
||
describe('unshadowNames', () => { | ||
function parseModules(text: string) { | ||
const idGenerator = newIdGenerator() | ||
const fake_path: SourceLookupPath = { normalizedPath: 'fake_path', toSourceName: () => 'fake_path' } | ||
|
||
const result = parse(idGenerator, 'test_location', fake_path, text) | ||
if (result.isLeft()) { | ||
assert.fail(`Expected no error, but got ${result.value.map(e => e.explanation)}`) | ||
} | ||
|
||
return result.unwrap() | ||
} | ||
|
||
it('returns a module with no shadowed names', () => { | ||
const { modules, table } = parseModules(` | ||
module A { | ||
def f(a) = a > 0 | ||
val b = val a = 1 { a } | ||
} | ||
module B { | ||
var a: int | ||
import A.* | ||
}`) | ||
|
||
const unshadowedModules = modules.map(m => unshadowNames(m, table)) | ||
|
||
assert.sameDeepMembers(unshadowedModules.map(moduleToString), [ | ||
dedent(`module A { | ||
| val b = val a_9 = 1 { a_9 } | ||
| def f = ((a_5) => igt(a_5, 0)) | ||
|}`), | ||
dedent(`module B { | ||
| var a: int | ||
| import A.* | ||
|}`), | ||
]) | ||
}) | ||
}) |