idSymbol = oneOf ":!$%&*+/<=>?@\\^|-~"
schemeLanguageDef :: LanguageDef st
schemeLanguageDef = emptyDef {
commentLine = ";;"
, identStart = letter <|> idSymbol
, identLetter = alphaNum <|> idSymbol
, opStart = parserZero
, opLetter = parserZero
}
In this case we are creating a small language definition based on emptyDef. We're saying that we want all what
emptyDef
has but changing the values of commentLine
, identStart
, identLetter
, opStart
and opLetter
.
The Text.Parsec.Language package contains several predefined language definitions which you can use to create yours. Some of these definitions are based on each other, for example the
haskell98Def
and haskellStyle
. This is accomplished using Haskell record syntax for defining data types.
Records in Haskell allow the creation of abstract data type definitions that contain named fields . For example, here's a small definition of a data type to store the information of an editor color theme:
data Theme = ColorTheme {
keywordsColor :: Color,
backgroundColor :: Color,
fontSize :: Int,
operatorsColor :: Color,
literalsColor :: Color
}
deriving Show
An instance of this record can be created as follows:
baseTheme = ColorTheme { keywordsColor = Black,
backgroundColor = White,
fontSize = 10,
operatorsColor = Black,
literalsColor = Black }
One nice feature of Haskell records is that it allows the creation of a new record instance based on a existing one. Back to our artificial example, say that we want to create a dark theme which is the same as the base but with colors inverted.
darkTheme = baseTheme {
keywordsColor = White,
backgroundColor = Black,
operatorsColor = White,
literalsColor = White
}
Here we're saying that we want a copy of baseTheme
with keywordsColor
, backgroundColor
, operatorsColor
and literalsColor
changed to another value.
Pattern matching can be used to extract parts of the record. For example, say that we want to define a function to increase the current font size of a theme:
increaseFontSize :: Int -> Theme -> Theme
increaseFontSize amount theme@ColorTheme { fontSize = currentFontSize} =
theme { fontSize = currentFontSize + amount }
We can use this definition :
*Tests> increaseFontSize 5 baseTheme
ColorTheme {keywordsColor = Black, backgroundColor = White, fontSize = 15, operatorsColor = Black, literalsColor = Black}
Also accesor functions are defined for each part of the record. For example:
*Tests> backgroundColor baseTheme
White
For future posts I'm going to explore similar features that exist in other programming languages.