Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/actions/spelling/expect.txt
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ IOTASKS
KPatterns
Leijen
lhs
linebreak
listify
NPlus
PCRE
Expand Down
59 changes: 49 additions & 10 deletions src/Haskell/Template/Task.hs
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@

import Control.Applicative ((<|>))
import Control.Monad (forM, guard, msum, unless, void, when)
import Control.Monad.Extra (whenJust)
import Control.Monad.Extra ((&&^), whenJust)
import Control.Monad.IO.Class (MonadIO)
import Data.Char (isUpper)
import Data.Functor.Identity (Identity (..))
Expand Down Expand Up @@ -78,7 +78,7 @@
)
import Test.HUnit (Counts (..))
import Text.PrettyPrint.Leijen.Text
(Doc, nest, text, vcat)
(Doc, (<+>), int, linebreak, nest, punctuate, text, vcat)
import Text.Read (readMaybe)
import Text.Regex.PCRE.Heavy (re, sub)

Expand Down Expand Up @@ -108,9 +108,10 @@
\# configHlintSuggestions - hlint hints to provide as suggestions
\# configLanguageExtensions - this sets LanguageExtensions for hlint as well
\# configModules - DEPRECATED (will be ignored)
\# maxLineLength - submissions with lines longer than this value are rejected
\# syntaxCutoff - determines the last step in the syntax phase (later steps are considered semantics)
\# possible values (and also the order of steps):
\# Compilation, GhcErrors, HlintErrors, TemplateMatch, TestSuite
\# CodeWidth, Compilation, GhcErrors, HlintErrors, TemplateMatch, TestSuite
\# default on omission is TemplateMatch; steps after TestSuite are (in this order):
\# GhcWarnings, HlintSuggestions
\# disableSemantics - will prevent the semantics phase (as determined by syntaxCutoff) from running;
Expand Down Expand Up @@ -197,12 +198,13 @@
-}|]

data FeedbackPhase
= Compilation
= CodeWidth
| Compilation
| GhcErrors
| HlintErrors
| TemplateMatch
| TestSuite
deriving (Enum, Generic, Show, FromJSON, ToJSON)
deriving (Enum, Eq, Generic, Show, FromJSON, ToJSON)

data FSolutionConfig m = SolutionConfig {
allowAdding :: m Bool,
Expand All @@ -221,6 +223,7 @@
configHlintSuggestions :: m [String],
configLanguageExtensions :: m [String],
configModules :: m [String],
maxLineLength :: m (Maybe Natural),
provideSampleSolution :: m Bool,
messageOnCloningSampleSolution :: m (Maybe String),
disableSemantics :: m Bool,
Expand Down Expand Up @@ -256,6 +259,7 @@
configHlintSuggestions = Just [],
configLanguageExtensions = Just ["NPlusKPatterns","ScopedTypeVariables"],
configModules = Nothing,
maxLineLength = Just Nothing,
provideSampleSolution = Just False,
messageOnCloningSampleSolution = Just Nothing,
disableSemantics = Just False,
Expand All @@ -280,6 +284,7 @@
<*> fmap Just configHlintSuggestions
<*> fmap Just configLanguageExtensions
<*> fmap Just configModules
<*> fmap Just maxLineLength
<*> fmap Just provideSampleSolution
<*> fmap Just messageOnCloningSampleSolution
<*> fmap Just disableSemantics
Expand All @@ -306,6 +311,7 @@
<*> fmap Identity configHlintSuggestions
<*> fmap Identity configLanguageExtensions
<*> fmap Identity configModules
<*> fmap Identity maxLineLength
<*> fmap Identity provideSampleSolution
<*> fmap Identity messageOnCloningSampleSolution
<*> fmap Identity disableSemantics
Expand All @@ -327,6 +333,7 @@
configHlintSuggestions = configHlintSuggestions x <|> configHlintSuggestions y,
configLanguageExtensions = configLanguageExtensions x <|> configLanguageExtensions y,
configModules = Just [],
maxLineLength = maxLineLength x <|> maxLineLength y,
provideSampleSolution = provideSampleSolution x <|> provideSampleSolution y,
messageOnCloningSampleSolution = messageOnCloningSampleSolution x <|> messageOnCloningSampleSolution y,
disableSemantics = disableSemantics x <|> disableSemantics y,
Expand All @@ -349,6 +356,7 @@
configHlintSuggestions = Nothing,
configLanguageExtensions = Nothing,
configModules = Nothing,
maxLineLength = Nothing,
provideSampleSolution = Nothing,
messageOnCloningSampleSolution = Nothing,
disableSemantics = Nothing,
Expand Down Expand Up @@ -496,22 +504,26 @@
-- ^ whether the conditions outlined in the description apply or not
grade withSyntax withSemantics reject inform dirname task submission = do
withSyntax $ checkUnsafe reject submission
(config, exts, (moduleName', template), others) <- processConfig
(config@SolutionConfig{..}, exts, (moduleName', template), others) <- processConfig
(rejectWithMessage reject $ string informTutorMessage)
(const $ pure ())
task
(modules, solutionFile) <- writeModules (moduleName', submission) others dirname
(modules, solutionFile) <- if runIdentity $ fmap (== CodeWidth) syntaxCutoff &&^ disableSemantics
-- Completely skip file writing if code length is the only syntax phase action
-- and semantics phase is disabled.
then pure (undefined, undefined)
else writeModules (moduleName', submission) others dirname
Comment thread
jvoigtlaender marked this conversation as resolved.
let
(syntax, semantics) = splitAt (fromEnum (syntaxCutoff config) + 1)
(syntax, semantics) = splitAt (fromEnum syntaxCutoff + 1)
$ testPhases reject inform template solutionFile modules config exts submission dirname
withSyntax $ sequence_ syntax
if runIdentity $ disableSemantics config
if runIdentity disableSemantics
then pure False
else do
withSemantics $ sequence_ semantics
case
(,) <$> lookup "SampleSolution" others
<*> runIdentity (messageOnCloningSampleSolution config)
<*> runIdentity messageOnCloningSampleSolution
of
Nothing -> pure False
Just (sampleSolution,message) -> catchSampleSolutionClone
Expand Down Expand Up @@ -596,7 +608,7 @@
liftIO $ unsafeRunInterpreterWithArgs ghcOpts (compiler dirname extensions modules)
checkResult reject ghcErrors how howMany $ const $ return ()
where
makeOpts xs = ("-w":) $ ("-Werror=" ++) <$> xs

Check notice on line 611 in src/Haskell/Template/Task.hs

View workflow job for this annotation

GitHub Actions / Check Spelling

Line matches candidate pattern (Compiler flags) ``(?:^|[\t ,"'`=(])-[DPWXYLlf](?=[A-Z]{2,}|[A-Z][a-z]|[a-z]{2,})`` (candidate-pattern)
ghcOpts = makeOpts $ msum (warnings config)
(warnings, how) =
if asError
Expand All @@ -616,7 +628,7 @@
-> String
-> m ()
matchTemplate reject config context exts template submission =
runMatchTestOn reject exts template submission $ \case

Check warning on line 631 in src/Haskell/Template/Task.hs

View workflow job for this annotation

GitHub Actions / build_and_test (ubuntu-latest, stack)

Pattern match(es) are non-exhaustive

Check warning on line 631 in src/Haskell/Template/Task.hs

View workflow job for this annotation

GitHub Actions / build_and_test (ubuntu-latest, stack)

Pattern match(es) are non-exhaustive
Fail loc -> mapM_ (rejectMatch rejectWithHint config context template submission) loc
where
rejectWithHint = rejectWithMessage reject rejectHint
Expand Down Expand Up @@ -812,7 +824,7 @@
(return . (,rawModules))
eConfig
where
configJson:rawModules = splitModules False configAndModules

Check warning on line 827 in src/Haskell/Template/Task.hs

View workflow job for this annotation

GitHub Actions / build_and_test (ubuntu-latest, stack)

Pattern match(es) are non-exhaustive

Check warning on line 827 in src/Haskell/Template/Task.hs

View workflow job for this annotation

GitHub Actions / build_and_test (ubuntu-latest, stack)

Pattern match(es) are non-exhaustive
eConfig :: Either ParseException SolutionConfigOpt
eConfig = decodeEither' $ BS.pack configJson

Expand Down Expand Up @@ -943,6 +955,10 @@
-> [m ()]
testPhases reject inform template solutionFile modules config exts submission dirname =
[
-- Reject if submission has lines violating the configured maximum line length.
-- Only checked if the setting is configured.
mapM_ (checkLineLength reject submission) $ runIdentity $ maxLineLength config
,
Comment thread
jvoigtlaender marked this conversation as resolved.
do
-- Reject if submission does not compile with provided hidden modules,
-- but without Test module.
Expand Down Expand Up @@ -990,3 +1006,26 @@
Your code is not compatible with the test suite.
Please do not change type signatures in the given code template.
|]

checkLineLength :: Applicative m => (forall a. Doc -> m a) -> String -> Natural -> m ()
checkLineLength reject code maxLength = case hasLonger of
[] -> pure ()
xs -> rejectWithHint $ separated
[ "Your submission contains overlong lines:"
, separated xs
, "The maximum line length allowed is" <+> string (show maxLength) <> "."
]
where
codeLines = lines code
hasLonger =
[ format i l lineLength
| (i, l) <- zip [1..] codeLines
, let lineLength = length l
, fromIntegral lineLength > maxLength
]
format i l lineLength = nest 2 $ vcat
[ "Line" <+> int i <+> "(length" <+> int lineLength <> "):"
, string l
]
separated = vcat . punctuate linebreak
rejectWithHint = rejectWithMessage reject rejectHint
Loading