From 1deaa0e71973adc211de0a423f2462ab85f0c441 Mon Sep 17 00:00:00 2001 From: Billy Moore Date: Wed, 4 Feb 2026 11:37:24 +0000 Subject: [PATCH 1/3] Parameterize end statement of parseStatements. --- src/HSPC/Parse.hs | 16 +++++++++------- src/HSPC/Tokenize.hs | 4 ++-- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/src/HSPC/Parse.hs b/src/HSPC/Parse.hs index e4f55ac..a6fe388 100644 --- a/src/HSPC/Parse.hs +++ b/src/HSPC/Parse.hs @@ -32,6 +32,7 @@ data Statement | NOP | Assignment String Expression | Halt Expression + | StatementBlock [Statement] | ExprStmt Expression deriving (Show, Eq) @@ -88,10 +89,10 @@ parse _ = Left MustStartWithProgramStatement parseMainProgramBlock :: [HSPCToken] -> Either ParseError Block parseMainProgramBlock (VarKeyWordTok : tocs) = do (vars, rest) <- parseVariableDeclarations [] tocs - (res, _) <- parseStatements [] rest + (res, _) <- parseStatements ProgramEndKeyWordTok [] rest return $ MainProgramBlock vars res parseMainProgramBlock (BeginKeyWordTok : tocs) = do - (res, _) <- parseStatements [] tocs + (res, _) <- parseStatements ProgramEndKeyWordTok [] tocs return $ MainProgramBlock [] res parseMainProgramBlock _ = Left InvalidStartOfBlock @@ -126,13 +127,14 @@ 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 +parseStatements :: HSPCToken -> [Statement] -> [HSPCToken] -> Either ParseError ([Statement], [HSPCToken]) +parseStatements endTok acc (x : xs) + | x == endTok = Right (reverse acc, xs) +parseStatements _ _ [] = Left ExpectedEndStatement +parseStatements endTok acc xs = do (expr, restWithSemiColon) <- parseStatement xs rest <- removeLeadingSemiColon restWithSemiColon - parseStatements (expr : acc) rest + parseStatements endTok (expr : acc) rest parseStatement :: [HSPCToken] -> Either ParseError (Statement, [HSPCToken]) parseStatement (IdentifierTok name : AssignmentTok : xs) = do diff --git a/src/HSPC/Tokenize.hs b/src/HSPC/Tokenize.hs index 92b9fc2..5f41233 100644 --- a/src/HSPC/Tokenize.hs +++ b/src/HSPC/Tokenize.hs @@ -12,7 +12,7 @@ data HSPCToken | BooleanTypeTok | VarKeyWordTok | BeginKeyWordTok - | EndKeyWordTok + | ProgramEndKeyWordTok | WhileKeyWordTok | DoKeyWordTok | IfKeyWordTok @@ -54,7 +54,7 @@ tokenize ('{' : cs) = tokenize $ dropWhile (/= '}') cs tokenize (c : cs) | "//" `isPrefixOf` (c : cs) = tokenize $ dropWhile (/= '\n') cs -- End includes this annoying '.' - | "END." `isPrefixOf` map toUpper (c : cs) = EndKeyWordTok : tokenize (drop 3 cs) + | "END." `isPrefixOf` map toUpper (c : cs) = ProgramEndKeyWordTok : tokenize (drop 3 cs) | isSpace c = tokenize cs | isDigit c = let (num, rest) = span isDigit (c : cs) From 7aa76857bcddc2aadea113d61a28d812e157b919 Mon Sep 17 00:00:00 2001 From: Billy Moore Date: Thu, 5 Feb 2026 10:26:19 +0000 Subject: [PATCH 2/3] Implement CodeGen for Subtract. --- src/HSPC/CodeGen.hs | 6 ++++++ test/Main.hs | 1 + test/sample_programs/operators_sub_two_ints.golden | 0 test/sample_programs/operators_sub_two_ints.pas | 5 +++++ 4 files changed, 12 insertions(+) create mode 100644 test/sample_programs/operators_sub_two_ints.golden create mode 100644 test/sample_programs/operators_sub_two_ints.pas diff --git a/src/HSPC/CodeGen.hs b/src/HSPC/CodeGen.hs index 316a5ce..301f13d 100644 --- a/src/HSPC/CodeGen.hs +++ b/src/HSPC/CodeGen.hs @@ -114,6 +114,12 @@ generateExpression offsetMap (Identifier str) = case Map.lookup str offsetMap of Just offset -> [0x48, 0x8b, 0x85] ++ int32ToLE offset -- mov rax [rbp - n] Nothing -> error $ "Variable " ++ str ++ " doesn't exist." +generateExpression offsetMap (Subtract op1 op2) = + generateExpression offsetMap op2 + ++ [0x50] -- push rax + ++ generateExpression offsetMap op1 + ++ [0x48, 0x2b, 0x04, 0x24] -- sub rax, [rsp] + ++ [0x48, 0x83, 0xc4, 0x08] -- add rsp, 8 (release stack space) generateExpression offsetMap (Add op1 op2) = generateExpression offsetMap op1 ++ [0x50] -- push rax diff --git a/test/Main.hs b/test/Main.hs index 18cdc24..5926612 100644 --- a/test/Main.hs +++ b/test/Main.hs @@ -28,6 +28,7 @@ testSuite = TestSpec "Exit code > 255" "ints_big_exit_code" 255, TestSpec "Halt after Halt" "ints_halt_after_halt" 0, TestSpec "Add literal ints" "operators_add_two_ints" 200, + TestSpec "Subtract literal ints" "operators_sub_two_ints" 90, TestSpec "Multiply literal ints" "operators_multiply_two_ints" 40, TestSpec "Divide(div) literal ints" "operators_divide_two_ints" 2, TestSpec "Brackets with literal ints" "operators_brackets" 26, diff --git a/test/sample_programs/operators_sub_two_ints.golden b/test/sample_programs/operators_sub_two_ints.golden new file mode 100644 index 0000000..e69de29 diff --git a/test/sample_programs/operators_sub_two_ints.pas b/test/sample_programs/operators_sub_two_ints.pas new file mode 100644 index 0000000..bd00f9b --- /dev/null +++ b/test/sample_programs/operators_sub_two_ints.pas @@ -0,0 +1,5 @@ +program SubtractExample; + +begin + halt(100-10); +end. From 41526cdb6ac8ab679b8848b6b4fb08c9a1fd6811 Mon Sep 17 00:00:00 2001 From: Billy Moore Date: Thu, 5 Feb 2026 10:27:01 +0000 Subject: [PATCH 3/3] Blocks as statements. --- README.md | 5 +++-- src/HSPC/CodeGen.hs | 3 ++- src/HSPC/Parse.hs | 3 +++ src/HSPC/Tokenize.hs | 2 ++ test/Main.hs | 2 ++ test/sample_programs/if_block.golden | 0 test/sample_programs/if_block.pas | 16 ++++++++++++++++ test/sample_programs/while_block.golden | 0 test/sample_programs/while_block.pas | 18 ++++++++++++++++++ 9 files changed, 46 insertions(+), 3 deletions(-) create mode 100644 test/sample_programs/if_block.golden create mode 100644 test/sample_programs/if_block.pas create mode 100644 test/sample_programs/while_block.golden create mode 100644 test/sample_programs/while_block.pas diff --git a/README.md b/README.md index 3a47e6d..4a6bd75 100644 --- a/README.md +++ b/README.md @@ -19,9 +19,10 @@ Increments: - [X] Branching (if statements) - [X] While Loops - [X] Comparisons -- [ ] Blocks as statements -- [ ] Strings (WriteLn("Hello World")) +- [X] Blocks as statements - [ ] For Loops +- [ ] Strings (WriteLn("Hello World")) +- [ ] Type Checking - [ ] Procedures - [ ] Functions - [ ] Arrays diff --git a/src/HSPC/CodeGen.hs b/src/HSPC/CodeGen.hs index 301f13d..2898197 100644 --- a/src/HSPC/CodeGen.hs +++ b/src/HSPC/CodeGen.hs @@ -88,6 +88,8 @@ generateStatement offsetMap (Halt ast) = -- exit_group syscall, exit code is in rdi ++ [0xb8, 0x3c, 00, 00, 00] -- mov rax 60 ++ [0x0f, 0x05] -- Syscall +generateStatement offsetMap (StatementBlock body) = + concatMap (generateStatement offsetMap) body generateStatement offsetMap (Assignment name op) = generateExpression offsetMap op ++ case Map.lookup name offsetMap of @@ -164,4 +166,3 @@ generateExpression offsetMap (BoolAnd op1 op2) = 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 a6fe388..a6e5403 100644 --- a/src/HSPC/Parse.hs +++ b/src/HSPC/Parse.hs @@ -148,6 +148,9 @@ parseStatement (internal, rest) <- splitToMatchingBracket xs op <- parseExpression internal return (Halt op, rest) +parseStatement (BeginKeyWordTok : xs) = do + (stmts, rest) <- parseStatements EndKeyWordTok [] xs + return (StatementBlock stmts, rest) -- "IF cond THEN statement (; | ELSE elseStatement;)" -- ELSE IF is simply an if statement where elseSatement diff --git a/src/HSPC/Tokenize.hs b/src/HSPC/Tokenize.hs index 5f41233..0ec501e 100644 --- a/src/HSPC/Tokenize.hs +++ b/src/HSPC/Tokenize.hs @@ -12,6 +12,7 @@ data HSPCToken | BooleanTypeTok | VarKeyWordTok | BeginKeyWordTok + | EndKeyWordTok | ProgramEndKeyWordTok | WhileKeyWordTok | DoKeyWordTok @@ -73,6 +74,7 @@ tokenizeIdentifierOrKeyWord "IF" = IfKeyWordTok tokenizeIdentifierOrKeyWord "ELSE" = ElseKeyWordTok tokenizeIdentifierOrKeyWord "THEN" = ThenKeyWordTok tokenizeIdentifierOrKeyWord "BEGIN" = BeginKeyWordTok +tokenizeIdentifierOrKeyWord "END" = EndKeyWordTok tokenizeIdentifierOrKeyWord "HALT" = HaltBuiltInTok tokenizeIdentifierOrKeyWord "DIV" = IntDivideTok tokenizeIdentifierOrKeyWord "INTEGER" = IntegerTypeTok diff --git a/test/Main.hs b/test/Main.hs index 5926612..96c2939 100644 --- a/test/Main.hs +++ b/test/Main.hs @@ -45,9 +45,11 @@ testSuite = TestSpec "IF ELSE" "if_else" 40, TestSpec "IF ELSE IF" "if_else_if" 40, TestSpec "IF with complex cond" "if_complex_cond" 40, + TestSpec "IF with block" "if_block" 15, TestSpec "WHILE basic example" "while" 0, TestSpec "WHILE not equal (<> 10)" "while_not_equal_10" 10, TestSpec "WHILE less than (< 1000)" "while_less_than" 100, + TestSpec "WHILE with block" "while_block" 250, TestSpec "Greater than (> and >=)" "comparison_greater_than" 40, TestSpec "Less than (< and <=)" "comparison_less_than" 23, TestSpec "Equality (= and <>)" "comparison_equality" 22 diff --git a/test/sample_programs/if_block.golden b/test/sample_programs/if_block.golden new file mode 100644 index 0000000..e69de29 diff --git a/test/sample_programs/if_block.pas b/test/sample_programs/if_block.pas new file mode 100644 index 0000000..d3fd0eb --- /dev/null +++ b/test/sample_programs/if_block.pas @@ -0,0 +1,16 @@ +program IfExample; + +var + i : integer; +begin + i := 10; + + if (True) then + begin + i := 10; + i := 20; + i := 15; + end; + + halt(i); +end. diff --git a/test/sample_programs/while_block.golden b/test/sample_programs/while_block.golden new file mode 100644 index 0000000..e69de29 diff --git a/test/sample_programs/while_block.pas b/test/sample_programs/while_block.pas new file mode 100644 index 0000000..b3431c3 --- /dev/null +++ b/test/sample_programs/while_block.pas @@ -0,0 +1,18 @@ +program WhileExample; + +var + i : integer; + j : integer; +begin + i := 0; + j := 0; + + while i < 25 do + begin + j := (j + 20) - 10; + i := i + 1; + end; + + + halt(j); +end.