Expressions are numbers joined by operators, so the scanner needs to recognize the six arithmetic operators as their own tokens. Today you emit an Operator token for each, and confirm that two operators sitting next to each other stay two separate tokens.
Emit a single-character Operator token for each of + - * / % and ^.
Each of the six operators is a single character, so its rule is trivial: emit one
Operator token whose text is that character and advance by one. The interesting
case is what happens when operators are adjacent. In 2*-3 the * and - sit
side by side, and the scanner must keep them as two separate one-character tokens
rather than trying to combine them into some *- operator. Because each operator
rule consumes exactly one character, this falls out for free: the parser will later
read -3 as a negation of 3, but that is the parser’s job, not the scanner’s.
Give the scanner a default case too, for any character it does not recognize.
Emitting an Illegal token (and still advancing) means the scanner always makes
progress and never loops forever on stray input like @; the parser can turn that
Illegal token into a clear error further down the line.
case c=='+' || c=='-' || c=='*' || c=='/' || c=='%' || c=='^':toks = append(toks, Token{Operator, string(c), i})i++default:// anything unrecognized becomes an Illegal token so the scanner// always makes progress and the parser can reject it latertoks = append(toks, Token{Illegal, string(c), i})i++