Check argument list length

This commit is contained in:
Eugen Wissner 2024-10-31 22:19:48 +01:00
parent 43882a3a06
commit e2d4b76c0b
Signed by: belka
GPG Key ID: A27FDC1E8EE902C0
3 changed files with 87 additions and 65 deletions

11
TODO
View File

@ -10,17 +10,8 @@
- Don't ignore relocations where the symbol is not defined in the symbol table. - Don't ignore relocations where the symbol is not defined in the symbol table.
Report an error about an undefined symbol. Report an error about an undefined symbol.
# Name analysis
- Format error messages.
- Return non-zero error code on errors.
# Register allocation # Register allocation
- Each temporary variales gets a tn register where n is the variable index. If - Each temporary variable gets a tn register where n is the variable index. If
there more variables the allocation will fail with out of bounds runtime there more variables the allocation will fail with out of bounds runtime
error. Implement spill over. error. Implement spill over.
# Other
- Type analysis.

View File

@ -3,42 +3,61 @@ module Language.Elna.Frontend.TypeAnalysis
, -- Error(..) , -- Error(..)
) where ) where
import Control.Monad (unless)
import Control.Monad.Trans.Class (MonadTrans(..))
import Control.Monad.Trans.Except (Except, runExcept, throwE)
import Control.Monad.Trans.Reader (ReaderT, runReaderT, withReaderT, ask)
import Data.Foldable (traverse_)
import qualified Data.Vector as Vector
import qualified Language.Elna.Frontend.AST as AST import qualified Language.Elna.Frontend.AST as AST
import Language.Elna.Frontend.SymbolTable ({-Info(..), ParameterInfo(..), -}SymbolTable) import Language.Elna.Frontend.SymbolTable (Info(..), {-ParameterInfo(..), -}SymbolTable)
import qualified Language.Elna.Frontend.SymbolTable as SymbolTable
import Language.Elna.Frontend.Types (Type(..), booleanType, intType)
import Language.Elna.Location (Identifier(..))
typeAnalysis :: SymbolTable -> AST.Program -> () -- Maybe Error typeAnalysis :: SymbolTable -> AST.Program -> Maybe Error
typeAnalysis _globalTable = const () {- either Just (const Nothing) typeAnalysis globalTable = either Just (const Nothing)
. runExcept . runExcept
. flip runReaderT globalTable . flip runReaderT globalTable
. runTypeAnalysis . runTypeAnalysis
. program -} . program
{- {-
import Control.Applicative (Alternative(..)) import Control.Applicative (Alternative(..))
import Control.Monad.Trans.Except (Except, runExcept, throwE) -}
import Control.Monad.Trans.Reader (ReaderT, asks, runReaderT, withReaderT, ask)
import qualified Data.Vector as Vector
import Language.Elna.Location (Identifier(..))
import qualified Language.Elna.SymbolTable as SymbolTable
import Language.Elna.Types (Type(..), booleanType, intType)
import Control.Monad.Trans.Class (MonadTrans(..))
import Control.Monad (unless, when)
import Data.Foldable (traverse_)
data Error data Error
= ArithmeticExpressionError Type = UnexpectedProcedureInfoError Info
| ComparisonExpressionError Type Type
| UnexpectedVariableInfoError Info
| UnexpectedProcedureInfoError Info
| UndefinedSymbolError Identifier | UndefinedSymbolError Identifier
| InvalidConditionTypeError Type
| InvalidAssignmentError Type
| ExpectedLvalueError AST.Expression
| ParameterCountMismatchError Int Int | ParameterCountMismatchError Int Int
| UnexpectedVariableInfoError Info
| ArithmeticExpressionError Type
| ComparisonExpressionError Type Type
| InvalidConditionTypeError Type
{- | InvalidAssignmentError Type
| ExpectedLvalueError AST.Expression
| ArgumentTypeMismatchError Type Type | ArgumentTypeMismatchError Type Type
| ArrayIndexError Type | ArrayIndexError Type
| ArrayAccessError Type | ArrayAccessError Type -}
deriving (Eq, Show) deriving Eq
instance Show Error
where
show (UnexpectedProcedureInfoError info) =
"Expected to encounter a procedure, got: " <> show info
show (UndefinedSymbolError identifier) =
concat ["Symbol \"", show identifier, "\" is not defined"]
show (ParameterCountMismatchError parameterCount argumentCount)
= "The function was expected to receive " <> show argumentCount
<> " arguments, but got " <> show parameterCount
show (UnexpectedVariableInfoError info) =
"Expected to encounter a variable, got: " <> show info
show (ArithmeticExpressionError got) =
"Expected an arithmetic expression to be an integer, got: " <> show got
show (ComparisonExpressionError lhs rhs)
= "Expected an arithmetic expression to be an integer, got \""
<> show lhs <> "\" and \"" <> show rhs <> "\""
show (InvalidConditionTypeError got) =
"Expected a condition to be a boolean, got: " <> show got
newtype TypeAnalysis a = TypeAnalysis newtype TypeAnalysis a = TypeAnalysis
{ runTypeAnalysis :: ReaderT SymbolTable (Except Error) a { runTypeAnalysis :: ReaderT SymbolTable (Except Error) a
@ -61,7 +80,7 @@ program :: AST.Program -> TypeAnalysis ()
program (AST.Program declarations) = traverse_ declaration declarations program (AST.Program declarations) = traverse_ declaration declarations
declaration :: AST.Declaration -> TypeAnalysis () declaration :: AST.Declaration -> TypeAnalysis ()
declaration (AST.ProcedureDefinition procedureName _ _ body) = do declaration (AST.ProcedureDeclaration procedureName _ _ body) = do
globalTable <- TypeAnalysis ask globalTable <- TypeAnalysis ask
case SymbolTable.lookup procedureName globalTable of case SymbolTable.lookup procedureName globalTable of
Just (ProcedureInfo localTable _) -> TypeAnalysis Just (ProcedureInfo localTable _) -> TypeAnalysis
@ -72,29 +91,29 @@ declaration (AST.ProcedureDefinition procedureName _ _ body) = do
$ UnexpectedProcedureInfoError anotherInfo $ UnexpectedProcedureInfoError anotherInfo
Nothing -> TypeAnalysis $ lift $ throwE Nothing -> TypeAnalysis $ lift $ throwE
$ UndefinedSymbolError procedureName $ UndefinedSymbolError procedureName
declaration _ = pure () declaration (AST.TypeDefinition _ _) = pure ()
statement :: SymbolTable -> AST.Statement -> TypeAnalysis () statement :: SymbolTable -> AST.Statement -> TypeAnalysis ()
statement globalTable = \case statement globalTable = \case
AST.EmptyStatement -> pure () AST.EmptyStatement -> pure ()
AST.AssignmentStatement lhs rhs -> do {- AST.AssignmentStatement lhs rhs -> do
lhsType <- variableAccess globalTable lhs lhsType <- variableAccess globalTable lhs
rhsType <- expression globalTable rhs rhsType <- expression globalTable rhs
unless (lhsType == intType) unless (lhsType == intType)
$ TypeAnalysis $ lift $ throwE $ InvalidConditionTypeError lhsType $ TypeAnalysis $ lift $ throwE $ InvalidConditionTypeError lhsType
unless (rhsType == intType) unless (rhsType == intType)
$ TypeAnalysis $ lift $ throwE $ InvalidConditionTypeError rhsType $ TypeAnalysis $ lift $ throwE $ InvalidConditionTypeError rhsType
AST.WhileStatement whileCondition whileStatement -> do
conditionType <- condition globalTable whileCondition
unless (conditionType == booleanType)
$ TypeAnalysis $ lift $ throwE $ InvalidConditionTypeError conditionType
statement globalTable whileStatement -}
AST.IfStatement ifCondition ifStatement elseStatement -> do AST.IfStatement ifCondition ifStatement elseStatement -> do
conditionType <- condition globalTable ifCondition conditionType <- condition globalTable ifCondition
unless (conditionType == booleanType) unless (conditionType == booleanType)
$ TypeAnalysis $ lift $ throwE $ InvalidConditionTypeError conditionType $ TypeAnalysis $ lift $ throwE $ InvalidConditionTypeError conditionType
statement globalTable ifStatement statement globalTable ifStatement
maybe (pure ()) (statement globalTable) elseStatement maybe (pure ()) (statement globalTable) elseStatement
AST.WhileStatement whileCondition whileStatement -> do
conditionType <- condition globalTable whileCondition
unless (conditionType == booleanType)
$ TypeAnalysis $ lift $ throwE $ InvalidConditionTypeError conditionType
statement globalTable whileStatement
AST.CompoundStatement statements -> traverse_ (statement globalTable) statements AST.CompoundStatement statements -> traverse_ (statement globalTable) statements
AST.CallStatement procedureName arguments -> AST.CallStatement procedureName arguments ->
case SymbolTable.lookup procedureName globalTable of case SymbolTable.lookup procedureName globalTable of
@ -110,7 +129,7 @@ statement globalTable = \case
Nothing -> TypeAnalysis $ lift $ throwE Nothing -> TypeAnalysis $ lift $ throwE
$ UndefinedSymbolError procedureName $ UndefinedSymbolError procedureName
where where
checkArgument ParameterInfo{..} argument = do checkArgument SymbolTable.ParameterInfo{} _argument = pure () {-
argumentType <- expression globalTable argument argumentType <- expression globalTable argument
unless (argumentType == type') unless (argumentType == type')
$ TypeAnalysis $ lift $ throwE $ ArgumentTypeMismatchError type' argumentType $ TypeAnalysis $ lift $ throwE $ ArgumentTypeMismatchError type' argumentType
@ -137,11 +156,11 @@ variableAccess globalTable (AST.ArrayAccess arrayExpression indexExpression) = d
ArrayType _ baseType -> pure baseType ArrayType _ baseType -> pure baseType
nonArrayType -> TypeAnalysis $ lift $ throwE nonArrayType -> TypeAnalysis $ lift $ throwE
$ ArrayAccessError nonArrayType $ ArrayAccessError nonArrayType
-}
expression :: SymbolTable -> AST.Expression -> TypeAnalysis Type expression :: SymbolTable -> AST.Expression -> TypeAnalysis Type
expression globalTable = \case expression globalTable = \case
AST.VariableExpression variableExpression -> do {- AST.VariableExpression variableExpression -> do
variableAccess globalTable variableExpression variableAccess globalTable variableExpression -}
AST.LiteralExpression literal' -> literal literal' AST.LiteralExpression literal' -> literal literal'
AST.NegationExpression negation -> do AST.NegationExpression negation -> do
operandType <- expression globalTable negation operandType <- expression globalTable negation
@ -179,8 +198,6 @@ condition globalTable = \case
else TypeAnalysis $ lift $ throwE $ ComparisonExpressionError lhsType rhsType else TypeAnalysis $ lift $ throwE $ ComparisonExpressionError lhsType rhsType
literal :: AST.Literal -> TypeAnalysis Type literal :: AST.Literal -> TypeAnalysis Type
literal (AST.IntegerLiteral _) = pure intType literal (AST.DecimalLiteral _) = pure intType
literal (AST.HexadecimalLiteral _) = pure intType literal (AST.HexadecimalLiteral _) = pure intType
literal (AST.CharacterLiteral _) = pure intType literal (AST.CharacterLiteral _) = pure intType
literal (AST.BooleanLiteral _) = pure booleanType
-}

View File

@ -15,25 +15,39 @@ import Data.Maybe (fromMaybe)
import System.FilePath (replaceExtension, takeFileName) import System.FilePath (replaceExtension, takeFileName)
import Text.Megaparsec (runParser, errorBundlePretty) import Text.Megaparsec (runParser, errorBundlePretty)
import qualified Data.Text.IO as Text import qualified Data.Text.IO as Text
import System.Exit (ExitCode(..), exitWith)
import Control.Exception (IOException, catch)
-- * Error codes
--
-- 1 - Command line parsing failed and other errors.
-- 2 - The input could not be read.
-- 3 - Parse error.
-- 4 - Name analysis error.
-- 5 - Type error.
main :: IO () main :: IO ()
main = execParser commandLine >>= withCommandLine main = execParser commandLine >>= withCommandLine
where where
withCommandLine CommandLine{..} = withCommandLine CommandLine{..} =
let defaultOutput = flip fromMaybe output let defaultOutputName = replaceExtension (takeFileName input) "o"
$ replaceExtension (takeFileName input) "o" outputName = fromMaybe defaultOutputName output
in Text.readFile input in catch (Text.readFile input) (printAndExit 2 :: IOException -> IO a)
>>= withParsedInput defaultOutput >>= withParsedInput outputName
. runParser programP input . runParser programP input
withParsedInput output (Right program) = withParsedInput output (Right program)
either print (withSymbolTable output program) = either (printAndExit 4) (withSymbolTable output program)
$ nameAnalysis program $ nameAnalysis program
withParsedInput _ (Left errorBundle) = putStrLn withParsedInput _ (Left errorBundle)
$ errorBundlePretty errorBundle = putStrLn (errorBundlePretty errorBundle)
withSymbolTable output program symbolTable = >> exitWith (ExitFailure 3)
let _ = typeAnalysis symbolTable program withSymbolTable output program symbolTable
instructions = generateRiscV | Just typeError <- typeAnalysis symbolTable program =
$ allocate riscVConfiguration printAndExit 5 typeError
$ glue symbolTable program | otherwise =
in elfObject output let instructions = generateRiscV
$ riscv32Elf instructions $ allocate riscVConfiguration
$ glue symbolTable program
in elfObject output $ riscv32Elf instructions
printAndExit :: Show b => forall a. Int -> b -> IO a
printAndExit failureCode e = print e >> exitWith (ExitFailure failureCode)