Open1

四則演算するだけ

szktkfmszktkfm
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))
}