<commit_before>// Copyright 2017 Tomas Machalek <tomas.machalek@gmail.com>
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package vertigo

import (
	"bufio"
	"compress/gzip"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"os"
	"strings"

	"golang.org/x/text/encoding/charmap"
	"golang.org/x/text/transform"
)

const (
	channelChunkSize          = 250000 // changing the value affects performance (10k...300k ~ 15%)
	logProgressEachNthDefault = 1000000
	LineTypeToken             = "token"
	LineTypeStruct            = "struct"
	LineTypeIgnored           = "ignored"

	AccumulatorTypeStack = "stack"
	AccumulatorTypeComb  = "comb"
	AccumulatorTypeNil   = "nil"

	CharsetISO8859_1   = "iso-8859-1"
	CharsetISO8859_2   = "iso-8859-2"
	CharsetISO8859_3   = "iso-8859-3"
	CharsetISO8859_4   = "iso-8859-4"
	CharsetISO8859_5   = "iso-8859-5"
	CharsetISO8859_6   = "iso-8859-6"
	CharsetISO8859_7   = "iso-8859-7"
	CharsetISO8859_8   = "iso-8859-8"
	CharsetWindows1250 = "windows-1250"
	CharsetWindows1251 = "windows-1251"
	CharsetWindows1252 = "windows-1252"
	CharsetWindows1253 = "windows-1253"
	CharsetWindows1254 = "windows-1254"
	CharsetWindows1255 = "windows-1255"
	CharsetWindows1256 = "windows-1256"
	CharsetWindows1257 = "windows-1257"
	CharsetWindows1258 = "windows-1258"
	CharsetUTF_8       = "utf-8"
)

// --------------------------------------------------------

// ParserConf contains configuration parameters for
// vertical file parser
type ParserConf struct {

	// Source vertical file (either a plain text file or a gzip one)
	InputFilePath string `json:"inputFilePath"`

	Encoding string `json:"encoding"`

	FilterArgs [][][]string `json:"filterArgs"`

	StructAttrAccumulator string `json:"structAttrAccumulator"`

	LogProgressEachNth int `json:"logProgressEachNth"`
}

// LoadConfig loads the configuration from a JSON file.
// In case of an error the program exits with panic.
func LoadConfig(path string) *ParserConf {
	rawData, err := ioutil.ReadFile(path)
	if err != nil {
		panic(err)
	}
	var conf ParserConf
	err = json.Unmarshal(rawData, &conf)
	if err != nil {
		panic(err)
	}
	return &conf
}

// ------

type structAttrAccumulator interface {
	Begin(value *Structure) error
	End(name string) (*Structure, error)
	GetAttrs() map[string]string
	Size() int
}

// --------------------------------------------------------

// LineProcessor describes an object able to handle
// Vertigo's parsing events.
type LineProcessor interface {

	// ProcToken is called each time the parser encounters a positional
	// attribute. In case parsing produces an error, it is passed to the
	// function without stopping the whole process.
	// In case the function returns an error, the parser stops
	// (in the simplest case it can be even the error it recieves)
	ProcToken(token *Token, line int, err error) error

	// ProcStruct is called each time parser encounters a structure opening
	// element (e.g. <doc>). In case parsing produces an error, it is passed
	// to the function without stopping the whole process.
	// In case the function returns an error, the parser stops.
	ProcStruct(strc *Structure, line int, err error) error

	// ProcStructClose is called each time parser encouters a structure
	// closing element (e.g. </doc>). In case parsing produces an error,
	// it is passed to the function without stopping the whole process.
	// In case the function returns an error, the parser stops.
	ProcStructClose(strc *StructureClose, line int, err error) error
}

// ----

type procItem struct {
	idx   int
	value interface{}
	err   error
}

// --------------------------------------------------------

func createStructAttrAccumulator(ident string) (structAttrAccumulator, error) {
	switch ident {
	case AccumulatorTypeStack:
		return newStack(), nil
	case AccumulatorTypeComb:
		return newStructAttrs(), nil
	case AccumulatorTypeNil:
		return newNilStructAttrs(), nil
	default:
		return nil, fmt.Errorf("Unknown accumulator type \"%s\"", ident)
	}
}

// SupportedCharsets returns a list of names of
// character sets.
func SupportedCharsets() []string {
	return []string{CharsetISO8859_2, CharsetUTF_8, CharsetWindows1250}
}

// GetCharmapByName returns a proper Charmap instance based
// on provided encoding name. The name detection is case
// insensitive (e.g. utf-8 is the same as UTF-8). The number
// of supported charsets is
func GetCharmapByName(name string) (*charmap.Charmap, error) {
	switch strings.ToLower(name) {
	case CharsetISO8859_1:
		return charmap.ISO8859_1, nil
	case CharsetISO8859_2:
		return charmap.ISO8859_2, nil
	case CharsetISO8859_3:
		return charmap.ISO8859_3, nil
	case CharsetISO8859_4:
		return charmap.ISO8859_4, nil
	case CharsetISO8859_5:
		return charmap.ISO8859_5, nil
	case CharsetISO8859_6:
		return charmap.ISO8859_6, nil
	case CharsetISO8859_7:
		return charmap.ISO8859_7, nil
	case CharsetISO8859_8:
		return charmap.ISO8859_8, nil
	case CharsetWindows1250:
		return charmap.Windows1250, nil
	case CharsetWindows1251:
		return charmap.Windows1251, nil
	case CharsetWindows1252:
		return charmap.Windows1252, nil
	case CharsetWindows1253:
		return charmap.Windows1253, nil
	case CharsetWindows1254:
		return charmap.Windows1254, nil
	case CharsetWindows1255:
		return charmap.Windows1255, nil
	case CharsetWindows1256:
		return charmap.Windows1256, nil
	case CharsetWindows1257:
		return charmap.Windows1257, nil
	case CharsetWindows1258:
		return charmap.Windows1258, nil
	case CharsetUTF_8:
		return nil, nil
	case "":
		log.Printf("No charset specified, assuming utf-8")
		return nil, nil
	default:
		return nil, fmt.Errorf("Unsupported charset '%s'", name)
	}
}

func importString(s string, ch *charmap.Charmap) string {
	if ch == nil {
		return s
	}
	ans, _, _ := transform.String(ch.NewDecoder(), s)
	// TODO handle error
	return ans
}

func openInputFile(path string) (io.Reader, error) {
	f, err := os.Open(path)
	if err != nil {
		return nil, err
	}
	finfo, err := f.Stat()
	if err != nil {
		return nil, err
	}
	if !finfo.Mode().IsRegular() {
		return nil, fmt.Errorf("Path %s is not a regular file", path)
	}

	var rd io.Reader
	if strings.HasSuffix(path, ".gz") {
		rd, err = gzip.NewReader(f)
		if err != nil {
			return nil, err
		}

	} else {
		rd = f
	}
	return rd, nil
}

// ParseVerticalFile processes a corpus vertical file
// line by line and applies a custom LineProcessor on
// them. The processing is parallelized in the sense
// that reading a file into lines and processing of
// the lines runs in different goroutines. But the
// function as a whole behaves synchronously - i.e.
// once it returns a value, the processing is finished.
func ParseVerticalFile(conf *ParserConf, lproc LineProcessor) error {
	logProgressEachNth := logProgressEachNthDefault
	if conf.LogProgressEachNth > 0 {
		logProgressEachNth = conf.LogProgressEachNth
	}
	rd, err := openInputFile(conf.InputFilePath)
	if err != nil {
		return err
	}
	brd := bufio.NewScanner(rd)

	stack, err := createStructAttrAccumulator(conf.StructAttrAccumulator)
	if err != nil {
		return err
	}

	chm, chErr := GetCharmapByName(conf.Encoding)
	if chErr != nil {
		return chErr
	}
	log.Printf("Configured conversion from charset %s", chm)
	ch := make(chan []procItem)
	chunk := make([]procItem, channelChunkSize)
	stop := make(chan struct{})
	defer close(stop)

	go func() {
		defer close(ch)
		i := 0
		lineNum := 0
		tokenNum := 0
		for brd.Scan() {
			line, parseErr := parseLine(importString(brd.Text(), chm), stack)
			tok, isTok := line.(*Token)
			if isTok {
				tok.Idx = tokenNum
				tokenNum++
			}
			chunk[i] = procItem{idx: lineNum, value: line, err: parseErr}
			i++
			if i == channelChunkSize {
				i = 0
				ch <- chunk
				chunk = make([]procItem, channelChunkSize)
			}
			if lineNum%logProgressEachNth == 0 {
				log.Printf("...processed %d lines.\n", lineNum)
			}
			lineNum++
			select {
			case <-stop:
				fmt.Println("STOPPING PARSING")
				return
			default:
			}
		}
		if i > 0 {
			ch <- chunk[:i]
		}
	}()

	var procErr error
	for items := range ch {
		for _, item := range items {
			switch item.value.(type) {
			case *Token:
				tk := item.value.(*Token)
				if tk.MatchesFilter(conf.FilterArgs) {
					procErr = lproc.ProcToken(tk, item.idx, err)
				}
			case *Structure:
				procErr = lproc.ProcStruct(item.value.(*Structure), item.idx, item.err)
			case *StructureClose:
				procErr = lproc.ProcStructClose(item.value.(*StructureClose), item.idx, item.err)
			}
			if procErr != nil {
				return procErr
			}
		}
	}

	log.Println("Parsing done. Metadata stack size: ", stack.Size())
	return nil
}

//ParseVerticalFileNoGoRo is just for benchmarking purposes
func ParseVerticalFileNoGoRo(conf *ParserConf, lproc LineProcessor) {
	f, err := os.Open(conf.InputFilePath)
	if err != nil {
		panic(err)
	}
	rd := bufio.NewScanner(f)
	stack := newStack()
	i := 0
	for rd.Scan() {
		token, err := parseLine(rd.Text(), stack)
		switch token.(type) {
		case *Token:
			lproc.ProcToken(token.(*Token), i, err)
		}
		i++
	}

	log.Println("Parsing done. Metadata stack size: ", stack.Size())
}
<commit_msg>Add support for pipe input from a script<commit_after>// Copyright 2017 Tomas Machalek <tomas.machalek@gmail.com>
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package vertigo

import (
	"bufio"
	"compress/gzip"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"os"
	"os/exec"
	"regexp"
	"strings"

	"golang.org/x/text/encoding/charmap"
	"golang.org/x/text/transform"
)

var (
	vertCmdSplit = regexp.MustCompile(`\s+`)
)

const (
	channelChunkSize          = 250000 // changing the value affects performance (10k...300k ~ 15%)
	logProgressEachNthDefault = 1000000
	LineTypeToken             = "token"
	LineTypeStruct            = "struct"
	LineTypeIgnored           = "ignored"

	AccumulatorTypeStack = "stack"
	AccumulatorTypeComb  = "comb"
	AccumulatorTypeNil   = "nil"

	CharsetISO8859_1   = "iso-8859-1"
	CharsetISO8859_2   = "iso-8859-2"
	CharsetISO8859_3   = "iso-8859-3"
	CharsetISO8859_4   = "iso-8859-4"
	CharsetISO8859_5   = "iso-8859-5"
	CharsetISO8859_6   = "iso-8859-6"
	CharsetISO8859_7   = "iso-8859-7"
	CharsetISO8859_8   = "iso-8859-8"
	CharsetWindows1250 = "windows-1250"
	CharsetWindows1251 = "windows-1251"
	CharsetWindows1252 = "windows-1252"
	CharsetWindows1253 = "windows-1253"
	CharsetWindows1254 = "windows-1254"
	CharsetWindows1255 = "windows-1255"
	CharsetWindows1256 = "windows-1256"
	CharsetWindows1257 = "windows-1257"
	CharsetWindows1258 = "windows-1258"
	CharsetUTF_8       = "utf-8"
)

// --------------------------------------------------------

// ParserConf contains configuration parameters for
// vertical file parser
type ParserConf struct {

	// Source vertical file (either a plain text file or a gzip one)
	InputFilePath string `json:"inputFilePath"`

	Encoding string `json:"encoding"`

	FilterArgs [][][]string `json:"filterArgs"`

	StructAttrAccumulator string `json:"structAttrAccumulator"`

	LogProgressEachNth int `json:"logProgressEachNth"`
}

// LoadConfig loads the configuration from a JSON file.
// In case of an error the program exits with panic.
func LoadConfig(path string) *ParserConf {
	rawData, err := ioutil.ReadFile(path)
	if err != nil {
		panic(err)
	}
	var conf ParserConf
	err = json.Unmarshal(rawData, &conf)
	if err != nil {
		panic(err)
	}
	return &conf
}

// ------

type structAttrAccumulator interface {
	Begin(value *Structure) error
	End(name string) (*Structure, error)
	GetAttrs() map[string]string
	Size() int
}

// --------------------------------------------------------

// LineProcessor describes an object able to handle
// Vertigo's parsing events.
type LineProcessor interface {

	// ProcToken is called each time the parser encounters a positional
	// attribute. In case parsing produces an error, it is passed to the
	// function without stopping the whole process.
	// In case the function returns an error, the parser stops
	// (in the simplest case it can be even the error it recieves)
	ProcToken(token *Token, line int, err error) error

	// ProcStruct is called each time parser encounters a structure opening
	// element (e.g. <doc>). In case parsing produces an error, it is passed
	// to the function without stopping the whole process.
	// In case the function returns an error, the parser stops.
	ProcStruct(strc *Structure, line int, err error) error

	// ProcStructClose is called each time parser encouters a structure
	// closing element (e.g. </doc>). In case parsing produces an error,
	// it is passed to the function without stopping the whole process.
	// In case the function returns an error, the parser stops.
	ProcStructClose(strc *StructureClose, line int, err error) error
}

// ----

type procItem struct {
	idx   int
	value interface{}
	err   error
}

// --------------------------------------------------------

func createStructAttrAccumulator(ident string) (structAttrAccumulator, error) {
	switch ident {
	case AccumulatorTypeStack:
		return newStack(), nil
	case AccumulatorTypeComb:
		return newStructAttrs(), nil
	case AccumulatorTypeNil:
		return newNilStructAttrs(), nil
	default:
		return nil, fmt.Errorf("Unknown accumulator type \"%s\"", ident)
	}
}

// SupportedCharsets returns a list of names of
// character sets.
func SupportedCharsets() []string {
	return []string{CharsetISO8859_2, CharsetUTF_8, CharsetWindows1250}
}

// GetCharmapByName returns a proper Charmap instance based
// on provided encoding name. The name detection is case
// insensitive (e.g. utf-8 is the same as UTF-8). The number
// of supported charsets is
func GetCharmapByName(name string) (*charmap.Charmap, error) {
	switch strings.ToLower(name) {
	case CharsetISO8859_1:
		return charmap.ISO8859_1, nil
	case CharsetISO8859_2:
		return charmap.ISO8859_2, nil
	case CharsetISO8859_3:
		return charmap.ISO8859_3, nil
	case CharsetISO8859_4:
		return charmap.ISO8859_4, nil
	case CharsetISO8859_5:
		return charmap.ISO8859_5, nil
	case CharsetISO8859_6:
		return charmap.ISO8859_6, nil
	case CharsetISO8859_7:
		return charmap.ISO8859_7, nil
	case CharsetISO8859_8:
		return charmap.ISO8859_8, nil
	case CharsetWindows1250:
		return charmap.Windows1250, nil
	case CharsetWindows1251:
		return charmap.Windows1251, nil
	case CharsetWindows1252:
		return charmap.Windows1252, nil
	case CharsetWindows1253:
		return charmap.Windows1253, nil
	case CharsetWindows1254:
		return charmap.Windows1254, nil
	case CharsetWindows1255:
		return charmap.Windows1255, nil
	case CharsetWindows1256:
		return charmap.Windows1256, nil
	case CharsetWindows1257:
		return charmap.Windows1257, nil
	case CharsetWindows1258:
		return charmap.Windows1258, nil
	case CharsetUTF_8:
		return nil, nil
	case "":
		log.Printf("No charset specified, assuming utf-8")
		return nil, nil
	default:
		return nil, fmt.Errorf("Unsupported charset '%s'", name)
	}
}

func importString(s string, ch *charmap.Charmap) string {
	if ch == nil {
		return s
	}
	ans, _, _ := transform.String(ch.NewDecoder(), s)
	// TODO handle error
	return ans
}

func openInputFile(path string) (io.Reader, error) {
	f, err := os.Open(path)
	if err != nil {
		return nil, err
	}
	finfo, err := f.Stat()
	if err != nil {
		return nil, err
	}
	if !finfo.Mode().IsRegular() {
		return nil, fmt.Errorf("Path %s is not a regular file", path)
	}

	var rd io.Reader
	if strings.HasSuffix(path, ".gz") {
		rd, err = gzip.NewReader(f)
		if err != nil {
			return nil, err
		}

	} else {
		rd = f
	}
	return rd, nil
}

// ParseVerticalFile processes a corpus vertical file
// line by line and applies a custom LineProcessor on
// them. The processing is parallelized in the sense
// that reading a file into lines and processing of
// the lines runs in different goroutines. But the
// function as a whole behaves synchronously - i.e.
// once it returns a value, the processing is finished.
func ParseVerticalFile(conf *ParserConf, lproc LineProcessor) error {

	chm, chErr := GetCharmapByName(conf.Encoding)
	if chErr != nil {
		return chErr
	}
	log.Printf("Configured conversion from charset %s", chm)

	if strings.HasPrefix(conf.InputFilePath, "|") {
		script := vertCmdSplit.Split(conf.InputFilePath, -1)
		if len(script) < 2 {
			return fmt.Errorf("invalid dynamically generated vertical file specification")
		}
		cmd := exec.Command(script[1], script[2:]...)
		cmd.Env = os.Environ()
		rd, err := cmd.StdoutPipe()
		if err != nil {
			return err
		}
		brd := bufio.NewScanner(rd)

		if err = cmd.Start(); err != nil {
			return err
		}
		if err = parseVerticalFromScanner(brd, chm, conf, lproc); err != nil {
			return err
		}
		if err := cmd.Wait(); err != nil {
			return err
		}

	} else {
		rd, err := openInputFile(conf.InputFilePath)
		if err != nil {
			return err
		}
		brd := bufio.NewScanner(rd)
		if err = parseVerticalFromScanner(brd, chm, conf, lproc); err != nil {
			return err
		}
	}
	return nil
}

func parseVerticalFromScanner(
	brd *bufio.Scanner, chm *charmap.Charmap, conf *ParserConf, lproc LineProcessor) error {
	ch := make(chan []procItem)
	chunk := make([]procItem, channelChunkSize)
	stop := make(chan struct{})
	defer close(stop)

	stack, err := createStructAttrAccumulator(conf.StructAttrAccumulator)
	if err != nil {
		return err
	}
	logProgressEachNth := logProgressEachNthDefault
	if conf.LogProgressEachNth > 0 {
		logProgressEachNth = conf.LogProgressEachNth
	}
	go func() {
		defer close(ch)
		i := 0
		lineNum := 0
		tokenNum := 0

		for brd.Scan() {
			line, parseErr := parseLine(importString(brd.Text(), chm), stack)
			tok, isTok := line.(*Token)
			if isTok {
				tok.Idx = tokenNum
				tokenNum++
			}
			chunk[i] = procItem{idx: lineNum, value: line, err: parseErr}
			i++
			if i == channelChunkSize {
				i = 0
				ch <- chunk
				chunk = make([]procItem, channelChunkSize)
			}
			if lineNum%logProgressEachNth == 0 {
				log.Printf("...processed %d lines.\n", lineNum)
			}
			lineNum++
			select {
			case <-stop:
				fmt.Println("STOPPING PARSING")
				return
			default:
			}
		}
		if i > 0 {
			ch <- chunk[:i]
		}
	}()

	var procErr error
	for items := range ch {
		for _, item := range items {
			switch item.value.(type) {
			case *Token:
				tk := item.value.(*Token)
				if tk.MatchesFilter(conf.FilterArgs) {
					procErr = lproc.ProcToken(tk, item.idx, err)
				}
			case *Structure:
				procErr = lproc.ProcStruct(item.value.(*Structure), item.idx, item.err)
			case *StructureClose:
				procErr = lproc.ProcStructClose(item.value.(*StructureClose), item.idx, item.err)
			}
			if procErr != nil {
				return procErr
			}
		}
	}

	log.Println("Parsing done. Metadata stack size: ", stack.Size())
	return nil
}

//ParseVerticalFileNoGoRo is just for benchmarking purposes
func ParseVerticalFileNoGoRo(conf *ParserConf, lproc LineProcessor) {
	f, err := os.Open(conf.InputFilePath)
	if err != nil {
		panic(err)
	}
	rd := bufio.NewScanner(f)
	stack := newStack()
	i := 0
	for rd.Scan() {
		token, err := parseLine(rd.Text(), stack)
		switch token.(type) {
		case *Token:
			lproc.ProcToken(token.(*Token), i, err)
		}
		i++
	}

	log.Println("Parsing done. Metadata stack size: ", stack.Size())
}
<|endoftext|>


<commit_before>package php

import (
	"fmt"

	"stephensearles.com/php/ast"
)

type parser struct {
	lexer *lexer

	previous   []Item
	idx        int
	current    Item
	errors     []error
	parenLevel int
	errorMap   map[int]bool
	errorCount int

	Debug     bool
	MaxErrors int
}

func NewParser(input string) *parser {
	p := &parser{
		idx:       -1,
		MaxErrors: 10,
		lexer:     newLexer(input),
		errorMap:  make(map[int]bool),
	}
	return p
}

func (p *parser) Parse() (nodes []ast.Node, errors []error) {
	defer func() {
		if r := recover(); r != nil {
			if p.Debug {
				for _, err := range p.errors {
					fmt.Println(err)
				}
				panic(r)
			}
			errors = append([]error{fmt.Errorf("%s", r)}, p.errors...)
		}
	}()
	// expecting either itemHTML or itemPHPBegin
	nodes = make([]ast.Node, 0, 1)
TokenLoop:
	for {
		p.next()
		switch p.current.typ {
		case itemEOF:
			break TokenLoop
		default:
			n := p.parseNode()
			if n != nil {
				nodes = append(nodes, n)
			}
		}
	}
	errors = p.errors
	return nodes, errors
}

func (p *parser) parseNode() ast.Node {
	switch p.current.typ {
	case itemHTML:
		return ast.Echo(ast.Literal{Type: ast.String})
	case itemPHPBegin:
		return nil
	case itemPHPEnd:
		return nil
	}
	return p.parseStmt()
}

func (p *parser) next() {
	p.idx += 1
	if len(p.previous) <= p.idx {
		p.current = p.lexer.nextItem()
		p.previous = append(p.previous, p.current)
	} else {
		p.current = p.previous[p.idx]
	}
}

func (p *parser) backup() {
	p.idx -= 1
	p.current = p.previous[p.idx]
}

func (p *parser) peek() (i Item) {
	p.next()
	i = p.current
	p.backup()
	return
}

func (p *parser) expectCurrent(i ItemType) {
	if p.current.typ != i {
		p.expected(i)
	}
}

func (p *parser) expectAndNext(i ItemType) {
	if p.current.typ != i {
		p.expected(i)
	}
	p.next()
}

func (p *parser) expect(i ItemType) {
	p.next()
	p.expectCurrent(i)
}

func (p *parser) expected(i ItemType) {
	p.errorf("Found %s, expected %s", p.current, i)
}

func (p *parser) errorf(str string, args ...interface{}) {
	p.errorCount += 1
	if p.errorCount > p.MaxErrors {
		panic("too many errors")
	}
	if _, ok := p.errorMap[p.current.pos.Line]; ok {
		return
	}
	errString := fmt.Sprintf(str, args...)
	p.errorMap[p.current.pos.Line] = true
	p.errors = append(p.errors, fmt.Errorf("%s: %s", p.errorPrefix(), errString))
}

func (p *parser) errorPrefix() string {
	return fmt.Sprintf("%s %d", p.lexer.file, p.current.pos.Line)
}

func (p *parser) parseNextExpression() ast.Expression {
	p.next()
	return p.parseExpression()
}

func (p *parser) parseFunctionCall(callable ast.Expression) *ast.FunctionCallExpression {
	expr := &ast.FunctionCallExpression{}
	expr.FunctionName = callable
	return p.parseFunctionArguments(expr)
}

func (p *parser) parseFunctionArguments(expr *ast.FunctionCallExpression) *ast.FunctionCallExpression {
	expr.Arguments = make([]ast.Expression, 0)
	p.expect(itemOpenParen)
	if p.peek().typ == itemCloseParen {
		p.expect(itemCloseParen)
		return expr
	}
	expr.Arguments = append(expr.Arguments, p.parseNextExpression())
	for p.peek().typ != itemCloseParen {
		p.expect(itemComma)
		arg := p.parseNextExpression()
		if arg == nil {
			break
		}
		expr.Arguments = append(expr.Arguments, arg)
	}
	p.expect(itemCloseParen)
	return expr

}

func (p *parser) parseStmt() ast.Statement {
	switch p.current.typ {
	case itemBlockBegin:
		p.backup()
		return p.parseBlock()
	case itemGlobal:
		p.next()
		g := &ast.GlobalDeclaration{
			Identifiers: make([]*ast.Variable, 0, 1),
		}
		for p.current.typ == itemVariableOperator {
			variable, ok := p.parseVariable().(*ast.Variable)
			if !ok {
				p.errorf("global declarations must be of standard variables")
				break
			}
			g.Identifiers = append(g.Identifiers, variable)
			if p.peek().typ != itemComma {
				break
			}
			p.expect(itemComma)
			p.next()
		}
		p.expectStmtEnd()
		return g
	case itemNamespace:
		p.expect(itemIdentifier)
		p.expectStmtEnd()
		// We are ignoring this for now
		return nil
	case itemUse:
		p.expect(itemIdentifier)
		if p.peek().typ == itemAsOperator {
			p.expect(itemAsOperator)
			p.expect(itemIdentifier)
		}
		p.expectStmtEnd()
		// We are ignoring this for now
		return nil
	case itemVariableOperator:
		ident := p.expressionize()
		switch p.peek().typ {
		case itemUnaryOperator:
			expr := ast.ExpressionStmt{p.parseOperation(p.parenLevel, ident)}
			p.expectStmtEnd()
			return expr
		case itemOpenParen:
			var expr ast.Expression
			expr = p.parseFunctionArguments(&ast.FunctionCallExpression{
				FunctionName: ident,
			})
			if p.peek().typ == itemObjectOperator {
				expr = p.parseObjectLookup(expr)
			}
			p.expectStmtEnd()
			return expr
		default:
			stmt := ast.ExpressionStmt{p.parseOperation(p.parenLevel, ident)}
			p.expectStmtEnd()
			return stmt
		}
	case itemUnaryOperator:
		expr := ast.ExpressionStmt{p.parseExpression()}
		p.expectStmtEnd()
		return expr
	case itemFunction:
		return p.parseFunctionStmt()
	case itemPHPEnd:
		if p.peek().typ == itemEOF {
			return nil
		}
		p.expect(itemHTML)
		expr := ast.Echo(&ast.Literal{Type: ast.String})
		p.next()
		if p.current.typ != itemEOF {
			p.expectCurrent(itemPHPBegin)
		}
		return expr
	case itemEcho:
		exprs := []ast.Expression{
			p.parseNextExpression(),
		}
		for p.peek().typ == itemComma {
			p.expect(itemComma)
			exprs = append(exprs, p.parseNextExpression())
		}
		p.expectStmtEnd()
		return ast.Echo(exprs...)
	case itemIf:
		return p.parseIf()
	case itemWhile:
		return p.parseWhile()
	case itemDo:
		return p.parseDo()
	case itemFor:
		return p.parseFor()
	case itemForeach:
		return p.parseForeach()
	case itemSwitch:
		return p.parseSwitch()
	case itemAbstract:
		fallthrough
	case itemClass:
		return p.parseClass()
	case itemInterface:
		return p.parseInterface()
	case itemReturn:
		p.next()
		stmt := ast.ReturnStmt{}
		if p.current.typ != itemStatementEnd {
			stmt.Expression = p.parseExpression()
			p.expectStmtEnd()
		}
		return stmt
	case itemBreak:
		p.next()
		stmt := ast.BreakStmt{}
		if p.current.typ != itemStatementEnd {
			stmt.Expression = p.parseExpression()
			p.expectStmtEnd()
		}
		return stmt
	case itemContinue:
		p.next()
		stmt := ast.ContinueStmt{}
		if p.current.typ != itemStatementEnd {
			stmt.Expression = p.parseExpression()
			p.expectStmtEnd()
		}
		return stmt
	case itemThrow:
		stmt := ast.ThrowStmt{Expression: p.parseNextExpression()}
		p.expectStmtEnd()
		return stmt
	case itemExit:
		stmt := ast.ExitStmt{}
		if p.peek().typ == itemOpenParen {
			p.expect(itemOpenParen)
			if p.peek().typ != itemCloseParen {
				stmt.Expression = p.parseNextExpression()
			}
			p.expect(itemCloseParen)
		}
		p.expectStmtEnd()
		return stmt
	case itemTry:
		stmt := &ast.TryStmt{}
		stmt.TryBlock = p.parseBlock()
		for p.expect(itemCatch); p.current.typ == itemCatch; p.next() {
			caught := &ast.CatchStmt{}
			p.expect(itemOpenParen)
			p.expect(itemIdentifier)
			caught.CatchType = p.current.val
			p.expect(itemVariableOperator)
			p.expect(itemIdentifier)
			caught.CatchVar = ast.NewVariable(p.current.val)
			p.expect(itemCloseParen)
			caught.CatchBlock = p.parseBlock()
			stmt.CatchStmts = append(stmt.CatchStmts, caught)
		}
		p.backup()
		return stmt
	case itemIgnoreErrorOperator:
		// Ignore this operator
		p.next()
		return p.parseStmt()
	default:
		expr := p.parseExpression()
		if expr != nil {
			p.expectStmtEnd()
			return ast.ExpressionStmt{expr}
		}
		p.errorf("Found %s, expected html or php begin", p.current)
		return nil
	}
}

func (p *parser) expectStmtEnd() {
	if p.peek().typ != itemPHPEnd {
		p.expect(itemStatementEnd)
	}
}
func (p *parser) parseFunctionStmt() *ast.FunctionStmt {
	stmt := &ast.FunctionStmt{}
	stmt.FunctionDefinition = p.parseFunctionDefinition()
	stmt.Body = p.parseBlock()
	return stmt
}

func (p *parser) parseFunctionDefinition() *ast.FunctionDefinition {
	def := &ast.FunctionDefinition{}
	p.expect(itemIdentifier)
	def.Name = p.current.val
	def.Arguments = make([]ast.FunctionArgument, 0)
	p.expect(itemOpenParen)
	if p.peek().typ == itemCloseParen {
		p.expect(itemCloseParen)
		return def
	}
	def.Arguments = append(def.Arguments, p.parseFunctionArgument())
	for {
		switch p.peek().typ {
		case itemComma:
			p.expect(itemComma)
			def.Arguments = append(def.Arguments, p.parseFunctionArgument())
		case itemCloseParen:
			p.expect(itemCloseParen)
			return def
		default:
			p.errorf("unexpected argument separator:", p.current)
			return def
		}
	}
}

func (p *parser) parseFunctionArgument() ast.FunctionArgument {
	arg := ast.FunctionArgument{}
	switch p.peek().typ {
	case itemIdentifier, itemArray:
		p.next()
		arg.TypeHint = p.current.val
	}
	if p.peek().typ == itemAmpersandOperator {
		p.next()
	}
	p.expect(itemVariableOperator)
	p.next()
	arg.Variable = ast.NewVariable(p.current.val)
	if p.peek().typ == itemAssignmentOperator {
		p.expect(itemAssignmentOperator)
		p.next()
		arg.Default = p.parseExpression()
	}
	return arg
}

func (p *parser) parseBlock() *ast.Block {
	p.expect(itemBlockBegin)
	b := p.parseStatementsUntil(itemBlockEnd)
	p.expectCurrent(itemBlockEnd)
	return b
}

func (p *parser) parseStatementsUntil(endTokens ...ItemType) *ast.Block {
	block := &ast.Block{}
	breakTypes := map[ItemType]bool{}
	for _, typ := range endTokens {
		breakTypes[typ] = true
	}
	for {
		p.next()
		if _, ok := breakTypes[p.current.typ]; ok {
			break
		}
		block.Statements = append(block.Statements, p.parseStmt())
	}
	return block
}
<commit_msg>Fixed an error message<commit_after>package php

import (
	"fmt"

	"stephensearles.com/php/ast"
)

type parser struct {
	lexer *lexer

	previous   []Item
	idx        int
	current    Item
	errors     []error
	parenLevel int
	errorMap   map[int]bool
	errorCount int

	Debug     bool
	MaxErrors int
}

func NewParser(input string) *parser {
	p := &parser{
		idx:       -1,
		MaxErrors: 10,
		lexer:     newLexer(input),
		errorMap:  make(map[int]bool),
	}
	return p
}

func (p *parser) Parse() (nodes []ast.Node, errors []error) {
	defer func() {
		if r := recover(); r != nil {
			if p.Debug {
				for _, err := range p.errors {
					fmt.Println(err)
				}
				panic(r)
			}
			errors = append([]error{fmt.Errorf("%s", r)}, p.errors...)
		}
	}()
	// expecting either itemHTML or itemPHPBegin
	nodes = make([]ast.Node, 0, 1)
TokenLoop:
	for {
		p.next()
		switch p.current.typ {
		case itemEOF:
			break TokenLoop
		default:
			n := p.parseNode()
			if n != nil {
				nodes = append(nodes, n)
			}
		}
	}
	errors = p.errors
	return nodes, errors
}

func (p *parser) parseNode() ast.Node {
	switch p.current.typ {
	case itemHTML:
		return ast.Echo(ast.Literal{Type: ast.String})
	case itemPHPBegin:
		return nil
	case itemPHPEnd:
		return nil
	}
	return p.parseStmt()
}

func (p *parser) next() {
	p.idx += 1
	if len(p.previous) <= p.idx {
		p.current = p.lexer.nextItem()
		p.previous = append(p.previous, p.current)
	} else {
		p.current = p.previous[p.idx]
	}
}

func (p *parser) backup() {
	p.idx -= 1
	p.current = p.previous[p.idx]
}

func (p *parser) peek() (i Item) {
	p.next()
	i = p.current
	p.backup()
	return
}

func (p *parser) expectCurrent(i ItemType) {
	if p.current.typ != i {
		p.expected(i)
	}
}

func (p *parser) expectAndNext(i ItemType) {
	if p.current.typ != i {
		p.expected(i)
	}
	p.next()
}

func (p *parser) expect(i ItemType) {
	p.next()
	p.expectCurrent(i)
}

func (p *parser) expected(i ItemType) {
	p.errorf("Found %s, expected %s", p.current, i)
}

func (p *parser) errorf(str string, args ...interface{}) {
	p.errorCount += 1
	if p.errorCount > p.MaxErrors {
		panic("too many errors")
	}
	if _, ok := p.errorMap[p.current.pos.Line]; ok {
		return
	}
	errString := fmt.Sprintf(str, args...)
	p.errorMap[p.current.pos.Line] = true
	p.errors = append(p.errors, fmt.Errorf("%s: %s", p.errorPrefix(), errString))
}

func (p *parser) errorPrefix() string {
	return fmt.Sprintf("%s %d", p.lexer.file, p.current.pos.Line)
}

func (p *parser) parseNextExpression() ast.Expression {
	p.next()
	return p.parseExpression()
}

func (p *parser) parseFunctionCall(callable ast.Expression) *ast.FunctionCallExpression {
	expr := &ast.FunctionCallExpression{}
	expr.FunctionName = callable
	return p.parseFunctionArguments(expr)
}

func (p *parser) parseFunctionArguments(expr *ast.FunctionCallExpression) *ast.FunctionCallExpression {
	expr.Arguments = make([]ast.Expression, 0)
	p.expect(itemOpenParen)
	if p.peek().typ == itemCloseParen {
		p.expect(itemCloseParen)
		return expr
	}
	expr.Arguments = append(expr.Arguments, p.parseNextExpression())
	for p.peek().typ != itemCloseParen {
		p.expect(itemComma)
		arg := p.parseNextExpression()
		if arg == nil {
			break
		}
		expr.Arguments = append(expr.Arguments, arg)
	}
	p.expect(itemCloseParen)
	return expr

}

func (p *parser) parseStmt() ast.Statement {
	switch p.current.typ {
	case itemBlockBegin:
		p.backup()
		return p.parseBlock()
	case itemGlobal:
		p.next()
		g := &ast.GlobalDeclaration{
			Identifiers: make([]*ast.Variable, 0, 1),
		}
		for p.current.typ == itemVariableOperator {
			variable, ok := p.parseVariable().(*ast.Variable)
			if !ok {
				p.errorf("global declarations must be of standard variables")
				break
			}
			g.Identifiers = append(g.Identifiers, variable)
			if p.peek().typ != itemComma {
				break
			}
			p.expect(itemComma)
			p.next()
		}
		p.expectStmtEnd()
		return g
	case itemNamespace:
		p.expect(itemIdentifier)
		p.expectStmtEnd()
		// We are ignoring this for now
		return nil
	case itemUse:
		p.expect(itemIdentifier)
		if p.peek().typ == itemAsOperator {
			p.expect(itemAsOperator)
			p.expect(itemIdentifier)
		}
		p.expectStmtEnd()
		// We are ignoring this for now
		return nil
	case itemVariableOperator:
		ident := p.expressionize()
		switch p.peek().typ {
		case itemUnaryOperator:
			expr := ast.ExpressionStmt{p.parseOperation(p.parenLevel, ident)}
			p.expectStmtEnd()
			return expr
		case itemOpenParen:
			var expr ast.Expression
			expr = p.parseFunctionArguments(&ast.FunctionCallExpression{
				FunctionName: ident,
			})
			if p.peek().typ == itemObjectOperator {
				expr = p.parseObjectLookup(expr)
			}
			p.expectStmtEnd()
			return expr
		default:
			stmt := ast.ExpressionStmt{p.parseOperation(p.parenLevel, ident)}
			p.expectStmtEnd()
			return stmt
		}
	case itemUnaryOperator:
		expr := ast.ExpressionStmt{p.parseExpression()}
		p.expectStmtEnd()
		return expr
	case itemFunction:
		return p.parseFunctionStmt()
	case itemPHPEnd:
		if p.peek().typ == itemEOF {
			return nil
		}
		p.expect(itemHTML)
		expr := ast.Echo(&ast.Literal{Type: ast.String})
		p.next()
		if p.current.typ != itemEOF {
			p.expectCurrent(itemPHPBegin)
		}
		return expr
	case itemEcho:
		exprs := []ast.Expression{
			p.parseNextExpression(),
		}
		for p.peek().typ == itemComma {
			p.expect(itemComma)
			exprs = append(exprs, p.parseNextExpression())
		}
		p.expectStmtEnd()
		return ast.Echo(exprs...)
	case itemIf:
		return p.parseIf()
	case itemWhile:
		return p.parseWhile()
	case itemDo:
		return p.parseDo()
	case itemFor:
		return p.parseFor()
	case itemForeach:
		return p.parseForeach()
	case itemSwitch:
		return p.parseSwitch()
	case itemAbstract:
		fallthrough
	case itemClass:
		return p.parseClass()
	case itemInterface:
		return p.parseInterface()
	case itemReturn:
		p.next()
		stmt := ast.ReturnStmt{}
		if p.current.typ != itemStatementEnd {
			stmt.Expression = p.parseExpression()
			p.expectStmtEnd()
		}
		return stmt
	case itemBreak:
		p.next()
		stmt := ast.BreakStmt{}
		if p.current.typ != itemStatementEnd {
			stmt.Expression = p.parseExpression()
			p.expectStmtEnd()
		}
		return stmt
	case itemContinue:
		p.next()
		stmt := ast.ContinueStmt{}
		if p.current.typ != itemStatementEnd {
			stmt.Expression = p.parseExpression()
			p.expectStmtEnd()
		}
		return stmt
	case itemThrow:
		stmt := ast.ThrowStmt{Expression: p.parseNextExpression()}
		p.expectStmtEnd()
		return stmt
	case itemExit:
		stmt := ast.ExitStmt{}
		if p.peek().typ == itemOpenParen {
			p.expect(itemOpenParen)
			if p.peek().typ != itemCloseParen {
				stmt.Expression = p.parseNextExpression()
			}
			p.expect(itemCloseParen)
		}
		p.expectStmtEnd()
		return stmt
	case itemTry:
		stmt := &ast.TryStmt{}
		stmt.TryBlock = p.parseBlock()
		for p.expect(itemCatch); p.current.typ == itemCatch; p.next() {
			caught := &ast.CatchStmt{}
			p.expect(itemOpenParen)
			p.expect(itemIdentifier)
			caught.CatchType = p.current.val
			p.expect(itemVariableOperator)
			p.expect(itemIdentifier)
			caught.CatchVar = ast.NewVariable(p.current.val)
			p.expect(itemCloseParen)
			caught.CatchBlock = p.parseBlock()
			stmt.CatchStmts = append(stmt.CatchStmts, caught)
		}
		p.backup()
		return stmt
	case itemIgnoreErrorOperator:
		// Ignore this operator
		p.next()
		return p.parseStmt()
	default:
		expr := p.parseExpression()
		if expr != nil {
			p.expectStmtEnd()
			return ast.ExpressionStmt{expr}
		}
		p.errorf("Found %s, statement or expression", p.current)
		return nil
	}
}

func (p *parser) expectStmtEnd() {
	if p.peek().typ != itemPHPEnd {
		p.expect(itemStatementEnd)
	}
}
func (p *parser) parseFunctionStmt() *ast.FunctionStmt {
	stmt := &ast.FunctionStmt{}
	stmt.FunctionDefinition = p.parseFunctionDefinition()
	stmt.Body = p.parseBlock()
	return stmt
}

func (p *parser) parseFunctionDefinition() *ast.FunctionDefinition {
	def := &ast.FunctionDefinition{}
	p.expect(itemIdentifier)
	def.Name = p.current.val
	def.Arguments = make([]ast.FunctionArgument, 0)
	p.expect(itemOpenParen)
	if p.peek().typ == itemCloseParen {
		p.expect(itemCloseParen)
		return def
	}
	def.Arguments = append(def.Arguments, p.parseFunctionArgument())
	for {
		switch p.peek().typ {
		case itemComma:
			p.expect(itemComma)
			def.Arguments = append(def.Arguments, p.parseFunctionArgument())
		case itemCloseParen:
			p.expect(itemCloseParen)
			return def
		default:
			p.errorf("unexpected argument separator:", p.current)
			return def
		}
	}
}

func (p *parser) parseFunctionArgument() ast.FunctionArgument {
	arg := ast.FunctionArgument{}
	switch p.peek().typ {
	case itemIdentifier, itemArray:
		p.next()
		arg.TypeHint = p.current.val
	}
	if p.peek().typ == itemAmpersandOperator {
		p.next()
	}
	p.expect(itemVariableOperator)
	p.next()
	arg.Variable = ast.NewVariable(p.current.val)
	if p.peek().typ == itemAssignmentOperator {
		p.expect(itemAssignmentOperator)
		p.next()
		arg.Default = p.parseExpression()
	}
	return arg
}

func (p *parser) parseBlock() *ast.Block {
	p.expect(itemBlockBegin)
	b := p.parseStatementsUntil(itemBlockEnd)
	p.expectCurrent(itemBlockEnd)
	return b
}

func (p *parser) parseStatementsUntil(endTokens ...ItemType) *ast.Block {
	block := &ast.Block{}
	breakTypes := map[ItemType]bool{}
	for _, typ := range endTokens {
		breakTypes[typ] = true
	}
	for {
		p.next()
		if _, ok := breakTypes[p.current.typ]; ok {
			break
		}
		block.Statements = append(block.Statements, p.parseStmt())
	}
	return block
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"io"
	"strconv"
)

// Parser represents a parser.
type Parser struct {
	s   *Scanner
	buf struct {
		t   Token  // last read token
		lit string // last read literal
		n   int    // buffer size (max=1)
	}
}

// NewParser returns a new instance of Parser.
func NewParser(r io.Reader) *Parser {
	return &Parser{s: NewScanner(r)}
}

// scan returns the next token from the underlying scanner.
// if a token has been unscanned then read that instead.
func (p *Parser) scan() (t Token, lit string) {

	if p.buf.n != 0 {
		p.buf.n = 0
		return p.buf.t, p.buf.lit
	}

	t, lit = p.s.Scan()

	p.buf.t, p.buf.lit = t, lit
	return
}

// unscan pushes the previously read token back onto the buffer.
func (p *Parser) unscan() { p.buf.n = 1 }

// scanIgnoreWhitespace scans the next non-whitespace token.
func (p *Parser) scanIgnoreWhitespace() (t Token, lit string) {
	t, lit = p.scan()
	if t == Space {
		t, lit = p.scan()
	}
	return
}

// Expression is the interface for an expression.
type Expression interface {
	String() string
	Evaluate() string
}

// Statement represents a code statement a = 2.
type Statement struct {
	Left  string
	Right string
}

// String retuns the statement as a string.
func (s Statement) String() string {
	return fmt.Sprintf("%v%v", s.Left, s.Right)
}

// Evaluate evaluates the given statement.
func (s Statement) Evaluate() string {
	return fmt.Sprintf("%v%v", s.Left, s.Right)
}

// Num represents a number statement.
type Num struct {
	n string
}

// String returns the string of the number
func (n Num) String() string {
	return fmt.Sprintf("%v", n.n)
}

// Evaluate returns the number value
func (n Num) Evaluate() string {
	return n.String()
}

// Value is an interface to handle different types.
type Value interface {
	String() string
	Evaluate() Value
}

// Int is a type to handle integers
type Int int64

// String returns the string representation of an integer.
func (i Int) String() string {
	return fmt.Sprintf("%d", i)
}

// Evaluate returns the value of the given integer.
func (i Int) Evaluate() Value {
	return i
}

func tryIntString(s string) (Value, error) {
	i, err := strconv.ParseInt(s, 10, 64)
	return Int(i), err
}

// Binary represents a binary statement
// example: 12 + 3
type Binary struct {
	Left     Value
	Right    Value
	Operator string
}

// String returns the string of the number
func (b Binary) String() string {
	return fmt.Sprintf("%v %v %v", b.Left, b.Operator, b.Right)
}

// Evaluate returns the number value
func (b Binary) Evaluate() string {
	if b.Operator == "+" {
		return add(b.Left, b.Right)
	} else if b.Operator == "-" {
		return add(b.Left, b.Right)
	}
	return ""
}

func add(a, b Value) string {
	return fmt.Sprintf("%d", a.(Int)+b.(Int))
}

func minus(a, b string) string {
	var ia, ib int64
	var err error
	ia, err = strconv.ParseInt(a, 10, 64)
	if err != nil {
		fmt.Println(err)
	}
	ib, err = strconv.ParseInt(b, 10, 64)
	return fmt.Sprintf("%d", ia-ib)
}

// ValueParse parse the string in the proper value
func ValueParse(s string) Value {
	v, err := tryIntString(s)
	if err != nil {
		return nil
	}
	return v
}

// Parse parse a assign statement a = b
func (p *Parser) Parse() (*Expression, error) {
	var left, right, operator string
	tok, lit := p.scanIgnoreWhitespace()
	var lastTok Token
	if tok == Identifier {
		left = lit
	} else if tok == Number {
		left = lit
		lastTok = Number
	} else {
		return nil, fmt.Errorf("found %q, expected left", lit)
	}

	tok, lit = p.scanIgnoreWhitespace()
	if tok == EOF && lastTok == Number {
		e := Expression(Num{left})
		return &e, nil
	}

	isAssign := tok == Assign
	if tok != Assign && tok != Operator {
		return nil, fmt.Errorf("found %q, expected '=' with tok: %v, expected %v", lit, tok, Assign)
	}
	// get token
	if tok == Operator {
		operator = lit
	}

	tok, lit = p.scanIgnoreWhitespace()
	if tok == Identifier {
		right = lit
	} else if tok == Number && !isAssign {
		right = lit
	} else {
		return nil, fmt.Errorf("found %q, expected identifier name", lit)
	}
	var expr Expression
	if isAssign {
		expr = Expression(Statement{Left: left, Right: right})
	} else {
		l := ValueParse(left)
		r := ValueParse(right)
		expr = Expression(Binary{Left: l, Right: r, Operator: operator})
	}
	return &expr, nil
}
<commit_msg>Better minus<commit_after>package main

import (
	"fmt"
	"io"
	"strconv"
)

// Parser represents a parser.
type Parser struct {
	s   *Scanner
	buf struct {
		t   Token  // last read token
		lit string // last read literal
		n   int    // buffer size (max=1)
	}
}

// NewParser returns a new instance of Parser.
func NewParser(r io.Reader) *Parser {
	return &Parser{s: NewScanner(r)}
}

// scan returns the next token from the underlying scanner.
// if a token has been unscanned then read that instead.
func (p *Parser) scan() (t Token, lit string) {

	if p.buf.n != 0 {
		p.buf.n = 0
		return p.buf.t, p.buf.lit
	}

	t, lit = p.s.Scan()

	p.buf.t, p.buf.lit = t, lit
	return
}

// unscan pushes the previously read token back onto the buffer.
func (p *Parser) unscan() { p.buf.n = 1 }

// scanIgnoreWhitespace scans the next non-whitespace token.
func (p *Parser) scanIgnoreWhitespace() (t Token, lit string) {
	t, lit = p.scan()
	if t == Space {
		t, lit = p.scan()
	}
	return
}

// Expression is the interface for an expression.
type Expression interface {
	String() string
	Evaluate() string
}

// Statement represents a code statement a = 2.
type Statement struct {
	Left  string
	Right string
}

// String retuns the statement as a string.
func (s Statement) String() string {
	return fmt.Sprintf("%v%v", s.Left, s.Right)
}

// Evaluate evaluates the given statement.
func (s Statement) Evaluate() string {
	return fmt.Sprintf("%v%v", s.Left, s.Right)
}

// Num represents a number statement.
type Num struct {
	n string
}

// String returns the string of the number
func (n Num) String() string {
	return fmt.Sprintf("%v", n.n)
}

// Evaluate returns the number value
func (n Num) Evaluate() string {
	return n.String()
}

// Value is an interface to handle different types.
type Value interface {
	String() string
	Evaluate() Value
}

// Int is a type to handle integers
type Int int64

// String returns the string representation of an integer.
func (i Int) String() string {
	return fmt.Sprintf("%d", i)
}

// Evaluate returns the value of the given integer.
func (i Int) Evaluate() Value {
	return i
}

func tryIntString(s string) (Value, error) {
	i, err := strconv.ParseInt(s, 10, 64)
	return Int(i), err
}

// Binary represents a binary statement
// example: 12 + 3
type Binary struct {
	Left     Value
	Right    Value
	Operator string
}

// String returns the string of the number
func (b Binary) String() string {
	return fmt.Sprintf("%v %v %v", b.Left, b.Operator, b.Right)
}

// Evaluate returns the number value
func (b Binary) Evaluate() string {
	if b.Operator == "+" {
		return add(b.Left, b.Right)
	} else if b.Operator == "-" {
		return minus(b.Left, b.Right)
	}
	return ""
}

func add(a, b Value) string {
	return fmt.Sprintf("%d", a.(Int)+b.(Int))
}

func minus(a, b Value) string {
	return fmt.Sprintf("%d", a.(Int)-b.(Int))
}

// ValueParse parse the string in the proper value
func ValueParse(s string) Value {
	v, err := tryIntString(s)
	if err != nil {
		return nil
	}
	return v
}

// Parse parse a assign statement a = b
func (p *Parser) Parse() (*Expression, error) {
	var left, right, operator string
	tok, lit := p.scanIgnoreWhitespace()
	var lastTok Token
	if tok == Identifier {
		left = lit
	} else if tok == Number {
		left = lit
		lastTok = Number
	} else {
		return nil, fmt.Errorf("found %q, expected left", lit)
	}

	tok, lit = p.scanIgnoreWhitespace()
	if tok == EOF && lastTok == Number {
		e := Expression(Num{left})
		return &e, nil
	}

	isAssign := tok == Assign
	if tok != Assign && tok != Operator {
		return nil, fmt.Errorf("found %q, expected '=' with tok: %v, expected %v", lit, tok, Assign)
	}
	// get token
	if tok == Operator {
		operator = lit
	}

	tok, lit = p.scanIgnoreWhitespace()
	if tok == Identifier {
		right = lit
	} else if tok == Number && !isAssign {
		right = lit
	} else {
		return nil, fmt.Errorf("found %q, expected identifier name", lit)
	}
	var expr Expression
	if isAssign {
		expr = Expression(Statement{Left: left, Right: right})
	} else {
		l := ValueParse(left)
		r := ValueParse(right)
		expr = Expression(Binary{Left: l, Right: r, Operator: operator})
	}
	return &expr, nil
}
<|endoftext|>


<commit_before>// Light-weight, event driven JSON parser.
package jo

const (
	_StateValue = iota
	_StateDone

	_StateObjectKeyOrBrace   // {
	_StateObjectColon        // {"foo"
	_StateObjectCommaOrBrace // {"foo":"bar"
	_StateObjectKey          // {"foo":"bar",

	_StateArrayValueOrBracket // [
	_StateArrayCommaOrBracket // ["any value"

	// leading whitespace must be be consumed before any of the above
	// states are processed
	_IgnoreSpace

	_StateStringUnicode  // "\u
	_StateStringUnicode2 // "\u1
	_StateStringUnicode3 // "\u12
	_StateStringUnicode4 // "\u123
	_StateString         // "
	_StateStringEscaped  // "\

	_StateKeyUnicode  // "\u
	_StateKeyUnicode2 // "\u1
	_StateKeyUnicode3 // "\u12
	_StateKeyUnicode4 // "\u123
	_StateKey         // "
	_StateKeyEscaped  // "\

	_StateNumberNegative      // -
	_StateNumberZero          // 0
	_StateNumber              // 123
	_StateNumberDotFirstDigit // 123.
	_StateNumberDotDigit      // 123.4
	_StateNumberExpSign       // 123e
	_StateNumberExpFirstDigit // 123e+
	_StateNumberExpDigit      // 123e+1

	_StateTrue  // t
	_StateTrue2 // tr
	_StateTrue3 // tru

	_StateFalse  // f
	_StateFalse2 // fa
	_StateFalse3 // fal
	_StateFalse4 // fals

	_StateNull  // n
	_StateNull2 // nu
	_StateNull3 // nul
)

// Parser state machine. Requires no initialization before use.
type Parser struct {
	state int
	queue []int
	err   error

	depth      int
	escape     bool
	escapeNext int
	escapeLast int
}

// Parses a byte slice containing JSON data. Returns the number of bytes
// read and an appropriate Event.
func (p *Parser) Parse(input []byte) (int, Event) {
	for i := 0; i < len(input); i++ {
		var event Event = Continue
		var b byte = input[i]

		if p.state < _IgnoreSpace && isSpace(b) {
			continue
		}

		switch p.state {
		case _StateValue:
			if b == '{' {
				event = ObjectStart
				p.state = _StateObjectKeyOrBrace
			} else if b == '[' {
				event = ArrayStart
				p.state = _StateArrayValueOrBracket
			} else if b == '"' {
				event = StringStart
				p.state = _StateString
			} else if b == '-' {
				event = NumberStart
				p.state = _StateNumberNegative
			} else if b == '0' {
				event = NumberStart
				p.state = _StateNumberZero
			} else if '1' <= b && b <= '9' {
				event = NumberStart
				p.state = _StateNumber
			} else if b == 't' {
				event = BoolStart
				p.state = _StateTrue
			} else if b == 'f' {
				event = BoolStart
				p.state = _StateFalse
			} else if b == 'n' {
				event = NullStart
				p.state = _StateNull
			} else {
				event = p.error(`expecting beginning of JSON value`)
			}

		case _StateObjectKeyOrBrace:
			if b == '}' {
				event = ObjectEnd
				p.state = p.next()
				break
			}

			// if it's not a brace, it must be a key
			p.state = _StateObjectKey
			fallthrough

		case _StateObjectKey:
			if b == '"' {
				event = KeyStart
				p.state = _StateKey
			} else {
				event = p.error(`expected object key`)
			}

		case _StateObjectColon:
			if b == ':' {
				p.state = _StateValue
				p.push(_StateObjectCommaOrBrace)
			} else {
				event = p.error(`expected ':' after object key`)
			}

		case _StateObjectCommaOrBrace:
			if b == ',' {
				p.state = _StateObjectKey
			} else if b == '}' {
				event = ObjectEnd
				p.state = p.next()
			} else {
				event = p.error(`expected ',' or '}' after object value`)
			}

		case _StateArrayValueOrBracket:
			if b == ']' {
				event = ArrayEnd
				p.state = p.next()
			} else {
				p.state = _StateValue
				p.push(_StateArrayCommaOrBracket)

				// rewind and let _StateValue parse this byte for us
				i--
			}

		case _StateArrayCommaOrBracket:
			if b == ',' {
				p.state = _StateValue
				p.push(_StateArrayCommaOrBracket)
			} else if b == ']' {
				event = ArrayEnd
				p.state = p.next()
			} else {
				event = p.error(`expected ',' or ']' after array value`)
			}

		case _StateStringUnicode, _StateKeyUnicode,
			_StateStringUnicode2, _StateKeyUnicode2,
			_StateStringUnicode3, _StateKeyUnicode3,
			_StateStringUnicode4, _StateKeyUnicode4:
			if isHex(b) {
				// move on to the next unicode byte state, or back to
				// `_State{String/Key}` if this was the fourth hexadecimal
				// character after "\u"
				p.state++
			} else {
				event = p.error(`expected four hexadecimal chars after "\u"`)
			}

		case _StateString, _StateKey:
			if b == '"' {
				if p.state == _StateKey {
					event = KeyEnd
					p.state = _StateObjectColon
				} else {
					event = StringEnd
					p.state = p.next()
				}
			} else if b == '\\' {
				// continue with `_State{String,Key}Escaped`
				p.state++
			} else if b < 0x20 {
				event = p.error(`expected valid string character`)
			}

		case _StateStringEscaped, _StateKeyEscaped:
			switch b {
			case 'b', 'f', 'n', 'r', 't', '\\', '/', '"':
				// jump back to _State{String,Key}
				p.state--
			case 'u':
				// continue to `_State{String,Key}Unicode`
				p.state -= 5
			default:
				event = p.error(`expected valid escape sequence after '\'`)
			}

		case _StateNumberNegative:
			if b == '0' {
				p.state = _StateNumberZero
			} else if '1' <= b && b <= '9' {
				p.state = _StateNumber
			} else {
				event = p.error(`digit after '-'`)
			}

		case _StateNumber:
			if isDecimal(b) {
				break
			}

			// the same limits apply here as in _StateNumberZero
			fallthrough

		case _StateNumberZero:
			if b == '.' {
				p.state = _StateNumberDotFirstDigit
			} else if b == 'e' || b == 'E' {
				p.state = _StateNumberExpSign
			} else {
				event = NumberEnd
				p.state = p.next()

				// rewind a byte, because the character we encountered was
				// not part of the number
				i--
			}

		case _StateNumberDotFirstDigit:
			if isDecimal(b) {
				p.state = _StateNumberDotDigit
			} else {
				event = p.error(`expected digit after dot in number`)
			}

		case _StateNumberDotDigit:
			if b == 'e' || b == 'E' {
				p.state = _StateNumberExpSign
			} else if !isDecimal(b) {
				event = NumberEnd
				p.state = p.next()

				// rewind a byte, because the character we encountered was
				// not part of the number
				i--
			}

		case _StateNumberExpSign:
			p.state = _StateNumberExpFirstDigit
			if b == '+' || b == '-' {
				break
			}
			fallthrough

		case _StateNumberExpFirstDigit:
			if !isDecimal(b) {
				event = p.error(`expected digit after exponent in number`)
			} else {
				p.state++
			}

		case _StateNumberExpDigit:
			if !isDecimal(b) {
				event = NumberEnd
				p.state = p.next()

				// rewind a byte, because the character we encountered was
				// not part of the number
				i--
			}

		case _StateTrue:
			if b == 'r' {
				p.state = _StateTrue2
			} else {
				event = p.error(`expected 'r' in literal true`)
			}

		case _StateTrue2:
			if b == 'u' {
				p.state = _StateTrue3
			} else {
				return i, p.error(`expected 'u' in literal true`)
			}

		case _StateTrue3:
			if b == 'e' {
				event = BoolEnd
				p.state = p.next()
			} else {
				return i, p.error(`expected 'e' in literal true`)
			}

		case _StateFalse:
			if b == 'a' {
				p.state = _StateFalse2
			} else {
				event = p.error(`expected 'a' in literal false`)
			}

		case _StateFalse2:
			if b == 'l' {
				p.state = _StateFalse3
			} else {
				event = p.error(`expected 'l' in literal false`)
			}

		case _StateFalse3:
			if b == 's' {
				p.state = _StateFalse4
			} else {
				event = p.error(`expected 's' in literal false`)
			}

		case _StateFalse4:
			if b == 'e' {
				event = BoolEnd
				p.state = p.next()
			} else {
				event = p.error(`expected 'e' in literal false`)
			}

		case _StateNull:
			if b == 'u' {
				p.state = _StateNull2
			} else {
				event = p.error(`expected 'u' in literal false`)
			}

		case _StateNull2:
			if b == 'l' {
				p.state = _StateNull3
			} else {
				event = p.error(`expected 'l' in literal false`)
			}

		case _StateNull3:
			if b == 'l' {
				event = NullEnd
				p.state = p.next()
			} else {
				event = p.error(`expected 'l' in literal false`)
			}

		case _StateDone:
			return i, p.error(`expected nothing after top-level value`)

		default:
			panic(`invalid state`)
		}

		switch event {
		case ObjectStart, ArrayStart:
			p.depth++
		case ObjectEnd, ArrayEnd:
			p.depth--
		case SyntaxError:
			// don't consume the byte that caused the error
			return i, SyntaxError
		}

		// if this byte didn't yield an event, try the next
		if event == Continue {
			continue
		}

		if p.escape && p.depth >= p.escapeNext {
			if p.depth == p.escapeNext {
				p.escape = (p.escapeNext > p.escapeLast)
				p.escapeNext--
			} else {
				continue
			}
		}

		return i + 1, event
	}

	return len(input), Continue
}

// Informs the parser not to expect any further input (most likely caused by
// EOF).
//
// Returns a SyntaxError event if invoked before the top-level value has been
// completely parsed. Otherwise returns dangling NumberEnd events, or Done.
func (p *Parser) End() Event {
	switch p.state {
	case _StateNumberZero,
		_StateNumber,
		_StateNumberDotDigit,
		_StateNumberExpDigit:
		p.state = _StateDone
		return NumberEnd
	case _StateDone:
		return Done
	}

	return p.error(`unexpected end of input`)
}

// Our own little implementation of the `error` interface.
type err string

func (e err) Error() string {
	return string(e)
}

// Returns the last syntax error detected by the parser, if any.
func (p *Parser) LastError() error {
	return p.err
}

// Returns the current depth of nested objects and arrays. Will be 0 for
// top-level literal values.
func (p *Parser) Depth() int {
	return p.depth
}

// When invoked, Parse() will only return Continue, SyntaxError, ObjectEnd or
// ArrayEnd events until the object or array `depth` levels up has been fully
// parsed.
//
//   input := []byte(`[{"foo":"bar"}]`
//
//   p := Parser{}
//   p.Parse(input[0:])  // -> (1, ArrayStart)
//   p.Parse(input[1:])  // -> (1, ObjectStart)
//
//   p.Escape(1)         // we don't care about what's in this object
//
//   p.Parse(input[2:])  // -> (12, ObjectEnd)
//
// In the above example, we avoided the KeyStart, KeyEnd, StringStart and
// StringEnd events that normally would have followed the ObjectStart event.
//
// Note that Escape can be invoked at any point - not just after ObjectStart or
// ArrayStart events.
func (p *Parser) Escape(depth int) {
	p.escape = true
	p.escapeNext = p.depth - 1
	p.escapeLast = p.depth - depth
}

// Convenience function for saving a syntax error.
func (p *Parser) error(s string) Event {
	p.err = err(s)
	return SyntaxError
}

// Puts a new state at the top of the queue.
func (p *Parser) push(state int) {
	p.queue = append(p.queue, state)
}

// Fetches the next state in the queue.
func (p *Parser) next() int {
	length := len(p.queue)

	// if the state queue is empty, the top level value has ended
	if length == 0 {
		return _StateDone
	}

	state := p.queue[length-1]
	p.queue = p.queue[:length-1]

	return state
}

// Returns true if b is a whitespace character.
func isSpace(b byte) bool {
	return b == ' ' || b == '\t' || b == '\n' || b == '\r'
}

// Returns true if b is a hexadecimal character.
func isHex(b byte) bool {
	return isDecimal(b) || ('a' <= b && b <= 'f') || ('A' <= b && b <= 'F')
}

// Returns true if b is a decimal digit.
func isDecimal(b byte) bool {
	return '0' <= b && b <= '9'
}
<commit_msg>Reword syntax error<commit_after>// Light-weight, event driven JSON parser.
package jo

const (
	_StateValue = iota
	_StateDone

	_StateObjectKeyOrBrace   // {
	_StateObjectColon        // {"foo"
	_StateObjectCommaOrBrace // {"foo":"bar"
	_StateObjectKey          // {"foo":"bar",

	_StateArrayValueOrBracket // [
	_StateArrayCommaOrBracket // ["any value"

	// leading whitespace must be be consumed before any of the above
	// states are processed
	_IgnoreSpace

	_StateStringUnicode  // "\u
	_StateStringUnicode2 // "\u1
	_StateStringUnicode3 // "\u12
	_StateStringUnicode4 // "\u123
	_StateString         // "
	_StateStringEscaped  // "\

	_StateKeyUnicode  // "\u
	_StateKeyUnicode2 // "\u1
	_StateKeyUnicode3 // "\u12
	_StateKeyUnicode4 // "\u123
	_StateKey         // "
	_StateKeyEscaped  // "\

	_StateNumberNegative      // -
	_StateNumberZero          // 0
	_StateNumber              // 123
	_StateNumberDotFirstDigit // 123.
	_StateNumberDotDigit      // 123.4
	_StateNumberExpSign       // 123e
	_StateNumberExpFirstDigit // 123e+
	_StateNumberExpDigit      // 123e+1

	_StateTrue  // t
	_StateTrue2 // tr
	_StateTrue3 // tru

	_StateFalse  // f
	_StateFalse2 // fa
	_StateFalse3 // fal
	_StateFalse4 // fals

	_StateNull  // n
	_StateNull2 // nu
	_StateNull3 // nul
)

// Parser state machine. Requires no initialization before use.
type Parser struct {
	state int
	queue []int
	err   error

	depth      int
	escape     bool
	escapeNext int
	escapeLast int
}

// Parses a byte slice containing JSON data. Returns the number of bytes
// read and an appropriate Event.
func (p *Parser) Parse(input []byte) (int, Event) {
	for i := 0; i < len(input); i++ {
		var event Event = Continue
		var b byte = input[i]

		if p.state < _IgnoreSpace && isSpace(b) {
			continue
		}

		switch p.state {
		case _StateValue:
			if b == '{' {
				event = ObjectStart
				p.state = _StateObjectKeyOrBrace
			} else if b == '[' {
				event = ArrayStart
				p.state = _StateArrayValueOrBracket
			} else if b == '"' {
				event = StringStart
				p.state = _StateString
			} else if b == '-' {
				event = NumberStart
				p.state = _StateNumberNegative
			} else if b == '0' {
				event = NumberStart
				p.state = _StateNumberZero
			} else if '1' <= b && b <= '9' {
				event = NumberStart
				p.state = _StateNumber
			} else if b == 't' {
				event = BoolStart
				p.state = _StateTrue
			} else if b == 'f' {
				event = BoolStart
				p.state = _StateFalse
			} else if b == 'n' {
				event = NullStart
				p.state = _StateNull
			} else {
				event = p.error(`expected beginning of JSON value`)
			}

		case _StateObjectKeyOrBrace:
			if b == '}' {
				event = ObjectEnd
				p.state = p.next()
				break
			}

			// if it's not a brace, it must be a key
			p.state = _StateObjectKey
			fallthrough

		case _StateObjectKey:
			if b == '"' {
				event = KeyStart
				p.state = _StateKey
			} else {
				event = p.error(`expected object key`)
			}

		case _StateObjectColon:
			if b == ':' {
				p.state = _StateValue
				p.push(_StateObjectCommaOrBrace)
			} else {
				event = p.error(`expected ':' after object key`)
			}

		case _StateObjectCommaOrBrace:
			if b == ',' {
				p.state = _StateObjectKey
			} else if b == '}' {
				event = ObjectEnd
				p.state = p.next()
			} else {
				event = p.error(`expected ',' or '}' after object value`)
			}

		case _StateArrayValueOrBracket:
			if b == ']' {
				event = ArrayEnd
				p.state = p.next()
			} else {
				p.state = _StateValue
				p.push(_StateArrayCommaOrBracket)

				// rewind and let _StateValue parse this byte for us
				i--
			}

		case _StateArrayCommaOrBracket:
			if b == ',' {
				p.state = _StateValue
				p.push(_StateArrayCommaOrBracket)
			} else if b == ']' {
				event = ArrayEnd
				p.state = p.next()
			} else {
				event = p.error(`expected ',' or ']' after array value`)
			}

		case _StateStringUnicode, _StateKeyUnicode,
			_StateStringUnicode2, _StateKeyUnicode2,
			_StateStringUnicode3, _StateKeyUnicode3,
			_StateStringUnicode4, _StateKeyUnicode4:
			if isHex(b) {
				// move on to the next unicode byte state, or back to
				// `_State{String/Key}` if this was the fourth hexadecimal
				// character after "\u"
				p.state++
			} else {
				event = p.error(`expected four hexadecimal chars after "\u"`)
			}

		case _StateString, _StateKey:
			if b == '"' {
				if p.state == _StateKey {
					event = KeyEnd
					p.state = _StateObjectColon
				} else {
					event = StringEnd
					p.state = p.next()
				}
			} else if b == '\\' {
				// continue with `_State{String,Key}Escaped`
				p.state++
			} else if b < 0x20 {
				event = p.error(`expected valid string character`)
			}

		case _StateStringEscaped, _StateKeyEscaped:
			switch b {
			case 'b', 'f', 'n', 'r', 't', '\\', '/', '"':
				// jump back to _State{String,Key}
				p.state--
			case 'u':
				// continue to `_State{String,Key}Unicode`
				p.state -= 5
			default:
				event = p.error(`expected valid escape sequence after '\'`)
			}

		case _StateNumberNegative:
			if b == '0' {
				p.state = _StateNumberZero
			} else if '1' <= b && b <= '9' {
				p.state = _StateNumber
			} else {
				event = p.error(`digit after '-'`)
			}

		case _StateNumber:
			if isDecimal(b) {
				break
			}

			// the same limits apply here as in _StateNumberZero
			fallthrough

		case _StateNumberZero:
			if b == '.' {
				p.state = _StateNumberDotFirstDigit
			} else if b == 'e' || b == 'E' {
				p.state = _StateNumberExpSign
			} else {
				event = NumberEnd
				p.state = p.next()

				// rewind a byte, because the character we encountered was
				// not part of the number
				i--
			}

		case _StateNumberDotFirstDigit:
			if isDecimal(b) {
				p.state = _StateNumberDotDigit
			} else {
				event = p.error(`expected digit after dot in number`)
			}

		case _StateNumberDotDigit:
			if b == 'e' || b == 'E' {
				p.state = _StateNumberExpSign
			} else if !isDecimal(b) {
				event = NumberEnd
				p.state = p.next()

				// rewind a byte, because the character we encountered was
				// not part of the number
				i--
			}

		case _StateNumberExpSign:
			p.state = _StateNumberExpFirstDigit
			if b == '+' || b == '-' {
				break
			}
			fallthrough

		case _StateNumberExpFirstDigit:
			if !isDecimal(b) {
				event = p.error(`expected digit after exponent in number`)
			} else {
				p.state++
			}

		case _StateNumberExpDigit:
			if !isDecimal(b) {
				event = NumberEnd
				p.state = p.next()

				// rewind a byte, because the character we encountered was
				// not part of the number
				i--
			}

		case _StateTrue:
			if b == 'r' {
				p.state = _StateTrue2
			} else {
				event = p.error(`expected 'r' in literal true`)
			}

		case _StateTrue2:
			if b == 'u' {
				p.state = _StateTrue3
			} else {
				return i, p.error(`expected 'u' in literal true`)
			}

		case _StateTrue3:
			if b == 'e' {
				event = BoolEnd
				p.state = p.next()
			} else {
				return i, p.error(`expected 'e' in literal true`)
			}

		case _StateFalse:
			if b == 'a' {
				p.state = _StateFalse2
			} else {
				event = p.error(`expected 'a' in literal false`)
			}

		case _StateFalse2:
			if b == 'l' {
				p.state = _StateFalse3
			} else {
				event = p.error(`expected 'l' in literal false`)
			}

		case _StateFalse3:
			if b == 's' {
				p.state = _StateFalse4
			} else {
				event = p.error(`expected 's' in literal false`)
			}

		case _StateFalse4:
			if b == 'e' {
				event = BoolEnd
				p.state = p.next()
			} else {
				event = p.error(`expected 'e' in literal false`)
			}

		case _StateNull:
			if b == 'u' {
				p.state = _StateNull2
			} else {
				event = p.error(`expected 'u' in literal false`)
			}

		case _StateNull2:
			if b == 'l' {
				p.state = _StateNull3
			} else {
				event = p.error(`expected 'l' in literal false`)
			}

		case _StateNull3:
			if b == 'l' {
				event = NullEnd
				p.state = p.next()
			} else {
				event = p.error(`expected 'l' in literal false`)
			}

		case _StateDone:
			return i, p.error(`expected nothing after top-level value`)

		default:
			panic(`invalid state`)
		}

		switch event {
		case ObjectStart, ArrayStart:
			p.depth++
		case ObjectEnd, ArrayEnd:
			p.depth--
		case SyntaxError:
			// don't consume the byte that caused the error
			return i, SyntaxError
		}

		// if this byte didn't yield an event, try the next
		if event == Continue {
			continue
		}

		if p.escape && p.depth >= p.escapeNext {
			if p.depth == p.escapeNext {
				p.escape = (p.escapeNext > p.escapeLast)
				p.escapeNext--
			} else {
				continue
			}
		}

		return i + 1, event
	}

	return len(input), Continue
}

// Informs the parser not to expect any further input (most likely caused by
// EOF).
//
// Returns a SyntaxError event if invoked before the top-level value has been
// completely parsed. Otherwise returns dangling NumberEnd events, or Done.
func (p *Parser) End() Event {
	switch p.state {
	case _StateNumberZero,
		_StateNumber,
		_StateNumberDotDigit,
		_StateNumberExpDigit:
		p.state = _StateDone
		return NumberEnd
	case _StateDone:
		return Done
	}

	return p.error(`unexpected end of input`)
}

// Our own little implementation of the `error` interface.
type err string

func (e err) Error() string {
	return string(e)
}

// Returns the last syntax error detected by the parser, if any.
func (p *Parser) LastError() error {
	return p.err
}

// Returns the current depth of nested objects and arrays. Will be 0 for
// top-level literal values.
func (p *Parser) Depth() int {
	return p.depth
}

// When invoked, Parse() will only return Continue, SyntaxError, ObjectEnd or
// ArrayEnd events until the object or array `depth` levels up has been fully
// parsed.
//
//   input := []byte(`[{"foo":"bar"}]`
//
//   p := Parser{}
//   p.Parse(input[0:])  // -> (1, ArrayStart)
//   p.Parse(input[1:])  // -> (1, ObjectStart)
//
//   p.Escape(1)         // we don't care about what's in this object
//
//   p.Parse(input[2:])  // -> (12, ObjectEnd)
//
// In the above example, we avoided the KeyStart, KeyEnd, StringStart and
// StringEnd events that normally would have followed the ObjectStart event.
//
// Note that Escape can be invoked at any point - not just after ObjectStart or
// ArrayStart events.
func (p *Parser) Escape(depth int) {
	p.escape = true
	p.escapeNext = p.depth - 1
	p.escapeLast = p.depth - depth
}

// Convenience function for saving a syntax error.
func (p *Parser) error(s string) Event {
	p.err = err(s)
	return SyntaxError
}

// Puts a new state at the top of the queue.
func (p *Parser) push(state int) {
	p.queue = append(p.queue, state)
}

// Fetches the next state in the queue.
func (p *Parser) next() int {
	length := len(p.queue)

	// if the state queue is empty, the top level value has ended
	if length == 0 {
		return _StateDone
	}

	state := p.queue[length-1]
	p.queue = p.queue[:length-1]

	return state
}

// Returns true if b is a whitespace character.
func isSpace(b byte) bool {
	return b == ' ' || b == '\t' || b == '\n' || b == '\r'
}

// Returns true if b is a hexadecimal character.
func isHex(b byte) bool {
	return isDecimal(b) || ('a' <= b && b <= 'f') || ('A' <= b && b <= 'F')
}

// Returns true if b is a decimal digit.
func isDecimal(b byte) bool {
	return '0' <= b && b <= '9'
}
<|endoftext|>


<commit_before>package sjson

import (
	"fmt"
	"strconv"
	"strings"
	"unicode"
	"unicode/utf16"
	"unicode/utf8"
)

// PreallocateObjectElems parameter allow to tune
// preallocated memory objects during the parsing process.
var PreallocateObjectElems = 2

// Decode function parse JSON Text into interface value. Rules are the same as in
// encoding/json module:
//	bool, for JSON booleans
//	float64, for JSON numbers
//	string, for JSON strings
//	[]interface{}, for JSON arrays
//	map[string]interface{}, for JSON objects
//	nil, for JSON null
//
func Decode(json string) (interface{}, error) {
	state := decodeState{cur: json}
	ret := state.decodeValue()
	return ret, state.err
}

type decodeState struct {
	cur string // current bytes
	err error
}

func (s *decodeState) skipSpaces() {
	for len(s.cur) > 0 {
		if s.cur[0] > '\x20' {
			return
		} else if s.cur[0] == '\x20' || s.cur[0] == '\x0A' || s.cur[0] == '\x0D' || s.cur[0] == '\x09' {
			s.cur = s.cur[1:]
		} else {
			return
		}
	}
}

const arr0Size int = 8

func (s *decodeState) decodeSlice() []interface{} {
	var arr0 [arr0Size]interface{}
	var i int

	s.skipSpaces()
	for len(s.cur) > 0 {
		if s.cur[0] == ']' {
			s.cur = s.cur[1:]
			return arr0[:i]
		}

		val := s.decodeValue()
		if s.err != nil {
			return arr0[:i]
		}
		arr0[i] = val
		i++

		s.skipSpaces()
		if len(s.cur) > 0 && s.cur[0] == ',' {
			s.cur = s.cur[1:]
			s.skipSpaces()
			if i >= arr0Size {
				break
			}
		}
	}

	arr := arr0[:]

	for len(s.cur) > 0 {
		if s.cur[0] == ']' {
			s.cur = s.cur[1:]
			return arr
		}

		val := s.decodeValue()
		if s.err != nil {
			return arr
		}
		arr = append(arr, val)

		s.skipSpaces()
		if len(s.cur) > 0 && s.cur[0] == ',' {
			s.cur = s.cur[1:]
			s.skipSpaces()
		}
	}

	s.err = fmt.Errorf("incorrect syntax")
	return arr
}

func (s *decodeState) decodeObject() map[string]interface{} {
	obj := make(map[string]interface{}, PreallocateObjectElems)

	for {
		s.skipSpaces()

		if len(s.cur) == 0 {
			s.err = fmt.Errorf("incorrect syntax")
			return obj
		}

		switch s.cur[0] {
		case '}':
			s.cur = s.cur[1:]
			return obj
		case '"':
			s.cur = s.cur[1:]
			key := s.decodeString()
			if s.err != nil {
				return obj
			}
			s.skipSpaces()
			if len(s.cur) > 0 && s.cur[0] == ':' {
				s.cur = s.cur[1:]
			} else {
				s.err = fmt.Errorf("incorrect syntax (expect ':' after key)")
				return obj
			}
			obj[key] = s.decodeValue()
			s.skipSpaces()
			if len(s.cur) > 0 && s.cur[0] == ',' {
				s.cur = s.cur[1:]
			}
		default:
			s.err = fmt.Errorf("incorrect syntax (expect object key)")
			return obj
		}
	}

	return obj
}

func (s *decodeState) decodeString() string {
	quotePos := strings.IndexByte(s.cur, '"')
	if quotePos < 0 {
		s.err = fmt.Errorf("incorrect syntax (expected close quote)")
		return ""
	}
	// fast path
	val := s.cur[:quotePos]
	if strings.IndexByte(val, '\\') < 0 {
		s.cur = s.cur[quotePos+1:]
		return val
	}

	// TODO(vovkasm): rewrite from zero
	// full decoding
	// - find end of string
	for s.cur[quotePos-1] == '\\' {
		n := strings.IndexByte(s.cur[quotePos+1:], '"')
		quotePos += n + 1
		if quotePos < 0 {
			s.err = fmt.Errorf("incorrect syntax (expected close quote)")
			return ""
		}
	}
	// (from standard json package)
	ret, ok := unquote(s.cur[:quotePos])
	s.cur = s.cur[quotePos+1:]
	if !ok {
		s.err = fmt.Errorf("syntax error (string contains invalid characters)")
	}

	return ret
}

// unquote converts a quoted JSON string literal s into an actual string t.
// The rules are different than for Go, so cannot use strconv.Unquote.
func unquote(s string) (t string, ok bool) {
	b, ok := unquoteBytes([]byte(s))
	t = string(b)
	return
}

func unquoteBytes(s []byte) (t []byte, ok bool) {
	// Check for unusual characters. If there are none,
	// then no unquoting is needed, so return a slice of the
	// original bytes.
	r := 0
	for r < len(s) {
		c := s[r]
		if c == '\\' || c == '"' || c < ' ' {
			break
		}
		if c < utf8.RuneSelf {
			r++
			continue
		}
		rr, size := utf8.DecodeRune(s[r:])
		if rr == utf8.RuneError && size == 1 {
			break
		}
		r += size
	}
	if r == len(s) {
		return s, true
	}

	b := make([]byte, len(s)+2*utf8.UTFMax)
	w := copy(b, s[0:r])
	for r < len(s) {
		// Out of room?  Can only happen if s is full of
		// malformed UTF-8 and we're replacing each
		// byte with RuneError.
		if w >= len(b)-2*utf8.UTFMax {
			nb := make([]byte, (len(b)+utf8.UTFMax)*2)
			copy(nb, b[0:w])
			b = nb
		}
		switch c := s[r]; {
		case c == '\\':
			r++
			if r >= len(s) {
				return
			}
			switch s[r] {
			default:
				return
			case '"', '\\', '/', '\'':
				b[w] = s[r]
				r++
				w++
			case 'b':
				b[w] = '\b'
				r++
				w++
			case 'f':
				b[w] = '\f'
				r++
				w++
			case 'n':
				b[w] = '\n'
				r++
				w++
			case 'r':
				b[w] = '\r'
				r++
				w++
			case 't':
				b[w] = '\t'
				r++
				w++
			case 'u':
				r--
				rr := getu4(s[r:])
				if rr < 0 {
					return
				}
				r += 6
				if utf16.IsSurrogate(rr) {
					rr1 := getu4(s[r:])
					if dec := utf16.DecodeRune(rr, rr1); dec != unicode.ReplacementChar {
						// A valid pair; consume.
						r += 6
						w += utf8.EncodeRune(b[w:], dec)
						break
					}
					// Invalid surrogate; fall back to replacement rune.
					rr = unicode.ReplacementChar
				}
				w += utf8.EncodeRune(b[w:], rr)
			}

		// Quote, control characters are invalid.
		case c == '"', c < ' ':
			return

		// ASCII
		case c < utf8.RuneSelf:
			b[w] = c
			r++
			w++

		// Coerce to well-formed UTF-8.
		default:
			rr, size := utf8.DecodeRune(s[r:])
			r += size
			w += utf8.EncodeRune(b[w:], rr)
		}
	}
	return b[0:w], true
}

// getu4 decodes \uXXXX from the beginning of s, returning the hex value,
// or it returns -1.
func getu4(s []byte) rune {
	if len(s) < 6 || s[0] != '\\' || s[1] != 'u' {
		return -1
	}
	r, err := strconv.ParseUint(string(s[2:6]), 16, 64)
	if err != nil {
		return -1
	}
	return rune(r)
}

func (s *decodeState) decodeNumber() float64 {
	var pos int = 0

	// sign
	if s.cur[pos] == '-' {
		pos++
	}

	// significand
	// - integer
	if len(s.cur) > pos && s.cur[pos] >= '1' && s.cur[pos] <= '9' {
		pos++
		for len(s.cur) > pos && len(s.cur) >= 1 && s.cur[pos] >= '0' && s.cur[pos] <= '9' {
			pos++
		}
	} else if len(s.cur) > pos && s.cur[pos] == '0' {
		pos++
	} else {
		s.err = fmt.Errorf("incorrect number (expected digit)")
		return 0.0
	}

	// - fractional
	if len(s.cur) > pos && s.cur[pos] == '.' {
		pos++
		if len(s.cur) > pos && s.cur[pos] >= '0' && s.cur[pos] <= '9' {
			pos++
			for len(s.cur) > pos && s.cur[pos] >= '0' && s.cur[pos] <= '9' {
				pos++
			}
		} else {
			s.err = fmt.Errorf("incorrect number (expected fractional)")
			return 0.0
		}
	}

	// exponential
	if len(s.cur) > pos && (s.cur[pos] == 'e' || s.cur[pos] == 'E') {
		pos++
		if len(s.cur) > pos && (s.cur[pos] == '+' || s.cur[pos] == '-') {
			pos++
		}

		if len(s.cur) > pos && s.cur[pos] >= '0' && s.cur[pos] <= '9' {
			pos++
			for len(s.cur) > pos && s.cur[pos] >= '0' && s.cur[pos] <= '9' {
				pos++
			}
		} else {
			s.err = fmt.Errorf("incorrect number (expected digit)")
			return 0.0
		}
	}

	val, err := strconv.ParseFloat(string(s.cur[:pos]), 64)
	if err != nil {
		s.err = err
	}
	s.cur = s.cur[pos:]

	return val
}

func (s *decodeState) decodeValue() interface{} {
	s.skipSpaces()
	if len(s.cur) == 0 {
		s.err = fmt.Errorf("incorrect syntax")
		return nil
	}
	switch s.cur[0] {
	case '"':
		s.cur = s.cur[1:]
		val := s.decodeString()
		if s.err != nil {
			return nil
		} else {
			return val
		}
	case '{':
		s.cur = s.cur[1:]
		val := s.decodeObject()
		if s.err != nil {
			return nil
		} else {
			return val
		}
	case '[':
		s.cur = s.cur[1:]
		val := s.decodeSlice()
		if s.err != nil {
			return nil
		} else {
			return val
		}
	case '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
		val := s.decodeNumber()
		if s.err != nil {
			return nil
		} else {
			return val
		}
	case 't':
		if len(s.cur) >= 4 && s.cur[:4] == "true" {
			s.cur = s.cur[4:]
			return true
		} else {
			s.err = fmt.Errorf("'true' expected")
		}
	case 'f':
		if len(s.cur) >= 5 && s.cur[:5] == "false" {
			s.cur = s.cur[5:]
			return false
		} else {
			s.err = fmt.Errorf("'false' expected")
		}
	case 'n':
		if len(s.cur) >= 4 && s.cur[:4] == "null" {
			s.cur = s.cur[4:]
			return nil
		} else {
			s.err = fmt.Errorf("'null' expected")
		}
	default:
		s.err = fmt.Errorf("incorrect syntax")
	}
	return nil
}
<commit_msg>Optimize integer parsing<commit_after>package sjson

import (
	"fmt"
	"strconv"
	"strings"
	"unicode"
	"unicode/utf16"
	"unicode/utf8"
)

// PreallocateObjectElems parameter allow to tune
// preallocated memory objects during the parsing process.
var PreallocateObjectElems = 2

// Decode function parse JSON Text into interface value. Rules are the same as in
// encoding/json module:
//	bool, for JSON booleans
//	float64, for JSON numbers
//	string, for JSON strings
//	[]interface{}, for JSON arrays
//	map[string]interface{}, for JSON objects
//	nil, for JSON null
//
func Decode(json string) (interface{}, error) {
	state := decodeState{cur: json}
	ret := state.decodeValue()
	return ret, state.err
}

type decodeState struct {
	cur string // current bytes
	err error
}

func (s *decodeState) skipSpaces() {
	for len(s.cur) > 0 {
		if s.cur[0] > '\x20' {
			return
		} else if s.cur[0] == '\x20' || s.cur[0] == '\x0A' || s.cur[0] == '\x0D' || s.cur[0] == '\x09' {
			s.cur = s.cur[1:]
		} else {
			return
		}
	}
}

const arr0Size int = 8

func (s *decodeState) decodeSlice() []interface{} {
	var arr0 [arr0Size]interface{}
	var i int

	s.skipSpaces()
	for len(s.cur) > 0 {
		if s.cur[0] == ']' {
			s.cur = s.cur[1:]
			return arr0[:i]
		}

		val := s.decodeValue()
		if s.err != nil {
			return arr0[:i]
		}
		arr0[i] = val
		i++

		s.skipSpaces()
		if len(s.cur) > 0 && s.cur[0] == ',' {
			s.cur = s.cur[1:]
			s.skipSpaces()
			if i >= arr0Size {
				break
			}
		}
	}

	arr := arr0[:]

	for len(s.cur) > 0 {
		if s.cur[0] == ']' {
			s.cur = s.cur[1:]
			return arr
		}

		val := s.decodeValue()
		if s.err != nil {
			return arr
		}
		arr = append(arr, val)

		s.skipSpaces()
		if len(s.cur) > 0 && s.cur[0] == ',' {
			s.cur = s.cur[1:]
			s.skipSpaces()
		}
	}

	s.err = fmt.Errorf("incorrect syntax")
	return arr
}

func (s *decodeState) decodeObject() map[string]interface{} {
	obj := make(map[string]interface{}, PreallocateObjectElems)

	for {
		s.skipSpaces()

		if len(s.cur) == 0 {
			s.err = fmt.Errorf("incorrect syntax")
			return obj
		}

		switch s.cur[0] {
		case '}':
			s.cur = s.cur[1:]
			return obj
		case '"':
			s.cur = s.cur[1:]
			key := s.decodeString()
			if s.err != nil {
				return obj
			}
			s.skipSpaces()
			if len(s.cur) > 0 && s.cur[0] == ':' {
				s.cur = s.cur[1:]
			} else {
				s.err = fmt.Errorf("incorrect syntax (expect ':' after key)")
				return obj
			}
			obj[key] = s.decodeValue()
			s.skipSpaces()
			if len(s.cur) > 0 && s.cur[0] == ',' {
				s.cur = s.cur[1:]
			}
		default:
			s.err = fmt.Errorf("incorrect syntax (expect object key)")
			return obj
		}
	}

	return obj
}

func (s *decodeState) decodeString() string {
	quotePos := strings.IndexByte(s.cur, '"')
	if quotePos < 0 {
		s.err = fmt.Errorf("incorrect syntax (expected close quote)")
		return ""
	}
	// fast path
	val := s.cur[:quotePos]
	if strings.IndexByte(val, '\\') < 0 {
		s.cur = s.cur[quotePos+1:]
		return val
	}

	// TODO(vovkasm): rewrite from zero
	// full decoding
	// - find end of string
	for s.cur[quotePos-1] == '\\' {
		n := strings.IndexByte(s.cur[quotePos+1:], '"')
		quotePos += n + 1
		if quotePos < 0 {
			s.err = fmt.Errorf("incorrect syntax (expected close quote)")
			return ""
		}
	}
	// (from standard json package)
	ret, ok := unquote(s.cur[:quotePos])
	s.cur = s.cur[quotePos+1:]
	if !ok {
		s.err = fmt.Errorf("syntax error (string contains invalid characters)")
	}

	return ret
}

// unquote converts a quoted JSON string literal s into an actual string t.
// The rules are different than for Go, so cannot use strconv.Unquote.
func unquote(s string) (t string, ok bool) {
	b, ok := unquoteBytes([]byte(s))
	t = string(b)
	return
}

func unquoteBytes(s []byte) (t []byte, ok bool) {
	// Check for unusual characters. If there are none,
	// then no unquoting is needed, so return a slice of the
	// original bytes.
	r := 0
	for r < len(s) {
		c := s[r]
		if c == '\\' || c == '"' || c < ' ' {
			break
		}
		if c < utf8.RuneSelf {
			r++
			continue
		}
		rr, size := utf8.DecodeRune(s[r:])
		if rr == utf8.RuneError && size == 1 {
			break
		}
		r += size
	}
	if r == len(s) {
		return s, true
	}

	b := make([]byte, len(s)+2*utf8.UTFMax)
	w := copy(b, s[0:r])
	for r < len(s) {
		// Out of room?  Can only happen if s is full of
		// malformed UTF-8 and we're replacing each
		// byte with RuneError.
		if w >= len(b)-2*utf8.UTFMax {
			nb := make([]byte, (len(b)+utf8.UTFMax)*2)
			copy(nb, b[0:w])
			b = nb
		}
		switch c := s[r]; {
		case c == '\\':
			r++
			if r >= len(s) {
				return
			}
			switch s[r] {
			default:
				return
			case '"', '\\', '/', '\'':
				b[w] = s[r]
				r++
				w++
			case 'b':
				b[w] = '\b'
				r++
				w++
			case 'f':
				b[w] = '\f'
				r++
				w++
			case 'n':
				b[w] = '\n'
				r++
				w++
			case 'r':
				b[w] = '\r'
				r++
				w++
			case 't':
				b[w] = '\t'
				r++
				w++
			case 'u':
				r--
				rr := getu4(s[r:])
				if rr < 0 {
					return
				}
				r += 6
				if utf16.IsSurrogate(rr) {
					rr1 := getu4(s[r:])
					if dec := utf16.DecodeRune(rr, rr1); dec != unicode.ReplacementChar {
						// A valid pair; consume.
						r += 6
						w += utf8.EncodeRune(b[w:], dec)
						break
					}
					// Invalid surrogate; fall back to replacement rune.
					rr = unicode.ReplacementChar
				}
				w += utf8.EncodeRune(b[w:], rr)
			}

		// Quote, control characters are invalid.
		case c == '"', c < ' ':
			return

		// ASCII
		case c < utf8.RuneSelf:
			b[w] = c
			r++
			w++

		// Coerce to well-formed UTF-8.
		default:
			rr, size := utf8.DecodeRune(s[r:])
			r += size
			w += utf8.EncodeRune(b[w:], rr)
		}
	}
	return b[0:w], true
}

// getu4 decodes \uXXXX from the beginning of s, returning the hex value,
// or it returns -1.
func getu4(s []byte) rune {
	if len(s) < 6 || s[0] != '\\' || s[1] != 'u' {
		return -1
	}
	r, err := strconv.ParseUint(string(s[2:6]), 16, 64)
	if err != nil {
		return -1
	}
	return rune(r)
}

func (s *decodeState) decodeNumber() float64 {
	var pos int = 0

	// sign
	if s.cur[pos] == '-' {
		pos++
	}

	// significand
	// - integer
	if len(s.cur) > pos && s.cur[pos] >= '1' && s.cur[pos] <= '9' {
		pos++
		for len(s.cur) > pos && len(s.cur) >= 1 && s.cur[pos] >= '0' && s.cur[pos] <= '9' {
			pos++
		}
	} else if len(s.cur) > pos && s.cur[pos] == '0' {
		pos++
	} else {
		s.err = fmt.Errorf("incorrect number (expected digit)")
		return 0.0
	}

	// - fractional
	var slowParsing bool
	if len(s.cur) > pos && s.cur[pos] == '.' {
		slowParsing = true
		pos++
		if len(s.cur) > pos && s.cur[pos] >= '0' && s.cur[pos] <= '9' {
			pos++
			for len(s.cur) > pos && s.cur[pos] >= '0' && s.cur[pos] <= '9' {
				pos++
			}
		} else {
			s.err = fmt.Errorf("incorrect number (expected fractional)")
			return 0.0
		}
	}

	// exponential
	if len(s.cur) > pos && (s.cur[pos] == 'e' || s.cur[pos] == 'E') {
		slowParsing = true
		pos++
		if len(s.cur) > pos && (s.cur[pos] == '+' || s.cur[pos] == '-') {
			pos++
		}

		if len(s.cur) > pos && s.cur[pos] >= '0' && s.cur[pos] <= '9' {
			pos++
			for len(s.cur) > pos && s.cur[pos] >= '0' && s.cur[pos] <= '9' {
				pos++
			}
		} else {
			s.err = fmt.Errorf("incorrect number (expected digit)")
			return 0.0
		}
	}

	if !slowParsing {
		var acc int64
		switch pos {
		case 1:
			acc = int64(s.cur[0] - '0')
		case 2:
			if s.cur[0] == '-' {
				acc = -int64(s.cur[1] - '0')
			} else {
				acc = 10*int64(s.cur[0]-'0') + int64(s.cur[1]-'0')
			}
		case 3:
			if s.cur[0] == '-' {
				acc = -10*int64(s.cur[1]-'0') - int64(s.cur[2]-'0')
			} else {
				acc = 100*int64(s.cur[0]-'0') + 10*int64(s.cur[1]-'0') + int64(s.cur[2]-'0')
			}
		case 4:
			if s.cur[0] == '-' {
				acc = -100*int64(s.cur[1]-'0') - 10*int64(s.cur[2]-'0') - int64(s.cur[3]-'0')
			} else {
				acc = 1000*int64(s.cur[0]-'0') + 100*int64(s.cur[1]-'0') + 10*int64(s.cur[2]-'0') + int64(s.cur[3]-'0')
			}
		default:
			i := pos - 1
			var mul int64 = 1
			for i > 0 {
				acc += mul * int64(s.cur[i]-'0')
				mul *= 10
				i--
			}
			if s.cur[0] == '-' {
				acc = -acc
			} else {
				acc += mul * int64(s.cur[0]-'0')
			}
		}
		s.cur = s.cur[pos:]
		return float64(acc)
	}

	val, err := strconv.ParseFloat(string(s.cur[:pos]), 64)
	if err != nil {
		s.err = err
	}
	s.cur = s.cur[pos:]

	return val
}

func (s *decodeState) decodeValue() interface{} {
	s.skipSpaces()
	if len(s.cur) == 0 {
		s.err = fmt.Errorf("incorrect syntax")
		return nil
	}
	switch s.cur[0] {
	case '"':
		s.cur = s.cur[1:]
		val := s.decodeString()
		if s.err != nil {
			return nil
		} else {
			return val
		}
	case '{':
		s.cur = s.cur[1:]
		val := s.decodeObject()
		if s.err != nil {
			return nil
		} else {
			return val
		}
	case '[':
		s.cur = s.cur[1:]
		val := s.decodeSlice()
		if s.err != nil {
			return nil
		} else {
			return val
		}
	case '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
		val := s.decodeNumber()
		if s.err != nil {
			return nil
		} else {
			return val
		}
	case 't':
		if len(s.cur) >= 4 && s.cur[:4] == "true" {
			s.cur = s.cur[4:]
			return true
		} else {
			s.err = fmt.Errorf("'true' expected")
		}
	case 'f':
		if len(s.cur) >= 5 && s.cur[:5] == "false" {
			s.cur = s.cur[5:]
			return false
		} else {
			s.err = fmt.Errorf("'false' expected")
		}
	case 'n':
		if len(s.cur) >= 4 && s.cur[:4] == "null" {
			s.cur = s.cur[4:]
			return nil
		} else {
			s.err = fmt.Errorf("'null' expected")
		}
	default:
		s.err = fmt.Errorf("incorrect syntax")
	}
	return nil
}
<|endoftext|>