implementation module UtilOptions import StdArray, StdBool, StdEnum, StdFile import UtilNewlinesFile import UtilStrictLists //-- :: Maybe x = Just x | Nothing //---- :: Label = {label :: !{#Char}} :: Value = {value :: !{#Char}} :: LineNumber :== Int :: Option = Option LineNumber Label (Maybe Value) [Option] :: *Input = { lineNumber :: !Int, lookAHead :: !String, file :: !*File} :: Conversions a = { toValue :: a -> (Maybe Value, [Option]), fromValue :: (Maybe Value) [Option] a -> a } // +++ remove tables (too much code) :: OptionsTableEntry a = E.value: { labelName :: !{#Char}, conversions :: !Conversions value, get :: a -> value, put :: value a -> a } :: OptionsTable a :== {!OptionsTableEntry a} //-- Simple :: Conversions a | toString, fromString a Simple = { toValue = \value -> ((Just {value = toString value}), []), fromValue = fromValue } where fromValue Nothing _ value = value fromValue (Just {value}) subOptionsTable list = fromString value SimpleWithStringConversion :: ({#Char} -> {#Char}) -> (Conversions a) | toString, fromString a SimpleWithStringConversion convert_string = { toValue = \value -> ((Just {value = toString value}), []), fromValue = fromValue } where fromValue Nothing _ value = value fromValue (Just {value}) subOptionsTable list = fromString (convert_string value) SimpleWithStringConversions :: ({#Char} -> {#Char}) ({#Char} -> {#Char}) -> Conversions a | toString, fromString a SimpleWithStringConversions convert_string_from convert_string_to = { toValue = \value -> ((Just {value = convert_string_to (toString value)}), []), fromValue = fromValue } where fromValue Nothing _ value = value fromValue (Just {value}) subOptionsTable list = fromString (convert_string_from value) Group :: (OptionsTable a) -> Conversions a Group subOptionTable = { toValue = \value -> (Nothing, PutOptions subOptionTable value), fromValue = \_ subOptions value -> GetOptions subOptionTable subOptions value } OptionalGroup :: (OptionsTable a) (a->Bool) -> Conversions a OptionalGroup subOptionTable is_empty_group = { toValue = \value = if (is_empty_group value) (Nothing, []) (Nothing, PutOptions subOptionTable value), fromValue = \_ subOptions value -> GetOptions subOptionTable subOptions value } List :: (OptionsTableEntry a) a -> Conversions (List a) List optionsTableEntry defaultValue = { toValue = \list -> (Nothing, putList list), fromValue = \_ table list -> getList table } where putList Nil = [] putList (h :! t) = case (PutOption h optionsTableEntry) of Nothing -> putList t Just option -> [option : putList t] getList [] = Nil getList [h : t] # h = GO optionsTableEntry h defaultValue # t = getList t = h :! t GO {conversions={fromValue}, get, put, labelName} (Option _ {label} value subOptions) currentValue | label <> labelName = currentValue = put (fromValue value subOptions (get currentValue)) currentValue SimpleOption l g p :== {labelName = l, conversions = Simple, get = g, put = p} GroupedOption l s g p :== {labelName = l, conversions = Group s, get = g, put = p} ListOption l s d g p :== {labelName = l, conversions = List s d, get = g, put = p} //-- EndOfInput :: *Input -> (!Bool, !*Input) EndOfInput input=:{lookAHead} = (lookAHead == "", input) LookAHead :: *File -> *Input LookAHead file = { lineNumber = 1, lookAHead = nextLine, file = nextFile } where (nextLine, nextFile) = readLine file NextLine :: *Input -> *Input NextLine input=:{file, lineNumber} = { input & lineNumber = lineNumber+1, lookAHead = nextLine, file = nextFile} where (nextLine, nextFile) = readLine file ConvertToString :: !String -> String ConvertToString str = StripNewline (FindColon 0 (size str) str) str where FindColon :: !Int !Int !String -> Int FindColon i len str | i >= len = i | str .[ i] == ':' = SkipSpaces (inc i) len str = FindColon (inc i) len str SkipSpaces :: !Int !Int !String -> Int SkipSpaces i len str | i >= len || str .[ i] <> ' ' = i = SkipSpaces (inc i) len str StripNewline :: !Int !String -> String StripNewline fro string | lmin1-fro < 0 = "" = string % (fro, dec lmin1) where lmin1 = dec (size string) CommentChar :== '#' LabelSeparatorChar :== ':' TabSize :== 4 copy a :== {e \\ e <-: a} RemoveWhiteSpace :: {#Char} -> {#Char} RemoveWhiteSpace string | firstFound >= stringSize = string | otherwise = shiftChars firstFound (firstFound+1) (copy string) where stringSize = size string firstFound = findChar 0 findChar i | i >= stringSize || string.[i] == ' ' || string.[i] == '\t' = i | otherwise = findChar (i+1) shiftChars :: !Int !Int *{#Char} -> {#Char} shiftChars toPos fromPos string | fromPos >= stringSize = string % (0, toPos-1) #! char = string.[fromPos] | char == ' ' || char == '\t' = shiftChars fromPos (toPos+1) string | otherwise = shiftChars (toPos+1) (fromPos+1) {string & [toPos] = char} RemoveNewLine :: {#Char} -> {#Char} RemoveNewLine string | string.[size string-1] == '\n' = string % (0, size string-2) | otherwise = string // lfold op :== foldl (flip op) lfold op r l :== lfold r l where lfold r [] = r lfold r [a:x] = lfold (op a r) x (:-) infixl 0 (:-) f a :== a f WriteOptionsFile :: !{#Char} ![Option] !*File -> *File WriteOptionsFile version options file = file :- fwrites ("Version: "+++version+++"\n") // DvA: set to 1.4 for New IDE development :- writeOptions 0 options where writeOptions _ [] file = file writeOptions indentation [h : t] file = writeOptions indentation t (writeOption indentation h file) writeOption :: Int Option !*File -> *File writeOption indentation (Option _ {label} value subOptions) file = file // ->> {' ' \\ i <- [0..indentation-1]} +++ label +++ " (writeOption)\n" :- fwrites {'\t' \\ i <- [0..indentation-1]} :- fwrites label :- writeValue value :- fwritec '\n' :- writeOptions (indentation+1) subOptions writeValue Nothing file = file writeValue (Just {value}) file = file :- fwrites ":\t" :- fwrites value ReadOptionsFile :: *File -> ([Option], *File) ReadOptionsFile file # (options, input) = parseOptions 0 (LookAHead file) = (options, input.file) where parseLabelAndOption :: !Int !Int !Int !{#Char} -> (Label, Maybe Value) parseLabelAndOption begin end size string | end >= size = ({label = RemoveWhiteSpace (string % (begin, end-1))}, Nothing) | string.[end] == LabelSeparatorChar = ({label = RemoveWhiteSpace (string % (begin, end-1))}, Just {value = skipSpaces (end+1)}) with skipSpaces position | position >= size = "" | string.[position] == ' ' || string.[position] == '\t' = skipSpaces (position+1) | otherwise = string % (position, size-1) | otherwise = parseLabelAndOption begin (end+1) size string parseOption :: !Int !Int !Int !*Input -> (!Option, !*Input) parseOption indentation currentIndentation offset input=:{lookAHead, lineNumber} | currentIndentation > indentation # (subOptions, input) = parseOptions currentIndentation input = (Option lineNumber {label=""} Nothing subOptions, input) | otherwise # (label, value) = parseLabelAndOption offset offset (size strippedLookAHead) strippedLookAHead with strippedLookAHead = (RemoveNewLine lookAHead) # (subOptions, input) = parseOptions (indentation+TabSize) (NextLine input) = (Option lineNumber label value subOptions, input) parseOptions :: !Int !*Input -> ([Option], !*Input) parseOptions indentation input # (currentIndentation, offset, input) = parseIndentation input # (eof, input) = EndOfInput input | eof || currentIndentation < indentation = ([], input) # (option, input) = parseOption indentation currentIndentation offset input # (options, input) = parseOptions indentation input = ([option : options], input) indentation :: {# Char } -> (!Int, !Int) indentation string = indentation 0 0 string where stringSize = size string - 1 indentation :: !Int !Int {# Char } -> (!Int, !Int) indentation offset indent string | offset >= stringSize || string.[offset] == CommentChar = (-1, offset) | string.[offset] == ' ' = indentation (offset+1) (indent+1) string | string.[offset] == '\t' = indentation (offset+1) (indent + (TabSize - (indent rem TabSize))) string | otherwise = (indent, offset) parseIndentation :: *Input -> (!Int, !Int, *Input) parseIndentation input=:{lookAHead} | lookAHead == "" = (0, 0, input) # (indentation, offset) = indentation lookAHead | indentation == (-1) = parseIndentation (NextLine input) | otherwise = (indentation, offset, input) ReadVersion :: !*File -> (!{#Char}, !*File) ReadVersion file # (string, file) = readLine file | EqualPrefix "Version:" string = (ConvertToString string, file) | otherwise = ("", file) where EqualPrefix :: String String -> Bool EqualPrefix prefix string = prefix == string % (0, size prefix - 1) //-- GetOptions :: !(OptionsTable a) ![Option] !a -> a GetOptions table [] value = value GetOptions table [option : options] value = GetOptions table options (GetOption table option value) GetOption :: (OptionsTable a) Option !a -> a GetOption table (Option _ {label=labelName} value subOptions) currentValue = case [optionEntry \\ optionEntry <-: table | optionEntry.labelName == labelName] of [] -> currentValue [{conversions={fromValue}, get, put} : _] -> put (fromValue value subOptions (get currentValue)) currentValue PutOptions :: !(OptionsTable a) a -> [Option] PutOptions table a = putOptions 0 where tableSize = size table putOptions i | i >= tableSize = [] // otherwise = case (PutOption a table.[i]) of Nothing -> putOptions (i+1) Just option -> [option : putOptions (i+1)] PutOption :: a (OptionsTableEntry a) -> Maybe Option PutOption value {labelName, conversions={toValue}, get} = (case (toValue (get value)) of (Nothing, []) -> Nothing (value, subOptions) -> Just (Option 0 {label=labelName} value subOptions))