Parent directory

lexer.go

2748 bytes
  1package rpn
  2
  3import (
  4	"fmt"
  5	"math"
  6	"strconv"
  7	"unicode"
  8)
  9
 10type Lexer struct {
 11	input *Input
 12}
 13
 14func (l *Lexer) parseNumber(negative bool) (*Token, error) {
 15	numStr := ""
 16	char := l.input.NextChar()
 17	for unicode.IsNumber(char) || char == '.' {
 18		numStr += string(char)
 19		char = l.input.Eat()
 20	}
 21
 22	num, err := strconv.ParseFloat(numStr, 64)
 23	if err != nil {
 24		return nil, err
 25	}
 26	if negative {
 27		numStr = "-" + numStr
 28		num = -num
 29	}
 30
 31	return &Token{number, 0, num, numStr}, nil
 32}
 33
 34func (l *Lexer) parseWord() (*Token, error) {
 35	word := ""
 36	char := l.input.NextChar()
 37	for unicode.IsLetter(char) {
 38		word += string(char)
 39		char = l.input.Eat()
 40	}
 41
 42	switch word {
 43	case "sqrt":
 44		return &Token{unaryOp, sqrt, 0, word}, nil
 45	case "dec":
 46		return &Token{dec, 0, 0, word}, nil
 47	case "bin":
 48		return &Token{bin, 0, 0, word}, nil
 49	case "hex":
 50		return &Token{hex, 0, 0, word}, nil
 51	case "pi":
 52		return &Token{number, 0, math.Pi, word}, nil
 53	case "pop":
 54		return &Token{pop, 0, 0, word}, nil
 55	case "swap":
 56		return &Token{swap, 0, 0, word}, nil
 57	case "clr":
 58		return &Token{clr, 0, 0, word}, nil
 59	case "help":
 60		return &Token{help, 0, 0, word}, nil
 61	case "exit":
 62		return &Token{exit, 0, 0, word}, nil
 63	default:
 64		return nil, fmt.Errorf("unknown input: %s", word)
 65	}
 66}
 67
 68func (l *Lexer) Parse(input string) ([]*Token, error) {
 69	var err error
 70	var token *Token
 71	l.input = NewInput(input)
 72	if l.input == nil {
 73		return nil, nil
 74	}
 75	tokens := []*Token{}
 76	for {
 77		char := l.input.NextChar()
 78		if char == 0 {
 79			break
 80		}
 81
 82		switch char {
 83		case '+':
 84			l.input.Eat()
 85			token = &Token{binaryOp, plus, 0, string(char)}
 86		case '-':
 87			char = l.input.Eat()
 88			if unicode.IsNumber(char) {
 89				token, err = l.parseNumber(true)
 90				if err != nil {
 91					return nil, err
 92				}
 93			} else {
 94				token = &Token{binaryOp, minus, 0, string(char)}
 95			}
 96		case '*':
 97			l.input.Eat()
 98			token = &Token{binaryOp, multiply, 0, string(char)}
 99		case '/':
100			l.input.Eat()
101			token = &Token{binaryOp, divide, 0, string(char)}
102		case '%':
103			l.input.Eat()
104			token = &Token{binaryOp, mod, 0, string(char)}
105		case '^':
106			l.input.Eat()
107			token = &Token{binaryOp, power, 0, string(char)}
108		case ' ':
109			l.input.Eat()
110			continue
111		case 0x0a:
112			l.input.Eat()
113			continue
114		case 0x04: // Ctrl-D
115			token = &Token{exit, 0, 0, "^D"}
116		case '?':
117			l.input.Eat()
118			token = &Token{help, 0, 0, string(char)}
119		default:
120			if unicode.IsNumber(char) {
121				token, err = l.parseNumber(false)
122				if err != nil {
123					return nil, err
124				}
125			} else if unicode.IsLetter(char) {
126				token, err = l.parseWord()
127				if err != nil {
128					return nil, err
129				}
130			} else {
131				return nil, fmt.Errorf("invalid input: %c", char)
132			}
133		}
134
135		tokens = append(tokens, token)
136	}
137
138	return tokens, nil
139}