module DDC.Core.Lexer.Offside
( Lexeme (..)
, applyOffside
, addStarts)
where
import DDC.Core.Lexer.Tokens
import DDC.Data.SourcePos
import DDC.Data.Token
data Lexeme n
= LexemeToken (Token (Tok n))
| LexemeStartLine Int
| LexemeStartBlock Int
deriving (Eq, Show)
data Paren
= ParenRound
| ParenBrace
deriving Show
type Context
= Int
applyOffside
:: (Eq n, Show n)
=> [Paren]
-> [Context]
-> [Lexeme n]
-> [Token (Tok n)]
applyOffside ps [] (LexemeToken t : ts)
| isToken t (KA KModule)
|| isKNToken t
= t : applyOffside ps [] ts
applyOffside ps [] ls
| LexemeToken t1
: (LexemeStartBlock n) : ls' <- ls
, isToken t1 (KA KExport)
|| isToken t1 (KA KImport)
|| isToken t1 (KA KLetRec)
|| isToken t1 (KA KWhere)
= t1 : newCBra ls'
: applyOffside (ParenBrace : ps) [n] ls'
| LexemeToken t1 : LexemeToken t2
: LexemeStartBlock n : ls' <- ls
, isToken t1 (KA KImport) || isToken t1 (KA KExport)
, isToken t2 (KA KType) || isToken t2 (KA KValue)
= t1 : t2 : newCBra ls'
: applyOffside (ParenBrace : ps) [n] ls'
| LexemeToken t1 : LexemeToken t2 : LexemeToken t3 : LexemeToken t4
: LexemeStartBlock n : ls' <- ls
, isToken t1 (KA KImport) || isToken t1 (KA KExport)
, isToken t2 (KA KForeign)
, isToken t4 (KA KType) || isToken t4 (KA KValue)
= t1 : t2 : t3 : t4 : newCBra ls'
: applyOffside (ParenBrace : ps) [n] ls'
applyOffside ps [] (LexemeStartLine _ : ts)
= applyOffside ps [] ts
applyOffside ps [] (LexemeStartBlock _ : ts)
= applyOffside ps [] ts
applyOffside ps mm@(m : ms) (t@(LexemeStartLine n) : ts)
| m == n
= newSemiColon ts : applyOffside ps mm ts
| n <= m
= case ps of
ParenBrace : ps'
-> newCKet ts : applyOffside ps' ms (t : ts)
ParenRound : _
-> applyOffside ps ms ts
_ -> error $ "ddc-core: paren / layout context mismatch."
| otherwise
= applyOffside ps mm ts
applyOffside ps mm@(m : ms) (LexemeStartBlock n : ts)
| n > m
= newCBra ts : applyOffside (ParenBrace : ps) (n : m : ms) ts
| tNext : _ <- dropNewLinesLexeme ts
= error $ "ddc-core: layout error on " ++ show tNext ++ "."
| [] <- dropNewLinesLexeme ts
= error "ddc-core: tried to start new context at end of file."
| otherwise
= newCBra ts : newCKet ts : applyOffside ps mm (LexemeStartLine n : ts)
applyOffside ps ms
(LexemeToken t@Token { tokenTok = KA KBraceBra } : ts)
= t : applyOffside (ParenBrace : ps) (0 : ms) ts
applyOffside ps mm
(LexemeToken t@Token { tokenTok = KA KBraceKet } : ts)
| 0 : ms <- mm
, ParenBrace : ps' <- ps
= t : applyOffside ps' ms ts
| _tNext : _ <- dropNewLinesLexeme ts
= [newOffsideClosingBrace ts]
applyOffside ps ms
(LexemeToken t@Token { tokenTok = KA KRoundBra } : ts)
= t : applyOffside (ParenRound : ps) ms ts
applyOffside (ParenBrace : ps) (m : ms)
(lt@(LexemeToken Token { tokenTok = KA KRoundKet }) : ts)
| m /= 0
= newCKet ts : applyOffside ps ms (lt : ts)
applyOffside (ParenRound : ps) ms
(LexemeToken t@Token { tokenTok = KA KRoundKet } : ts)
= t : applyOffside ps ms ts
applyOffside ps ms (LexemeToken t : ts)
= t : applyOffside ps ms ts
applyOffside _ [] [] = []
applyOffside ps (_ : ms) []
= newCKet [] : applyOffside ps ms []
addStarts :: (Eq n, Show n) => [Token (Tok n)] -> [Lexeme n]
addStarts ts
= case dropNewLines ts of
(t1 : tsRest)
| not $ or $ map (isToken t1) [KA KBraceBra]
-> LexemeStartBlock (tokenColumn t1) : addStarts' (t1 : tsRest)
| otherwise
-> addStarts' (t1 : tsRest)
[] -> []
addStarts' :: Eq n => [Token (Tok n)] -> [Lexeme n]
addStarts' ts
| Just (ts1, ts2) <- splitBlockStart ts
, [] <- dropNewLines ts2
= [LexemeToken t | t <- ts1]
++ [LexemeStartBlock 0]
| Just (ts1, ts2) <- splitBlockStart ts
, t2 : tsRest <- dropNewLines ts2
, not $ isToken t2 (KA KBraceBra)
= [LexemeToken t | t <- ts1]
++ [LexemeStartBlock (tokenColumn t2)]
++ addStarts' (t2 : tsRest)
| t1 : ts' <- ts
, isToken t1 (KA KBraceBra)
= LexemeToken t1 : addStarts' ts'
| t1 : ts' <- ts
, isToken t1 (KA KBraceKet)
= LexemeToken t1 : addStarts' ts'
| t1 : ts' <- ts
, isToken t1 (KM KNewLine)
, t2 : tsRest <- dropNewLines ts'
, not $ isToken t2 (KA KBraceBra)
= LexemeStartLine (tokenColumn t2)
: addStarts' (t2 : tsRest)
| t1 : ts' <- ts
, isToken t1 (KM KNewLine)
= addStarts' ts'
| t1 : ts' <- ts
= LexemeToken t1 : addStarts' ts'
| otherwise
= []
dropNewLines :: Eq n => [Token (Tok n)] -> [Token (Tok n)]
dropNewLines [] = []
dropNewLines (t1:ts)
| isToken t1 (KM KNewLine)
= dropNewLines ts
| otherwise
= t1 : ts
dropNewLinesLexeme :: Eq n => [Lexeme n] -> [Lexeme n]
dropNewLinesLexeme ll
= case ll of
[] -> []
LexemeToken t1 : ts
| isToken t1 (KM KNewLine)
-> dropNewLinesLexeme ts
l : ls
-> l : dropNewLinesLexeme ls
splitBlockStart
:: [Token (Tok n)]
-> Maybe ([Token (Tok n)], [Token (Tok n)])
splitBlockStart toks
| t1@Token { tokenTok = KA KExport } : t2@Token { tokenTok = KA KType } : ts
<- toks = Just ([t1, t2], ts)
| t1@Token { tokenTok = KA KExport } : t2@Token { tokenTok = KA KValue } : ts
<- toks = Just ([t1, t2], ts)
| t1@Token { tokenTok = KA KExport } : t2@Token { tokenTok = KA KForeign }
: t3 : t4@Token { tokenTok = KA KValue } : ts
<- toks = Just ([t1, t2, t3, t4], ts)
| t1@Token { tokenTok = KA KImport } : t2@Token { tokenTok = KA KType } : ts
<- toks = Just ([t1, t2], ts)
| t1@Token { tokenTok = KA KImport } : t2@Token { tokenTok = KA KValue } : ts
<- toks = Just ([t1, t2], ts)
| t1@Token { tokenTok = KA KImport } : t2@Token { tokenTok = KA KForeign }
: t3 : t4@Token { tokenTok = KA KType } : ts
<- toks = Just ([t1, t2, t3, t4], ts)
| t1@Token { tokenTok = KA KImport} : t2@Token { tokenTok = KA KForeign}
: t3 : t4@Token { tokenTok = KA KValue } : ts
<- toks = Just ([t1, t2, t3, t4], ts)
| t1@Token { tokenTok = KA KDo } : ts <- toks = Just ([t1], ts)
| t1@Token { tokenTok = KA KOf } : ts <- toks = Just ([t1], ts)
| t1@Token { tokenTok = KA KLetRec } : ts <- toks = Just ([t1], ts)
| t1@Token { tokenTok = KA KWhere } : ts <- toks = Just ([t1], ts)
| t1@Token { tokenTok = KA KExport } : ts <- toks = Just ([t1], ts)
| t1@Token { tokenTok = KA KImport } : ts <- toks = Just ([t1], ts)
| otherwise
= Nothing
isToken :: Eq n => Token (Tok n) -> Tok n -> Bool
isToken (Token tok _) tok2 = tok == tok2
isKNToken :: Eq n => Token (Tok n) -> Bool
isKNToken (Token (KN _) _) = True
isKNToken _ = False
newCBra :: [Lexeme n] -> Token (Tok n)
newCBra ts
= (takeTok ts) { tokenTok = KA KBraceBra }
newCKet :: [Lexeme n] -> Token (Tok n)
newCKet ts
= (takeTok ts) { tokenTok = KA KBraceKet }
newSemiColon :: [Lexeme n] -> Token (Tok n)
newSemiColon ts
= (takeTok ts) { tokenTok = KA KSemiColon }
newOffsideClosingBrace :: [Lexeme n] -> Token (Tok n)
newOffsideClosingBrace ts
= (takeTok ts) { tokenTok = KM KOffsideClosingBrace }
takeTok :: [Lexeme n] -> Token (Tok n)
takeTok []
= Token (KJunk "") (SourcePos "" 0 0)
takeTok (l : ls)
= case l of
LexemeToken (Token { tokenTok = KM KNewLine })
-> takeTok ls
LexemeToken t -> t
LexemeStartLine _ -> takeTok ls
LexemeStartBlock _ -> takeTok ls