Implemented instruction splitting as a generator
This commit is contained in:
@ -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
|
||||
})
|
||||
|
||||
return nodes, err
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return nodes, nil
|
||||
}
|
||||
|
@ -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
|
||||
}
|
@ -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
|
||||
}
|
||||
|
61
src/token/Instructions.go
Normal file
61
src/token/Instructions.go
Normal file
@ -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:])
|
||||
}
|
||||
}
|
20
src/token/Instructions_test.go
Normal file
20
src/token/Instructions_test.go
Normal file
@ -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"})
|
||||
}
|
Reference in New Issue
Block a user