Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Drasil Research Group Meeting, Monday, Apr 29, 3:00 pm, ITB/225 #3712

Closed
smiths opened this issue Apr 16, 2024 · 11 comments
Closed

Drasil Research Group Meeting, Monday, Apr 29, 3:00 pm, ITB/225 #3712

smiths opened this issue Apr 16, 2024 · 11 comments

Comments

@smiths
Copy link
Collaborator

smiths commented Apr 16, 2024

  1. How does code generation in Drasil really work? (@balacij)
  2. Regular meeting dates for the summer
  3. Regular meeting between Jason and the summer research assistants?
  4. Onboarding document

Regrets: ?

Virtual: Levi

@smiths smiths changed the title Drasil Research Group Meeting, Monday, Apr 29, 3:00 pm, Room TBD Drasil Research Group Meeting, Monday, Apr 29, 3:00 pm, ITB/225 Apr 16, 2024
@smiths
Copy link
Collaborator Author

smiths commented Apr 26, 2024

Sounds like a good topic for discussion @balacij. At the moment I don't have any other ideas for discussion points, so if someone has an idea for a second agenda item, please feel free to suggest it. 😄

@balacij
Copy link
Collaborator

balacij commented Apr 26, 2024

Thanks! I'm trying to map out the way the code generator works (it's proving to be a bit challenging!), so I'm hoping we can clarify things in the meeting. I'm also trying to be a bit thorough with what I bring to the discussion, so we can get some further documentation out of it!

@smiths
Copy link
Collaborator Author

smiths commented Apr 26, 2024

Sounds like a great use of our time @balacij!

@samm82
Copy link
Collaborator

samm82 commented Apr 29, 2024

Super minor thing, but "onboarding" seems to be one word (although my Firefox spellchecker disagrees!) - the rest of the document looks good, although maybe we could add contact information to the section on team members?

@smiths
Copy link
Collaborator Author

smiths commented Apr 29, 2024

Thanks @samm82. I agree that onboarding should be one word. I thought about including contact information, but since the onboarding document is on a public repo, I'm not sure everyone would want this.

@J1aM1ng
Copy link
Collaborator

J1aM1ng commented Apr 29, 2024

I will be virtual today, as I need to wait for the window repair crew to arrive in the afternoon. Looking forward to discussing generator and summer work related topics :)!

@balacij
Copy link
Collaborator

balacij commented Apr 29, 2024

Extending the Code Generator: Fully Unmodular Code Generation

Objective

No modules, no functions generation (#3512)

Extend the Drasil's code generator for “big ball of mud”/single-file project generation.

Context

I have a vague idea of how the code generation works, but now I'm working with it, and don't want to introduce any technical debts through confused patches. So, I want to understand it and extend it properly, which can only be done by first examining the design of Drasil.

Problem

The task should not be complicated (because we have all the needed pieces of code already written), but stitching it together (in an understandable, maintainable) way is proving difficult, in part because the code is undocumented.

Exploration

Assumptions

  1. drasil-code defines:
    1. one kind of “story” (i.e., simple code specifications/CodeSpec, “story” meaning a chunk that accumulates a series of other chunks that are only valid within that story and is centred around capturing one “large” piece of information [a “system” of sorts]),
    2. how it can be translated into another story (i.e., drasil-gool-based code),
    3. how another story can translate into it (i.e., SystemInformation, which is yet to be properly defined, but contains user requirements and scientific information about programs users desire)

Journey

0. Looking at generated artifacts

Projectile under an unmodular, “program” program type with no logging appears to be the most relevant

Key finding: we can generate relatively minimal forms of Projectile already using the “unpopular program” program type.

Observations:

  1. The quantity formulas defined in the instance models and data definitions section of the Projectile SRS are used to declare a series of functions corresponding to each formula.
    1. Function names of the form “func_$x$” where $x$ corresponds to the quantity defined by the formula that the function represents.
    2. Function arguments intermix two sets of information from the SRS: the program input variables and the variables marked as “inputs” to the formula defined in the equational theory that defines the quantity calculated by the function.
  2. There is a get_input function that reads in the variables marked as “program inputs” in the SRS, from a file.
  3. There is a input_constraints function that does range checks for the read inputs from (2).
  4. There is a write_input function that writes the variables marked as “program outputs” in the SRS, to a file.
  5. The program, after all definitions, has a series of steps that sequentially read in the program inputs, validate them, calculate the outputs making sure to follow a logical train of variable calculations, and then writes the output variables to a file.

Analysis (ignoring topics unrelated to the issue at hand for now…):

  1. We should have all the pieces necessary to do what we're interested in.
  2. We just need to figure out how to collapse those pieces together into one big snippet of code.
  3. A key differentiating feature of this generated artifact from the others is the “unmodular” option.

1. Tracking down where Modular & Unmodular program types came from

Key finding: the Modularity data type:

data Modularity = Modular InputModule | Unmodular

Insufficiently documented -- the reason for InputModule being a parameter of the Modular constructor is unclear. A “codebase” can have multiple program entries, so I'm not sure what it means. Looking at the definition of InputModule, it looks like InputModule is about where the input-reader functions go, so it appears that Modularity is focused on single-entry programs that are expected to read in inputs.

2. Finding where Modularity, and Unmodular specifically are used

Only searching for where Modularity + Unmodular makes a key difference in the code generated is:

-- | Generates an SCS program based on the problem and the user's design choices.
genProgram :: (OOProg r) => GenState (GSProgram r)
genProgram = do
g <- get
ms <- chooseModules $ modular g
let n = pName $ codeSpec g
let p = show $ sentenceDoc (sysinfodb $ codeSpec g) Implementation OneLine $ foldlSent $ purpose $ codeSpec g
return $ prog n p ms
-- | Generates either a single module or many modules, based on the users choice
-- of modularity.
chooseModules :: (OOProg r) => Modularity -> GenState [SFile r]
chooseModules Unmodular = liftS genUnmodular
chooseModules (Modular _) = genModules

Key finding: genUnmodular is what orchestrates building a software package with an “unmodular” layout.

-- | Generates an entire SCS program as a single module.
genUnmodular :: (OOProg r) => GenState (SFile r)
genUnmodular = do
g <- get
umDesc <- unmodularDesc
let n = pName $ codeSpec g
cls = any (`member` clsMap g)
["get_input", "derived_values", "input_constraints"]
genModuleWithImports n umDesc (concatMap (^. imports) (elems $ extLibMap g))
(genMainFunc
: map (fmap Just) (map genCalcFunc (execOrder $ codeSpec g)
++ concatMap genModFuncs (modules g))
++ ((if cls then [] else [genInputFormat Pub, genInputDerived Pub,
genInputConstraints Pub]) ++ [genOutputFormat]))
([genInputClass Auxiliary, genConstClass Auxiliary]
++ map (fmap Just) (concatMap genModClasses $ modules g))

Analysis:

  1. It derives a module with a name, imports, flow, program entry function, and declared functions from a “code specification.”
  2. To make the truly unmodular program, we need to instead derive a program entry function that encapsulates everything other than the imports and the name.

Commentary:

  1. Ignoring what we know about Drasil -- unclear why drasil-code refers to SCS at all.
  2. I have no idea why cls exists in the body of genModular nor how it works. It relies on implicit knowledge of certain names.
  3. A module is declared "all in one go."
  4. Naively removing all declared functions in the module constructed other than genMainFunc permits creation of a program with references to things that don't exist, and with without any warnings or errors.

3. Investigating the Code Specification

CodeSpec declaration:

-- | Code specifications. Holds information needed to generate code.
data CodeSpec where
CodeSpec :: (HasName a) => {
-- | Program name.
pName :: Name,
-- | Authors.
authors :: [a],
-- | Purpose.
purpose :: Purpose,
-- | Example Background.
background :: Background,
-- | All inputs.
inputs :: [Input],
-- | Explicit inputs (values to be supplied by a file).
extInputs :: [Input],
-- | Derived inputs (each calculated from explicit inputs in a single step).
derivedInputs :: [Derived],
-- | All outputs.
outputs :: [Output],
-- | List of files that must be in same directory for running the executable.
configFiles :: [FilePath],
-- | Mathematical definitions, ordered so that they form a path from inputs to
-- outputs.
execOrder :: [Def],
-- | Map from 'UID's to constraints for all constrained chunks used in the problem.
cMap :: ConstraintCEMap,
-- | List of all constants used in the problem.
constants :: [Const],
-- | Map containing all constants used in the problem.
constMap :: ConstantMap,
-- | Additional modules required in the generated code, which Drasil cannot yet
-- automatically define.
mods :: [Mod], -- medium hack
-- | The database of all chunks used in the problem.
sysinfodb :: ChunkDB
} -> CodeSpec

CodeSpec constructor:

-- | Defines a 'CodeSpec' based on the 'SystemInformation', 'Choices', and 'Mod's
-- defined by the user.
codeSpec :: SystemInformation -> Choices -> [Mod] -> CodeSpec
codeSpec SI {_sys = sys
, _authors = as
, _purpose = ps
, _background = bk
, _instModels = ims
, _datadefs = ddefs
, _configFiles = cfp
, _inputs = ins
, _outputs = outs
, _constraints = cs
, _constants = cnsts
, _sysinfodb = db} chs ms =
let n = programName sys
inputs' = map quantvar ins
const' = map qtov (filter ((`Map.notMember` conceptMatch (maps chs)) . (^. uid))
cnsts)
derived = map qtov $ getDerivedInputs ddefs inputs' const' db
rels = (map qtoc (getEqModQdsFromIm ims ++ mapMaybe qdEFromDD ddefs) \\ derived)
++ mapODE (getODE $ extLibs chs)
-- TODO: When we have better DEModels, we should be deriving our ODE information
-- directly from the instance models (ims) instead of directly from the choices.
outs' = map quantvar outs
allInputs = nub $ inputs' ++ map quantvar derived
exOrder = getExecOrder rels (allInputs ++ map quantvar cnsts) outs' db
in CodeSpec {
pName = n,
authors = as,
purpose = ps,
background = bk,
inputs = allInputs,
extInputs = inputs',
derivedInputs = derived,
outputs = outs',
configFiles = cfp,
execOrder = exOrder,
cMap = constraintMap cs,
constants = const',
constMap = assocToMap const',
mods = ms,
sysinfodb = db
}

Analysis:

  1. A CodeSpec copies some information directly from a SystemInformation into itself, implying that uses of that CodeSpec relies on "higher-level" knowledge (I'm saying this in the Multi-level Modelling sense, all features dependent on this CodeSpec are also dependent on a specific higher-level SystemInformation).
  2. A CodeSpec derives input and output variables, and a list of “relations” (formulas) from a SystemInformation.
    1. The IO variables are of type CodeVarChunk, which are really just wrappers around a CodeChunk, which are really just wrappers around a QuantityDict. However, a CodeChunk carries one additional piece of information: VarOrFunc (comment on the chunk definition: "the kind of code", presuming to mean how we want it to be represented [?]).
    2. Constant variables and derived variables are captured using CodeDefinitions (constructed using qtov or qtoc, variables or functions, respectively), which are more or less clones of a QDefinition, except using a CodeChunk and a CodeExpr instead of a QuantityDict and polymorphic type parameter. Derived variables, notably, are defined using qtoc (functions!) declarations.

Comments:

  1. I don't think the name fits what's in the record. A “code”/codebase specification, to me, either refers to the design of a codebase (implementation-design knowledge), expectations of a codebase (axiomatic behaviour of an implementation), or both.

Relevant snippets:

-- | Details if a piece of code is meant to be a variable or a function.
data VarOrFunc = Var | Func
-- | Basic chunk representation in the code generation context.
-- Contains a QuantityDict and the kind of code (variable or function).
data CodeChunk = CodeC { _qc :: QuantityDict
, kind :: VarOrFunc -- TODO: Jason: Once we have function spaces, I believe we won't need to store this
}
makeLenses ''CodeChunk

-- | Chunk representing a variable. The @obv@ field represents the object containing
-- this variable, if it is an object field.
data CodeVarChunk = CodeVC {_ccv :: CodeChunk,
_obv :: Maybe CodeChunk}
makeLenses ''CodeVarChunk

4. Attempt #1: Naïvely

Since a CodeSpec appears to merely declare how we want variables and such from an SRS to appear in a codebase: we should be able to change our declaration type of derived variables from Func to Var to inline their definition, right?

      rels = (map qtoc (getEqModQdsFromIm ims ++ mapMaybe qdEFromDD ddefs) \\ derived)
        ++ mapODE (getODE $ extLibs chs)

->

      rels = (map qtov (getEqModQdsFromIm ims ++ mapMaybe qdEFromDD ddefs) \\ derived)
        ++ mapODE (getODE $ extLibs chs)

Diff:

diff --strip-trailing-cr -r -X ../.gitignore stable/projectile/projectile_u_p_nol_u_wi_v_d/src/python/Projectile.py build/projectile/projectile_u_p_nol_u_wi_v_d/src/python/Projectile.py
13c13
< def func_t_flight(v_launch, theta, g):
---
> def t_flight(v_launch, theta, g):
21c21
< def func_p_land(v_launch, theta, g):
---
> def p_land(v_launch, theta, g):
28c28
< def func_d_offset(p_target, p_land):
---
> def d_offset(p_target, p_land):
36c36
< def func_s(p_target, epsilon, d_offset):
---
> def s(p_target, epsilon, d_offset):
113,116c113,116
< t_flight = func_t_flight(v_launch, theta, g)
< p_land = func_p_land(v_launch, theta, g)
< d_offset = func_d_offset(p_target, p_land)
< s = func_s(p_target, epsilon, d_offset)
---
> t_flight = t_flight(v_launch, theta, g)
> p_land = p_land(v_launch, theta, g)
> d_offset = d_offset(p_target, p_land)
> s = s(p_target, epsilon, d_offset)

Analysis:

  1. CodeSpec does not specify what I thought it does, nor is it clear what it specifies.
  2. VarOrFunc is only used to decide whether to append a func_ to a QuantityDicts symbol or not.
  3. QuantityDicts are carried, within a CodeSpec, in containers of conflicting types: CodeVarChunk and a CodeFuncChunk can both relate to the same QuantityDict, and there won't be any sort of scoping issue found by the CodeSpec (but it seems to be a part of the design?).

5. Attempt #2. Modifying genMainFunc

genMainFunc:

-- | Generates a main function, to act as the controller for an SCS program.
-- The controller declares input and constant variables, then calls the
-- functions for reading input values, calculating derived inputs, checking
-- constraints, calculating outputs, and printing outputs.
-- Returns Nothing if the user chose to generate a library.
genMainFunc :: (OOProg r) => GenState (Maybe (SMethod r))
genMainFunc = do
g <- get
let mainFunc Library = return Nothing
mainFunc Program = do
v_filename <- mkVar $ quantvar inFileName
logInFile <- maybeLog v_filename
co <- initConsts
ip <- getInputDecl
ics <- getAllInputCalls
varDef <- mapM getCalcCall (execOrder $ codeSpec g)
wo <- getOutputCall
return $ Just $ (if CommentFunc `elem` commented g then docMain else
mainFunction) $ bodyStatements $ initLogFileVar (logKind g)
++ varDecDef v_filename (arg 0)
: logInFile
-- Constants must be declared before inputs because some derived
-- input definitions or input constraints may use the constants
++ catMaybes [co, ip] ++ ics ++ catMaybes (varDef ++ [wo])
mainFunc $ implType g

The line of interest that generates the calculation-assignment steps using strictly function calls.

varDef <- mapM getCalcCall (execOrder $ codeSpec g)

The function calls:

-- | Generates a call to a calculation function, given the 'CodeDefinition' for the
-- value being calculated.
getCalcCall :: (OOProg r) => CodeDefinition -> GenState (Maybe (MSStatement r))
getCalcCall c = do
t <- codeType c
val <- getFuncCall (codeName c) (convType t) (getCalcParams c)
v <- mkVar $ quantvar c
l <- maybeLog v
return $ fmap (multi . (: l) . varDecDef v) val

We can naively modify this area of code to, instead of generate function calls, inline the CodeDefinition-provided formulas necessary to calculating things, but:

  1. there is no sanity checking (scopes, types, etc.),
  2. CodeSpec is left confused,
  3. Modularity would need to be passed around to areas related but also that heavily diverge,
  4. CodeXChunks and CodeChunks remain data wrappers that add little information, but would also always be generated despite their irrelevance.

This solution is hacky, in my opinion. To me, the Modularity should denote two kinds, as they are now, but those two modularity kinds should also denote unique transformers that derive program designs from SCS calculation schemes. SCS calculation schemes are what CodeSpec really should denote, too.

drasil-code's data types

ripgrep-searched for 'data/(new)?type' declarations
rg "(data\s+[A-Z]+[A-Za-z]+|(new)?type\s+[A-Z]+[A-Za-z]+\s+=)" -ths > drasil-code-data-types.out
lib/Language/Drasil/Code/ExternalLibrary.hs:type Condition = CodeExpr
lib/Language/Drasil/Code/ExternalLibrary.hs:type Requires = String
lib/Language/Drasil/Code/ExternalLibrary.hs:type ExternalLibrary = [StepGroup]
lib/Language/Drasil/Code/ExternalLibrary.hs:type StepGroup = NonEmpty [Step]
lib/Language/Drasil/Code/ExternalLibrary.hs:data Step = Call FunctionInterface
lib/Language/Drasil/Code/ExternalLibrary.hs:data FunctionInterface = FI (NonEmpty Requires) FuncType CodeFuncChunk [Argument] (Maybe Result)
lib/Language/Drasil/Code/ExternalLibrary.hs:data Result = Assign CodeVarChunk | Return
lib/Language/Drasil/Code/ExternalLibrary.hs:data Argument = Arg (Maybe NamedArgument) ArgumentInfo -- Maybe named argument
lib/Language/Drasil/Code/ExternalLibrary.hs:data ArgumentInfo =
lib/Language/Drasil/Code/ExternalLibrary.hs:data Parameter = LockedParam ParameterChunk | NameableParam Space
lib/Language/Drasil/Code/ExternalLibrary.hs:data ClassInfo = Regular [MethodInfo] | Implements String [MethodInfo]
lib/Language/Drasil/Code/ExternalLibrary.hs:data MethodInfo = CI Description [Parameter] [Step]
lib/Language/Drasil/Code/ExternalLibrary.hs:data FuncType = Function | Method CodeVarChunk | Constructor
lib/Language/Drasil/Code/Code.hs:newtype Code = Code { unCode :: [(FilePath, Doc)]}
lib/Language/Drasil/Code/Lang.hs:data Lang = Cpp
lib/Language/Drasil/Code/ExternalLibraryCall.hs:type ExternalLibraryCall = [StepGroupFill]
lib/Language/Drasil/Code/ExternalLibraryCall.hs:data StepGroupFill = SGF Int [StepFill]
lib/Language/Drasil/Code/ExternalLibraryCall.hs:data StepFill = CallF FunctionIntFill
lib/Language/Drasil/Code/ExternalLibraryCall.hs:newtype FunctionIntFill = FIF [ArgumentFill]
lib/Language/Drasil/Code/ExternalLibraryCall.hs:data ArgumentFill = UserDefinedArgF (Maybe NamedArgument) CodeExpr -- ^ For arguments that are completely dependent on use case.
lib/Language/Drasil/Code/ExternalLibraryCall.hs:data ParameterFill = NameableParamF ParameterChunk | UserDefined ParameterChunk
lib/Language/Drasil/Code/ExternalLibraryCall.hs:data ClassInfoFill = RegularF [MethodInfoFill] | ImplementsF [MethodInfoFill]
lib/Language/Drasil/Code/ExternalLibraryCall.hs:data MethodInfoFill = CIF [ParameterFill] [Initializer] [StepFill]
lib/Language/Drasil/Code/ExtLibImport.hs:data ExtLibState = ELS {
lib/Language/Drasil/Chunk/CodeDefinition.hs:data DefinitionType = Definition | ODE
lib/Language/Drasil/Chunk/CodeDefinition.hs:data CodeDefinition = CD { _cchunk   :: CodeChunk
lib/Language/Drasil/Chunk/ConstraintMap.hs:type ConstraintCE = Constraint CodeExpr
lib/Language/Drasil/Chunk/ConstraintMap.hs:type ConstraintCEMap = Map.Map UID [ConstraintCE]
lib/Language/Drasil/Choices.hs:data Choices = Choices {
lib/Language/Drasil/Choices.hs:data Architecture = Archt {
lib/Language/Drasil/Choices.hs:data Modularity = Modular InputModule -- ^ Different modules. For controller,
lib/Language/Drasil/Choices.hs:data InputModule = Combined -- ^ Input-related functions combined in one module.
lib/Language/Drasil/Choices.hs:data ImplementationType = Library -- ^ Generated code does not include Controller.
lib/Language/Drasil/Choices.hs:data DataInfo = DataInfo {
lib/Language/Drasil/Choices.hs:data Structure = Unbundled -- ^ Individual variables
lib/Language/Drasil/Choices.hs:data ConstantStructure = Inline -- ^ Inline values for constants.
lib/Language/Drasil/Choices.hs:data ConstantRepr = Var -- ^ Constants represented as regular variables.
lib/Language/Drasil/Choices.hs:data Maps = Maps {
lib/Language/Drasil/Choices.hs:type ConceptMatchMap = Map UID [CodeConcept]
lib/Language/Drasil/Choices.hs:type MatchedConceptMap = Map UID CodeConcept
lib/Language/Drasil/Choices.hs:data CodeConcept = Pi deriving Eq
lib/Language/Drasil/Choices.hs:type SpaceMatch = Space -> [CodeType]
lib/Language/Drasil/Choices.hs:data OptionalFeatures = OptFeats{
lib/Language/Drasil/Choices.hs:data DocConfig = DocConfig {
lib/Language/Drasil/Choices.hs:data Comments = CommentFunc -- ^ Function/method-level comments.
lib/Language/Drasil/Choices.hs:data Verbosity = Verbose | Quiet
lib/Language/Drasil/Choices.hs:data Visibility = Show
lib/Language/Drasil/Choices.hs:data LogConfig = LogConfig {
lib/Language/Drasil/Choices.hs:data Logging = LogFunc -- ^ Log messages generated for function calls.
lib/Language/Drasil/Choices.hs:data AuxFile = SampleInput FilePath
lib/Language/Drasil/Choices.hs:data Constraints = Constraints{
lib/Language/Drasil/Choices.hs:data ConstraintBehaviour = Warning -- ^ Print warning when constraint violated.
lib/Language/Drasil/Choices.hs:newtype ExtLib = Math ODE
lib/Language/Drasil/Choices.hs:data ODE = ODE{
lib/Language/Drasil/Chunk/NamedArgument.hs:newtype NamedArgument = NA {_qtd :: QuantityDict}
lib/Language/Drasil/CodeSpec.hs:type Input = CodeVarChunk
lib/Language/Drasil/CodeSpec.hs:type Output = CodeVarChunk
lib/Language/Drasil/CodeSpec.hs:type Const = CodeDefinition
lib/Language/Drasil/CodeSpec.hs:type Derived = CodeDefinition
lib/Language/Drasil/CodeSpec.hs:type Def = CodeDefinition
lib/Language/Drasil/CodeSpec.hs:data CodeSpec where
lib/Language/Drasil/CodeSpec.hs:type ConstantMap = Map.Map UID CodeDefinition
lib/Language/Drasil/CodeSpec.hs:type Known = CodeVarChunk
lib/Language/Drasil/CodeSpec.hs:type Need  = CodeVarChunk
lib/Language/Drasil/Chunk/Parameter.hs:data PassBy = Val | Ref
lib/Language/Drasil/Chunk/Parameter.hs:data ParameterChunk = PC {_pcc :: CodeChunk
lib/Language/Drasil/Mod.hs:type Name = String
lib/Language/Drasil/Mod.hs:type Description = String
lib/Language/Drasil/Mod.hs:type Import = String
lib/Language/Drasil/Mod.hs:type Version = String
lib/Language/Drasil/Mod.hs:data Mod = Mod Name Description [Import] [Class] [Func]
lib/Language/Drasil/Mod.hs:data Class = ClassDef {
lib/Language/Drasil/Mod.hs:data StateVariable = SV {
lib/Language/Drasil/Mod.hs:data Func = FDef FuncDef
lib/Language/Drasil/Mod.hs:data FuncData where
lib/Language/Drasil/Mod.hs:data FuncDef where
lib/Language/Drasil/Mod.hs:type Initializer = (CodeVarChunk, CodeExpr)
lib/Language/Drasil/Mod.hs:data FuncStmt where
lib/Language/Drasil/Code/Imperative/Parameters.hs:data ParamType = In | Out deriving Eq
lib/Language/Drasil/Data/ODEInfo.hs:data ODEInfo = ODEInfo {
lib/Language/Drasil/Data/ODEInfo.hs:data ODEOptions = ODEOpts {
lib/Language/Drasil/Data/ODEInfo.hs:data ODEMethod = RK45 | BDF | Adams
lib/Language/Drasil/Code/Imperative/GenerateGOOL.hs:data ClassType = Primary | Auxiliary
lib/Language/Drasil/Data/ODELibPckg.hs:data ODELibPckg = ODELib {
lib/Language/Drasil/Code/Imperative/GenODE.hs:type ODEGenInfo = (Maybe FilePath, [(Name, ExtLibState)], (Name,Version))
lib/Language/Drasil/Code/DataDesc.hs:type DataItem = CodeVarChunk
lib/Language/Drasil/Code/DataDesc.hs:data DataDesc' = DD Data' Delimiter DataDesc' | End Data'
lib/Language/Drasil/Code/DataDesc.hs:data Data' = Datum DataItem' -- ^ Single data item.
lib/Language/Drasil/Code/DataDesc.hs:data DataItem' = DI
lib/Language/Drasil/Code/DataDesc.hs:type Delimiter = String
lib/Language/Drasil/Code/DataDesc.hs:type DataDesc = [Data]
lib/Language/Drasil/Code/DataDesc.hs:type Delim = Char  -- delimiter
lib/Language/Drasil/Code/DataDesc.hs:data Data = Singleton DataItem                      -- ^ Single datum.
lib/Language/Drasil/Code/DataDesc.hs:data LinePattern = Straight [DataItem] -- ^ Line of data with no pattern.
lib/Language/Drasil/Code/Imperative/DrasilState.hs:type MatchedSpaces = Space -> GenState CodeType
lib/Language/Drasil/Code/Imperative/DrasilState.hs:type ExtLibMap = Map String ExtLibState
lib/Language/Drasil/Code/Imperative/DrasilState.hs:type ModExportMap = Map String String
lib/Language/Drasil/Code/Imperative/DrasilState.hs:type ClassDefinitionMap = Map String String
lib/Language/Drasil/Code/Imperative/DrasilState.hs:type GenState = State DrasilState
lib/Language/Drasil/Code/Imperative/DrasilState.hs:data DrasilState = DrasilState {
lib/Language/Drasil/Code/Imperative/DrasilState.hs:type ModExp = (String, String)
lib/Language/Drasil/Code/Imperative/DrasilState.hs:type ClassDef = (String, String)
lib/Language/Drasil/Code/Imperative/GOOL/Data.hs:data AuxData = AD {auxFilePath :: FilePath, auxDoc :: Doc}
lib/Language/Drasil/Code/Imperative/GOOL/Data.hs:data PackData = PackD {packProg :: ProgData, packAux :: [AuxData]}
lib/Language/Drasil/Code/Imperative/Doxygen/Import.hs:type OptimizeChoice = Doc
lib/Language/Drasil/Code/Imperative/Doxygen/Import.hs:type ProjName = String
lib/Language/Drasil/Code/Imperative/GOOL/ClassInterface.hs:type LangAbbrev = String
lib/Language/Drasil/Code/Imperative/GOOL/ClassInterface.hs:type LangVers = String
lib/Language/Drasil/Code/Imperative/GOOL/ClassInterface.hs:type CaseName = String
lib/Language/Drasil/Code/Imperative/GOOL/ClassInterface.hs:type ExamplePurpose = String
lib/Language/Drasil/Code/Imperative/GOOL/ClassInterface.hs:type ExampleDescr = String
lib/Language/Drasil/Code/Imperative/GOOL/ClassInterface.hs:type Contributor = String
lib/Language/Drasil/Code/Imperative/GOOL/ClassInterface.hs:type InFile = String -- TODO: There may not always be an Input/Output File
lib/Language/Drasil/Code/Imperative/GOOL/ClassInterface.hs:type OutFile = String
lib/Language/Drasil/Code/Imperative/GOOL/ClassInterface.hs:data ReadMeInfo = ReadMeInfo {
lib/Language/Drasil/Code/Imperative/Parsers/ConfigParser.hs:data Configuration = Config {genLang :: String, exImp :: Maybe String, opts :: Options}
lib/Language/Drasil/Code/Imperative/Modules.hs:type ConstraintCE = Constraint CodeExpr
lib/Language/Drasil/Code/Imperative/Modules.hs:data CalcType = CalcAssign | CalcReturn deriving Eq
lib/Language/Drasil/Code/Imperative/Build/AST.hs:type CommandFragment = MakeString
lib/Language/Drasil/Code/Imperative/Build/AST.hs:data BuildName = BMain
lib/Language/Drasil/Code/Imperative/Build/AST.hs:data Ext = CodeExt
lib/Language/Drasil/Code/Imperative/Build/AST.hs:data BuildDependencies = BcSource
lib/Language/Drasil/Code/Imperative/Build/AST.hs:data BuildConfig = BuildConfig
lib/Language/Drasil/Code/Imperative/Build/AST.hs:data RunType = Standalone
lib/Language/Drasil/Code/Imperative/Build/AST.hs:data Runnable = Runnable BuildName NameOpts RunType
lib/Language/Drasil/Code/Imperative/Build/AST.hs:data DocConfig = DocConfig Dependencies [Command]
lib/Language/Drasil/Code/Imperative/Build/AST.hs:data NameOpts = NameOpts {
lib/Language/Drasil/Code/Imperative/Build/AST.hs:type BuildCommand = [CommandFragment]
lib/Language/Drasil/Code/Imperative/Build/AST.hs:type InterpreterCommand = String
lib/Language/Drasil/Code/Imperative/Build/AST.hs:type InterpreterOption = String
lib/Language/Drasil/Code/Imperative/Build/Import.hs:data CodeHarness = Ch {

@samm82
Copy link
Collaborator

samm82 commented Apr 29, 2024

My Takeaways

Library version of Projectile

  • Should likely just be Calculations and potentially InputConstraints
  • InputFormat and OutputFormat don't contain logical library-defined functions
  • It is currently Modular with a Separated input module

Our generator makes a lot of assumptions

  • In Imperative/Generator.hs, if ANY of InputFormat, DerivedInputs, or InputConstraints are found (lines 182-183), they are ALL implied to be present (line 188)
  • Also in Imperative/Generator.hs, OutputFormat is ALWAYS implied to be present (line 189)
  • As shown in the diagram from No modules, no functions generation #3512 (assuming its up-to-date), constraints are mandated

How Modularity's option of InputFormat affects code generation

I looked into this based on what @balacij said and I'm pretty sure this is what happens. Modular code is generated by genModules:

-- | Generates either a single module or many modules, based on the users choice
-- of modularity.
chooseModules :: (OOProg r) => Modularity -> GenState [SFile r]
chooseModules Unmodular = liftS genUnmodular
chooseModules (Modular _) = genModules

which is defined here:

-- | Generates all modules for an SCS program.
genModules :: (OOProg r) => GenState [SFile r]
genModules = do
g <- get
mn <- genMain
inp <- chooseInModule $ inMod g
con <- genConstMod
cal <- genCalcMod
out <- genOutputMod
moddef <- traverse genModDef (modules g) -- hack ?
return $ mn : inp ++ con ++ cal : out ++ moddef

Note that inMod gets whether the code to generate is Separate or Combined:

-- | Determines whether input modules are 'Combined' or 'Separated' based on the
-- 'Modularity' stored in 'DrasilState'.
inMod :: DrasilState -> InputModule
inMod ds = inMod' $ modular ds
where inMod' Unmodular = Combined
inMod' (Modular im) = im

which is then handled by chooseInModule:

-- | Generates either a single module containing all input-related components, or
-- separate modules for each input-related component, depending on the user's
-- modularity choice.
chooseInModule :: (OOProg r) => InputModule -> GenState [SFile r]
chooseInModule Combined = genInputModCombined
chooseInModule Separated = genInputModSeparated

@smiths
Copy link
Collaborator Author

smiths commented Apr 30, 2024

All agenda items were covered.

@smiths smiths closed this as completed Apr 30, 2024
@balacij
Copy link
Collaborator

balacij commented May 1, 2024

The onboarding document is excellent, @smiths 😄 !

Regarding the Learn You a Haskell link, the current one has issues with rendering. The new (and updated content-wise) link is: https://learnyouahaskell.github.io/ .

Regarding point 10 in the Initial Tasks section, it might also be worth noting that they can ask us for help with (a) solving issues (the 'newcomers' issues notably are expected to have a concrete set of steps that a newcomer can follow), and (b) finding new issues (we can assign them at the start, or they can find some on their own, but we also have the issue tracker which they can scavenge through).

@smiths
Copy link
Collaborator Author

smiths commented May 3, 2024

Good advice @balacij. I've modified the onboarding document to reflect your feedback.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants