Load .mima-symbol file
This commit is contained in:
parent
d6db284c22
commit
bc2594bf69
3 changed files with 154 additions and 34 deletions
|
|
@ -1,7 +1,11 @@
|
||||||
module Mima.Parse.Common
|
module Mima.Parse.Common
|
||||||
( Parser
|
( Parser
|
||||||
|
-- * Character specifications
|
||||||
|
, isConnecting
|
||||||
|
, isWhitespace
|
||||||
-- * Basic parsers
|
-- * Basic parsers
|
||||||
, whitespace
|
, whitespace
|
||||||
|
, labelName
|
||||||
-- ** Number literals
|
-- ** Number literals
|
||||||
, binDigit
|
, binDigit
|
||||||
, decDigit
|
, decDigit
|
||||||
|
|
@ -14,14 +18,6 @@ module Mima.Parse.Common
|
||||||
, asLargeValue
|
, asLargeValue
|
||||||
, asSmallValue
|
, asSmallValue
|
||||||
, fixedWidthHexAddress
|
, fixedWidthHexAddress
|
||||||
-- * Nice error messages
|
|
||||||
, defaultPosState
|
|
||||||
, WeedError
|
|
||||||
, WeedErrorBundle
|
|
||||||
-- ** Remembering an element's offset
|
|
||||||
, WithOffset
|
|
||||||
, errorAt
|
|
||||||
, errorAt'
|
|
||||||
) where
|
) where
|
||||||
|
|
||||||
import Data.Char
|
import Data.Char
|
||||||
|
|
@ -34,11 +30,27 @@ import Mima.Word
|
||||||
|
|
||||||
type Parser = Parsec Void T.Text
|
type Parser = Parsec Void T.Text
|
||||||
|
|
||||||
|
{- Character specifications -}
|
||||||
|
|
||||||
|
isConnecting :: Char -> Bool
|
||||||
|
isConnecting '_' = True
|
||||||
|
isConnecting '-' = True
|
||||||
|
isConnecting _ = False
|
||||||
|
|
||||||
|
isWhitespace :: Char -> Bool
|
||||||
|
isWhitespace '\n' = False
|
||||||
|
isWhitespace c = isSpace c
|
||||||
|
|
||||||
|
{- Basic parsers -}
|
||||||
|
|
||||||
whitespace :: Parser Char
|
whitespace :: Parser Char
|
||||||
whitespace = label "whitespace" $ satisfy isWhitespace
|
whitespace = label "whitespace" $ satisfy isWhitespace
|
||||||
where
|
|
||||||
isWhitespace '\n' = False
|
labelName :: Parser T.Text
|
||||||
isWhitespace c = isSpace c
|
labelName = label "label" $ do
|
||||||
|
t <- satisfy isAlpha
|
||||||
|
ts <- takeWhileP Nothing (\c -> isAlphaNum c || isConnecting c)
|
||||||
|
pure $ T.singleton t <> ts
|
||||||
|
|
||||||
binDigit :: (Num a) => Parser a
|
binDigit :: (Num a) => Parser a
|
||||||
binDigit = label "binary digit" $ token helper Set.empty
|
binDigit = label "binary digit" $ token helper Set.empty
|
||||||
|
|
@ -127,26 +139,3 @@ fixedWidthHexAddress :: Parser MimaAddress
|
||||||
fixedWidthHexAddress = label "fixed-width hexadecimal address"
|
fixedWidthHexAddress = label "fixed-width hexadecimal address"
|
||||||
$ asLargeValue
|
$ asLargeValue
|
||||||
$ fixedWidthHex 5
|
$ fixedWidthHex 5
|
||||||
|
|
||||||
{- Nice error messages -}
|
|
||||||
|
|
||||||
defaultPosState :: FilePath -> T.Text -> PosState T.Text
|
|
||||||
defaultPosState filename input = PosState
|
|
||||||
{ pstateInput = input
|
|
||||||
, pstateOffset = 0
|
|
||||||
, pstateSourcePos = initialPos filename
|
|
||||||
, pstateTabWidth = defaultTabWidth
|
|
||||||
, pstateLinePrefix = ""
|
|
||||||
}
|
|
||||||
|
|
||||||
type WeedError = ParseError T.Text Void
|
|
||||||
type WeedErrorBundle = ParseErrorBundle T.Text Void
|
|
||||||
|
|
||||||
data WithOffset a = WithOffset Int a
|
|
||||||
deriving (Show)
|
|
||||||
|
|
||||||
errorAt :: WithOffset a -> String -> WeedError
|
|
||||||
errorAt wo errorMsg = errorAt' wo [errorMsg]
|
|
||||||
|
|
||||||
errorAt' :: WithOffset a -> [String] -> WeedError
|
|
||||||
errorAt' (WithOffset o _) = FancyError o . Set.fromList . map ErrorFail
|
|
||||||
|
|
|
||||||
74
src/Mima/Parse/SymbolFile.hs
Normal file
74
src/Mima/Parse/SymbolFile.hs
Normal file
|
|
@ -0,0 +1,74 @@
|
||||||
|
{-# LANGUAGE OverloadedStrings #-}
|
||||||
|
|
||||||
|
module Mima.Parse.SymbolFile
|
||||||
|
( parseSymbolFile
|
||||||
|
, weedSymbolFile
|
||||||
|
, loadSymbolFile
|
||||||
|
) where
|
||||||
|
|
||||||
|
import Control.Monad
|
||||||
|
import qualified Data.Map as Map
|
||||||
|
import qualified Data.Text as T
|
||||||
|
import Text.Megaparsec
|
||||||
|
|
||||||
|
import Mima.Parse.Common
|
||||||
|
import Mima.Parse.Lexeme
|
||||||
|
import Mima.Parse.Weed
|
||||||
|
import Mima.Word
|
||||||
|
|
||||||
|
type LabelName = T.Text
|
||||||
|
|
||||||
|
{- Parsing -}
|
||||||
|
|
||||||
|
lWhitespace :: Parser Char
|
||||||
|
lWhitespace = lexeme whitespace
|
||||||
|
|
||||||
|
lAddress :: Parser MimaAddress
|
||||||
|
lAddress = lexeme fixedWidthHexAddress
|
||||||
|
|
||||||
|
lLabels :: Parser [WithOffset LabelName]
|
||||||
|
lLabels = lexeme $ sepBy1 (withOffset labelName) lWhitespace
|
||||||
|
|
||||||
|
lLine :: Parser (MimaAddress, [WithOffset LabelName])
|
||||||
|
lLine = do
|
||||||
|
addr <- lAddress
|
||||||
|
void $ symbol ":"
|
||||||
|
labels <- lLabels
|
||||||
|
lNewlines
|
||||||
|
pure (addr, labels)
|
||||||
|
|
||||||
|
-- Does not keep the last list to appear for a certain key, but concatenates
|
||||||
|
-- them all.
|
||||||
|
combineLines :: [(MimaAddress, [WithOffset LabelName])]
|
||||||
|
-> Map.Map MimaAddress [WithOffset LabelName]
|
||||||
|
combineLines = ($ Map.empty) . mconcat . reverse . map (uncurry $ Map.insertWith (++))
|
||||||
|
|
||||||
|
parseSymbolFile :: Parser (Map.Map MimaAddress [WithOffset LabelName])
|
||||||
|
parseSymbolFile = space *> many lNewline *> (combineLines <$> many lLine) <* eof
|
||||||
|
|
||||||
|
{- Weeding -}
|
||||||
|
|
||||||
|
wBuildMap :: [(WithOffset LabelName, MimaAddress)]
|
||||||
|
-> Weed WeedError (Map.Map LabelName MimaAddress)
|
||||||
|
wBuildMap = foldM helper Map.empty
|
||||||
|
where
|
||||||
|
helper :: Map.Map LabelName MimaAddress
|
||||||
|
-> (WithOffset LabelName, MimaAddress)
|
||||||
|
-> Weed WeedError (Map.Map LabelName MimaAddress)
|
||||||
|
helper m (l, addr)
|
||||||
|
| name `Map.member` m = do
|
||||||
|
harmless $ errorAt l "label was specified more than once"
|
||||||
|
pure m
|
||||||
|
| otherwise = pure $ Map.insert name addr m
|
||||||
|
where name = woValue l
|
||||||
|
|
||||||
|
weedSymbolFile :: Map.Map MimaAddress [WithOffset LabelName]
|
||||||
|
-> Weed WeedError (Map.Map LabelName MimaAddress)
|
||||||
|
weedSymbolFile m =
|
||||||
|
let pairs = [(l, a) | (a, ls) <- Map.assocs m, l <- ls]
|
||||||
|
in wBuildMap pairs
|
||||||
|
|
||||||
|
loadSymbolFile :: FilePath -> T.Text -> Either WeedErrorBundle (Map.Map LabelName MimaAddress)
|
||||||
|
loadSymbolFile filename input = do
|
||||||
|
unweeded <- parse parseSymbolFile filename input
|
||||||
|
runWeedBundle filename input $ weedSymbolFile unweeded
|
||||||
|
|
@ -5,11 +5,28 @@ module Mima.Parse.Weed
|
||||||
, runWeed
|
, runWeed
|
||||||
, critical
|
, critical
|
||||||
, harmless
|
, harmless
|
||||||
|
-- * Nice error messages
|
||||||
|
, defaultPosState
|
||||||
|
, WeedError
|
||||||
|
, WeedErrorBundle
|
||||||
|
-- ** Remembering an element's offset
|
||||||
|
, WithOffset(..)
|
||||||
|
, withOffset
|
||||||
|
, errorAt
|
||||||
|
, errorAt'
|
||||||
|
, runWeedBundle
|
||||||
) where
|
) where
|
||||||
|
|
||||||
import qualified Data.List.NonEmpty as NE
|
import qualified Data.List.NonEmpty as NE
|
||||||
import Data.Monoid
|
import Data.Monoid
|
||||||
|
import qualified Data.Set as Set
|
||||||
|
import qualified Data.Text as T
|
||||||
|
import Data.Void
|
||||||
|
import Text.Megaparsec
|
||||||
|
|
||||||
|
import Mima.Parse.Common
|
||||||
|
|
||||||
|
-- The star of the show
|
||||||
data Weed e a = Weed (Endo [e]) (Either e a)
|
data Weed e a = Weed (Endo [e]) (Either e a)
|
||||||
|
|
||||||
instance Functor (Weed e) where
|
instance Functor (Weed e) where
|
||||||
|
|
@ -45,3 +62,43 @@ critical e = Weed mempty (Left e)
|
||||||
|
|
||||||
harmless :: e -> Weed e ()
|
harmless :: e -> Weed e ()
|
||||||
harmless e = Weed (Endo (e:)) (Right ())
|
harmless e = Weed (Endo (e:)) (Right ())
|
||||||
|
|
||||||
|
{- Nice error messages -}
|
||||||
|
|
||||||
|
defaultPosState :: FilePath -> T.Text -> PosState T.Text
|
||||||
|
defaultPosState filename input = PosState
|
||||||
|
{ pstateInput = input
|
||||||
|
, pstateOffset = 0
|
||||||
|
, pstateSourcePos = initialPos filename
|
||||||
|
, pstateTabWidth = defaultTabWidth
|
||||||
|
, pstateLinePrefix = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
type WeedError = ParseError T.Text Void
|
||||||
|
type WeedErrorBundle = ParseErrorBundle T.Text Void
|
||||||
|
|
||||||
|
data WithOffset a = WithOffset
|
||||||
|
{ woOffset :: Int
|
||||||
|
, woValue :: a
|
||||||
|
}
|
||||||
|
deriving (Show)
|
||||||
|
|
||||||
|
instance (Eq a) => Eq (WithOffset a) where
|
||||||
|
a == b = woValue a == woValue b
|
||||||
|
|
||||||
|
instance (Ord a) => Ord (WithOffset a) where
|
||||||
|
compare a b = compare (woValue a) (woValue b)
|
||||||
|
|
||||||
|
withOffset :: Parser a -> Parser (WithOffset a)
|
||||||
|
withOffset p = WithOffset <$> getOffset <*> p
|
||||||
|
|
||||||
|
errorAt :: WithOffset a -> String -> WeedError
|
||||||
|
errorAt wo errorMsg = errorAt' wo [errorMsg]
|
||||||
|
|
||||||
|
errorAt' :: WithOffset a -> [String] -> WeedError
|
||||||
|
errorAt' wo = FancyError (woOffset wo) . Set.fromList . map ErrorFail
|
||||||
|
|
||||||
|
runWeedBundle :: FilePath -> T.Text -> Weed WeedError a -> Either WeedErrorBundle a
|
||||||
|
runWeedBundle filename input w = case runWeed w of
|
||||||
|
Left errors -> Left $ ParseErrorBundle errors $ defaultPosState filename input
|
||||||
|
Right a -> Right a
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue