packages feed

ideas 1.6 → 1.7

raw patch · 105 files changed

+1925/−446 lines, 105 filesdep +blaze-builderdep +bytestringdep +case-insensitivedep −cgidep ~QuickCheckPVP ok

version bump matches the API change (PVP)

Dependencies added: blaze-builder, bytestring, case-insensitive, http-types, network, semigroups, streaming-commons, wai

Dependencies removed: cgi

Dependency ranges changed: QuickCheck

API changes (from Hackage documentation)

- Ideas.Common.Classes: instance Ideas.Common.Classes.Container Data.Set.Base.Set
- Ideas.Common.Rewriting.Difference: differenceEqual :: IsTerm a => (a -> a -> Bool) -> a -> a -> Maybe (a, a)
- Ideas.Common.Rewriting.Difference: differenceEqualWith :: View Term a -> (a -> a -> Bool) -> a -> a -> Maybe (a, a)
- Ideas.Common.Rewriting.Term: instance (Ideas.Common.Rewriting.Term.IsTerm a, GHC.Classes.Ord a) => Ideas.Common.Rewriting.Term.IsTerm (Data.Set.Base.Set a)
- Ideas.Common.Rewriting.Term: instance (Ideas.Common.Rewriting.Term.IsTerm a, Ideas.Common.Rewriting.Term.IsTerm b, GHC.Classes.Ord a) => Ideas.Common.Rewriting.Term.IsTerm (Data.Map.Base.Map a b)
- Ideas.Service.Diagnose: differenceEqual :: Exercise a -> a -> a -> Maybe (a, a)
- Ideas.Text.JSON: instance Ideas.Text.JSON.InJSON GHC.IO.Exception.IOException
+ Ideas.Common.Classes: instance Ideas.Common.Classes.Container Data.Set.Internal.Set
+ Ideas.Common.Constraint: Error :: String -> Result a
+ Ideas.Common.Constraint: Irrelevant :: Result a
+ Ideas.Common.Constraint: Ok :: a -> Result a
+ Ideas.Common.Constraint: data Constraint a
+ Ideas.Common.Constraint: data Result a
+ Ideas.Common.Constraint: getResult :: Constraint a -> a -> Result ()
+ Ideas.Common.Constraint: instance GHC.Base.Alternative Ideas.Common.Constraint.Result
+ Ideas.Common.Constraint: instance GHC.Base.Applicative Ideas.Common.Constraint.Result
+ Ideas.Common.Constraint: instance GHC.Base.Functor Ideas.Common.Constraint.Result
+ Ideas.Common.Constraint: instance GHC.Base.Monad Ideas.Common.Constraint.Result
+ Ideas.Common.Constraint: instance GHC.Base.MonadPlus Ideas.Common.Constraint.Result
+ Ideas.Common.Constraint: instance GHC.Classes.Eq (Ideas.Common.Constraint.Constraint a)
+ Ideas.Common.Constraint: instance GHC.Classes.Ord (Ideas.Common.Constraint.Constraint a)
+ Ideas.Common.Constraint: instance GHC.Show.Show (Ideas.Common.Constraint.Constraint a)
+ Ideas.Common.Constraint: instance Ideas.Common.Id.HasId (Ideas.Common.Constraint.Constraint a)
+ Ideas.Common.Constraint: instance Ideas.Common.View.LiftView Ideas.Common.Constraint.Constraint
+ Ideas.Common.Constraint: isRelevant :: Constraint a -> a -> Bool
+ Ideas.Common.Constraint: isSatisfied :: Constraint a -> a -> Bool
+ Ideas.Common.Constraint: isViolated :: Constraint a -> a -> Maybe String
+ Ideas.Common.Constraint: makeConstraint :: IsId n => n -> (a -> Result ()) -> Constraint a
+ Ideas.Common.Constraint: relevance :: Result a -> Result a
+ Ideas.Common.Environment: instance Data.Semigroup.Semigroup Ideas.Common.Environment.Environment
+ Ideas.Common.Exercise: [constraints] :: Exercise a -> [Constraint (Context a)]
+ Ideas.Common.Exercise: violations :: Exercise a -> Context a -> [(Id, String)]
+ Ideas.Common.Id: (<>) :: Semigroup a => a -> a -> a
+ Ideas.Common.Id: infixr 6 <>
+ Ideas.Common.Id: instance Data.Semigroup.Semigroup Ideas.Common.Id.Id
+ Ideas.Common.Rewriting.RewriteRule: instance (Ideas.Common.Rewriting.RewriteRule.Different a, Ideas.Common.Rewriting.RewriteRule.Different b) => Ideas.Common.Rewriting.RewriteRule.Different (a, b)
+ Ideas.Common.Rewriting.RewriteRule: instance (Ideas.Common.Rewriting.RewriteRule.Different a, Ideas.Common.Rewriting.RewriteRule.Different b, Ideas.Common.Rewriting.RewriteRule.Different c) => Ideas.Common.Rewriting.RewriteRule.Different (a, b, c)
+ Ideas.Common.Rewriting.RewriteRule: instance Ideas.Common.Rewriting.RewriteRule.Different GHC.Integer.Type.Integer
+ Ideas.Common.Rewriting.RewriteRule: instance Ideas.Common.Rewriting.RewriteRule.Different GHC.Real.Rational
+ Ideas.Common.Rewriting.RewriteRule: instance Ideas.Common.Rewriting.RewriteRule.Different GHC.Types.Bool
+ Ideas.Common.Rewriting.RewriteRule: instance Ideas.Common.Rewriting.RewriteRule.Different GHC.Types.Double
+ Ideas.Common.Rewriting.RewriteRule: instance Ideas.Common.Rewriting.RewriteRule.Different GHC.Types.Float
+ Ideas.Common.Rewriting.RewriteRule: instance Ideas.Common.Rewriting.RewriteRule.Different GHC.Types.Int
+ Ideas.Common.Rewriting.Substitution: instance Data.Semigroup.Semigroup Ideas.Common.Rewriting.Substitution.Substitution
+ Ideas.Common.Rewriting.Term: instance (Ideas.Common.Rewriting.Term.IsTerm a, GHC.Classes.Ord a) => Ideas.Common.Rewriting.Term.IsTerm (Data.Set.Internal.Set a)
+ Ideas.Common.Rewriting.Term: instance (Ideas.Common.Rewriting.Term.IsTerm a, Ideas.Common.Rewriting.Term.IsTerm b, GHC.Classes.Ord a) => Ideas.Common.Rewriting.Term.IsTerm (Data.Map.Internal.Map a b)
+ Ideas.Common.Rule.Recognizer: instance Data.Semigroup.Semigroup (Ideas.Common.Rule.Recognizer.Recognizer a)
+ Ideas.Common.Rule.Transformation: instance Data.Semigroup.Semigroup (Ideas.Common.Rule.Transformation.Trans a b)
+ Ideas.Common.Strategy: orelse :: IsStrategy f => [f a] -> Strategy a
+ Ideas.Common.Strategy: preference :: IsStrategy f => [f a] -> Strategy a
+ Ideas.Common.Strategy.Combinators: orelse :: IsStrategy f => [f a] -> Strategy a
+ Ideas.Common.Strategy.Combinators: preference :: IsStrategy f => [f a] -> Strategy a
+ Ideas.Common.Strategy.Configuration: instance Data.Semigroup.Semigroup Ideas.Common.Strategy.Configuration.StrategyCfg
+ Ideas.Common.Strategy.Prefix: instance Data.Semigroup.Semigroup (Ideas.Common.Strategy.Prefix.Prefix a)
+ Ideas.Common.Strategy.Traversal: instance Data.Semigroup.Semigroup (Ideas.Common.Strategy.Traversal.Option a)
+ Ideas.Common.Traversal.Navigator: instance Data.Semigroup.Semigroup Ideas.Common.Traversal.Navigator.Location
+ Ideas.Encoding.Encoder: (<>) :: Semigroup a => a -> a -> a
+ Ideas.Encoding.Encoder: infixr 6 <>
+ Ideas.Encoding.Encoder: instance Data.Semigroup.Semigroup t => Data.Semigroup.Semigroup (Ideas.Encoding.Encoder.Encoder a s t)
+ Ideas.Encoding.LinkManager: [urlForConstraints] :: LinkManager -> forall a. Exercise a -> String
+ Ideas.Encoding.LinkManager: escapeInURL :: String -> String
+ Ideas.Encoding.Options: instance Data.Semigroup.Semigroup Ideas.Encoding.Options.Options
+ Ideas.Encoding.Request: instance Data.Semigroup.Semigroup Ideas.Encoding.Request.Request
+ Ideas.Main.CGI: run :: Application -> IO ()
+ Ideas.Service.DomainReasoner: instance Data.Semigroup.Semigroup Ideas.Service.DomainReasoner.DomainReasoner
+ Ideas.Service.FeedbackScript.Syntax: instance Data.Semigroup.Semigroup Ideas.Service.FeedbackScript.Syntax.Script
+ Ideas.Service.FeedbackScript.Syntax: instance Data.Semigroup.Semigroup Ideas.Service.FeedbackScript.Syntax.Text
+ Ideas.Service.Types: [Constraint] :: Const a (Constraint (Context a))
+ Ideas.Service.Types: [MathML] :: Const a MathML
+ Ideas.Service.Types: tConstraint :: Type a (Constraint (Context a))
+ Ideas.Service.Types: tMathML :: Type a MathML
+ Ideas.Text.HTML: addStyle :: String -> HTMLPage -> HTMLPage
+ Ideas.Text.HTML.Templates: Button :: String -> (HTMLBuilder -> HTMLBuilder) -> HTMLBuilder -> Button
+ Ideas.Text.HTML.Templates: WebPage :: String -> [Button] -> Style -> Style -> Int -> HTMLBuilder -> [Button] -> Style -> Style -> HTMLBuilder -> HTMLBuilder -> Style -> WebPage
+ Ideas.Text.HTML.Templates: [buttonStyle] :: Button -> HTMLBuilder -> HTMLBuilder
+ Ideas.Text.HTML.Templates: [buttonText] :: Button -> HTMLBuilder
+ Ideas.Text.HTML.Templates: [buttonUrl] :: Button -> String
+ Ideas.Text.HTML.Templates: [content] :: WebPage -> HTMLBuilder
+ Ideas.Text.HTML.Templates: [footerStyle] :: WebPage -> Style
+ Ideas.Text.HTML.Templates: [footer] :: WebPage -> HTMLBuilder
+ Ideas.Text.HTML.Templates: [iconBarsStyle] :: WebPage -> Style
+ Ideas.Text.HTML.Templates: [iconCloseStyle] :: WebPage -> Style
+ Ideas.Text.HTML.Templates: [menuButtons] :: WebPage -> [Button]
+ Ideas.Text.HTML.Templates: [menuStyle] :: WebPage -> Style
+ Ideas.Text.HTML.Templates: [sideButtons] :: WebPage -> [Button]
+ Ideas.Text.HTML.Templates: [sideHeader] :: WebPage -> HTMLBuilder
+ Ideas.Text.HTML.Templates: [sideStyle] :: WebPage -> Style
+ Ideas.Text.HTML.Templates: [sideWidth] :: WebPage -> Int
+ Ideas.Text.HTML.Templates: [title] :: WebPage -> String
+ Ideas.Text.HTML.Templates: data Button
+ Ideas.Text.HTML.Templates: data WebPage
+ Ideas.Text.HTML.Templates: fontAwesome :: BuildXML a => String -> a
+ Ideas.Text.HTML.Templates: type Style = HTMLBuilder -> HTMLBuilder
+ Ideas.Text.HTML.Templates: webpage :: WebPage -> HTMLPage
+ Ideas.Text.HTML.W3CSS: Amber :: Color
+ Ideas.Text.HTML.W3CSS: Aqua :: Color
+ Ideas.Text.HTML.W3CSS: Black :: Color
+ Ideas.Text.HTML.W3CSS: Blue :: Color
+ Ideas.Text.HTML.W3CSS: BlueGray :: Color
+ Ideas.Text.HTML.W3CSS: Bottom :: Position
+ Ideas.Text.HTML.W3CSS: BottomLeft :: Position
+ Ideas.Text.HTML.W3CSS: BottomRight :: Position
+ Ideas.Text.HTML.W3CSS: Brown :: Color
+ Ideas.Text.HTML.W3CSS: Center :: Position
+ Ideas.Text.HTML.W3CSS: CenterLeft :: Position
+ Ideas.Text.HTML.W3CSS: CenterRight :: Position
+ Ideas.Text.HTML.W3CSS: Cyan :: Color
+ Ideas.Text.HTML.W3CSS: D1 :: ColorTheme
+ Ideas.Text.HTML.W3CSS: D2 :: ColorTheme
+ Ideas.Text.HTML.W3CSS: D3 :: ColorTheme
+ Ideas.Text.HTML.W3CSS: D4 :: ColorTheme
+ Ideas.Text.HTML.W3CSS: D5 :: ColorTheme
+ Ideas.Text.HTML.W3CSS: DarkGray :: Color
+ Ideas.Text.HTML.W3CSS: DeepOrange :: Color
+ Ideas.Text.HTML.W3CSS: DeepPurple :: Color
+ Ideas.Text.HTML.W3CSS: Gray :: Color
+ Ideas.Text.HTML.W3CSS: Green :: Color
+ Ideas.Text.HTML.W3CSS: Indigo :: Color
+ Ideas.Text.HTML.W3CSS: Jumbo :: Size
+ Ideas.Text.HTML.W3CSS: Khaki :: Color
+ Ideas.Text.HTML.W3CSS: L1 :: ColorTheme
+ Ideas.Text.HTML.W3CSS: L2 :: ColorTheme
+ Ideas.Text.HTML.W3CSS: L3 :: ColorTheme
+ Ideas.Text.HTML.W3CSS: L4 :: ColorTheme
+ Ideas.Text.HTML.W3CSS: L5 :: ColorTheme
+ Ideas.Text.HTML.W3CSS: Large :: Size
+ Ideas.Text.HTML.W3CSS: LightBlue :: Color
+ Ideas.Text.HTML.W3CSS: LightGray :: Color
+ Ideas.Text.HTML.W3CSS: LightGreen :: Color
+ Ideas.Text.HTML.W3CSS: Lime :: Color
+ Ideas.Text.HTML.W3CSS: Medium :: Size
+ Ideas.Text.HTML.W3CSS: Orange :: Color
+ Ideas.Text.HTML.W3CSS: PaleBlue :: Color
+ Ideas.Text.HTML.W3CSS: PaleGreen :: Color
+ Ideas.Text.HTML.W3CSS: PaleRed :: Color
+ Ideas.Text.HTML.W3CSS: PaleYellow :: Color
+ Ideas.Text.HTML.W3CSS: Pink :: Color
+ Ideas.Text.HTML.W3CSS: Purple :: Color
+ Ideas.Text.HTML.W3CSS: Red :: Color
+ Ideas.Text.HTML.W3CSS: Sand :: Color
+ Ideas.Text.HTML.W3CSS: Small :: Size
+ Ideas.Text.HTML.W3CSS: Teal :: Color
+ Ideas.Text.HTML.W3CSS: Tiny :: Size
+ Ideas.Text.HTML.W3CSS: Top :: Position
+ Ideas.Text.HTML.W3CSS: TopLeft :: Position
+ Ideas.Text.HTML.W3CSS: TopRight :: Position
+ Ideas.Text.HTML.W3CSS: White :: Color
+ Ideas.Text.HTML.W3CSS: XL :: Size
+ Ideas.Text.HTML.W3CSS: XXL :: Size
+ Ideas.Text.HTML.W3CSS: XXXL :: Size
+ Ideas.Text.HTML.W3CSS: Yellow :: Color
+ Ideas.Text.HTML.W3CSS: animate :: BuildXML a => Position -> a -> a
+ Ideas.Text.HTML.W3CSS: animateFading :: BuildXML a => a -> a
+ Ideas.Text.HTML.W3CSS: animateInput :: BuildXML a => a -> a
+ Ideas.Text.HTML.W3CSS: animateOpacity :: BuildXML a => a -> a
+ Ideas.Text.HTML.W3CSS: animateZoom :: BuildXML a => a -> a
+ Ideas.Text.HTML.W3CSS: background :: BuildXML a => Color -> a -> a
+ Ideas.Text.HTML.W3CSS: badge :: BuildXML a => a -> a
+ Ideas.Text.HTML.W3CSS: bar :: BuildXML a => a -> a
+ Ideas.Text.HTML.W3CSS: barBlock :: BuildXML a => a -> a
+ Ideas.Text.HTML.W3CSS: barItem :: BuildXML a => a -> a
+ Ideas.Text.HTML.W3CSS: barPos :: BuildXML a => Position -> a -> a
+ Ideas.Text.HTML.W3CSS: border :: BuildXML a => a -> a
+ Ideas.Text.HTML.W3CSS: borderColor :: BuildXML a => Color -> a -> a
+ Ideas.Text.HTML.W3CSS: borderPos :: BuildXML a => Position -> a -> a
+ Ideas.Text.HTML.W3CSS: borderTheme :: BuildXML a => a -> a
+ Ideas.Text.HTML.W3CSS: bordered :: BuildXML a => a -> a
+ Ideas.Text.HTML.W3CSS: bottom :: BuildXML a => a -> a
+ Ideas.Text.HTML.W3CSS: btn :: BuildXML a => String -> a -> a
+ Ideas.Text.HTML.W3CSS: button :: BuildXML a => String -> a -> a
+ Ideas.Text.HTML.W3CSS: card :: BuildXML a => a -> a
+ Ideas.Text.HTML.W3CSS: card2 :: BuildXML a => a -> a
+ Ideas.Text.HTML.W3CSS: card4 :: BuildXML a => a -> a
+ Ideas.Text.HTML.W3CSS: cell :: BuildXML a => a -> a
+ Ideas.Text.HTML.W3CSS: cellBottom :: BuildXML a => a -> a
+ Ideas.Text.HTML.W3CSS: cellMiddle :: BuildXML a => a -> a
+ Ideas.Text.HTML.W3CSS: cellRow :: BuildXML a => a -> a
+ Ideas.Text.HTML.W3CSS: cellTop :: BuildXML a => a -> a
+ Ideas.Text.HTML.W3CSS: center :: BuildXML a => a -> a
+ Ideas.Text.HTML.W3CSS: centered :: BuildXML a => a -> a
+ Ideas.Text.HTML.W3CSS: check :: BuildXML a => a -> a
+ Ideas.Text.HTML.W3CSS: circle :: BuildXML a => a -> a
+ Ideas.Text.HTML.W3CSS: code :: BuildXML a => a -> a
+ Ideas.Text.HTML.W3CSS: codespan :: BuildXML a => a -> a
+ Ideas.Text.HTML.W3CSS: col :: BuildXML a => a -> a
+ Ideas.Text.HTML.W3CSS: collapse :: BuildXML a => a -> a
+ Ideas.Text.HTML.W3CSS: container :: BuildXML a => a -> a
+ Ideas.Text.HTML.W3CSS: content :: BuildXML a => a -> a
+ Ideas.Text.HTML.W3CSS: data Color
+ Ideas.Text.HTML.W3CSS: data ColorTheme
+ Ideas.Text.HTML.W3CSS: data Position
+ Ideas.Text.HTML.W3CSS: data Size
+ Ideas.Text.HTML.W3CSS: display :: BuildXML a => Position -> a -> a
+ Ideas.Text.HTML.W3CSS: displayHover :: BuildXML a => a -> a
+ Ideas.Text.HTML.W3CSS: dropdownClick :: BuildXML a => a -> a
+ Ideas.Text.HTML.W3CSS: dropdownHover :: BuildXML a => a -> a
+ Ideas.Text.HTML.W3CSS: fontSize :: BuildXML a => Size -> a -> a
+ Ideas.Text.HTML.W3CSS: grayscale :: BuildXML a => a -> a
+ Ideas.Text.HTML.W3CSS: grayscaleMax :: BuildXML a => a -> a
+ Ideas.Text.HTML.W3CSS: grayscaleMin :: BuildXML a => a -> a
+ Ideas.Text.HTML.W3CSS: half :: BuildXML a => a -> a
+ Ideas.Text.HTML.W3CSS: hide :: BuildXML a => a -> a
+ Ideas.Text.HTML.W3CSS: hideLarge :: BuildXML a => a -> a
+ Ideas.Text.HTML.W3CSS: hideMedium :: BuildXML a => a -> a
+ Ideas.Text.HTML.W3CSS: hideSmall :: BuildXML a => a -> a
+ Ideas.Text.HTML.W3CSS: hover :: BuildXML a => Color -> a -> a
+ Ideas.Text.HTML.W3CSS: hoverColor :: BuildXML a => Color -> a -> a
+ Ideas.Text.HTML.W3CSS: hoverGrayscale :: BuildXML a => a -> a
+ Ideas.Text.HTML.W3CSS: hoverNone :: BuildXML a => a -> a
+ Ideas.Text.HTML.W3CSS: hoverOpacity :: BuildXML a => a -> a
+ Ideas.Text.HTML.W3CSS: hoverOpacityOff :: BuildXML a => a -> a
+ Ideas.Text.HTML.W3CSS: hoverSepia :: BuildXML a => a -> a
+ Ideas.Text.HTML.W3CSS: hoverShadow :: BuildXML a => a -> a
+ Ideas.Text.HTML.W3CSS: hoverable :: BuildXML a => a -> a
+ Ideas.Text.HTML.W3CSS: image :: BuildXML a => a -> a
+ Ideas.Text.HTML.W3CSS: input :: BuildXML a => a -> a
+ Ideas.Text.HTML.W3CSS: instance GHC.Classes.Eq Ideas.Text.HTML.W3CSS.Position
+ Ideas.Text.HTML.W3CSS: instance GHC.Classes.Eq Ideas.Text.HTML.W3CSS.Size
+ Ideas.Text.HTML.W3CSS: instance GHC.Classes.Ord Ideas.Text.HTML.W3CSS.Size
+ Ideas.Text.HTML.W3CSS: instance GHC.Show.Show Ideas.Text.HTML.W3CSS.Color
+ Ideas.Text.HTML.W3CSS: instance GHC.Show.Show Ideas.Text.HTML.W3CSS.ColorTheme
+ Ideas.Text.HTML.W3CSS: instance GHC.Show.Show Ideas.Text.HTML.W3CSS.Position
+ Ideas.Text.HTML.W3CSS: instance GHC.Show.Show Ideas.Text.HTML.W3CSS.Size
+ Ideas.Text.HTML.W3CSS: justify :: BuildXML a => a -> a
+ Ideas.Text.HTML.W3CSS: left :: BuildXML a => a -> a
+ Ideas.Text.HTML.W3CSS: leftAlign :: BuildXML a => a -> a
+ Ideas.Text.HTML.W3CSS: mainPage :: BuildXML a => a -> a
+ Ideas.Text.HTML.W3CSS: margin :: BuildXML a => a -> a
+ Ideas.Text.HTML.W3CSS: marginPos :: BuildXML a => Position -> a -> a
+ Ideas.Text.HTML.W3CSS: mobile :: BuildXML a => a -> a
+ Ideas.Text.HTML.W3CSS: modal :: BuildXML a => a -> a
+ Ideas.Text.HTML.W3CSS: modalContent :: BuildXML a => a -> a
+ Ideas.Text.HTML.W3CSS: noBorder :: BuildXML a => a -> a
+ Ideas.Text.HTML.W3CSS: onBottom :: Position -> Bool
+ Ideas.Text.HTML.W3CSS: onLeft :: Position -> Bool
+ Ideas.Text.HTML.W3CSS: onRight :: Position -> Bool
+ Ideas.Text.HTML.W3CSS: onTop :: Position -> Bool
+ Ideas.Text.HTML.W3CSS: opacity :: BuildXML a => a -> a
+ Ideas.Text.HTML.W3CSS: opacityMax :: BuildXML a => a -> a
+ Ideas.Text.HTML.W3CSS: opacityMin :: BuildXML a => a -> a
+ Ideas.Text.HTML.W3CSS: opacityOff :: BuildXML a => a -> a
+ Ideas.Text.HTML.W3CSS: overlay :: BuildXML a => a -> a
+ Ideas.Text.HTML.W3CSS: padding :: BuildXML a => Size -> a -> a
+ Ideas.Text.HTML.W3CSS: panel :: BuildXML a => a -> a
+ Ideas.Text.HTML.W3CSS: quarter :: BuildXML a => a -> a
+ Ideas.Text.HTML.W3CSS: radio :: BuildXML a => a -> a
+ Ideas.Text.HTML.W3CSS: responsive :: BuildXML a => a -> a
+ Ideas.Text.HTML.W3CSS: rest :: BuildXML a => a -> a
+ Ideas.Text.HTML.W3CSS: right :: BuildXML a => a -> a
+ Ideas.Text.HTML.W3CSS: rightAlign :: BuildXML a => a -> a
+ Ideas.Text.HTML.W3CSS: ripple :: BuildXML a => String -> a -> a
+ Ideas.Text.HTML.W3CSS: rounded :: BuildXML a => Size -> a -> a
+ Ideas.Text.HTML.W3CSS: row :: BuildXML a => a -> a
+ Ideas.Text.HTML.W3CSS: rowPadding :: BuildXML a => a -> a
+ Ideas.Text.HTML.W3CSS: section :: BuildXML a => a -> a
+ Ideas.Text.HTML.W3CSS: select :: BuildXML a => a -> a
+ Ideas.Text.HTML.W3CSS: sepia :: BuildXML a => a -> a
+ Ideas.Text.HTML.W3CSS: sepiaMax :: BuildXML a => a -> a
+ Ideas.Text.HTML.W3CSS: sepiaMin :: BuildXML a => a -> a
+ Ideas.Text.HTML.W3CSS: serif :: BuildXML a => a -> a
+ Ideas.Text.HTML.W3CSS: showBlock :: BuildXML a => a -> a
+ Ideas.Text.HTML.W3CSS: showInlineBlock :: BuildXML a => a -> a
+ Ideas.Text.HTML.W3CSS: sidebar :: BuildXML a => a -> a
+ Ideas.Text.HTML.W3CSS: spin :: BuildXML a => a -> a
+ Ideas.Text.HTML.W3CSS: striped :: BuildXML a => a -> a
+ Ideas.Text.HTML.W3CSS: table :: BuildXML a => a -> a
+ Ideas.Text.HTML.W3CSS: tableAll :: BuildXML a => a -> a
+ Ideas.Text.HTML.W3CSS: tag :: BuildXML a => a -> a
+ Ideas.Text.HTML.W3CSS: textColor :: BuildXML a => Color -> a -> a
+ Ideas.Text.HTML.W3CSS: textTheme :: BuildXML a => a -> a
+ Ideas.Text.HTML.W3CSS: theme :: BuildXML a => ColorTheme -> a -> a
+ Ideas.Text.HTML.W3CSS: theme_ :: BuildXML a => a -> a
+ Ideas.Text.HTML.W3CSS: third :: BuildXML a => a -> a
+ Ideas.Text.HTML.W3CSS: threequarter :: BuildXML a => a -> a
+ Ideas.Text.HTML.W3CSS: tooltip :: BuildXML a => a -> a
+ Ideas.Text.HTML.W3CSS: tooltipText :: BuildXML a => a -> a
+ Ideas.Text.HTML.W3CSS: top :: BuildXML a => a -> a
+ Ideas.Text.HTML.W3CSS: transparent :: BuildXML a => a -> a
+ Ideas.Text.HTML.W3CSS: twothird :: BuildXML a => a -> a
+ Ideas.Text.HTML.W3CSS: ul :: BuildXML a => [a] -> a
+ Ideas.Text.HTML.W3CSS: ulWith :: BuildXML a => (a -> a) -> [a] -> a
+ Ideas.Text.HTML.W3CSS: uncamel :: String -> String
+ Ideas.Text.HTML.W3CSS: vpadding :: BuildXML a => Size -> a -> a
+ Ideas.Text.HTML.W3CSS: w3class :: BuildXML a => String -> a -> a
+ Ideas.Text.HTML.W3CSS: w3classIf :: BuildXML a => Bool -> String -> a -> a
+ Ideas.Text.HTML.W3CSS: w3css :: HTMLPage -> HTMLPage
+ Ideas.Text.HTML.W3CSS: wide :: BuildXML a => a -> a
+ Ideas.Text.Latex: instance Data.Semigroup.Semigroup Ideas.Text.Latex.Latex
+ Ideas.Text.MathML: MEnclose :: MathML
+ Ideas.Text.MathML: MError :: MathML
+ Ideas.Text.MathML: MFenced :: String -> String -> MathML -> MathML
+ Ideas.Text.MathML: MFrac :: MathML -> MathML -> MathML
+ Ideas.Text.MathML: MId :: String -> MathML
+ Ideas.Text.MathML: MLabeledTableRow :: MathML
+ Ideas.Text.MathML: MNumber :: String -> MathML
+ Ideas.Text.MathML: MOperator :: String -> MathML
+ Ideas.Text.MathML: MOver :: MathML
+ Ideas.Text.MathML: MPadded :: MathML
+ Ideas.Text.MathML: MPhantom :: MathML
+ Ideas.Text.MathML: MRoot :: MathML -> MathML -> MathML
+ Ideas.Text.MathML: MRow :: [MathML] -> MathML
+ Ideas.Text.MathML: MSpace :: MathML
+ Ideas.Text.MathML: MSqrt :: MathML -> MathML
+ Ideas.Text.MathML: MString :: String -> MathML
+ Ideas.Text.MathML: MStyle :: MathML
+ Ideas.Text.MathML: MSub :: MathML -> MathML -> MathML
+ Ideas.Text.MathML: MSubSup :: MathML -> MathML -> MathML -> MathML
+ Ideas.Text.MathML: MSup :: MathML -> MathML -> MathML
+ Ideas.Text.MathML: MTable :: MathML
+ Ideas.Text.MathML: MTableData :: MathML
+ Ideas.Text.MathML: MTableRow :: MathML
+ Ideas.Text.MathML: MText :: String -> MathML
+ Ideas.Text.MathML: MUnder :: MathML
+ Ideas.Text.MathML: MUnderOver :: MathML
+ Ideas.Text.MathML: data MathML
+ Ideas.Text.MathML: instance Data.Generics.Uniplate.Operations.Uniplate Ideas.Text.MathML.MathML
+ Ideas.Text.MathML: instance GHC.Classes.Eq Ideas.Text.MathML.MathML
+ Ideas.Text.MathML: instance GHC.Show.Show Ideas.Text.MathML.MathML
+ Ideas.Text.MathML: instance Ideas.Text.XML.InXML Ideas.Text.MathML.MathML
+ Ideas.Text.MathML: mathml2xml :: MathML -> XML
+ Ideas.Text.MathML: xml2mathml :: XML -> Either String MathML
+ Ideas.Text.XML: (<>) :: Semigroup a => a -> a -> a
+ Ideas.Text.XML: infixr 6 <>
+ Ideas.Text.XML: instance Data.Semigroup.Semigroup Ideas.Text.XML.XMLBuilder
+ Ideas.Text.XML: instance Data.String.IsString Ideas.Text.XML.XMLBuilder
+ Ideas.Utils.QuickCheck: instance Data.Semigroup.Semigroup (Ideas.Utils.QuickCheck.ArbGen a)
+ Ideas.Utils.TestSuite: (<>) :: Semigroup a => a -> a -> a
+ Ideas.Utils.TestSuite: infixr 6 <>
+ Ideas.Utils.TestSuite: instance Data.Semigroup.Semigroup Ideas.Utils.TestSuite.Message
+ Ideas.Utils.TestSuite: instance Data.Semigroup.Semigroup Ideas.Utils.TestSuite.Rating
+ Ideas.Utils.TestSuite: instance Data.Semigroup.Semigroup Ideas.Utils.TestSuite.Result
+ Ideas.Utils.TestSuite: instance Data.Semigroup.Semigroup Ideas.Utils.TestSuite.Status
+ Ideas.Utils.TestSuite: instance Data.Semigroup.Semigroup Ideas.Utils.TestSuite.TestSuite
- Ideas.Common.Classes: class Arrow arr => BiArrow arr where (!->) f = f <-> errBiArrow (<-!) f = errBiArrow <-> f
+ Ideas.Common.Classes: class Arrow arr => BiArrow arr
- Ideas.Common.Classes: class BiFunctor f where mapFirst = flip biMap id mapSecond = biMap id
+ Ideas.Common.Classes: class BiFunctor f
- Ideas.Common.Classes: class BoolValue a where true = fromBool True false = fromBool False fromBool b = if b then true else false
+ Ideas.Common.Classes: class BoolValue a
- Ideas.Common.Classes: class Buggy a where buggy = setBuggy True
+ Ideas.Common.Classes: class Buggy a
- Ideas.Common.Classes: class Fix a where fix f = let a = f a in a
+ Ideas.Common.Classes: class Fix a
- Ideas.Common.Classes: class Minor a where minor = setMinor True isMajor = not . isMinor
+ Ideas.Common.Classes: class Minor a
- Ideas.Common.Environment: class HasEnvironment env where deleteRef a = changeEnv (Env . delete (getId a) . envMap) insertRef r = let f b = Env . insert (getId b) b . envMap in changeEnv . f . Binding r changeRef r f env = maybe id (insertRef r . f) (r ? env) env
+ Ideas.Common.Environment: class HasEnvironment env
- Ideas.Common.Environment: class HasRefs a where getRefIds a = [getId r | Some r <- getRefs a] getRefs = sortBy cmp . nubBy eq . allRefs where cmp :: Some Ref -> Some Ref -> Ordering cmp (Some x) (Some y) = compareId (getId x) (getId y) eq a b = cmp a b == EQ
+ Ideas.Common.Environment: class HasRefs a
- Ideas.Common.Environment: class (IsTerm a, Typeable a, Show a, Read a) => Reference a where makeRef n = Ref (newId n) show readM termView typeable makeRefList n = Ref (newId n) show readM termView typeable
+ Ideas.Common.Environment: class (IsTerm a, Typeable a, Show a, Read a) => Reference a
- Ideas.Common.Exercise: NewExercise :: Id -> Status -> (String -> Either String a) -> (a -> String) -> (Context a -> Context a -> Bool) -> (Context a -> Context a -> Bool) -> Predicate a -> Predicate a -> LabeledStrategy (Context a) -> Bool -> [Rule (Context a)] -> (Rule (Context a) -> Rule (Context a) -> Ordering) -> (a -> ContextNavigator a) -> Examples a -> Maybe (QCGen -> Maybe Difficulty -> a) -> Maybe (Gen a) -> Maybe (View Term a) -> Maybe (IsTypeable a) -> Map Id (Dynamic a) -> Exercise a
+ Ideas.Common.Exercise: NewExercise :: Id -> Status -> (String -> Either String a) -> (a -> String) -> (Context a -> Context a -> Bool) -> (Context a -> Context a -> Bool) -> Predicate a -> Predicate a -> LabeledStrategy (Context a) -> Bool -> [Rule (Context a)] -> (Rule (Context a) -> Rule (Context a) -> Ordering) -> [Constraint (Context a)] -> (a -> ContextNavigator a) -> Examples a -> Maybe (QCGen -> Maybe Difficulty -> a) -> Maybe (Gen a) -> Maybe (View Term a) -> Maybe (IsTypeable a) -> Map Id (Dynamic a) -> Exercise a
- Ideas.Common.Id: class IsId a where concatId = mconcat . map newId
+ Ideas.Common.Id: class IsId a
- Ideas.Common.Predicate: class BoolValue a where true = fromBool True false = fromBool False fromBool b = if b then true else false
+ Ideas.Common.Predicate: class BoolValue a
- Ideas.Common.Rewriting.Term: class IsTerm a where toTermList = TList . map toTerm fromTermList (TList xs) = mapM fromTerm xs fromTermList _ = fail "fromTermList: not a list"
+ Ideas.Common.Rewriting.Term: class IsTerm a
- Ideas.Common.Rewriting.Term: class WithFunctions a where symbol s = function s [] getSymbol a = case getFunction a of { Just (t, []) -> return t _ -> fail "Ideas.Common.Term.getSymbol" }
+ Ideas.Common.Rewriting.Term: class WithFunctions a
- Ideas.Common.Rule.Recognizer: class Recognizable f where recognizeAll r a b = map snd $ transApply (recognizeTrans r) (a, b) recognize r a b = listToMaybe $ recognizeAll r a b recognizeTrans = unR . recognizer
+ Ideas.Common.Rule.Recognizer: class Recognizable f
- Ideas.Common.Strategy.Choice: class Choice a where choice xs = if null xs then empty else foldr1 (.|.) xs preference xs = if null xs then empty else foldr1 (./.) xs orelse xs = if null xs then empty else foldr1 (|>) xs
+ Ideas.Common.Strategy.Choice: class Choice a
- Ideas.Common.Strategy.Sequence: class Firsts s where type Elem s where {
+ Ideas.Common.Strategy.Sequence: class Firsts s where {
- Ideas.Common.Strategy.Sequence: class Sequence a where type Sym a single s = s ~> done sequence xs = if null xs then done else foldr1 (.*.) xs where {
+ Ideas.Common.Strategy.Sequence: class Sequence a where {
- Ideas.Common.Traversal.Iterator: class Iterator a where first = fixp previous final = fixp next position = pred . length . fixpl previous
+ Ideas.Common.Traversal.Iterator: class Iterator a
- Ideas.Common.Traversal.Navigator: class Navigator a where downLast = fmap (fixp right) . down childnr = pred . length . fixpl left location = toLocation . map childnr . drop 1 . reverse . fixpl up
+ Ideas.Common.Traversal.Navigator: class Navigator a
- Ideas.Common.Traversal.Utils: class Focus a where type Unfocus a focus = fromMaybe (error "no focus") . focusM focusM = Just . focus where {
+ Ideas.Common.Traversal.Utils: class Focus a where {
- Ideas.Common.Traversal.Utils: mplus :: MonadPlus m => forall a. m a -> m a -> m a
+ Ideas.Common.Traversal.Utils: mplus :: MonadPlus m => forall a. () => m a -> m a -> m a
- Ideas.Common.View: (+++) :: ArrowChoice a => a b c -> a b' c' -> a (Either b b') (Either c c')
+ Ideas.Common.View: (+++) :: ArrowChoice a => a b c -> a b' c' -> a Either b b' Either c c'
- Ideas.Common.View: (|||) :: ArrowChoice a => a b d -> a c d -> a (Either b c) d
+ Ideas.Common.View: (|||) :: ArrowChoice a => a b d -> a c d -> a Either b c d
- Ideas.Common.View: class IsMatcher f where match = runKleisli . unM . matcher matcher = makeMatcher . match
+ Ideas.Common.View: class IsMatcher f
- Ideas.Common.View: class IsMatcher f => IsView f where build f = build (toView f) toView f = makeView (match f) (build f)
+ Ideas.Common.View: class IsMatcher f => IsView f
- Ideas.Common.View: class LiftView f where liftView v = liftViewIn (v &&& identity)
+ Ideas.Common.View: class LiftView f
- Ideas.Common.View: left :: ArrowChoice a => a b c -> a (Either b d) (Either c d)
+ Ideas.Common.View: left :: ArrowChoice a => a b c -> a Either b d Either c d
- Ideas.Common.View: right :: ArrowChoice a => a b c -> a (Either d b) (Either d c)
+ Ideas.Common.View: right :: ArrowChoice a => a b c -> a Either d b Either d c
- Ideas.Encoding.LinkManager: LinkManager :: (String -> String) -> (String -> String) -> String -> String -> String -> String -> (Service -> String) -> (forall a. Exercise a -> String) -> (forall a. Exercise a -> String) -> (forall a. Exercise a -> String) -> (forall a. Exercise a -> String) -> (forall a. Exercise a -> String) -> (forall a. Exercise a -> Rule (Context a) -> String) -> (forall a. Exercise a -> String) -> (forall a. Exercise a -> Difficulty -> String) -> (forall a. State a -> String) -> (forall a. State a -> String) -> (forall a. State a -> String) -> (forall a. State a -> String) -> (forall a. State a -> String) -> LinkManager
+ Ideas.Encoding.LinkManager: LinkManager :: (String -> String) -> (String -> String) -> String -> String -> String -> String -> (Service -> String) -> (forall a. Exercise a -> String) -> (forall a. Exercise a -> String) -> (forall a. Exercise a -> String) -> (forall a. Exercise a -> String) -> (forall a. Exercise a -> String) -> (forall a. Exercise a -> String) -> (forall a. Exercise a -> Rule (Context a) -> String) -> (forall a. Exercise a -> String) -> (forall a. Exercise a -> Difficulty -> String) -> (forall a. State a -> String) -> (forall a. State a -> String) -> (forall a. State a -> String) -> (forall a. State a -> String) -> (forall a. State a -> String) -> LinkManager
- Ideas.Text.JSON: class InJSON a where listToJSON = Array . map toJSON listFromJSON (Array xs) = mapM fromJSON xs listFromJSON _ = fail "expecting an array"
+ Ideas.Text.JSON: class InJSON a
- Ideas.Text.Latex: (<>) :: Monoid m => m -> m -> m
+ Ideas.Text.Latex: (<>) :: Semigroup a => a -> a -> a
- Ideas.Text.Latex: class ToLatex a where toLatex = toLatexPrec 0 toLatexPrec = const toLatex toLatexList = brackets . commas . map toLatex
+ Ideas.Text.Latex: class ToLatex a
- Ideas.Text.XML: class Monoid a => BuildXML a where string = unescaped . escape text = string . show element s = tag s . mconcat emptyTag s = tag s mempty
+ Ideas.Text.XML: class (Semigroup a, Monoid a) => BuildXML a
- Ideas.Text.XML: class InXML a where listToXML = Element "list" [] . map (Right . toXML) listFromXML xml | name xml == "list" && null (attributes xml) = mapM fromXML (children xml) | otherwise = fail "expecting a list tag"
+ Ideas.Text.XML: class InXML a
- Ideas.Utils.Parsing: (*>) :: Applicative f => forall a b. f a -> f b -> f b
+ Ideas.Utils.Parsing: (*>) :: Applicative f => forall a b. () => f a -> f b -> f b
- Ideas.Utils.Parsing: (<$) :: Functor f => forall a b. a -> f b -> f a
+ Ideas.Utils.Parsing: (<$) :: Functor f => forall a b. () => a -> f b -> f a
- Ideas.Utils.Parsing: (<*) :: Applicative f => forall a b. f a -> f b -> f a
+ Ideas.Utils.Parsing: (<*) :: Applicative f => forall a b. () => f a -> f b -> f a
- Ideas.Utils.Parsing: (<*>) :: Applicative f => forall a b. f (a -> b) -> f a -> f b
+ Ideas.Utils.Parsing: (<*>) :: Applicative f => forall a b. () => f (a -> b) -> f a -> f b
- Ideas.Utils.Uniplate: (|*) :: Type (to -> from) to -> to -> Type from to
+ Ideas.Utils.Uniplate: (|*) :: () => Type (to -> from) to -> to -> Type from to
- Ideas.Utils.Uniplate: (|-) :: Type (item -> from) to -> item -> Type from to
+ Ideas.Utils.Uniplate: (|-) :: () => Type (item -> from) to -> item -> Type from to
- Ideas.Utils.Uniplate: (||*) :: Type ([to] -> from) to -> [to] -> Type from to
+ Ideas.Utils.Uniplate: (||*) :: () => Type ([to] -> from) to -> [to] -> Type from to
- Ideas.Utils.Uniplate: plate :: from -> Type from to
+ Ideas.Utils.Uniplate: plate :: () => from -> Type from to
- Ideas.Utils.Uniplate: rewriteM :: (Monad m, Uniplate on) => (on -> m (Maybe on)) -> on -> m on
+ Ideas.Utils.Uniplate: rewriteM :: (Monad m, Uniplate on) => (on -> m Maybe on) -> on -> m on

Files

CHANGELOG.txt view
@@ -1,3 +1,22 @@+Changelog ideas-1.6 => ideas.1.7++* replacing network.cgi package by WAI+* adding support for MathML+* adding constraints (to exercises) for constraint-based tutoring+* adding support for W3CSS (css classes and templates)+* restyling html output+* set busy timeout on database connection (when logging requests)+* changing name of logging database for v2 schema, and make v2 the default+* logging is now by default enabled: see 'logging' flag in cabal package+* improving performance of rulesinfo service+* merging duplicate attributes in xml/html+* simplifying the computation of the difference between terms+* extending xml-encoder with cases for lists, attributes and content data+* adding missing strategy combinators preference and orelse+* adding instances for type class Different+* improving Show instance for Context+* show logging enabled/disabled in html output+ Changelog ideas-1.5 => ideas.1.6  * dynamic strategy combinator that uses the current object/term
NOTICE.txt view
@@ -1,4 +1,4 @@-Copyright 2016 Ideas project team+Copyright 2018 Ideas project team     Licensed under the Apache License, Version 2.0 (the "License");    you may not use this file except in compliance with the License.@@ -35,4 +35,4 @@ CREDITS     Harrie Passier, Arthur van Leeuwen, Josje Lodder, Hieke Keuning, -   Marcell van Geest, Jordy van Dortmont+   Marcell van Geest, Jordy van Dortmont, Jorn van Wijk
ideas.cabal view
@@ -1,25 +1,25 @@ name:                   ideas-version:                1.6+version:                1.7 synopsis:               Feedback services for intelligent tutoring systems homepage:               http://ideas.cs.uu.nl/www/ description: -  Ideas (Interactive Domain-specific Exercise Assistants) is a joint research -  project between the Open University of the Netherlands and Utrecht -  University. The project's goal is to use software and compiler technology to -  build state-of-the-art components for intelligent tutoring systems (ITS) and -  learning environments. The 'ideas' software package provides a generic -  framework for constructing the expert knowledge module (also known as a -  domain reasoner) for an ITS or learning environment. Domain knowledge is -  offered as a set of feedback services that are used by external tools such -  as the digital mathematical environment (DME), MathDox, and the Math-Bridge -  system. We have developed several domain reasoners based on this framework, -  including reasoners for mathematics, linear algebra, logic, learning Haskell -  (the Ask-Elle programming tutor) and evaluating Haskell expressions, and for +  Ideas (Interactive Domain-specific Exercise Assistants) is a joint research+  project between the Open University of the Netherlands and Utrecht+  University. The project's goal is to use software and compiler technology to+  build state-of-the-art components for intelligent tutoring systems (ITS) and+  learning environments. The 'ideas' software package provides a generic+  framework for constructing the expert knowledge module (also known as a+  domain reasoner) for an ITS or learning environment. Domain knowledge is+  offered as a set of feedback services that are used by external tools such+  as the digital mathematical environment (DME), MathDox, and the Math-Bridge+  system. We have developed several domain reasoners based on this framework,+  including reasoners for mathematics, linear algebra, logic, learning Haskell+  (the Ask-Elle programming tutor) and evaluating Haskell expressions, and for   practicing communication skills (the serious game Communicate!).  category:               Education-copyright:              (c) 2016+copyright:              (c) 2018 license:                Apache-2.0 license-file:           LICENSE.txt author:                 Bastiaan Heeren, Alex Gerdes, Johan Jeuring@@ -28,7 +28,7 @@ extra-source-files:     NOTICE.txt, CHANGELOG.txt build-type:             Simple cabal-version:          >= 1.8.0.2-tested-with:            GHC == 7.10.3+tested-with:            GHC == 7.10.3, GHC == 8.0.2, GHC == 8.2.2, GHC == 8.4.2  source-repository head   type:     git@@ -36,7 +36,7 @@  flag logging   description: enable support for logging interactions to a sqlite3 database-  default: False+  default: True  -------------------------------------------------------------------------------- @@ -48,22 +48,32 @@    ghc-options:       -Wall   hs-source-dirs:    src-  Build-Depends:     +  Build-Depends:      base >= 4.8 && < 5,-     cgi >= 3001.3.0.0,+     blaze-builder >= 0.4,+     bytestring,+     case-insensitive,      QuickCheck >= 2.8,      Diff,      containers,-     directory, -     filepath, -     parsec, +     directory,+     filepath,+     http-types,+     network,+     parsec,      random,-     time, +     streaming-commons < 0.2,+     time,      uniplate, +     wai,      wl-pprint +  if !impl(ghc >= 8.0)+    Build-Depends: semigroups == 0.18.*+   Exposed-modules:     Ideas.Common.Classes+    Ideas.Common.Constraint     Ideas.Common.Context     Ideas.Common.Derivation     Ideas.Common.DerivationTree@@ -123,6 +133,7 @@     Ideas.Encoding.RulePresenter     Ideas.Encoding.RulesInfo     Ideas.Encoding.StrategyInfo+    Ideas.Main.CGI     Ideas.Main.CmdLineOptions     Ideas.Main.Default     Ideas.Main.Revision@@ -140,8 +151,11 @@     Ideas.Service.Submit     Ideas.Service.Types     Ideas.Text.HTML+    Ideas.Text.HTML.Templates+    Ideas.Text.HTML.W3CSS     Ideas.Text.JSON     Ideas.Text.Latex+    Ideas.Text.MathML     Ideas.Text.OpenMath.Dictionary.Arith1     Ideas.Text.OpenMath.Dictionary.Calculus1     Ideas.Text.OpenMath.Dictionary.Fns1@@ -172,4 +186,3 @@     Ideas.Utils.Uniplate  ---------------------------------------------------------------------------------
src/Ideas/Common/Classes.hs view
@@ -1,5 +1,5 @@ -----------------------------------------------------------------------------
--- Copyright 2016, Ideas project team. This file is distributed under the
+-- Copyright 2018, Ideas project team. This file is distributed under the
 -- terms of the Apache License 2.0. For more information, see the files
 -- "LICENSE.txt" and "NOTICE.txt", which are included in the distribution.
 -----------------------------------------------------------------------------
+ src/Ideas/Common/Constraint.hs view
@@ -0,0 +1,113 @@+-----------------------------------------------------------------------------
+-- Copyright 2018, Ideas project team. This file is distributed under the
+-- terms of the Apache License 2.0. For more information, see the files
+-- "LICENSE.txt" and "NOTICE.txt", which are included in the distribution.
+-----------------------------------------------------------------------------
+-- |
+-- Maintainer  :  bastiaan.heeren@ou.nl
+-- Stability   :  provisional
+-- Portability :  portable (depends on ghc)
+--
+-- Constraints for constraint-based tutors
+--
+-----------------------------------------------------------------------------
+
+module Ideas.Common.Constraint
+  ( Constraint, makeConstraint
+  , isRelevant, isSatisfied, isViolated, getResult
+  , Result(..), relevance
+  ) where
+
+import Control.Applicative
+import Control.Monad
+import Ideas.Common.Id
+import Ideas.Common.View
+
+---------------------------------------------------------------------------
+-- Constraint
+
+data Constraint a = C
+  { constraintId :: Id
+  , getResult    :: a -> Result ()
+  }
+
+instance Show (Constraint a) where
+  show = showId
+
+instance Eq (Constraint a) where
+  r1 == r2 = constraintId r1 == constraintId r2
+
+instance Ord (Constraint a) where
+  compare = compareId
+
+instance HasId (Constraint a) where
+  getId        = constraintId
+  changeId f r = r { constraintId = f (constraintId r) }
+
+instance LiftView Constraint where
+   liftViewIn v (C n f) = C n (maybe Irrelevant (f . fst) . match v)
+
+makeConstraint :: IsId n => n -> (a -> Result ()) -> Constraint a
+makeConstraint = C . newId
+
+--  | Relevance condition
+isRelevant :: Constraint a -> a -> Bool
+isRelevant p a =
+   case getResult p a of
+      Irrelevant -> False
+      _          -> True
+
+-- | Satisfaction condition
+isSatisfied :: Constraint a -> a -> Bool
+isSatisfied p a =
+   case getResult p a of
+      Ok _ -> True
+      _    -> False
+
+-- | Satisfaction condition
+isViolated :: Constraint a -> a -> Maybe String
+isViolated p a =
+   case getResult p a of
+      Error s -> Just s
+      _       -> Nothing
+
+---------------------------------------------------------------------------
+-- Result
+
+data Result a = Irrelevant | Error String | Ok a
+
+instance Functor Result where
+   fmap _ Irrelevant  = Irrelevant
+   fmap _ (Error msg) = Error msg
+   fmap f (Ok a)      = Ok (f a)
+
+instance Applicative Result where
+   pure = Ok
+   Irrelevant <*> _          = Irrelevant
+   Error msg  <*> _          = Error msg
+   Ok _       <*> Irrelevant = Irrelevant
+   Ok _       <*> Error msg  = Error msg
+   Ok f       <*> Ok a       = Ok (f a)
+
+instance Alternative Result where
+   empty = Error ""
+   Irrelevant <|> r       = r
+   Error msg  <|> Error _ = Error msg -- left-biased
+   Error _    <|> r       = r
+   Ok a       <|> _       = Ok a
+
+instance Monad Result where
+   return = Ok
+   fail   = Error
+   Irrelevant >>= _ = Irrelevant
+   Error msg  >>= _ = Error msg
+   Ok a       >>= f = f a
+
+instance MonadPlus Result where
+   mzero = empty
+   mplus = (<|>)
+
+-- | Turn errors into irrelevant results
+relevance :: Result a -> Result a
+relevance (Error _) = Irrelevant
+relevance r = r
src/Ideas/Common/Context.hs view
@@ -1,6 +1,6 @@ {-# LANGUAGE GADTs, RankNTypes #-}
 -----------------------------------------------------------------------------
--- Copyright 2016, Ideas project team. This file is distributed under the
+-- Copyright 2018, Ideas project team. This file is distributed under the
 -- terms of the Apache License 2.0. For more information, see the files
 -- "LICENSE.txt" and "NOTICE.txt", which are included in the distribution.
 -----------------------------------------------------------------------------
@@ -41,7 +41,7 @@ ----------------------------------------------------------
 -- Abstract data type
 
--- | Abstract data type for a context: a context stores an envrionent.
+-- | Abstract data type for a context: a context stores an environment.
 data Context a = C
    { getEnvironment :: Environment
    , getNavigator   :: ContextNavigator a
@@ -64,7 +64,7 @@    show c@(C env a) =
       let rest | noBindings env = ""
                | otherwise      = "  {" ++ show env ++ "}"
-      in maybe "??" show (currentNavigator a) ++
+      in maybe (maybe "??" show (currentT a)) show (currentNavigator a) ++
          " @ " ++ show (location c) ++ rest
 
 instance Navigator (Context a) where
src/Ideas/Common/Derivation.hs view
@@ -1,5 +1,5 @@ -----------------------------------------------------------------------------
--- Copyright 2016, Ideas project team. This file is distributed under the
+-- Copyright 2018, Ideas project team. This file is distributed under the
 -- terms of the Apache License 2.0. For more information, see the files
 -- "LICENSE.txt" and "NOTICE.txt", which are included in the distribution.
 -----------------------------------------------------------------------------
src/Ideas/Common/DerivationTree.hs view
@@ -1,5 +1,5 @@ -----------------------------------------------------------------------------
--- Copyright 2016, Ideas project team. This file is distributed under the
+-- Copyright 2018, Ideas project team. This file is distributed under the
 -- terms of the Apache License 2.0. For more information, see the files
 -- "LICENSE.txt" and "NOTICE.txt", which are included in the distribution.
 -----------------------------------------------------------------------------
src/Ideas/Common/Environment.hs view
@@ -1,6 +1,6 @@ {-# LANGUAGE ExistentialQuantification #-}
 -----------------------------------------------------------------------------
--- Copyright 2016, Ideas project team. This file is distributed under the
+-- Copyright 2018, Ideas project team. This file is distributed under the
 -- terms of the Apache License 2.0. For more information, see the files
 -- "LICENSE.txt" and "NOTICE.txt", which are included in the distribution.
 -----------------------------------------------------------------------------
@@ -28,6 +28,8 @@ import Control.Monad
 import Data.Function
 import Data.List
+import Data.Monoid hiding ((<>))
+import Data.Semigroup as Sem
 import Ideas.Common.Id
 import Ideas.Common.Rewriting.Term
 import Ideas.Common.View
@@ -127,9 +129,12 @@ instance Show Environment where
    show = intercalate ", " . map show . bindings
 
+instance Sem.Semigroup Environment where
+   a <> b = Env (envMap a `mappend` envMap b) -- left has presedence
+
 instance Monoid Environment where
-   mempty = Env mempty
-   mappend a b = Env (envMap a `mappend` envMap b) -- left has presedence
+   mempty  = Env mempty
+   mappend = (<>)
 
 instance HasRefs Environment where
    allRefs env = [ Some ref | Binding ref _ <- bindings env ]
src/Ideas/Common/Exercise.hs view
@@ -1,6 +1,6 @@ {-# LANGUAGE Rank2Types, ExistentialQuantification #-}
 -----------------------------------------------------------------------------
--- Copyright 2016, Ideas project team. This file is distributed under the
+-- Copyright 2018, Ideas project team. This file is distributed under the
 -- terms of the Apache License 2.0. For more information, see the files
 -- "LICENSE.txt" and "NOTICE.txt", which are included in the distribution.
 -----------------------------------------------------------------------------
@@ -21,7 +21,7 @@      Exercise(..), emptyExercise, makeExercise
      -- * Convenience functions
    , prettyPrinterContext, isReady, isSuitable
-   , ruleset, getRule, ruleOrderingWith
+   , ruleset, getRule, ruleOrderingWith, violations
      -- * Status
    , Status(..), isPublic, isPrivate
      -- * Examples
@@ -45,6 +45,7 @@ import Data.Maybe
 import Data.Ord
 import Ideas.Common.Classes
+import Ideas.Common.Constraint
 import Ideas.Common.Context
 import Ideas.Common.Derivation
 import Ideas.Common.Environment
@@ -110,6 +111,9 @@      -- rule can be used (e.g. feedback services onefirst and derivation; other
      -- feedback services return all possible rules).
    , ruleOrdering :: Rule (Context a) -> Rule (Context a) -> Ordering
+     -- | Constraints for constraint-based tutors. A constraint contains a
+     -- relevance condition and a satisfaction condition.
+   , constraints  :: [Constraint (Context a)]
      -- | A navigator is needed for traversing the expression and for using the
      -- traversal strategy combinators. By default, an exercise has no
      -- navigator.
@@ -164,6 +168,7 @@    , properties     = M.empty
      -- strategies and rules
    , strategy       = label "Fail" S.fail
+   , constraints    = []
    , navigation     = noNavigator
    , canBeRestarted = True
    , extraRules     = []
@@ -225,6 +230,14 @@       (Just _,  Nothing) -> LT
       (Nothing, Just _ ) -> GT
       (Nothing, Nothing) -> compareId r1 r2
+
+-- | Get all constraint violations
+violations :: Exercise a -> Context a -> [(Id, String)]
+violations ex ctx =
+   [ (getId c, msg)
+   | c <- constraints ex
+   , msg <- maybeToList (isViolated c ctx)
+   ]
 
 -----------------------------------------------------------------------------
 -- Status
src/Ideas/Common/ExerciseTests.hs view
@@ -1,5 +1,5 @@ -----------------------------------------------------------------------------
--- Copyright 2016, Ideas project team. This file is distributed under the
+-- Copyright 2018, Ideas project team. This file is distributed under the
 -- terms of the Apache License 2.0. For more information, see the files
 -- "LICENSE.txt" and "NOTICE.txt", which are included in the distribution.
 -----------------------------------------------------------------------------
src/Ideas/Common/Id.hs view
@@ -1,5 +1,5 @@ -----------------------------------------------------------------------------
--- Copyright 2016, Ideas project team. This file is distributed under the
+-- Copyright 2018, Ideas project team. This file is distributed under the
 -- terms of the Apache License 2.0. For more information, see the files
 -- "LICENSE.txt" and "NOTICE.txt", which are included in the distribution.
 -----------------------------------------------------------------------------
@@ -40,13 +40,15 @@    , Identify(..)
      -- re-export
    , module Data.Monoid
+   , (<>)
    ) where
 
 import Control.Monad
 import Data.Char
 import Data.List
-import Data.Monoid
+import Data.Monoid hiding ((<>))
 import Data.Ord
+import Data.Semigroup as Sem
 import Ideas.Common.Classes
 import Ideas.Utils.Prelude (splitsWithElem)
 import Ideas.Utils.StringRef
@@ -76,9 +78,12 @@ instance Ord Id where
    compare = comparing idRef
 
+instance Sem.Semigroup Id where
+   (<>) = ( # )
+
 instance Monoid Id where
    mempty  = emptyId
-   mappend = ( # )
+   mappend = (<>)
 
 instance Arbitrary Id where
    arbitrary = frequency
src/Ideas/Common/Library.hs view
@@ -1,5 +1,5 @@ -----------------------------------------------------------------------------
--- Copyright 2016, Ideas project team. This file is distributed under the
+-- Copyright 2018, Ideas project team. This file is distributed under the
 -- terms of the Apache License 2.0. For more information, see the files
 -- "LICENSE.txt" and "NOTICE.txt", which are included in the distribution.
 -----------------------------------------------------------------------------
@@ -19,6 +19,7 @@    ) where
 
 import Ideas.Common.Classes as Export
+import Ideas.Common.Constraint as Export hiding (Result(..))
 import Ideas.Common.Context as Export
 import Ideas.Common.Derivation as Export
 import Ideas.Common.Environment as Export
src/Ideas/Common/Predicate.hs view
@@ -1,6 +1,6 @@ {-# LANGUAGE ExistentialQuantification #-}
 -----------------------------------------------------------------------------
--- Copyright 2016, Ideas project team. This file is distributed under the
+-- Copyright 2018, Ideas project team. This file is distributed under the
 -- terms of the Apache License 2.0. For more information, see the files
 -- "LICENSE.txt" and "NOTICE.txt", which are included in the distribution.
 -----------------------------------------------------------------------------
src/Ideas/Common/Rewriting.hs view
@@ -1,5 +1,5 @@ -----------------------------------------------------------------------------
--- Copyright 2016, Ideas project team. This file is distributed under the
+-- Copyright 2018, Ideas project team. This file is distributed under the
 -- terms of the Apache License 2.0. For more information, see the files
 -- "LICENSE.txt" and "NOTICE.txt", which are included in the distribution.
 -----------------------------------------------------------------------------
src/Ideas/Common/Rewriting/AC.hs view
@@ -1,5 +1,5 @@ -----------------------------------------------------------------------------
--- Copyright 2016, Ideas project team. This file is distributed under the
+-- Copyright 2018, Ideas project team. This file is distributed under the
 -- terms of the Apache License 2.0. For more information, see the files
 -- "LICENSE.txt" and "NOTICE.txt", which are included in the distribution.
 -----------------------------------------------------------------------------
src/Ideas/Common/Rewriting/Confluence.hs view
@@ -1,5 +1,5 @@ -----------------------------------------------------------------------------
--- Copyright 2016, Ideas project team. This file is distributed under the
+-- Copyright 2018, Ideas project team. This file is distributed under the
 -- terms of the Apache License 2.0. For more information, see the files
 -- "LICENSE.txt" and "NOTICE.txt", which are included in the distribution.
 -----------------------------------------------------------------------------
@@ -16,6 +16,7 @@    , Config, defaultConfig, showTerm, complexity, termEquality
    ) where
 
+import Data.List
 import Data.Maybe
 import Ideas.Common.Id
 import Ideas.Common.Rewriting.RewriteRule
@@ -78,7 +79,7 @@ 
 criticalPairs :: [RewriteRule a] -> [(Term, Pair a, Pair a)]
 criticalPairs rs =
-   [ (a, (r1, b1), (r2, b2))
+   [ (renumber a, (r1, renumber b1), (r2, renumber b2))
    | r1       <- rs
    , r2       <- rs
    , na <- superImpose r1 r2
@@ -86,6 +87,10 @@    , let a = unfocus na
    , b1 <- rewriteTerm r1 a
    , b2 <- map unfocus (changeG (rewriteTerm r2) na)
+   , let is  = nub (concatMap metaVars [a, b1, b2])
+         sub = zip is [0..]
+         renumber (TMeta i) = TMeta (fromMaybe i (lookup i sub))
+         renumber t = descend renumber t
    ]
 
 noDiamondPairs :: Config -> [RewriteRule a] -> [(Term, Triple a, Triple a)]
@@ -133,25 +138,25 @@ 
 ----------------------------------------------------
 -- Example
-{-
-r1, r2, r3, r4, r5 :: RewriteRule SLogic
-r1 = rewriteRule "R1" $ \p q r -> p :||: (q :||: r) :~> (p :||: q) :||: r
-r2 = rewriteRule "R2" $ \p q   -> p :||: q :~> q :||: p
-r3 = rewriteRule "R3" $ \p     -> p :||: p :~> p
-r4 = rewriteRule "R4" $ \p     -> p :||: T :~> T
-r5 = rewriteRule "R5" $ \p     -> p :||: F :~> p
 
-this = [r1, r2, r3, r4, r5, r6]
-go = reportPairs $ noDiamondPairs this
+_go :: IO ()
+_go = checkConfluence [r1, r2, r3]
+ where
+   r1, r2, r3 :: RewriteRule Term
+   r1 = makeRewriteRule "a1" $ \a -> plus (TNum 0) a :~> a
+   r2 = makeRewriteRule "a2" $ \a b c -> plus a (plus b c) :~> plus (plus a b) c
+   r3 = makeRewriteRule "a3" $ \a -> plus a (TNum 0) :~> a
 
-r6 :: RewriteRule SLogic
-r6 = rewriteRule "R6" $ \p -> p :||: T :~> F
+   plus :: Term -> Term -> Term
+   plus x y = TCon (newSymbol "plus") [x, y]
 
-r1, r2, r3 :: RewriteRule Expr
-r1 = rewriteRule "a1" $ \a -> 0+a :~> a
-r2 = rewriteRule "a3" $ \a b c -> a+(b+c) :~> (a+b)+c
-r3 = rewriteRule "a2" $ \a -> a+0 :~> a
+_go2 :: IO ()
+_go2 = checkConfluence [r1,r2,r3]
+ where
+   -- example 7.7 in Baader-Nipkow
+   r1, r2,r3  :: RewriteRule Term
+   r1 = makeRewriteRule "a1" $ \x y z -> f(f(x,y),z) :~> f(x,f(y,z))
+   r2 = makeRewriteRule "a2" $ \x -> f(x,x) :~> x
+   r3 = makeRewriteRule "a3" $ \x y -> f(f(x,y),x) :~> x
 
-go = do -- putStrLn $ unlines $ map show $ criticalPairs [r1,r2]
-        checkConfluence [r1,r2,r3]
--}+   f(x,y) = TCon (newSymbol "f") [x,y]
src/Ideas/Common/Rewriting/Difference.hs view
@@ -1,5 +1,5 @@ -----------------------------------------------------------------------------
--- Copyright 2016, Ideas project team. This file is distributed under the
+-- Copyright 2018, Ideas project team. This file is distributed under the
 -- terms of the Apache License 2.0. For more information, see the files
 -- "LICENSE.txt" and "NOTICE.txt", which are included in the distribution.
 -----------------------------------------------------------------------------
@@ -13,10 +13,7 @@ --
 -----------------------------------------------------------------------------
 
-module Ideas.Common.Rewriting.Difference
-   ( difference, differenceEqual
-   , differenceWith, differenceEqualWith
-   ) where
+module Ideas.Common.Rewriting.Difference (difference, differenceWith) where
 
 import Control.Monad
 import Data.Function
@@ -24,64 +21,38 @@ import Ideas.Common.Rewriting.Term
 import Ideas.Common.View
 
-differenceWith :: View Term a -> a -> a -> Maybe (a, a)
-differenceWith = diff (\_ _ -> True)
-
-differenceEqualWith :: View Term a -> (a -> a -> Bool) -> a -> a -> Maybe (a, a)
-differenceEqualWith v eq p q = guard (eq p q) >> diff eq v p q
-
 difference :: IsTerm a => a -> a -> Maybe (a, a)
-difference = diff (\_ _ -> True) termView
-
--- | This function returns the difference, except that the
--- returned terms should be logically equivalent. Nothing can signal that
--- there is no difference, or that the terms to start with are not equivalent.
-differenceEqual :: IsTerm a => (a -> a -> Bool) -> a -> a -> Maybe (a, a)
-differenceEqual eq p q = do
-   guard (eq p q)
-   diff eq termView p q
-
-collectSym :: Symbol -> Term -> [Term]
-collectSym s a = maybe [a] (uncurry ((++) `on` collectSym s)) (isBinary s a)
+difference = differenceWith termView
 
--- local implementation function
-diff :: (a -> a -> Bool) -> View Term a -> a -> a -> Maybe (a, a)
-diff eq v a b = do
-   let eqT x y = fromMaybe False $ liftM2 eq (match v x) (match v y)
-   (t1, t2) <- diffTerm eqT (build v a) (build v b)
+differenceWith :: View Term a -> a -> a -> Maybe (a, a)
+differenceWith v a b = do
+   (t1, t2) <- diffTerm (build v a) (build v b)
    liftM2 (,) (match v t1) (match v t2)
 
-diffTerm :: (Term -> Term -> Bool) -> Term -> Term -> Maybe (Term, Term)
-diffTerm eq = rec
+-- returns a result if the terms are different
+diffTerm :: Term -> Term -> Maybe (Term, Term)
+diffTerm p q =
+   case (getFunctionA p, getFunctionA q) of
+      (Just (s1, ps), Just (s2, qs))
+         | s1 == s2  -> diffList ps qs
+         | otherwise -> here
+      _  | p == q    -> Nothing
+         | otherwise -> here
  where
-   rec p q =
-      case (getFunction p, getFunction q) of
-         (Just (s1, ps), Just (s2, qs))
-            | s1 /= s2         -> Just (p, q)
-            | isAssociative s1 -> (diffA s1 `on` collectSym s1) p q
-            | otherwise        -> diffList ps qs
-         _  | p == q           -> Nothing
-            | otherwise        -> Just (p, q)
+   here = Just (p, q)
 
    diffList xs ys
-      | length xs /= length ys = Nothing
+      | length xs /= length ys = here
       | otherwise =
-           case catMaybes (zipWith rec xs ys) of
-              [p] -> Just p
-              _   -> Nothing
+           case catMaybes (zipWith diffTerm xs ys) of
+              []    -> Nothing
+              [one] -> Just one
+              _     -> here
 
-   diffA s = curry (make . rev . f . rev . f)
-    where
-      f (p:ps, q:qs) | not (null ps || null qs) &&
-                       isNothing (rec p q) &&
-                       equal ps qs =
-         f (ps, qs)
-      f pair = pair
+getFunctionA :: (Monad m, WithFunctions a) => a -> m (Symbol, [a])
+getFunctionA a = f <$> getFunction a
+ where
+   f (s, xs) = (s, if isAssociative s then collectSym s a else xs)
 
-      equal     = eq `on` builder
-      rev       = reverse *** reverse
-      builder   = foldr1 (binary s)
-      make pair =
-         case pair of
-            ([p], [q]) -> rec p q
-            (ps, qs)   -> Just (builder ps, builder qs)+collectSym :: WithFunctions a => Symbol -> a -> [a]
+collectSym s a = maybe [a] (uncurry ((++) `on` collectSym s)) (isBinary s a)
src/Ideas/Common/Rewriting/RewriteRule.hs view
@@ -1,7 +1,7 @@ {-# LANGUAGE ExistentialQuantification, MultiParamTypeClasses,
        FunctionalDependencies, FlexibleInstances, UndecidableInstances #-}
 -----------------------------------------------------------------------------
--- Copyright 2016, Ideas project team. This file is distributed under the
+-- Copyright 2018, Ideas project team. This file is distributed under the
 -- terms of the Apache License 2.0. For more information, see the files
 -- "LICENSE.txt" and "NOTICE.txt", which are included in the distribution.
 -----------------------------------------------------------------------------
@@ -72,11 +72,42 @@ instance Different a => Different [a] where
    different = ([], [fst different])
 
+instance Different Int where
+   different = (0, 1)
+
+instance Different Integer where
+   different = (0, 1)
+
+instance Different Double where
+   different = (0, 1)
+
+instance Different Float where
+   different = (0, 1)
+
+instance Different Rational where
+   different = (0, 1)
+
+instance Different Bool where
+   different = (True, False)
+
 instance Different Char where
    different = ('a', 'b')
 
 instance Different Term where
    different = (TNum 0, TNum 1)
+
+instance (Different a, Different b) => Different (a, b) where
+   different =
+      let (a1, a2) = different
+          (b1, b2) = different
+      in ((a1, b1), (a2, b2))
+
+instance (Different a, Different b, Different c) => Different (a, b, c) where
+   different =
+      let (a1, a2) = different
+          (b1, b2) = different
+          (c1, c2) = different
+      in ((a1, b1, c1), (a2, b2, c2))
 
 class (IsTerm a, Show a) => RuleBuilder t a | t -> a where
    buildRuleSpec  :: Int -> t -> RuleSpec Term
src/Ideas/Common/Rewriting/Substitution.hs view
@@ -1,5 +1,5 @@ -----------------------------------------------------------------------------
--- Copyright 2016, Ideas project team. This file is distributed under the
+-- Copyright 2018, Ideas project team. This file is distributed under the
 -- terms of the Apache License 2.0. For more information, see the files
 -- "LICENSE.txt" and "NOTICE.txt", which are included in the distribution.
 -----------------------------------------------------------------------------
@@ -21,6 +21,8 @@ import Control.Monad
 import Data.List
 import Data.Maybe
+import Data.Monoid hiding ((<>))
+import Data.Semigroup as Sem
 import Ideas.Common.Rewriting.Term
 import Ideas.Utils.TestSuite
 import Ideas.Utils.Uniplate
@@ -35,9 +37,12 @@ newtype Substitution = S { unS :: IM.IntMap Term }
    deriving Eq
 
+instance Sem.Semigroup Substitution where
+   (<>) = (@@)
+
 instance Monoid Substitution where
    mempty  = emptySubst
-   mappend = (@@)
+   mappend = (<>)
 
 infixr 5 |->
 infixr 6 @@
src/Ideas/Common/Rewriting/Term.hs view
@@ -1,6 +1,6 @@ {-# OPTIONS -fno-warn-orphans #-}
 -----------------------------------------------------------------------------
--- Copyright 2016, Ideas project team. This file is distributed under the
+-- Copyright 2018, Ideas project team. This file is distributed under the
 -- terms of the Apache License 2.0. For more information, see the files
 -- "LICENSE.txt" and "NOTICE.txt", which are included in the distribution.
 -----------------------------------------------------------------------------
@@ -39,7 +39,7 @@ import Ideas.Common.Id
 import Ideas.Common.View
 import Ideas.Utils.Prelude (ShowString(..))
-import Ideas.Utils.QuickCheck
+import Ideas.Utils.QuickCheck hiding (function)
 import Ideas.Utils.Uniplate
 import qualified Data.IntSet as IS
 import qualified Data.Map as M
@@ -211,7 +211,7 @@    getFunction _           = fail "Ideas.Common.Rewriting.getFunction"
 
 isSymbol :: WithFunctions a => Symbol -> a -> Bool
-isSymbol s = maybe False (==s) . getSymbol
+isSymbol s = (== Just s) . getSymbol
 
 isFunction :: (WithFunctions a, Monad m) => Symbol -> a -> m [a]
 isFunction s a =
src/Ideas/Common/Rewriting/Unification.hs view
@@ -1,5 +1,5 @@ -----------------------------------------------------------------------------
--- Copyright 2016, Ideas project team. This file is distributed under the
+-- Copyright 2018, Ideas project team. This file is distributed under the
 -- terms of the Apache License 2.0. For more information, see the files
 -- "LICENSE.txt" and "NOTICE.txt", which are included in the distribution.
 -----------------------------------------------------------------------------
src/Ideas/Common/Rule.hs view
@@ -1,5 +1,5 @@ -----------------------------------------------------------------------------
--- Copyright 2016, Ideas project team. This file is distributed under the
+-- Copyright 2018, Ideas project team. This file is distributed under the
 -- terms of the Apache License 2.0. For more information, see the files
 -- "LICENSE.txt" and "NOTICE.txt", which are included in the distribution.
 -----------------------------------------------------------------------------
src/Ideas/Common/Rule/Abstract.hs view
@@ -1,6 +1,6 @@ {-# LANGUAGE MultiParamTypeClasses #-}
 -----------------------------------------------------------------------------
--- Copyright 2016, Ideas project team. This file is distributed under the
+-- Copyright 2018, Ideas project team. This file is distributed under the
 -- terms of the Apache License 2.0. For more information, see the files
 -- "LICENSE.txt" and "NOTICE.txt", which are included in the distribution.
 -----------------------------------------------------------------------------
src/Ideas/Common/Rule/Parameter.hs view
@@ -1,5 +1,5 @@ -----------------------------------------------------------------------------
--- Copyright 2016, Ideas project team. This file is distributed under the
+-- Copyright 2018, Ideas project team. This file is distributed under the
 -- terms of the Apache License 2.0. For more information, see the files
 -- "LICENSE.txt" and "NOTICE.txt", which are included in the distribution.
 -----------------------------------------------------------------------------
src/Ideas/Common/Rule/Recognizer.hs view
@@ -1,5 +1,5 @@ -----------------------------------------------------------------------------
--- Copyright 2016, Ideas project team. This file is distributed under the
+-- Copyright 2018, Ideas project team. This file is distributed under the
 -- terms of the Apache License 2.0. For more information, see the files
 -- "LICENSE.txt" and "NOTICE.txt", which are included in the distribution.
 -----------------------------------------------------------------------------
@@ -18,6 +18,8 @@    ) where
 
 import Data.Maybe
+import Data.Monoid hiding ((<>))
+import Data.Semigroup as Sem
 import Ideas.Common.Environment
 import Ideas.Common.Rule.Transformation
 import Ideas.Common.View
@@ -42,9 +44,12 @@       let f = fmap fst . match v
       in R $ makeTrans f *** makeTrans f >>> unR r
 
+instance Sem.Semigroup (Recognizer a) where
+   f <> g = R $ unR f `mappend` unR g
+
 instance Monoid (Recognizer a) where
-   mempty      = R mempty
-   mappend f g = R $ unR f `mappend` unR g
+   mempty  = R mempty
+   mappend = (<>)
 
 instance Recognizable Recognizer where
    recognizer = id
src/Ideas/Common/Rule/Transformation.hs view
@@ -1,6 +1,6 @@ {-# LANGUAGE GADTs, Rank2Types #-}
 -----------------------------------------------------------------------------
--- Copyright 2016, Ideas project team. This file is distributed under the
+-- Copyright 2018, Ideas project team. This file is distributed under the
 -- terms of the Apache License 2.0. For more information, see the files
 -- "LICENSE.txt" and "NOTICE.txt", which are included in the distribution.
 -----------------------------------------------------------------------------
@@ -35,6 +35,8 @@ import Control.Applicative
 import Control.Arrow
 import Data.Maybe
+import Data.Monoid hiding ((<>))
+import Data.Semigroup as Sem
 import Ideas.Common.Classes
 import Ideas.Common.Context
 import Ideas.Common.Environment
@@ -82,9 +84,12 @@    left f  = f :++: identity
    right f = identity :++: f
 
+instance Sem.Semigroup (Trans a b) where
+   (<>) = (<+>)
+
 instance Monoid (Trans a b) where
    mempty  = zeroArrow
-   mappend = (<+>)
+   mappend = (<>)
 
 instance Functor (Trans a) where
    fmap f t = t >>^ f
src/Ideas/Common/Strategy.hs view
@@ -1,5 +1,5 @@ -----------------------------------------------------------------------------
--- Copyright 2016, Ideas project team. This file is distributed under the
+-- Copyright 2018, Ideas project team. This file is distributed under the
 -- terms of the Apache License 2.0. For more information, see the files
 -- "LICENSE.txt" and "NOTICE.txt", which are included in the distribution.
 -----------------------------------------------------------------------------
@@ -26,7 +26,8 @@      -- ** Basic combinators
    , (.*.), (.|.), (.%.), (.@.), (!~>)
    , succeed, fail, atomic, label, inits
-   , sequence, choice, alternatives, interleave, permute
+   , sequence, choice, preference, orelse
+   , alternatives, interleave, permute
      -- ** EBNF combinators
    , many, many1, replicate, option
      -- ** Negation and greedy combinators
src/Ideas/Common/Strategy/Abstract.hs view
@@ -1,6 +1,6 @@ {-# LANGUAGE TypeFamilies, RankNTypes #-}
 -----------------------------------------------------------------------------
--- Copyright 2016, Ideas project team. This file is distributed under the
+-- Copyright 2018, Ideas project team. This file is distributed under the
 -- terms of the Apache License 2.0. For more information, see the files
 -- "LICENSE.txt" and "NOTICE.txt", which are included in the distribution.
 -----------------------------------------------------------------------------
src/Ideas/Common/Strategy/Choice.hs view
@@ -1,5 +1,5 @@ -----------------------------------------------------------------------------
--- Copyright 2016, Ideas project team. This file is distributed under the
+-- Copyright 2018, Ideas project team. This file is distributed under the
 -- terms of the Apache License 2.0. For more information, see the files
 -- "LICENSE.txt" and "NOTICE.txt", which are included in the distribution.
 -----------------------------------------------------------------------------
src/Ideas/Common/Strategy/Combinators.hs view
@@ -1,5 +1,5 @@ -----------------------------------------------------------------------------
--- Copyright 2016, Ideas project team. This file is distributed under the
+-- Copyright 2018, Ideas project team. This file is distributed under the
 -- terms of the Apache License 2.0. For more information, see the files
 -- "LICENSE.txt" and "NOTICE.txt", which are included in the distribution.
 -----------------------------------------------------------------------------
@@ -85,6 +85,14 @@ -- | Combines a list of alternative strategies
 choice :: IsStrategy f => [f a] -> Strategy a
 choice = Choice.choice . map toStrategy
+
+-- | Combines a list of strategies with left-preference
+preference :: IsStrategy f => [f a] -> Strategy a
+preference = Choice.preference . map toStrategy
+
+-- | Combines a list of strategies with left-biased choice
+orelse :: IsStrategy f => [f a] -> Strategy a
+orelse = Choice.orelse . map toStrategy
 
 -- | Merges a list of strategies (in parallel)
 interleave :: IsStrategy f => [f a] -> Strategy a
src/Ideas/Common/Strategy/Configuration.hs view
@@ -1,6 +1,6 @@ {-# LANGUAGE TypeFamilies #-}
 -----------------------------------------------------------------------------
--- Copyright 2016, Ideas project team. This file is distributed under the
+-- Copyright 2018, Ideas project team. This file is distributed under the
 -- terms of the Apache License 2.0. For more information, see the files
 -- "LICENSE.txt" and "NOTICE.txt", which are included in the distribution.
 -----------------------------------------------------------------------------
@@ -22,6 +22,8 @@    ) where
 
 import Data.Char
+import Data.Monoid hiding ((<>))
+import Data.Semigroup as Sem
 import Ideas.Common.Classes
 import Ideas.Common.Id
 import Ideas.Common.Rule
@@ -43,10 +45,13 @@ instance Show StrategyCfg where
    show (Cfg xs) = show xs
 
+instance Sem.Semigroup StrategyCfg where
+   (Cfg xs) <> (Cfg ys) = Cfg (xs ++ ys)
+
 instance Monoid StrategyCfg where
    mempty  = Cfg []
    mconcat xs = Cfg [ y | Cfg ys <- xs, y <- ys ]
-   mappend (Cfg xs) (Cfg ys) = Cfg (xs ++ ys)
+   mappend = (<>)
 
 newtype ConfigLocation = ByName Id
 
src/Ideas/Common/Strategy/CyclicTree.hs view
@@ -1,5 +1,5 @@ -----------------------------------------------------------------------------
--- Copyright 2016, Ideas project team. This file is distributed under the
+-- Copyright 2018, Ideas project team. This file is distributed under the
 -- terms of the Apache License 2.0. For more information, see the files
 -- "LICENSE.txt" and "NOTICE.txt", which are included in the distribution.
 -----------------------------------------------------------------------------
src/Ideas/Common/Strategy/Derived.hs view
@@ -1,6 +1,6 @@ {-# LANGUAGE FlexibleContexts #-}
 -----------------------------------------------------------------------------
--- Copyright 2016, Ideas project team. This file is distributed under the
+-- Copyright 2018, Ideas project team. This file is distributed under the
 -- terms of the Apache License 2.0. For more information, see the files
 -- "LICENSE.txt" and "NOTICE.txt", which are included in the distribution.
 -----------------------------------------------------------------------------
src/Ideas/Common/Strategy/Legacy.hs view
@@ -1,5 +1,5 @@ -----------------------------------------------------------------------------
--- Copyright 2016, Ideas project team. This file is distributed under the
+-- Copyright 2018, Ideas project team. This file is distributed under the
 -- terms of the Apache License 2.0. For more information, see the files
 -- "LICENSE.txt" and "NOTICE.txt", which are included in the distribution.
 -----------------------------------------------------------------------------
src/Ideas/Common/Strategy/Location.hs view
@@ -1,5 +1,5 @@ -----------------------------------------------------------------------------
--- Copyright 2016, Ideas project team. This file is distributed under the
+-- Copyright 2018, Ideas project team. This file is distributed under the
 -- terms of the Apache License 2.0. For more information, see the files
 -- "LICENSE.txt" and "NOTICE.txt", which are included in the distribution.
 -----------------------------------------------------------------------------
src/Ideas/Common/Strategy/Prefix.hs view
@@ -1,6 +1,6 @@ {-# LANGUAGE TypeFamilies #-}
 -----------------------------------------------------------------------------
--- Copyright 2016, Ideas project team. This file is distributed under the
+-- Copyright 2018, Ideas project team. This file is distributed under the
 -- terms of the Apache License 2.0. For more information, see the files
 -- "LICENSE.txt" and "NOTICE.txt", which are included in the distribution.
 -----------------------------------------------------------------------------
@@ -30,6 +30,8 @@ import Data.Char
 import Data.List (intercalate)
 import Data.Maybe
+import Data.Monoid hiding ((<>))
+import Data.Semigroup as Sem
 import Ideas.Common.Classes
 import Ideas.Common.Environment
 import Ideas.Common.Rewriting.Term
@@ -51,9 +53,12 @@ instance Show (Prefix a) where
    show = intercalate ";" . map show . prefixPaths
 
+instance Sem.Semigroup (Prefix a) where
+   (Prefix xs p) <> (Prefix ys q) = Prefix (xs ++ ys) (p .|. q)
+
 instance Monoid (Prefix a) where
-   mempty = noPrefix
-   mappend (Prefix xs p) (Prefix ys q) = Prefix (xs ++ ys) (p .|. q)
+   mempty  = noPrefix
+   mappend = (<>)
 
 instance Firsts (Prefix a) where
    type Elem (Prefix a) = (Rule a, a, Environment)
src/Ideas/Common/Strategy/Process.hs view
@@ -1,6 +1,6 @@ {-# LANGUAGE TypeFamilies #-}
 -----------------------------------------------------------------------------
--- Copyright 2016, Ideas project team. This file is distributed under the
+-- Copyright 2018, Ideas project team. This file is distributed under the
 -- terms of the Apache License 2.0. For more information, see the files
 -- "LICENSE.txt" and "NOTICE.txt", which are included in the distribution.
 -----------------------------------------------------------------------------
src/Ideas/Common/Strategy/Sequence.hs view
@@ -1,6 +1,6 @@ {-# LANGUAGE TypeFamilies #-}
 -----------------------------------------------------------------------------
--- Copyright 2016, Ideas project team. This file is distributed under the
+-- Copyright 2018, Ideas project team. This file is distributed under the
 -- terms of the Apache License 2.0. For more information, see the files
 -- "LICENSE.txt" and "NOTICE.txt", which are included in the distribution.
 -----------------------------------------------------------------------------
src/Ideas/Common/Strategy/StrategyTree.hs view
@@ -1,6 +1,6 @@ {-# LANGUAGE RankNTypes, ExistentialQuantification #-}
 -----------------------------------------------------------------------------
--- Copyright 2016, Ideas project team. This file is distributed under the
+-- Copyright 2018, Ideas project team. This file is distributed under the
 -- terms of the Apache License 2.0. For more information, see the files
 -- "LICENSE.txt" and "NOTICE.txt", which are included in the distribution.
 -----------------------------------------------------------------------------
src/Ideas/Common/Strategy/Symbol.hs view
@@ -1,5 +1,5 @@ -----------------------------------------------------------------------------
--- Copyright 2016, Ideas project team. This file is distributed under the
+-- Copyright 2018, Ideas project team. This file is distributed under the
 -- terms of the Apache License 2.0. For more information, see the files
 -- "LICENSE.txt" and "NOTICE.txt", which are included in the distribution.
 -----------------------------------------------------------------------------
src/Ideas/Common/Strategy/Traversal.hs view
@@ -1,5 +1,5 @@ -----------------------------------------------------------------------------
--- Copyright 2016, Ideas project team. This file is distributed under the
+-- Copyright 2018, Ideas project team. This file is distributed under the
 -- terms of the Apache License 2.0. For more information, see the files
 -- "LICENSE.txt" and "NOTICE.txt", which are included in the distribution.
 -----------------------------------------------------------------------------
@@ -30,7 +30,9 @@    , ruleUp, ruleDown, ruleDownLast, ruleLeft, ruleRight
    ) where
 
-import Data.Monoid
+import Data.Monoid hiding ((<>))
+import qualified Data.Semigroup as Sem
+import Data.Semigroup ((<>))
 import Ideas.Common.Classes
 import Ideas.Common.Rule
 import Ideas.Common.Strategy.Abstract
@@ -106,9 +108,12 @@ 
 newtype Option a = O { unO :: Info a -> Info a }
 
+instance Sem.Semigroup (Option a) where
+   O f <> O g = O (f . g)
+
 instance Monoid (Option a) where
-   mempty            = O id
-   O f `mappend` O g = O (f . g)
+   mempty  = O id
+   mappend = (<>)
 
 fromOptions :: [Option a] -> Info a
 fromOptions xs = unO (mconcat xs) (Info VisitOne Choice [] True False)
src/Ideas/Common/Traversal/Iterator.hs view
@@ -1,6 +1,6 @@ {-# LANGUAGE TypeFamilies #-}
 -----------------------------------------------------------------------------
--- Copyright 2016, Ideas project team. This file is distributed under the
+-- Copyright 2018, Ideas project team. This file is distributed under the
 -- terms of the Apache License 2.0. For more information, see the files
 -- "LICENSE.txt" and "NOTICE.txt", which are included in the distribution.
 -----------------------------------------------------------------------------
src/Ideas/Common/Traversal/Navigator.hs view
@@ -1,6 +1,6 @@ {-# LANGUAGE TypeFamilies, GeneralizedNewtypeDeriving #-}
 -----------------------------------------------------------------------------
--- Copyright 2016, Ideas project team. This file is distributed under the
+-- Copyright 2018, Ideas project team. This file is distributed under the
 -- terms of the Apache License 2.0. For more information, see the files
 -- "LICENSE.txt" and "NOTICE.txt", which are included in the distribution.
 -----------------------------------------------------------------------------
@@ -35,6 +35,8 @@ import Data.Function
 import Data.Generics.Str
 import Data.Maybe
+import Data.Monoid hiding ((<>))
+import Data.Semigroup as Sem
 import Ideas.Common.Traversal.Iterator
 import Ideas.Common.Traversal.Utils
 import Ideas.Utils.Uniplate
@@ -49,9 +51,12 @@ instance Show Location where
    show = show . fromLocation
 
+instance Sem.Semigroup Location where
+   L xs <> L ys = L (xs ++ ys)
+
 instance Monoid Location where
-   mempty = L []
-   L xs `mappend` L ys = L (xs ++ ys)
+   mempty  = L []
+   mappend = (<>)
 
 toLocation :: [Int] -> Location
 toLocation = L
src/Ideas/Common/Traversal/Tests.hs view
@@ -1,5 +1,5 @@ -----------------------------------------------------------------------------
--- Copyright 2016, Ideas project team. This file is distributed under the
+-- Copyright 2018, Ideas project team. This file is distributed under the
 -- terms of the Apache License 2.0. For more information, see the files
 -- "LICENSE.txt" and "NOTICE.txt", which are included in the distribution.
 -----------------------------------------------------------------------------
@@ -17,6 +17,7 @@ 
 import Control.Monad
 import Data.Maybe
+import Data.Semigroup as Sem
 import Ideas.Common.Traversal.Iterator
 import Ideas.Common.Traversal.Navigator
 import Ideas.Common.Traversal.Utils
src/Ideas/Common/Traversal/Utils.hs view
@@ -1,6 +1,6 @@ {-# LANGUAGE TypeFamilies #-}
 -----------------------------------------------------------------------------
--- Copyright 2016, Ideas project team. This file is distributed under the
+-- Copyright 2018, Ideas project team. This file is distributed under the
 -- terms of the Apache License 2.0. For more information, see the files
 -- "LICENSE.txt" and "NOTICE.txt", which are included in the distribution.
 -----------------------------------------------------------------------------
src/Ideas/Common/View.hs view
@@ -1,6 +1,6 @@ {-# LANGUAGE GADTs, GeneralizedNewtypeDeriving #-}
 -----------------------------------------------------------------------------
--- Copyright 2016, Ideas project team. This file is distributed under the
+-- Copyright 2018, Ideas project team. This file is distributed under the
 -- terms of the Apache License 2.0. For more information, see the files
 -- "LICENSE.txt" and "NOTICE.txt", which are included in the distribution.
 -----------------------------------------------------------------------------
src/Ideas/Encoding/DecoderJSON.hs view
@@ -1,6 +1,6 @@ {-# LANGUAGE GADTs #-}
 -----------------------------------------------------------------------------
--- Copyright 2016, Ideas project team. This file is distributed under the
+-- Copyright 2018, Ideas project team. This file is distributed under the
 -- terms of the Apache License 2.0. For more information, see the files
 -- "LICENSE.txt" and "NOTICE.txt", which are included in the distribution.
 -----------------------------------------------------------------------------
src/Ideas/Encoding/DecoderXML.hs view
@@ -1,6 +1,6 @@ {-# LANGUAGE GADTs #-}
 -----------------------------------------------------------------------------
--- Copyright 2016, Ideas project team. This file is distributed under the
+-- Copyright 2018, Ideas project team. This file is distributed under the
 -- terms of the Apache License 2.0. For more information, see the files
 -- "LICENSE.txt" and "NOTICE.txt", which are included in the distribution.
 -----------------------------------------------------------------------------
@@ -26,6 +26,7 @@ import Ideas.Encoding.Request
 import Ideas.Service.State
 import Ideas.Service.Types
+import Ideas.Text.MathML
 import Ideas.Text.OpenMath.Object
 import Ideas.Text.XML
 
@@ -34,6 +35,10 @@ xmlDecoder :: TypedDecoder a XML
 xmlDecoder tp =
    case tp of
+      Tag s (Const String) ->
+         decodeChild s decodeData
+       `mplus`
+         decodeAttribute s
       Tag s t
          | s == "answer" ->
               decodeChild "answer" (xmlDecoder t)
@@ -44,6 +49,12 @@          | otherwise ->
               decodeChild s (xmlDecoder t)
       Iso p t  -> from p <$> xmlDecoder t
+      List t -> do
+         x  <- xmlDecoder t
+         xs <- xmlDecoder (List t)
+         return (x:xs)
+       `mplus`
+         return []
       Pair t1 t2 -> do
          x <- xmlDecoder t1
          y <- xmlDecoder t2
@@ -67,6 +78,8 @@             Id          -> -- improve!
                            decodeChild "location" $
                               makeDecoder (newId . getData)
+            MathML      -> decodeMathML
+            String      -> decodeData
             _ -> fail $ "No support for argument type in XML: " ++ show tp
       _ -> fail $ "No support for argument type in XML: " ++ show tp
 
@@ -133,6 +146,9 @@       Just a  -> return a
       Nothing -> fail "Invalid OpenMath object for this exercise"
 
+decodeMathML :: XMLDecoder a MathML
+decodeMathML = decodeFirstChild "math" $ decoderFor fromXML
+
 decodeEnvironment :: XMLDecoder a Environment
 decodeEnvironment =
    decodeChild "context" (decoderFor $ foldM add mempty . children)
@@ -187,10 +203,31 @@    termBinding :: String -> Term -> Binding
    termBinding = makeBinding . makeRef
 
+decodeData :: XMLDecoder a String
+decodeData = split $ \xml ->
+   case content xml of
+      Left s:rest -> Right (s, xml {content = rest})
+      _           -> Left "Could not find data"
+
 decodeChild :: String -> XMLDecoder a b -> XMLDecoder a b
 decodeChild s m = split f >>= (m //)
  where
    p     = either (const False) ((==s) . name)
    f xml = case break p (content xml) of
               (xs, Right y:ys) -> Right (y, xml { content = xs ++ ys })
-              _ -> Left $ "Could not find child " ++ s+              _ -> Left $ "Could not find child " ++ s
+
+decodeFirstChild :: String -> XMLDecoder a b -> XMLDecoder a b
+decodeFirstChild s m = split f >>= (m //)
+ where
+   f xml = case content xml of
+              Right y:ys | name y == s -> Right (y, xml { content = ys })
+              _ -> Left $ "Could not find first child " ++ s
+
+decodeAttribute :: String -> XMLDecoder a String
+decodeAttribute s = split $ \xml ->
+   case break p (attributes xml) of
+      (xs, (_ := val):ys) -> Right (val, xml {attributes = xs ++ ys })
+      _ -> Left $ "Could not find attribute " ++ s
+ where
+   p (n := _) = n == s
src/Ideas/Encoding/Encoder.hs view
@@ -1,6 +1,6 @@ {-# LANGUAGE RankNTypes #-}
 -----------------------------------------------------------------------------
--- Copyright 2016, Ideas project team. This file is distributed under the
+-- Copyright 2018, Ideas project team. This file is distributed under the
 -- terms of the Apache License 2.0. For more information, see the files
 -- "LICENSE.txt" and "NOTICE.txt", which are included in the distribution.
 -----------------------------------------------------------------------------
@@ -32,13 +32,14 @@    , split, symbol, setInput
      -- re-export
    , module Export
+   , (<>)
    ) where
 
 import Control.Applicative as Export hiding (Const)
 import Control.Arrow as Export
 import Control.Monad
 import Data.Maybe
-import Data.Monoid as Export
+import Data.Semigroup as Sem
 import Ideas.Common.Library hiding (exerciseId, symbol)
 import Ideas.Encoding.Options
 import Ideas.Encoding.Request
@@ -219,9 +220,12 @@    fromOptions  f = Enc $ \(_, opts) _ -> return (f opts)
    run f ex opts  = runErrorM . runEnc f (ex, opts)
 
+instance Sem.Semigroup t => Sem.Semigroup (Encoder a s t) where
+   (<>) = liftA2 (<>)
+
 instance Monoid t => Monoid (Encoder a s t) where
    mempty  = pure mempty
-   mappend = liftA2 (<>)
+   mappend = liftA2 mappend
 
 instance BuildXML t => BuildXML (Encoder a s t) where
    n .=. s   = pure (n .=. s)
src/Ideas/Encoding/EncoderHTML.hs view
@@ -1,6 +1,6 @@-{-# LANGUAGE GADTs #-}
+{-# LANGUAGE GADTs, OverloadedStrings #-}
 -----------------------------------------------------------------------------
--- Copyright 2016, Ideas project team. This file is distributed under the
+-- Copyright 2018, Ideas project team. This file is distributed under the
 -- terms of the Apache License 2.0. For more information, see the files
 -- "LICENSE.txt" and "NOTICE.txt", which are included in the distribution.
 -----------------------------------------------------------------------------
@@ -19,10 +19,11 @@ import Data.List
 import Data.Maybe
 import Data.Ord
-import Ideas.Common.Library hiding (alternatives)
+import Ideas.Common.Library hiding (alternatives, left, right, collapse, Medium)
 import Ideas.Common.Strategy.Symbol
-import Ideas.Encoding.Encoder
+import Ideas.Encoding.Encoder hiding (left, right)
 import Ideas.Encoding.LinkManager
+import Ideas.Encoding.Logging
 import Ideas.Encoding.Request
 import Ideas.Encoding.RulePresenter
 import Ideas.Encoding.RulesInfo
@@ -32,12 +33,15 @@ import Ideas.Service.DomainReasoner
 import Ideas.Service.State
 import Ideas.Service.Types
-import Ideas.Text.HTML
+import Ideas.Text.HTML hiding (table, keyValueTable)
+import Ideas.Text.HTML.Templates
+import Ideas.Text.HTML.W3CSS hiding (tag, ul, top, table, content)
 import Ideas.Text.OpenMath.FMP
 import Ideas.Text.OpenMath.Object
-import Ideas.Text.XML
+import Ideas.Text.XML hiding (content)
 import Ideas.Utils.TestSuite
 import System.IO.Unsafe
+import qualified Ideas.Text.HTML.W3CSS as W3
 
 type HTMLEncoder a t = Encoder a t HTMLBuilder
 
@@ -50,34 +54,68 @@    makePage lm dr ex <$> encodeType lm dr
 
 makePage :: LinkManager -> DomainReasoner -> Exercise a -> HTMLBuilder -> HTMLPage
-makePage lm dr ex a =
-   addCSS (urlForCSS lm "ideas.css") $
+makePage lm dr ex body =
    (if hasLatexEncoding ex then addScript mathJaxUrl else id) $
-   htmlPage "Ideas: documentation pages" $ mconcat
-      [ divClass "page-header" $ mconcat
-           [ divClass  "ideas-logo" space
-           , divClass  "ounl-logo"  space
-           , spanClass "menuitem"   $ linkToIndex lm $ string "Index"
-           , spanClass "menuitem"   $ linkToExercises lm $ string "Exercises"
-           , spanClass "menuitem"   $ linkToServices lm  $ string "Services"
-           ]
-      , divClass "page-content" a
-      , divClass "page-footer" $
-           string (fullVersion dr)
-      ]
+   -- addCSS "http://ideas.cs.uu.nl/css/2007-chili-pepper.css" $
+   addCSS (urlForCSS lm "2007-chili-pepper.css") $
+   webpage myWebPage
  where
    mathJaxUrl =
       "https://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-MML-AM_CHTML"
 
+   myWebPage = WebPage
+      { title       = "Ideas: documentation pages"
+      , menuButtons =
+           [ Button "http://ideas.cs.uu.nl/ " (theme L1) (fontAwesome "lightbulb-o" <> tag "span" (fontSize Large " I") <> tag "span" (fontSize Medium "DEAS"))
+           , Button (urlForIndex lm)     (hover White) "Index"
+           , Button (urlForExercises lm) (hover White) $ "Exercises " <> nrBadge (length (exercises dr))
+           , Button (urlForServices lm)  (hover White) $ "Services " <> nrBadge (length (services dr))
+           ]
+      , menuStyle   = theme_ . fontSize Large
+      , iconBarsStyle = hover White . fontSize Large . theme L1
+      , sideWidth = 150
+      , sideHeader  = tag "h4" $ barItem $ bold $ textTheme "Exercise"
+      , sideButtons =
+           let mk f = Button (f lm ex) (hover Black)
+           in if getId ex == mempty then [] else
+              [ mk urlForExercise    "Information"
+              , mk urlForStrategy    "Strategy"
+              , mk urlForRules       "Rules"
+              , mk urlForConstraints "Constraints"
+              , mk urlForExamples    "Examples"
+              , mk urlForDerivations "Derivations"
+              , mk urlForTestReport  "Test report"
+              ]
+      , sideStyle = theme L3 . fontSize Medium
+      , iconCloseStyle = fontSize XL . hover Black
+      , content = body
+      , footer = italic $ string $ fullVersion dr
+      , footerStyle = theme L1
+      }
+
+nrBadge :: BuildXML a => Int -> a
+nrBadge = badge . theme L1 . fontSize Small . text
+
+table :: BuildXML a => Bool -> [[a]] -> a
+table hasHeader = tableAll . mconcat . zipWith f (hasHeader : repeat False)
+ where
+   f header = tag "tr" . mconcat . map makeCell
+    where
+      makeCell | header    = tag "th"
+               | otherwise = tag "td"
+
+keyValueTable :: BuildXML a => [(String, a)] -> a
+keyValueTable = table False . map (\(s, a) -> [string s, a])
+
 encodeType :: LinkManager -> DomainReasoner -> HTMLEncoder a (TypedValue (Type a))
 encodeType lm dr =
    (encodeIndex, tDomainReasoner) <?>
-   (exerciseHeader lm <> htmlDiagnosis lm dr, tDiagnosis) <?>
-   (exerciseHeader lm <> encodeExampleList lm, tList (tPair tDifficulty tContext)) <?>
-   (exerciseHeader lm <> htmlFirsts lm, tList (tPair tStepInfo tState)) <?>
-   (exerciseHeader lm <> htmlAllApplications lm, tList (tTuple3 tRule tLocation tState)) <?>
-   (exerciseHeader lm <> encodeDerivation lm, tDerivation (tPair tRule tEnvironment) tContext) <?>
-   (exerciseHeader lm <> encodeDerivationList lm, tList (tDerivation (tPair tRule tEnvironment) tContext)) <?>
+   (exerciseHeader (htmlDiagnosis lm dr), tDiagnosis) <?>
+   (exerciseHeader (encodeExampleList lm), tList (tPair tDifficulty tContext)) <?>
+   (exerciseHeader (htmlFirsts lm), tList (tPair tStepInfo tState)) <?>
+   (exerciseHeader (htmlAllApplications lm), tList (tTuple3 tRule tLocation tState)) <?>
+   (exerciseHeader (encodeDerivation lm), tDerivation (tPair tRule tEnvironment) tContext) <?>
+   (exerciseHeader (encodeDerivationList lm), tList (tDerivation (tPair tRule tEnvironment) tContext)) <?>
    encoderFor (\(val ::: tp) ->
         case tp of
            Iso iso t  -> encodeType lm dr // (to iso val ::: t)
@@ -89,7 +127,7 @@                             Right x -> encodeType lm dr // (x ::: t2)
            List (Const Service) -> encodeServiceList lm // val
            List (Const SomeExercise) -> encodeExerciseList lm // val
-           List (Const Rule) -> exerciseHeader lm <> encodeRuleList lm // val
+           List (Const Rule) -> exerciseHeader (encodeRuleList lm // val)
            List t -> ul [ encodeType lm dr // (x ::: t) | x <- val ]
            Const t -> encodeConst lm dr // (val ::: t)
            _ -> string $ "unknown: " ++ show tp)
@@ -98,16 +136,16 @@ encodeConst lm dr = encoderFor $ \tv@(val ::: tp) ->
    case tp of
       Service     -> encodeService // val
-      Exercise    -> exerciseHeader lm <> encodeExercise lm // val
-      Strategy    -> exerciseHeader lm <> encodeStrategy // val
+      Exercise    -> exerciseHeader (encodeExercise lm // val)
+      Strategy    -> exerciseHeader (encodeStrategy // val)
       Rule        -> encodeRule // val
-      State       -> exerciseHeader lm <> (encodeState lm dr // val)
+      State       -> exerciseHeader (encodeState lm dr // val)
       Location    -> text val
       Environment -> text val
       Term        -> text val
       Context     -> encodeContext // val
       String      -> string val
-      Result      -> exerciseHeader lm <> encodeResult lm val
+      Result      -> exerciseHeader (encodeResult lm val)
       _           -> text tv
 
 encodeContext :: HTMLEncoder a (Context a)
@@ -116,13 +154,8 @@ 
 encodeIndex :: HTMLEncoder a DomainReasoner
 encodeIndex = makeEncoder $ \dr -> mconcat
-   [ h1 $ "Domain reasoner " ++ showId dr
-   , htmlDescription dr
-   , keyValueTable
-        [ ("version", string $ fullVersion dr)
-        , ("exercises", text $ length $ exercises dr)
-        , ("services", text $ length $ services dr)
-        ]
+   [ htmlDescription "Domain reasoner" dr
+   , panel $ right $ string "Logging: " <> bool logEnabled
    , munless (null $ aliases dr) $
         h2 "Exercise aliases" <>
         table True (
@@ -185,10 +218,9 @@ 
 encodeService :: HTMLEncoder a Service
 encodeService = makeEncoder $ \srv -> mconcat
-   [ h1 $ "Service " ++ showId srv
+   [ htmlDescription "Service" srv
    , mwhen (serviceDeprecated srv) $
         para $ spanClass "warning" $ string "Warning: this service is deprecated"
-   , htmlDescription srv
    , case serviceFunction srv of
         _ ::: tp ->
            let (xs, ys) = inputOutputTypes tp
@@ -238,11 +270,12 @@           [ para $ submitStateInfo lm ex ]
    ]
  where
-   generalInfo ex = keyValueTable
-      [ ("Code",   ttText (showId ex))
+   generalInfo ex = container $ panel $ left $ keyValueTable
+      [ ("Code",   string $ showId ex)
       , ("Status", text $ status ex)
       , ("Strategy", linkToStrategy lm ex $ string (showId $ strategy ex))
       , ("Rules", text nrOfSoundRules)
+      , ("Constraints", text (length (constraints ex)))
       , ("Buggy rules", text nrOfBuggyRules)
       , ("OpenMath support", bool $ isJust $ hasTermView ex)
       , ("Restartable strategy", bool $ canBeRestarted ex)
@@ -253,27 +286,12 @@       (nrOfBuggyRules, nrOfSoundRules) =
          mapBoth length (partition isBuggy (ruleset ex))
 
-exerciseHeader :: LinkManager -> HTMLEncoder a b
-exerciseHeader lm = withExercise $ \ex -> mconcat
-   [ exerciseMenu lm
-   , h1 $ "Exercise " ++ showId ex
-   , pure $ htmlDescription ex
+exerciseHeader :: HTMLEncoder a b -> HTMLEncoder a b
+exerciseHeader body = withExercise $ \ex -> tag "div" $ mconcat
+   [ pure (htmlDescription "Exercise" ex)
+   , body
    ]
 
-exerciseMenu :: LinkManager -> HTMLEncoder a b
-exerciseMenu lm = divClass "menubox" $
-   bold (string "Exercise") <>
-   ul [ with linkToExercise    "information"
-      , with linkToStrategy "   strategy"
-      , with linkToRules       "rules"
-      , with linkToExamples    "examples"
-      , with linkToDerivations "derivations"
-      , with linkToTestReport  "test report"
-      ]
- where
-   with f s = withExercise $ \ex ->
-      pure $ f lm ex (string s)
-
 encodeStrategy :: HTMLEncoder a (Strategy (Context a))
 encodeStrategy = exerciseEncoder $ \ex s -> mconcat
    [ h2 "Strategy"
@@ -369,7 +387,7 @@                 , string "Used", string "Siblings", string "Rewrite rule"
                 ]
        used = rulesInStrategy (strategy ex)
-       f r  = [ linkToRule lm ex r $ ttText (showId r)
+       f r  = [ linkToRule lm ex r $ string $ showId r
               , text $ length $ getRefs r
               , bool $ r `elem` used
               , string $ intercalate ", " $ map show $ ruleSiblings r
@@ -386,8 +404,7 @@ 
 encodeRule :: HTMLEncoder a (Rule (Context a))
 encodeRule = exerciseEncoder $ \ex r -> mconcat
-   [ h1 $ "Rule " ++ showId r
-   , htmlDescription r
+   [ htmlDescription "Rule" r
    , let commas  = string . intercalate ", "
          idList  = commas . map showId
          refList = commas . map show . getRefIds
@@ -414,19 +431,15 @@ encodeExampleList :: LinkManager -> HTMLEncoder a [(Difficulty, Context a)]
 encodeExampleList lm = exerciseEncoder $ \ex pairs -> mconcat $
    h2 "Examples" :
-   [ h3 (s ++ " (" ++ show (length xs) ++ ")")
-       <> mconcat (map (make ex) xs)
+   [  container . third $
+       h3 (s ++ " (" ++ show (length xs) ++ ")")
+       <> W3.ulWith hoverable (map ((fontAwesome "hand-o-right" <>) . padding Small . make ex) xs)
    | (_, s, xs) <- orderedGroupsWith show fst pairs
    ]
  where
-   make ex (_, x) = para $
-      ( let st = emptyStateContext ex x
-        in spanClass "statelink" $ linkToState lm st $ external lm)
-      <> htmlContext False ex x
-
-external :: BuildXML a => LinkManager -> a
-external lm = element "img"
-   ["src" .=. urlForImage lm "external.png", "width" .=. "15"]
+   make ex (_, x) =
+      let st = emptyStateContext ex x
+      in button (escapeInURL (urlForState lm st)) (htmlContext False ex x)
 
 encodeDerivation :: LinkManager -> HTMLEncoder a (Derivation (Rule (Context a), Environment) (Context a))
 encodeDerivation lm =
@@ -448,8 +461,8 @@                 Just True -> mempty
                 _ -> spanClass "error" (string "Final term is not finished")
        forStep ((r, env1), env2) =
-          let showEnv e = munless (noBindings e) $ string $ ", " ++ show e in
-          spanClass "derivation-step" $ mconcat
+          let showEnv e = munless (noBindings e) $ string $ "," ++ show e in
+          container $ marginPos CenterLeft $ mconcat
              [ unescaped "&#8658; "
              , linkToRule lm ex r $ string $ showId r
              , showEnv env1 -- local environment
@@ -460,7 +473,7 @@ 
 htmlState :: LinkManager -> HTMLEncoder a (State a)
 htmlState lm = makeEncoder $ \state ->
-   para $ divClass "state" $
+   para $ container $ background LightGray $ para $
       stateLink lm state
       <> htmlContext True (exercise state) (stateContext state)
       <> string "ready: " <> bool (finished state)
@@ -471,14 +484,15 @@    textLines = mconcat . intersperse br . map string . lines
    f = if useDiv then divClass else spanClass
 
-   inline s = "\\(" ++ s ++ "\\)"
+   inline s = "\\(" ++ s ++ "\\)" :: String
    printer
       | hasLatexEncoding ex = inline . show . latexPrinterContext ex
       | otherwise = prettyPrinterContext ex
 
 stateLink :: LinkManager -> State a -> HTMLBuilder
 stateLink lm st =
-   spanClass "derivation-statelink" $ linkToState lm st $ external lm
+   container $ right $ linkToState lm st $
+   tag "span" $ textTheme $ fontAwesome "external-link"
 
 encodeState :: LinkManager -> DomainReasoner -> HTMLEncoder a (State a)
 encodeState lm dr =
@@ -489,18 +503,20 @@       in mconcat
          [ h2 "Feedback"
          , submitDiagnose lm state
-         , ul [ case xs of
-                   Right (hd:_) -> linkToState lm (snd hd) $ string "onefirst"
+         , tag "p" $ padding Small  $ mconcat [ case xs of
+                   Right (hd:_) -> linkToState lm (snd hd) $ serviceButton $ string "onefirst"
                    _ -> string "(no onefirst)"
-              , linkToFirsts lm state $ string $ "allfirsts (" ++ show n ++ ")"
-              , linkToApplications lm state $ string "allapplications"
-              , linkToDerivation lm state $ string "derivation"
-              , linkToMicrosteps lm state $ string "microsteps"
-              ]
+              , linkToFirsts lm state $ serviceButton $ string $ "allfirsts (" ++ show n ++ ")"
+              , linkToApplications lm state $ serviceButton $ string "allapplications"
+              , linkToDerivation lm state $ serviceButton $ string "derivation"
+              , linkToMicrosteps lm state $ serviceButton $ string "microsteps"
+           ]
          , munless (noBindings state) $
               h2 "Environment" <> text (environment state)
          , encodePrefix state (statePrefix state)
          ])
+     where
+       serviceButton = theme L1  . W3.w3class "w3-button"
 
 -- use allfirsts service of domain reasoner, instead of calling the service
 -- directly. Note that the service can be redefined (e.g. for the Ask-Elle tutor)
@@ -509,7 +525,7 @@ 
 useAllFirstsIO :: DomainReasoner -> State a -> IO (Either String [(StepInfo a, State a)])
 useAllFirstsIO dr st = do
-   srv <- findService dr (newId "allfirsts")
+   srv <- findService dr (newId ("allfirsts" :: String))
    case serviceFunction srv of
       f ::: tp -> do
          conv <- equalM tp (tState .-> tError (tList (tPair tStepInfo tState)))
@@ -547,7 +563,7 @@ 
 htmlDerivationWith :: HTMLBuilder -> (s -> HTMLBuilder) -> (t -> HTMLBuilder) -> Derivation s t -> HTMLBuilder
 htmlDerivationWith before forStep forTerm d =
-   divClass "derivation" $ mconcat $
+   container $ background LightGray $ para $ mconcat $
       before : forTerm (firstTerm d) :
          [ forStep s <> forTerm a | (_, s, a) <- triples d ]
 
@@ -599,21 +615,25 @@       Unknown _ s ->
          h2 "Unknown" <> encodeState lm dr // s
 
-htmlDescription :: HasId a => a -> HTMLBuilder
-htmlDescription a = munless (null (description a)) $
-   para $
-      bold (string "Description") <> br
-      <> spanClass "description" (string (description a))
+htmlDescription :: HasId a => String -> a -> HTMLBuilder
+htmlDescription tp a = munless (null (description a)) $
+   para $ panel $ barPos CenterLeft $ borderTheme $ background LightGray $ para $ mconcat
+      [ bold $ string $ tp ++ " " ++ showId a
+      , br
+      , italic $ string $ description a
+      ]
 
 submitForm :: HTMLBuilder -> HTMLBuilder
-submitForm this = element "form"
-   [ "name"     .=. "myform"
-   , "onsubmit" .=. "return submitTerm()"
-   , "method"   .=. "post"
-   , this
-   , element "input" ["type" .=. "text", "name" .=. "myterm"]
-   , element "input" ["type"  .=. "submit", "value" .=. "Submit"]
-   ]
+submitForm this = form . padding Tiny $ mconcat
+  [ para $ mconcat [termLabel, termInput mempty]
+  , this
+  , para $ submitBtn mempty
+  ]
+ where
+   form      = tag "form"  . ("name" .=. "myform" <>) . ("method" .=. "post" <>) . ("onsubmit" .=. "return submitTerm()" <>) . w3class "w3-container"
+   termLabel = tag "label" $ tag "b" "Input:"
+   termInput = tag "input" . ("name" .=. "myterm" <>) . ("type" .=. "text" <>) . border . W3.input
+   submitBtn = tag "input" . ("value" .=. "Submit" <>) . ("type" .=. "submit" <>) . theme L1 . W3.w3class "w3-button"
 
 -- stateinfo service
 submitStateInfo :: LinkManager -> Exercise a -> HTMLBuilder
@@ -624,7 +644,7 @@    request = "<request service='stateinfo' exerciseid='" ++ showId ex
           ++ "' encoding='html'><state><expr>\" + getTerm() + \"</expr></state></request>"
 
--- diagnose service
+-- diagnose service *here*
 submitDiagnose :: LinkManager -> State a -> HTMLBuilder
 submitDiagnose lm st = submitForm mempty <> submitRequest lm request
  where
src/Ideas/Encoding/EncoderJSON.hs view
@@ -1,6 +1,6 @@ {-# LANGUAGE GADTs #-}
 -----------------------------------------------------------------------------
--- Copyright 2016, Ideas project team. This file is distributed under the
+-- Copyright 2018, Ideas project team. This file is distributed under the
 -- terms of the Apache License 2.0. For more information, see the files
 -- "LICENSE.txt" and "NOTICE.txt", which are included in the distribution.
 -----------------------------------------------------------------------------
src/Ideas/Encoding/EncoderXML.hs view
@@ -1,6 +1,6 @@ {-# LANGUAGE GADTs #-}
 -----------------------------------------------------------------------------
--- Copyright 2016, Ideas project team. This file is distributed under the
+-- Copyright 2018, Ideas project team. This file is distributed under the
 -- terms of the Apache License 2.0. For more information, see the files
 -- "LICENSE.txt" and "NOTICE.txt", which are included in the distribution.
 -----------------------------------------------------------------------------
@@ -20,7 +20,7 @@ 
 import Data.Char
 import Data.Maybe
-import Data.Monoid
+import Data.Monoid hiding ((<>))
 import Ideas.Common.Library hiding (exerciseId, alternatives)
 import Ideas.Encoding.Encoder
 import Ideas.Encoding.OpenMathSupport
src/Ideas/Encoding/Evaluator.hs view
@@ -1,6 +1,6 @@ {-# LANGUAGE GADTs, RankNTypes #-}
 -----------------------------------------------------------------------------
--- Copyright 2016, Ideas project team. This file is distributed under the
+-- Copyright 2018, Ideas project team. This file is distributed under the
 -- terms of the Apache License 2.0. For more information, see the files
 -- "LICENSE.txt" and "NOTICE.txt", which are included in the distribution.
 -----------------------------------------------------------------------------
src/Ideas/Encoding/LinkManager.hs view
@@ -1,6 +1,6 @@ {-# LANGUAGE RankNTypes #-}
 -----------------------------------------------------------------------------
--- Copyright 2016, Ideas project team. This file is distributed under the
+-- Copyright 2018, Ideas project team. This file is distributed under the
 -- terms of the Apache License 2.0. For more information, see the files
 -- "LICENSE.txt" and "NOTICE.txt", which are included in the distribution.
 -----------------------------------------------------------------------------
@@ -25,6 +25,7 @@      -- links to state information (dynamic)
    , linkToState, linkToFirsts, linkToApplications, linkToDerivation
    , linkToMicrosteps
+   , escapeInURL
    ) where
 
 import Data.Maybe
@@ -50,6 +51,7 @@    , urlForExercise      :: forall a . Exercise a -> String
    , urlForStrategy      :: forall a . Exercise a -> String
    , urlForRules         :: forall a . Exercise a -> String
+   , urlForConstraints   :: forall a . Exercise a -> String
    , urlForExamples      :: forall a . Exercise a -> String
    , urlForDerivations   :: forall a . Exercise a -> String
    , urlForRule          :: forall a . Exercise a -> Rule (Context a) -> String
@@ -143,6 +145,7 @@    , urlForExercise    = url . exerciseRequest "exerciseinfo"
    , urlForStrategy    = url . exerciseRequest "strategyinfo"
    , urlForRules       = url . exerciseRequest "rulelist"
+   , urlForConstraints = url . exerciseRequest "constraintlist"
    , urlForTestReport  = url . exerciseRequest "testreport"
    , urlForExamples    = url . exerciseRequest "examples"
    , urlForDerivations = url . exerciseRequest "examplederivations"
src/Ideas/Encoding/Logging.hs view
@@ -1,6 +1,6 @@ {-# LANGUAGE CPP, FlexibleContexts #-}
 -----------------------------------------------------------------------------
--- Copyright 2016, Ideas project team. This file is distributed under the
+-- Copyright 2018, Ideas project team. This file is distributed under the
 -- terms of the Apache License 2.0. For more information, see the files
 -- "LICENSE.txt" and "NOTICE.txt", which are included in the distribution.
 -----------------------------------------------------------------------------
@@ -29,7 +29,7 @@ #ifdef DB
 import Data.List
 import Database.HDBC
-import Database.HDBC.Sqlite3 (connectSqlite3)
+import Database.HDBC.Sqlite3 (connectSqlite3, setBusyTimeout)
 #endif
 
 type Diff = NominalDiffTime
@@ -129,8 +129,8 @@ logEnabled = True
 logRecord schema logRef =
    case schema of
-      V1 -> connectSqlite3 "service.db"  >>= logRecordWith V1 logRef
-      V2 -> connectSqlite3 "requests.db" >>= logRecordWith V2 logRef
+      V1 -> logRecordWith "service.db"  V1 logRef
+      V2 -> logRecordWith "requests.db" V2 logRef
       NoLogging -> return ()
 #else
 -- without logging
@@ -165,8 +165,11 @@       , get errormsg, get serviceinfo, get ruleid, get input, get output
       ]
 
-logRecordWith :: IConnection c => Schema -> LogRef -> c -> IO ()
-logRecordWith schema logRef conn = do
+logRecordWith :: FilePath -> Schema -> LogRef -> IO ()
+logRecordWith file schema logRef = do
+   -- connect to database
+   conn <- connectSqlite3 file
+   setBusyTimeout conn 200 -- milliseconds
    -- calculate duration
    r   <- getRecord logRef
    end <- getCurrentTime
@@ -175,8 +178,8 @@    insertRecord schema r {responsetime = diff} conn
    -- close the connection to the database
    disconnect conn
- `catchSql` \err ->
-   putStrLn $ "Error in logging to database: " ++ show err
+ `catchSql` \_ ->
+   return ()
 
 insertRecord :: IConnection c => Schema -> Record -> c ->  IO ()
 insertRecord schema r conn =
src/Ideas/Encoding/ModeJSON.hs view
@@ -1,5 +1,5 @@ -----------------------------------------------------------------------------
--- Copyright 2016, Ideas project team. This file is distributed under the
+-- Copyright 2018, Ideas project team. This file is distributed under the
 -- terms of the Apache License 2.0. For more information, see the files
 -- "LICENSE.txt" and "NOTICE.txt", which are included in the distribution.
 -----------------------------------------------------------------------------
@@ -17,6 +17,7 @@ import Control.Monad
 import Data.Char
 import Data.Maybe
+import Data.Semigroup ((<>))
 import Ideas.Common.Library hiding (exerciseId)
 import Ideas.Encoding.DecoderJSON
 import Ideas.Encoding.EncoderJSON
src/Ideas/Encoding/ModeXML.hs view
@@ -1,5 +1,5 @@ -----------------------------------------------------------------------------
--- Copyright 2016, Ideas project team. This file is distributed under the
+-- Copyright 2018, Ideas project team. This file is distributed under the
 -- terms of the Apache License 2.0. For more information, see the files
 -- "LICENSE.txt" and "NOTICE.txt", which are included in the distribution.
 -----------------------------------------------------------------------------
@@ -38,13 +38,17 @@     `catch` handler
    let showXML | compactOutput req = compactXML
                | otherwise = show
+       showHtmlDoc doc = "<!DOCTYPE html>" ++ compactXML doc
    if htmlOutput req
-      then return (req, showXML resp, "text/html")
+      then return (req, showHtmlDoc resp, "text/html")
       else let out = addVersion (version dr) resp
            in return (req, showXML out, "application/xml")
  where
-   handler :: IOException -> IO XML
-   handler = resultError logRef . ioeGetErrorString
+   handler :: SomeException -> IO XML
+   handler e = resultError logRef $
+      case fromException e of
+         Just ioe -> ioeGetErrorString ioe
+         Nothing  -> show e
 
 addVersion :: String -> XML -> XML
 addVersion s xml =
src/Ideas/Encoding/OpenMathSupport.hs view
@@ -1,6 +1,6 @@ {-# LANGUAGE Rank2Types #-}
 -----------------------------------------------------------------------------
--- Copyright 2016, Ideas project team. This file is distributed under the
+-- Copyright 2018, Ideas project team. This file is distributed under the
 -- terms of the Apache License 2.0. For more information, see the files
 -- "LICENSE.txt" and "NOTICE.txt", which are included in the distribution.
 -----------------------------------------------------------------------------
src/Ideas/Encoding/Options.hs view
@@ -1,5 +1,5 @@ -----------------------------------------------------------------------------
--- Copyright 2016, Ideas project team. This file is distributed under the
+-- Copyright 2018, Ideas project team. This file is distributed under the
 -- terms of the Apache License 2.0. For more information, see the files
 -- "LICENSE.txt" and "NOTICE.txt", which are included in the distribution.
 -----------------------------------------------------------------------------
@@ -17,7 +17,8 @@    ) where
 
 import Control.Applicative
-import Data.Monoid
+import Data.Monoid hiding ((<>))
+import Data.Semigroup as Sem
 import Ideas.Common.Library (Exercise, getId)
 import Ideas.Encoding.Request
 import Ideas.Service.DomainReasoner
@@ -41,9 +42,8 @@    , maxTime  :: Maybe Int    -- timeout for services, in seconds
    }
 
-instance Monoid Options where
-   mempty = Options mempty Nothing mempty Nothing Nothing
-   mappend x y = Options
+instance Sem.Semigroup Options where
+   x <> y = Options
       { request  = request x <> request y
       , qcGen    = make qcGen
       , script   = script x <> script y
@@ -52,6 +52,10 @@       }
     where
       make f = f x <|> f y
+
+instance Monoid Options where
+   mempty  = Options mempty Nothing mempty Nothing Nothing
+   mappend = (<>)
 
 optionHtml :: Options
 optionHtml = mempty
src/Ideas/Encoding/Request.hs view
@@ -1,5 +1,5 @@ -----------------------------------------------------------------------------
--- Copyright 2016, Ideas project team. This file is distributed under the
+-- Copyright 2018, Ideas project team. This file is distributed under the
 -- terms of the Apache License 2.0. For more information, see the files
 -- "LICENSE.txt" and "NOTICE.txt", which are included in the distribution.
 -----------------------------------------------------------------------------
@@ -16,6 +16,8 @@ import Data.Char
 import Data.List
 import Data.Maybe
+import Data.Monoid hiding ((<>))
+import Data.Semigroup as Sem
 import Ideas.Common.Library hiding (exerciseId)
 import Ideas.Utils.Prelude
 
@@ -32,10 +34,8 @@    , encoding       :: [Encoding]
    }
 
-instance Monoid Request where
-   mempty = Request Nothing Nothing Nothing Nothing
-                    Nothing Nothing Nothing Nothing Nothing []
-   mappend x y = Request
+instance Sem.Semigroup Request where
+   x <> y = Request
       { serviceId      = make serviceId
       , exerciseId     = make exerciseId
       , source         = make source
@@ -50,10 +50,15 @@     where
       make f = f x <|> f y
 
+instance Monoid Request where
+   mempty = Request Nothing Nothing Nothing Nothing
+                    Nothing Nothing Nothing Nothing Nothing []
+   mappend = (<>)
+
 data Schema = V1 | V2 | NoLogging deriving (Show, Eq)
 
 getSchema :: Request -> Schema
-getSchema = fromMaybe V1 . logSchema -- log schema V1 is the default
+getSchema = fromMaybe V2 . logSchema -- log schema V2 is the default
 
 readSchema :: Monad m => String -> m Schema
 readSchema s0
src/Ideas/Encoding/RulePresenter.hs view
@@ -1,5 +1,5 @@ -----------------------------------------------------------------------------
--- Copyright 2016, Ideas project team. This file is distributed under the
+-- Copyright 2018, Ideas project team. This file is distributed under the
 -- terms of the Apache License 2.0. For more information, see the files
 -- "LICENSE.txt" and "NOTICE.txt", which are included in the distribution.
 -----------------------------------------------------------------------------
src/Ideas/Encoding/RulesInfo.hs view
@@ -1,5 +1,5 @@ -----------------------------------------------------------------------------
--- Copyright 2016, Ideas project team. This file is distributed under the
+-- Copyright 2018, Ideas project team. This file is distributed under the
 -- terms of the Apache License 2.0. For more information, see the files
 -- "LICENSE.txt" and "NOTICE.txt", which are included in the distribution.
 -----------------------------------------------------------------------------
@@ -25,6 +25,8 @@ rulesInfoXML :: Exercise a -> (a -> XMLBuilder) -> XMLBuilder
 rulesInfoXML ex enc = mconcat (map ruleInfoXML (ruleset ex))
  where
+   exampleMap = collectExamples ex
+
    ruleInfoXML r = element "rule"
       [ "name"        .=. showId r
       , "buggy"       .=. f (isBuggy r)
@@ -48,7 +50,7 @@                 ]
       -- Examples
       , mconcat [ element "example" [enc a, enc b]
-                | let pairs = M.findWithDefault [] (getId r) (collectExamples ex)
+                | let pairs = M.findWithDefault [] (getId r) exampleMap
                 , (a, b) <- take 3 pairs
                 ]
       ]
src/Ideas/Encoding/StrategyInfo.hs view
@@ -1,5 +1,5 @@ -----------------------------------------------------------------------------
--- Copyright 2016, Ideas project team. This file is distributed under the
+-- Copyright 2018, Ideas project team. This file is distributed under the
 -- terms of the Apache License 2.0. For more information, see the files
 -- "LICENSE.txt" and "NOTICE.txt", which are included in the distribution.
 -----------------------------------------------------------------------------
+ src/Ideas/Main/CGI.hs view
@@ -0,0 +1,167 @@+{-# LANGUAGE OverloadedStrings #-}
+-- Original source: https://hackage.haskell.org/package/wai-extra-3.0.20.0/docs/Network-Wai-Handler-CGI.html
+module Ideas.Main.CGI (run) where
+
+import Blaze.ByteString.Builder (fromByteString, toLazyByteString, flush)
+import Blaze.ByteString.Builder.Char8 (fromChar, fromString)
+import Control.Arrow ((***))
+import Control.Monad (unless, void)
+import Data.ByteString.Lazy.Internal (defaultChunkSize)
+import Data.Char (toLower)
+import Data.Function (fix)
+import Data.IORef
+import Data.Maybe (fromMaybe)
+import Network.HTTP.Types (Status (..), hRange, hContentType, hContentLength)
+import Network.Socket (getAddrInfo, addrAddress)
+import Network.Wai
+import Network.Wai.Internal
+import System.Environment (getEnvironment)
+import System.IO (Handle)
+import qualified Data.ByteString.Char8 as B
+import qualified Data.ByteString.Lazy as L
+import qualified Data.CaseInsensitive as CI
+import qualified Data.Streaming.ByteString.Builder as Blaze
+import qualified Data.String as String
+import qualified Network.HTTP.Types as H
+import qualified System.IO
+
+safeRead :: Read a => a -> String -> a
+safeRead d s =
+  case reads s of
+    ((x, _):_) -> x
+    [] -> d
+
+lookup' :: String -> [(String, String)] -> String
+lookup' key pairs = fromMaybe "" $ lookup key pairs
+
+-- | Run an application using CGI.
+run :: Application -> IO ()
+run app = do
+    vars <- getEnvironment
+    let input = requestBodyHandle System.IO.stdin
+        output = B.hPut System.IO.stdout
+    runGeneric vars input output Nothing app
+
+-- | A generic CGI helper, which allows other backends (FastCGI and SCGI) to
+-- use the same code as CGI. Most users will not need this function, and can
+-- stick with 'run' or 'runSendfile'.
+runGeneric
+     :: [(String, String)] -- ^ all variables
+     -> (Int -> IO (IO B.ByteString)) -- ^ responseBody of input
+     -> (B.ByteString -> IO ()) -- ^ destination for output
+     -> Maybe B.ByteString -- ^ does the server support the X-Sendfile header?
+     -> Application
+     -> IO ()
+runGeneric vars inputH outputH xsendfile app = do
+    let rmethod = B.pack $ lookup' "REQUEST_METHOD" vars
+        pinfo = lookup' "PATH_INFO" vars
+        qstring = lookup' "QUERY_STRING" vars
+        contentLength = safeRead 0 $ lookup' "CONTENT_LENGTH" vars
+        remoteHost' =
+            let s = fromMaybe "" (lookup "REMOTE_HOST" vars)
+            in fromMaybe s (lookup "REMOTE_ADDR" vars)
+
+        isSecure' =
+            case map toLower $ lookup' "SERVER_PROTOCOL" vars of
+                "https" -> True
+                _ -> False
+    addrs <- getAddrInfo Nothing (Just remoteHost') Nothing
+    requestBody' <- inputH contentLength
+    let addr =
+            case addrs of
+                a:_ -> addrAddress a
+                [] -> error $ "Invalid REMOTE_ADDR or REMOTE_HOST: " ++ remoteHost'
+        reqHeaders = map (cleanupVarName *** B.pack) vars
+        env = Request
+            { requestMethod = rmethod
+            , rawPathInfo = B.pack pinfo
+            , pathInfo = H.decodePathSegments $ B.pack pinfo
+            , rawQueryString = B.pack qstring
+            , queryString = H.parseQuery $ B.pack qstring
+            , requestHeaders = reqHeaders
+            , isSecure = isSecure'
+            , remoteHost = addr
+            , httpVersion = H.http11 -- FIXME
+            , requestBody = requestBody'
+            , vault = mempty
+            , requestBodyLength = KnownLength $ fromIntegral contentLength
+            , requestHeaderHost = lookup "host" reqHeaders
+            , requestHeaderRange = lookup hRange reqHeaders
+            , requestHeaderReferer = lookup "referer" reqHeaders
+            , requestHeaderUserAgent = lookup "user-agent" reqHeaders
+            }
+    void $ app env $ \res ->
+        case (xsendfile, res) of
+            (Just sf, ResponseFile s hs fp Nothing) -> do
+                mapM_ outputH $ L.toChunks $ toLazyByteString $ sfBuilder s hs sf fp
+                return ResponseReceived
+            _ -> do
+                let (s, hs, wb) = responseToStream res
+                (blazeRecv, blazeFinish) <- Blaze.newByteStringBuilderRecv Blaze.defaultStrategy
+                wb $ \b -> do
+                    let sendBuilder builder = do
+                            popper <- blazeRecv builder
+                            fix $ \loop -> do
+                                bs <- popper
+                                unless (B.null bs) $ do
+                                    outputH bs
+                                    loop
+                    sendBuilder $ headers s hs `mappend` fromChar '\n'
+                    b sendBuilder (sendBuilder flush)
+                blazeFinish >>= maybe (return ()) outputH
+                return ResponseReceived
+  where
+    headers s hs = mconcat (map header $ status s : map header' (fixHeaders hs))
+    status (Status i m) = (fromByteString "Status", mconcat
+        [ fromString $ show i
+        , fromChar ' '
+        , fromByteString m
+        ])
+    header' (x, y) = (fromByteString $ CI.original x, fromByteString y)
+    header (x, y) = mconcat
+        [ x
+        , fromByteString ": "
+        , y
+        , fromChar '\n'
+        ]
+    sfBuilder s hs sf fp = mconcat
+        [ headers s hs
+        , header (fromByteString sf, fromString fp)
+        , fromChar '\n'
+        , fromByteString sf
+        , fromByteString " not supported"
+        ]
+    fixHeaders h =
+        case lookup hContentType h of
+            Nothing -> (hContentType, "text/html; charset=utf-8") : h
+            Just _ -> h
+
+cleanupVarName :: String -> CI.CI B.ByteString
+cleanupVarName "CONTENT_TYPE" = hContentType
+cleanupVarName "CONTENT_LENGTH" = hContentLength
+cleanupVarName "SCRIPT_NAME" = "CGI-Script-Name"
+cleanupVarName s =
+    case s of
+        'H':'T':'T':'P':'_':a:as -> String.fromString $ a : helper' as
+        _ -> String.fromString s -- FIXME remove?
+  where
+    helper' ('_':x:rest) = '-' : x : helper' rest
+    helper' (x:rest) = toLower x : helper' rest
+    helper' [] = []
+
+requestBodyHandle :: Handle -> Int -> IO (IO B.ByteString)
+requestBodyHandle h = requestBodyFunc $ \i -> do
+    bs <- B.hGet h i
+    return $ if B.null bs then Nothing else Just bs
+
+requestBodyFunc :: (Int -> IO (Maybe B.ByteString)) -> Int -> IO (IO B.ByteString)
+requestBodyFunc get count0 = do
+    ref <- newIORef count0
+    return $ do
+        count <- readIORef ref
+        if count <= 0
+            then return B.empty
+            else do
+                mbs <- get $ min count defaultChunkSize
+                writeIORef ref $ count - maybe 0 B.length mbs
+                return $ fromMaybe B.empty mbs
src/Ideas/Main/CmdLineOptions.hs view
@@ -1,5 +1,5 @@ -----------------------------------------------------------------------------
--- Copyright 2016, Ideas project team. This file is distributed under the
+-- Copyright 2018, Ideas project team. This file is distributed under the
 -- terms of the Apache License 2.0. For more information, see the files
 -- "LICENSE.txt" and "NOTICE.txt", which are included in the distribution.
 -----------------------------------------------------------------------------
@@ -33,7 +33,7 @@ header :: String
 header =
    "IDEAS: Intelligent Domain-specific Exercise Assistants\n" ++
-   "Copyright 2016, Open Universiteit Nederland\n" ++
+   "Copyright 2018, Open Universiteit Nederland\n" ++
    versionText ++
    "\n\nUsage: ideas [OPTION]     (by default, CGI protocol)\n" ++
    "\nOptions:"
src/Ideas/Main/Default.hs view
@@ -1,5 +1,5 @@ -----------------------------------------------------------------------------
--- Copyright 2016, Ideas project team. This file is distributed under the
+-- Copyright 2018, Ideas project team. This file is distributed under the
 -- terms of the Apache License 2.0. For more information, see the files
 -- "LICENSE.txt" and "NOTICE.txt", which are included in the distribution.
 -----------------------------------------------------------------------------
@@ -21,7 +21,11 @@ 
 import Control.Exception
 import Control.Monad
+import Data.ByteString (ByteString, unpack)
+import Data.Char
 import Data.Maybe
+import Data.Semigroup ((<>))
+import Data.String
 import Ideas.Encoding.ModeJSON (processJSON)
 import Ideas.Encoding.ModeXML (processXML)
 import Ideas.Encoding.Options (Options, maxTime, optionCgiBin)
@@ -32,12 +36,15 @@ import Ideas.Service.ServiceList
 import Ideas.Service.Types (Service)
 import Ideas.Utils.BlackBoxTests
+import Ideas.Utils.Prelude
 import Ideas.Utils.TestSuite
-import Network.CGI
+import Network.HTTP.Types
+import Network.Wai hiding (Request)
 import System.IO
-import System.IO.Error (ioeGetErrorString)
 import qualified Ideas.Encoding.Logging as Log
+import qualified Ideas.Main.CGI as CGI
 import qualified Ideas.Main.CmdLineOptions as Options
+import qualified Network.Wai as CGI
 
 defaultMain :: DomainReasoner -> IO ()
 defaultMain = defaultMainWith mempty
@@ -51,53 +58,51 @@ 
 -- Invoked as a cgi binary
 defaultCGI :: Options -> DomainReasoner -> IO ()
-defaultCGI options dr = runCGI $ handleErrors $ do
+defaultCGI options dr = CGI.run $ \req respond -> do
    -- create a record for logging
-   logRef  <- liftIO Log.newLogRef
+   logRef  <- Log.newLogRef
    -- query environment
-   addr    <- remoteAddr       -- the IP address of the remote host
-   cgiBin  <- scriptName       -- get name of binary
-   input   <- inputOrDefault
+   let script = fromMaybe "" (findHeader "CGI-Script-Name" req) -- get name of binary
+       addr   = fromMaybe "" (findHeader "REMOTE_ADDR" req)     -- the IP address of the remote host
+   input   <- inputOrDefault req
    -- process request
-   (req, txt, ctp) <- liftIO $
-      process (options <> optionCgiBin cgiBin) dr logRef input
+   (preq, txt, ctp) <-
+      process (options <> optionCgiBin script) dr logRef input
    -- log request to database
-   when (useLogging req) $ liftIO $ do
-      Log.changeLog logRef $ \r -> Log.addRequest req r
+   when (useLogging preq) $ do
+      Log.changeLog logRef $ \r -> Log.addRequest preq r
          { Log.ipaddress = addr
          , Log.version   = shortVersion
          , Log.input     = input
          , Log.output    = txt
          }
-      Log.logRecord (getSchema req) logRef
+      Log.logRecord (getSchema preq) logRef
 
    -- write header and output
-   setHeader "Content-type" ctp
-   -- Cross-Origin Resource Sharing (CORS) prevents browser warnings
-   -- about cross-site scripting
-   setHeader "Access-Control-Allow-Origin" "*"
-   output txt
+   respond $ responseLBS
+      status200
+      [ (fromString "Content-Type", fromString ctp)
+        -- Cross-Origin Resource Sharing (CORS) prevents browser warnings
+        -- about cross-site scripting
+      , (fromString "Access-Control-Allow-Origin", fromString "*")
+      ]
+      (fromString txt)
 
-inputOrDefault :: CGI String
-inputOrDefault = do
-   inHtml <- acceptsHTML
-   ms     <- getInput "input" -- read variable 'input'
-   case ms of
+inputOrDefault :: CGI.Request -> IO String
+inputOrDefault req = do
+   maybeInput <- inputFromRequest req
+   case maybeInput of
       Just s -> return s
       Nothing
-         | inHtml    -> return defaultBrowser
-         | otherwise -> fail "environment variable 'input' is empty"
+         | acceptsHTML -> return defaultBrowser
+         | otherwise   -> fail "environment variable 'input' is empty"
  where
    -- Invoked from browser
    defaultBrowser :: String
    defaultBrowser = "<request service='index' encoding='html'/>"
 
-   acceptsHTML :: CGI Bool
-   acceptsHTML = do
-      maybeAcceptCT <- requestAccept
-      let htmlCT = ContentType "text" "html" []
-          xs = negotiate [htmlCT] maybeAcceptCT
-      return (isJust maybeAcceptCT && not (null xs))
+   acceptsHTML :: Bool
+   acceptsHTML = "text/html" `elem` accepts req
 
 -- Invoked from command-line with flags
 defaultCommandLine :: Options -> DomainReasoner -> [CmdLineOption] -> IO ()
@@ -113,7 +118,7 @@          -- process input file
          InputFile file ->
             withBinaryFile file ReadMode $ \h -> do
-               logRef <- liftIO Log.newLogRef
+               logRef <- Log.newLogRef
                input  <- hGetContents h
                (req, txt, _) <- process options dr logRef input
                putStrLn txt
@@ -139,8 +144,8 @@ process options dr logRef input = do
    format <- discoverDataFormat input
    run format options {maxTime = Just 5} (addVersion dr) logRef input
- `catch` \ioe -> do
-   let msg = "Error: " ++ ioeGetErrorString ioe
+ `catch` \e -> do
+   let msg = "Error: " ++ show (e :: SomeException)
    Log.changeLog logRef (\r -> r { Log.errormsg = msg })
    return (mempty, msg, "text/plain")
  where
@@ -158,4 +163,28 @@    , fullVersion = update fullVersion Options.fullVersion
    }
  where
-   update f s = if null (f dr) then s else f dr+   update f s = if null (f dr) then s else f dr
+
+-- local helper functions
+
+findHeader :: String -> CGI.Request -> Maybe String
+findHeader s = fmap fromByteString . lookup (fromString s) . requestHeaders
+
+inputFromRequest :: CGI.Request -> IO (Maybe String)
+inputFromRequest req =
+   -- first try query string (for GET requests) ...
+   case inputFromQuery (queryString req) of
+      Just s  -> return (Just s)
+      Nothing -> do
+         -- ... then try request body (for POST requests)
+         body <- requestBody req
+         return (inputFromQuery (parseQuery body))
+
+inputFromQuery :: Query -> Maybe String
+inputFromQuery = fmap fromByteString . join . lookup (fromString "input")
+
+accepts :: CGI.Request -> [String]
+accepts = maybe [] (splitsWithElem ',') . findHeader "Accept"
+
+fromByteString :: ByteString -> String
+fromByteString = map (chr . fromEnum) . unpack
src/Ideas/Main/Revision.hs view
@@ -1,11 +1,11 @@--- Automatically generated by Makefile.  Do not change.-module Ideas.Main.Revision where--ideasVersion :: String-ideasVersion = "1.6"--ideasRevision :: String-ideasRevision = "893c5ef99623f5741cb221bd14b52c17d133cc69"--ideasLastChanged :: String-ideasLastChanged = "Wed Dec 14 14:07:10 2016 +0100"+-- Automatically generated by Makefile.  Do not change.
+module Ideas.Main.Revision where
+
+ideasVersion :: String
+ideasVersion = "1.6.1"
+
+ideasRevision :: String
+ideasRevision = "40c006abd80b9dc73dd0f33bc4b6692d9a5b338e"
+
+ideasLastChanged :: String
+ideasLastChanged = "Fri May 18 15:48:19 2018 +0200"
src/Ideas/Service/BasicServices.hs view
@@ -1,5 +1,5 @@ -----------------------------------------------------------------------------
--- Copyright 2016, Ideas project team. This file is distributed under the
+-- Copyright 2018, Ideas project team. This file is distributed under the
 -- terms of the Apache License 2.0. For more information, see the files
 -- "LICENSE.txt" and "NOTICE.txt", which are included in the distribution.
 -----------------------------------------------------------------------------
src/Ideas/Service/Diagnose.hs view
@@ -1,6 +1,6 @@ {-# LANGUAGE FlexibleInstances, MultiParamTypeClasses #-}
 -----------------------------------------------------------------------------
--- Copyright 2016, Ideas project team. This file is distributed under the
+-- Copyright 2018, Ideas project team. This file is distributed under the
 -- terms of the Apache License 2.0. For more information, see the files
 -- "LICENSE.txt" and "NOTICE.txt", which are included in the distribution.
 -----------------------------------------------------------------------------
@@ -16,10 +16,9 @@ module Ideas.Service.Diagnose
    ( Diagnosis(..), tDiagnosis, diagnose
    , getState, getStateAndReady
-   , difference, differenceEqual
+   , difference
    ) where
 
-import Data.Function
 import Data.List (intercalate, sortBy)
 import Data.Maybe
 import Ideas.Common.Library hiding (ready)
@@ -135,10 +134,9 @@       , (_, env) <- recognizeRule ex r sub1 sub2
       ]
     where
-      diff = if searchForBuggy then difference else differenceEqual
       (sub1, sub2) = fromMaybe (stateContext state, new) $ do
          newTerm <- fromContext new
-         (a, b)  <- diff ex (stateTerm state) newTerm
+         (a, b)  <- difference ex (stateTerm state) newTerm
          return (inContext ex a, inContext ex b)
 
 ----------------------------------------------------------------
@@ -175,13 +173,6 @@ difference ex a b = do
    v <- hasTermView ex
    Diff.differenceWith v a b
-
--- Used by the FP tutor
-differenceEqual :: Exercise a -> a -> a -> Maybe (a, a)
-differenceEqual ex a b = do
-   v <- hasTermView ex
-   let simpleEq = equivalence ex `on` inContext ex
-   Diff.differenceEqualWith v simpleEq a b
 
 ----------------------------------------------------------------
 -- Compare answer sets (and search for missing parts/incorrect parts)
src/Ideas/Service/DomainReasoner.hs view
@@ -1,5 +1,5 @@ -----------------------------------------------------------------------------
--- Copyright 2016, Ideas project team. This file is distributed under the
+-- Copyright 2018, Ideas project team. This file is distributed under the
 -- terms of the Apache License 2.0. For more information, see the files
 -- "LICENSE.txt" and "NOTICE.txt", which are included in the distribution.
 -----------------------------------------------------------------------------
@@ -19,7 +19,8 @@ 
 import Data.List
 import Data.Maybe
-import Data.Monoid
+import Data.Monoid hiding ((<>))
+import Data.Semigroup as Sem
 import Data.Ord
 import Ideas.Common.Library
 import Ideas.Service.FeedbackScript.Parser
@@ -41,9 +42,8 @@    , fullVersion :: String
    }
 
-instance Monoid DomainReasoner where
-   mempty = DR mempty mempty mempty mempty mempty mempty mempty mempty mempty
-   mappend c1 c2 = DR
+instance Sem.Semigroup DomainReasoner where
+   c1 <> c2 = DR
       { reasonerId  = reasonerId c1  <> reasonerId c2
       , exercises   = exercises c1   <> exercises c2
       , services    = services c1    <> services c2
@@ -54,6 +54,10 @@       , version     = version c1     <> version c2
       , fullVersion = fullVersion c1 <> fullVersion c2
       }
+
+instance Monoid DomainReasoner where
+   mempty  = DR mempty mempty mempty mempty mempty mempty mempty mempty mempty
+   mappend = (<>)
 
 instance HasId DomainReasoner where
    getId = reasonerId
src/Ideas/Service/FeedbackScript/Analysis.hs view
@@ -1,5 +1,5 @@ -----------------------------------------------------------------------------
--- Copyright 2016, Ideas project team. This file is distributed under the
+-- Copyright 2018, Ideas project team. This file is distributed under the
 -- terms of the Apache License 2.0. For more information, see the files
 -- "LICENSE.txt" and "NOTICE.txt", which are included in the distribution.
 -----------------------------------------------------------------------------
src/Ideas/Service/FeedbackScript/Parser.hs view
@@ -1,5 +1,5 @@ -----------------------------------------------------------------------------
--- Copyright 2016, Ideas project team. This file is distributed under the
+-- Copyright 2018, Ideas project team. This file is distributed under the
 -- terms of the Apache License 2.0. For more information, see the files
 -- "LICENSE.txt" and "NOTICE.txt", which are included in the distribution.
 -----------------------------------------------------------------------------
@@ -20,7 +20,8 @@ import Control.Monad
 import Data.Char
 import Data.List
-import Data.Monoid
+import Data.Monoid hiding ((<>))
+import Data.Semigroup
 import Ideas.Common.Id
 import Ideas.Service.FeedbackScript.Syntax
 import Ideas.Utils.Parsing
src/Ideas/Service/FeedbackScript/Run.hs view
@@ -1,5 +1,5 @@ -----------------------------------------------------------------------------
--- Copyright 2016, Ideas project team. This file is distributed under the
+-- Copyright 2018, Ideas project team. This file is distributed under the
 -- terms of the Apache License 2.0. For more information, see the files
 -- "LICENSE.txt" and "NOTICE.txt", which are included in the distribution.
 -----------------------------------------------------------------------------
src/Ideas/Service/FeedbackScript/Syntax.hs view
@@ -1,5 +1,5 @@ -----------------------------------------------------------------------------
--- Copyright 2016, Ideas project team. This file is distributed under the
+-- Copyright 2018, Ideas project team. This file is distributed under the
 -- terms of the Apache License 2.0. For more information, see the files
 -- "LICENSE.txt" and "NOTICE.txt", which are included in the distribution.
 -----------------------------------------------------------------------------
@@ -21,6 +21,8 @@ import Data.Char
 import Data.List
 import Data.Maybe
+import Data.Monoid hiding ((<>))
+import Data.Semigroup as Sem
 import Ideas.Common.Library
 import Ideas.Utils.Uniplate
 
@@ -102,13 +104,19 @@    showList xs ys =
       foldr (combine . show) ys (concatMap textItems xs)
 
+instance Sem.Semigroup Script where
+   s <> t = makeScript (scriptDecls s ++ scriptDecls t)
+
 instance Monoid Script where
-   mempty = makeScript []
-   mappend s t = makeScript (scriptDecls s ++ scriptDecls t)
+   mempty  = makeScript []
+   mappend = (<>)
 
+instance Sem.Semigroup Text where
+   (<>) = (:<>:)
+
 instance Monoid Text where
    mempty  = TextEmpty
-   mappend = (:<>:)
+   mappend = (<>)
 
 instance Uniplate Condition where
    uniplate (CondNot a) = plate CondNot |* a
src/Ideas/Service/FeedbackText.hs view
@@ -1,6 +1,6 @@ {-# LANGUAGE FlexibleInstances, MultiParamTypeClasses, UndecidableInstances #-}
 -----------------------------------------------------------------------------
--- Copyright 2016, Ideas project team. This file is distributed under the
+-- Copyright 2018, Ideas project team. This file is distributed under the
 -- terms of the Apache License 2.0. For more information, see the files
 -- "LICENSE.txt" and "NOTICE.txt", which are included in the distribution.
 -----------------------------------------------------------------------------
src/Ideas/Service/ProblemDecomposition.hs view
@@ -1,6 +1,6 @@ {-# LANGUAGE FlexibleInstances, MultiParamTypeClasses, TypeFamilies #-}
 -----------------------------------------------------------------------------
--- Copyright 2016, Ideas project team. This file is distributed under the
+-- Copyright 2018, Ideas project team. This file is distributed under the
 -- terms of the Apache License 2.0. For more information, see the files
 -- "LICENSE.txt" and "NOTICE.txt", which are included in the distribution.
 -----------------------------------------------------------------------------
src/Ideas/Service/ServiceList.hs view
@@ -1,5 +1,5 @@ -----------------------------------------------------------------------------
--- Copyright 2016, Ideas project team. This file is distributed under the
+-- Copyright 2018, Ideas project team. This file is distributed under the
 -- terms of the Apache License 2.0. For more information, see the files
 -- "LICENSE.txt" and "NOTICE.txt", which are included in the distribution.
 -----------------------------------------------------------------------------
@@ -46,7 +46,7 @@ metaServiceList dr =
    [ indexS dr, servicelistS dr, serviceinfoS dr, exerciselistS dr
    , rulelistS, ruleinfoS, rulesinfoS, strategyinfoS, exerciseinfoS
-   , stateinfoS, examplederivationsS, testreportS, logS
+   , constraintlistS, stateinfoS, examplederivationsS, testreportS, logS
    ]
 
 ------------------------------------------------------
@@ -305,6 +305,11 @@ exerciseinfoS = makeService "meta.exerciseinfo"
    "Exercise information" $
    id ::: tExercise .-> tExercise
+
+constraintlistS :: Service
+constraintlistS = makeService "meta.constraintlist"
+   "Returns list of constraints" $
+   constraints ::: tExercise .-> tList tConstraint
 
 stateinfoS :: Service
 stateinfoS = makeService "meta.stateinfo"
src/Ideas/Service/State.hs view
@@ -1,6 +1,6 @@ {-# LANGUAGE TypeFamilies #-}
 -----------------------------------------------------------------------------
--- Copyright 2016, Ideas project team. This file is distributed under the
+-- Copyright 2018, Ideas project team. This file is distributed under the
 -- terms of the Apache License 2.0. For more information, see the files
 -- "LICENSE.txt" and "NOTICE.txt", which are included in the distribution.
 -----------------------------------------------------------------------------
src/Ideas/Service/Submit.hs view
@@ -1,6 +1,6 @@ {-# LANGUAGE FlexibleInstances, MultiParamTypeClasses #-}
 -----------------------------------------------------------------------------
--- Copyright 2016, Ideas project team. This file is distributed under the
+-- Copyright 2018, Ideas project team. This file is distributed under the
 -- terms of the Apache License 2.0. For more information, see the files
 -- "LICENSE.txt" and "NOTICE.txt", which are included in the distribution.
 -----------------------------------------------------------------------------
src/Ideas/Service/Types.hs view
@@ -1,6 +1,6 @@ {-# LANGUAGE GADTs, RankNTypes, FlexibleInstances, FlexibleContexts #-}
 -----------------------------------------------------------------------------
--- Copyright 2016, Ideas project team. This file is distributed under the
+-- Copyright 2018, Ideas project team. This file is distributed under the
 -- terms of the Apache License 2.0. For more information, see the files
 -- "LICENSE.txt" and "NOTICE.txt", which are included in the distribution.
 -----------------------------------------------------------------------------
@@ -19,11 +19,12 @@    , TypeRep(..), Const(..), Type, TypedValue(..)
    , Equal(..), ShowF(..), equalM
      -- * Constructing types
-   , tEnvironment, tLocation, tRule, tUnit, tTuple3, tTuple4, tTuple5, tPair
+   , tEnvironment, tLocation, tRule, tConstraint
+   , tUnit, tTuple3, tTuple4, tTuple5, tPair
    , tTerm, tStrategy, tTree, tState, tBool, tMaybe, tString, tList
    , tId, tService, tSomeExercise, tText, tDifficulty, tUserId ,tContext
    , tDerivation, tError, (.->), tIO, tExercise, tTestSuiteResult, tQCGen
-   , tScript, tExamples, tStrategyCfg, tInt
+   , tScript, tExamples, tStrategyCfg, tMathML, tInt
      -- * Searching a typed value
    , findValuesOfType
    ) where
@@ -36,6 +37,7 @@ import Ideas.Common.Library
 import Ideas.Service.FeedbackScript.Syntax
 import Ideas.Service.State
+import Ideas.Text.MathML
 import Test.QuickCheck.Random (QCGen)
 import qualified Ideas.Utils.TestSuite as TestSuite
 
@@ -85,11 +87,13 @@    equal Int         Int         = Just id
    equal Bool        Bool        = Just id
    equal String      String      = Just id
+   equal MathML      MathML      = Just id
    equal Service     Service     = Just id
    equal Exercise    Exercise    = Just id
    equal Strategy    Strategy    = Just id
    equal State       State       = Just id
-   equal Rule         Rule       = Just id
+   equal Rule        Rule        = Just id
+   equal Constraint  Constraint  = Just id
    equal Context     Context     = Just id
    equal Id          Id          = Just id
    equal Location    Location    = Just id
@@ -140,6 +144,7 @@    Strategy     :: Const a (Strategy (Context a))
    State        :: Const a (State a)
    Rule         :: Const a (Rule (Context a))
+   Constraint   :: Const a (Constraint (Context a))
    Context      :: Const a (Context a)
    -- other types
    Id           :: Const a Id
@@ -152,6 +157,7 @@    QCGen        :: Const a QCGen
    Result       :: Const a TestSuite.Result
    SomeExercise :: Const a (Some Exercise)
+   MathML       :: Const a MathML
    -- basic types
    Bool         :: Const a Bool
    Int          :: Const a Int
@@ -198,6 +204,7 @@          Exercise         -> showId val
          Strategy         -> show val
          Rule             -> showId val
+         Constraint       -> showId val
          Id               -> showId val
          SomeExercise     -> case val of Some ex -> showId ex
          State            -> show val
@@ -210,6 +217,7 @@          Text             -> show val
          QCGen            -> show val
          Result           -> show val
+         MathML           -> show val
          Bool             -> map toLower (show val)
          Int              -> show val
          String           -> val
@@ -223,6 +231,7 @@    showF Strategy     = "Strategy"
    showF State        = "State"
    showF Rule         = "Rule"
+   showF Constraint   = "Constraint"
    showF Context      = "Context"
    showF Id           = "Id"
    showF Location     = "Location"
@@ -234,6 +243,7 @@    showF QCGen        = "QCGen"
    showF Result       = "TestSuiteResult"
    showF SomeExercise = "Exercise"
+   showF MathML       = "MathML"
    showF Bool         = "Bool"
    showF Int          = "Int"
    showF String       = "String"
@@ -299,6 +309,9 @@ tContext :: Type a (Context a)
 tContext = Const Context
 
+tMathML :: Type a MathML
+tMathML = Const MathML
+
 tBool :: Type a Bool
 tBool = Const Bool
 
@@ -307,6 +320,9 @@ 
 tRule :: Type a (Rule (Context a))
 tRule = Const Rule
+
+tConstraint :: Type a (Constraint (Context a))
+tConstraint = Const Constraint
 
 tLocation :: Type a Location
 tLocation = Const Location
src/Ideas/Text/HTML.hs view
@@ -1,5 +1,5 @@ -----------------------------------------------------------------------------
--- Copyright 2016, Ideas project team. This file is distributed under the
+-- Copyright 2018, Ideas project team. This file is distributed under the
 -- terms of the Apache License 2.0. For more information, see the files
 -- "LICENSE.txt" and "NOTICE.txt", which are included in the distribution.
 -----------------------------------------------------------------------------
@@ -15,7 +15,7 @@ 
 module Ideas.Text.HTML
    ( HTMLPage, HTMLBuilder
-   , addCSS, addScript, showHTML
+   , addCSS, addScript, addStyle, showHTML
    , string, text
    , htmlPage, link
    , h1, h2, h3, h4, h5, h6
@@ -32,7 +32,7 @@ 
 import Data.Char
 import Data.List
-import Data.Monoid
+import Data.Monoid hiding ((<>))
 import Ideas.Text.XML
 import Prelude hiding (div)
 import qualified Ideas.Text.XML as XML
@@ -43,6 +43,7 @@    { title       :: String
    , styleSheets :: [FilePath]
    , scripts     :: [FilePath]
+   , styleTxts   :: [String]
    , htmlContent :: HTMLBuilder
    }
 
@@ -59,6 +60,10 @@               | css <- styleSheets page
               ]
          , mconcat
+              [ tag "style" (unescaped txt)
+              | txt <- styleTxts page
+              ]
+         , mconcat
               [ element "script" ["src" .=. js, "type" .=. "text/javascript", string " "]
               | js <- scripts page
               ]
@@ -75,9 +80,12 @@ addScript :: FilePath -> HTMLPage -> HTMLPage
 addScript js page = page { scripts = js : scripts page }
 
+addStyle :: String -> HTMLPage -> HTMLPage
+addStyle txt page = page { styleTxts = txt : styleTxts page }
+
 -- html helper functions
 htmlPage :: String -> HTMLBuilder -> HTMLPage
-htmlPage s = HTMLPage s [] []
+htmlPage s = HTMLPage s [] [] []
 
 link :: BuildXML a => String -> a -> a
 link url body = tag "a" $
+ src/Ideas/Text/HTML/Templates.hs view
@@ -0,0 +1,135 @@+{-# LANGUAGE OverloadedStrings #-}
+-----------------------------------------------------------------------------
+-- Copyright 2018, Ideas project team. This file is distributed under the
+-- terms of the Apache License 2.0. For more information, see the files
+-- "LICENSE.txt" and "NOTICE.txt", which are included in the distribution.
+-----------------------------------------------------------------------------
+-- |
+-- Maintainer  :  bastiaan.heeren@ou.nl
+-- Stability   :  provisional
+-- Portability :  portable (depends on ghc)
+--
+-- W3CSS templates for html pages
+--
+-----------------------------------------------------------------------------
+
+module Ideas.Text.HTML.Templates
+   ( webpage, WebPage(..), Button(..), Style
+   , fontAwesome
+   ) where
+
+import Ideas.Text.HTML
+import Ideas.Text.HTML.W3CSS hiding (tag, ul, top, table, content)
+import Ideas.Text.XML hiding (content)
+import qualified Ideas.Text.HTML.W3CSS as W3
+
+data WebPage = WebPage
+   { title          :: String
+   , menuButtons    :: [Button]
+   , menuStyle      :: Style
+   , iconBarsStyle  :: Style
+   , sideWidth      :: Int
+   , sideHeader     :: HTMLBuilder
+   , sideButtons    :: [Button]
+   , sideStyle      :: Style
+   , iconCloseStyle :: Style
+   , content        :: HTMLBuilder
+   , footer         :: HTMLBuilder
+   , footerStyle    :: Style
+   }
+
+type Style = HTMLBuilder -> HTMLBuilder
+
+data Button = Button
+   { buttonUrl   :: String
+   , buttonStyle :: HTMLBuilder -> HTMLBuilder
+   , buttonText  :: HTMLBuilder
+   }
+
+fromButtons :: [Button] -> HTMLBuilder
+fromButtons = mconcat . map fromButton
+
+fromButton :: Button -> HTMLBuilder
+fromButton b = button (buttonUrl b) $ barItem $ buttonStyle b $ buttonText b
+
+fontAwesome :: BuildXML a => String -> a
+fontAwesome s = italic $ ("class" .=. ("fa fa-" ++ s)) <> string " "
+
+-- https://www.w3schools.com/w3css/tryw3css_templates_webpage.htm
+webpage :: WebPage -> HTMLPage
+webpage wp = w3css $
+   addCSS "https://fonts.googleapis.com/css?family=Roboto" $
+   addCSS "https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css" $
+   addStyle styleText $
+   htmlPage (title wp) $
+      navBar <> sideBar <> overlayEffect <> mainContent <> script
+ where
+   navBar = tag "div" $ W3.top $
+      tag "div" $ bar $ W3.top $ leftAlign $ menuStyle wp $ mconcat
+         [ if null (sideButtons wp) then mempty else
+           button "javascript:void(0)" $ barItem $ right $ hideLarge $ iconBarsStyle wp $
+              ("onclick" .=. "w3_open()") <> fontAwesome "bars"
+         , fromButtons (menuButtons wp)
+         ]
+
+   sideBar = if null (sideButtons wp) then mempty else
+      tag "nav" $ sidebar $ barBlock $ collapse $ sideStyle wp $ mconcat
+         [ "id" .=. "mySidebar"
+         , link "javascript:void(0)" $ right $ padding Large $ iconCloseStyle wp $ hideLarge $ mconcat
+              [ "onclick" .=. "w3_close()"
+              , "title"   .=. "Close Menu"
+              , fontAwesome "remove"
+              ]
+         , sideHeader wp
+         , fromButtons (sideButtons wp)
+         ]
+
+   overlayEffect = tag "div" $ overlay $ hideLarge $ mconcat
+      [ "onclick" .=. "w3_close()"
+      , "style"   .=. "cursor:pointer"
+      , "title"   .=. "close side menu"
+      , "id"      .=. "myOverlay"
+      , " "
+      ]
+
+   script = tag "script" (unescaped scriptText)
+
+   width | null (sideButtons wp) = "0px"
+         | otherwise             = show (sideWidth wp) ++ "px"
+
+   mainContent = tag "div" $ mainPage $ mconcat
+      [ "style" .=. ("margin-left:" ++ width)
+      , container $ vpadding XXXL $ content wp
+      , tag "footer" $ mconcat
+           [ "id" .=. "myFooter"
+           , container $ footerStyle wp $ para $ footer wp
+           ]
+      ]
+
+   styleText =
+      "html,body,h1,h2,h3,h4,h5,h6 {font-family: \"Roboto\", sans-serif;}\
+      \.w3-sidebar {\
+      \  z-index: 3;\
+      \  width: " ++ width ++ ";\
+      \  top: 43px;\
+      \  bottom: 0;\
+      \  height: inherit;\
+      \}"
+
+scriptText :: String
+scriptText =
+   "var mySidebar = document.getElementById(\"mySidebar\");\
+   \var overlayBg = document.getElementById(\"myOverlay\");\
+   \function w3_open() {\
+   \    if (mySidebar.style.display === 'block') {\
+   \        mySidebar.style.display = 'none';\
+   \        overlayBg.style.display = 'none';\
+   \    } else {\
+   \        mySidebar.style.display = 'block';\
+   \        overlayBg.style.display = 'block';\
+   \    }\
+   \}\
+   \function w3_close() {\
+   \    mySidebar.style.display = 'none';\
+   \    overlayBg.style.display = 'none';\
+   \}"
+ src/Ideas/Text/HTML/W3CSS.hs view
@@ -0,0 +1,633 @@+-----------------------------------------------------------------------------
+-- Copyright 2018, Ideas project team. This file is distributed under the
+-- terms of the Apache License 2.0. For more information, see the files
+-- "LICENSE.txt" and "NOTICE.txt", which are included in the distribution.
+-----------------------------------------------------------------------------
+-- |
+-- Maintainer  :  bastiaan.heeren@ou.nl
+-- Stability   :  provisional
+-- Portability :  portable (depends on ghc)
+--
+-- A minimal interface for using W3.CSS, a modern CSS framework with built-in
+-- responsiveness.
+-- See https://www.w3schools.com/w3css/
+--
+-----------------------------------------------------------------------------
+module Ideas.Text.HTML.W3CSS where
+
+import Data.Char
+import Ideas.Text.HTML
+import Ideas.Text.XML
+import qualified Ideas.Text.XML as XML
+
+w3css :: HTMLPage -> HTMLPage
+w3css = addCSS "https://www.w3schools.com/w3css/4/w3.css"
+
+w3class :: BuildXML a => String -> a -> a
+w3class s a = ("class" .=. s) <> a
+
+w3classIf :: BuildXML a => Bool -> String -> a -> a
+w3classIf b s = if b then w3class s else id
+
+data Color
+   = Red | Pink | Purple | DeepPurple | Indigo | Blue | LightBlue
+   | Cyan | Aqua | Teal | Green | LightGreen | Lime | Sand | Khaki
+   | Yellow | Amber | Orange | DeepOrange | BlueGray | Brown | LightGray
+   | Gray | DarkGray | Black | PaleRed | PaleYellow | PaleGreen | PaleBlue | White
+ deriving Show
+
+data Size = Tiny | Small | Medium | Large | XL | XXL | XXXL | Jumbo
+ deriving (Eq, Ord)
+
+data Position = TopLeft    | Top    | TopRight
+              | CenterLeft | Center | CenterRight
+              | BottomLeft | Bottom | BottomRight
+ deriving (Show, Eq)
+
+onTop, onLeft, onRight, onBottom :: Position -> Bool
+onTop    = (`elem` [TopLeft, Top, TopRight])
+onLeft   = (`elem` [TopLeft, CenterLeft, BottomLeft])
+onRight  = (`elem` [TopRight, CenterRight, BottomRight])
+onBottom = (`elem` [BottomLeft, Bottom, BottomRight])
+
+instance Show Size where
+   show Tiny   = "tiny"
+   show Small  = "small"
+   show Medium = "medium"
+   show Large  = "large"
+   show XL     = "xlarge"
+   show XXL    = "xxlarge"
+   show XXXL   = "xxxlarge"
+   show Jumbo  = "jumbo"
+
+uncamel :: String -> String
+uncamel = concatMap f
+ where
+   f c = if isUpper c then ['-', toLower c] else [c]
+
+--------------------------------------------------------------------------------
+-- * Container Classes
+
+-- | HTML container with 16px left and right padding
+container :: BuildXML a => a -> a
+container = XML.tag "div" . w3class "w3-container"
+
+-- | HTML container with 16px left and right padding and 16px top and bottom margin
+panel :: BuildXML a => a -> a
+panel = XML.tag "div" . w3class "w3-panel"
+
+-- | Circular badge
+badge :: BuildXML a => a -> a
+badge = XML.tag "span" . w3class "w3-badge"
+
+-- | Rectangular tag
+tag :: BuildXML a => a -> a
+tag = XML.tag "span" . w3class "w3-tag"
+
+-- | Unordered list
+ul :: BuildXML a => [a] -> a
+ul = ulWith id
+
+-- | Unordered list
+ulWith :: BuildXML a => (a -> a) -> [a] -> a
+ulWith f = XML.tag "ul" . w3class "w3-ul" . f . mconcat . map (XML.tag "li")
+
+-- | Code container
+code :: BuildXML a => a -> a
+code = XML.tag "div" . w3class "w3-code"
+
+-- | Inline code container (for code snippets)
+codespan :: BuildXML a => a -> a
+codespan = XML.tag "code" . w3class "w3-codespan"
+
+--------------------------------------------------------------------------------
+-- * Table Classes
+
+-- | Container for an HTML table
+table :: BuildXML a => a -> a
+table = XML.tag "table" .  w3class "w3-table"
+
+-- | All properties set
+tableAll :: BuildXML a => a -> a
+tableAll = XML.tag "table" . w3class "w3-table-all"
+
+-- | Striped table
+striped :: BuildXML a => a -> a
+striped = w3class "w3-striped"
+
+-- | Bordered lines
+bordered :: BuildXML a => a -> a
+bordered = w3class "w3-bordered"
+
+-- | Centered table
+centered :: BuildXML a => a -> a
+centered = w3class "w3-centered"
+
+-- | Hoverable table
+hoverable :: BuildXML a => a -> a
+hoverable = w3class "w3-hoverable"
+
+-- | Creates a responsive table
+responsive :: BuildXML a => a -> a
+responsive = w3class "w3-responsive"
+
+--------------------------------------------------------------------------------
+-- * Card Classes
+
+-- | Same as w3-card-2
+card :: BuildXML a => a -> a
+card = w3class "w3-card"
+
+-- | Container for any HTML content (2px bordered shadow)
+card2 :: BuildXML a => a -> a
+card2 = w3class "w3-card-2"
+
+-- | Container for any HTML content (4px bordered shadow)
+card4 :: BuildXML a => a -> a
+card4 = w3class "w3-card-4"
+
+--------------------------------------------------------------------------------
+-- * Responsive Classes
+
+-- | Container for one row of fluid responsive content
+row :: BuildXML a => a -> a
+row = w3class "w3-row"
+
+-- | Row where all columns have a default padding
+rowPadding :: BuildXML a => a -> a
+rowPadding = w3class "w3-row-padding"
+
+-- | Container for fixed size centered content
+content :: BuildXML a => a -> a
+content = w3class "w3-content"
+
+-- | Half (1/2) screen column container
+half :: BuildXML a => a -> a
+half = w3class "w3-half"
+
+-- | Third (1/3) screen column container
+third :: BuildXML a => a -> a
+third = w3class "w3-third"
+
+-- | Two third (2/3) screen column container
+twothird :: BuildXML a => a -> a
+twothird = w3class "w3-twothird"
+
+-- | Quarter (1/4) screen column container
+quarter :: BuildXML a => a -> a
+quarter = w3class "w3-quarter"
+
+-- | Three quarters (3/4) screen column container
+threequarter :: BuildXML a => a -> a
+threequarter = w3class "w3-threequarter"
+
+-- | Column container for any HTML content
+col :: BuildXML a => a -> a
+col = w3class "w3-col"
+
+-- | Occupies the rest of the column width
+rest :: BuildXML a => a -> a
+rest = w3class "w3-rest"
+
+-- | Hide content on small screens (less than 601px)
+hideSmall :: BuildXML a => a -> a
+hideSmall = w3class "w3-hide-small"
+
+-- | Hide content on medium screens
+hideMedium :: BuildXML a => a -> a
+hideMedium = w3class "w3-hide-medium"
+
+-- | Hide content on large screens (larger than 992px)
+hideLarge :: BuildXML a => a -> a
+hideLarge = w3class "w3-hide-large"
+
+-- | Responsive image
+image :: BuildXML a => a -> a
+image = w3class "w3-image"
+
+-- | Adds mobile-first responsiveness to any element. Displays elements as block elements on mobile devices.
+mobile :: BuildXML a => a -> a
+mobile = w3class "w3-mobile"
+
+-- l1 - l12 Responsive sizes for large screens
+-- m1 - m12 Responsive sizes for medium screens
+-- s1 - s12 Responsive sizes for small screens
+
+--------------------------------------------------------------------------------
+-- * Layout Classes
+
+-- | Container for layout columns (cells).
+cellRow :: BuildXML a => a -> a
+cellRow = w3class "w3-cell-row"
+
+-- | Layout column (cell).
+cell :: BuildXML a => a -> a
+cell = w3class "w3-cell"
+
+-- | Aligns content at the top of a column (cell).
+cellTop :: BuildXML a => a -> a
+cellTop = w3class "w3-cell-top"
+
+-- | Aligns content at the vertical middle of a column (cell).
+cellMiddle :: BuildXML a => a -> a
+cellMiddle = w3class "w3-cell-middle"
+
+-- | Aligns content at the bottom of a column (cell).
+cellBottom :: BuildXML a => a -> a
+cellBottom = w3class "w3-cell-bottom"
+
+--------------------------------------------------------------------------------
+-- * Bar Classes - Navigation
+
+-- | Horizontal bar
+bar :: BuildXML a => a -> a
+bar = w3class "w3-bar"
+
+-- | Vertical bar
+barBlock :: BuildXML a => a -> a
+barBlock = w3class "w3-bar-block"
+
+-- | Provides common style for bar items
+barItem :: BuildXML a => a -> a
+barItem = w3class "w3-bar-item"
+
+-- | Side bar
+sidebar :: BuildXML a => a -> a
+sidebar = w3class "w3-sidebar"
+
+-- | Used together with w3-sidebar to create a fully automatic responsive side navigation. For this class to work, the page content must be within a "w3-main" class
+collapse :: BuildXML a => a -> a
+collapse = w3class "w3-collapse"
+
+-- | Container for page content when using the w3-collapse class for responsive side navigations
+mainPage :: BuildXML a => a -> a
+mainPage = w3class "w3-main"
+
+--------------------------------------------------------------------------------
+-- * Dropdown Classes
+
+-- | Clickable dropdown element
+dropdownClick :: BuildXML a => a -> a
+dropdownClick = w3class "w3-dropdown-click"
+
+-- | Hoverable dropdown element
+dropdownHover :: BuildXML a => a -> a
+dropdownHover = w3class "w3-dropdown-hover"
+
+--------------------------------------------------------------------------------
+-- * Button Classes
+
+-- | Rectangular button with grey background color on hover
+button :: BuildXML a => String -> a -> a
+button url = link url . w3class "w3-button"
+
+-- | Rectangular button with shadows on hover
+btn :: BuildXML a => String -> a -> a
+btn url = link url . w3class "w3-btn"
+
+-- | Rectangular button with ripple effect
+ripple :: BuildXML a => String -> a -> a
+ripple url = link url . w3class "w3-ripple"
+
+--------------------------------------------------------------------------------
+-- * Input Classes
+
+-- | Input elements
+input :: BuildXML a => a -> a
+input = w3class "w3-input"
+
+-- | Checkbox input type
+check :: BuildXML a => a -> a
+check = w3class "w3-check"
+
+-- | Radio input type
+radio :: BuildXML a => a -> a
+radio = w3class "w3-radio"
+
+-- | Input select element
+select :: BuildXML a => a -> a
+select = w3class "w3-select"
+
+--------------------------------------------------------------------------------
+-- * Modal Classes
+
+-- | Modal container
+modal :: BuildXML a => a -> a
+modal = w3class "w3-modal"
+
+-- | Modal pop-up element
+modalContent :: BuildXML a => a -> a
+modalContent = w3class "w3-modal-content"
+
+-- | Tooltip element
+tooltip :: BuildXML a => a -> a
+tooltip = w3class "w3-tooltip"
+
+-- | Tooltip text
+tooltipText :: BuildXML a => a -> a
+tooltipText = w3class "w3-text"
+
+--------------------------------------------------------------------------------
+-- * Animation Classes
+
+-- | Animates an element from -300px to 0px
+animate :: BuildXML a => Position -> a -> a
+animate p = w3classIf (onTop p)    "w3-animate-top"
+          . w3classIf (onLeft p)   "w3-animate-left"
+          . w3classIf (onBottom p) "w3-animate-bottom"
+          . w3classIf (onRight p)  "w3-animate-right"
+
+-- | Animates an element's opacity from 0 to 1
+animateOpacity :: BuildXML a => a -> a
+animateOpacity = w3class "w3-animate-opacity"
+
+-- | Animates an element from 0 to 100% in size
+animateZoom :: BuildXML a => a -> a
+animateZoom = w3class "w3-animate-zoom"
+
+-- | Animates an element's opacity from 0 to 1 and 1 to 0 (fades in AND out)
+animateFading :: BuildXML a => a -> a
+animateFading = w3class "w3-animate-fading"
+
+-- | Spin an icon 360 degrees
+spin :: BuildXML a => a -> a
+spin = w3class "w3-spin"
+
+-- | Animates the width of an input field to 100%
+animateInput :: BuildXML a => a -> a
+animateInput = w3class "w3-animate-input"
+
+--------------------------------------------------------------------------------
+-- * Font and Text Classes
+
+-- | Specifies a font size: tiny 10px, small 12px, large 18px, xlarge 24px, xxlarge 32px, xxxlarge 48px, jumbo 64px
+fontSize :: BuildXML a => Size -> a -> a
+fontSize = w3class . ("w3-" ++) . show
+
+-- | Specifies a wider text
+wide :: BuildXML a => a -> a
+wide = w3class "w3-wide"
+
+-- | Changes the font to serif
+serif :: BuildXML a => a -> a
+serif = w3class "w3-serif"
+
+--------------------------------------------------------------------------------
+-- * Display Classes
+
+-- | Centered content
+center :: BuildXML a => a -> a
+center = w3class "w3-center"
+
+-- | Floats an element to the left (float: left)
+left :: BuildXML a => a -> a
+left = w3class "w3-left"
+
+-- | Floats an element to the right (float: right)
+right :: BuildXML a => a -> a
+right = w3class "w3-right"
+
+-- | Left aligned text
+leftAlign :: BuildXML a => a -> a
+leftAlign = w3class "w3-left-align"
+
+-- | Right aligned text
+rightAlign :: BuildXML a => a -> a
+rightAlign = w3class "w3-right-align"
+
+-- | Right and left aligned text
+justify :: BuildXML a => a -> a
+justify = w3class "w3-justify"
+
+-- | Circled content
+circle :: BuildXML a => a -> a
+circle = w3class "w3-circle"
+
+-- | Hidden content (display:none)
+hide :: BuildXML a => a -> a
+hide = w3class "w3-hide"
+
+-- | Alias of w3-show (display:block)
+showBlock :: BuildXML a => a -> a
+showBlock = w3class "w3-show-block"
+
+-- | Show content as inline-block (display:inline-block)
+showInlineBlock :: BuildXML a => a -> a
+showInlineBlock = w3class "w3-show-inline-block"
+
+-- | Fixed content at the top of a page
+top :: BuildXML a => a -> a
+top = w3class "w3-top"
+
+-- | Fixed content at the bottom of a page
+bottom :: BuildXML a => a -> a
+bottom = w3class "w3-bottom"
+
+-- | Container for w3-display-classes (position: relative)
+display :: BuildXML a => Position -> a -> a
+display p = w3class "w3-display-container" . w3class (f p)
+ where
+   f TopLeft     = "w3-display-topleft"
+   f Top         = "w3-display-topmiddle"
+   f TopRight    = "w3-display-topright"
+   f CenterLeft  = "w3-display-left"
+   f Center      = "w3-display-middle"
+   f CenterRight = "w3-display-right"
+   f BottomLeft  = "w3-display-bottomleft"
+   f Bottom      = "w3-display-bottommiddle"
+   f BottomRight = "w3-display-bottomright"
+
+-- | Displays content on hover inside the w3-display-container
+displayHover :: BuildXML a => a -> a
+displayHover = w3class "w3-display-hover"
+
+--------------------------------------------------------------------------------
+-- * Effect Classes
+
+-- | Adds opacity/transparency to an element (opacity: 0.6)
+opacity :: BuildXML a => a -> a
+opacity = w3class "w3-opacity"
+
+-- | Turns off opacity/transparency (opacity: 1)
+opacityOff :: BuildXML a => a -> a
+opacityOff = w3class "w3-opacity-off"
+
+-- | Adds opacity/transparency to an element (opacity: 0.75)
+opacityMin :: BuildXML a => a -> a
+opacityMin = w3class "w3-opacity-min"
+
+-- | Adds opacity/transparency to an element (opacity: 0.25)
+opacityMax :: BuildXML a => a -> a
+opacityMax = w3class "w3-opacity-max"
+
+-- | Adds a grayscale effect to an element (grayscale: 50%)
+grayscaleMin :: BuildXML a => a -> a
+grayscaleMin = w3class "w3-grayscale-min"
+
+-- | Adds a grayscale effect to an element (grayscale: 75%)
+grayscale :: BuildXML a => a -> a
+grayscale = w3class "w3-grayscale"
+
+-- | Adds a grayscale effect to an element (grayscale: 100%)
+grayscaleMax :: BuildXML a => a -> a
+grayscaleMax = w3class "w3-grayscale-max"
+
+-- | Adds a sepia effect to an element (sepia: 50%)
+sepiaMin :: BuildXML a => a -> a
+sepiaMin = w3class "w3-sepia-min"
+
+-- | Adds a sepia effect to an element (sepia: 75%)
+sepia :: BuildXML a => a -> a
+sepia = w3class "w3-sepia"
+
+-- | Adds a sepia effect to an element (sepia: 100%)
+sepiaMax :: BuildXML a => a -> a
+sepiaMax = w3class "w3-sepia-max"
+
+-- | Creates an overlay effect
+overlay :: BuildXML a => a -> a
+overlay = w3class "w3-overlay"
+
+--------------------------------------------------------------------------------
+-- * Background Color Classes
+
+-- | Background color
+background :: BuildXML a => Color -> a -> a
+background = w3class . ("w3" ++) .  uncamel . show
+
+-- | Transparent background-color
+transparent :: BuildXML a => a -> a
+transparent = w3class "w3-transparent"
+
+--------------------------------------------------------------------------------
+--  * Color Classes
+
+-- | Hover color
+hover :: BuildXML a => Color -> a -> a
+hover = w3class . ("w3-hover" ++) .  uncamel . show
+
+--------------------------------------------------------------------------------
+-- * Text Color Classes
+
+-- | Text color
+textColor :: BuildXML a => Color -> a -> a
+textColor = w3class . ("w3-text" ++) .  uncamel . show
+
+--------------------------------------------------------------------------------
+-- * Hover Classes
+
+-- | Hover text color
+hoverColor :: BuildXML a => Color -> a -> a
+hoverColor = w3class . ("w3-hover-text" ++) .  uncamel . show
+
+-- | Adds transparency to an element on hover (opacity: 0.6)
+hoverOpacity :: BuildXML a => a -> a
+hoverOpacity = w3class "w3-hover-opacity"
+
+-- | Removes transparency from an element on hover (100% opacity)
+hoverOpacityOff :: BuildXML a => a -> a
+hoverOpacityOff = w3class "w3-hover-opacity-off"
+
+-- | Adds shadow to an element on hover
+hoverShadow :: BuildXML a => a -> a
+hoverShadow = w3class "w3-hover-shadow"
+
+-- | Adds a black and white (100% grayscale) effect to an element
+hoverGrayscale :: BuildXML a => a -> a
+hoverGrayscale = w3class "w3-hover-grayscale"
+
+-- | Adds a sepia effect to an element on hover
+hoverSepia :: BuildXML a => a -> a
+hoverSepia = w3class "w3-hover-sepia"
+
+-- | Removes hover effects from an element
+hoverNone :: BuildXML a => a -> a
+hoverNone = w3class "w3-hover-none"
+
+--------------------------------------------------------------------------------
+-- * Round Classes
+
+-- | Element rounded (border-radius): small 2px, medium 4px, large 8px, xlarge 16px, xxlarge 32px
+rounded :: BuildXML a => Size -> a -> a
+rounded s = w3class "w3-round" . w3class ("w3-round-" ++ show s)
+
+--------------------------------------------------------------------------------
+-- * Padding Classes
+
+-- | Small: Padding 4px top and bottom, and 8px left and right, Medium: Padding 8px top and bottom, and 16px left and right, Large: Padding 12px top and bottom, and 24px left and right.
+padding :: BuildXML a => Size -> a -> a
+padding s
+   | s < Medium = w3class "w3-padding-small"
+   | s > Medium = w3class "w3-padding-large"
+   | otherwise  = w3class "w3-padding-small"
+
+-- | Padding top and bottom: medium 16px, large 24px, xlarge 32px, xxlarge 48px, xxxlarge 64px
+vpadding :: BuildXML a => Size -> a -> a
+vpadding s
+   | s <= Medium = w3class "w3-padding-16"
+   | s == Large  = w3class "w3-padding-24"
+   | s == XL     = w3class "w3-padding-32"
+   | s == XXL    = w3class "w3-padding-48"
+   | otherwise   = w3class "w3-padding-64"
+
+--------------------------------------------------------------------------------
+-- * Margin Classes
+
+-- | Adds an 16px margin to an element
+margin :: BuildXML a => a -> a
+margin = w3class "w3-margin"
+
+marginPos :: BuildXML a => Position -> a -> a
+marginPos p = w3classIf (onTop p)    "w3-margin-top"
+            . w3classIf (onLeft p)   "w3-margin-left"
+            . w3classIf (onBottom p) "w3-margin-bottom"
+            . w3classIf (onRight p)  "w3-margin-right"
+
+-- | Adds an 16px top and bottom margin to an element
+section :: BuildXML a => a -> a
+section = w3class "w3-section"
+
+--------------------------------------------------------------------------------
+-- * Border Classes
+
+-- | Borders (top, right, bottom, left)
+border :: BuildXML a => a -> a
+border = w3class "w3-border"
+
+borderPos :: BuildXML a => Position -> a -> a
+borderPos p = w3classIf (onTop p)    "w3-border-top"
+            . w3classIf (onLeft p)   "w3-border-left"
+            . w3classIf (onBottom p) "w3-border-bottom"
+            . w3classIf (onRight p)  "w3-border-right"
+
+-- | Removes all borders
+noBorder :: BuildXML a => a -> a
+noBorder = w3class "w3-border-0"
+
+-- | Border color
+borderColor :: BuildXML a => Color -> a -> a
+borderColor = w3class . ("w3-border" ++) .  uncamel . show
+
+-- | Adds a thick border (bar) to an element
+barPos :: BuildXML a => Position -> a -> a
+barPos p = w3classIf (onTop p)    "w3-topbar"
+         . w3classIf (onLeft p)   "w3-leftbar"
+         . w3classIf (onBottom p) "w3-bottombar"
+         . w3classIf (onRight p)  "w3-rightbar"
+
+--------------------------------------------------------------------------------
+-- * Color themes
+
+data ColorTheme
+   = L1 | L2 | L3 | L4 | L5 -- light
+   | D1 | D2 | D3 | D4 | D5 -- dark
+ deriving Show
+
+-- standard color theme
+theme_, textTheme, borderTheme :: BuildXML a => a -> a
+theme_      = w3class "w3-theme"
+textTheme   = w3class "w3-text-theme"
+borderTheme = w3class "w3-border-theme"
+
+theme :: BuildXML a => ColorTheme -> a -> a
+theme = w3class . ("w3-theme-" ++) . map toLower . show
src/Ideas/Text/JSON.hs view
@@ -1,5 +1,5 @@ -----------------------------------------------------------------------------
--- Copyright 2016, Ideas project team. This file is distributed under the
+-- Copyright 2018, Ideas project team. This file is distributed under the
 -- terms of the Apache License 2.0. For more information, see the files
 -- "LICENSE.txt" and "NOTICE.txt", which are included in the distribution.
 -----------------------------------------------------------------------------
@@ -151,11 +151,6 @@    fromJSON (Array [a, b, c, d]) = (,,,) <$> fromJSON a <*> fromJSON b <*> fromJSON c <*> fromJSON d
    fromJSON _                    = fail "expecting an array with 4 elements"
 
-instance InJSON IOException where
-   toJSON     = toJSON . ioeGetErrorString
-   fromJSON (String s) = return (userError s)
-   fromJSON _ = fail "excepting a string"
-
 --------------------------------------------------------
 -- Parser
 
@@ -260,8 +255,10 @@          return (okResponse json (requestId req))
        `catch` handler req
  where
-   handler :: RPCRequest -> IOException -> IO RPCResponse
-   handler req e = return (errorResponse (toJSON e) (requestId req))
+   handler :: RPCRequest -> SomeException -> IO RPCResponse
+   handler req e =
+      let msg = maybe (show e) ioeGetErrorString (fromException e)
+      in return $ errorResponse (toJSON msg) (requestId req)
 
 --------------------------------------------------------
 -- Testing parser/pretty-printer
src/Ideas/Text/Latex.hs view
@@ -1,6 +1,6 @@ {-# LANGUAGE OverloadedStrings #-}
 -----------------------------------------------------------------------------
--- Copyright 2016, Ideas project team. This file is distributed under the
+-- Copyright 2018, Ideas project team. This file is distributed under the
 -- terms of the Apache License 2.0. For more information, see the files
 -- "LICENSE.txt" and "NOTICE.txt", which are included in the distribution.
 -----------------------------------------------------------------------------
@@ -20,7 +20,8 @@    ) where
 
 import Data.List
-import Data.Monoid
+import Data.Monoid hiding ((<>))
+import Data.Semigroup as Sem
 import Data.String
 
 newtype Latex = L { showLatex :: String }
@@ -31,9 +32,12 @@ instance IsString Latex where
    fromString = L
 
+instance Sem.Semigroup Latex where
+   L xs <> L ys = L (xs <> ys)
+
 instance Monoid Latex where
-   mempty = L []
-   L xs `mappend` L ys = L (mappend xs ys)
+   mempty  = L []
+   mappend = (<>)
 
 class ToLatex a where
    toLatex     :: a -> Latex
+ src/Ideas/Text/MathML.hs view
@@ -0,0 +1,136 @@+-----------------------------------------------------------------------------
+-- Copyright 2018, Ideas project team. This file is distributed under the
+-- terms of the Apache License 2.0. For more information, see the files
+-- "LICENSE.txt" and "NOTICE.txt", which are included in the distribution.
+-----------------------------------------------------------------------------
+-- |
+-- Maintainer  :  bastiaan.heeren@ou.nl
+-- Stability   :  provisional
+-- Portability :  portable (depends on ghc)
+--
+-----------------------------------------------------------------------------
+
+module Ideas.Text.MathML
+   ( MathML(..), xml2mathml, mathml2xml
+   ) where
+
+import Data.Either
+import Data.Maybe
+import Ideas.Text.XML
+import Ideas.Utils.Uniplate hiding (children)
+
+data MathML = MRow [MathML]
+            | MId String
+            | MNumber String
+            | MOperator String
+            | MString String
+            | MText String
+            | MSqrt MathML
+            | MRoot MathML MathML
+            | MSup MathML MathML -- base, superscript
+            | MSub MathML MathML -- base, subscript
+            | MSubSup MathML MathML MathML -- base, subscript, superscript
+            | MFrac MathML MathML -- numerator, denominator
+            | MFenced String String MathML -- left, right, content
+            | MSpace
+            | MStyle
+            | MPadded
+            | MPhantom
+            | MError
+            | MEnclose
+            | MUnder
+            | MOver
+            | MUnderOver
+            | MTable
+            | MTableRow
+            | MLabeledTableRow
+            | MTableData
+   deriving (Show, Eq)
+{-
+from: https://www.w3.org/TR/MathML2/chapter3.html#presm.mn @ 3.2.1
+Conversely, since mn is a presentation element, there are a few situations where it may desirable to include arbitrary text in the content of an mn that should merely render as a numeric literal
+-}
+
+instance InXML MathML where
+   toXML   = mathml2xml
+   fromXML = either fail return . xml2mathml
+
+instance Uniplate MathML where
+   uniplate math =
+      case math of
+         MRow xs       -> plate MRow ||* xs
+         _             -> plate math
+
+----------------------------------------------------------
+-- conversion functions: XML <-> MathML
+
+xml2mathml :: XML -> Either String MathML
+xml2mathml = rec
+ where
+   rec xml = case xml of
+       Element "mrow" _ _ -> MRow <$> mapM rec (children xml)
+       Element "mi" _ [Left s] -> return (MId s)
+       Element "mn" _ [Left s] -> return (MNumber s)
+       Element "mo" _ [Left s] -> return (MOperator s)
+       Element "ms" _ [Left s] -> return (MString s)
+       Element "mtext" _ [] -> return (MText "")
+       Element "mtext" _ [Left s] -> return (MText s)
+       Element "mroot" _ [Right c, Right d] -> MRoot <$> rec c <*> rec d
+       Element "msup" _ [Right c, Right d] -> MSup <$> rec c <*> rec d
+       Element "msub" _ [Right c, Right d] -> MSub <$> rec c <*> rec d
+       Element "msubsup" _ [Right c, Right d, Right e] -> MSubSup <$> rec c <*> rec d <*> rec e
+       Element "mfrac" _ [Right c, Right d] -> MFrac <$> rec c <*> rec d
+       Element "mfenced" _ [Right c] -> MFenced (fromMaybe "(" (findAttribute "open" xml)) (fromMaybe ")" (findAttribute "close" xml)) <$> rec c
+       Element "mspace" _ _ -> return MSpace
+       Element "mtable" _ _ -> return MTable
+       Element "mtr" _ _ -> return MTableRow
+       Element "mlabeledtr" _ _ -> return MLabeledTableRow
+       Element "munder" _ _ -> return MUnder
+       Element "mover" _ _ -> return MOver
+       Element "munderover" _ _ -> return MUnderOver
+       -- below are cases that have 1* arguments, when none-one an mrow is implied.
+       Element "math" _ xs -> impliedMRow xs
+       Element "msqrt" _ xs -> MSqrt <$> impliedMRow xs
+       Element "mphantom" _ _ -> return MPhantom
+       Element "mpadded" _ _ -> return MPadded
+       Element "mstyle" _ _ -> return MStyle
+       Element "merror" _ _ -> return MError
+       Element "mtd" _ _ -> return MTableData
+       Element "menclose" _ _ -> return MEnclose
+       _ -> fail ("unsupported MathML: " ++ show xml)
+   impliedMRow :: [Either String Element] -> Either String MathML
+   impliedMRow [Right r] = rec r
+   impliedMRow xs = MRow <$> mapM rec (rights xs)
+
+mathml2xml :: MathML -> XML
+mathml2xml = makeXML "math" . rec
+ where
+   rec :: MathML -> XMLBuilder
+   rec math =
+      case math of
+         MRow ms           -> element "mrow" (map rec ms)
+         MId s             -> element "mi" [string s]
+         MNumber s         -> element "mn" [string s]
+         MOperator s       -> element "mo" [string s]
+         MString s         -> element "ms" [string s]
+         MText s           -> element "mtext" [string s]
+         MSqrt m           -> element "msqrt" [rec m]
+         MRoot m1 m2       -> element "mroot" [rec m1, rec m2]
+         MSup m1 m2        -> element "msup" [rec m1, rec m2]
+         MSub m1 m2        -> element "msub" [rec m1, rec m2]
+         MSubSup m1 m2 m3  -> element "msubsup" [rec m1, rec m2, rec m3]
+         MFrac m1 m2       -> element "mfrac" [rec m1, rec m2]
+         MFenced s1 s2 m   -> element "mfenced" ["open" .=. s1, "close" .=. s2, rec m]
+         MSpace            -> element "mspace" []
+         MStyle            -> element "mstyle" []
+         MPadded           -> element "mpadded" []
+         MPhantom          -> element "mphantom" []
+         MError            -> element "merror" []
+         MEnclose          -> element "menclose" []
+         MUnder            -> element "munder" []
+         MOver             -> element "mover" []
+         MUnderOver        -> element "munderover" []
+         MTable            -> element "mtable" []
+         MTableRow         -> element "mtr" []
+         MLabeledTableRow  -> element "mlabeledtr" []
+         MTableData        -> element "mtd" []
src/Ideas/Text/OpenMath/FMP.hs view
@@ -1,5 +1,5 @@ -----------------------------------------------------------------------------
--- Copyright 2016, Ideas project team. This file is distributed under the
+-- Copyright 2018, Ideas project team. This file is distributed under the
 -- terms of the Apache License 2.0. For more information, see the files
 -- "LICENSE.txt" and "NOTICE.txt", which are included in the distribution.
 -----------------------------------------------------------------------------
src/Ideas/Text/OpenMath/Object.hs view
@@ -1,5 +1,5 @@ -----------------------------------------------------------------------------
--- Copyright 2016, Ideas project team. This file is distributed under the
+-- Copyright 2018, Ideas project team. This file is distributed under the
 -- terms of the Apache License 2.0. For more information, see the files
 -- "LICENSE.txt" and "NOTICE.txt", which are included in the distribution.
 -----------------------------------------------------------------------------
src/Ideas/Text/OpenMath/Symbol.hs view
@@ -1,5 +1,5 @@ -----------------------------------------------------------------------------
--- Copyright 2016, Ideas project team. This file is distributed under the
+-- Copyright 2018, Ideas project team. This file is distributed under the
 -- terms of the Apache License 2.0. For more information, see the files
 -- "LICENSE.txt" and "NOTICE.txt", which are included in the distribution.
 -----------------------------------------------------------------------------
src/Ideas/Text/OpenMath/Tests.hs view
@@ -1,5 +1,5 @@ -----------------------------------------------------------------------------
--- Copyright 2016, Ideas project team. This file is distributed under the
+-- Copyright 2018, Ideas project team. This file is distributed under the
 -- terms of the Apache License 2.0. For more information, see the files
 -- "LICENSE.txt" and "NOTICE.txt", which are included in the distribution.
 -----------------------------------------------------------------------------
src/Ideas/Text/UTF8.hs view
@@ -1,5 +1,5 @@ -----------------------------------------------------------------------------
--- Copyright 2016, Ideas project team. This file is distributed under the
+-- Copyright 2018, Ideas project team. This file is distributed under the
 -- terms of the Apache License 2.0. For more information, see the files
 -- "LICENSE.txt" and "NOTICE.txt", which are included in the distribution.
 -----------------------------------------------------------------------------
src/Ideas/Text/XML.hs view
@@ -1,5 +1,5 @@ -----------------------------------------------------------------------------
--- Copyright 2016, Ideas project team. This file is distributed under the
+-- Copyright 2018, Ideas project team. This file is distributed under the
 -- terms of the Apache License 2.0. For more information, see the files
 -- "LICENSE.txt" and "NOTICE.txt", which are included in the distribution.
 -----------------------------------------------------------------------------
@@ -19,15 +19,21 @@    , parseXML, parseXMLFile, compactXML, findAttribute
    , children, Attribute(..), fromBuilder, findChild, findChildren, getData
    , BuildXML(..)
-   , module Data.Monoid, munless, mwhen
+   , module Data.Monoid
+   , (<>)
+   , munless, mwhen
    ) where
 
 import Control.Monad
 import Data.Char
 import Data.Foldable (toList)
-import Data.Monoid
+import Data.List
+import Data.Monoid hiding ((<>))
+import Data.Semigroup as Sem
+import Data.String
 import Ideas.Text.XML.Interface hiding (parseXML)
 import System.IO
+import qualified Data.Map as M
 import qualified Data.Sequence as Seq
 import qualified Ideas.Text.XML.Interface as I
 
@@ -72,9 +78,9 @@ ----------------------------------------------------------------
 -- XML builders
 
-infix 3 .=.
+infix 7 .=.
 
-class Monoid a => BuildXML a where
+class (Sem.Semigroup a, Monoid a) => BuildXML a where
    (.=.)     :: String -> String -> a   -- attribute
    unescaped :: String -> a             -- parsed character data (unescaped!)
    builder   :: Element -> a            -- (named) xml element
@@ -92,20 +98,32 @@ 
 data XMLBuilder = BS (Seq.Seq Attr) (Seq.Seq (Either String Element))
 
--- local helper
+-- local helper: merge attributes, but preserve order
 fromBS :: XMLBuilder -> (AttrList, Content)
-fromBS (BS as elts) = (toList as, toList elts)
+fromBS (BS as elts) = (attrList, toList elts)
+ where
+   attrMap = foldr add M.empty as
+   add (k := v) = M.insertWith (\x y -> x ++ " " ++ y) k v
+   attrList = nubBy eqKey (map make (toList as))
+   make (k := _) = k := M.findWithDefault "" k attrMap
+   eqKey (k1 := _) (k2 := _) = k1 == k2
 
-instance Monoid XMLBuilder where
-   mempty = BS mempty mempty
-   mappend (BS as1 elts1) (BS as2 elts2) =
+instance Sem.Semigroup XMLBuilder where
+  BS as1 elts1 <> BS as2 elts2 =
       BS (as1 <> as2) (elts1 <> elts2)
 
+instance Monoid XMLBuilder where
+   mempty  = BS mempty mempty
+   mappend = (<>)
+
 instance BuildXML XMLBuilder where
    n .=. s   = BS (Seq.singleton (n := escapeAttr s)) mempty
    unescaped = BS mempty . Seq.singleton . Left
    builder   = BS mempty . Seq.singleton . Right
    tag s     = builder . uncurry (Element s) . fromBS
+
+instance IsString XMLBuilder where
+   fromString = string
 
 makeXML :: String -> XMLBuilder -> XML
 makeXML s = uncurry (Element s) . fromBS
src/Ideas/Text/XML/Document.hs view
@@ -1,5 +1,5 @@ -----------------------------------------------------------------------------
--- Copyright 2016, Ideas project team. This file is distributed under the
+-- Copyright 2018, Ideas project team. This file is distributed under the
 -- terms of the Apache License 2.0. For more information, see the files
 -- "LICENSE.txt" and "NOTICE.txt", which are included in the distribution.
 -----------------------------------------------------------------------------
src/Ideas/Text/XML/Interface.hs view
@@ -1,5 +1,5 @@ -----------------------------------------------------------------------------
--- Copyright 2016, Ideas project team. This file is distributed under the
+-- Copyright 2018, Ideas project team. This file is distributed under the
 -- terms of the Apache License 2.0. For more information, see the files
 -- "LICENSE.txt" and "NOTICE.txt", which are included in the distribution.
 -----------------------------------------------------------------------------
src/Ideas/Text/XML/Parser.hs view
@@ -1,6 +1,6 @@ {-# LANGUAGE FlexibleContexts #-}
 -----------------------------------------------------------------------------
--- Copyright 2016, Ideas project team. This file is distributed under the
+-- Copyright 2018, Ideas project team. This file is distributed under the
 -- terms of the Apache License 2.0. For more information, see the files
 -- "LICENSE.txt" and "NOTICE.txt", which are included in the distribution.
 -----------------------------------------------------------------------------
src/Ideas/Text/XML/Unicode.hs view
@@ -1,5 +1,5 @@ -----------------------------------------------------------------------------
--- Copyright 2016, Ideas project team. This file is distributed under the
+-- Copyright 2018, Ideas project team. This file is distributed under the
 -- terms of the Apache License 2.0. For more information, see the files
 -- "LICENSE.txt" and "NOTICE.txt", which are included in the distribution.
 -----------------------------------------------------------------------------
src/Ideas/Utils/BlackBoxTests.hs view
@@ -1,5 +1,5 @@ -----------------------------------------------------------------------------
--- Copyright 2016, Ideas project team. This file is distributed under the
+-- Copyright 2018, Ideas project team. This file is distributed under the
 -- terms of the Apache License 2.0. For more information, see the files
 -- "LICENSE.txt" and "NOTICE.txt", which are included in the distribution.
 -----------------------------------------------------------------------------
src/Ideas/Utils/Parsing.hs view
@@ -1,5 +1,5 @@ -----------------------------------------------------------------------------
--- Copyright 2016, Ideas project team. This file is distributed under the
+-- Copyright 2018, Ideas project team. This file is distributed under the
 -- terms of the Apache License 2.0. For more information, see the files
 -- "LICENSE.txt" and "NOTICE.txt", which are included in the distribution.
 -----------------------------------------------------------------------------
src/Ideas/Utils/Prelude.hs view
@@ -1,6 +1,6 @@ {-# LANGUAGE ExistentialQuantification #-}
 -----------------------------------------------------------------------------
--- Copyright 2016, Ideas project team. This file is distributed under the
+-- Copyright 2018, Ideas project team. This file is distributed under the
 -- terms of the Apache License 2.0. For more information, see the files
 -- "LICENSE.txt" and "NOTICE.txt", which are included in the distribution.
 -----------------------------------------------------------------------------
src/Ideas/Utils/QuickCheck.hs view
@@ -1,5 +1,5 @@ -----------------------------------------------------------------------------
--- Copyright 2016, Ideas project team. This file is distributed under the
+-- Copyright 2018, Ideas project team. This file is distributed under the
 -- terms of the Apache License 2.0. For more information, see the files
 -- "LICENSE.txt" and "NOTICE.txt", which are included in the distribution.
 -----------------------------------------------------------------------------
@@ -25,6 +25,8 @@ 
 import Control.Arrow
 import Control.Monad
+import Data.Monoid hiding ((<>))
+import Data.Semigroup as Sem
 import Data.Ratio
 import Test.QuickCheck
 
@@ -33,9 +35,12 @@ 
 newtype ArbGen a = AG [(Rational, (Int, Gen ([a] -> a)))]
 
+instance Sem.Semigroup (ArbGen a) where
+   AG xs <> AG ys = AG (xs <> ys)
+
 instance Monoid (ArbGen a) where
-   mempty = AG mempty
-   AG xs `mappend` AG ys = AG (xs `mappend` ys)
+   mempty  = AG mempty
+   mappend = (<>)
 
 generator :: ArbGen a -> Gen a
 generator (AG pairs) = sized rec
src/Ideas/Utils/StringRef.hs view
@@ -1,5 +1,5 @@ -----------------------------------------------------------------------------
--- Copyright 2016, Ideas project team. This file is distributed under the
+-- Copyright 2018, Ideas project team. This file is distributed under the
 -- terms of the Apache License 2.0. For more information, see the files
 -- "LICENSE.txt" and "NOTICE.txt", which are included in the distribution.
 -----------------------------------------------------------------------------
src/Ideas/Utils/TestSuite.hs view
@@ -1,5 +1,5 @@ -----------------------------------------------------------------------------
--- Copyright 2016, Ideas project team. This file is distributed under the
+-- Copyright 2018, Ideas project team. This file is distributed under the
 -- terms of the Apache License 2.0. For more information, see the files
 -- "LICENSE.txt" and "NOTICE.txt", which are included in the distribution.
 -----------------------------------------------------------------------------
@@ -17,7 +17,9 @@ 
 module Ideas.Utils.TestSuite
    ( -- * TestSuite
-     TestSuite, module Data.Monoid
+     TestSuite
+   , module Data.Monoid
+   , (<>)
    , suite, useProperty, usePropertyWith
    , assertTrue, assertNull, assertEquals, assertIO
    , assertMessage, assertMessageIO
@@ -44,7 +46,8 @@ import Data.IORef
 import Data.List
 import Data.Maybe
-import Data.Monoid
+import Data.Monoid hiding ((<>))
+import Data.Semigroup as Sem
 import Data.Time
 import System.IO
 import Test.QuickCheck hiding (Result)
@@ -58,9 +61,12 @@ data Test = Case  String (IO Message)
           | Suite String TestSuite
 
+instance Sem.Semigroup TestSuite where
+   TS xs <> TS ys = TS (xs <> ys)
+
 instance Monoid TestSuite where
-   mempty = TS mempty
-   TS xs `mappend` TS ys = TS (xs <> ys)
+   mempty  = TS mempty
+   mappend = (<>)
 
 tests :: TestSuite -> [Test]
 tests (TS xs) = toList xs
@@ -243,9 +249,8 @@       ", warnings: " ++ show (nrOfWarnings result) ++
       ", "           ++ show (diffTime result)     ++ ")"
 
-instance Monoid Result where
-   mempty = Result mempty mempty 0 0 0 0 mempty
-   x `mappend` y = Result
+instance Sem.Semigroup Result where
+   x <> y = Result
       { suites       = suites x <> suites y
       , cases        = cases x  <> cases y
       , diffTime     = diffTime x     + diffTime y
@@ -255,6 +260,10 @@       , resultRating = resultRating x <> resultRating y
       }
 
+instance Monoid Result where
+   mempty  = Result mempty mempty 0 0 0 0 mempty
+   mappend = (<>)
+
 instance HasStatus Result where
    getStatus r | nrOfErrors r   > 0 = Error
                | nrOfWarnings r > 0 = Warning
@@ -352,9 +361,12 @@          | null (messageLines a) = "ok"
          | otherwise             = ""
 
+instance Sem.Semigroup Message where
+   M s r xs <> M t q ys = M (s <> t) (r <> q) (xs <> ys)
+
 instance Monoid Message where
-   mempty = M mempty mempty mempty
-   M s r xs `mappend` M t q ys = M (s <> t) (r <> q) (xs <> ys)
+   mempty  = M mempty mempty mempty
+   mappend = (<>)
 
 instance HasStatus Message where
    getStatus = messageStatus
@@ -375,9 +387,12 @@ data Status = Ok | Warning | Error
    deriving (Eq, Ord)
 
+instance Sem.Semigroup Status where
+   (<>) = max
+
 instance Monoid Status where
    mempty  = Ok
-   mappend = max
+   mappend = (<>)
 
 class HasStatus a where
    getStatus :: a -> Status
@@ -393,9 +408,12 @@ data Rating = Rating !Int | MaxRating
    deriving (Eq, Ord)
 
+instance Sem.Semigroup Rating where
+   (<>) = min
+
 instance Monoid Rating where
    mempty  = MaxRating
-   mappend = min
+   mappend = (<>)
 
 class HasRating a where
    rating :: a -> Maybe Int
src/Ideas/Utils/Typeable.hs view
@@ -2,7 +2,7 @@ {-# LANGUAGE TypeOperators #-}
 {-# LANGUAGE TypeFamilies #-}
 -----------------------------------------------------------------------------
--- Copyright 2016, Ideas project team. This file is distributed under the
+-- Copyright 2018, Ideas project team. This file is distributed under the
 -- terms of the Apache License 2.0. For more information, see the files
 -- "LICENSE.txt" and "NOTICE.txt", which are included in the distribution.
 -----------------------------------------------------------------------------
src/Ideas/Utils/Uniplate.hs view
@@ -1,5 +1,5 @@ -----------------------------------------------------------------------------
--- Copyright 2016, Ideas project team. This file is distributed under the
+-- Copyright 2018, Ideas project team. This file is distributed under the
 -- terms of the Apache License 2.0. For more information, see the files
 -- "LICENSE.txt" and "NOTICE.txt", which are included in the distribution.
 -----------------------------------------------------------------------------