MIRA_LANG — moteur (lexer, parser, interpreter, main)

Bienvenue sur CFTPfr.fr Forums modules MIRA_LANG — moteur (lexer, parser, interpreter, main)

  • Ce sujet est vide.
Affichage de 1 message (sur 1 au total)
  • Auteur
    Messages
  • #218
    Mario Da Conceicao
    Maître des clés

    Sauvegarde de travail — version du 25/07/2026. Le moteur du langage MIRA_LANG : lexer → parser → interpréteur.

    lexer.py

    import re
    
    TOKEN_SPEC = [
        ("NUMBER", r"\d+(\.\d+)?"),
        ("STRING", r'"[^"]*"'),
        ("LET", r"\blet\b"),
        ("PRINT", r"\bprint\b"),
        ("IF", r"\bif\b"),
        ("THEN", r"\bthen\b"),
        ("ELSE", r"\belse\b"),
        ("WHILE", r"\bwhile\b"),
        ("DO", r"\bdo\b"),
        ("FUNC", r"\bfunc\b"),
        ("RETURN", r"\breturn\b"),
        ("IMPORT", r"\bimport\b"),
        ("API", r"\bapi\b"),
        ("IDENT", r"[A-Za-z_][A-Za-z0-9_]*"),
        ("OP", r"==|!=|<=|>=|[+\-*/=<>(){},]"),
        ("NEWLINE", r"\n"),
        ("SKIP", r"[ \t]+"),
        ("MISMATCH", r"."),
    ]
    
    TOKEN_RE = re.compile("|".join(f"(?P<{name}>{pattern})" for name, pattern in TOKEN_SPEC))
    
    class Token:
        def __init__(self, type_, value):
            self.type = type_
            self.value = value
    
        def __repr__(self):
            return f"Token({self.type}, {repr(self.value)})"
    
    def tokenize(text):
        tokens = []
        for match in TOKEN_RE.finditer(text):
            kind = match.lastgroup
            value = match.group()
            if kind == "NUMBER":
                value = float(value) if "." in value else int(value)
                tokens.append(Token("NUMBER", value))
            elif kind == "STRING":
                tokens.append(Token("STRING", value[1:-1]))
            elif kind in ("LET", "PRINT", "IF", "THEN", "ELSE", "WHILE", "DO", "FUNC", "RETURN", "IMPORT", "API", "IDENT", "OP"):
                tokens.append(Token(kind, value))
            elif kind == "NEWLINE":
                tokens.append(Token("NEWLINE", value))
            elif kind == "SKIP":
                continue
            elif kind == "MISMATCH":
                raise SyntaxError(f"Caractère invalide: {value}")
        tokens.append(Token("EOF", None))
        return tokens

    ast_nodes.py

    class Node:
        pass
    
    class Program(Node):
        def __init__(self, statements):
            self.statements = statements
    
    class PrintNode(Node):
        def __init__(self, value):
            self.value = value
    
    class LetNode(Node):
        def __init__(self, name, value):
            self.name = name
            self.value = value
    
    class NumberNode(Node):
        def __init__(self, value):
            self.value = value
    
    class StringNode(Node):
        def __init__(self, value):
            self.value = value
    
    class VarNode(Node):
        def __init__(self, name):
            self.name = name
    
    class BinOpNode(Node):
        def __init__(self, left, op, right):
            self.left = left
            self.op = op
            self.right = right
    
    class IfNode(Node):
        def __init__(self, condition, body, else_body=None):
            self.condition = condition
            self.body = body
            self.else_body = else_body or []
    
    class WhileNode(Node):
        def __init__(self, condition, body):
            self.condition = condition
            self.body = body
    
    class FuncDefNode(Node):
        def __init__(self, name, params, body):
            self.name = name
            self.params = params
            self.body = body
    
    class FuncCallNode(Node):
        def __init__(self, name, args):
            self.name = name
            self.args = args
    
    class ReturnNode(Node):
        def __init__(self, value):
            self.value = value
    
    class ImportNode(Node):
        def __init__(self, module_name):
            self.module_name = module_name
    
    class ApiCallNode(Node):
        def __init__(self, endpoint, args):
            self.endpoint = endpoint
            self.args = args

    parser.py

    from ast_nodes import *
    
    class Parser:
        def __init__(self, tokens):
            self.tokens = tokens
            self.pos = 0
    
        def current(self):
            return self.tokens[self.pos]
    
        def eat(self, type_=None, value=None):
            tok = self.current()
            if type_ and tok.type != type_:
                raise SyntaxError(f"Attendu {type_}, reçu {tok.type} à la position {self.pos}")
            if value and tok.value != value:
                raise SyntaxError(f"Attendu {value}, reçu {tok.value} à la position {self.pos}")
            self.pos += 1
            return tok
    
        def parse(self):
            statements = []
            while self.current().type != "EOF":
                if self.current().type == "NEWLINE":
                    self.eat("NEWLINE")
                    continue
                statements.append(self.statement())
                if self.current().type == "NEWLINE":
                    self.eat("NEWLINE")
            return Program(statements)
    
        def statement(self):
            tok = self.current()
    
            if tok.type == "PRINT":
                self.eat("PRINT")
                value = self.expression()
                return PrintNode(value)
    
            if tok.type == "LET":
                self.eat("LET")
                name = self.eat("IDENT").value
                self.eat("OP", "=")
                value = self.expression()
                return LetNode(name, value)
    
            if tok.type == "IF":
                self.eat("IF")
                condition = self.expression()
                self.eat("THEN")
                body = [self.statement()]
                if self.current().type == "ELSE":
                    self.eat("ELSE")
                    else_body = [self.statement()]
                    return IfNode(condition, body, else_body)
                return IfNode(condition, body)
    
            if tok.type == "WHILE":
                self.eat("WHILE")
                condition = self.expression()
                self.eat("DO")
                body = [self.statement()]
                return WhileNode(condition, body)
    
            if tok.type == "FUNC":
                self.eat("FUNC")
                name = self.eat("IDENT").value
                self.eat("OP", "(")
                params = []
                if not (self.current().type == "OP" and self.current().value == ")"):
                    params.append(self.eat("IDENT").value)
                    while self.current().type == "OP" and self.current().value == ",":
                        self.eat("OP", ",")
                        params.append(self.eat("IDENT").value)
                self.eat("OP", ")")
                self.eat("OP", "{")
                body = []
                while not (self.current().type == "OP" and self.current().value == "}"):
                    body.append(self.statement())
                self.eat("OP", "}")
                return FuncDefNode(name, params, body)
    
            if tok.type == "RETURN":
                self.eat("RETURN")
                value = self.expression()
                return ReturnNode(value)
    
            if tok.type == "IMPORT":
                self.eat("IMPORT")
                module_name = self.eat("IDENT").value
                return ImportNode(module_name)
    
            if tok.type == "API":
                self.eat("API")
                endpoint = self.eat("IDENT").value
                self.eat("OP", "(")
                args = []
                if not (self.current().type == "OP" and self.current().value == ")"):
                    args.append(self.expression())
                    while self.current().type == "OP" and self.current().value == ",":
                        self.eat("OP", ",")
                        args.append(self.expression())
                self.eat("OP", ")")
                return ApiCallNode(endpoint, args)
    
            if tok.type == "IDENT":
                name = self.eat("IDENT").value
                if self.current().type == "OP" and self.current().value == "(":
                    self.eat("OP", "(")
                    args = []
                    if not (self.current().type == "OP" and self.current().value == ")"):
                        args.append(self.expression())
                        while self.current().type == "OP" and self.current().value == ",":
                            self.eat("OP", ",")
                            args.append(self.expression())
                    self.eat("OP", ")")
                    return FuncCallNode(name, args)
    
            raise SyntaxError(f"Instruction inconnue près de {tok}")
    
        def expression(self):
            return self.comparison()
    
        def comparison(self):
            node = self.term()
            while self.current().type == "OP" and self.current().value in ["==", "!=", "<", ">", "<=", ">="]:
                op = self.eat("OP").value
                right = self.term()
                node = BinOpNode(node, op, right)
            return node
    
        def term(self):
            node = self.factor()
            while self.current().type == "OP" and self.current().value in ["*", "/"]:
                op = self.eat("OP").value
                right = self.factor()
                node = BinOpNode(node, op, right)
            return node
    
        def factor(self):
            tok = self.current()
    
            if tok.type == "NUMBER":
                return NumberNode(self.eat("NUMBER").value)
    
            if tok.type == "STRING":
                return StringNode(self.eat("STRING").value)
    
            if tok.type == "IDENT":
                return VarNode(self.eat("IDENT").value)
    
            if tok.type == "OP" and tok.value == "(":
                self.eat("OP", "(")
                node = self.expression()
                self.eat("OP", ")")
                return node
    
            if tok.type == "OP" and tok.value == "-":
                self.eat("OP", "-")
                return BinOpNode(NumberNode(0), "-", self.factor())
    
            raise SyntaxError(f"Expression invalide près de {tok}")

    interpreter.py

    import operator as op
    import requests
    from ast_nodes import *
    from pathlib import Path
    import importlib.util
    
    BASE_DIR = Path(__file__).parent
    
    class Interpreter:
        def __init__(self):
            self.variables = {}
            self.functions = {
                "len": len,
                "str": str,
                "int": int,
                "float": float,
                "print": print,
                "__read_file__": self._read_file,
                "__write_file__": self._write_file
            }
            self.user_functions = {}
            self.api_url = "http://127.0.0.1:5000"
    
        def _read_file(self, path):
            with open(path, "r", encoding="utf-8") as f:
                return f.read()
    
        def _write_file(self, path, content):
            with open(path, "w", encoding="utf-8") as f:
                f.write(content)
            return True
    
        def visit(self, node):
            method = getattr(self, f"visit_{type(node).__name__}", None)
            if not method:
                raise Exception(f"Pas de visiteur pour {type(node).__name__}")
            return method(node)
    
        def visit_Program(self, node):
            result = None
            for stmt in node.statements:
                result = self.visit(stmt)
            return result
    
        def visit_PrintNode(self, node):
            value = self.visit(node.value)
            print(value)
            return value
    
        def visit_LetNode(self, node):
            value = self.visit(node.value)
            self.variables[node.name] = value
            return value
    
        def visit_NumberNode(self, node):
            return node.value
    
        def visit_StringNode(self, node):
            return node.value
    
        def visit_VarNode(self, node):
            if node.name not in self.variables:
                raise NameError(f"Variable inconnue: {node.name}")
            return self.variables[node.name]
    
        def visit_BinOpNode(self, node):
            left = self.visit(node.left)
            right = self.visit(node.right)
    
            ops = {
                "+": op.add,
                "-": op.sub,
                "*": op.mul,
                "/": op.truediv,
                "==": op.eq,
                "!=": op.ne,
                "<": op.lt,
                ">": op.gt,
                "<=": op.le,
                ">=": op.ge,
            }
    
            if node.op not in ops:
                raise SyntaxError(f"Opérateur non supporté: {node.op}")
    
            return ops[node.op](left, right)
    
        def visit_IfNode(self, node):
            if self.visit(node.condition):
                for stmt in node.body:
                    self.visit(stmt)
            else:
                for stmt in node.else_body:
                    self.visit(stmt)
    
        def visit_WhileNode(self, node):
            count = 0
            while self.visit(node.condition):
                for stmt in node.body:
                    self.visit(stmt)
                count += 1
                if count > 1000:
                    raise RuntimeError("Boucle trop longue")
    
        def visit_FuncDefNode(self, node):
            self.user_functions[node.name] = (node.params, node.body)
            return None
    
        def visit_ReturnNode(self, node):
            return self.visit(node.value)
    
        def visit_FuncCallNode(self, node):
            if node.name in self.functions:
                args = [self.visit(arg) for arg in node.args]
                return self.functions[node.name](*args)
            elif node.name in self.user_functions:
                params, body = self.user_functions[node.name]
                if len(node.args) != len(params):
                    raise RuntimeError(f"Nombre d'arguments incorrect pour {node.name}")
                old_vars = self.variables.copy()
                self.variables.update(zip(params, [self.visit(arg) for arg in node.args]))
                result = None
                for stmt in body:
                    result = self.visit(stmt)
                    if isinstance(stmt, ReturnNode):
                        break
                self.variables = old_vars
                return result
            else:
                raise NameError(f"Fonction inconnue: {node.name}")
    
        def visit_ImportNode(self, node):
            module_path = BASE_DIR / "stdlib" / f"{node.module_name}.mira"
            if not module_path.exists():
                raise ImportError(f"Module {node.module_name} introuvable")
            with open(module_path, "r", encoding="utf-8") as f:
                module_code = f.read()
            old_vars = self.variables.copy()
            old_funcs = self.functions.copy()
            old_user_funcs = self.user_functions.copy()
            from main import run_code
            run_code(module_code)
            self.variables = old_vars
            self.functions = old_funcs
            self.user_functions = old_user_funcs
            return None
    
        def visit_ApiCallNode(self, node):
            endpoint = node.endpoint
            args = [self.visit(arg) for arg in node.args]
            try:
                if endpoint == "execute":
                    response = requests.post(
                        f"{self.api_url}/execute",
                        json={"code": args[0]}
                    )
                elif endpoint == "plugins":
                    response = requests.get(f"{self.api_url}/plugins")
                else:
                    response = requests.post(
                        f"{self.api_url}/plugins/{endpoint}",
                        json={"args": args}
                    )
                if response.status_code != 200:
                    return f"Erreur API: {response.json().get('error', 'Inconnu')}"
                return response.json().get("output") or response.json().get("result")
            except Exception as e:
                return f"Erreur API: {e}"

    main.py

    from lexer import tokenize
    from parser import Parser
    from interpreter import Interpreter
    import sys
    
    def run_code(code):
        try:
            tokens = tokenize(code)
            parser = Parser(tokens)
            ast = parser.parse()
            interpreter = Interpreter()
            return interpreter.visit(ast)
        except Exception as e:
            return f"Erreur: {e}"
    
    def main():
        print("MIRA_LANG v2.0 - Tapez votre code (ligne vide pour exécuter)")
        lines = []
        while True:
            try:
                line = input("> ")
            except EOFError:
                break
            if line.strip() == "":
                break
            lines.append(line)
        code = "\n".join(lines)
        try:
            result = run_code(code)
            if result is not None:
                print(result)
        except Exception as e:
            print(f"Erreur: {e}")
    
    if __name__ == "__main__":
        main()
Affichage de 1 message (sur 1 au total)
  • Vous devez être connecté pour répondre à ce sujet.
Retour en haut