eval

package
v0.0.0-...-917641f Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 30, 2019 License: MIT Imports: 10 Imported by: 0

Documentation

Overview

Package eval implements evaluated objects

Package eval implements evaluated objects

Package eval implements expression values that can be evaluated

Expression syntax

The language used by eval is a very simple infix expression. The terms can be numbers or quoted strings or names.

Arrays and objects can be specified like so:

(1, 2, 3)
(x  = 42, y = 23)

Empty arrays can be specified like: ()

Arrays with single items can be expressed via (x,). Just doing (x) will only work for function call args -- everywhere else, it is treated as a grouping operator

Empty objects can be found via the global "nil"

Object definitions automatically create local lexical scopes:

(x = y + 100, y = 23) == (x = 123, y = 23)

Scoping is lexical (nesting appropriately) and the order of definitions is not important all all expressions are immutable.

Fields and methods can be accessed via dot:

(1, 2, 3).count == 3

Arrays and maps support filter, map and reduce:

(1, 2, 3).filter(value <= 2)

The expression within filter and map can refer to "value" and "key/index". These are dynamically scoped to a specific array element. Reduce also provides a "last" variable to track cumulative result.

(1, 2, 3).reduce(100, last + value) == 106

The Parser

An expression value can be produced by use of Parse:

eval.Parse(scope, "list.map(value + 2)")
=> equivalent to the following:

// first define all the tokens
dot := &data.Ref{ID: types.S16(".")}
plus := &data.Ref{ID: types.S16("+")}
list := &data.Ref{ID: types.S16("list")}
doMap := &data.Ref{ID: types.S16("map")}
value := &data.Ref{ID: types.S16("value")}
two := changes.Atomic{Value: 2}

now create a call expression for the above
expr := &data.Call{A: types.A{
    // first element evaluates to list.map
    &data.Call{A: types.A{dot, list, doMap}},
    // second element represents value+2
    &data.Call{A: types.A{plus, value, two}},
}}

Evaluation

An expression can be evaluated with Eval(). Eval converts call expression to the actual evaluated values using the provided scope. Eval also walks the contents of any containers, replacing any expressions with their evaluated values. Eval honors references via Dir (so this is an easy way to create local scopes).

Package eval implements evaluated objects

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func Eval

func Eval(s Scope, v changes.Value) changes.Value

Eval evaluates a value within a particular scope

Values of type CallExpr evaluate to the corresponding call. All other values are simply the same values but with any nested call expressions evaluated.

The provided scope is used to lookup IDs with Ref{} values. Any Dir values automatically contribute by creating a new scope

func Parse

func Parse(s Scope, code string) changes.Value

Parse converts an expression into the corresponding AST

The result is not yet evaluated, and needs a call to Eval to work.

Types

type Call

type Call struct {
	types.A
}

Call represents a "call" expression. The first element of the array is expected to be "Callable" which can be called with the rest as args.

All errors result in a nil result.

func (*Call) Apply

func (cx *Call) Apply(ctx changes.Context, c changes.Change) changes.Value

Apply implements changes.Value.

func (*Call) Eval

func (cx *Call) Eval(s Scope) changes.Value

Eval evaluates a call expression in the provided scope

type Callable

type Callable func(s Scope, args []changes.Value) changes.Value

Callable wraps a function into a value type that can be called

var Dot Callable = func(s Scope, args []changes.Value) changes.Value {
	var receiver changes.Value = changes.Nil
	if len(args) > 0 {
		receiver = args[0]
		for _, arg := range args[1:] {
			receiver = dot(s, receiver, arg)
		}
	}
	return receiver
}

Dot is the "callable" which evaluates args[0].args[1].args[2]...

var Equal Callable = equality(func(x, y changes.Value) bool {

	return x == y
})

Equal compares if value are all the same

var NotEqual Callable = equality(func(x, y changes.Value) bool {

	return x != y
})

NotEqual compares if values are all different

var NumLess Callable = compare(func(x, y int) bool {
	return x < y
})

NumLess compares if numbers are in ascending order

var NumLessThanEqual Callable = compare(func(x, y int) bool {
	return x <= y
})

NumLessThanEqual is like NumEqual but with equality allowed

var NumMore Callable = compare(func(x, y int) bool {
	return x > y
})

NumMore compares if numbers are in descending order

var NumMoreThanEqual Callable = compare(func(x, y int) bool {
	return x >= y
})

NumMoreThanEqual compares if numbers are in descending order

var Sum Callable = func(s Scope, args []changes.Value) changes.Value {
	var sum int
	for _, arg := range args {
		atomic, _ := Eval(s, arg).(changes.Atomic)
		val, _ := atomic.Value.(int)
		sum += val
	}
	return changes.Atomic{Value: sum}
}

Sum is the "callable" which evaluates the sum of all args

Non-numeric values are treated as zeros

func (Callable) Apply

func (cx Callable) Apply(ctx changes.Context, c changes.Change) changes.Value

Apply implements changes.Value

type ParseError

type ParseError struct {
	Offset  int
	Message string
}

ParseError captures the error state

func (ParseError) Error

func (p ParseError) Error() string

Error implements the Error interface

type Scope

type Scope func(v interface{}) changes.Value

Scope is any scope lookup function. Scope lookups are expected to return evaluated values but nested fields may contain unevaluated values.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL