Add support for local labels

This commit is contained in:
2021-11-15 14:05:18 +01:00
parent 5ad4114405
commit 18ddfa611d
8 changed files with 344 additions and 255 deletions

View File

@@ -58,26 +58,30 @@ spec = do
parseAmpersand [] `shouldBe` Nothing
describe "parseLabelDef" $ do
it "parses 'label:'" $
parseLabelDef [T.Identifier "label", T.Colon] `shouldBe` success (LabelDef "label") 2
it "parses global label def" $
parseLabelDef [T.Identifier "label", T.Colon] `shouldBe` success (LabelDef Global "label") 2
it "parses local label def" $
parseLabelDef [T.Dot, T.Identifier "label", T.Colon] `shouldBe` success (LabelDef Local "label") 3
it "requires label" $
parseLabelDef [T.Colon] `shouldBe` Nothing
it "requires colon" $
parseLabelDef [T.Identifier "label"] `shouldBe` Nothing
it "supports non-truncated input" $ do
parseLabelDef [T.Identifier "sum", T.Colon, T.Operator Nop] `shouldBe` success (LabelDef "sum") 2
parseLabelDef [T.Identifier "sum", T.Colon, T.Operator Nop] `shouldBe` success (LabelDef Global "sum") 2
it "supports empty input" $
parseLabelDef [] `shouldBe` Nothing
describe "parseLabelRef" $ do
it "parses '&label'" $
parseLabelRef [T.Ampersand, T.Identifier "label"] `shouldBe` success (LabelRef "label") 2
it "parses global label ref" $
parseLabelRef [T.Ampersand, T.Identifier "label"] `shouldBe` success (LabelRef Global "label") 2
it "parses local label" $
parseLabelRef [T.Ampersand, T.Dot, T.Identifier "label"] `shouldBe` success (LabelRef Local "label") 3
it "requires label" $
parseLabelRef [T.Ampersand] `shouldBe` Nothing
it "requires ampersand" $
parseLabelRef [T.Identifier "label"] `shouldBe` Nothing
it "supports non-truncated input" $ do
parseLabelRef [T.Ampersand, T.Identifier "sum", T.Operator Nop] `shouldBe` success (LabelRef "sum") 2
parseLabelRef [T.Ampersand, T.Identifier "sum", T.Operator Nop] `shouldBe` success (LabelRef Global "sum") 2
it "supports empty input" $
parseLabelRef [] `shouldBe` Nothing
@@ -88,7 +92,7 @@ spec = do
let expected = map (flip success 1 . Param . Integer) ints
map parseParam input `shouldBe` expected
it "parses label references" $ do
let expected = success (Param (LabelRef "program")) 2
let expected = success (Param (LabelRef Global "program")) 2
parseParam [T.Ampersand, T.Identifier "program"] `shouldBe` expected
it "supports non-truncated input" $ do
let expected = success (Param (Integer 1)) 1
@@ -118,7 +122,7 @@ spec = do
let expected = success (Instruction
(Operator Call)
(Params [
(Param (LabelRef "program"))
(Param (LabelRef Global "program"))
])
) (length input)
parseInstr input `shouldBe` expected
@@ -141,18 +145,18 @@ spec = do
parseInstr input `shouldBe` expected
it "parses operator with multiple param ref params" $ do
let input = [T.Operator Push
, T.Ampersand, T.Identifier "program"
, T.Ampersand, T.Identifier "main"
, T.Ampersand, T.Dot, T.Identifier "program"
, T.Ampersand, T.Dot, T.Identifier "main"
, T.Ampersand, T.Identifier "foo"
, T.Ampersand, T.Identifier "bar"
, T.Ampersand, T.Dot, T.Identifier "bar"
]
let expected = success (Instruction
(Operator Push)
(Params [
(Param (LabelRef "program")),
(Param (LabelRef "main")),
(Param (LabelRef "foo")),
(Param (LabelRef "bar"))
(Param (LabelRef Local "program")),
(Param (LabelRef Local "main")),
(Param (LabelRef Global "foo")),
(Param (LabelRef Local "bar"))
])
) (length input)
parseInstr input `shouldBe` expected
@@ -160,23 +164,23 @@ spec = do
let input = [T.Operator Push
, T.Ampersand, T.Identifier "program"
, T.IntLiteral 4
, T.Ampersand, T.Identifier "main"
, T.Ampersand, T.Dot, T.Identifier "main"
, T.Ampersand, T.Identifier "foo"
, T.IntLiteral 10
, T.IntLiteral 11
, T.Ampersand, T.Identifier "bar"
, T.Ampersand, T.Dot, T.Identifier "bar"
, T.IntLiteral 20
]
let expected = success (Instruction
(Operator Push)
(Params [
(Param (LabelRef "program")),
(Param (LabelRef Global "program")),
(Param (Integer 4)),
(Param (LabelRef "main")),
(Param (LabelRef "foo")),
(Param (LabelRef Local "main")),
(Param (LabelRef Global "foo")),
(Param (Integer 10)),
(Param (Integer 11)),
(Param (LabelRef "bar")),
(Param (LabelRef Local "bar")),
(Param (Integer 20))
])
) (length input)
@@ -186,7 +190,7 @@ spec = do
, T.Ampersand, T.Identifier "program"
, T.IntLiteral 4
, T.Ampersand, T.Identifier "main"
, T.Ampersand, T.Identifier "foo"
, T.Ampersand, T.Dot, T.Identifier "foo"
, T.IntLiteral 10
, T.IntLiteral 11
, T.Ampersand, T.Identifier "bar"
@@ -197,29 +201,29 @@ spec = do
let expected = success (Instruction
(Operator Push)
(Params [
(Param (LabelRef "program")),
(Param (LabelRef Global "program")),
(Param (Integer 4)),
(Param (LabelRef "main")),
(Param (LabelRef "foo")),
(Param (LabelRef Global "main")),
(Param (LabelRef Local "foo")),
(Param (Integer 10)),
(Param (Integer 11)),
(Param (LabelRef "bar")),
(Param (LabelRef Global "bar")),
(Param (Integer 20))
])
) 13
) 14
parseInstr input `shouldBe` expected
it "supports empty input" $
parseInstr [] `shouldBe` Nothing
describe "parseLine" $ do
it "supports label definition and operator in the same line" $ do
let input = [T.Identifier "main", T.Colon, T.Operator Call, T.Ampersand, T.Identifier "program"]
let input = [T.Dot, T.Identifier "main", T.Colon, T.Operator Call, T.Ampersand, T.Identifier "program"]
let expected = success (Line
(LabelDef "main")
(LabelDef Local "main")
(Instruction
(Operator Call)
(Params [
(Param (LabelRef "program"))
(Param (LabelRef Global "program"))
])
)
) (length input)
@@ -227,18 +231,18 @@ spec = do
it "supports line with just label definition" $ do
let input = [T.Identifier "main", T.Colon]
let expected = success (Line
(LabelDef "main")
(LabelDef Global "main")
Empty
) (length input)
parseLine input `shouldBe` expected
it "supports line with just operator" $ do
let input = [T.Operator Call, T.Ampersand, T.Identifier "program"]
let input = [T.Operator Call, T.Ampersand, T.Dot, T.Identifier "program"]
let expected = success (Line
Empty
(Instruction
(Operator Call)
(Params [
(Param (LabelRef "program"))
(Param (LabelRef Local "program"))
])
)
) (length input)
@@ -246,11 +250,11 @@ spec = do
it "supports non-truncated input" $ do
let input = [T.Identifier "main", T.Colon, T.Operator Call, T.Ampersand, T.Identifier "program", T.Identifier "exit"]
let expected = success (Line
(LabelDef "main")
(LabelDef Global "main")
(Instruction
(Operator Call)
(Params [
(Param (LabelRef "program"))
(Param (LabelRef Global "program"))
])
)
) 5
@@ -381,7 +385,7 @@ spec = do
, success (Integer 4) 1
, Nothing
, Nothing
, success (LabelDef "not me") 2
, success (LabelDef Local "not me") 2
, Nothing
, success (Instruction (Operator Push) Empty) 1
, Nothing
@@ -402,7 +406,7 @@ spec = do
parseAny [] input `shouldBe` Nothing
it "supports empty input irrespective of wrapped parsers" $ do
let parsers = map const [ success (Integer 4) 1
, success (LabelDef "not me") 2
, success (LabelDef Local "not me") 2
, success (Instruction (Operator Push) Empty) 1
, Nothing
, success Ampersand 1
@@ -450,7 +454,7 @@ spec = do
parseSeq pattern combiner input `shouldBe` Nothing
it "supports empty input irrespective of wrapped parsers" $ do
let pattern = map const [ success (Integer 4) 1
, success (LabelDef "not me") 2
, success (LabelDef Global "not me") 2
, success (Instruction (Operator Push) Empty) 1
, success Ampersand 1
, success Colon 1
@@ -481,10 +485,10 @@ spec = do
it "parses line by line" $ do
let input = "add1_2: push 1\npush 2\nadd"
let (Right tokens) = T.tokenize input
-- Labels: Operations: Params:
let expected = Program [ (Line (LabelDef "add1_2") (Instruction (Operator Push) (Params [Param $ Integer 1])))
, (Line Empty (Instruction (Operator Push) (Params [Param $ Integer 2])))
, (Line Empty (Instruction (Operator Add) Empty))
-- Labels: Operations: Params:
let expected = Program [ (Line (LabelDef Global "add1_2") (Instruction (Operator Push) (Params [Param $ Integer 1])))
, (Line Empty (Instruction (Operator Push) (Params [Param $ Integer 2])))
, (Line Empty (Instruction (Operator Add) Empty))
]
parse tokens `shouldBe` (Right $ expected :: Either String AST)
it "rejects multiple instructions in single line" $ do
@@ -528,13 +532,13 @@ spec = do
\ sum: add\n\
\ ret"
let (Right tokens) = T.tokenize input
-- Labels: Operations: Params:
let expected = Program [ (Line (LabelDef "main") Empty)
, (Line Empty (Instruction (Operator Push) (Params [Param $ Integer 7])))
, (Line Empty (Instruction (Operator Push) (Params [Param $ Integer 4])))
, (Line Empty (Instruction (Operator Call) (Params [Param $ LabelRef "sum"])))
, (Line Empty (Instruction (Operator Halt) Empty))
, (Line (LabelDef "sum") (Instruction (Operator Add) Empty))
, (Line Empty (Instruction (Operator Ret) Empty))
-- Labels: Operations: Params:
let expected = Program [ (Line (LabelDef Global "main") Empty)
, (Line Empty (Instruction (Operator Push) (Params [Param $ Integer 7])))
, (Line Empty (Instruction (Operator Push) (Params [Param $ Integer 4])))
, (Line Empty (Instruction (Operator Call) (Params [Param $ LabelRef Global "sum"])))
, (Line Empty (Instruction (Operator Halt) Empty))
, (Line (LabelDef Global "sum") (Instruction (Operator Add) Empty))
, (Line Empty (Instruction (Operator Ret) Empty))
]
parse tokens `shouldBe` (Right $ expected :: Either String AST)