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.
|
// 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)
|
nodes := make(AST, 0, len(tokens)/64)
|
||||||
|
|
||||||
err := eachInstruction(tokens, func(instruction token.List) error {
|
for tokens := range tokens.Instructions {
|
||||||
node, err := parseInstruction(instruction, file, nodes)
|
node, err := parseInstruction(tokens, file, nodes)
|
||||||
|
|
||||||
if node != nil {
|
if node != nil {
|
||||||
nodes = append(nodes, node)
|
nodes = append(nodes, node)
|
||||||
}
|
}
|
||||||
|
|
||||||
return err
|
if err != nil {
|
||||||
})
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return nodes, 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) {
|
func parseCases(tokens token.List, file *fs.File) ([]Case, error) {
|
||||||
var cases []Case
|
var cases []Case
|
||||||
|
|
||||||
err := eachInstruction(tokens, func(caseTokens token.List) error {
|
for caseTokens := range tokens.Instructions {
|
||||||
blockStart, _, body, err := block(caseTokens, file)
|
blockStart, _, body, err := block(caseTokens, file)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
conditionTokens := caseTokens[:blockStart]
|
conditionTokens := caseTokens[:blockStart]
|
||||||
@ -30,9 +30,7 @@ func parseCases(tokens token.List, file *fs.File) ([]Case, error) {
|
|||||||
Condition: condition,
|
Condition: condition,
|
||||||
Body: body,
|
Body: body,
|
||||||
})
|
})
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return cases, nil
|
||||||
})
|
|
||||||
|
|
||||||
return cases, err
|
|
||||||
}
|
}
|
||||||
|
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