1-- SPDX-FileCopyrightText: 2025 Sören Tempel <soeren+git@soeren-tempel.net>2--3-- SPDX-License-Identifier: GPL-3.0-only45-- | This module provides top-level definitions for representing programs6-- written in the [QBE](https://c9x.me/compile/) intermediate representation.7module Language.QBE8 ( Program,9 Definition (..),10 globalFuncs,11 Language.QBE.parse,12 ExecError (..),13 parseAndFind,14 )15where1617import Control.Monad.Catch (Exception, MonadThrow, throwM)18import Data.List (find)19import Data.Maybe (mapMaybe)20import Language.QBE.Parser (dataDef, fileDef, funcDef, skipInitComments, typeDef)21import Language.QBE.Types (DataDef, FuncDef, GlobalIdent, TypeDef, fName)22import Text.ParserCombinators.Parsec23 ( ParseError,24 Parser,25 SourceName,26 choice,27 eof,28 many,29 parse,30 try,31 )3233-- | A QBE program consists of a sequence of definitions. Four types of objects34-- can be defined: aggregate types, data, functions, and debugging information.35--36-- See also: The corresponding section of the [QBE specification](https://c9x.me/compile/doc/il-v1.2.html#Definitions).37data Definition38 = -- | Definition of data (e.g. a string).39 DefData DataDef40 | -- | Definition of an aggregate data type.41 DefType TypeDef42 | -- | Definition of a function.43 DefFunc FuncDef44 | -- | Definition of a debug file.45 DefFile String46 deriving (Eq, Show)4748parseDef :: Parser Definition49parseDef =50 choice51 [ DefType <$> typeDef,52 -- Need to try funcDef as both funcDef and53 -- dataDef start with a linkage definition.54 --55 -- TODO: Try parsing linkage then funcDef <|> dataDef.56 DefData <$> try dataDef,57 DefFunc <$> funcDef,58 DefFile <$> fileDef59 ]6061-- | A parsed QBE program, represented as a list of 'Definition' values.62type Program = [Definition]6364-- | Wrapper to parse a QBE program using 'Text.ParserCombinators.Parsec.parse'.65parse :: SourceName -> String -> Either ParseError Program66parse =67 Text.ParserCombinators.Parsec.parse68 (skipInitComments *> many parseDef <* eof)6970-- | Utility function to obtain all functions defined in a QBE 'Program'.71globalFuncs :: Program -> [FuncDef]72globalFuncs = mapMaybe globalFuncs'73 where74 globalFuncs' :: Definition -> Maybe FuncDef75 globalFuncs' (DefFunc f) = Just f76 globalFuncs' _ = Nothing7778------------------------------------------------------------------------7980-- | Custom 'Exception' used for error handling in 'parseAndFind'.81data ExecError82 = -- | The input is not a valid QBE program.83 ESyntaxError ParseError84 | -- | The given entry function is not defined in the QBE program.85 EUnknownEntry GlobalIdent86 deriving (Show)8788instance Exception ExecError8990-- | Utility function for the common task of parsing an input as a QBE91-- 'Program' and, within that program, finding the entry function. If the92-- function doesn't exist or a the input is invalid a 'ExecError' exception93-- is thrown.94parseAndFind ::95 (MonadThrow m) =>96 GlobalIdent ->97 String ->98 m (Program, FuncDef)99parseAndFind entryIdent input = do100 prog <- case Language.QBE.parse "" input of -- TODO: file name101 Right rt -> pure rt102 Left err -> throwM $ ESyntaxError err103104 let funcs = globalFuncs prog105 func <- case find (\f -> fName f == entryIdent) funcs of106 Just x -> pure x107 Nothing -> throwM $ EUnknownEntry entryIdent108109 pure (prog, func)