From 9b4edcdd0184e7eafc543f6bb0d599e9afed2568 Mon Sep 17 00:00:00 2001 From: LordBaryhobal Date: Wed, 22 Jul 2026 12:00:36 +0200 Subject: [PATCH] fix(report): improve fn-link --- report/chapters/03_theory.typ | 2 +- report/chapters/04_implementation/04_midas_language.typ | 4 ++-- report/chapters/04_implementation/05_python_checking.typ | 4 ++-- report/utils.typ | 9 ++------- 4 files changed, 7 insertions(+), 12 deletions(-) diff --git a/report/chapters/03_theory.typ b/report/chapters/03_theory.typ index 0c05ab4..9597872 100644 --- a/report/chapters/03_theory.typ +++ b/report/chapters/03_theory.typ @@ -13,7 +13,7 @@ This chapter presents some type theory notions that form the basis of a type checker and establishes some typing rules we would wish to implement for Python. The reader may skip this more theoretical chapter to the implementation in @chap:impl. These notions are only explained to justify some implementation decisions and provide a solid basis on which to build the type checker but are not strictly necessary to understand the concrete development. ] -The first step to build a useful type checker for Python is to identify the concrete requirements. By identifying what kind of expressions and relationships we are dealing with, we can then select the rules we need to define and handle, and implement appropriate solutions. The concepts and ideas covered in this chapter are mainly derived from the wonderful #acr("TaPL") by Benjamin C. Pierce. This books serves as a reference for all type theory notions presented in this report. This report does not intend to become the new #acr("TaPL"). +The first step to build a useful type checker for Python is to identify the concrete requirements. By identifying what kind of expressions and relationships we are dealing with, we can then select the rules we need to define and handle, and implement appropriate solutions. The concepts and ideas covered in this chapter are mainly derived from the wonderful #acr("TaPL") by Benjamin C. Pierce. This book serves as a reference for all type theory notions presented in this report. This report does not intend to become the new #acr("TaPL"). == Principles diff --git a/report/chapters/04_implementation/04_midas_language.typ b/report/chapters/04_implementation/04_midas_language.typ index 286b618..18d4dbd 100644 --- a/report/chapters/04_implementation/04_midas_language.typ +++ b/report/chapters/04_implementation/04_midas_language.typ @@ -7,7 +7,7 @@ = Midas Definition Processing -For maximum flexibility and control over the whole process, it has been chosen to implement a custom parser for the Midas language from scratch. Moreover, the student already had some experience in implementing a parser and interpreter. Thus, most of the lexer and parser logic implemented in this section has been adapted from the student's previous #fn-link(, "https://git.kb28.ch/HEL/pebble")[pebble] project, which is itself based on the wonderful _Crafting Interpreters_ by Robert Nystrom@Nystrom2021. +For maximum flexibility and control over the whole process, it has been chosen to implement a custom parser for the Midas language from scratch. Moreover, the student already had some experience in implementing a parser and interpreter. Thus, most of the lexer and parser logic implemented in this section has been adapted from the student's previous #fn-link(, "https://git.kb28.ch/HEL/pebble")[pebble] project, which is itself based on the wonderful _Crafting Interpreters_ by Robert Nystrom@Nystrom2021. Processing a Midas definitions file is done in 3 steps: + lexing, i.e. turning raw text bytes into tokens @@ -18,7 +18,7 @@ Each of these steps map to a dedicated class, respectively `MidasLexer`, `MidasP == Lexing and parsing -The lexer and parser follow the structure presented by Nystrom@Nystrom2021 and implemented in #fn-link(, "https://git.kb28.ch/HEL/pebble")[pebble]@Pebble. +The lexer and parser follow the structure presented by Nystrom@Nystrom2021 and implemented in #fn-link(, "https://git.kb28.ch/HEL/pebble", new: false)[pebble]@Pebble. Concretely, the lexer scans the source code character by character and produces tokens. Several token types are defined to cover all needs in Midas, such as operators, identifiers, keywords and punctuation. The full list of token types is included in @tab:token-types. Whitespace and comments are also converted to tokens but are ignored by the parser. Each token, as represented by the class shown in @fig:token-class, has a token type, a lexeme (i.e. the raw characters making up that token) and a position. That position is crucial for reporting diagnostics as it allows precisely showing the user where an error is in the source file. Tokens representing literals also contain their literal value, like strings, numbers and constants. diff --git a/report/chapters/04_implementation/05_python_checking.typ b/report/chapters/04_implementation/05_python_checking.typ index bcc1e70..3621f42 100644 --- a/report/chapters/04_implementation/05_python_checking.typ +++ b/report/chapters/04_implementation/05_python_checking.typ @@ -44,7 +44,7 @@ Although we will not _evaluate_ Python expressions, we will need to know what bi caption: [Example of variable scoping in Python] ) -The implementation is mostly adapted from a previous project, #fn-link(, "https://git.kb28.ch/HEL/pebble")[pebble] and based on Nystrom's@Nystrom2021 tutorial-like approach. We will not describe the whole implementation of our `Resolver`, which is available in the repository in #code-ref(, "midas/checker/resolver.py"), but only mention two points of interest. +The implementation is mostly adapted from a previous project, #fn-link(, "https://git.kb28.ch/HEL/pebble")[pebble] and based on Nystrom's@Nystrom2021 tutorial-like approach. We will not describe the whole implementation of our `Resolver`, which is available in the repository in #code-ref(, "midas/checker/resolver.py"), but only mention two points of interest. The first point is that this resolver will also perform a form of definite assignment analysis, which is "a data-flow analysis used by compilers to conservatively ensure that a variable or location is always assigned before it is used"@enwiki:1326872870. This is distinguishing variable declaration and definition, which is not explicit in Python's syntax. It allows producing useful error diagnostics such as in @fig:resolver-visit_variable_expr. @@ -563,7 +563,7 @@ In practice, we keep track of a scope dictionary containing all defined variable == Frames and Columns -There is still one big part of the type checker that we have not covered: dataframes and columns. Properly type checking dataframe operations is a never-ending rabbit hole. Libraries like `pandas` or `polars` provide a enormous amount of features, as methods and syntax sugars. They also have highly polymorphic functions, accepting operations with all kinds of values, such as multiplying a dataframe by a scalar, a list, a column or even another dataframe. The results of these operations may vary quite a lot depending on the operands, or sometimes on the parameters passed to some functions. As discussed in @chap:state-of-the-art, some libraries do try and make developers' lives a little better by providing some static type-checking like #fn-link(, "https://strictly-typed-pandas.readthedocs.io")[_Strictly Typed Pandas_], or runtime schema verification like #fn-link(, "https://pandera.readthedocs.io")[_Pandera_]@niels_bantilan-proc-scipy-2020. +There is still one big part of the type checker that we have not covered: dataframes and columns. Properly type checking dataframe operations is a never-ending rabbit hole. Libraries like `pandas` or `polars` provide a enormous amount of features, as methods and syntax sugars. They also have highly polymorphic functions, accepting operations with all kinds of values, such as multiplying a dataframe by a scalar, a list, a column or even another dataframe. The results of these operations may vary quite a lot depending on the operands, or sometimes on the parameters passed to some functions. As discussed in @chap:state-of-the-art, some libraries do try and make developers' lives a little better by providing some static type-checking like #fn-link(, "https://strictly-typed-pandas.readthedocs.io")[_Strictly Typed Pandas_], or runtime schema verification like #fn-link(, "https://pandera.readthedocs.io")[_Pandera_]@niels_bantilan-proc-scipy-2020. Midas stands in between by providing some static type-checking for dataframe schemas and operations as well as generating runtime checks when a value is cast to such a type. We will explore in this section how we can not only provide type-checking for frame columns, for either accessing or assigning to them, but also handle some method calls with best effort inference of the resulting type. diff --git a/report/utils.typ b/report/utils.typ index 72b46da..c678aed 100644 --- a/report/utils.typ +++ b/report/utils.typ @@ -20,17 +20,12 @@ ) } -#let _fn-links = state("_fn-links", ()) - -#let fn-link(lbl, url, body) = context { +#let fn-link(lbl, url, body, new: true) = context { link(url, body) - let links = _fn-links.get() - let name = str(lbl) - if name in links { + if not new { footnote(lbl) } else { [#footnote(link(url)) #lbl] - _fn-links.update(links => links + (name,)) } }