From c96a122d8a0c0ce38e4550b511cf331e239d1867 Mon Sep 17 00:00:00 2001 From: Alexandre Hajjar Date: Fri, 24 Apr 2020 21:32:05 +0200 Subject: [PATCH] =?UTF-8?q?WIP=20=E2=9A=99=EF=B8=8F=20Detect=20cycles:=20R?= =?UTF-8?q?ecalculBroken=20&=20visit=20Formule=20node?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- publicodes/source/cyclesLib.ts | 252 +++++++++++++++++++++++++-------- 1 file changed, 192 insertions(+), 60 deletions(-) diff --git a/publicodes/source/cyclesLib.ts b/publicodes/source/cyclesLib.ts index 418561f28..6ff396883 100644 --- a/publicodes/source/cyclesLib.ts +++ b/publicodes/source/cyclesLib.ts @@ -22,7 +22,90 @@ type ASTNode = { [_: string]: {} | undefined } // [XXX] - Vaudrait-il mieux utiliser les DottedNames directement ici? // A priori non car on peut imaginer cette lib comme étant indépendante des règles existantes et // fonctionnant par ex en "mode serveur". -type RuleNode = /* ASTNode & */ ParsedRule +type RuleNode = ASTNode & ParsedRule + +type RuleProp = ASTNode & { + category: 'ruleProp' + rulePropType: string +} +export function isRuleProp(node: ASTNode): node is RuleProp { + return ( + (node as RuleProp).category === 'ruleProp' && + typeof (node as RuleProp).rulePropType === 'string' + ) +} + +type CondRuleProp = RuleProp & { + rulePropType: 'cond' +} +export function isCondRuleProp(node: ASTNode): node is CondRuleProp { + return isRuleProp(node) && (node as CondRuleProp).rulePropType === 'cond' +} + +type ApplicableSi = CondRuleProp & { + dottedName: 'applicable si' + explanation: ASTNode +} +export function isApplicableSi(node: ASTNode): node is ApplicableSi { + const applicableSi = node as ApplicableSi + return ( + isCondRuleProp(node) && + applicableSi.dottedName === 'applicable si' && + applicableSi.explanation !== undefined + ) +} + +type NonApplicableSi = CondRuleProp & { + dottedName: 'non applicable si' + explanation: ASTNode +} +export function isNonApplicableSi(node: ASTNode): node is NonApplicableSi { + const nonApplicableSi = node as NonApplicableSi + return ( + isCondRuleProp(node) && + nonApplicableSi.dottedName === 'non applicable si' && + nonApplicableSi.explanation !== undefined + ) +} + +type Formule = RuleProp & { + name: 'formule' + rulePropType: 'formula' + explanation: FormuleExplanation +} +export function isFormule( + node: ASTNode +): node is Formule { + const formule = node as Formule + return ( + isRuleProp(node) && + formule.name === 'formule' && + formule.rulePropType === 'formula' && + isFormuleExplanation(formule.explanation) + ) +} + +type FormuleExplanation = + | Value + | Operation + | Possibilities + | Possibilities2 + | Reference + | RecalculBroken + | AnyMechanism +export function isFormuleExplanation( + node: ASTNode +): node is FormuleExplanation { + return ( + isValue(node) || + isOperation(node) || + isReference(node) || + isPossibilities(node) || + isPossibilities2(node) || + isRecalculBroken(node) || + isAnyMechanism(node) + ) +} type Value = ASTNode & { nodeValue: number | string | boolean @@ -98,30 +181,23 @@ export function isReference( ) } -type Recalcul = ASTNode & { - explanation: { - recalcul: Reference - amendedSituation: Record> - } +type RecalculBroken = ASTNode & { + avec: Record // Note: TS doesn't allow `Record`! + règle?: Name + evaluate: Function } -export function isRecalcul( +export function isRecalculBroken( node: ASTNode -): node is Recalcul { - const recalcul = node as Recalcul - const isReferenceSpec = isReference as ( - node: ASTNode - ) => node is Reference +): node is RecalculBroken { + const recalculBroken = node as RecalculBroken + // Very defensive because we don't want to take risks with this not-well-defined kind of node: return ( - typeof recalcul.explanation === 'object' && - typeof recalcul.explanation.recalcul === 'object' && - isReferenceSpec(recalcul.explanation.recalcul as ASTNode) && - typeof recalcul.explanation.amendedSituation === 'object' - // [XXX] - We would like to do - // && R.all(isDottedName, R.keys(recalcul.explanation.amendedSituation)) - // but it seems there is no simple way to get a type's guard in Typescript - // apart if it's built as a class. Or we could rebuild everything here with - // passing this guard ƒ as a context everywhere along with the ASTNodes, - // with a context monad for example. Overkill. + R.all( + key => R.includes(key, ['avec', 'règle', 'evaluate']), + R.keys(recalculBroken) + ) && + typeof recalculBroken.avec === 'object' && + recalculBroken.evaluate instanceof Function ) } @@ -136,6 +212,33 @@ export function isAbstractMechanism(node: ASTNode): node is AbstractMechanism { ) } +type RecalculMech = AbstractMechanism & { + explanation: { + recalcul: Reference + amendedSituation: Record> + } +} +export function isRecalculMech( + node: ASTNode +): node is RecalculMech { + const recalculMech = node as RecalculMech + const isReferenceSpec = isReference as ( + node: ASTNode + ) => node is Reference + return ( + typeof recalculMech.explanation === 'object' && + typeof recalculMech.explanation.recalcul === 'object' && + isReferenceSpec(recalculMech.explanation.recalcul as ASTNode) && + typeof recalculMech.explanation.amendedSituation === 'object' + // [XXX] - We would like to do + // && R.all(isDottedName, R.keys(recalculMech.explanation.amendedSituation)) + // but it seems there is no simple way to get a type's guard in Typescript + // apart if it's built as a class. Or we could rebuild everything here with + // passing this guard ƒ as a context everywhere along with the ASTNodes, + // with a context monad for example. Overkill. + ) +} + type EncadrementMech = AbstractMechanism & { name: 'encadrement' explanation: { @@ -465,16 +568,37 @@ export function isDureeMech(node: ASTNode): node is DureeMech { ) } -type AnyMechanism = EncadrementMech | SommeMech -export function isAnyMechanism(node: ASTNode): node is AnyMechanism { +type AnyMechanism = + | RecalculMech + | EncadrementMech + | SommeMech + | ProduitMech + | VariationsMech + | AllegementMech + | BaremeMech + | InversionNumMech + | ArrondiMech + | MaxMech + | MinMech + | ComposantesMech + | UneConditionsMech + | ToutesConditionsMech + | SyncMech + | GrilleMech + | TauxProgMech + | DureeMech +export function isAnyMechanism( + node: ASTNode +): node is AnyMechanism { return ( + isRecalculMech(node) || isEncadrementMech(node) || isSommeMech(node) || isProduitMech(node) || isVariationsMech(node) || isAllegementMech(node) || isBaremeMech(node) || - isInversionNumMech(node) || + isInversionNumMech(node) || isArrondiMech(node) || isMaxMech(node) || isMinMech(node) || @@ -488,28 +612,6 @@ export function isAnyMechanism(node: ASTNode): node is AnyMechanism { ) } -type FormuleNode = - | Value - | Operation - | Possibilities - | Possibilities2 - | Reference - | Recalcul - | AnyMechanism -export function isFormuleNode( - node: ASTNode -): node is FormuleNode { - return ( - isValue(node) || - isOperation(node) || - isReference(node) || - isPossibilities(node) || - isPossibilities2(node) || - isRecalcul(node) || - isAnyMechanism(node) - ) -} - function logVisit(depth: number, typeName: string, obj: {}): void { return let cleanRepr = obj @@ -523,6 +625,14 @@ export function ruleDependenciesOfNode( depth: number, node: ASTNode ): Array { + function ruleDependenciesOfFormule( + depth: number, + formule: Formule + ): Array { + logVisit(depth, 'formule', '') + return ruleDependenciesOfNode(depth + 1, formule.explanation) + } + function ruleDependenciesOfValue(depth: number, value: Value): Array { logVisit(depth, 'value', value.nodeValue) return [] @@ -568,16 +678,24 @@ export function ruleDependenciesOfNode( return [reference.dottedName] } - function ruleDependenciesOfRecalcul( + function ruleDependenciesOfRecalculBroken( depth: number, - recalcul: Recalcul + recalculBroken: RecalculBroken + ): Array { + logVisit(depth, 'recalcul broken', recalculBroken.règle || '') + return recalculBroken.règle ? [recalculBroken.règle] : [] + } + + function ruleDependenciesOfRecalculMech( + depth: number, + recalculMech: RecalculMech ): Array { logVisit( depth, 'recalcul', - recalcul.explanation.recalcul.partialReference as string + recalculMech.explanation.recalcul.partialReference as string ) - return [recalcul.explanation.recalcul.partialReference] + return [recalculMech.explanation.recalcul.partialReference] } function ruleDependenciesOfEncadrementMech( @@ -873,7 +991,9 @@ export function ruleDependenciesOfNode( return result } - if (isValue(node)) { + if (isFormule(node)) { + return ruleDependenciesOfFormule(depth, node) + } else if (isValue(node)) { return ruleDependenciesOfValue(depth, node) } else if (isOperation(node)) { return ruleDependenciesOfOperation(depth, node) @@ -883,8 +1003,10 @@ export function ruleDependenciesOfNode( return ruleDependenciesOfPossibilities(depth, node) } else if (isPossibilities2(node)) { return ruleDependenciesOfPossibilities2(depth, node) - } else if (isRecalcul(node)) { - return ruleDependenciesOfRecalcul(depth, node) + } else if (isRecalculBroken(node)) { + return ruleDependenciesOfRecalculBroken(depth, node) + } else if (isRecalculMech(node)) { + return ruleDependenciesOfRecalculMech(depth, node) } else if (isEncadrementMech(node)) { return ruleDependenciesOfEncadrementMech(depth, node) } else if (isSommeMech(node)) { @@ -939,22 +1061,32 @@ export function ruleDependenciesOfNode( return [] } -function ruleDependenciesOfRule( +function ruleDependenciesOfRuleNode( depth: number, rule: RuleNode ): Array { - logVisit(depth, 'rule', rule.dottedName as string) + logVisit(depth, 'Rule', rule.dottedName) + if (rule.formule) { - const formuleNode: FormuleNode = rule.formule.explanation + const formuleNode = rule.formule + return ruleDependenciesOfNode(depth + 1, formuleNode) + const formuleExplanationNode: FormuleExplanationNode = + rule.formule.explanation // // This is for comfort, as the responsibility over structure isn't owned by this piece of code: - // if (!isFormuleNode(formuleNode)) { + // if (!isFormuleExplanationNode(formuleExplanationNode)) { // debugger // // throw Error( // // `This rule's formule is not of a known type: ${rule.dottedName}` // // ) // } - return ruleDependenciesOfNode(depth + 1, formuleNode) + return ruleDependenciesOfNode(depth + 1, formuleExplanationNode) } + // if (rule['applicable si']) { + // debugger + // } + // if (rule['non applicable si']) { + // debugger + // } return [] } @@ -977,7 +1109,7 @@ export function buildRulesDependencies( return R.map( ([dottedName, ruleNode]: [Name, RuleNode]): [Name, Array] => [ dottedName, - ruleDependenciesOfRule(0, ruleNode) + ruleDependenciesOfRuleNode(0, ruleNode) ], pairs )