diff --git a/TODO b/TODO index 6327ff2..ef7c324 100644 --- a/TODO +++ b/TODO @@ -15,16 +15,15 @@ - Format error messages. - Return non-zero error code on errors. -# Built-in - -- Implement printc (with int argument). -- Implement exit() as standalone function. - # Register allocation - Temporary variables always use the same register, t0. Allocate registers for temporaries. +# Prarsing and abstract syntax tree + +- Parse signed hexadecimal numbers. + # Other - Type analysis. diff --git a/lib/Language/Elna/Backend/Allocator.hs b/lib/Language/Elna/Backend/Allocator.hs index 9ad849e..1ffc85d 100644 --- a/lib/Language/Elna/Backend/Allocator.hs +++ b/lib/Language/Elna/Backend/Allocator.hs @@ -36,6 +36,8 @@ allocate MachineConfiguration{..} = fmap function SubtractionQuadruple (operand operand1) (operand operand2) (Store temporaryRegister) quadruple (NegationQuadruple operand1 _) = NegationQuadruple (operand operand1) (Store temporaryRegister) + quadruple (ProductQuadruple operand1 operand2 _) = + ProductQuadruple (operand operand1) (operand operand2) (Store temporaryRegister) operand :: Operand Variable -> Operand (Store r) operand (IntOperand x) = IntOperand x operand (VariableOperand _) = VariableOperand (Store temporaryRegister) diff --git a/lib/Language/Elna/Backend/Intermediate.hs b/lib/Language/Elna/Backend/Intermediate.hs index 01a026b..e7c66cf 100644 --- a/lib/Language/Elna/Backend/Intermediate.hs +++ b/lib/Language/Elna/Backend/Intermediate.hs @@ -30,11 +30,11 @@ data Quadruple v | AddQuadruple (Operand v) (Operand v) v | SubtractionQuadruple (Operand v) (Operand v) v | NegationQuadruple (Operand v) v + | ProductQuadruple (Operand v) (Operand v) v {-| GoToQuadruple Label | AssignQuadruple Operand Variable | ArrayQuadruple Variable Operand Variable | ArrayAssignQuadruple Operand Operand Variable - | ProductQuadruple Operand Operand Variable | DivisionQuadruple Operand Operand Variable | EqualQuadruple Operand Operand Label | NonEqualQuadruple Operand Operand Label diff --git a/lib/Language/Elna/Frontend/AST.hs b/lib/Language/Elna/Frontend/AST.hs index 738ddcc..4c1c20b 100644 --- a/lib/Language/Elna/Frontend/AST.hs +++ b/lib/Language/Elna/Frontend/AST.hs @@ -12,10 +12,12 @@ module Language.Elna.Frontend.AST , Literal(..) ) where +import Data.Char (chr) import Data.Int (Int32) import Data.List (intercalate) -import Data.Word ({-Word16, -}Word32) +import Data.Word (Word8, Word32) import Language.Elna.Location (Identifier(..), showArrayType) +import Numeric (showHex) newtype Program = Program [Declaration] deriving Eq @@ -67,8 +69,8 @@ data Statement = EmptyStatement {-| AssignmentStatement VariableAccess Expression | IfStatement Condition Statement (Maybe Statement) - | WhileStatement Condition Statement - | CompoundStatement [Statement]-} + | WhileStatement Condition Statement -} + | CompoundStatement [Statement] | CallStatement Identifier [Expression] deriving Eq @@ -83,9 +85,9 @@ instance Show Statement , maybe "" ((<> " else ") . show) else' ] show (WhileStatement expression statement) = - concat ["while (", show expression, ") ", show statement, ";"] + concat ["while (", show expression, ") ", show statement, ";"]-} show (CompoundStatement statements) = - concat ["{\n", unlines (show <$> statements), " }"]-} + concat ["{\n", unlines (show <$> statements), " }"] show (CallStatement name parameters) = show name <> "(" <> intercalate ", " (show <$> parameters) <> ")" @@ -93,22 +95,18 @@ data VariableDeclaration = VariableDeclaration Identifier TypeExpression deriving Eq -newtype Literal +data Literal = IntegerLiteral Int32 - {- | HexadecimalLiteral Int32 - | CharacterLiteral Word16 - | BooleanLiteral Bool -} + | HexadecimalLiteral Int32 + | CharacterLiteral Word8 deriving Eq instance Show Literal where show (IntegerLiteral integer) = show integer - {- show (HexadecimalLiteral integer) = '0' : 'x' : showHex integer "" + show (HexadecimalLiteral integer) = '0' : 'x' : showHex integer "" show (CharacterLiteral character) = '\'' : chr (fromEnum character) : ['\''] - show (BooleanLiteral boolean) - | boolean = "true" - | otherwise = "false" -} instance Show VariableDeclaration where @@ -120,8 +118,8 @@ data Expression | SumExpression Expression Expression | SubtractionExpression Expression Expression | NegationExpression Expression -{- | VariableExpression VariableAccess | ProductExpression Expression Expression +{- | VariableExpression VariableAccess | DivisionExpression Expression Expression -} deriving Eq @@ -131,13 +129,10 @@ instance Show Expression show (SumExpression lhs rhs) = concat [show lhs, " + ", show rhs] show (SubtractionExpression lhs rhs) = concat [show lhs, " - ", show rhs] show (NegationExpression negation) = '-' : show negation - {- show (VariableExpression variable) = show variable show (ProductExpression lhs rhs) = concat [show lhs, " * ", show rhs] + {- show (VariableExpression variable) = show variable show (DivisionExpression lhs rhs) = concat [show lhs, " / ", show rhs] -} {- -import Data.Char (chr) -import Numeric (showHex) - data VariableAccess = VariableAccess Identifier | ArrayAccess VariableAccess Expression diff --git a/lib/Language/Elna/Frontend/NameAnalysis.hs b/lib/Language/Elna/Frontend/NameAnalysis.hs index 2915331..dc39826 100644 --- a/lib/Language/Elna/Frontend/NameAnalysis.hs +++ b/lib/Language/Elna/Frontend/NameAnalysis.hs @@ -144,11 +144,11 @@ expression globalTable (AST.SubtractionExpression lhs rhs) >> expression globalTable rhs expression globalTable (AST.NegationExpression negation) = expression globalTable negation -{- expression globalTable (AST.VariableExpression variableExpression) = - variableAccess globalTable variableExpression expression globalTable (AST.ProductExpression lhs rhs) = expression globalTable lhs >> expression globalTable rhs +{- expression globalTable (AST.VariableExpression variableExpression) = + variableAccess globalTable variableExpression expression globalTable (AST.DivisionExpression lhs rhs) = expression globalTable lhs >> expression globalTable rhs @@ -158,6 +158,8 @@ statement _ AST.EmptyStatement = pure () statement globalTable (AST.CallStatement name arguments) = checkSymbol globalTable name >> traverse_ (expression globalTable) arguments +statement globalTable (AST.CompoundStatement statements) = + traverse_ (statement globalTable) statements {- statement globalTable (AST.AssignmentStatement lvalue rvalue) = variableAccess globalTable lvalue >> expression globalTable rvalue @@ -168,8 +170,6 @@ statement globalTable (AST.IfStatement ifCondition ifStatement elseStatement) statement globalTable (AST.WhileStatement whileCondition loop) = condition globalTable whileCondition >> statement globalTable loop -statement globalTable (AST.CompoundStatement statements) = - traverse_ (statement globalTable) statements variableAccess :: SymbolTable -> AST.VariableAccess -> NameAnalysis () variableAccess globalTable (AST.ArrayAccess arrayExpression indexExpression) diff --git a/lib/Language/Elna/Frontend/Parser.hs b/lib/Language/Elna/Frontend/Parser.hs index 4093f25..75f359e 100644 --- a/lib/Language/Elna/Frontend/Parser.hs +++ b/lib/Language/Elna/Frontend/Parser.hs @@ -34,27 +34,24 @@ import Text.Megaparsec import qualified Text.Megaparsec.Char.Lexer as Lexer import Text.Megaparsec.Char ( alphaNumChar --- , char + , char , letterChar , space1 --- , string + , string ) import Control.Applicative (Alternative(..)) import Data.Maybe (isJust) --- import Data.Functor (($>)) type Parser = Parsec Void Text literalP :: Parser Literal literalP - = {- HexadecimalLiteral <$> (string "0x" *> lexeme Lexer.hexadecimal) - <|> -} IntegerLiteral <$> Lexer.signed space integerP - {- <|> CharacterLiteral <$> lexeme charP - <|> BooleanLiteral <$> (symbol "true" $> True) - <|> BooleanLiteral <$> (symbol "false" $> False) + = HexadecimalLiteral <$> (string "0x" *> lexeme Lexer.hexadecimal) + <|> IntegerLiteral <$> Lexer.signed space integerP + <|> CharacterLiteral <$> lexeme charP where charP = fromIntegral . fromEnum - <$> between (char '\'') (char '\'') Lexer.charLiteral -} + <$> between (char '\'') (char '\'') Lexer.charLiteral {- typeDefinitionP :: Parser Declaration typeDefinitionP = TypeDefinition @@ -73,7 +70,7 @@ termP = choice operatorTable :: [[Operator Parser Expression]] operatorTable = [ unaryOperator - -- , factorOperator + , factorOperator , termOperator ] where @@ -81,10 +78,10 @@ operatorTable = [ prefix "-" NegationExpression , prefix "+" id ] - {- factorOperator = + factorOperator = [ binary "*" ProductExpression - , binary "/" DivisionExpression - ] -} + -- , binary "/" DivisionExpression + ] termOperator = [ binary "+" SumExpression , binary "-" SubtractionExpression diff --git a/lib/Language/Elna/Frontend/SymbolTable.hs b/lib/Language/Elna/Frontend/SymbolTable.hs index 9ace33f..e90a942 100644 --- a/lib/Language/Elna/Frontend/SymbolTable.hs +++ b/lib/Language/Elna/Frontend/SymbolTable.hs @@ -40,9 +40,22 @@ scope parent (SymbolTable _ mappings) = SymbolTable (Just parent) mappings builtInSymbolTable :: SymbolTable builtInSymbolTable = SymbolTable Nothing $ HashMap.fromList - [ ("printi", ProcedureInfo empty Vector.empty) + [ ("printi", ProcedureInfo empty (Vector.singleton printiX)) + , ("printc", ProcedureInfo empty (Vector.singleton printcI)) + , ("exit", ProcedureInfo empty Vector.empty) , ("int", TypeInfo intType) ] + where + printiX = ParameterInfo + { name = "x" + , type' = intType + , isReferenceParameter = False + } + printcI = ParameterInfo + { name = "i" + , type' = intType + , isReferenceParameter = False + } toMap :: SymbolTable -> HashMap Identifier Info toMap (SymbolTable _ map') = map' diff --git a/lib/Language/Elna/Glue.hs b/lib/Language/Elna/Glue.hs index 2313b2b..85c8401 100644 --- a/lib/Language/Elna/Glue.hs +++ b/lib/Language/Elna/Glue.hs @@ -70,6 +70,8 @@ statement localTable (AST.CallStatement (AST.Identifier callName) arguments) = d $ CallQuadruple callName $ fromIntegral $ Vector.length argumentStatements +statement localTable (AST.CompoundStatement statements) = + fold <$> traverse (statement localTable) statements {- statement localTable (AST.AssignmentStatement variableAccess' assignee) = do (rhsOperand, rhsStatements) <- expression localTable assignee let variableType' = variableType variableAccess' localTable @@ -105,9 +107,7 @@ statement localTable (AST.WhileStatement whileCondition whileStatement) = do <> conditionStatements <> Vector.fromList [jumpConstructor startLabel, GoToQuadruple endLabel, LabelQuadruple startLabel] <> whileStatements - <> Vector.fromList [GoToQuadruple conditionLabel, LabelQuadruple endLabel] -statement localTable (AST.CompoundStatement statements) = - fold <$> traverse (statement localTable) statements -} + <> Vector.fromList [GoToQuadruple conditionLabel, LabelQuadruple endLabel] -} createTemporary :: Glue Variable createTemporary = do @@ -233,6 +233,8 @@ expression localTable = \case ( VariableOperand tempVariable , Vector.snoc statements negationQuadruple ) + (AST.ProductExpression lhs rhs) -> + binaryExpression ProductQuadruple lhs rhs {- (AST.VariableExpression variableExpression) -> do let variableType' = variableType variableExpression localTable variableAccess' <- variableAccess localTable variableExpression Nothing variableType' mempty @@ -246,8 +248,6 @@ expression localTable = \case ( VariableOperand arrayAddress , Vector.snoc statements arrayStatement ) - (AST.ProductExpression lhs rhs) -> - binaryExpression ProductQuadruple lhs rhs (AST.DivisionExpression lhs rhs) -> binaryExpression DivisionQuadruple lhs rhs -} where @@ -263,8 +263,5 @@ expression localTable = \case literal :: AST.Literal -> Operand Variable literal (AST.IntegerLiteral integer) = IntOperand integer -{-literal (AST.HexadecimalLiteral integer) = IntOperand integer +literal (AST.HexadecimalLiteral integer) = IntOperand integer literal (AST.CharacterLiteral character) = IntOperand $ fromIntegral character -literal (AST.BooleanLiteral boolean) - | boolean = IntOperand 1 - | otherwise = IntOperand 0 -} diff --git a/lib/Language/Elna/RiscV/CodeGenerator.hs b/lib/Language/Elna/RiscV/CodeGenerator.hs index d20488c..439f99c 100644 --- a/lib/Language/Elna/RiscV/CodeGenerator.hs +++ b/lib/Language/Elna/RiscV/CodeGenerator.hs @@ -132,6 +132,32 @@ quadruple (NegationQuadruple operand1 (Store register)) $ RiscV.BaseInstruction RiscV.Op $ RiscV.R register RiscV.SUB RiscV.Zero operandRegister1 $ RiscV.Funct7 0b0100000 +quadruple (ProductQuadruple operand1 operand2 (Store register)) + | IntOperand immediateOperand1 <- operand1 + , IntOperand immediateOperand2 <- operand2 = + lui (immediateOperand1 * immediateOperand2) register + | VariableOperand variableOperand1 <- operand1 + , VariableOperand variableOperand2 <- operand2 = + let Store operandRegister1 = variableOperand1 + Store operandRegister2 = variableOperand2 + in pure $ Instruction + $ RiscV.BaseInstruction RiscV.Op + $ RiscV.R register RiscV.MUL operandRegister1 operandRegister2 (RiscV.Funct7 0b0000001) + | VariableOperand variableOperand1 <- operand1 + , IntOperand immediateOperand2 <- operand2 = + multiplyImmediateRegister variableOperand1 immediateOperand2 + | IntOperand immediateOperand1 <- operand1 + , VariableOperand variableOperand2 <- operand2 = + multiplyImmediateRegister variableOperand2 immediateOperand1 + where + multiplyImmediateRegister variableOperand immediateOperand = + let statements = lui immediateOperand register + Store operandRegister = variableOperand + in Vector.snoc statements + $ Instruction + $ RiscV.BaseInstruction RiscV.Op + $ RiscV.R register RiscV.MUL register operandRegister + $ RiscV.Funct7 0b0000001 loadImmediateOrRegister :: RiscVOperand -> RiscV.XRegister -> (RiscV.XRegister, Vector Statement) loadImmediateOrRegister (IntOperand intValue) targetRegister = diff --git a/tests/expectations/exit_between_statements.txt b/tests/expectations/exit_between_statements.txt new file mode 100644 index 0000000..f2ad6c7 --- /dev/null +++ b/tests/expectations/exit_between_statements.txt @@ -0,0 +1 @@ +c diff --git a/tests/expectations/print_2_statements.txt b/tests/expectations/print_2_statements.txt new file mode 100644 index 0000000..783955e --- /dev/null +++ b/tests/expectations/print_2_statements.txt @@ -0,0 +1,2 @@ +13 +2097150 diff --git a/tests/expectations/print_char.txt b/tests/expectations/print_char.txt new file mode 100644 index 0000000..587be6b --- /dev/null +++ b/tests/expectations/print_char.txt @@ -0,0 +1 @@ +x diff --git a/tests/expectations/print_product.txt b/tests/expectations/print_product.txt new file mode 100644 index 0000000..83b33d2 --- /dev/null +++ b/tests/expectations/print_product.txt @@ -0,0 +1 @@ +1000 diff --git a/tests/expectations/printi_hex.txt b/tests/expectations/printi_hex.txt new file mode 100644 index 0000000..b0d7324 --- /dev/null +++ b/tests/expectations/printi_hex.txt @@ -0,0 +1 @@ +129 diff --git a/tests/vm/exit_between_statements.elna b/tests/vm/exit_between_statements.elna new file mode 100644 index 0000000..9a79e86 --- /dev/null +++ b/tests/vm/exit_between_statements.elna @@ -0,0 +1,5 @@ +proc main() { + printc('c'); + exit(); + printi(1234); +} diff --git a/tests/vm/print_2_statements.elna b/tests/vm/print_2_statements.elna new file mode 100644 index 0000000..e2b5970 --- /dev/null +++ b/tests/vm/print_2_statements.elna @@ -0,0 +1,4 @@ +proc main() { + printi(13); + printi(2097150); +} diff --git a/tests/vm/print_char.elna b/tests/vm/print_char.elna new file mode 100644 index 0000000..c2c8fed --- /dev/null +++ b/tests/vm/print_char.elna @@ -0,0 +1,3 @@ +proc main() { + printc('x'); +} diff --git a/tests/vm/print_product.elna b/tests/vm/print_product.elna new file mode 100644 index 0000000..b337428 --- /dev/null +++ b/tests/vm/print_product.elna @@ -0,0 +1,3 @@ +proc main() { + printi(20 * 50); +} diff --git a/tests/vm/printi_hex.elna b/tests/vm/printi_hex.elna new file mode 100644 index 0000000..57ec6e3 --- /dev/null +++ b/tests/vm/printi_hex.elna @@ -0,0 +1,3 @@ +proc main() { + printi(0x81); +} diff --git a/tools/builtin.s b/tools/builtin.s index ecea15b..0f5f82c 100644 --- a/tools/builtin.s +++ b/tools/builtin.s @@ -1,6 +1,12 @@ .global printi .type printi, @function +.global printc +.type printc, @function + +.global exit +.type exit, @function + .global _start .type _start, @function @@ -53,8 +59,35 @@ printi: addi sp, sp, 16 ret -_start: - call main +printc: + addi sp, sp, -12 + sw s0, 0(sp) + sw ra, 4(sp) + addi s0, sp, 12 + + addi t1, zero, '\n' + sb t1, -1(s0) + + lw t0, 0(s0) + sb t0, -2(s0) + + addi a0, zero, 1 + addi a1, s0, -2 + addi a2, zero, 2 + addi a7, zero, 64 + ecall + + lw s0, 0(sp) + lw ra, 4(sp) + addi sp, sp, 12 + ret + +exit: addi a0, zero, 0 addi a7, zero, 93 ecall + # ret + +_start: + call main + call exit