repos
/ orchard main

orchard

mirror

Every site I host, in one repo, along with the Cloudflare Tunnel and Caddy that front them. It's all Go, Vite, and SQLite, and it runs on a desktop at home with nothing listening on an inbound port.

blogbuncaddycloudflare-tunneldockergogolanghomelabhtml-templatemonorepoself-hostedseosqlitestatic-sitetypstuptime-monitoringviteweb-analytics

1.6 KB · 76 lines · Go Raw History
 1package tools
 2
 3import (
 4	"fmt"
 5	"go/ast"
 6	"go/parser"
 7	"go/token"
 8	"strconv"
 9)
10
11// evalExpr walks a parsed Go expression rather than shelling out to anything.
12// go/parser is already in the standard library and it rejects everything that
13// is not an expression for free, so the whole risk surface is the node types
14// allowed below.
15func evalExpr(s string) (float64, error) {
16	node, err := parser.ParseExpr(s)
17	if err != nil {
18		return 0, fmt.Errorf("that is not an arithmetic expression")
19	}
20	return evalNode(node)
21}
22
23func evalNode(n ast.Expr) (float64, error) {
24	switch e := n.(type) {
25	case *ast.BasicLit:
26		switch e.Kind {
27		case token.INT, token.FLOAT:
28			return strconv.ParseFloat(e.Value, 64)
29		}
30		return 0, fmt.Errorf("only numbers are allowed")
31	case *ast.ParenExpr:
32		return evalNode(e.X)
33	case *ast.UnaryExpr:
34		v, err := evalNode(e.X)
35		if err != nil {
36			return 0, err
37		}
38		switch e.Op {
39		case token.SUB:
40			return -v, nil
41		case token.ADD:
42			return v, nil
43		}
44		return 0, fmt.Errorf("unsupported sign")
45	case *ast.BinaryExpr:
46		l, err := evalNode(e.X)
47		if err != nil {
48			return 0, err
49		}
50		r, err := evalNode(e.Y)
51		if err != nil {
52			return 0, err
53		}
54		switch e.Op {
55		case token.ADD:
56			return l + r, nil
57		case token.SUB:
58			return l - r, nil
59		case token.MUL:
60			return l * r, nil
61		case token.QUO:
62			if r == 0 {
63				return 0, fmt.Errorf("division by zero")
64			}
65			return l / r, nil
66		case token.REM:
67			if int64(r) == 0 {
68				return 0, fmt.Errorf("division by zero")
69			}
70			return float64(int64(l) % int64(r)), nil
71		}
72		return 0, fmt.Errorf("unsupported operator %s", e.Op)
73	}
74	return 0, fmt.Errorf("only arithmetic is supported")
75}