Skip to content

Commit

Permalink
Most of rest of the conditional block stuff (#37)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
UnstoppableMango authored Feb 3, 2025
1 parent 1179d42 commit ccd850d
Show file tree
Hide file tree
Showing 5 changed files with 229 additions and 33 deletions.
8 changes: 4 additions & 4 deletions ast/ast.go
Original file line number Diff line number Diff line change
Expand Up @@ -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() {}
Expand All @@ -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()
}
2 changes: 1 addition & 1 deletion ast/ast_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
2 changes: 1 addition & 1 deletion e2e_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand Down
58 changes: 39 additions & 19 deletions parser/parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -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,
}
}

Expand All @@ -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)
Expand Down Expand Up @@ -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()
Expand Down
192 changes: 184 additions & 8 deletions parser/parser_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand All @@ -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)
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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),
},
Expand All @@ -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),
}))
})

Expand Down

0 comments on commit ccd850d

Please sign in to comment.