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, fileDef, 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    try,
29  )
30
31data Definition
32  = DefData DataDef
33  | DefType TypeDef
34  | DefFunc FuncDef
35  | DefFile String
36  deriving (Eq, Show)
37
38parseDef :: Parser Definition
39parseDef =
40  choice
41    [ DefType <$> typeDef,
42      -- Need to try funcDef as both funcDef and
43      -- dataDef start with a linkage definition.
44      --
45      -- TODO: Try parsing linkage then funcDef <|> dataDef.
46      DefData <$> try dataDef,
47      DefFunc <$> funcDef,
48      DefFile <$> fileDef
49    ]
50
51type Program = [Definition]
52
53globalFuncs :: Program -> [FuncDef]
54globalFuncs = mapMaybe globalFuncs'
55  where
56    globalFuncs' :: Definition -> Maybe FuncDef
57    globalFuncs' (DefFunc f) = Just f
58    globalFuncs' _ = Nothing
59
60parse :: SourceName -> String -> Either ParseError Program
61parse =
62  Text.ParserCombinators.Parsec.parse
63    (skipInitComments *> many parseDef <* eof)
64
65------------------------------------------------------------------------
66
67data ExecError
68  = ESyntaxError ParseError
69  | EUnknownEntry GlobalIdent
70  deriving (Show)
71
72instance Exception ExecError
73
74-- | Utility function for the common task of parsing an input as a QBE
75-- 'Program' and, within that program, finding the entry function. If the
76-- function doesn't exist or a the input is invalid an exception is thrown.
77parseAndFind ::
78  (MonadThrow m) =>
79  GlobalIdent ->
80  String ->
81  m (Program, FuncDef)
82parseAndFind entryIdent input = do
83  prog <- case Language.QBE.parse "" input of -- TODO: file name
84    Right rt -> pure rt
85    Left err -> throwM $ ESyntaxError err
86
87  let funcs = globalFuncs prog
88  func <- case find (\f -> fName f == entryIdent) funcs of
89    Just x -> pure x
90    Nothing -> throwM $ EUnknownEntry entryIdent
91
92  pure (prog, func)