Design#

The fundamental building block of Stylist is the “Rule.” Each rule implements a single check which the source code must pass.

skinparam class {
    BackgroundColor<<unwritten>> AlliceBlue
    BorderColor<<unwritten>> LightSkyBlue
}
skinparam groupInheritance 2

abstract class rule.Rule {
    +{abstract}examine(subject: source.SourceTree): issue.Issue<<collection>>
    }

class rule.FortranCharacterset {
    +examine( subject: source.SourceTree ): issue.Issue<<collection>>
}
rule.Rule <|-- rule.FortranCharacterset

class rule.TrailingWhitespace {
    +examine( subject: source.SourceTree ): issue.Issue<<collection>>
}
rule.Rule ^-- rule.TrailingWhitespace

abstract class rule.FortranRule {
    +examine(subject: source.SourceTree): issue.Issue<<collection>>
    +{abstract}examine_fortran(subject: source.FortranSource): issue.Issue<<collection>>
    }
rule.Rule <|--- rule.FortranRule

class rule.MissingVisibility <<unwritten>> {
    +examine_fortran(subject: source.FortranSource): issue.Issue<<collection>>
    }
rule.FortranRule <|-- rule.MissingVisibility

class rule.MissingImplicit {
    +<<create>>__init__(require_everywhere: Bool = False)
    +examine_fortran(subject: source.FortranSource): issue.Issue<<collection>>
    }
rule.FortranRule <|-- rule.MissingImplicit

class rule.MissingLegalNotice <<unwritten>> {
    +examine_fortran(subject: source.FortranSource): issue.Issue<<collection>>
    }
rule.FortranRule <|-- rule.MissingLegalNotice

abstract class rule.CRule <<unwritten>> {
    +examine( subject: SourceTree ): Issue<<collection>>
    +{abstract}examine_c(subject: source.CSource): issue.Issue<collection>
    }
rule.Rule <|-- rule.CRule

UML class diagram showing “rule” module classes.#

Rules are collected into “Styles” allowing different sets of rules to be used for different purposes.

skinparam class {
    BackgroundColor<<unwritten>> AlliceBlue
    BorderColor<<unwritten>> LightSkyBlue
}
skinparam groupInheritance 2

left to right direction

rule.Rule "*" -o style.Style
abstract class style.Style {
    +<<create>>__init__( rules: Rule<<collection>> )
    +<<property>>name(): String
    +<<setter>>name(new: String)
    +list_rules(): Rule<<collection>>
    +check( source: SourceTree ): issue.Issue<<collection>>
    }

class LFRicStyle {
    +__init__()
    }
style.Style <|-- LFRicStyle

class UMStyle <<unwritten>> {
    +__init__()
    }
style.Style <|-- UMStyle

class style.BespokeStyle <<unwritten>> {
    +__init__()
    +addRule( rule: Rule )
    +removeRule( rule: Rule )
    }
style.Style <|-- style.BespokeStyle

UML class diagram showing “style” module classes.#

Source is presented either line-by-line as strings from the text file or as an abstract syntax tree, gained by parsing the source.

skinparam class {
    BackgroundColor<<unwritten>> AlliceBlue
    BorderColor<<unwritten>> LightSkyBlue
}


class source.CPreProcessor <<concrete-decorator>> {
    +{static}get_name(): String
    +get_text(): String
}
source.TextProcessor <|-- source.CPreProcessor


class source.FortranPreProcessor <<concrete-decorator>> {
    +{static}get_name(): String
    +get_text(): String
}
source.TextProcessor <|-- source.FortranPreProcessor


class source.pFUnitProcessor <<concrete-decorator>> {
    +{static}get_name(): String
    +get_text(): String
}
source.TextProcessor <|-- source.pFUnitProcessor


abstract class source.TextProcessor <<decorator>> {
    +<<create>>__init__(source: SourceText)
    +{abstract}{static}get_name(): String
    +{abstract}get_text(): String
}
source.TextProcessor *-- source.SourceText


class source.FileReader <<concrete-component>> {
    +<<create>>__init__(source_file: Path|TextIO)
    +get_text(): String
}
source.SourceText <|-- source.FileReader


class source.StringReader <<concrete-component>> {
    +<<create>>__init__(source_string: String)
    +get_text(): String
}
source.SourceText <|-- source.StringReader


abstract class source.SourceText <<component>> {
    +{abstract}get_text(): String
}
source.SourceText <|-- source.TextProcessor


abstract class source.SourceTree {
    +<<create>>__init__( text: SourceText )
    +{abstract}get_tree()
    +{abstract}get_tree_error(): String
    +get_text(): String
    }
source.SourceTree *---- source.SourceText


class source.FortranSource {
    +get_tree(): fparser.Fortran2003.Program
    +get_tree_error(): String
    +get_first_statement(root: fparser.Fortran2003.Block): fparser.Fortran2003.StmtBlock
    +path( path: String, root: root: fparser.Fortran2003.Block ): fparser.Fortran2003.Base<<list>>
    +find_all(find_node: fparser.Fortran2003.Base<<type>>, root: fparser.Fortran2003.Base): fparser.Fortran2003.Base<<list>>
    +{static}print_tree(root: fparser.Fortran2003.Base, indent: Integer = 0)
    }
source.SourceTree <|-- source.FortranSource


class source.CSource <<unwritten>> {
    +get_tree()
    +get_tree_error()
    }
source.SourceTree <|-- source.CSource


class source.PlainText {
    +{static}get_name(): String
    +get_tree(): String<<generator>>
    +get_tree_error(): String<<optional>>
}
source.SourceTree <|-- source.PlainText

UML class diagram showing “source” module classes.#

The line based SourceText can come from either a file or a string buffer. This text can have preprocessors applied to it using the decorator pattern.

Operation is orchestrated through the Engine class. It makes use of a factory class to create the correct source object from a file.

''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' stylist.source module

class source.FilePipe {
    +<<create>>__init__(parser: SourceTree<<type>>, preprocessors: TextProcessor<<type>><<vararg>>)
}


source.FilePipe "*" --o source.SourceFactory
class source.SourceFactory {
    +{class}add_extension_mapping(extension: String, pipe: source.FilePipe)
    +{class}get_extensions(): String<<iterable>>
    +{class}read_file(source_file: Path|TextIO): source.SourceTree
}

''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' stylist.engine module

class engine.CheckEngine {
    -styles: Style<<collection>>
    +__init__( styles: Style<<collection>>)
    +check( filename: String <<collection>> ): Issue<<collection>>
    }
style.Style "+" -o engine.CheckEngine

''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' stylist.issue module

class issue.Issue {
    -description: String
    -filename: Path <<optional>>
    -line: Integer <<optional>>
    +__init__( description: String, line: Integer <<optional>>, filename: Path <<optional>> )
    +__str__(): String
    }

UML class diagram showing various orchestration classes.#

Sample operation is shown in the following sequence diagram:

@startuml Checker Sequence Diagram
participant UserInterface
participant engine.CheckEngine as CheckEngine
participant "my_style:rule.LFRicStyle" as LFRicStyle
participant "rule.MissingImplicit" as MissingImplicit
participant source.SourceFactory as SourceFactory
participant "some_text:source.SourceFileReader" as SourceFileReader
participant "source_tree:source.FortranSource" as FortranSource

activate SourceFactory

UserInterface -\ LFRicStyle : LFRicStyle() <<create>>
activate LFRicStyle

LFRicStyle -\ MissingImplicit : <<create>>
activate MissingImplicit

UserInterface -\ CheckEngine : CheckEngine(my_style) <<create>>
activate CheckEngine

UserInterface -> SourceFactory: read_file(filename)

SourceFactory -\ SourceFileReader: SourceFileReader(filename) <<create>>
activate SourceFileReader

SourceFactory -\ FortranSource: FortranSource(some_text) <<create>>
activate FortranSource

SourceFactory --> UserInterface: source_tree

UserInterface -> CheckEngine : check(source_tree)

CheckEngine -> FortranSource : get_tree()
FortranSource --> CheckEngine : root:Program

CheckEngine -> LFRicStyle : check(root)
LFRicStyle -> MissingImplicit : examine(root)
MissingImplicit --> LFRicStyle : Issues[]
LFRicStyle --> CheckEngine : Issues[]

CheckEngine --> UserInterface : Issues[]

@enduml

UML sequence diagram showing example operation.#