diff --git a/parser.go b/parser.go index 48c9dcd..90897e0 100644 --- a/parser.go +++ b/parser.go @@ -191,10 +191,11 @@ func (self *parser) Parse(scanner *scanner, termSymbols ...Symbol) (tree *ast, e func (self *parser) parseGeneral(scanner *scanner, allowForest bool, allowVarRef bool, termSymbols ...Symbol) (tree *ast, err error) { var selectorTerm *term = nil var currentTerm *term = nil + var tk *Token tree = NewAst() firstToken := true lastSym := SymUnknown - for tk := scanner.Next(); err == nil && tk != nil && !tk.IsTerm(termSymbols); tk = scanner.Next() { + for tk = scanner.Next(); err == nil && tk != nil && !tk.IsTerm(termSymbols); tk = scanner.Next() { if tk.Sym == SymComment { continue } @@ -281,6 +282,9 @@ func (self *parser) parseGeneral(scanner *scanner, allowForest bool, allowVarRef } lastSym = tk.Sym } + if err == nil { + err = tk.Error() + } return } diff --git a/scanner.go b/scanner.go index e4eb848..aaa24af 100644 --- a/scanner.go +++ b/scanner.go @@ -280,62 +280,116 @@ func (self *scanner) sync(err error) error { return err } +func isBinaryDigit(ch byte) bool { + return ch == '0' || ch == '1' +} + +func isOctalDigit(ch byte) bool { + return ch >= '0' && ch <= '7' +} + +func isDecimalDigit(ch byte) bool { + return ch >= '0' && ch <= '9' +} + +func isHexDigit(ch byte) bool { + return (ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'f') || (ch >= 'A' && ch <= 'F') +} + +func (self *scanner) initBase(sb *strings.Builder, currentFirstCh byte) (firstCh byte, numBase int, digitFunc func(byte) bool, err error) { + var ch byte + var digitType string + firstCh = currentFirstCh + digitFunc = isDecimalDigit + numBase = 10 + + if ch, err = self.peek(); err == nil { + if ch == 'b' || ch == 'B' { + numBase = 2 + digitType = "binary" + self.readChar() + digitFunc = isBinaryDigit + firstCh, err = self.readChar() + } else if ch == 'o' || ch == 'O' { + numBase = 8 + digitType = "octal" + self.readChar() + digitFunc = isOctalDigit + firstCh, err = self.readChar() + } else if ch == 'x' || ch == 'X' { + numBase = 16 + digitType = "hex" + self.readChar() + digitFunc = isHexDigit + firstCh, err = self.readChar() + } + if err == nil && !digitFunc(firstCh) { + if len(digitType) == 0 { + digitType = "decimal" + } + err = fmt.Errorf("expected %s digit, got '%c'", digitType, firstCh) + } + } else if err == io.EOF { + err = nil + } + return +} + func (self *scanner) parseNumber(firstCh byte) (tk *Token) { var err error var ch byte var sym Symbol = SymInteger - var value any var sb strings.Builder + var isDigit func(byte) bool = isDecimalDigit + var numBase = 10 - for ch = firstCh; err == nil && (ch >= '0' && ch <= '9'); ch, err = self.readChar() { + if firstCh == '0' { + firstCh, numBase, isDigit, err = self.initBase(&sb, firstCh) + } + for ch = firstCh; err == nil && isDigit(ch); ch, err = self.readChar() { sb.WriteByte(ch) } - if ch == '.' { - sym = SymFloat - sb.WriteByte(ch) - ch, err = self.readChar() - if ch >= '0' && ch <= '9' { - for ; err == nil && (ch >= '0' && ch <= '9'); ch, err = self.readChar() { - sb.WriteByte(ch) - } - } - } - if ch == 'e' || ch == 'E' { - sym = SymFloat - sb.WriteByte(ch) - if ch, err = self.readChar(); err == nil { - if ch == '+' || ch == '-' { - sb.WriteByte(ch) - ch, err = self.readChar() - } + + if numBase == 10 { + if err == nil && ch == '.' { + sym = SymFloat + sb.WriteByte(ch) + ch, err = self.readChar() if ch >= '0' && ch <= '9' { for ; err == nil && (ch >= '0' && ch <= '9'); ch, err = self.readChar() { sb.WriteByte(ch) } - //err = self.sync(err) - } else { - err = errors.New("expected integer exponent") } } - // } else { - // err = self.sync(err) + if err == nil && (ch == 'e' || ch == 'E') { + sym = SymFloat + sb.WriteByte(ch) + if ch, err = self.readChar(); err == nil { + if ch == '+' || ch == '-' { + sb.WriteByte(ch) + ch, err = self.readChar() + } + if ch >= '0' && ch <= '9' { + for ; err == nil && (ch >= '0' && ch <= '9'); ch, err = self.readChar() { + sb.WriteByte(ch) + } + } else { + err = errors.New("expected integer exponent") + } + } + } } if err != nil && err != io.EOF { tk = self.makeErrorToken(err) } else { + var value any err = self.sync(err) txt := sb.String() if sym == SymFloat { value, err = strconv.ParseFloat(txt, 64) - } else if strings.HasPrefix(txt, "0x") { - value, err = strconv.ParseInt(txt, 16, 64) - } else if strings.HasPrefix(txt, "0o") { - value, err = strconv.ParseInt(txt, 8, 64) - } else if strings.HasPrefix(txt, "0b") { - value, err = strconv.ParseInt(txt, 2, 64) } else { - value, err = strconv.ParseInt(txt, 10, 64) + value, err = strconv.ParseInt(txt, numBase, 64) } if err == nil { tk = self.makeValueToken(sym, txt, value) diff --git a/token.go b/token.go index 76f6bba..c515e35 100644 --- a/token.go +++ b/token.go @@ -67,3 +67,17 @@ func (self *Token) Errorf(template string, args ...any) (err error) { err = fmt.Errorf(fmt.Sprintf("[%d:%d] ", self.row, self.col)+template, args...) return } + +func (self *Token) Error() (err error) { + if self.Sym == SymError { + if msg, ok := self.Value.(error); ok { + err = fmt.Errorf("[%d:%d] %v", self.row, self.col, msg) + } + } + return +} + +func (self *Token) Errors(msg string) (err error) { + err = fmt.Errorf("[%d:%d] %v", self.row, self.col, msg) + return +}