|
|
|
|
@ -1,3 +1,4 @@
|
|
|
|
|
// Package parser defines the structure and methods for parsing JSON.
|
|
|
|
|
package parser
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
@ -10,15 +11,18 @@ import (
|
|
|
|
|
"gitea.paas.celticinfo.fr/oabrivard/gojson/token"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
// Parser struct represents a parser with a lexer, current and peek tokens,
|
|
|
|
|
// and a slice to store parsing errors.
|
|
|
|
|
type Parser struct {
|
|
|
|
|
lexer *lexer.Lexer
|
|
|
|
|
lexer *lexer.Lexer // the lexer from which the parser receives tokens
|
|
|
|
|
|
|
|
|
|
curToken token.Token
|
|
|
|
|
peekToken token.Token
|
|
|
|
|
curToken token.Token // current token under examination
|
|
|
|
|
peekToken token.Token // next token in the input
|
|
|
|
|
|
|
|
|
|
errors []string
|
|
|
|
|
errors []string // slice to store errors encountered during parsing
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// NewParser creates and initializes a new Parser with the given lexer.
|
|
|
|
|
func NewParser(l *lexer.Lexer) *Parser {
|
|
|
|
|
p := &Parser{lexer: l}
|
|
|
|
|
// Initialize curToken and peekToken
|
|
|
|
|
@ -27,24 +31,26 @@ func NewParser(l *lexer.Lexer) *Parser {
|
|
|
|
|
return p
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// nextToken advances both curToken and peekToken.
|
|
|
|
|
func (p *Parser) nextToken() {
|
|
|
|
|
p.curToken = p.peekToken
|
|
|
|
|
p.peekToken = p.lexer.NextToken()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Methods to parse JSON structure
|
|
|
|
|
|
|
|
|
|
// JsonObject and JsonArray are types to represent JSON objects and arrays, respectively.
|
|
|
|
|
type JsonObject map[string]interface{}
|
|
|
|
|
type JsonArray []interface{}
|
|
|
|
|
|
|
|
|
|
// Parse starts the parsing process and returns the top-level JSON object.
|
|
|
|
|
func (p *Parser) Parse() JsonObject {
|
|
|
|
|
return p.parseObject()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// parseObject parses a JSON object from the token stream.
|
|
|
|
|
func (p *Parser) parseObject() JsonObject {
|
|
|
|
|
object := make(JsonObject)
|
|
|
|
|
|
|
|
|
|
// Expect the current token to be TokenBeginObject
|
|
|
|
|
// Ensure the current token is the beginning of an object
|
|
|
|
|
if !p.curTokenIs(token.BEGIN_OBJECT) {
|
|
|
|
|
p.addError("expected '{'")
|
|
|
|
|
return nil
|
|
|
|
|
@ -53,14 +59,14 @@ func (p *Parser) parseObject() JsonObject {
|
|
|
|
|
// Move to the next token
|
|
|
|
|
p.nextToken()
|
|
|
|
|
|
|
|
|
|
// Loop until we find an end object token
|
|
|
|
|
// Loop until the end of the object is reached
|
|
|
|
|
for !p.curTokenIs(token.END_OBJECT) && !p.curTokenIs(token.EOF) {
|
|
|
|
|
key := p.parseObjectKey()
|
|
|
|
|
if key == "" {
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Expect a name separator (:)
|
|
|
|
|
// Ensure a name separator (:) follows the key
|
|
|
|
|
if !p.expectPeek(token.NAME_SEPARATOR) {
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
@ -76,12 +82,12 @@ func (p *Parser) parseObject() JsonObject {
|
|
|
|
|
|
|
|
|
|
object[key] = value
|
|
|
|
|
|
|
|
|
|
// Move past the value, potentially to a comma or the end object
|
|
|
|
|
// Move past the value
|
|
|
|
|
p.nextToken()
|
|
|
|
|
|
|
|
|
|
// If we have a comma, the object continues
|
|
|
|
|
// Handle comma separation for multiple key-value pairs
|
|
|
|
|
if p.curTokenIs(token.VALUE_SEPARATOR) {
|
|
|
|
|
if p.peekToken.Type == token.END_OBJECT { // no comma just before the end of the object
|
|
|
|
|
if p.peekToken.Type == token.END_OBJECT { // No comma just before the end of the object
|
|
|
|
|
p.addError("No ',' before '}'")
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
@ -90,7 +96,7 @@ func (p *Parser) parseObject() JsonObject {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Expect the end object token
|
|
|
|
|
// Ensure the end of the object is reached
|
|
|
|
|
if !p.curTokenIs(token.END_OBJECT) {
|
|
|
|
|
p.addError("expected '}'")
|
|
|
|
|
return nil
|
|
|
|
|
@ -99,10 +105,11 @@ func (p *Parser) parseObject() JsonObject {
|
|
|
|
|
return object
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// parseArray parses a JSON array from the token stream.
|
|
|
|
|
func (p *Parser) parseArray() JsonArray {
|
|
|
|
|
array := JsonArray{}
|
|
|
|
|
|
|
|
|
|
// Expect the current token to be TokenBeginArray
|
|
|
|
|
// Ensure the current token is the beginning of an array
|
|
|
|
|
if !p.curTokenIs(token.BEGIN_ARRAY) {
|
|
|
|
|
p.addError("expected '['")
|
|
|
|
|
return nil
|
|
|
|
|
@ -111,7 +118,7 @@ func (p *Parser) parseArray() JsonArray {
|
|
|
|
|
// Move to the next token
|
|
|
|
|
p.nextToken()
|
|
|
|
|
|
|
|
|
|
// Loop until we find an end array token
|
|
|
|
|
// Loop until the end of the array is reached
|
|
|
|
|
for !p.curTokenIs(token.END_ARRAY) {
|
|
|
|
|
// Parse the value
|
|
|
|
|
value, err := p.parseValue()
|
|
|
|
|
@ -124,13 +131,13 @@ func (p *Parser) parseArray() JsonArray {
|
|
|
|
|
// Move past the value
|
|
|
|
|
p.nextToken()
|
|
|
|
|
|
|
|
|
|
// If we have a value separator (comma), continue to the next value
|
|
|
|
|
// Handle comma separation for multiple values
|
|
|
|
|
if p.curTokenIs(token.VALUE_SEPARATOR) {
|
|
|
|
|
p.nextToken()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Expect the end array token
|
|
|
|
|
// Ensure the end of the array is reached
|
|
|
|
|
if !p.curTokenIs(token.END_ARRAY) {
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
@ -138,10 +145,12 @@ func (p *Parser) parseArray() JsonArray {
|
|
|
|
|
return array
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// addError appends an error message to the parser's errors slice.
|
|
|
|
|
func (p *Parser) addError(msg string) {
|
|
|
|
|
p.errors = append(p.errors, msg)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// parseObjectKey parses and returns the key of an object field.
|
|
|
|
|
func (p *Parser) parseObjectKey() string {
|
|
|
|
|
if p.curToken.Type != token.STRING {
|
|
|
|
|
p.addError("expected string for key")
|
|
|
|
|
@ -150,6 +159,7 @@ func (p *Parser) parseObjectKey() string {
|
|
|
|
|
return p.curToken.Value
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// parseValue parses a JSON value based on the current token type.
|
|
|
|
|
func (p *Parser) parseValue() (interface{}, error) {
|
|
|
|
|
switch p.curToken.Type {
|
|
|
|
|
case token.STRING:
|
|
|
|
|
@ -164,18 +174,17 @@ func (p *Parser) parseValue() (interface{}, error) {
|
|
|
|
|
return p.parseObject(), nil
|
|
|
|
|
case token.BEGIN_ARRAY:
|
|
|
|
|
return p.parseArray(), nil
|
|
|
|
|
// ... other cases
|
|
|
|
|
default:
|
|
|
|
|
p.addError("unexpected token")
|
|
|
|
|
return nil, errors.New("unexpected token")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// parseNumber parses a number token into an appropriate Go numeric type.
|
|
|
|
|
func (p *Parser) parseNumber() interface{} {
|
|
|
|
|
// Assuming the number is in a string format in the token
|
|
|
|
|
numStr := p.curToken.Value
|
|
|
|
|
|
|
|
|
|
// Check if the number is an integer or a float
|
|
|
|
|
// Check for float or integer representation
|
|
|
|
|
if strings.Contains(numStr, ".") || strings.ContainsAny(numStr, "eE") {
|
|
|
|
|
// Parse as float
|
|
|
|
|
val, err := strconv.ParseFloat(numStr, 64)
|
|
|
|
|
@ -195,10 +204,12 @@ func (p *Parser) parseNumber() interface{} {
|
|
|
|
|
return val
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// parseBoolean returns a boolean value based on the current token.
|
|
|
|
|
func (p *Parser) parseBoolean() bool {
|
|
|
|
|
return p.curToken.Type == token.TRUE
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// expectPeek checks if the next token is of the expected type.
|
|
|
|
|
func (p *Parser) expectPeek(t token.TokenType) bool {
|
|
|
|
|
if p.peekToken.Type == t {
|
|
|
|
|
p.nextToken()
|
|
|
|
|
@ -209,14 +220,7 @@ func (p *Parser) expectPeek(t token.TokenType) bool {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// curTokenIs checks if the current token is of a specific type.
|
|
|
|
|
func (p *Parser) curTokenIs(t token.TokenType) bool {
|
|
|
|
|
return p.curToken.Type == t
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
func (p *Parser) parseArray() *JsonArray {
|
|
|
|
|
// Implementation for parsing an array
|
|
|
|
|
}
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
// ... other parse methods for different types
|
|
|
|
|
|