all repos

json2go @ fc9c7dff8393608d5d61f0e8f5274ad0904c93c4

convert json to go type annotations

json2go/parser.go (view raw)

Oleksandr Smirnov Oleksandr Smirnov
olexsmir@gmail.com
refactor: use an actual parser instead of reflection..., 11 days ago
1
package json2go
2
3
import (
4
	"fmt"
5
	"strconv"
6
)
7
8
type Parser struct {
9
	lexer *Lexer
10
	cur   Token
11
	peek  Token
12
}
13
14
func NewParser(l *Lexer) *Parser {
15
	p := &Parser{lexer: l}
16
	p.advance() // populate .peek
17
	p.advance() // populate .cur
18
	return p
19
}
20
21
// Parse starts parsing and returns the root Value AST.
22
// Expects well-formed JSON with a single top-level value.
23
// Returns an error if the JSON is malformed or has unexpected tokens after the main value.
24
func (p *Parser) Parse() (Value, error) {
25
	p.skipNoise()
26
	v, err := p.parseValue()
27
	if err != nil {
28
		return Value{}, err
29
	}
30
	p.skipNoise()
31
	if !p.got(EOF) {
32
		return Value{}, fmt.Errorf("unexpected token after value: %q", p.cur.Literal)
33
	}
34
	return v, nil
35
}
36
37
func (p *Parser) parseValue() (Value, error) {
38
	p.skipNoise()
39
	switch p.cur.Type {
40
	case LBRACE:
41
		return p.parseObject()
42
	case LBRACKET:
43
		return p.parseArray()
44
	case STRING:
45
		v := Value{Kind: StringValue, Str: p.cur.Literal}
46
		p.advance()
47
		return v, nil
48
	case NUMBER:
49
		n, err := strconv.ParseInt(p.cur.Literal, 10, 64)
50
		if err != nil {
51
			f, ferr := strconv.ParseFloat(p.cur.Literal, 64)
52
			if ferr != nil {
53
				return Value{}, fmt.Errorf("invalid number: %w", err)
54
			}
55
			p.advance()
56
			return Value{Kind: DecimalValue, Float: f}, nil
57
		}
58
		p.advance()
59
		return Value{Kind: NumberValue, Int: n}, nil
60
	case DECIMAL:
61
		f, err := strconv.ParseFloat(p.cur.Literal, 64)
62
		if err != nil {
63
			return Value{}, fmt.Errorf("invalid decimal: %w", err)
64
		}
65
		p.advance()
66
		return Value{Kind: DecimalValue, Float: f}, nil
67
	case BOOL:
68
		v := Value{Kind: BoolValue, Bool: p.cur.Literal == "true"}
69
		p.advance()
70
		return v, nil
71
	case NULL:
72
		p.advance()
73
		return Value{Kind: NullValue}, nil
74
	default:
75
		return Value{}, fmt.Errorf("unexpected token %q (%q)", p.cur.Type, p.cur.Literal)
76
	}
77
}
78
79
func (p *Parser) parseObject() (Value, error) {
80
	p.advance()
81
	var fields []Field
82
	for {
83
		p.skipNoise()
84
		if p.got(RBRACE) {
85
			p.advance()
86
			return Value{Kind: ObjectValue, Object: fields}, nil
87
		}
88
		if p.got(EOF) {
89
			return Value{}, fmt.Errorf("unterminated object")
90
		}
91
92
		keyTok, err := p.expect(STRING)
93
		if err != nil {
94
			return Value{}, err
95
		}
96
97
		p.skipNoise()
98
		if _, cerr := p.expect(COLON); cerr != nil {
99
			return Value{}, cerr
100
		}
101
102
		val, err := p.parseValue()
103
		if err != nil {
104
			return Value{}, err
105
		}
106
		fields = append(fields, Field{keyTok.Literal, val})
107
108
		p.skipNoise()
109
		if p.got(COMMA) {
110
			p.advance()
111
		} else if !p.got(RBRACE) {
112
			_, err := p.expect(RBRACE)
113
			return Value{}, err
114
		}
115
	}
116
}
117
118
func (p *Parser) parseArray() (Value, error) {
119
	p.advance()
120
	var items []Value
121
	for {
122
		p.skipNoise()
123
		if p.got(RBRACKET) {
124
			p.advance()
125
			return Value{Kind: ArrayValue, Array: items}, nil
126
		}
127
		if p.got(EOF) {
128
			return Value{}, fmt.Errorf("unterminated array")
129
		}
130
131
		val, err := p.parseValue()
132
		if err != nil {
133
			return Value{}, err
134
		}
135
		items = append(items, val)
136
137
		p.skipNoise()
138
		if p.got(COMMA) {
139
			p.advance()
140
		} else if !p.got(RBRACKET) {
141
			_, err := p.expect(RBRACKET)
142
			return Value{}, err
143
		}
144
	}
145
}
146
147
func (p *Parser) got(kind TokenType) bool { return p.cur.Type == kind }
148
func (p *Parser) advance() Token {
149
	prev := p.cur
150
	p.cur = p.peek
151
	p.peek = p.lexer.Next()
152
	return prev
153
}
154
155
func (p *Parser) expect(kind TokenType) (Token, error) {
156
	if p.got(kind) {
157
		return p.advance(), nil
158
	}
159
	return p.cur, fmt.Errorf("expected %s, got %s", kind, p.cur.Type)
160
}
161
162
func (p *Parser) skipNoise() {
163
	for p.cur.Type == NEWLINE || p.cur.Type == INDENT ||
164
		p.cur.Type == COMMENTLINE || p.cur.Type == COMMENTBLOCK {
165
		p.advance()
166
	}
167
}