From ccd850d1668248a21cd6023eee916a9ba8288429 Mon Sep 17 00:00:00 2001 From: Erik Rasmussen Date: Mon, 3 Feb 2025 12:37:33 -0600 Subject: [PATCH] Most of rest of the conditional block stuff (#37) * Increase e2e test timeout * Parse else block text * Parse an else block with a condition * Parse an else block with a condition * An error test case * More test cases * Parse the big guy * Rename Arg -> VarName --- ast/ast.go | 8 +- ast/ast_test.go | 2 +- e2e_test.go | 2 +- parser/parser.go | 58 ++++++++----- parser/parser_test.go | 192 ++++++++++++++++++++++++++++++++++++++++-- 5 files changed, 229 insertions(+), 33 deletions(-) diff --git a/ast/ast.go b/ast/ast.go index 6dfa397..4928549 100644 --- a/ast/ast.go +++ b/ast/ast.go @@ -288,9 +288,9 @@ func (d *IfeqDir) End() token.Pos { // IfeqDir represents a conditional directive block using `ifdef` or `ifndef`. type IfdefDir struct { - Tok token.Token // IFDEF or IFNDEF - TokPos token.Pos // position of Tok - Arg Expr // variable-name + Tok token.Token // IFDEF or IFNDEF + TokPos token.Pos // position of Tok + VarName Expr // variable-name } func (*IfdefDir) ifDirNode() {} @@ -302,5 +302,5 @@ func (d *IfdefDir) Pos() token.Pos { // End implements node func (d *IfdefDir) End() token.Pos { - return d.Arg.End() + return d.VarName.End() } diff --git a/ast/ast_test.go b/ast/ast_test.go index 1aa0ab9..d5621a2 100644 --- a/ast/ast_test.go +++ b/ast/ast_test.go @@ -315,7 +315,7 @@ var _ = Describe("Ast", func() { It("should return the position after the arg", func() { err := quick.Check(func(n int) bool { - d := &ast.IfdefDir{Arg: &ast.Text{ValuePos: token.Pos(n)}} + d := &ast.IfdefDir{VarName: &ast.Text{ValuePos: token.Pos(n)}} return d.End() == token.Pos(n) }, nil) diff --git a/e2e_test.go b/e2e_test.go index 37c83b9..1e5a6c4 100644 --- a/e2e_test.go +++ b/e2e_test.go @@ -36,7 +36,7 @@ var _ = Describe("E2E", func() { Eventually(func() token.Token { _, tok, _ := s.Scan() return tok - }, "1s", "1ms").Should(Equal(token.EOF)) + }, "5s", "1ms").Should(Equal(token.EOF)) }) It("should parse this repo's Makefile", Pending, func() { diff --git a/parser/parser.go b/parser/parser.go index dd9ae08..433dffc 100644 --- a/parser/parser.go +++ b/parser/parser.go @@ -207,9 +207,9 @@ func (p *Parser) parseIfdefDir() *ast.IfdefDir { arg := p.parseExpression() return &ast.IfdefDir{ - Tok: tok, - TokPos: pos, - Arg: arg, + Tok: tok, + TokPos: pos, + VarName: arg, } } @@ -233,34 +233,45 @@ func (p *Parser) parseIfeqDir() *ast.IfeqDir { } } +func (p *Parser) parseIfDir() (d ast.IfDir) { + switch p.tok { + case token.IFDEF, token.IFNDEF: + d = p.parseIfdefDir() + case token.IFEQ, token.IFNEQ: + d = p.parseIfeqDir() + } + + return +} + func (p *Parser) parseElseBlock() *ast.ElseBlock { pos := p.expect(token.ELSE) + condition := p.parseIfDir() + + p.skipWhitespace() + text := p.parseObjList() return &ast.ElseBlock{ - Else: pos, + Else: pos, + Condition: condition, + Text: text, } } func (p *Parser) parseIfBlock() *ast.IfBlock { - var ifdir ast.IfDir - switch p.tok { - case token.IFDEF, token.IFNDEF: - ifdir = p.parseIfdefDir() - case token.IFEQ, token.IFNEQ: - ifdir = p.parseIfeqDir() - } + ifdir := p.parseIfDir() p.skipWhitespace() - - var text []ast.Obj - for p.tok != token.EOF && p.tok != token.ENDIF && p.tok != token.ELSE { - text = append(text, p.parseObj()) - p.skipWhitespace() - } + text := p.parseObjList() var eblocks []*ast.ElseBlock - if p.tok == token.ELSE { - eblocks = append(eblocks, p.parseElseBlock()) + for p.tok == token.ELSE { + b := p.parseElseBlock() + eblocks = append(eblocks, b) p.skipWhitespace() + + if b.Condition == nil { + break + } } endif := p.expect(token.ENDIF) @@ -305,6 +316,15 @@ func (p *Parser) parseObj() ast.Obj { } } +func (p *Parser) parseObjList() (l []ast.Obj) { + for p.tok != token.EOF && p.tok != token.ENDIF && p.tok != token.ELSE { + l = append(l, p.parseObj()) + p.skipWhitespace() + } + + return +} + func (p *Parser) parseVar(name ast.Expr) ast.Obj { op, opPos := p.tok, p.pos p.next() diff --git a/parser/parser_test.go b/parser/parser_test.go index ae15847..1bc5070 100644 --- a/parser/parser_test.go +++ b/parser/parser_test.go @@ -492,7 +492,34 @@ var _ = Describe("Parser", func() { Entry(nil, "VAR =", token.RECURSIVE_ASSIGN), ) - It("should Parse a conditional directive", func() { + It("should Parse an ifneq conditional directive", func() { + buf := bytes.NewBufferString("ifneq (baz, bin)\nendif") + p := parser.New(buf, file) + + f, err := p.ParseFile() + + Expect(err).NotTo(HaveOccurred()) + Expect(f.Contents).To(ConsistOf(&ast.IfBlock{ + Directive: &ast.IfeqDir{ + Tok: token.IFNEQ, + TokPos: token.Pos(1), + Open: token.Pos(7), + Arg1: &ast.Text{ + Value: "baz", + ValuePos: token.Pos(8), + }, + Comma: token.Pos(11), + Arg2: &ast.Text{ + Value: "bin", + ValuePos: token.Pos(13), + }, + Close: token.Pos(16), + }, + Endif: token.Pos(18), + })) + }) + + It("should Parse an ifeq conditional directive", func() { buf := bytes.NewBufferString("ifeq (baz, bin)\nendif") p := parser.New(buf, file) @@ -519,6 +546,46 @@ var _ = Describe("Parser", func() { })) }) + It("should Parse an ifdef conditional directive", func() { + buf := bytes.NewBufferString("ifdef FOO\nendif") + p := parser.New(buf, file) + + f, err := p.ParseFile() + + Expect(err).NotTo(HaveOccurred()) + Expect(f.Contents).To(ConsistOf(&ast.IfBlock{ + Directive: &ast.IfdefDir{ + Tok: token.IFDEF, + TokPos: token.Pos(1), + VarName: &ast.Text{ + Value: "FOO", + ValuePos: token.Pos(7), + }, + }, + Endif: token.Pos(11), + })) + }) + + It("should Parse an ifndef conditional directive", func() { + buf := bytes.NewBufferString("ifndef FOO\nendif") + p := parser.New(buf, file) + + f, err := p.ParseFile() + + Expect(err).NotTo(HaveOccurred()) + Expect(f.Contents).To(ConsistOf(&ast.IfBlock{ + Directive: &ast.IfdefDir{ + Tok: token.IFNDEF, + TokPos: token.Pos(1), + VarName: &ast.Text{ + Value: "FOO", + ValuePos: token.Pos(8), + }, + }, + Endif: token.Pos(12), + })) + }) + It("should Parse a conditional directive with text", func() { buf := bytes.NewBufferString("ifeq (baz, bin)\ntarget:\nendif") p := parser.New(buf, file) @@ -586,7 +653,116 @@ var _ = Describe("Parser", func() { })) }) - It("should Parse a conditional directive", Pending, func() { + It("should Parse a conditional directive with an else block with text", func() { + buf := bytes.NewBufferString("ifeq (baz, bin)\nelse\ntarget:\nendif") + p := parser.New(buf, file) + + f, err := p.ParseFile() + + Expect(err).NotTo(HaveOccurred()) + Expect(f.Contents).To(ConsistOf(&ast.IfBlock{ + Directive: &ast.IfeqDir{ + Tok: token.IFEQ, + TokPos: token.Pos(1), + Open: token.Pos(6), + Arg1: &ast.Text{ + Value: "baz", + ValuePos: token.Pos(7), + }, + Comma: token.Pos(10), + Arg2: &ast.Text{ + Value: "bin", + ValuePos: token.Pos(12), + }, + Close: token.Pos(15), + }, + Else: []*ast.ElseBlock{{ + Else: token.Pos(17), + Text: []ast.Obj{&ast.Rule{ + Targets: []ast.Expr{&ast.Text{ + Value: "target", + ValuePos: token.Pos(22), + }}, + Colon: token.Pos(28), + PreReqs: []ast.Expr{}, + OrderPreReqs: []ast.Expr{}, + Recipes: []*ast.Recipe{}, + }}, + }}, + Endif: token.Pos(30), + })) + }) + + It("should Parse a conditional directive with an else block that has a condition", func() { + buf := bytes.NewBufferString("ifeq (baz, bin)\nelse ifeq (baz, bin)\ntarget:\nendif") + p := parser.New(buf, file) + + f, err := p.ParseFile() + + Expect(err).NotTo(HaveOccurred()) + Expect(f.Contents).To(ConsistOf(&ast.IfBlock{ + Directive: &ast.IfeqDir{ + Tok: token.IFEQ, + TokPos: token.Pos(1), + Open: token.Pos(6), + Arg1: &ast.Text{ + Value: "baz", + ValuePos: token.Pos(7), + }, + Comma: token.Pos(10), + Arg2: &ast.Text{ + Value: "bin", + ValuePos: token.Pos(12), + }, + Close: token.Pos(15), + }, + Else: []*ast.ElseBlock{{ + Else: token.Pos(17), + Condition: &ast.IfeqDir{ + Tok: token.IFEQ, + TokPos: token.Pos(22), + Open: token.Pos(27), + Arg1: &ast.Text{ + Value: "baz", + ValuePos: token.Pos(28), + }, + Comma: token.Pos(31), + Arg2: &ast.Text{ + Value: "bin", + ValuePos: token.Pos(33), + }, + Close: token.Pos(36), + }, + Text: []ast.Obj{&ast.Rule{ + Targets: []ast.Expr{&ast.Text{ + Value: "target", + ValuePos: token.Pos(38), + }}, + Colon: token.Pos(44), + PreReqs: []ast.Expr{}, + OrderPreReqs: []ast.Expr{}, + Recipes: []*ast.Recipe{}, + }}, + }}, + Endif: token.Pos(46), + })) + }) + + It("should error when a plain else block preceds an else block with a condition", func() { + buf := bytes.NewBufferString(`ifeq (baz, bin) +else +else ifeq (baz, bin) +endif +`) + + p := parser.New(buf, file) + + _, err := p.ParseFile() + + Expect(err).To(MatchError("test:3:1: expected 'endif', found 'else'")) + }) + + It("should Parse a conditional directive with lots of stuff in it", func() { buf := bytes.NewBufferString(`ifeq (baz, bin) FOO := BAR else ifdef test @@ -635,7 +811,7 @@ endif Condition: &ast.IfdefDir{ Tok: token.IFDEF, TokPos: token.Pos(33), - Arg: &ast.Text{ + VarName: &ast.Text{ Value: "test", ValuePos: token.Pos(39), }, @@ -646,22 +822,22 @@ endif ValuePos: token.Pos(44), }, Op: token.IFNDEF_ASSIGN, - OpPos: token.Pos(49), + OpPos: token.Pos(48), }}, }, { - Else: token.Pos(52), + Else: token.Pos(51), Text: []ast.Obj{&ast.Variable{ Name: &ast.Text{ Value: "BAR", - ValuePos: token.Pos(57), + ValuePos: token.Pos(56), }, Op: token.IMMEDIATE_ASSIGN, - OpPos: token.Pos(61), + OpPos: token.Pos(60), }}, }, }, - Endif: token.Pos(66), + Endif: token.Pos(65), })) })