quebex

A software analysis framework built around the QBE intermediate language

git clone https://git.8pit.net/quebex.git

  1-- SPDX-FileCopyrightText: 2025 Sören Tempel <soeren+git@soeren-tempel.net>
  2--
  3-- SPDX-License-Identifier: GPL-3.0-only
  4
  5-- | This module provides top-level definitions for representing programs
  6-- written in the [QBE](https://c9x.me/compile/) intermediate representation.
  7module Language.QBE
  8  ( Program,
  9    Definition (..),
 10    globalFuncs,
 11    Language.QBE.parse,
 12    ExecError (..),
 13    parseAndFind,
 14  )
 15where
 16
 17import 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.Parsec
 23  ( ParseError,
 24    Parser,
 25    SourceName,
 26    choice,
 27    eof,
 28    many,
 29    parse,
 30    try,
 31  )
 32
 33-- | A QBE program consists of a sequence of definitions. Four types of objects
 34-- 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 Definition
 38  = -- | Definition of data (e.g. a string).
 39    DefData DataDef
 40  | -- | Definition of an aggregate data type.
 41    DefType TypeDef
 42  | -- | Definition of a function.
 43    DefFunc FuncDef
 44  | -- | Definition of a debug file.
 45    DefFile String
 46  deriving (Eq, Show)
 47
 48parseDef :: Parser Definition
 49parseDef =
 50  choice
 51    [ DefType <$> typeDef,
 52      -- Need to try funcDef as both funcDef and
 53      -- dataDef start with a linkage definition.
 54      --
 55      -- TODO: Try parsing linkage then funcDef <|> dataDef.
 56      DefData <$> try dataDef,
 57      DefFunc <$> funcDef,
 58      DefFile <$> fileDef
 59    ]
 60
 61-- | A parsed QBE program, represented as a list of 'Definition' values.
 62type Program = [Definition]
 63
 64-- | Wrapper to parse a QBE program using 'Text.ParserCombinators.Parsec.parse'.
 65parse :: SourceName -> String -> Either ParseError Program
 66parse =
 67  Text.ParserCombinators.Parsec.parse
 68    (skipInitComments *> many parseDef <* eof)
 69
 70-- | Utility function to obtain all functions defined in a QBE 'Program'.
 71globalFuncs :: Program -> [FuncDef]
 72globalFuncs = mapMaybe globalFuncs'
 73  where
 74    globalFuncs' :: Definition -> Maybe FuncDef
 75    globalFuncs' (DefFunc f) = Just f
 76    globalFuncs' _ = Nothing
 77
 78------------------------------------------------------------------------
 79
 80-- | Custom 'Exception' used for error handling in 'parseAndFind'.
 81data ExecError
 82  = -- | The input is not a valid QBE program.
 83    ESyntaxError ParseError
 84  | -- | The given entry function is not defined in the QBE program.
 85    EUnknownEntry GlobalIdent
 86  deriving (Show)
 87
 88instance Exception ExecError
 89
 90-- | Utility function for the common task of parsing an input as a QBE
 91-- 'Program' and, within that program, finding the entry function. If the
 92-- function doesn't exist or a the input is invalid a 'ExecError' exception
 93-- is thrown.
 94parseAndFind ::
 95  (MonadThrow m) =>
 96  GlobalIdent ->
 97  String ->
 98  m (Program, FuncDef)
 99parseAndFind entryIdent input = do
100  prog <- case Language.QBE.parse "" input of -- TODO: file name
101    Right rt -> pure rt
102    Left err -> throwM $ ESyntaxError err
103
104  let funcs = globalFuncs prog
105  func <- case find (\f -> fName f == entryIdent) funcs of
106    Just x -> pure x
107    Nothing -> throwM $ EUnknownEntry entryIdent
108
109  pure (prog, func)