1// Copyright 2011 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5// Parse nodes.
6
7package mof
8
9import (
10	"fmt"
11	"strconv"
12)
13
14// A Node is an element in the parse tree. The interface is trivial.
15// The interface contains an unexported method so that only
16// types local to this package can satisfy it.
17type node interface {
18	Type() nodeType
19	Value() interface{}
20	Position() pos // byte position of start of node in full original input string
21	// Make sure only functions in this package can create Nodes.
22	unexported()
23}
24
25// NodeType identifies the type of a parse tree node.
26type nodeType int
27
28// Pos represents a byte position in the original input text from which
29// this template was parsed.
30type pos int
31
32func (p pos) Position() pos {
33	return p
34}
35
36// unexported keeps Node implementations local to the package.
37// All implementations embed Pos, so this takes care of it.
38func (pos) unexported() {
39}
40
41// Type returns itself and provides an easy default implementation
42// for embedding in a Node. Embedded in all non-trivial Nodes.
43func (t nodeType) Type() nodeType {
44	return t
45}
46
47const (
48	nodeClass nodeType = iota
49	nodeInstance
50	nodeArray
51	nodeString
52	nodeNumber
53	nodeBool
54	nodeNil
55)
56
57// Nodes.
58
59// NumberNode holds a number: signed or unsigned integer or float.
60// The value is parsed and stored under all the types that can represent the value.
61// This simulates in a small amount of code the behavior of Go's ideal constants.
62type numberNode struct {
63	nodeType
64	pos
65	IsInt   bool    // Number has an unsigned integral value.
66	IsFloat bool    // Number has a floating-point value.
67	Int64   int64   // The unsigned integer value.
68	Float64 float64 // The floating-point value.
69	Text    string  // The original textual representation from the input.
70}
71
72func newNumber(pos pos, text string) (*numberNode, error) {
73	n := &numberNode{nodeType: nodeNumber, pos: pos, Text: text}
74	u, err := strconv.ParseInt(text, 0, 64) // will fail for -0.
75	if err == nil {
76		n.IsInt = true
77		n.Int64 = u
78	}
79	// If an integer extraction succeeded, promote the float.
80	if !n.IsInt {
81		f, err := strconv.ParseFloat(text, 64)
82		if err == nil {
83			n.IsFloat = true
84			n.Float64 = f
85		}
86	}
87	if !n.IsInt && !n.IsFloat {
88		return nil, fmt.Errorf("illegal number syntax: %q", text)
89	}
90	return n, nil
91}
92
93func (n *numberNode) Value() interface{} {
94	if n.IsInt {
95		return n.Int64
96	}
97	return n.Float64
98}
99
100// StringNode holds a string constant. The value has been "unquoted".
101type stringNode struct {
102	nodeType
103	pos
104	Quoted string // The original text of the string, with quotes.
105	Text   string // The string, after quote processing.
106}
107
108func newString(pos pos, orig, text string) *stringNode {
109	return &stringNode{nodeType: nodeString, pos: pos, Quoted: orig, Text: text}
110}
111
112func (s *stringNode) Value() interface{} {
113	return s.Text
114}
115
116type boolNode struct {
117	nodeType
118	pos
119	Val bool
120}
121
122func newBool(pos pos, val bool) *boolNode {
123	return &boolNode{nodeType: nodeBool, pos: pos, Val: val}
124}
125
126func (b *boolNode) Value() interface{} {
127	return b.Val
128}
129
130type nilNode struct {
131	nodeType
132	pos
133}
134
135func newNil(pos pos) *nilNode {
136	return &nilNode{nodeType: nodeNil, pos: pos}
137}
138
139func (b *nilNode) Value() interface{} {
140	return nil
141}
142
143type classNode struct {
144	nodeType
145	pos
146	Members members
147}
148
149func newClass(pos pos) *classNode {
150	return &classNode{
151		nodeType: nodeClass,
152		pos:      pos,
153		Members:  make(members),
154	}
155}
156
157type members map[string]node
158
159func (c *classNode) Value() interface{} {
160	return c.Members.Value()
161}
162
163func (m members) Value() interface{} {
164	r := make(map[string]interface{}, len(m))
165	for k, v := range m {
166		r[k] = v.Value()
167	}
168	return r
169}
170
171type instanceNode struct {
172	nodeType
173	pos
174	Members members
175}
176
177func newInstance(pos pos) *instanceNode {
178	return &instanceNode{
179		nodeType: nodeInstance,
180		pos:      pos,
181		Members:  make(members),
182	}
183}
184
185func (i *instanceNode) Value() interface{} {
186	return i.Members.Value()
187}
188
189type arrayNode struct {
190	nodeType
191	pos
192	Array []node
193}
194
195func newArray(pos pos) *arrayNode {
196	return &arrayNode{nodeType: nodeArray, pos: pos}
197}
198
199func (a *arrayNode) Value() interface{} {
200	r := make([]interface{}, len(a.Array))
201	for i, v := range a.Array {
202		r[i] = v.Value()
203	}
204	return r
205}
206
207// Walk invokes f on n and sub-nodes of n.
208func walk(n node, f func(node)) {
209	f(n)
210	switch n := n.(type) {
211	case *arrayNode:
212		for _, a := range n.Array {
213			walk(a, f)
214		}
215	case *classNode:
216		for _, a := range n.Members {
217			walk(a, f)
218		}
219	case *numberNode, *stringNode:
220		// Ignore.
221	default:
222		panic(fmt.Errorf("other type: %T", n))
223	}
224}
225