logger+parser: add a Tracer class featuring function-wrappers and reflection
Generates clean function call stack traces as they are called
This commit is contained in:
parent
3e9d308e40
commit
017aefa750
2 changed files with 66 additions and 2 deletions
|
|
@ -1,8 +1,11 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import enum
|
||||
import inspect
|
||||
import logging
|
||||
import os
|
||||
import typing
|
||||
from typing import Callable
|
||||
|
||||
_TRACE = (logging.DEBUG + logging.NOTSET) // 2
|
||||
logging.addLevelName(_TRACE, "TRACE")
|
||||
|
|
@ -78,4 +81,53 @@ class Logger:
|
|||
self.log(LogLevel.Trace, *args)
|
||||
|
||||
|
||||
T = typing.TypeVar("T")
|
||||
|
||||
|
||||
class Tracer:
|
||||
def __init__(self, logger: Logger, level: LogLevel = LogLevel.Trace, max_depth: int = 15):
|
||||
self.depth: int = 0
|
||||
self.level = level
|
||||
self.logger = logger
|
||||
self.max_depth: int = max_depth
|
||||
|
||||
def trace_method(self, func_or_nothing: Callable[..., T] | None = None, **kwargs):
|
||||
if func_or_nothing is not None:
|
||||
return self._trace_method(func=func_or_nothing, **kwargs)
|
||||
|
||||
return lambda func: self._trace_method(func, **kwargs)
|
||||
|
||||
def _trace_method(self, func: Callable[..., T], level: LogLevel = None) -> Callable[..., T]:
|
||||
if level is None:
|
||||
level = self.level
|
||||
|
||||
if self.logger.level > level:
|
||||
return func
|
||||
|
||||
func_info = inspect.signature(func)
|
||||
|
||||
def _wrapped(*args, **kwargs) -> T:
|
||||
bound_args = func_info.bind(*args, **kwargs)
|
||||
signature = ", ".join(
|
||||
key if key == "self" else f"{key}={repr(value)}"
|
||||
for key, value in bound_args.arguments.items()
|
||||
)
|
||||
prefix = f"[{self.depth: >4}]" + " " * self.depth
|
||||
|
||||
if self.depth == self.max_depth:
|
||||
self.logger.log(level, f"{prefix} > ... (max depth of {self.max_depth} reached)")
|
||||
if self.depth < self.max_depth:
|
||||
self.logger.log(level, f"{prefix} > Entering {func.__name__}({signature})")
|
||||
|
||||
self.depth += 1
|
||||
r: T = func(*args, **kwargs)
|
||||
self.depth -= 1
|
||||
|
||||
if self.depth < self.max_depth:
|
||||
self.logger.log(level, f"{prefix} < Leaving {func.__name__}({signature})")
|
||||
return r
|
||||
|
||||
return _wrapped
|
||||
|
||||
|
||||
logger = Logger("compiler")
|
||||
|
|
|
|||
|
|
@ -3,13 +3,13 @@ from __future__ import annotations
|
|||
from beartype.typing import List, Dict, Callable
|
||||
|
||||
from .errors import CompilationError, UnexpectedTokenError
|
||||
from .logger import Logger
|
||||
from .logger import Logger, Tracer, LogLevel
|
||||
from .nodes import Float, Sum, Value, Product, Node, Division, Sub, Integer, Expression, Identifier, Assignment, \
|
||||
Variable
|
||||
from .tokenizer import Tokens, Token
|
||||
|
||||
logger = Logger(__name__)
|
||||
|
||||
tracer = Tracer(logger, level=LogLevel.Debug)
|
||||
|
||||
class Parser:
|
||||
def __init__(self, tokens: List[Token]):
|
||||
|
|
@ -31,6 +31,7 @@ class Parser:
|
|||
self.pos += 1
|
||||
logger.debug(f"Advancing to token {self.pos} {self.token}")
|
||||
|
||||
@tracer.trace_method(level=LogLevel.Trace)
|
||||
def accept(self, *token_types: Tokens) -> False | Token:
|
||||
tok = self.token
|
||||
if self.token.kind in token_types:
|
||||
|
|
@ -45,6 +46,7 @@ class Parser:
|
|||
return tok
|
||||
return False
|
||||
|
||||
@tracer.trace_method(level=LogLevel.Trace)
|
||||
def expect(self, token_type: Tokens) -> Token:
|
||||
r = self.accept(token_type)
|
||||
logger.debug(f"Expecting {token_type}, got {r}")
|
||||
|
|
@ -52,6 +54,7 @@ class Parser:
|
|||
raise UnexpectedTokenError(self.token, token_type)
|
||||
return r
|
||||
|
||||
@tracer.trace_method
|
||||
def number(self, mandatory: bool = False):
|
||||
if tok := self.accept(Tokens.Float):
|
||||
logger.debug(f"Found float {tok}")
|
||||
|
|
@ -62,18 +65,21 @@ class Parser:
|
|||
elif mandatory:
|
||||
raise UnexpectedTokenError(self.token, "integer or float")
|
||||
|
||||
@tracer.trace_method
|
||||
def identifier(self, mandatory: bool = False) -> Identifier:
|
||||
if ident := self.accept(Tokens.Identifier):
|
||||
return Identifier(location=ident.loc, name=str(ident.value))
|
||||
elif mandatory:
|
||||
raise UnexpectedTokenError(self.token, "identifier")
|
||||
|
||||
@tracer.trace_method
|
||||
def variable(self, mandatory: bool = False) -> Variable:
|
||||
if ident := self.identifier(mandatory=False):
|
||||
return Variable(location=ident.location(), identifier=ident)
|
||||
elif mandatory:
|
||||
raise UnexpectedTokenError(self.token, "variable identifier")
|
||||
|
||||
@tracer.trace_method
|
||||
def binary_op(self, operand_func: Callable[[], Value], operators: Dict[Tokens, Value]):
|
||||
operand = operand_func()
|
||||
|
||||
|
|
@ -85,6 +91,7 @@ class Parser:
|
|||
|
||||
return operand
|
||||
|
||||
@tracer.trace_method
|
||||
def factor(self) -> Value:
|
||||
if self.accept(Tokens.Parens_Left):
|
||||
v = self.expression()
|
||||
|
|
@ -97,18 +104,21 @@ class Parser:
|
|||
else:
|
||||
raise UnexpectedTokenError(self.token, "parenthesized expression, number or variable")
|
||||
|
||||
@tracer.trace_method
|
||||
def term(self) -> Value:
|
||||
return self.binary_op(self.factor, operators={
|
||||
Tokens.Op_Multiply: Product,
|
||||
Tokens.Op_Divide: Division,
|
||||
})
|
||||
|
||||
@tracer.trace_method
|
||||
def summation(self) -> Sum:
|
||||
return self.binary_op(self.term, operators={
|
||||
Tokens.Op_Plus: Sum,
|
||||
Tokens.Op_Minus: Sub,
|
||||
})
|
||||
|
||||
@tracer.trace_method
|
||||
def assignment(self, mandatory: bool = False) -> Assignment:
|
||||
if ident := self.identifier(mandatory):
|
||||
self.expect(Tokens.Equal)
|
||||
|
|
@ -117,12 +127,14 @@ class Parser:
|
|||
elif mandatory:
|
||||
raise UnexpectedTokenError(self.token, "assignment")
|
||||
|
||||
@tracer.trace_method
|
||||
def expression(self) -> Value:
|
||||
if self.peek(Tokens.Identifier):
|
||||
return Expression(self.assignment())
|
||||
else:
|
||||
return Expression(self.summation())
|
||||
|
||||
@tracer.trace_method
|
||||
def root(self) -> Node:
|
||||
return self.expression()
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue