aboutsummaryrefslogtreecommitdiff
path: root/scanner.go
blob: a07981dbb21df28235ff5444668a5aca64817b2a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
package main

//go:generate stringer -type=Token
type Token int

const (
	INVALID Token = iota 
	EOF              
	NUM             
	ADD            
	SUB           
	MUL          
	DIV         
	EXP        
)

type scanner struct {
	c   byte
	i   int
	buf []byte
}

func newScanner(s []byte) *scanner {
	return &scanner{
		buf: s,
	}
}

func (s *scanner) read() bool {
	if len(s.buf) <= s.i {
		return false
	}
	s.c = s.buf[s.i]
	s.i++
	return true
}

func (s *scanner) unread() {
	s.i--
	s.c = s.buf[s.i]
}

// Pos returns the index of the next character
func (s *scanner) Pos() int {
	return s.i
}

func (s *scanner) Scan() (t Token, d []byte) {
read:
	ok := s.read()
	if !ok {
		return EOF, []byte{}
	}
	switch s.c {
	case '+':
		return ADD, nil
	case '-':
		return SUB, nil
	case '*':
		return MUL, nil
	case '/':
		return DIV, nil
	case '^':
		return EXP, nil
	case ' ', '\n', '\t':
		goto read
	default:
		if (s.c >= '0' && s.c <= '9') {
			s.unread()
			return s.scanNumber(false)
		}
		if s.c == '-' {
			return s.scanNumber(true)
		}
	}
	return INVALID, []byte{s.c}
}

func (s *scanner) scanNumber(neg bool) (t Token, d []byte) {
	if neg {
		d = append(d, '-')
	}
	for s.read() {
		if (s.c >= '0' && s.c <= '9') {
			d = append(d, s.c)
		} else {
			s.unread()
			break
		}
	}
	return NUM, d
}