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
 5module Language.QBE
 6  ( Program,
 7    Definition (..),
 8    globalFuncs,
 9    Language.QBE.parse,
10    ExecError (..),
11    parseAndFind,
12  )
13where
14
15import Control.Monad.Catch (Exception, MonadThrow, throwM)
16import Data.List (find)
17import Data.Maybe (mapMaybe)
18import Language.QBE.Parser (dataDef, funcDef, skipInitComments, typeDef)
19import Language.QBE.Types (DataDef, FuncDef, GlobalIdent, TypeDef, fName)
20import Text.ParserCombinators.Parsec
21  ( ParseError,
22    Parser,
23    SourceName,
24    choice,
25    eof,
26    many,
27    parse,
28  )
29
30data Definition
31  = DefData DataDef
32  | DefType TypeDef
33  | DefFunc FuncDef
34  deriving (Eq, Show)
35
36parseDef :: Parser Definition
37parseDef =
38  choice
39    [ DefType <$> typeDef,
40      DefFunc <$> funcDef,
41      DefData <$> dataDef
42    ]
43
44type Program = [Definition]
45
46globalFuncs :: Program -> [FuncDef]
47globalFuncs = mapMaybe globalFuncs'
48  where
49    globalFuncs' :: Definition -> Maybe FuncDef
50    globalFuncs' (DefFunc f) = Just f
51    globalFuncs' _ = Nothing
52
53parse :: SourceName -> String -> Either ParseError Program
54parse =
55  Text.ParserCombinators.Parsec.parse
56    (skipInitComments *> many parseDef <* eof)
57
58------------------------------------------------------------------------
59
60data ExecError
61  = ESyntaxError ParseError
62  | EUnknownEntry GlobalIdent
63  deriving (Show)
64
65instance Exception ExecError
66
67-- | Utility function for the common task of parsing an input as a QBE
68-- 'Program' and, within that program, finding the entry function. If the
69-- function doesn't exist or a the input is invalid an exception is thrown.
70parseAndFind ::
71  (MonadThrow m) =>
72  GlobalIdent ->
73  String ->
74  m (Program, FuncDef)
75parseAndFind entryIdent input = do
76  prog <- case Language.QBE.parse "" input of -- TODO: file name
77    Right rt -> pure rt
78    Left err -> throwM $ ESyntaxError err
79
80  let funcs = globalFuncs prog
81  func <- case find (\f -> fName f == entryIdent) funcs of
82    Just x -> pure x
83    Nothing -> throwM $ EUnknownEntry entryIdent
84
85  pure (prog, func)