-
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathcst.go
More file actions
145 lines (118 loc) · 3.28 KB
/
Copy pathcst.go
File metadata and controls
145 lines (118 loc) · 3.28 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
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
package miniohm
import (
"fmt"
"unsafe"
"github.com/tetratelabs/wazero/api"
)
const (
// Node type constants
NodeTypeNonterminal = 0
NodeTypeTerminal = -1
NodeTypeIter = -2
// CST node structure size constants
cstNodeHeaderSize = 12 // 3 uint32 fields (count, matchLen, type)
uint32Size = 4
)
// CstNode represents a node in the Concrete Syntax Tree
type CstNode struct {
ruleNames []string
memory api.Memory
base uint32
}
// NewCstNode creates a new CstNode with the given parameters
func NewCstNode(ruleNames []string, memory api.Memory, offset uint32) *CstNode {
return &CstNode{
ruleNames: ruleNames,
memory: memory,
base: offset,
}
}
// IsNonterminal returns true if this node represents a nonterminal
func (n *CstNode) IsNonterminal() bool {
return n.Type() >= 0
}
// IsTerminal returns true if this node represents a terminal
func (n *CstNode) IsTerminal() bool {
return n.Type() == NodeTypeTerminal
}
// IsIter returns true if this node represents an iteration
func (n *CstNode) IsIter() bool {
return n.Type() == NodeTypeIter
}
// RuleName returns the name of the rule that created this node
func (n *CstNode) RuleName() (string, error) {
data, ok := n.memory.Read(n.base+8, 4)
if !ok {
return "", fmt.Errorf("failed to read rule ID at address %d", n.base+8)
}
id := readInt32(data, 0)
if id < 0 {
return "", nil
}
if len(n.ruleNames) == 0 {
return fmt.Sprintf("rule_%d", id), nil
}
if int(id) >= len(n.ruleNames) {
return fmt.Sprintf("rule_%d", id), nil
}
if n.ruleNames[id] == "" {
return fmt.Sprintf("rule_%d", id), nil
}
return n.ruleNames[id], nil
}
// count returns the number of child nodes
func (n *CstNode) count() (uint32, error) {
data, ok := n.memory.Read(n.base, 4)
if !ok {
return 0, fmt.Errorf("failed to read count at address %d", n.base)
}
return readUint32(data, 0), nil
}
// MatchLength returns the length of the matched text
func (n *CstNode) MatchLength() (uint32, error) {
data, ok := n.memory.Read(n.base+4, 4)
if !ok {
return 0, fmt.Errorf("failed to read match length at address %d", n.base+4)
}
return readUint32(data, 0), nil
}
// Type returns the type of this node (0 for nonterminal, -1 for terminal, -2 for iter)
func (n *CstNode) Type() int32 {
data, ok := n.memory.Read(n.base+8, 4)
if !ok {
return 0 // Return nonterminal as default in case of error
}
t := readInt32(data, 0)
if t < 0 {
return t
}
return NodeTypeNonterminal
}
// Children returns a slice of child nodes
func (n *CstNode) Children() ([]*CstNode, error) {
count, err := n.count()
if err != nil {
return nil, err
}
if count == 0 {
return []*CstNode{}, nil
}
children := make([]*CstNode, count)
for i := uint32(0); i < count; i++ {
slotOffset := n.base + cstNodeHeaderSize + i*uint32Size
data, ok := n.memory.Read(slotOffset, 4)
if !ok {
return nil, fmt.Errorf("failed to read child pointer at address %d", slotOffset)
}
childOffset := readUint32(data, 0)
children[i] = NewCstNode(n.ruleNames, n.memory, childOffset)
}
return children, nil
}
// Helper functions for reading values from memory
func readUint32(data []byte, offset uint32) uint32 {
return *(*uint32)(unsafe.Pointer(&data[offset]))
}
func readInt32(data []byte, offset uint32) int32 {
return *(*int32)(unsafe.Pointer(&data[offset]))
}