This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package main | |
import ( | |
"bufio" | |
"fmt" | |
"log" | |
"os" | |
"strconv" | |
"unicode" | |
) | |
type TokenType int | |
const ( | |
NumberToken = iota | |
OperatorToken = iota | |
SpaceToken = iota | |
) | |
type Token struct { | |
Literal string | |
Type TokenType | |
} | |
func (t Token) String() string { | |
switch t.Type { | |
case NumberToken: | |
return "NUMBER: " + t.Literal | |
case OperatorToken: | |
return "OPERATOR: " + t.Literal | |
case SpaceToken: | |
return "SPACE: " + t.Literal | |
default: | |
return "(unknown)" | |
} | |
} | |
func main() { | |
tokens := Lex(bufio.NewReader(os.Stdin)) | |
for _, t := range tokens { | |
fmt.Println(t) | |
} | |
answer := Parse(tokens) | |
fmt.Println("answer: ", answer) | |
} | |
func Parse(tokens []Token) int { | |
return ParseFirstNum(tokens) | |
} | |
func ParseFirstNum(tokens []Token) int { | |
tok := tokens[0] | |
if tok.Type != NumberToken { | |
log.Fatalln("ERROR! invalid syntax at: " + tok.Literal) | |
} | |
return ParseOp(tokens[1:], tok) | |
} | |
func ParseOp(tokens []Token, leftNum Token) int { | |
tok := tokens[0] | |
if tok.Type != OperatorToken { | |
log.Fatalln("ERROR! two numbers, no operator at: " + tok.Literal) | |
} | |
return ParseSecondNum(tokens[1:], leftNum, tok) | |
} | |
func ParseSecondNum(tokens []Token, leftNum, operator Token) int { | |
tok := tokens[0] | |
if tok.Type != NumberToken { | |
log.Fatalln("ERROR! invalid syntax, expect number at: " + tok.Literal) | |
} | |
left, _ := strconv.Atoi(leftNum.Literal) | |
right, _ := strconv.Atoi(tok.Literal) | |
result := 0 | |
switch operator.Literal { | |
case "+": | |
result = left + right | |
case "-": | |
result = left - right | |
default: | |
log.Fatalln("ERROR! unsupported operator: " + tok.Literal) | |
} | |
tokens = tokens[1:] | |
if len(tokens) == 0 { | |
return result | |
} | |
if len(tokens) < 2 { // at least 1 more operator and 1 more number | |
log.Fatalln("ERROR! leftover token: " + tokens[0].Literal) | |
} | |
return ParseOp(tokens, Token{ | |
Literal: strconv.Itoa(result), | |
Type: NumberToken, | |
}) | |
} | |
func Lex(reader *bufio.Reader) (tokens []Token) { | |
for { | |
r, _, err := reader.ReadRune() | |
if err != nil { | |
fmt.Println(err) | |
break | |
} | |
if unicode.IsSpace(r) { | |
// automatically ignore space tokens | |
continue | |
} | |
switch r { | |
case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': | |
reader.UnreadRune() | |
tokens = append(tokens, LexNumber(reader)) | |
case '+', '-', '*', '/': | |
tokens = append(tokens, Token{ | |
Literal: string(r), | |
Type: OperatorToken, | |
}) | |
default: | |
fmt.Printf("invalid token: %#v\n", r) | |
break | |
} | |
} | |
return | |
} | |
func LexNumber(reader *bufio.Reader) Token { | |
scratch := "" | |
LexNum: | |
for { | |
r, _, err := reader.ReadRune() | |
if err != nil { | |
fmt.Println(err) | |
break | |
} | |
switch r { | |
case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': | |
scratch = scratch + string(r) | |
default: | |
reader.UnreadRune() | |
break LexNum | |
} | |
} | |
return Token{ | |
Literal: scratch, | |
Type: NumberToken, | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment