func (p *parser) checkIdList(list []ast.Node) bool {
	for _, node := range list {
		if _, isId := node.(*ast.Id); !isId {
			return false
		}
	}
	return true
}
check if an expression list contains only identifiers
package parse
import (
	"fmt"
	"github.com/glhrmfrts/yo/ast"
	"strconv"
)
type parser struct {
	tok            ast.Token
	literal        string
	ignoreNewlines bool
	tokenizer      tokenizer
}
type ParseError struct {
	Guilty  ast.Token
	Line    int
	File    string
	Message string
}
func (err *ParseError) Error() string {
	return fmt.Sprintf("%s:%d: %s", err.File, err.Line, err.Message)
}
func parseNumber(typ ast.Token, str string) float64 {
	if typ == ast.TokenFloat {
		f, err := strconv.ParseFloat(str, 64)
		if err != nil {
			panic(err)
		}
		return f
	} else {
		i, err := strconv.Atoi(str)
		if err != nil {
			panic(err)
		}
		return float64(i)
	}
}
func (p *parser) error(msg string) {
	t := p.tokenizer
	panic(&ParseError{Guilty: p.tok, Line: t.lineno, File: t.filename, Message: msg})
}
func (p *parser) errorExpected(expected string) {
	p.error(fmt.Sprintf("unexpected %s, expected %s", p.tok, expected))
}
func (p *parser) line() int {
	return p.tokenizer.lineno
}
func (p *parser) next() {
	p.tok, p.literal = p.tokenizer.nextToken()
	for p.ignoreNewlines && p.tok == ast.TokenNewline {
		p.tok, p.literal = p.tokenizer.nextToken()
	}
}
func (p *parser) accept(toktype ast.Token) bool {
	if p.tok == toktype {
		p.next()
		return true
	}
	return false
}
func (p *parser) makeId() *ast.Id {
	return &ast.Id{Value: p.literal}
}
func (p *parser) makeSelector(left ast.Node) *ast.Selector {
	return &ast.Selector{Left: left, Value: p.literal}
}
func (p *parser) idList() []*ast.Id {
	var list []*ast.Id
	for p.tok == ast.TokenId {
		list = append(list, &ast.Id{Value: p.literal})
		p.next()
		if !p.accept(ast.TokenComma) {
			break
		}
	}
	return list
}