def tryNew(cls, lineNumber: int, indentation: int, line: str) -> Optional[ParsedLine]: """Teste si la ligne respecte le modèle variable = expression et crée l'item correspondant le cas échéant :param lineNumber: numéro de la ligne :type lineNumber: int :param indentation: indentation de la ligne :type indentation: int :param line: ligne à analyser :type line: str :return: noeud du type print ou None :rtype: Optional[ParsedLine_Print] :raises: ParseError si l'expression ou variable détectée n'ont pas la bonne forme """ allGroup = re.search(cls.regex(), line) if allGroup is None: return None variableName = allGroup[1].strip() # la variable expressionStr = allGroup[ 2] # tout ce qu'il y a dans les ( ) de l'input if not ExpressionParser.strIsVariableName(variableName): raise ParseError( "La variable <{}> est incorrecte.".format(variableName), {"lineNumber": lineNumber}) expr = ExpressionParser.buildExpression(expressionStr) if not isinstance(expr, ArithmeticExpressionNode): raise ParseError("L'expression <{}> est incorrecte.".format(expr), {"lineNumber": lineNumber}) return ParsedLine_Affectation(lineNumber, indentation, Variable.add(variableName), expr)
def _parseCode(cls, lignesCode: List[str]) -> List[StructureNode]: """Parse du programme donné sous forme d'une liste de lignes :param lignesCode: lignes du programme :type filename: List[str] :return: liste de noeuds représentant le programme :rtype: List[StructureNode] """ # on réinitialise le manager des variables Variable.resetVariableManager() listParsedLines: List[ParsedLine] = [] for index, line in enumerate(lignesCode): objLine = cls._parseLine(line, index + 1) # Traitement ligne non vide if not objLine.empty: # Ajout des informations parsées dans le listing listParsedLines.append(objLine) indentationErrorLineNumber = cls._verifyIndent(listParsedLines) if indentationErrorLineNumber >= 0: raise ParseError("Erreur d'indentation.", {"lineNumber": indentationErrorLineNumber}) elseErrorLineNumber = cls._verifyElse(listParsedLines) if elseErrorLineNumber >= 0: raise ParseError( "elif ou else n'est pas correctement lié à un if.", {"lineNumber": elseErrorLineNumber}) groupedList = cls._groupBlocs(listParsedLines) return cls._convertParsedLinesToStructurNodes(groupedList)
def tryNew(cls, lineNumber: int, indentation: int, line: str) -> Optional[ParsedLine]: """Teste si la ligne respecte le modèle print et crée l'item correspondant le cas échéant :param lineNumber: numéro de la ligne :type lineNumber: int :param indentation: indentation de la ligne :type indentation: int :param line: ligne à analyser :type line: str :return: noeud du type print ou None :rtype: Optional[ParsedLine_Print] :raises: ParseError si la variable n'a pas la bonne forme """ allGroup = re.search(cls.regex(), line) if allGroup is None: return None variableName = allGroup[1].strip() # la variable if not ExpressionParser.strIsVariableName(variableName): raise ParseError( "La variable <{}> est incorrecte.".format(variableName), {"lineNumber": lineNumber}) return ParsedLine_Input(lineNumber, indentation, Variable.add(variableName))
def tryNew(cls, lineNumber: int, indentation: int, line: str) -> Optional[ParsedLine]: """Teste si la ligne respecte le modèle print et crée l'item correspondant le cas échéant :param lineNumber: numéro de la ligne :type lineNumber: int :param indentation: indentation de la ligne :type indentation: int :param line: ligne à analyser :type line: str :return: noeud du type print ou None :rtype: Optional[ParsedLine_Print] :raises: ParseError si l'expression détectée n'est pas du bon type """ allGroup = re.search(cls.regex(), line) if allGroup is None: return None firstGroup = allGroup[1] # tout ce qui match dans les ( ) expr = ExpressionParser.buildExpression(firstGroup) if not isinstance(expr, ArithmeticExpressionNode): raise ParseError("L'expression <{}> est incorrecte.".format(expr), {"lineNumber": lineNumber}) return ParsedLine_Print(lineNumber, indentation, expr)
def tryNew(cls, lineNumber: int, indentation: int, line: str) -> Optional[ParsedLine]: """Teste si la ligne respecte le modèle [mot-clef] condition et crée l'item correspondant le cas échéant :param lineNumber: numéro de la ligne :type lineNumber: int :param indentation: indentation de la ligne :type indentation: int :param line: ligne à analyser :type line: str :return: noeud du type reconnu ou None :rtype: Union[ParsedLine_If, ParsedLine_Elif, ParsedLine_While, None] :raises: ParseError si l'expression trouvée n'a pas le bon type """ allGroup = re.search(cls.regex(), line) if allGroup is None: return None firstGroup = allGroup[ 1] # tout ce qui match après testStructureKeyword et avant les : condition = ExpressionParser.buildExpression(firstGroup) if not isinstance(condition, (LogicExpressionNode, ComparaisonExpressionNode)): raise ParseError( "L'expression <{}> n'est pas une condition.".format(condition), {"lineNumber": lineNumber}) node = cls(lineNumber, indentation, condition) return node
def _parseLine(cls, originalLine: str, lineNumber: int) -> ParsedLine: """parse d'une ligne :param originalLine: ligne d'origine :type originalLine: str :param lineNumber: numéro de la ligne d'origine :type line Number: int :return: noeud de type LP :raises: ParseError si type de ligne pas reconnue """ cleanLine = cls._suppCommentsAndEndSpaces(originalLine) emptyLine = (cleanLine == "") indentation = cls._countIndentation(originalLine) if emptyLine: return ParsedLine(lineNumber) classesToTry = (ParsedLine_If, ParsedLine_Elif, ParsedLine_While, ParsedLine_Else, ParsedLine_Print, ParsedLine_Input, ParsedLine_Affectation) # remarque : il faut tester input avant affectation car input() génère autrement une erreur # si interprété comme une expression for c in classesToTry: lineObject: Optional[ParsedLine] = c.tryNew( lineNumber, indentation, cleanLine) if not lineObject is None: return lineObject raise ParseError("Erreur de syntaxe : <{}>".format(cleanLine), {"lineNumber": lineNumber})
def _convertParsedLinesToStructurNodes( cls, listParsedLine: List[ParsedLine]) -> List['StructureNode']: """Convertit une liste de ParsedLine en liste de StructureNode :param listParsedLine: liste des objets à convertir :type parsedLine: List[ParsedLine] :return: liste sous forme de StructureNode :rtype: List[StructureNode] """ reversedListParsedLines = listParsedLine[::-1] outList: List[StructureNode] = [] pendingElse: Optional[ParsedLine_Else] = None newNode: StructureNode children: List[StructureNode] elseChildren: List[StructureNode] for line in reversedListParsedLines: if isinstance(line, ParsedLine_Else): pendingElse = line continue elif isinstance( line, ParsedLine_While): # While avant If, Attention héritage ! children = cls._convertParsedLinesToStructurNodes( line.children) newNode = WhileNode(line.lineNumber, line.condition, children) elif isinstance(line, ParsedLine_If) and not (pendingElse is None): children = cls._convertParsedLinesToStructurNodes( line.children) elseChildren = cls._convertParsedLinesToStructurNodes( pendingElse.children) newNode = IfElseNode(line.lineNumber, line.condition, children, pendingElse.lineNumber, elseChildren) elif isinstance(line, ParsedLine_If): children = cls._convertParsedLinesToStructurNodes( line.children) newNode = IfNode(line.lineNumber, line.condition, children) elif isinstance(line, ParsedLine_Print): newNode = TransfertNode(line.lineNumber, None, line.expression) elif isinstance(line, ParsedLine_Input): newNode = TransfertNode(line.lineNumber, line.variable, None) elif isinstance(line, ParsedLine_Affectation): newNode = TransfertNode(line.lineNumber, line.variable, line.expression) else: raise ParseError("Erreur imprévue.", {"lineNumber": line.lineNumber}) outList.append(newNode) return outList[::-1]
def parse(cls, **options) -> List[StructureNode]: """ parse le contenu de l'entité fournie : options doit contenir l'un des attributs : - filename : nom de fichier contenant le code - code : chaîne de caractère contenant le code """ if "filename" in options: filename = options["filename"] return cls._parseFile(filename) if "code" in options: code = options["code"] return cls._parseCode(code.split("\n")) raise ParseError("Il faut donner 'filename' ou 'code'")