diff --git a/src/ast/Parse.go b/src/ast/Parse.go index 20634e4..ea7c758 100644 --- a/src/ast/Parse.go +++ b/src/ast/Parse.go @@ -6,18 +6,20 @@ import ( ) // Parse generates an AST from a list of tokens. -func Parse(tokens []token.Token, file *fs.File) (AST, error) { +func Parse(tokens token.List, file *fs.File) (AST, error) { nodes := make(AST, 0, len(tokens)/64) - err := eachInstruction(tokens, func(instruction token.List) error { - node, err := parseInstruction(instruction, file, nodes) + for tokens := range tokens.Instructions { + node, err := parseInstruction(tokens, file, nodes) if node != nil { nodes = append(nodes, node) } - return err - }) + if err != nil { + return nil, err + } + } - return nodes, err + return nodes, nil } diff --git a/src/ast/eachInstruction.go b/src/ast/eachInstruction.go deleted file mode 100644 index 0bed958..0000000 --- a/src/ast/eachInstruction.go +++ /dev/null @@ -1,69 +0,0 @@ -package ast - -import "git.urbach.dev/cli/q/src/token" - -// eachInstruction calls the function on each AST node. -func eachInstruction(tokens token.List, call func(token.List) error) error { - start := 0 - groupLevel := 0 - blockLevel := 0 - - for i, t := range tokens { - switch t.Kind { - case token.NewLine: - if start == i { - start = i + 1 - continue - } - - if groupLevel > 0 || blockLevel > 0 { - continue - } - - err := call(tokens[start:i]) - - if err != nil { - return err - } - - start = i + 1 - - case token.GroupStart: - groupLevel++ - - case token.GroupEnd: - groupLevel-- - - case token.BlockStart: - blockLevel++ - - case token.BlockEnd: - blockLevel-- - - if groupLevel > 0 || blockLevel > 0 { - continue - } - - err := call(tokens[start : i+1]) - - if err != nil { - return err - } - - start = i + 1 - - case token.EOF: - if start < i { - return call(tokens[start:i]) - } - - return nil - } - } - - if start < len(tokens) { - return call(tokens[start:]) - } - - return nil -} diff --git a/src/ast/parseCases.go b/src/ast/parseCases.go index dbb5412..2ea4cf1 100644 --- a/src/ast/parseCases.go +++ b/src/ast/parseCases.go @@ -10,11 +10,11 @@ import ( func parseCases(tokens token.List, file *fs.File) ([]Case, error) { var cases []Case - err := eachInstruction(tokens, func(caseTokens token.List) error { + for caseTokens := range tokens.Instructions { blockStart, _, body, err := block(caseTokens, file) if err != nil { - return err + return nil, err } conditionTokens := caseTokens[:blockStart] @@ -30,9 +30,7 @@ func parseCases(tokens token.List, file *fs.File) ([]Case, error) { Condition: condition, Body: body, }) + } - return nil - }) - - return cases, err + return cases, nil } diff --git a/src/token/Instructions.go b/src/token/Instructions.go new file mode 100644 index 0000000..183d6db --- /dev/null +++ b/src/token/Instructions.go @@ -0,0 +1,61 @@ +package token + +// Instructions yields on each AST node. +func (list List) Instructions(yield func(List) bool) { + start := 0 + groupLevel := 0 + blockLevel := 0 + + for i, t := range list { + switch t.Kind { + case NewLine: + if start == i { + start = i + 1 + continue + } + + if groupLevel > 0 || blockLevel > 0 { + continue + } + + if !yield(list[start:i]) { + return + } + + start = i + 1 + + case GroupStart: + groupLevel++ + + case GroupEnd: + groupLevel-- + + case BlockStart: + blockLevel++ + + case BlockEnd: + blockLevel-- + + if groupLevel > 0 || blockLevel > 0 { + continue + } + + if !yield(list[start : i+1]) { + return + } + + start = i + 1 + + case EOF: + if start < i { + yield(list[start:i]) + } + + return + } + } + + if start < len(list) { + yield(list[start:]) + } +} diff --git a/src/token/Instructions_test.go b/src/token/Instructions_test.go new file mode 100644 index 0000000..cb06501 --- /dev/null +++ b/src/token/Instructions_test.go @@ -0,0 +1,20 @@ +package token_test + +import ( + "testing" + + "git.urbach.dev/cli/q/src/token" + "git.urbach.dev/go/assert" +) + +func TestInstructions(t *testing.T) { + src := []byte("a := 1\nb := 2\n") + tokens := token.Tokenize(src) + nodes := []string{} + + for param := range tokens.Instructions { + nodes = append(nodes, param.Text(src)) + } + + assert.DeepEqual(t, nodes, []string{"a:=1", "b:=2"}) +}