packages feed

remarks 0.1.4 → 0.1.5

raw patch · 7 files changed

+97/−39 lines, 7 filesPVP: major bump suggested

API removals or changes: PVP suggests a major version bump

API changes (from Hackage documentation)

- PrettyPrinter: toMrk :: [Judgement] -> String
+ Parser.Impl: parseMaxPoints :: ReadP Double
+ PrettyPrinter: ppJs :: [Judgement] -> String
+ Validator: NoPointsInBottomJudgement :: Judgement -> Invalid
- Validator: validate :: Judgement -> Either Invalid ()
+ Validator: validate :: Judgement -> Either Invalid Judgement

Files

README.md view
@@ -15,9 +15,9 @@  ## Design Goals -  1. One human-readable/editable file per student.-  2. Export options to spreadsheet-formats.-  3. git-friendly file format.+  1. One, or several, human-readable/editable file(s) _per student_.+  2. git-friendly file format.+  3. Export options to spreadsheet-formats.   4. Synchronization options with Dropbox and/or Google Drive.  Goal 4 is not necessarily related to `remarks`, but is related to marking@@ -26,12 +26,16 @@  ## Status -There is a [parser](src/Parser/Impl.hs) and baseline-[validator](src/Validator.hs). These can be invoked using `remarks parse` and-`remarks check`, respectively.+There is:+* a [parser](src/Parser/Impl.hs);+* a (basic) [validator](src/Validator.hs); and+* a [pretty printer](src/PrettyPrinter.hs). +These can be invoked using `remarks parse`, `remarks check`, and `remarks+show`, respectively.+ See [Issues](https://github.com/oleks/remarks/issues) for a roadmap. Feel free-to add or fix some.+to add or fix a couple issues.  ## Installation @@ -89,14 +93,14 @@ this:  ```-## T1: 15/15+## T1: /15 -## Formal requirements: 5/5+### Formal requirements: /5   * Has code   * Has XML in a reloadable/testable form   * Has an explanation -### Quality assessment: 10/10+### Quality assessment: /10   * Understands semaphores   * Understands deadlocks   * Understands starvation@@ -143,6 +147,19 @@   * Solves the problem     + In a complicated way, but yes. ```++## Points and Sums++You do not need to manually enter sums. Points are only required for the+bottom-most judgements. Given the template above you can do a `remarks check`+to check if all necessary points have been given. This makes a "pointless"+template a rather useful starting point for grading.++Points can be integers or half-points, that is, the number may end in `.5`.+Please note, `.5` is exactly representable in an IEEE-754 (the internal+representation of points), so no numerical imprecision occurs when dealing with+half-points. Maximum points may only be integers (the internal representation+of maximum points is still an IEEE-754 double to avoid numerical conversions).  ## Files and Directories 
app/Main.hs view
@@ -5,7 +5,7 @@ import Validator import PrettyPrinter -import Control.Monad ( void, filterM )+import Control.Monad ( void, filterM, liftM ) import Data.List ( sort ) import System.Directory   ( doesFileExist, doesDirectoryExist, listDirectory )@@ -63,12 +63,12 @@     Right js -> pure js     Left e -> parseError e -parseFileInDir :: FilePath -> IO Judgement+parseFileInDir :: FilePath -> IO [Judgement] parseFileInDir path = do   js <- parsePath path   case js of-    [j] -> pure j-    _ -> noTopLevelJudgement path+    [] -> noTopLevelJudgement path+    _ -> pure js  parseFileWithDir :: FilePath -> IO ([Judgement], Judgement) parseFileWithDir path = do@@ -88,7 +88,7 @@   paths <- listDirectory path   let mrkPaths = filter isMrkPath paths   let fullMrkPaths = map (path </>) (sort mrkPaths)-  mapM parseFileInDir fullMrkPaths+  liftM concat $ mapM parseFileInDir fullMrkPaths  extPaths :: FilePath -> (FilePath, FilePath) extPaths path =@@ -119,10 +119,13 @@ parsePaths = mapM parsePath  check :: [Judgement] -> IO ()-check js = putStrLn $ pretty $ map validate js+check js = do+  case mapM validate js of+    Right newJs -> printJs newJs+    Left e -> putStrLn $ show e -ppMarkup :: [Judgement] -> IO ()-ppMarkup = putStrLn . toMrk+printJs :: [Judgement] -> IO ()+printJs = putStrLn . ppJs  main :: IO () main = do@@ -131,5 +134,5 @@     [] -> noCommand     ("parse" : paths) -> parsePaths paths >>= putStrLn . pretty     ("check" : paths) -> parsePaths paths >>= mapM_ check-    ("show" : paths) -> parsePaths paths >>= mapM_ ppMarkup+    ("show" : paths) -> parsePaths paths >>= mapM_ printJs     (c:args) -> invalidCommand c args
remarks.cabal view
@@ -1,5 +1,5 @@ name:                remarks-version:             0.1.4+version:             0.1.5 synopsis:            A DSL for marking student work description:         A DSL for marking student work; see README.md for further details. homepage:            https://github.com/oleks/remarks#readme
src/Parser/Impl.hs view
@@ -30,11 +30,18 @@ parsePoints :: ReadP Double parsePoints = do   is <- parseIntegral-  fs <- (char '.' *> parseIntegral) +++ pure "0"+  fs <- (string ".5") +++ pure "0"   case (maybeRead (is ++ "." ++ fs)) of     Just x -> pure x     _ -> pfail +parseMaxPoints :: ReadP Double+parseMaxPoints = do+  is <- parseIntegral+  case (maybeRead is) of+    Just x -> pure x+    _ -> pfail+ lineToken :: ReadP a -> ReadP a lineToken p = munch (`elem` [' ', '\t', '\r', '\v', '\f']) *> p @@ -51,9 +58,9 @@   void $ char ' '   title <- lineToken $ munchTillExcl ':' -  points <- lineToken $ parsePoints+  points <- (lineToken $ parsePoints) +++ (return $ 1/0)   void $ lineToken $ char '/'-  maxPoints <- lineToken $ parsePoints+  maxPoints <- lineToken $ parseMaxPoints    void $ lineBreak 
src/PrettyPrinter.hs view
@@ -1,11 +1,12 @@-module PrettyPrinter (toMrk) where+module PrettyPrinter (ppJs) where  import Ast  import Text.PrettyPrint+import Data.List (intersperse) -toMrk :: [Judgement] -> String-toMrk js = render $ vcat $ map (formatJudgement 1) js+ppJs :: [Judgement] -> String+ppJs = render . vcat . intersperse (text "") . map (formatJudgement 1)  formatJudgement :: Int -> Judgement -> Doc formatJudgement level (Judgement (header, comments, judgements)) =@@ -17,12 +18,13 @@ isIntegral x = x == fromInteger (round x)  pointsDoc :: Double -> Doc+pointsDoc v | isNaN v = empty pointsDoc v | isIntegral v = integer (round v) pointsDoc v = double v  formatHeader :: Int -> Header -> Doc formatHeader level (Header (title, point, maxPoints)) =-  (text $ replicate level '#') <+> text title <> colon <+>+  (text $ replicate level '#') <+> text title <> colon <> space <>     pointsDoc point <> text "/" <> pointsDoc maxPoints  formatComment :: Comment -> Doc
src/Validator.hs view
@@ -12,6 +12,7 @@   = PointsExceedMaxPoints Header   | BadSubJudgementPointsSum Judgement   | BadSubJudgementMaxPointsSum Judgement+  | NoPointsInBottomJudgement Judgement   deriving (Eq, Show, Generic)  instance Out Invalid@@ -24,18 +25,30 @@ (~=) :: Double -> Double -> Bool x ~= y = abs (x - y) <= 0.01 -validate :: Judgement -> Either Invalid ()-validate j @ (Judgement (h @ (Header (_, p, maxP)), _, subjs)) = do+validateSubJs :: Judgement -> Either Invalid Judgement+validateSubJs (Judgement (h @ (Header (t, _, maxP)), cs, subJs)) = do+  newSubJs <- mapM validate subJs+  let newP = sum $ map points newSubJs+  pure $ Judgement (Header (t, newP, maxP), cs, newSubJs)++validate :: Judgement -> Either Invalid Judgement+validate j @ (Judgement (h @ (Header (_, p, maxP)), _, [])) | isInfinite p = do+  Left $ NoPointsInBottomJudgement j+validate j @ (Judgement (h @ (Header (_, p, maxP)), _, subJs @ (_:_))) | isInfinite p = do+  try ((sum $ map maxPoints subJs) ~= maxP)+    (BadSubJudgementMaxPointsSum j)+  validateSubJs j+validate j @ (Judgement (h @ (Header (_, p, maxP)), _, subJs)) = do   try (p <= maxP)     (PointsExceedMaxPoints h)-  case subjs of-    [] -> return ()+  case subJs of+    [] -> pure j     _ -> do-      try ((sum $ map points subjs) ~= p)+      try ((sum $ map points subJs) ~= p)         (BadSubJudgementPointsSum j)-      try ((sum $ map maxPoints subjs) ~= maxP)+      try ((sum $ map maxPoints subJs) ~= maxP)         (BadSubJudgementMaxPointsSum j)-  forM_ subjs validate+      validateSubJs j  points :: Judgement -> Double points (Judgement (Header (_, v, _), _, _)) = v
test/ValidatorTests.hs view
@@ -13,7 +13,7 @@  import Text.PrettyPrint.GenericPretty -validateStr :: String -> [Either Invalid ()]+validateStr :: String -> [Either Invalid Judgement] validateStr s =   case parseString s of     Right js -> map validate js@@ -23,16 +23,28 @@ posUnitTests = testGroup "Positive Unit Tests"   [ testCase "Lone header line" $       validateStr "# A: 0/0\n" @?=-        [Right ()]+        [ Right $+          Judgement (Header ("A", 0.0, 0.0), [], [])]   , testCase "A couple same-depth header lines" $       validateStr "# A: 0/0\n# B: 0/0\n" @?=-        [Right (), Right ()]+        [ Right $+          Judgement (Header ("A", 0.0, 0.0), [], [])+        , Right $+          Judgement (Header ("B", 0.0, 0.0), [], [])+        ]   , testCase "A simple hierarchy of headers" $       validateStr "# A: 0/0\n## B: 0/0\n" @?=-        [Right ()]+        [ Right $+          Judgement (Header ("A", 0.0, 0.0), [],+            [Judgement (Header ("B", 0.0, 0.0), [], [])])]   , testCase "A couple simple hierarchies" $       validateStr "# A: 0/0\n## B: 0/0\n# C: 0/0\n" @?=-        [Right (), Right ()]+        [ Right $+          Judgement (Header ("A", 0.0, 0.0), [],+            [Judgement (Header ("B", 0.0, 0.0), [], [])])+        , Right $+          Judgement (Header ("C", 0.0, 0.0), [], [])+        ]   ]  negUnitTests :: TestTree@@ -50,6 +62,10 @@         [Left $ BadSubJudgementMaxPointsSum           (Judgement (Header ("A", 0.0, 0.0), [],             [Judgement (Header ("B", 0.0, 1.0), [], [])]))]+  , testCase "Single judgement with no points" $+      validateStr "# A: /0\n" @?=+        [Left $ NoPointsInBottomJudgement+          (Judgement (Header ("A", 1/0, 0.0), [], []))]   ]  qcTests :: TestTree