-
Notifications
You must be signed in to change notification settings - Fork 7
Expand file tree
/
Copy pathmultiline.go
More file actions
100 lines (89 loc) · 2.17 KB
/
Copy pathmultiline.go
File metadata and controls
100 lines (89 loc) · 2.17 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
package apl
import (
"fmt"
"strings"
"github.com/ktye/iv/apl/scan"
)
// ParseLines parses multiple lines separated by newline, that may contain continuation lines.
// Continuation lines are only allowed for lambda functions.
func (a *Apl) ParseLines(lines string) (Program, error) {
b := NewLineBuffer(a)
v := strings.Split(lines, "\n")
for i, s := range v {
if ok, err := b.Add(s); err != nil {
return nil, err
} else if i == len(v)-1 {
if ok == false {
return nil, fmt.Errorf("unbalanced {")
}
return b.Parse()
}
}
return nil, nil
}
// LineBuffer buffers multiline statements for lambda functions.
type LineBuffer struct {
a *Apl
tokens []scan.Token
level int
}
func NewLineBuffer(a *Apl) *LineBuffer {
return &LineBuffer{a: a}
}
// Add a line to the buffer.
// The function returns ok, if the line is complete and can be parsed.
func (b *LineBuffer) Add(line string) (bool, error) {
if b.a == nil {
return false, fmt.Errorf("linebuffer is not initialized (no APL)")
}
tokens, err := b.a.Scan(line)
if err != nil {
b.reset()
return false, err
}
if len(tokens) == 0 {
return false, nil
}
// Join with diamonds. Ommit the diamond if the last token is LeftBrace
// or the next token is a RightBrace.
diamond := true
if len(b.tokens) == 0 {
diamond = false
} else if b.tokens[len(b.tokens)-1].T == scan.LeftBrace {
diamond = false
}
if len(tokens) > 0 && diamond == true {
b.tokens = append(b.tokens, scan.Token{T: scan.Diamond, S: "⋄"})
}
b.tokens = append(b.tokens, tokens...)
for _, t := range tokens {
if t.T == scan.LeftBrace {
b.level++
} else if t.T == scan.RightBrace {
b.level--
if b.level < 0 {
b.reset()
return false, fmt.Errorf("too many }")
}
}
}
if b.level == 0 {
return true, nil
}
return false, nil
}
// Parse parses the tokens in the buffer.
// Lines must be pushed to the buffer with Add and Parse should only be called if Add returned true.
func (b *LineBuffer) Parse() (Program, error) {
defer b.reset()
return b.a.parse(b.tokens)
}
func (b *LineBuffer) Len() int {
return len(b.tokens)
}
func (b *LineBuffer) reset() {
b.level = 0
if len(b.tokens) > 0 {
b.tokens = b.tokens[:0]
}
}