Mercurial > hgrepos > Python > libs > pygments-lexer-pseudocode2
view pygments_lexer_pseudocode2/lexers/algpseudocode.py @ 281:ee512932d603
Make "\T" and "\E" aliases for "\TEXT" and "\EXPRESSION"
| author | Franz Glasner <fzglas.hg@dom66.de> |
|---|---|
| date | Wed, 20 May 2026 17:23:41 +0200 |
| parents | 397ed930a5ba |
| children | 90946f87d77d |
line wrap: on
line source
# -*- coding: utf-8 -*- # :- # SPDX-FileCopyrightText: © 2026 Franz Glasner # SPDX-License-Identifier: MIT # :- r"""A pseudocode lexer along the lines of CTAN's algpseudocode or algpseudocodex. """ __all__ = ["AlgPseudocodeLexer", "AlgPseudocodeLexer_DE", "AlgPseudocodeLexer_FR"] import logging import re import pygments.util from pygments.lexer import bygroups, include, words from pygments.token import (Comment, Error, Generic, Keyword, Name, Operator, Punctuation, Text, Whitespace) # # Relative imports do not work with pygments.lexers.load_lexer_from_file() # in all of our supported Python releases. # from pygments_lexer_pseudocode2.lexers.bases import LexBase from pygments_lexer_pseudocode2.utils import REVERSED_STANDARD_TYPES from pygments_lexer_pseudocode2 import uniprops # # As in the local imports: use an explicit name because __name__ is # __builtins__ # _logger = logging.getLogger("pygments_lexer_pseudocode2.algpseudocode") class AlgPseudocodeLexer(LexBase): """A pseudocode lexer along the lines of CTAN's algpseudocode or algpseudocodex. Some ideas (e.g. strings) are borrowed from Pygment's Python lexer. """ name = "AlgPseudocode" aliases = ["algpseudocode", "algpseudo"] filenames = ["*.algpseudo", "*.algpseudocode"] mimetypes = [] flags = re.MULTILINE LANG = "en" TRANSLATIONS = { "PROG": "PROGRAM", "PROGRAM": "PROGRAM", "ALGO": "ALGORITHM", "ALGORITHM": "ALGORITHM", "PROC": "PROCEDURE", "PROCEDURE": "PROCEDURE", "FUNC": "FUNCTION", "FUNCTION": "FUNCTION", "FN": "FUNCTION", "CLASS": "CLASS", "INPUT": "Input:", "INPUTS": "Inputs:", "OUTPUT": "Output:", "OUTPUTS": "Outputs:", "RETURNS": "Returns:", "ENSURE": "Ensure:", "REQUIRE": "Require:", "IS": "IS", "WITH": "WITH", "IF": "IF", "THEN": "THEN", "ELSE": "ELSE", "ELSEIF": "ELSE IF", "ELSIF": "ELSE IF", "ELIF": "ELSE IF", "DO": "DO", # in WHILE ... DO "WHILE": "WHILE", "FOR": "FOR", "FORALL": "FOR ALL", "FROM": "FROM", "TO": "TO", "IN": "IN", # as in FOR ... IN "STEP": "STEP", "LOOP": "LOOP", "REPEAT": "REPEAT", "UNTIL": "UNTIL", "RETURN": "RETURN", "BEGIN": "BEGIN", "END": "END", # not in END_TRANSLATIONS } END_TRANSLATIONS = { "PROG": "END OF PROGRAM", "PROGRAM": "END OF PROGRAM", "ALGO": "END OF ALGORITHM", "ALGORITHM": "END OF ALGORITHM", "PROC": "END OF PROCEDURE", "PROCEDURE": "END OF PROCEDURE", "FUNC": "END OF FUNCTION", "FUNCTION": "END OF FUNCTION", "FN": "END OF FUNCTION", "CLASS": "END OF CLASS", "IF": "END IF", "WHILE": "END WHILE", "FOR": "END FOR", "FORALL": "END FOR ALL", "LOOP": "END LOOP", } DEFAULT_END_PREFIX = "END OF " SYMBOL_REMARK = u"▷" # U+25B7: Unicode 1.0 (Geometric Shapes) # SYMBOL_REMARK = u"▻" # U+25BB: Unicode 1.0 (Geometric Shapes) # SYMBOL_REMARK = u"⍝" # U+235D: Unicode 1.1 (Misc. Technical, APL) SYMBOL_BLOCK = u"◆" # U+25C6: Unicode 1.0 (Geometric Shapes) # SYMBOL_BLOCK = u"┃" # U+2503: Unicode 1.0 (Bow Drawing) # SYMBOL_BLOCK = u"●" # U+25CF: Unicode 1.0 (Geometric Shapes) SYMBOL_TEXTSTATEMENT = u"▪" # U+25AA: Unicode 1.0 (Geometric Shapes) # SYMBOL_TEXTSTATEMENT = u"■" # U+25A0: Unicode 1.0 (Geometric Shapes) SYMBOLS = { # Group REMARK "REMARK": SYMBOL_REMARK, "REM": SYMBOL_REMARK, # Group STATEMENT "STATEMENT": SYMBOL_BLOCK, "STATE": SYMBOL_BLOCK, "BLOCK": SYMBOL_BLOCK, # Group TEXTSTATEMENT "TEXTSTATEMENT": SYMBOL_TEXTSTATEMENT, "TEXTSTATE": SYMBOL_TEXTSTATEMENT, "TSTATEMENT": SYMBOL_TEXTSTATEMENT, "TSTATE": SYMBOL_TEXTSTATEMENT, "TEXTBLOCK": SYMBOL_TEXTSTATEMENT, "TBLOCK": SYMBOL_TEXTSTATEMENT, "<-": u"⟵", # U+27F5: Unicode 3.2 (Supplemental Arrows-A) "->": u"⟶", # U+27F6: Unicode 3.2 (Supplemental Arrows-A) "<->": u"⟷", # U+27F7: Unicode 3.2 (Supplemental Arrows-A) # "=>": u"⟹", # U+27F9: Unicode 3.2 (Supplemental Arrows-A) # "<=>": u"⟺", # U+27FA: Unicode 3.2 (Supplemental Arrows-A) # "<-": u"←", # U+2190: Unicode 1.0 (Arrows) # "->": u"→", # U+2192: Unicode 1.0 (Arrows) # "<->": u"↔", # U+2194: Unicode 1.0 (Arrows) "=>": u"⇒", # U+21D2: Unicode 1.0 (Arrows) "<=>": u"⇔", # U+21D4: Unicode 1.0 (Arrows) "<=": u"≤", # U+2264: Unicode 1.0 (Mathematical Operators) ">=": u"≥", # U+2265: Unicode 1.0 (Mathematical Operators) "<>": u"≠", # U+2260: Unicode 1.0 (Mathematical Operators) "!=": u"≠", # U+2260: Unicode 1.0 (Mathematical Operators) ":=": u"∶=", # "≔" U+2254 not recognizable in my (small) mono font "=:": u"=∶", # "≕" U+2255 not recognizable in my (small) mono font "?=": u"≟", # U+225F: Unicode 1.0 (Mathematical Operators) } def op_translate(toktype): def _op_translate(lexer, match, ctx=None): kw = match.group().upper() yield match.start(), toktype, lexer.TRANSLATIONS.get(kw, kw) if ctx: ctx.pos = match.end() return _op_translate def op_opt_end_translate(toktype): def _op_end_translate(lexer, match, ctx=None): if not lexer.no_end: kw = match.group().upper() yield (match.start(), toktype, lexer.END_TRANSLATIONS.get( kw, lexer.DEFAULT_END_PREFIX + kw)) if ctx: ctx.pos = match.end() return _op_end_translate def op_opt_ignore(toktype): def _op_opt_ignore(lexer, match, ctx=None): if not lexer.no_end: yield match.start(), toktype, match.group() if ctx: ctx.pos = match.end() return _op_opt_ignore def op_opt_ignore_or_fixed(toktype, value): """Yield a fixed given token type and value or -- if the lexer's `no_end` setting evals to ``True`` nothing. """ def _op_opt_ignore_or_fixed(lexer, match, ctx=None): if not lexer.no_end: yield match.start(), toktype, value if ctx: ctx.pos = match.end() return _op_opt_ignore_or_fixed def op_gets(lexer, match, ctx=None): yield match.start(), Operator, lexer.symbol_gets def op_remark(lexer, match, ctx=None): yield match.start(), Comment.Single, lexer.symbol_remark def op_symbol(toktype): def _op_symbol(lexer, match, ctx=None): kw = match.group().upper() yield match.start(), toktype, lexer.SYMBOLS.get(kw, kw) if ctx: ctx.pos = match.end() return _op_symbol def op_explicit_tokentype(lexer, match, ctx=None): needed_css = match.group("type") toktype = REVERSED_STANDARD_TYPES.get(needed_css, None) if toktype is None: # Be more error friendly toktype = Generic.Error val = match.group() _logger.warning("Unhandled explicit token type: %s", val) else: val = match.group("character") yield match.start(), toktype, val if ctx: ctx.pos = match.end() tokens = { "root": [ (r"\n", Whitespace), (r"/\*", Comment.Multiline, "multiline-nested-comment"), (r"\(\*", Comment.Multiline, "multiline-nested-comment-alt"), (r"(//|#).*$", Comment.Single), include("remark"), (r"(?i)\\(block|state(?:ment)?)[ \t]*(\{)", bygroups(op_symbol(Text), LexBase.op_fixed(Whitespace, " ")), "block-expr"), (r"(?i)\\(" r"(?:textstate(?:ment)?)" r"|(?:tstate(?:ment)?)" r"|(?:textblock)" r"|(?:tblock)" r")[ \t]*(\{)", bygroups(op_symbol(Text), LexBase.op_fixed(Whitespace, " ")), "text-statement"), (r"(?i)\\(" r"(?:input(?:s)?)" r"|(?:output(?:s)?)" r"|(?:ensure)" r"|(?:require)" r"|(?:returns)" r")[ \t]*(\{)", bygroups(op_translate(Keyword), LexBase.op_fixed(Whitespace, " ")), "text-statement"), (r"(?i)\\(" r"(?:if)" r"|(?:then)" r"|(?:else)" r"|(?:el(?:s(?:e)?)?if)" r"|(?:do)" # as in WHILE ... DO not DO ... UNTIL r"|(?:while)" r"|(?:forall)" r"|(?:for)" r"|(?:from)" r"|(?:to)" r"|(?:step)" r"|(?:in)" r"|(?:loop)" r"|(?:repeat)" r"|(?:until)" r"|(?:return)" r")\b", bygroups(op_translate(Keyword))), (r"\\\n", Text), (r"(?i)\\(" r"(?:prog(?:ram)?)" r"|(?:algo(?:rithm)?)" r"|(?:proc(?:edure)?)" r"|(?:func(?:tion)?|(?:fn))" r"|(?:class)" r")[ \t]*(\{)", bygroups(op_translate(Keyword), LexBase.op_fixed(Whitespace, " ")), "entity-name"), # ENDxxx keywords with optional entity name in two parts: # 1. with name (r"(?i)\\end(?:[_\-]|(?:[ \t]+))?(" r"(?:prog(?:ram)?)" r"|(?:algo(?:rithm)?)" r"|(?:proc(?:edure)?)" r"|(?:func(?:tion)?)" r"|(?:fn)" r"|(?:class)" r")(?:[_\-]|(?:[\t ]+))?(\{)", bygroups(op_opt_end_translate(Keyword), op_opt_ignore_or_fixed(Whitespace, " ")), "entity-name-end"), # 2. without name # 3. AND keywords that do not allow a param (e.g. endif) (r"(?i)\\end(?:[_\-]|(?:[ \t]+))?(" r"(?:prog(?:ram)?)" r"|(?:algo(?:rithm)?)" r"|(?:proc(?:edure)?)" r"|(?:func(?:tion)?)" r"|(?:fn)" r"|(?:class)" r"|(?:if)" r"|(?:while)" r"|(?:for)" r"|(?:forall)" r"|(?:loop)" r")\b", bygroups(op_opt_end_translate(Keyword))), # # A single begin or end that is never suppressed because # it is supposed to be paired with begin # (r"(?i)\\(begin|end)\b", bygroups(op_translate(Keyword))), # Keywords (r"(?i)\\(" r"(?:is)" r"|(?:with)" r")\b", bygroups(op_translate(Keyword))), include("expr"), include("unicode-separators"), include("unicode-other"), (r"[^\S\n]+", Text), # (r".", Generic.Error), # tolerance for errors (r".", Error), ], "remark": [ (r"(?i)\\(remark|rem)\b(.*)$", bygroups(op_remark, Comment.Single)), ], "entity-name": [ # may be multiline (r"[^\\}]+", Name.Entity), (r"\}", LexBase.op_ignore, "#pop"), (r"\\\}", LexBase.op_fixed(Name.Entity, "}")), (r"\\\\", LexBase.op_fixed(Name.Entity, "\\")), (r"\\", LexBase.op_fixed(Generic.Error, "\\")), ], "entity-name-end": [ # may be multiline -- suppressed if no_end (r"[^\\}]+", op_opt_ignore(Name.Entity)), (r"\}", LexBase.op_ignore, "#pop"), (r"\\\}", op_opt_ignore_or_fixed(Name.Entity, "}")), (r"\\\\", op_opt_ignore_or_fixed(Name.Entity, "\\")), (r"\\", op_opt_ignore_or_fixed(Generic.Error, "\\")), ], "expr": [ include("math-symbols"), # must be before punctuation include("ascii-punctuation"), include("unicode-punctuation"), include("escaped-string-start"), include("py-strings"), include("py-numbers"), (r"(?i)\\(call|name)[ \t]*(\{)", LexBase.op_ignore, "entity-name"), (r"(?i)\\gets\b", op_gets), (r"(?i)\\e(?:xpr(?:ession)?)?[ \t]*\{", LexBase.op_ignore, "block-expr"), include("explicit-tokentype"), (r"(?i)\\t(?:ext)?[ \t]*\{", LexBase.op_ignore, "text-statement"), include("remark"), include("keyword-constants"), include("word-operators"), include("math-builtins"), include("py-name"), ], "expr-in-braces": [ include("math-symbols"), # must be before punctuation include("ascii-punctuation-in-braces"), include("unicode-punctuation"), include("escaped-string-start"), include("py-strings"), include("py-numbers"), (r"(?i)\\(call|name)[ \t]*(\{)", LexBase.op_ignore, "entity-name"), (r"(?i)\\gets\b", op_gets), (r"(?i)\\e(?:xpr(?:ession)?)?[ \t]*\{", LexBase.op_ignore, "block-expr"), include("explicit-tokentype"), (r"(?i)\\t(?:ext)?[ \t]*\{", LexBase.op_ignore, "text-statement"), include("remark"), include("keyword-constants"), include("word-operators"), include("math-builtins"), include("py-name"), ], "block-expr": [ # somewhat similar to "root" (r"\}", LexBase.op_ignore, "#pop"), (r"\n", Whitespace), include("expr-in-braces"), (r"\\\\", LexBase.op_fixed(Text, "\\")), (r"\\", LexBase.op_fixed(Generic.Error, "\\")), include("unicode-separators"), include("unicode-other"), (r"[^\S\n]+", Text), (r".", Error), ], "text-statement": [ # like block but default to text-mode (r"[^\\}\n]+", Text), (r"\}", LexBase.op_ignore, "#pop"), (r"\n", Whitespace), (r"\\\}", LexBase.op_fixed(Text, "}")), (r"(?i)\\e(?:xpr(?:ession)?)?[ \t]*\{", LexBase.op_ignore, "block-expr"), include("explicit-tokentype"), (r"(?i)\\t(?:ext)?[ \t]*\{", LexBase.op_ignore, "#push"), include("remark"), (r"\\\\", LexBase.op_fixed(Text, "\\")), (r"\\", LexBase.op_fixed(Text, "\\")), # in text-mode: leave Text (r".", Error), ], "math-builtins": [ (words(("sqrt", "pow", "cos", "sin", "tan", "arcos", "arcsin", "arctan", "arctan2", "mod", "exp", "ln", "log", "min", "max"), prefix=r"(?<!\.)", suffix=r"\b"), Name.Builtin), ], "math-symbols": [ (r"<=>|<->|<-|->|=>|<=|>=|<>|!=|:=|=:|\?=", op_symbol(Operator)), (r"[!&<>=+\-*/%|~]", Operator), # ASCII (u"[%s]" % (uniprops.Sm,), Operator), # other Unicode ], "word-operators": [ (words(("IN", "In", "in", "IS", "Is", "is", "AND", "And", "and", "OR", "Or", "or", "XOR", "Xor", "xor", "NOT", "Not", "not"), prefix=r"(?<!\.)", suffix=r"\b"), Operator.Word), ], "keyword-constants": [ (words(("True", "TRUE", "true", "False", "FALSE", "false", "None", "NONE", "none", "Nil", "NIL", "nil", "Null", "NULL", "null", "Empty", "EMPTY", "empty"), prefix=r"(?<!\.)", suffix=r"\b"), Keyword.Constant), ], "ascii-punctuation": [ (r"\.+", Punctuation), (r"[{}:(),;[\]?@]", Punctuation), ], "ascii-punctuation-in-braces": [ # # Like "punctuation" but needs an escaped curly brace for } because # a single closing curly brace pops the current state here. # (r"\\\}", LexBase.op_fixed(Punctuation, "}")), (r"[{:(),;[\]?@]", Punctuation), ], "unicode-separators": [ (u"[%s]" % (uniprops.Zl,), Whitespace), (u"[%s]" % (uniprops.Zp,), Whitespace), (u"[%s]" % (uniprops.Zs,), Whitespace), ], "unicode-punctuation": [ (u"[%s]" % (uniprops.Pc,), Punctuation), (u"[%s]" % (uniprops.Pd,), Punctuation), (u"[%s]" % (uniprops.Ps,), Punctuation), (u"[%s]" % (uniprops.Pe,), Punctuation), (u"[%s]" % (uniprops.Pi,), Punctuation), (u"[%s]" % (uniprops.Pf,), Punctuation), (u"[%s]" % (uniprops.Po,), Punctuation), ], "unicode-other": [ (u"[%s]" % (uniprops.Sc,), Text), # Currency (u"[%s]" % (uniprops.So,), Text), # Other symbols ], "escaped-string-start": [ (r"""\\(['"])""", bygroups(Punctuation)), ], "explicit-tokentype": [ # All these REs are CASE-SENSITIVE! # Multiple characters possible, but no escaping! (r"""\\ttx\-(?P<type>[a-zA-Z0-9_-]+?)""" r"""(?P<sep>[/?.,:;%|=*+!\\$~"'#@_-])""" r"""(?P<character>(.|\n)+?)(?P=sep)""", op_explicit_tokentype), (r"\\ttx\-(?P<type>[a-zA-Z0-9_-]+?)\{(?P<character>[^}]+?)\}", op_explicit_tokentype), (r"\\ttx\-(?P<type>[a-zA-Z0-9_-]+?)\((?P<character>[^)]+?)\)", op_explicit_tokentype), (r"\\ttx\-(?P<type>[a-zA-Z0-9_-]+?)<(?P<character>[^>]+?)>", op_explicit_tokentype), (r"\\ttx\-(?P<type>[a-zA-Z0-9_-]+?)\[(?P<character>[^\]]+?)\]", op_explicit_tokentype), # Every character is possible: no escaping needed! (r"\\tt-(?P<type>[^/]+?)/(?P<character>(?:.|\n))", op_explicit_tokentype), ], } def __init__(self, **options): self.no_end = pygments.util.get_bool_opt( options, "no_end", default=False) self.symbol_gets = options.get("gets", None) if self.symbol_gets is None: self.symbol_gets = self.SYMBOLS["<-"] # Default: "⟵" # U+27F5 self.symbol_remark = options.get("remark", None) if self.symbol_remark is None: self.symbol_remark = self.SYMBOLS["REM"] # Default: "▷" # U+25B7 LexBase.__init__(self, **options) # # Do this after calling the base because a "filters" option should be # allowed to set **any** filter. # self.prohibit_raiseonerror_filter = pygments.util.get_bool_opt( options, "prohibit_raiseonerror_filter", default=False) def add_filter(self, filter_, **options): # # May be called by Lexer.__init__ when # self.prohibit_raiseonerror_filter is not yet set. # This is intentionally so. # if getattr(self, "prohibit_raiseonerror_filter", False): if filter_ in ("raiseonerror",): return LexBase.add_filter(self, filter_, **options) class AlgPseudocodeLexer_DE(AlgPseudocodeLexer): """ .. seealso:: - https://de.wikipedia.org/wiki/Pseudocode .. todo:: Complete german translations """ name = "AlgPseudocodeDE" aliases = ["algpseudocode-de", "algpseudo-de"] filenames = ["*.algpseudo-de", "*.algpseudocode-de"] LANG = "de" TRANSLATIONS = AlgPseudocodeLexer.TRANSLATIONS.copy() TRANSLATIONS.update({ "PROG": "PROGRAMM", "PROGRAM": "PROGRAMM", "ALGO": "ALGORITHMUS", "ALGORITHM": "ALGORITHMUS", "PROC": "PROZEDUR", "PROCEDURE": "PROZEDUR", "FUNC": "FUNKTION", "FUNCTION": "FUNKTION", "FN": "FUNKTION", "CLASS": "KLASSE", "IS": "IST", "WITH": "MIT", "IF": "WENN", "THEN": "DANN", "ELSE": "ANDERNFALLS", "ELSEIF": "ANDERNFALLS WENN", "ELSIF": "ANDERNFALLS WENN", "ELIF": "ANDERNFALLS WENN", # "DO": # XXX TBD # in WHILE WHILE ... DO # noqa # in WHILE ... DO "WHILE": "SOLANGE", "FOR": "FÜR", "FORALL": "FÜR ALLE", "FROM": "VON", "TO": "BIS", "IN": "IN", "STEP": "SCHRITTWEITE", # "LOOP": XXX TBD # noqa "REPEAT": "WIEDERHOLE", "UNTIL": "BIS", # "RETURN": XXX TBD # noqa "BEGIN": "START", "END": "ENDE", }) END_TRANSLATIONS = AlgPseudocodeLexer.END_TRANSLATIONS.copy() END_TRANSLATIONS.update({ "PROG": "ENDE DES PROGRAMMS", "PROGRAM": "ENDE DES PROGRAMMS", "ALGO": "ENDE DES ALGORITHMUS", "ALGORITHM": "ENDE DES ALGORITHMUS", "PROC": "ENDE DER PROZEDUR", "PROCEDURE": "ENDE DER PROZEDUR", "FUNC": "ENDE DER FUNKTION", "FUNCTION": "ENDE DER FUNKTION", "FN": "ENDE DER FUNKTION", "CLASS": "ENDE DER KLASSE", "IF": "ENDE WENN", "WHILE": "ENDE SOLANGE", "FOR": "ENDE FÜR", "FORALL": "ENDE FÜR ALLE", # "LOOP": "ENDE XXX", # XXX TBD # noqa }) DEFAULT_END_PREFIX = "ENDE VON " class AlgPseudocodeLexer_FR(AlgPseudocodeLexer): """ .. seealso:: - https://info.blaisepascal.fr/pseudo-code/ - https://fr.wikipedia.org/wiki/Pseudo-code - https://fr.wikipedia.org/wiki/Structure_de_contr%C3%B4le .. todo:: Complete french translations """ name = "AlgPseudocodeFR" aliases = ["algpseudocode-fr", "algpseudo-fr"] filenames = ["*.algpseudo-fr", "*.algpseudocode-fr"] LANG = "fr" TRANSLATIONS = AlgPseudocodeLexer.TRANSLATIONS.copy() TRANSLATIONS.update({ "PROG": "PROGRAMME", "PROGRAM": "PROGRAMME", "ALGO": "ALGORITHME", "ALGORITHM": "ALGORITHME", "PROC": "PROCÉDURE", "PROCEDURE": "PROCÉDURE", "FUNC": "FONCTION", "FUNCTION": "FOUNCTION", "FN": "FONCTION", "CLASS": "CLASSE", "IS": "EST", "WITH": "AVEC", "IF": "SI", "THEN": "ALORS", "ELSE": "SINON", "ELSEIF": "SINONSI", "ELSIF": "SINONSI", "ELIF": "SINONSI", "DO": "FAIRE", # as in in WHILE ... DO (not DO ... UNTIL) "WHILE": "TANTQUE", "FOR": "POUR", "FORALL": "POUR CHAQUE", "FROM": "DE", "TO": "JUSQU'À", # or just "À", "IN": "DANS", # as in FOR ... IN "STEP": "PAR PAS DE", "LOOP": "BOUCLE", # XXX FIXME??? "REPEAT": "RÉPÉTER", "UNTIL": "JUSQUACEQUE", "RETURN": "RENVOYER", "BEGIN": "DÉBUT", "END": "FIN", }) END_TRANSLATIONS = AlgPseudocodeLexer.END_TRANSLATIONS.copy() END_TRANSLATIONS.update({ "PROG": "FIN DE PROGRAMME", "PROGRAM": "FIN DE PROGRAMME", "ALGO": "FIN D'ALGORITHME", "ALGORITHM": "FIN D'ALGORITHME", "PROC": "FIN DE PROCÉDURE", "PROCEDURE": "FIN DE PROCÉDURE", "FUNC": "FIN DE FONCTION", "FUNCTION": "FIN DE FOUNCTION", "FN": "FIN DE FONCTION", "CLASS": "FIN DE CLASSE", "SI": "FIN SI", "FOR": "FIN POUR", "FORALL": "FIN POUR CHAQUE", "WHILE": "FIN TANTQUE", "LOOP": "FIN BOUCLE", }) DEFAULT_END_PREFIX = "FIN DE "
