File: eval.go

package info (click to toggle)
lltsv 0.7.0-3
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 136 kB
  • sloc: makefile: 21
file content (78 lines) | stat: -rw-r--r-- 1,678 bytes parent folder | download | duplicates (3)
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
package main

import (
	"errors"
	"go/ast"
	"go/constant"
	"go/parser"
	"strconv"
)

// Vars is a map for LTSV's label and value.
type Vars map[string]string

// ExprRunner is an expression runner for Go code.
type ExprRunner struct {
	expr ast.Expr
}

// ExprContext is a context for ExprRunner.
type ExprContext struct {
	vars Vars
}

func parseExpr(e string) (ast.Expr, error) {
	expr, err := parser.ParseExpr(e)
	if err != nil {
		return nil, err
	}

	return expr, nil
}

func evalExpr(expr ast.Expr, ctx *ExprContext) (constant.Value, error) {
	switch e := expr.(type) {
	case *ast.BasicLit:
		return evalBasicLit(e, ctx)
	case *ast.BinaryExpr:
		return evalBinaryExpr(e, ctx)
	case *ast.Ident:
		return evalIdent(e, ctx)
	case *ast.ParenExpr:
		return evalExpr(e.X, ctx)
	}

	return constant.MakeUnknown(), errors.New("unknown expr")
}

func evalBasicLit(expr *ast.BasicLit, ctx *ExprContext) (constant.Value, error) {
	return constant.MakeFromLiteral(expr.Value, expr.Kind, 0), nil
}

func evalBinaryExpr(expr *ast.BinaryExpr, ctx *ExprContext) (constant.Value, error) {
	x, err := evalExpr(expr.X, ctx)
	if err != nil {
		return constant.MakeUnknown(), err
	}

	y, err := evalExpr(expr.Y, ctx)
	if err != nil {
		return constant.MakeUnknown(), err
	}

	return constant.BinaryOp(x, expr.Op, y), nil
}

func evalIdent(expr *ast.Ident, ctx *ExprContext) (constant.Value, error) {
	name, ok := ctx.vars[expr.Name]
	if !ok {
		return constant.MakeUnknown(), errors.New("unknown variable name")
	}

	n, err := strconv.ParseFloat(name, 64)
	if err != nil {
		return constant.MakeUnknown(), errors.New("variable type must be numeric")
	}

	return constant.MakeFloat64(n), nil
}