Implemented instruction splitting as a generator

This commit is contained in:
2025-03-20 13:53:23 +01:00
parent c2e489f987
commit ac8bd65054
5 changed files with 93 additions and 81 deletions

View File

@ -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
}

View File

@ -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
}

View File

@ -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
View 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:])
}
}

View 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"})
}