Open1
四則演算するだけ
package main
import (
"fmt"
"io"
"log"
"os"
"strconv"
)
type Token struct {
txt string
next *Token
}
type NodeType int
const (
Num NodeType = iota
Add
Sub
Mul
Div
Kakko
)
type Node struct {
txt string
typ NodeType
left *Node
right *Node
}
// unary = "(" expr ")" | number
func unary(tok **Token) *Node {
if (*tok).txt == "(" {
*tok = (*tok).next
node := expr(tok)
if (*tok).txt != ")" {
log.Fatalf("unsuported token %v", (*tok).txt)
}
*tok = (*tok).next
return &Node{
txt: "",
typ: Kakko,
left: node,
}
}
for _, s := range (*tok).txt {
if '0' > s && s > '9' {
log.Fatalf("unsuported token %v", (*tok).txt)
}
}
node := &Node{
txt: (*tok).txt,
typ: Num,
}
// consume
*tok = (*tok).next
return node
}
// mul = unary ("*" unary | "/" unary)*
func mul(tok **Token) *Node {
node := unary(tok)
for {
switch (*tok).txt {
case "*":
*tok = (*tok).next
newNode := &Node{
txt: "*",
typ: Mul,
left: node,
right: unary(tok),
}
node = newNode
continue
case "/":
*tok = (*tok).next
newNode := &Node{
txt: "/",
typ: Mul,
left: node,
right: unary(tok),
}
node = newNode
continue
}
return node
}
}
func add(tok **Token) *Node {
node := mul(tok)
for {
switch (*tok).txt {
case "+":
*tok = (*tok).next
newNode := &Node{
txt: "+",
typ: Add,
left: node,
right: mul(tok),
}
node = newNode
continue
case "-":
*tok = (*tok).next
newNode := &Node{
txt: "-",
typ: Sub,
left: node,
right: mul(tok),
}
node = newNode
continue
}
return node
}
}
// expr = add
// add = mul ("+" mul | "-" mul)*
// mul = unary ("*" unary | "/" unary)*
func expr(tok **Token) *Node {
return add(tok)
}
func parse(tok *Token) *Node {
return expr(&tok)
}
type Visitor func(node *Node)
func walk(node *Node, v Visitor) {
if node == nil {
return
}
walk(node.left, v)
walk(node.right, v)
v(node)
}
func debugWalk(node *Node) {
if node == nil {
return
}
debugWalk(node.left)
debugWalk(node.right)
fmt.Println(node.txt)
}
func eval(n *Node) int {
switch n.typ {
case Add:
return eval(n.left) + eval(n.right)
case Sub:
return eval(n.left) - eval(n.right)
case Mul:
return eval(n.left) * eval(n.right)
case Div:
return eval(n.left) / eval(n.right)
case Num:
ret, _ := strconv.Atoi(n.txt)
return ret
case Kakko:
return eval(n.left)
}
return 127
}
func isDigit(p byte) bool {
return '0' <= p && p <= '9'
}
func isPuct(s byte) bool {
return s == '+' || s == '-' || s == '*' || s == '/' || s == '(' || s == ')'
}
func isEmpty(p byte) bool {
return ' ' == p || p == '\n'
}
func tokenize(program string) *Token {
tokHead := &Token{}
tok := tokHead
for i := 0; i < len(program); i++ {
if isDigit(program[i]) {
j := 0
// consume
for i+j < len(program) && isDigit(program[i+j]) {
j++
}
newTok := &Token{
txt: program[i : i+j], next: nil,
}
tok.next = newTok
tok = newTok
i += j - 1
continue
}
// binary opcode
if isPuct(program[i]) {
newTok := &Token{
txt: string(program[i]), next: nil,
}
tok.next = newTok
tok = newTok
}
}
// sentinel
newTok := &Token{
txt: string("SENTINEL"), next: nil,
}
tok.next = newTok
tok = newTok
return tokHead
}
func printTok(tok *Token) {
current := tok
for current != nil {
fmt.Println(current.txt)
current = current.next
}
}
func main() {
b, err := io.ReadAll(os.Stdin)
if err != nil {
log.Fatalln("cannot read")
}
program := string(b)
// program := "(1 + 2) * 3"
tokHead := tokenize(program)
// printTok(tokHead)
node := parse(tokHead.next)
// debugWalk(node)
fmt.Println(eval(node))
}