1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135
|
package main
import (
"fmt"
"strconv"
"strings"
"text/scanner"
"github.com/alecthomas/kong"
"github.com/alecthomas/participle/v2"
"github.com/alecthomas/participle/v2/lexer"
"github.com/alecthomas/repr"
)
type operatorPrec struct{ Left, Right int }
var operatorPrecs = map[string]operatorPrec{
"+": {1, 1},
"-": {1, 1},
"*": {3, 2},
"/": {5, 4},
"%": {7, 6},
}
type (
Expr interface{ expr() }
ExprIdent struct{ Name string }
ExprString struct{ Value string }
ExprNumber struct{ Value float64 }
ExprParens struct{ Sub Expr }
ExprUnary struct {
Op string
Sub Expr
}
ExprBinary struct {
Lhs Expr
Op string
Rhs Expr
}
)
func (ExprIdent) expr() {}
func (ExprString) expr() {}
func (ExprNumber) expr() {}
func (ExprParens) expr() {}
func (ExprUnary) expr() {}
func (ExprBinary) expr() {}
func parseExprAny(lex *lexer.PeekingLexer) (Expr, error) { return parseExprPrec(lex, 0) }
func parseExprAtom(lex *lexer.PeekingLexer) (Expr, error) {
switch peek := lex.Peek(); {
case peek.Type == scanner.Ident:
return ExprIdent{lex.Next().Value}, nil
case peek.Type == scanner.String:
val, err := strconv.Unquote(lex.Next().Value)
if err != nil {
return nil, err
}
return ExprString{val}, nil
case peek.Type == scanner.Int || peek.Type == scanner.Float:
val, err := strconv.ParseFloat(lex.Next().Value, 64)
if err != nil {
return nil, err
}
return ExprNumber{val}, nil
case peek.Value == "(":
_ = lex.Next()
inner, err := parseExprAny(lex)
if err != nil {
return nil, err
}
if lex.Peek().Value != ")" {
return nil, fmt.Errorf("expected closing ')'")
}
_ = lex.Next()
return ExprParens{inner}, nil
default:
return nil, participle.NextMatch
}
}
func parseExprPrec(lex *lexer.PeekingLexer, minPrec int) (Expr, error) {
var lhs Expr
if peeked := lex.Peek(); peeked.Value == "-" || peeked.Value == "!" {
op := lex.Next().Value
atom, err := parseExprAtom(lex)
if err != nil {
return nil, err
}
lhs = ExprUnary{op, atom}
} else {
atom, err := parseExprAtom(lex)
if err != nil {
return nil, err
}
lhs = atom
}
for {
peek := lex.Peek()
prec, isOp := operatorPrecs[peek.Value]
if !isOp || prec.Left < minPrec {
break
}
op := lex.Next().Value
rhs, err := parseExprPrec(lex, prec.Right)
if err != nil {
return nil, err
}
lhs = ExprBinary{lhs, op, rhs}
}
return lhs, nil
}
type Expression struct {
X Expr `@@`
}
var parser = participle.MustBuild[Expression](participle.ParseTypeWith(parseExprAny))
func main() {
var cli struct {
Expr []string `arg required help:"Expression to parse."`
}
ctx := kong.Parse(&cli)
expr, err := parser.ParseString("", strings.Join(cli.Expr, " "))
ctx.FatalIfErrorf(err)
repr.Println(expr)
}
|