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)
type Context
= Int
applyOffside
:: (Eq n, Show n)
=> [Context]
-> [Lexeme n]
-> [Token (Tok n)]
applyOffside [] (LexemeToken t : ts)
| isToken t (KA KModule)
|| isKNToken t
= t : applyOffside [] ts
applyOffside [] (LexemeToken t1 : (LexemeStartBlock n) : ls)
| isToken t1 (KA KLetRec)
|| isToken t1 (KA KExports)
|| isToken t1 (KA KImports)
= t1 : newCBra ls : applyOffside [n] ls
applyOffside [] (LexemeStartLine _ : ts)
= applyOffside [] ts
applyOffside [] (LexemeStartBlock _ : ts)
= applyOffside [] ts
applyOffside mm@(m : ms) (t@(LexemeStartLine n) : ts)
| m == n
= newSemiColon ts : applyOffside mm ts
| n < m
= newSemiColon ts : newCKet ts : applyOffside ms (t : ts)
| otherwise
= applyOffside mm ts
applyOffside mm@(m : ms) (LexemeStartBlock n : ts)
| n > m
= newCBra ts : applyOffside (n : m : ms) ts
| tNext : _ <- dropNewLinesLexeme ts
= error $ "DDC.Core.Lexer.Tokens.Offside: layout error on " ++ show tNext ++ "."
| [] <- dropNewLinesLexeme ts
= error "DDC.Core.Lexer.Tokens.Offside: tried to start new context at end of file."
| otherwise
= newCBra ts : newCKet ts : applyOffside mm (LexemeStartLine n : ts)
applyOffside mm (LexemeToken t@Token { tokenTok = KA KBraceKet } : ts)
| 0 : ms <- mm
= t : applyOffside ms ts
| _tNext : _ <- dropNewLinesLexeme ts
= [newOffsideClosingBrace ts]
applyOffside ms (LexemeToken t@Token { tokenTok = KA KBraceBra } : ts)
= t : applyOffside (0 : ms) ts
applyOffside ms (LexemeToken t : ts)
= t : applyOffside ms ts
applyOffside [] [] = []
applyOffside (_ : ms) [] = newCKet [] : applyOffside ms []
addStarts :: Eq 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' [] = []
addStarts' (t1 : ts)
| isBlockStart t1
, [] <- dropNewLines ts
= LexemeToken t1 : [LexemeStartBlock 0]
| isBlockStart t1
, t2 : tsRest <- dropNewLines ts
, not $ isToken t2 (KA KBraceBra)
= LexemeToken t1 : LexemeStartBlock (tokenColumn t2)
: addStarts' (t2 : tsRest)
| isToken t1 (KA KBraceBra)
= LexemeToken t1 : addStarts' ts
| isToken t1 (KA KBraceKet)
= LexemeToken t1 : addStarts' ts
| isToken t1 (KM KNewLine)
, t2 : tsRest <- dropNewLines ts
, not $ isToken t2 (KA KBraceBra)
= LexemeStartLine (tokenColumn t2)
: addStarts' (t2 : tsRest)
| isToken t1 (KM KNewLine)
= addStarts' ts
| otherwise
= LexemeToken t1 : addStarts' ts
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
isBlockStart :: Token (Tok n) -> Bool
isBlockStart Token { tokenTok = tok }
= case tok of
KA KDo -> True
KA KOf -> True
KA KLetRec -> True
KA KWhere -> True
KA KExports -> True
KA KImports -> True
_ -> False
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