Si vous constatez la perte de tickets, de commentaires ou de projets, veuillez consulter la page https://status.framasoft.org/incident/626.

Commit c5f20011 authored by Christophe Benz's avatar Christophe Benz

Update transpiler

parent 4af13e93
# -*- coding: utf-8 -*-
import importlib
from toolz import get_in
from toolz import keyfilter
from .formulas_helpers import * # noqa
def get_variable_definition(variable_name, default=None):
from .generated.variables_definitions import variable_definition_by_name
return variable_definition_by_name.get(variable_name, default)
saisies = None # Global variable used by formulas, filled by `evaluate_formulas`
def get_variable_type(variable_name):
"""Return variable type given its name or None if `variable_name` is not a variable."""
return get_variable_definition(variable_name, {}).get('type')
# Core functions
def is_constant(variable_name):
from .generated import constants
return variable_name in constants.__dict__
def evaluate_formulas(variables_saisies=None):
"""
Evaluate formulas and return results.
Defer import of .generated.formulas module to inject `variables_saisies` in global variable.
"""
from .generated.variables_definitions import variable_definition_by_name
if variables_saisies is None:
variables_saisies = {}
global saisies
saisies = variables_saisies
formulas_module = importlib.import_module(name='.generated.formulas', package='calculateur_impots')
variables_calculees = keyfilter(
lambda key: key in variable_definition_by_name,
formulas_module.__dict__,
)
return variables_calculees
def is_base_variable(variable_name):
return is_calculee_variable(variable_name) and \
'base' in get_in(['attributes', 'tags'], get_variable_definition(variable_name, {}), default=[])
def get_variable_type(variable_name):
"""Return variable type given its name or None if `variable_name` is not a variable."""
from .generated.variables_definitions import variable_definition_by_name
return variable_definition_by_name.get(variable_name, {}).get('type')
def is_calculee_variable(variable_name):
return get_variable_type(variable_name) == 'variable_calculee'
def is_saisie_variable(variable_name):
return get_variable_type(variable_name) == 'variable_saisie'
......@@ -3,7 +3,7 @@
import math
from toolz.curried.operator import eq, ge, gt, ne
from toolz.curried.operator import ge, gt, is_, ne
# M language functions
......@@ -11,7 +11,7 @@ from toolz.curried.operator import eq, ge, gt, ne
arr = round
inf = math.floor
null = eq(0)
null = is_(None)
positif = gt(0)
positif_ou_nul = ge(0)
present = ne(0)
......
......@@ -44,7 +44,7 @@ def visit_infix_expression(node, operators={}):
else operators.get(operand_or_operator, operand_or_operator)
for index, operand_or_operator in interleave(node['operands'], node['operators'])
)
return ' '.join(map(str, tokens))
return '({})'.format(' '.join(map(str, tokens)))
# Main visitor
......@@ -107,9 +107,9 @@ def visit_boolean_expression(node):
def visit_comparaison(node):
return '{} {} {}'.format(
visit_node(node['left_operand']),
visit_node(node['left_operand'], parenthesised=True),
{'=': '=='}.get(node['operator'], node['operator']),
visit_node(node['right_operand']),
visit_node(node['right_operand'], parenthesised=True),
)
......@@ -130,9 +130,12 @@ def visit_float(node):
def visit_formula(node):
expression_source = visit_node(node['expression'])
sanitized_formula_name = sanitized_variable_name(node['name'])
expression_source = visit_node(node['expression'])
source = '{} = {}'.format(sanitized_formula_name, expression_source)
if 'pour_formula' in node:
pour_formula_name = node['pour_formula']['name']
source = '# From {}\n'.format(pour_formula_name) + source
return (sanitized_formula_name, source)
......@@ -163,11 +166,17 @@ def visit_loop_expression(node):
def visit_pour_formula(node):
return map(visit_node, iter_unlooped_nodes(
loop_variables_nodes=node['loop_variables'],
node=node['formula'],
unloop_keys=['name'],
))
def update_and_visit_node(node1):
node1.update({'pour_formula': node['formula']})
return visit_node(node1)
return map(
update_and_visit_node,
iter_unlooped_nodes(
loop_variables_nodes=node['loop_variables'],
node=node['formula'],
unloop_keys=['name'],
),
)
def visit_product_expression(node):
......@@ -187,8 +196,13 @@ def visit_sum_expression(node):
def visit_symbol(node):
symbol = node['value']
return 'saisies.get({!r}, 0)'.format(symbol) \
if core.get_variable_type(symbol) == 'variable_saisie' else sanitized_variable_name(symbol)
return 'saisie_variables.get({!r}, 0)'.format(symbol) \
if core.is_saisie_variable(symbol) \
else (
'base_variables[{!r}]'.format(symbol)
if core.is_base_variable(symbol)
else sanitized_variable_name(symbol)
)
def visit_ternary_operator(node):
......@@ -206,7 +220,7 @@ def visit_variable_const(node):
def visit_verif(node):
return '# verif {name}\n{assertions}\n'.format(
assertions='\n'.join(
'assert {expression}, get_error({error_name!r})'.format(
'assert not ({expression}), get_error({error_name!r})'.format(
error_name=condition_node['error_name'],
expression=visit_node(condition_node['expression']),
)
......
......@@ -3,15 +3,21 @@
import argparse
import json
import logging
import os
import pkg_resources
import sys
from toolz import keyfilter
from toolz.curried import concatv, itemmap, keyfilter, mapcat, pipe, valmap
# Note: absolute notation is used here since we are in a script.
from calculateur_impots import core
from calculateur_impots.generated.verif_regles import verif_regles
# from calculateur_impots.generated import chap_ini_formulas, formulas
from calculateur_impots.generated import formulas
from calculateur_impots.generated.variables_definitions import variable_definition_by_name
# from calculateur_impots.generated.verif_regles import verif_regles
# Globals
......@@ -32,7 +38,7 @@ def iter_variables_calculees(variables_names):
yield variable_name
def iter_variables_saisies(values):
def iter_saisie_variables(values):
for value, words in map(lambda value: (value, value.strip('=').split('=', 1)), values):
if len(words) == 1:
parser.error('Missing value for variable saisie: "{}"'.format(value))
......@@ -54,27 +60,120 @@ def main():
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument('-v', '--verbose', action='store_true', default=False, help='Increase output verbosity')
parser.add_argument('-d', '--debug', action='store_true', default=False, help='Display debug messages')
parser.add_argument('calculees', default=['IINET'], metavar='variable', nargs='*', help='Variables calculées')
parser.add_argument('--saisie', dest='saisies', metavar='nom=valeur', nargs='+', help='Variables saisies')
parser.add_argument('calculees', default=['IINETIR'], metavar='variable', nargs='*', help='Variables calculées')
parser.add_argument('--saisie', dest='saisie_variables', metavar='nom=valeur', nargs='+', help='Variables saisies')
args = parser.parse_args()
logging.basicConfig(level=logging.DEBUG if args.verbose or args.debug else logging.WARNING, stream=sys.stdout)
logging.basicConfig(
level=logging.DEBUG if args.debug else (logging.INFO if args.verbose else logging.WARNING),
stream=sys.stdout,
)
# Required variables_saisies: ANREV, REGCO (tag "contexte"?)
# Set V_IND_TRAIT to "primitif" (value 0?)
variables_saisies = dict(iter_variables_saisies(args.saisies)) \
if args.saisies is not None \
saisie_variables = dict(iter_saisie_variables(args.saisie_variables)) \
if args.saisie_variables is not None \
else {}
verif_regles(variables_saisies)
log.debug('variables_saisies: {}'.format(variables_saisies))
core.evaluate_formulas(variables_saisies=variables_saisies)
requested_variables_calculees = list(iter_variables_calculees(args.calculees))
variables_calculees = core.evaluate_formulas(variables_saisies=variables_saisies)
print(keyfilter(
lambda key: key in requested_variables_calculees,
variables_calculees,
))
log.debug('saisie_variables: {}'.format(saisie_variables))
base_variables_value_by_name = {} # Override base variables values
base_variables = pipe(
variable_definition_by_name,
keyfilter(core.is_base_variable),
itemmap(lambda item: (item[0], base_variables_value_by_name.get(item[0], 0))),
)
# Utilisé depuis chap-ini
# src/tgvH.m:10943:RIDEFRIBIS : calculee : "Vaut 1 si reduction different de 0 et majo et un revenu 1731 bis " type REEL ; # noqa
# src/tgvH.m:52:10MINSC : calculee : "deductions hors droits d'auteur plafonnees" ;
# src/tgvH.m:57:10MINSV : calculee : "deductions hors droits d'auteur plafonnees" ;
# src/tgvH.m:12128:SHBA : calculee : "Somme des revenus categoriels nets hors BA" ; (chap-7)
# src/tgvH.m:4019:INDCOLS : calculee restituee : "Indicateur avis CSG (colonne Contrib.sal. 2,5 %) ligne total net" ; # noqa
# src/tgvH.m:4024:INDCTXS : calculee restituee : "Indicateur avis CSG (contrib. salariale 2,5 %) degrevement" ;
# Utilisé depuis chap-aff
# src/chap-aff.m:3016:LIGBPTPGJ = positif(BPTP19WGWJ) * LIG1 * LIG2 * (1 - null(2 - V_REGCO)) * (1 - null(4 - V_REGCO)); # noqa
# src/tgvH.m:1770:BPTP19WGWJ : calculee restituee : "Revenus au taux proportionnel 19% 3WG 3WJ" ;
# src/tgvH.m:5649:LIGBPTPGJ : calculee restituee : "Indicateur plus values et creances imposees a 19 % (3WG, 3WJ)" ;
# chap_ini_formulas_results = chap_ini_formulas.compute(
# base_variables=base_variables,
# saisie_variables=saisie_variables,
# )
# log.debug('chap_ini_formulas_results: {}'.format(chap_ini_formulas_results))
# m_language_parser_dir_path = pkg_resources.get_distribution('m_language_parser').location
# formulas_dependencies_file_path = os.path.join(m_language_parser_dir_path, 'json', 'data',
# 'formulas_dependencies.json')
# with open(formulas_dependencies_file_path) as formulas_dependencies_file:
# formulas_dependencies_str = formulas_dependencies_file.read()
# formulas_dependencies_by_name = json.loads(formulas_dependencies_str)
# # log.debug(len(list(concat(formulas_dependencies_by_name.values()))))
# formulas_dependencies_by_name = valmap(
# lambda variables_names: list(sorted(filter(
# lambda formula_name: (not core.is_saisie_variable(formula_name) and
# not core.is_constant(formula_name) and
# not core.is_base_variable(formula_name)),
# variables_names,
# ))),
# formulas_dependencies_by_name,
# )
# # log.debug(len(list(concat(formulas_dependencies_by_name.values()))))
#
# # dependencies_cache = {}
#
# def get_dependencies(formula_name, missing_dependencies, depth=0):
# # if formula_name not in dependencies_cache:
# if formula_name in formulas_dependencies_by_name:
# dependencies = formulas_dependencies_by_name[formula_name]
# # log.debug('A:{}{} {}'.format(' ' * 2 * depth, formula_name, dependencies))
# if dependencies:
# result = list(concatv(
# dependencies,
# mapcat(
# lambda formula_name: get_dependencies(formula_name, missing_dependencies, depth + 1),
# dependencies,
# ),
# ))
# # log.debug('B:{}{} {} => {}'.format(' ' * 2 * depth, formula_name, dependencies, result))
# # dependencies_cache[formula_name] = result
# return result
# else:
# # dependencies_cache[formula_name] = dependencies
# return dependencies
# else:
# log.debug('C:{}{} => None (type: {}, attributes: {})'.format(' ' * 2 * depth, formula_name,
# core.get_variable_type(formula_name),
# core.get_variable_definition(formula_name, {}).get('attributes')))
# missing_dependencies.append(formula_name)
# # dependencies_cache[formula_name] = []
# return []
# # log.debug(len(dependencies_cache))
# # return dependencies_cache[formula_name]
# requested_variable_name = args.calculees[0]
# log.debug('requested_variable_name = {}'.format(requested_variable_name))
# missing_dependencies = []
# requested_variable_dependencies = set(get_dependencies(requested_variable_name, missing_dependencies))
# import ipdb; ipdb.set_trace()
formulas_results = formulas.compute(
base_variables=base_variables,
saisie_variables=saisie_variables,
)
log.debug('formulas_results: {}'.format(formulas_results))
# variables_calculees = core.evaluate_formulas(module_name='formulas')
#
# verif_regles(variables_saisies)
#
# requested_variables_calculees = list(iter_variables_calculees(args.calculees))
# variables_calculees = core.evaluate_formulas(module_name='formulas')
# # FIXME Verify that variables_calculees has type "restituee".
# print(keyfilter(
# lambda key: key in requested_variables_calculees,
# variables_calculees,
# ))
return 0
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment