From c515d1ef1d94d51db6b4f7bb63157cbe7162faec Mon Sep 17 00:00:00 2001 From: Billy Moore Date: Tue, 20 Jan 2026 15:46:30 +0000 Subject: [PATCH 1/3] Tidy parseIntOperand and add TODO for multiple variable declaration. --- src/HSPC/Parse.hs | 65 ++++++++++++++++++++++++----------------------- 1 file changed, 33 insertions(+), 32 deletions(-) diff --git a/src/HSPC/Parse.hs b/src/HSPC/Parse.hs index 03ddc86..890213d 100644 --- a/src/HSPC/Parse.hs +++ b/src/HSPC/Parse.hs @@ -90,6 +90,7 @@ parseVariableDeclarations acc xs = do parseVariableDeclarations (expr : acc) rest parseVariableDeclaration :: [HSPCToken] -> Either ParseError ((String, HSPCDataType), [HSPCToken]) +-- TODO: Handle multiple identifiers seperated by commas parseVariableDeclaration (IdentifierTok name : ColonTok : dataTypeToc : xs) = do datatype <- toDataType dataTypeToc rest <- removeLeadingSemiColon xs @@ -163,35 +164,35 @@ parseIntOperand toks = case breakLastOuter (`elem` [PlusTok, MinusTok]) toks of right <- parseTerm after return $ Subtract left right _ -> error "Should be unreachable since we break on PlusTok || MinusTok" - -parseTerm :: [HSPCToken] -> Either ParseError Expression -parseTerm toks = case breakLastOuter (`elem` [IntDivideTok, MultiplyTok]) toks of - (before, []) -> parseFactor before - (_, [IntDivideTok]) -> Left $ ExpectedOperand "Expected operand after 'div'" - (before, IntDivideTok : after) -> do - left <- parseIntOperand before - right <- parseFactor after - return $ IntDivide left right - (_, [MultiplyTok]) -> Left $ ExpectedOperand "Expected operand after *" - (before, MultiplyTok : after) -> do - left <- parseIntOperand before - right <- parseFactor after - return $ Multiply left right - _ -> error "Should be unreachable since we break on IntDivideTok || MultiplyTok" - -parseFactor :: [HSPCToken] -> Either ParseError Expression -parseFactor [LiteralIntTok n] = Right $ IntLiteral n -parseFactor [IdentifierTok name] = Right $ Identifier name -parseFactor (PlusTok : xs) = do - operand <- parseFactor xs - return $ UnaryPlus operand -parseFactor (MinusTok : xs) = do - operand <- parseFactor xs - return $ UnaryMinus operand -parseFactor (OpenBracketTok : xs) = do - (inside, remaining) <- splitToMatchingBracket xs - case remaining of - [] -> parseIntOperand inside - (tok : _) -> Left $ UnexpectedToken tok -parseFactor (tok : _) = Left $ UnexpectedToken tok -parseFactor [] = Left $ ExpectedOperand "Expected operand" + where + parseTerm :: [HSPCToken] -> Either ParseError Expression + parseTerm toks = case breakLastOuter (`elem` [IntDivideTok, MultiplyTok]) toks of + (before, []) -> parseFactor before + (_, [IntDivideTok]) -> Left $ ExpectedOperand "Expected operand after 'div'" + (before, IntDivideTok : after) -> do + left <- parseIntOperand before + right <- parseFactor after + return $ IntDivide left right + (_, [MultiplyTok]) -> Left $ ExpectedOperand "Expected operand after *" + (before, MultiplyTok : after) -> do + left <- parseIntOperand before + right <- parseFactor after + return $ Multiply left right + _ -> error "Should be unreachable since we break on IntDivideTok || MultiplyTok" + + parseFactor :: [HSPCToken] -> Either ParseError Expression + parseFactor [LiteralIntTok n] = Right $ IntLiteral n + parseFactor [IdentifierTok name] = Right $ Identifier name + parseFactor (PlusTok : xs) = do + operand <- parseFactor xs + return $ UnaryPlus operand + parseFactor (MinusTok : xs) = do + operand <- parseFactor xs + return $ UnaryMinus operand + parseFactor (OpenBracketTok : xs) = do + (inside, remaining) <- splitToMatchingBracket xs + case remaining of + [] -> parseIntOperand inside + (tok : _) -> Left $ UnexpectedToken tok + parseFactor (tok : _) = Left $ UnexpectedToken tok + parseFactor [] = Left $ ExpectedOperand "Expected operand" From e0688e3f5f09cb90cda41981d247471479c6efe9 Mon Sep 17 00:00:00 2001 From: Billy Moore Date: Wed, 21 Jan 2026 20:35:34 +0000 Subject: [PATCH 2/3] Support for booleans (not yet boolean arithmetic) with better support for adding datatypes. --- src/HSPC/CodeGen.hs | 5 +++ src/HSPC/Parse.hs | 50 +++++++++++++++------- src/HSPC/Tokenize.hs | 5 +++ test/Main.hs | 3 +- test/sample_programs/variables_bool.golden | 0 test/sample_programs/variables_bool.pas | 8 ++++ 6 files changed, 54 insertions(+), 17 deletions(-) create mode 100644 test/sample_programs/variables_bool.golden create mode 100644 test/sample_programs/variables_bool.pas diff --git a/src/HSPC/CodeGen.hs b/src/HSPC/CodeGen.hs index b89fd0d..e139bbd 100644 --- a/src/HSPC/CodeGen.hs +++ b/src/HSPC/CodeGen.hs @@ -13,6 +13,8 @@ type OffsetMap = Map.Map String Int32 dataTypeToSize :: HSPCDataType -> Int dataTypeToSize IntegerType = 64 +-- space efficency is clearly a top priority +dataTypeToSize BooleanType = 64 buildOffsetMap :: [(String, HSPCDataType)] -> (Int32, OffsetMap) buildOffsetMap vars = (totalSize, Map.fromList mappedVars) @@ -72,6 +74,9 @@ generateStatement _ ast = error $ "CodeGen for " ++ show ast ++ " not implemente generateExpression :: OffsetMap -> Expression -> [Word8] generateExpression _ (IntLiteral i) = [0x48, 0xb8] ++ int64ToLE i -- movabs rax i +generateExpression _ (BoolLiteral b) = + [0x48, 0xb8] + ++ (int64ToLE . fromIntegral . fromEnum) b -- movabs rax i generateExpression offsetMap (Identifier str) = case Map.lookup str offsetMap of Just offset -> [0x48, 0x8b, 0x85] ++ int32ToLE offset -- mov rax [rbp - n] diff --git a/src/HSPC/Parse.hs b/src/HSPC/Parse.hs index 890213d..cc0432b 100644 --- a/src/HSPC/Parse.hs +++ b/src/HSPC/Parse.hs @@ -10,12 +10,15 @@ module HSPC.Parse where import Data.Int (Int64) +import Data.Map qualified as Map import HSPC.Tokenize (HSPCToken (..)) data Program = Program String [Block] deriving (Show, Eq) -data HSPCDataType = IntegerType +data HSPCDataType + = IntegerType + | BooleanType deriving (Show, Eq) data Block @@ -33,6 +36,7 @@ data Statement data Expression = Identifier String | IntLiteral Int64 + | BoolLiteral Bool | Add Expression Expression | Subtract Expression Expression | Multiply Expression Expression @@ -49,14 +53,16 @@ data ParseError | ExpectedVariableDeclaration [HSPCToken] | ExpectedOperand String | UnmatchedBracket + | VariableNotDeclared String | InvalidDataType HSPCToken | UnexpectedToken HSPCToken | ExpectedToken HSPCToken - | NotImplemented [HSPCToken] + | NotImplemented String [HSPCToken] deriving (Show) toDataType :: HSPCToken -> Either ParseError HSPCDataType toDataType IntegerTypeTok = Right IntegerType +toDataType BooleanTypeTok = Right BooleanType toDataType toc = Left $ InvalidDataType toc parse :: [HSPCToken] -> Either ParseError Program @@ -71,10 +77,10 @@ parse _ = Left MustStartWithProgramStatement parseMainProgramBlock :: [HSPCToken] -> Either ParseError Block parseMainProgramBlock (VarKeyWordTok : tocs) = do (vars, rest) <- parseVariableDeclarations [] tocs - (res, _) <- parseStatements [] rest + (res, _) <- parseStatements (Map.fromList vars) [] rest return $ MainProgramBlock vars res parseMainProgramBlock (BeginKeyWordTok : tocs) = do - (res, _) <- parseStatements [] tocs + (res, _) <- parseStatements Map.empty [] tocs return $ MainProgramBlock [] res parseMainProgramBlock _ = Left InvalidStartOfBlock @@ -120,35 +126,43 @@ splitToMatchingBracket toks = go toks [] 0 go (x : xs) acc i = go xs (x : acc) i go [] _ _ = Left UnmatchedBracket -parseStatements :: [Statement] -> [HSPCToken] -> Either ParseError ([Statement], [HSPCToken]) -parseStatements acc (EndKeyWordTok : xs) = Right (reverse acc, xs) -parseStatements _ [] = Left ExpectedEndStatement -parseStatements acc xs = do - (expr, rest) <- parseStatement xs - parseStatements (expr : acc) rest +parseStatements :: Map.Map String HSPCDataType -> [Statement] -> [HSPCToken] -> Either ParseError ([Statement], [HSPCToken]) +parseStatements _ acc (EndKeyWordTok : xs) = Right (reverse acc, xs) +parseStatements _ _ [] = Left ExpectedEndStatement +parseStatements vars acc xs = do + (expr, rest) <- parseStatement vars xs + parseStatements vars (expr : acc) rest -parseStatement :: [HSPCToken] -> Either ParseError (Statement, [HSPCToken]) -parseStatement (IdentifierTok name : AssignmentTok : xs) = do +parseStatement :: Map.Map String HSPCDataType -> [HSPCToken] -> Either ParseError (Statement, [HSPCToken]) +parseStatement varMap (IdentifierTok name : AssignmentTok : xs) = do let (rhs, rawRest) = break (== SemiColonTok) xs rest <- removeLeadingSemiColon rawRest - op <- parseIntOperand rhs + op <- case Map.lookup name varMap of + Just dt -> parseOperand dt rhs + Nothing -> Left $ VariableNotDeclared name return (Assignment name op, rest) parseStatement + _ (HaltBuiltInTok : OpenBracketTok : CloseBracketTok : SemiColonTok : xs) = Right (Halt (IntLiteral 0), xs) parseStatement + _ (HaltBuiltInTok : OpenBracketTok : xs) = do (internal, rawRest) <- splitToMatchingBracket xs rest <- removeLeadingSemiColon rawRest - op <- parseIntOperand internal + op <- parseOperand IntegerType internal return (Halt op, rest) -parseStatement tok = Left $ NotImplemented tok +parseStatement _ tok = Left $ NotImplemented "parseStatement" tok + +parseOperand :: HSPCDataType -> [HSPCToken] -> Either ParseError Expression +parseOperand IntegerType = parseIntOperand +parseOperand BooleanType = parseBoolOperand -- IntOperand = Term { ("+" | "-") Term } -- Term = Factor { ("*" | "div") Factor } -- Factor = ("+" | "-") Factor | Number | "(" IntOperand ")" -- Number = // an integer parseIntOperand :: [HSPCToken] -> Either ParseError Expression -parseIntOperand toks = case breakLastOuter (`elem` [PlusTok, MinusTok]) toks of +parseIntOperand tokens = case breakLastOuter (`elem` [PlusTok, MinusTok]) tokens of -- starts with +- ([], after) -> parseTerm after -- doesn't contain +- @@ -196,3 +210,7 @@ parseIntOperand toks = case breakLastOuter (`elem` [PlusTok, MinusTok]) toks of (tok : _) -> Left $ UnexpectedToken tok parseFactor (tok : _) = Left $ UnexpectedToken tok parseFactor [] = Left $ ExpectedOperand "Expected operand" + +parseBoolOperand :: [HSPCToken] -> Either ParseError Expression +parseBoolOperand [LiteralBoolTok bool] = Right $ BoolLiteral bool +parseBoolOperand xs = Left $ NotImplemented "parseBoolOperand" xs diff --git a/src/HSPC/Tokenize.hs b/src/HSPC/Tokenize.hs index 7edf8bf..085fe36 100644 --- a/src/HSPC/Tokenize.hs +++ b/src/HSPC/Tokenize.hs @@ -6,8 +6,10 @@ import Data.List (isPrefixOf) data HSPCToken = LiteralIntTok Int64 + | LiteralBoolTok Bool | ProgramKeyWordTok | IntegerTypeTok + | BooleanTypeTok | VarKeyWordTok | BeginKeyWordTok | EndKeyWordTok @@ -48,6 +50,9 @@ tokenizeIdentifierOrKeyWord "BEGIN" = BeginKeyWordTok tokenizeIdentifierOrKeyWord "HALT" = HaltBuiltInTok tokenizeIdentifierOrKeyWord "DIV" = IntDivideTok tokenizeIdentifierOrKeyWord "INTEGER" = IntegerTypeTok +tokenizeIdentifierOrKeyWord "BOOLEAN" = BooleanTypeTok +tokenizeIdentifierOrKeyWord "TRUE" = LiteralBoolTok True +tokenizeIdentifierOrKeyWord "FALSE" = LiteralBoolTok False tokenizeIdentifierOrKeyWord s = IdentifierTok s tokenizeOperator :: String -> [HSPCToken] diff --git a/test/Main.hs b/test/Main.hs index a9ee010..f3c6425 100644 --- a/test/Main.hs +++ b/test/Main.hs @@ -35,7 +35,8 @@ testSuite = TestSpec "Unary plus with literal ints" "operators_unary_plus_int" 25, TestSpec "{} comments" "comments_curly_brace" 12, TestSpec "// comments" "comments_single_line" 12, - TestSpec "Single variable" "variables_single_var" 10 + TestSpec "Single variable" "variables_single_var" 10, + TestSpec "Boolean variable" "variables_bool" 0 ] main :: IO () diff --git a/test/sample_programs/variables_bool.golden b/test/sample_programs/variables_bool.golden new file mode 100644 index 0000000..e69de29 diff --git a/test/sample_programs/variables_bool.pas b/test/sample_programs/variables_bool.pas new file mode 100644 index 0000000..b2b7f9b --- /dev/null +++ b/test/sample_programs/variables_bool.pas @@ -0,0 +1,8 @@ +program BoolVariable; + +var + i : boolean; +begin + i := true; + halt(); +end. From bf9184d0383bd764fff86e189af1c55d098b1608 Mon Sep 17 00:00:00 2001 From: Billy Moore Date: Thu, 22 Jan 2026 15:51:42 +0000 Subject: [PATCH 3/3] Boolean AND, OR and NOT. --- README.md | 5 ++- src/HSPC/CodeGen.hs | 15 +++++++ src/HSPC/Parse.hs | 43 ++++++++++++++++++- src/HSPC/Tokenize.hs | 6 +++ test/Main.hs | 3 ++ .../sample_programs/operators_bool_and.golden | 0 test/sample_programs/operators_bool_and.pas | 8 ++++ .../sample_programs/operators_bool_not.golden | 0 test/sample_programs/operators_bool_not.pas | 8 ++++ test/sample_programs/operators_bool_or.golden | 0 test/sample_programs/operators_bool_or.pas | 8 ++++ 11 files changed, 92 insertions(+), 4 deletions(-) create mode 100644 test/sample_programs/operators_bool_and.golden create mode 100644 test/sample_programs/operators_bool_and.pas create mode 100644 test/sample_programs/operators_bool_not.golden create mode 100644 test/sample_programs/operators_bool_not.pas create mode 100644 test/sample_programs/operators_bool_or.golden create mode 100644 test/sample_programs/operators_bool_or.pas diff --git a/README.md b/README.md index 013174b..728932c 100644 --- a/README.md +++ b/README.md @@ -15,9 +15,10 @@ Increments: - [X] Binary Operators on Ints - [X] Unary Operators on Ints - [X] Local Variables and Assignment -- [ ] Booleans and Comparisons -- [ ] WriteLn("Hello World") (finally) +- [X] Booleans - [ ] Branching (if statements) +- [ ] Comparisons +- [ ] WriteLn("Hello World") (finally) - [ ] Loops - [ ] Procedures - [ ] Functions diff --git a/src/HSPC/CodeGen.hs b/src/HSPC/CodeGen.hs index e139bbd..532a4ba 100644 --- a/src/HSPC/CodeGen.hs +++ b/src/HSPC/CodeGen.hs @@ -104,4 +104,19 @@ generateExpression offsetMap (IntDivide op1 op2) = ++ [0x48, 0x99] -- cqo ++ [0x48, 0xf7, 0x3c, 0x24] -- idiv qword [rsp] ++ [0x48, 0x83, 0xc4, 0x08] -- add rsp 8 (release stack space) +generateExpression offsetMap (BoolOr op1 op2) = + generateExpression offsetMap op2 + ++ [0x50] -- push rax + ++ generateExpression offsetMap op1 + ++ [0x48, 0x0B, 0x04, 0x24] -- or rax, [rsp] + ++ [0x48, 0x83, 0xc4, 0x08] -- add rsp 8 (release stack space) +generateExpression offsetMap (BoolAnd op1 op2) = + generateExpression offsetMap op2 + ++ [0x50] -- push rax + ++ generateExpression offsetMap op1 + ++ [0x48, 0x23, 0x04, 0x24] -- and rax, [rsp] + ++ [0x48, 0x83, 0xc4, 0x08] -- add rsp 8 (release stack space) +generateExpression offsetMap (BoolNot op) = + generateExpression offsetMap op + ++ [0x48, 0x83, 0xf0, 0x01] -- xor rax, 0x1 generateExpression _ ast = error $ "CodeGen for " ++ show ast ++ " not implemented" diff --git a/src/HSPC/Parse.hs b/src/HSPC/Parse.hs index cc0432b..af5f4a5 100644 --- a/src/HSPC/Parse.hs +++ b/src/HSPC/Parse.hs @@ -37,6 +37,9 @@ data Expression = Identifier String | IntLiteral Int64 | BoolLiteral Bool + | BoolNot Expression + | BoolAnd Expression Expression + | BoolOr Expression Expression | Add Expression Expression | Subtract Expression Expression | Multiply Expression Expression @@ -211,6 +214,42 @@ parseIntOperand tokens = case breakLastOuter (`elem` [PlusTok, MinusTok]) tokens parseFactor (tok : _) = Left $ UnexpectedToken tok parseFactor [] = Left $ ExpectedOperand "Expected operand" +-- BoolOperand = BoolTerm { or BoolTerm } +-- BoolTerm = BoolFactor { and BoolFactor } +-- BoolFactor = not BoolFactor | BoolPrimary +-- BoolPrimary = True | False | ( BoolOperand ) parseBoolOperand :: [HSPCToken] -> Either ParseError Expression -parseBoolOperand [LiteralBoolTok bool] = Right $ BoolLiteral bool -parseBoolOperand xs = Left $ NotImplemented "parseBoolOperand" xs +parseBoolOperand toks = case breakLastOuter (== OrTok) toks of + (before, []) -> parseBoolTerm before + (_, [OrTok]) -> Left $ ExpectedOperand "Expected operand after 'div'" + (before, OrTok : after) -> do + left <- parseBoolTerm before + right <- parseBoolTerm after + return $ BoolOr left right + _ -> error "Should be unreachable since we break on OrTok" + +parseBoolTerm :: [HSPCToken] -> Either ParseError Expression +parseBoolTerm toks = case breakLastOuter (== AndTok) toks of + (before, []) -> parseBoolFactor before + (_, [AndTok]) -> Left $ ExpectedOperand "Expected operand after 'div'" + (before, AndTok : after) -> do + left <- parseBoolFactor before + right <- parseBoolFactor after + return $ BoolAnd left right + _ -> error "Should be unreachable since we break on AndTok" + +parseBoolFactor :: [HSPCToken] -> Either ParseError Expression +parseBoolFactor (NotTok : op) = do + opAsExpr <- parseBoolFactor op + return $ BoolNot opAsExpr +parseBoolFactor toks = parseBoolPrimary toks + +parseBoolPrimary :: [HSPCToken] -> Either ParseError Expression +parseBoolPrimary [LiteralBoolTok bool] = Right $ BoolLiteral bool +parseBoolPrimary (OpenBracketTok : xs) = do + (inside, remaining) <- splitToMatchingBracket xs + case remaining of + [] -> parseBoolOperand inside + (tok : _) -> Left $ UnexpectedToken tok +parseBoolPrimary (tok : _) = Left $ UnexpectedToken tok +parseBoolPrimary [] = Left $ ExpectedOperand "Expected boolean operand" diff --git a/src/HSPC/Tokenize.hs b/src/HSPC/Tokenize.hs index 085fe36..f612d91 100644 --- a/src/HSPC/Tokenize.hs +++ b/src/HSPC/Tokenize.hs @@ -20,6 +20,9 @@ data HSPCToken | ColonTok | SemiColonTok | IntDivideTok + | OrTok + | AndTok + | NotTok | PlusTok | MinusTok | MultiplyTok @@ -53,6 +56,9 @@ tokenizeIdentifierOrKeyWord "INTEGER" = IntegerTypeTok tokenizeIdentifierOrKeyWord "BOOLEAN" = BooleanTypeTok tokenizeIdentifierOrKeyWord "TRUE" = LiteralBoolTok True tokenizeIdentifierOrKeyWord "FALSE" = LiteralBoolTok False +tokenizeIdentifierOrKeyWord "OR" = OrTok +tokenizeIdentifierOrKeyWord "AND" = AndTok +tokenizeIdentifierOrKeyWord "NOT" = NotTok tokenizeIdentifierOrKeyWord s = IdentifierTok s tokenizeOperator :: String -> [HSPCToken] diff --git a/test/Main.hs b/test/Main.hs index f3c6425..f085a4a 100644 --- a/test/Main.hs +++ b/test/Main.hs @@ -33,6 +33,9 @@ testSuite = TestSpec "Brackets with literal ints" "operators_brackets" 26, TestSpec "Unary minus with literal ints" "operators_unary_minus_int" 75, TestSpec "Unary plus with literal ints" "operators_unary_plus_int" 25, + TestSpec "Or between bools" "operators_bool_or" 0, + TestSpec "And between bools" "operators_bool_and" 0, + TestSpec "Negating bools" "operators_bool_not" 0, TestSpec "{} comments" "comments_curly_brace" 12, TestSpec "// comments" "comments_single_line" 12, TestSpec "Single variable" "variables_single_var" 10, diff --git a/test/sample_programs/operators_bool_and.golden b/test/sample_programs/operators_bool_and.golden new file mode 100644 index 0000000..e69de29 diff --git a/test/sample_programs/operators_bool_and.pas b/test/sample_programs/operators_bool_and.pas new file mode 100644 index 0000000..3d3e53b --- /dev/null +++ b/test/sample_programs/operators_bool_and.pas @@ -0,0 +1,8 @@ +program BoolAnd; + +var + b : boolean; +begin + b := true and false; + halt(); +end. diff --git a/test/sample_programs/operators_bool_not.golden b/test/sample_programs/operators_bool_not.golden new file mode 100644 index 0000000..e69de29 diff --git a/test/sample_programs/operators_bool_not.pas b/test/sample_programs/operators_bool_not.pas new file mode 100644 index 0000000..4e79aab --- /dev/null +++ b/test/sample_programs/operators_bool_not.pas @@ -0,0 +1,8 @@ +program BoolNot; + +var + b : boolean; +begin + b := not (true and false); + halt(); +end. diff --git a/test/sample_programs/operators_bool_or.golden b/test/sample_programs/operators_bool_or.golden new file mode 100644 index 0000000..e69de29 diff --git a/test/sample_programs/operators_bool_or.pas b/test/sample_programs/operators_bool_or.pas new file mode 100644 index 0000000..01734db --- /dev/null +++ b/test/sample_programs/operators_bool_or.pas @@ -0,0 +1,8 @@ +program BoolOR; + +var + b : boolean; +begin + b := true or false; + halt(); +end.