Skip to content
This repository was archived by the owner on Feb 14, 2025. It is now read-only.

Commit

Permalink
fix(optim): manage private rules
Browse files Browse the repository at this point in the history
  • Loading branch information
EmileRolley committed Feb 1, 2024
1 parent 75d9b17 commit 624c3ed
Show file tree
Hide file tree
Showing 3 changed files with 50 additions and 38 deletions.
77 changes: 40 additions & 37 deletions source/optims/constantFolding.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import Engine, {
traverseASTNode,
Unit,
EvaluatedNode,
utils,
} from 'publicodes'
import type { RuleNode, ASTNode } from 'publicodes'
import { RuleName } from '../commons'
Expand Down Expand Up @@ -59,7 +60,6 @@ function addMapEntry(map: RefMap, key: RuleName, values: RuleName[]) {

function initFoldingCtx(
engine: Engine,
parsedRules: ParsedRules<RuleName>,
toKeep?: PredicateOnRule,
foldingParams?: FoldingParams,
): FoldingCtx {
Expand All @@ -68,6 +68,10 @@ function initFoldingCtx(
childs: new Map(),
}
const unfoldableRules = new Set<RuleName>()
// // PERF: could it be avoided?
// JSON.parse(JSON.stringify(engine.baseContext.parsedRules))

const parsedRules = copyFullParsedRules(engine)

// NOTE: we need to traverse the AST to find all the references of a rule.
// We can't use the [referencesMap] from the engine's context because it
Expand Down Expand Up @@ -110,7 +114,7 @@ function initFoldingCtx(
) ?? new Set()

const traversedVariables: RuleName[] = Array.from(reducedAST).filter(
(name) => !name.endsWith(' . $SITUATION'),
(name) => !name.endsWith('$SITUATION'),
)

if (traversedVariables.length > 0) {
Expand All @@ -133,22 +137,24 @@ function initFoldingCtx(
}
}

function isFoldable(
rule: RuleNode | undefined,
childs: Set<RuleName> | undefined,
contextRules: Set<RuleName>,
): boolean {
const unfoldableAttr = ['par défaut', 'question']

function isFoldable(ctx: FoldingCtx, rule: RuleNode): boolean {
let childInContext = false
const childs = ctx.refs.childs.get(rule.dottedName)

childs?.forEach((child) => {
if (contextRules.has(child)) {
if (ctx.unfoldableRules.has(child)) {
childInContext = true
return
}
})

return (
rule !== undefined && !contextRules.has(rule.dottedName) && !childInContext
rule !== undefined &&
!unfoldableAttr.find((attr) => attr in rule.rawNode) &&
!ctx.unfoldableRules.has(rule.dottedName) &&
!childInContext
)
}

Expand All @@ -168,13 +174,7 @@ function searchAndReplaceConstantValueInParentRefs(
for (const parentName of refs) {
const parentRule = ctx.parsedRules[parentName]

if (
isFoldable(
parentRule,
ctx.refs.childs.get(parentName),
ctx.unfoldableRules,
)
) {
if (isFoldable(ctx, parentRule)) {
const newRule = traverseASTNode(
transformAST((node, _) => {
if (node.nodeKind === 'reference' && node.dottedName === ruleName) {
Expand Down Expand Up @@ -219,7 +219,7 @@ function deleteRule(ctx: FoldingCtx, dottedName: RuleName): void {
const ruleNode = ctx.parsedRules[dottedName]
if (
(ctx.toKeep === undefined || !ctx.toKeep([dottedName, ruleNode])) &&
isFoldable(ruleNode, ctx.refs.childs.get(dottedName), ctx.unfoldableRules)
isFoldable(ctx, ruleNode)
) {
removeRuleFromRefs(ctx.refs.parents, dottedName)
removeRuleFromRefs(ctx.refs.childs, dottedName)
Expand Down Expand Up @@ -328,11 +328,11 @@ function isNullable(node: ASTNode): boolean {
function fold(ctx: FoldingCtx, ruleName: RuleName, rule: RuleNode): void {
if (
rule !== undefined &&
(!isFoldable(rule, ctx.refs.childs.get(ruleName), ctx.unfoldableRules) ||
(!isFoldable(ctx, rule) ||
!utils.isAccessible(ctx.parsedRules, '', rule.dottedName) ||
isAlreadyFolded(ctx.params, rule) ||
!(ruleName in ctx.parsedRules))
) {
// Already managed rule
return
}

Expand Down Expand Up @@ -384,6 +384,24 @@ function fold(ctx: FoldingCtx, ruleName: RuleName, rule: RuleNode): void {
}
}

/**
* Deep copies the private [parsedRules] field of [engine] (without the '$SITUATION'
* rules).
*/
function copyFullParsedRules(engine: Engine): ParsedRules<RuleName> {
const parsedRules: ParsedRules<RuleName> = {}

for (const ruleName in engine.baseContext.parsedRules) {
if (!ruleName.endsWith('$SITUATION')) {
parsedRules[ruleName] = structuredClone(
engine.baseContext.parsedRules[ruleName],
)
}
}

return parsedRules
}

/**
* Applies a constant folding optimisation pass on parsed rules of [engine].
*
Expand All @@ -399,11 +417,7 @@ export function constantFolding(
toKeep?: PredicateOnRule,
params?: FoldingParams,
): ParsedRules<RuleName> {
const parsedRules: ParsedRules<RuleName> =
// PERF: could it be avoided?
JSON.parse(JSON.stringify(engine.getParsedRules()))

let ctx = initFoldingCtx(engine, parsedRules, toKeep, params)
let ctx = initFoldingCtx(engine, toKeep, params)

let nbRules = Object.keys(ctx.parsedRules).length
let nbRulesBefore = undefined
Expand All @@ -412,14 +426,7 @@ export function constantFolding(
for (const ruleName in ctx.parsedRules) {
const ruleNode = ctx.parsedRules[ruleName]

if (
isFoldable(
ruleNode,
ctx.refs.childs.get(ruleName),
ctx.unfoldableRules,
) &&
!isAlreadyFolded(ctx.params, ruleNode)
) {
if (isFoldable(ctx, ruleNode) && !isAlreadyFolded(ctx.params, ruleNode)) {
fold(ctx, ruleName, ruleNode)
}
}
Expand All @@ -433,11 +440,7 @@ export function constantFolding(
const parents = ctx.refs.parents.get(ruleName)

if (
isFoldable(
ruleNode,
ctx.refs.childs.get(ruleName),
ctx.unfoldableRules,
) &&
isFoldable(ctx, ruleNode) &&
!toKeep([ruleName, ruleNode]) &&
(!parents || parents?.size === 0)
) {
Expand Down
2 changes: 1 addition & 1 deletion source/serializeParsedRules.ts
Original file line number Diff line number Diff line change
Expand Up @@ -426,7 +426,7 @@ export function serializeParsedRules(
const rawRules = {}

for (const [rule, node] of Object.entries(parsedRules)) {
if (rule.endsWith(' . $SITUATION')) {
if (rule.endsWith('$SITUATION') || rule.includes('$INTERNAL')) {
delete rawRules[rule]
continue
}
Expand Down
9 changes: 9 additions & 0 deletions test/optims/constantFolding.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1013,6 +1013,7 @@ describe('Constant folding [base]', () => {
valeur: 2,
},
'foo remplacé dans résultat 2': {
'applicable si': 'non',
remplace: {
'références à': 'foo',
'sauf dans': ['résultat 1'],
Expand Down Expand Up @@ -1065,12 +1066,20 @@ describe('Constant folding [base]', () => {
},
},
},
foo: {
privé: 'oui',
'par défaut': 10,
},
}
expect(constantFoldingWith(rawRules)).toStrictEqual({
cotisation: {
optimized: 'fully',
valeur: '58.8 €',
},
foo: {
privé: 'oui',
'par défaut': 10,
},
})
})
})

0 comments on commit 624c3ed

Please sign in to comment.