pax_global_header 0000666 0000000 0000000 00000000064 13726232776 0014531 g ustar 00root root 0000000 0000000 52 comment=7dd2fd39e4cc52b0b68694294e2414b59785a535
expr-1.8.9/ 0000775 0000000 0000000 00000000000 13726232776 0012526 5 ustar 00root root 0000000 0000000 expr-1.8.9/.github/ 0000775 0000000 0000000 00000000000 13726232776 0014066 5 ustar 00root root 0000000 0000000 expr-1.8.9/.github/workflows/ 0000775 0000000 0000000 00000000000 13726232776 0016123 5 ustar 00root root 0000000 0000000 expr-1.8.9/.github/workflows/build.yml 0000664 0000000 0000000 00000000517 13726232776 0017750 0 ustar 00root root 0000000 0000000 name: build
on: [push]
jobs:
test:
name: go1.13
runs-on: ubuntu-latest
steps:
- name: Set up Go 1.13
uses: actions/setup-go@v1
with:
go-version: 1.13
id: go
- name: Check out code into the Go module directory
uses: actions/checkout@v2
- name: Test
run: go test ./...
expr-1.8.9/.gitignore 0000664 0000000 0000000 00000000055 13726232776 0014516 0 ustar 00root root 0000000 0000000 *.exe
*.exe~
*.dll
*.so
*.dylib
*.test
*.out
expr-1.8.9/.travis.yml 0000664 0000000 0000000 00000000034 13726232776 0014634 0 ustar 00root root 0000000 0000000 language: go
go:
- 1.13.x
expr-1.8.9/LICENSE 0000664 0000000 0000000 00000002057 13726232776 0013537 0 ustar 00root root 0000000 0000000 MIT License
Copyright (c) 2019 Anton Medvedev
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
expr-1.8.9/README.md 0000664 0000000 0000000 00000011076 13726232776 0014012 0 ustar 00root root 0000000 0000000 # Expr
[](https://travis-ci.org/antonmedv/expr)
[](https://goreportcard.com/report/github.com/antonmedv/expr)
[](https://godoc.org/github.com/antonmedv/expr)
**Expr** package provides an engine that can compile and evaluate expressions.
An expression is a one-liner that returns a value (mostly, but not limited to, booleans).
It is designed for simplicity, speed and safety.
The purpose of the package is to allow users to use expressions inside configuration for more complex logic.
It is a perfect candidate for the foundation of a _business rule engine_.
The idea is to let configure things in a dynamic way without recompile of a program:
```coffeescript
# Get the special price if
user.Group in ["good_customers", "collaborator"]
# Promote article to the homepage when
len(article.Comments) > 100 and article.Category not in ["misc"]
# Send an alert when
product.Stock < 15
```
## Features
* Seamless integration with Go (no need to redefine types)
* Static typing ([example](https://godoc.org/github.com/antonmedv/expr#example-Env)).
```go
out, err := expr.Compile(`name + age`)
// err: invalid operation + (mismatched types string and int)
// | name + age
// | .....^
```
* User-friendly error messages.
* Reasonable set of basic operators.
* Builtins `all`, `none`, `any`, `one`, `filter`, `map`.
```coffeescript
all(Tweets, {.Size <= 280})
```
* Fast ([benchmarks](https://github.com/antonmedv/golang-expression-evaluation-comparison#readme)): uses bytecode virtual machine and optimizing compiler.
## Install
```
go get github.com/antonmedv/expr
```
## Documentation
* See [Getting Started](docs/Getting-Started.md) page for developer documentation.
* See [Language Definition](docs/Language-Definition.md) page to learn the syntax.
## Expr Code Editor
Also, I have an embeddable code editor written in JavaScript which allows editing expressions with syntax highlighting and autocomplete based on your types declaration.
[Learn more →](https://antonmedv.github.io/expr/)
## Examples
[Play Online](https://play.golang.org/p/z7T8ytJ1T1d)
```go
package main
import (
"fmt"
"github.com/antonmedv/expr"
)
func main() {
env := map[string]interface{}{
"greet": "Hello, %v!",
"names": []string{"world", "you"},
"sprintf": fmt.Sprintf,
}
code := `sprintf(greet, names[0])`
program, err := expr.Compile(code, expr.Env(env))
if err != nil {
panic(err)
}
output, err := expr.Run(program, env)
if err != nil {
panic(err)
}
fmt.Println(output)
}
```
[Play Online](https://play.golang.org/p/4S4brsIvU4i)
```go
package main
import (
"fmt"
"github.com/antonmedv/expr"
)
type Tweet struct {
Len int
}
type Env struct {
Tweets []Tweet
}
func main() {
code := `all(Tweets, {.Len <= 240})`
program, err := expr.Compile(code, expr.Env(Env{}))
if err != nil {
panic(err)
}
env := Env{
Tweets: []Tweet{{42}, {98}, {69}},
}
output, err := expr.Run(program, env)
if err != nil {
panic(err)
}
fmt.Println(output)
}
```
## Contributing
**Expr** consist of a few packages for parsing source code to AST, type checking AST, compiling to bytecode and VM for running bytecode program.
Also expr provides powerful tool [exe](cmd/exe) for debugging. It has interactive terminal debugger for our bytecode virtual machine.
## Who is using Expr?

Aviasales are actively using Expr for different parts of the search engine.
|

Mystery Minds uses Expr to allow easy yet powerful customization of its matching algorithm.
|
[Add you company too](https://github.com/antonmedv/expr/edit/master/README.md)
## License
[MIT](LICENSE)
expr-1.8.9/ast/ 0000775 0000000 0000000 00000000000 13726232776 0013315 5 ustar 00root root 0000000 0000000 expr-1.8.9/ast/node.go 0000664 0000000 0000000 00000003704 13726232776 0014575 0 ustar 00root root 0000000 0000000 package ast
import (
"reflect"
"regexp"
"github.com/antonmedv/expr/file"
)
// Node represents items of abstract syntax tree.
type Node interface {
Location() file.Location
SetLocation(file.Location)
Type() reflect.Type
SetType(reflect.Type)
}
func Patch(node *Node, newNode Node) {
newNode.SetType((*node).Type())
newNode.SetLocation((*node).Location())
*node = newNode
}
type base struct {
loc file.Location
nodeType reflect.Type
}
func (n *base) Location() file.Location {
return n.loc
}
func (n *base) SetLocation(loc file.Location) {
n.loc = loc
}
func (n *base) Type() reflect.Type {
return n.nodeType
}
func (n *base) SetType(t reflect.Type) {
n.nodeType = t
}
type NilNode struct {
base
}
type IdentifierNode struct {
base
Value string
}
type IntegerNode struct {
base
Value int
}
type FloatNode struct {
base
Value float64
}
type BoolNode struct {
base
Value bool
}
type StringNode struct {
base
Value string
}
type ConstantNode struct {
base
Value interface{}
}
type UnaryNode struct {
base
Operator string
Node Node
}
type BinaryNode struct {
base
Operator string
Left Node
Right Node
}
type MatchesNode struct {
base
Regexp *regexp.Regexp
Left Node
Right Node
}
type PropertyNode struct {
base
Node Node
Property string
}
type IndexNode struct {
base
Node Node
Index Node
}
type SliceNode struct {
base
Node Node
From Node
To Node
}
type MethodNode struct {
base
Node Node
Method string
Arguments []Node
}
type FunctionNode struct {
base
Name string
Arguments []Node
Fast bool
}
type BuiltinNode struct {
base
Name string
Arguments []Node
}
type ClosureNode struct {
base
Node Node
}
type PointerNode struct {
base
}
type ConditionalNode struct {
base
Cond Node
Exp1 Node
Exp2 Node
}
type ArrayNode struct {
base
Nodes []Node
}
type MapNode struct {
base
Pairs []Node
}
type PairNode struct {
base
Key Node
Value Node
}
expr-1.8.9/ast/print.go 0000664 0000000 0000000 00000002163 13726232776 0015002 0 ustar 00root root 0000000 0000000 package ast
import (
"fmt"
"reflect"
"regexp"
)
func Dump(node Node) string {
return dump(reflect.ValueOf(node), "")
}
func dump(v reflect.Value, ident string) string {
if !v.IsValid() {
return "nil"
}
t := v.Type()
switch t.Kind() {
case reflect.Struct:
out := t.Name() + "{\n"
for i := 0; i < t.NumField(); i++ {
f := t.Field(i)
if isPrivate(f.Name) {
continue
}
s := v.Field(i)
out += fmt.Sprintf("%v%v: %v,\n", ident+"\t", f.Name, dump(s, ident+"\t"))
}
return out + ident + "}"
case reflect.Slice:
if v.Len() == 0 {
return "[]"
}
out := "[\n"
for i := 0; i < v.Len(); i++ {
s := v.Index(i)
out += fmt.Sprintf("%v%v,", ident+"\t", dump(s, ident+"\t"))
if i+1 < v.Len() {
out += "\n"
}
}
return out + "\n" + ident + "]"
case reflect.Ptr:
return dump(v.Elem(), ident)
case reflect.Interface:
return dump(reflect.ValueOf(v.Interface()), ident)
case reflect.String:
return fmt.Sprintf("%q", v)
default:
return fmt.Sprintf("%v", v)
}
}
var isCapital = regexp.MustCompile("^[A-Z]")
func isPrivate(s string) bool {
return !isCapital.Match([]byte(s))
}
expr-1.8.9/ast/visitor.go 0000664 0000000 0000000 00000003660 13726232776 0015350 0 ustar 00root root 0000000 0000000 package ast
import "fmt"
type Visitor interface {
Enter(node *Node)
Exit(node *Node)
}
type walker struct {
visitor Visitor
}
func Walk(node *Node, visitor Visitor) {
w := walker{
visitor: visitor,
}
w.walk(node)
}
func (w *walker) walk(node *Node) {
w.visitor.Enter(node)
switch n := (*node).(type) {
case *NilNode:
w.visitor.Exit(node)
case *IdentifierNode:
w.visitor.Exit(node)
case *IntegerNode:
w.visitor.Exit(node)
case *FloatNode:
w.visitor.Exit(node)
case *BoolNode:
w.visitor.Exit(node)
case *StringNode:
w.visitor.Exit(node)
case *ConstantNode:
w.visitor.Exit(node)
case *UnaryNode:
w.walk(&n.Node)
w.visitor.Exit(node)
case *BinaryNode:
w.walk(&n.Left)
w.walk(&n.Right)
w.visitor.Exit(node)
case *MatchesNode:
w.walk(&n.Left)
w.walk(&n.Right)
w.visitor.Exit(node)
case *PropertyNode:
w.walk(&n.Node)
w.visitor.Exit(node)
case *IndexNode:
w.walk(&n.Node)
w.walk(&n.Index)
w.visitor.Exit(node)
case *SliceNode:
if n.From != nil {
w.walk(&n.From)
}
if n.To != nil {
w.walk(&n.To)
}
w.visitor.Exit(node)
case *MethodNode:
w.walk(&n.Node)
for i := range n.Arguments {
w.walk(&n.Arguments[i])
}
w.visitor.Exit(node)
case *FunctionNode:
for i := range n.Arguments {
w.walk(&n.Arguments[i])
}
w.visitor.Exit(node)
case *BuiltinNode:
for i := range n.Arguments {
w.walk(&n.Arguments[i])
}
w.visitor.Exit(node)
case *ClosureNode:
w.walk(&n.Node)
w.visitor.Exit(node)
case *PointerNode:
w.visitor.Exit(node)
case *ConditionalNode:
w.walk(&n.Cond)
w.walk(&n.Exp1)
w.walk(&n.Exp2)
w.visitor.Exit(node)
case *ArrayNode:
for i := range n.Nodes {
w.walk(&n.Nodes[i])
}
w.visitor.Exit(node)
case *MapNode:
for i := range n.Pairs {
w.walk(&n.Pairs[i])
}
w.visitor.Exit(node)
case *PairNode:
w.walk(&n.Key)
w.walk(&n.Value)
w.visitor.Exit(node)
default:
panic(fmt.Sprintf("undefined node type (%T)", node))
}
}
expr-1.8.9/ast/visitor_test.go 0000664 0000000 0000000 00000002273 13726232776 0016406 0 ustar 00root root 0000000 0000000 package ast_test
import (
"testing"
"github.com/antonmedv/expr/ast"
"github.com/stretchr/testify/assert"
)
type visitor struct {
identifiers []string
}
func (v *visitor) Enter(node *ast.Node) {}
func (v *visitor) Exit(node *ast.Node) {
if n, ok := (*node).(*ast.IdentifierNode); ok {
v.identifiers = append(v.identifiers, n.Value)
}
}
func TestWalk(t *testing.T) {
var node ast.Node
node = &ast.BinaryNode{
Operator: "+",
Left: &ast.IdentifierNode{Value: "foo"},
Right: &ast.IdentifierNode{Value: "bar"},
}
visitor := &visitor{}
ast.Walk(&node, visitor)
assert.Equal(t, []string{"foo", "bar"}, visitor.identifiers)
}
type patcher struct{}
func (p *patcher) Enter(node *ast.Node) {
if _, ok := (*node).(*ast.IdentifierNode); ok {
*node = &ast.NilNode{}
}
}
func (p *patcher) Exit(node *ast.Node) {}
func TestWalk_patch(t *testing.T) {
var node ast.Node
node = &ast.BinaryNode{
Operator: "+",
Left: &ast.IdentifierNode{Value: "foo"},
Right: &ast.IdentifierNode{Value: "bar"},
}
patcher := &patcher{}
ast.Walk(&node, patcher)
assert.IsType(t, &ast.NilNode{}, node.(*ast.BinaryNode).Left)
assert.IsType(t, &ast.NilNode{}, node.(*ast.BinaryNode).Right)
}
expr-1.8.9/bench_test.go 0000664 0000000 0000000 00000305335 13726232776 0015204 0 ustar 00root root 0000000 0000000 package expr_test
import (
"testing"
"github.com/antonmedv/expr"
"github.com/antonmedv/expr/vm"
)
func Benchmark_expr(b *testing.B) {
params := make(map[string]interface{})
params["Origin"] = "MOW"
params["Country"] = "RU"
params["Adults"] = 1
params["Value"] = 100
program, err := expr.Compile(`(Origin == "MOW" || Country == "RU") && (Value >= 100 || Adults == 1)`, expr.Env(params))
if err != nil {
b.Fatal(err)
}
var out interface{}
b.ResetTimer()
for n := 0; n < b.N; n++ {
out, err = vm.Run(program, params)
}
b.StopTimer()
if err != nil {
b.Fatal(err)
}
if !out.(bool) {
b.Fail()
}
}
func Benchmark_expr_reuseVm(b *testing.B) {
params := make(map[string]interface{})
params["Origin"] = "MOW"
params["Country"] = "RU"
params["Adults"] = 1
params["Value"] = 100
program, err := expr.Compile(`(Origin == "MOW" || Country == "RU") && (Value >= 100 || Adults == 1)`, expr.Env(params))
if err != nil {
b.Fatal(err)
}
var out interface{}
v := vm.VM{}
b.ResetTimer()
for n := 0; n < b.N; n++ {
out, err = v.Run(program, params)
}
b.StopTimer()
if err != nil {
b.Fatal(err)
}
if !out.(bool) {
b.Fail()
}
}
func Benchmark_filter(b *testing.B) {
params := make(map[string]interface{})
params["max"] = 50
program, err := expr.Compile(`filter(1..100, {# > max})`, expr.Env(params))
if err != nil {
b.Fatal(err)
}
for n := 0; n < b.N; n++ {
_, err = vm.Run(program, params)
}
if err != nil {
b.Fatal(err)
}
}
func Benchmark_access(b *testing.B) {
type Price struct {
Value int
}
type Env struct {
Price Price
}
program, err := expr.Compile(`Price.Value > 0`, expr.Env(Env{}))
if err != nil {
b.Fatal(err)
}
env := Env{Price: Price{Value: 1}}
for n := 0; n < b.N; n++ {
_, err = vm.Run(program, env)
}
if err != nil {
b.Fatal(err)
}
}
func Benchmark_accessMap(b *testing.B) {
type Price struct {
Value int
}
env := map[string]interface{}{
"price": Price{Value: 1},
}
program, err := expr.Compile(`price.Value > 0`, expr.Env(env))
if err != nil {
b.Fatal(err)
}
for n := 0; n < b.N; n++ {
_, err = vm.Run(program, env)
}
if err != nil {
b.Fatal(err)
}
}
func Benchmark_call(b *testing.B) {
type Env struct {
Fn func(string, string, string) bool
}
program, err := expr.Compile(`Fn("a", "b", "ab")`, expr.Env(Env{}))
if err != nil {
b.Fatal(err)
}
env := Env{
Fn: func(s1, s2, s3 string) bool {
return s1+s2 == s3
},
}
for n := 0; n < b.N; n++ {
_, err = vm.Run(program, env)
}
if err != nil {
b.Fatal(err)
}
}
func Benchmark_callFast(b *testing.B) {
type Env struct {
Fn func(...interface{}) interface{}
}
program, err := expr.Compile(`Fn("a", "b", "ab")`, expr.Env(Env{}))
if err != nil {
b.Fatal(err)
}
env := Env{
Fn: func(s ...interface{}) interface{} {
return s[0].(string)+s[1].(string) == s[2].(string)
},
}
for n := 0; n < b.N; n++ {
_, err = vm.Run(program, env)
}
if err != nil {
b.Fatal(err)
}
}
func Benchmark_callConstExpr(b *testing.B) {
env := map[string]interface{}{
"Fn": func(s ...interface{}) interface{} { return s[0].(string)+s[1].(string) == s[2].(string) },
}
program, err := expr.Compile(`Fn("a", "b", "ab")`, expr.Env(env), expr.ConstExpr("Fn"))
if err != nil {
b.Fatal(err)
}
var out interface{}
for n := 0; n < b.N; n++ {
out, err = vm.Run(program, env)
}
if err != nil {
b.Fatal(err)
}
if !out.(bool) {
b.Fail()
}
}
func Benchmark_largeStructAccess(b *testing.B) {
type Env struct {
Data [1024 * 1024 * 10]byte
Field int
}
program, err := expr.Compile(`Field > 0 && Field > 1 && Field < 20`, expr.Env(Env{}))
if err != nil {
b.Fatal(err)
}
env := Env{Field: 21}
for n := 0; n < b.N; n++ {
_, err = vm.Run(program, &env)
}
if err != nil {
b.Fatal(err)
}
}
func Benchmark_largeNestedStructAccess(b *testing.B) {
type Env struct {
Inner struct {
Data [1024 * 1024 * 10]byte
Field int
}
}
program, err := expr.Compile(`Inner.Field > 0 && Inner.Field > 1 && Inner.Field < 20`, expr.Env(Env{}))
if err != nil {
b.Fatal(err)
}
env := Env{}
env.Inner.Field = 21
for n := 0; n < b.N; n++ {
_, err = vm.Run(program, &env)
}
if err != nil {
b.Fatal(err)
}
}
func Benchmark_largeNestedArrayAccess(b *testing.B) {
type Env struct {
Data [1][1024 * 1024 * 10]byte
}
program, err := expr.Compile(`Data[0][0] > 0`, expr.Env(Env{}))
if err != nil {
b.Fatal(err)
}
env := Env{}
for n := 0; n < b.N; n++ {
_, err = vm.Run(program, &env)
}
if err != nil {
b.Fatal(err)
}
}
func createEnv() interface{} {
type DirectFlightsDays struct {
Start string
Days string
}
type RouteSegment struct {
Origin string
OriginName string
Destination string
DestinationName string
Date string
OriginCountry string
DestinationCountry string
TranslatedOrigin string
TranslatedDestination string
UserOrigin string
UserDestination string
DirectFlightsDays *DirectFlightsDays
}
type Passengers struct {
Adults uint32
Children uint32
Infants uint32
}
type UserAgentFeatures struct {
Assisted bool
TopPlacement bool
TourTickets bool
}
type SearchParamsEnv struct {
Segments []*RouteSegment
OriginCountry string
DestinationCountry string
SearchDepth int
Passengers *Passengers
TripClass string
UserIP string
KnowEnglish bool
Market string
Marker string
CleanMarker string
Locale string
ReferrerHost string
CountryCode string
CurrencyCode string
IsOpenJaw bool
Os string
OsVersion string
AppVersion string
IsAffiliate bool
InitializedAt int64
Random float32
TravelPayoutsAPI bool
Features *UserAgentFeatures
GateID int32
UserAgentDevice string
UserAgentType string
IsDesktop bool
IsMobile bool
}
type Env struct {
SearchParamsEnv
}
return Env{
SearchParamsEnv: SearchParamsEnv{
Segments: []*RouteSegment{
{
Origin: "VOG",
Destination: "SHJ",
},
{
Origin: "SHJ",
Destination: "VOG",
},
},
OriginCountry: "RU",
DestinationCountry: "RU",
SearchDepth: 44,
Passengers: &Passengers{1, 0, 0},
TripClass: "Y",
UserIP: "::1",
KnowEnglish: true,
Market: "ru",
Marker: "123456.direct",
CleanMarker: "123456",
Locale: "ru",
ReferrerHost: "www.aviasales.ru",
CountryCode: "",
CurrencyCode: "usd",
IsOpenJaw: false,
Os: "",
OsVersion: "",
AppVersion: "",
IsAffiliate: true,
InitializedAt: 1570788719,
Random: 0.13497187,
TravelPayoutsAPI: false,
Features: &UserAgentFeatures{},
GateID: 421,
UserAgentDevice: "DESKTOP",
UserAgentType: "WEB",
IsDesktop: true,
IsMobile: false,
},
}
}
func Benchmark_realWorld(b *testing.B) {
env := createEnv()
expression := `(UserAgentDevice == 'DESKTOP') and ((OriginCountry == 'RU' or DestinationCountry == 'RU') and Market in ['ru', 'kz','by','uz','ua','az','am'])`
program, err := expr.Compile(expression, expr.Env(env))
if err != nil {
b.Fatal(err)
}
var out interface{}
for n := 0; n < b.N; n++ {
out, err = vm.Run(program, env)
}
if err != nil {
b.Fatal(err)
}
if !out.(bool) {
b.Fail()
}
}
func Benchmark_realWorld_reuseVm(b *testing.B) {
env := createEnv()
expression := `(UserAgentDevice == 'DESKTOP') and ((OriginCountry == 'RU' or DestinationCountry == 'RU') and Market in ['ru', 'kz','by','uz','ua','az','am'])`
program, err := expr.Compile(expression, expr.Env(env))
if err != nil {
b.Fatal(err)
}
var out interface{}
v := vm.VM{}
for n := 0; n < b.N; n++ {
out, err = v.Run(program, env)
}
if err != nil {
b.Fatal(err)
}
if !out.(bool) {
b.Fail()
}
}
func Benchmark_realWorldInsane(b *testing.B) {
env := createEnv()
expression := `(UserAgentDevice == 'DESKTOP') and (Segments[0].Origin in ['HKT','GOJ'] and Segments[0].Destination in ['HKT','GOJ'] or Segments[0].Origin in ['SKG','GOJ'] and Segments[0].Destination in ['SKG','GOJ'] or Segments[0].Origin in ['SSH','SVX'] and Segments[0].Destination in ['SSH','SVX'] or Segments[0].Origin in ['AYT','LED'] and Segments[0].Destination in ['AYT','LED'] or Segments[0].Origin in ['PUJ','KRR'] and Segments[0].Destination in ['PUJ','KRR'] or Segments[0].Origin in ['USM','CEK'] and Segments[0].Destination in ['USM','CEK'] or Segments[0].Origin in ['SHJ','LED'] and Segments[0].Destination in ['SHJ','LED'] or Segments[0].Origin in ['MOW','PRG'] and Segments[0].Destination in ['MOW','PRG'] or Segments[0].Origin in ['BKK','NOZ'] and Segments[0].Destination in ['BKK','NOZ'] or Segments[0].Origin in ['NHA','GOJ'] and Segments[0].Destination in ['NHA','GOJ'] or Segments[0].Origin in ['HRG','VOG'] and Segments[0].Destination in ['HRG','VOG'] or Segments[0].Origin in ['CFU','MSQ'] and Segments[0].Destination in ['CFU','MSQ'] or Segments[0].Origin in ['UFA','PUJ'] and Segments[0].Destination in ['UFA','PUJ'] or Segments[0].Origin in ['OMS','PUJ'] and Segments[0].Destination in ['OMS','PUJ'] or Segments[0].Origin in ['SKG','MSQ'] and Segments[0].Destination in ['SKG','MSQ'] or Segments[0].Origin in ['SSH','VOZ'] and Segments[0].Destination in ['SSH','VOZ'] or Segments[0].Origin in ['SSH','EGO'] and Segments[0].Destination in ['SSH','EGO'] or Segments[0].Origin in ['UUS','NHA'] and Segments[0].Destination in ['UUS','NHA'] or Segments[0].Origin in ['PUJ','MCX'] and Segments[0].Destination in ['PUJ','MCX'] or Segments[0].Origin in ['NHA','VVO'] and Segments[0].Destination in ['NHA','VVO'] or Segments[0].Origin in ['SKD','MOW'] and Segments[0].Destination in ['SKD','MOW'] or Segments[0].Origin in ['REN','NHA'] and Segments[0].Destination in ['REN','NHA'] or Segments[0].Origin in ['ASF','VRA'] and Segments[0].Destination in ['ASF','VRA'] or Segments[0].Origin in ['YKS','VRA'] and Segments[0].Destination in ['YKS','VRA'] or Segments[0].Origin in ['MOW','RIX'] and Segments[0].Destination in ['MOW','RIX'] or Segments[0].Origin in ['HER','IEV'] and Segments[0].Destination in ['HER','IEV'] or Segments[0].Origin in ['HRG','EGO'] and Segments[0].Destination in ['HRG','EGO'] or Segments[0].Origin in ['MOW','ATH'] and Segments[0].Destination in ['MOW','ATH'] or Segments[0].Origin in ['EGO','SSH'] and Segments[0].Destination in ['EGO','SSH'] or Segments[0].Origin in ['CEK','CUN'] and Segments[0].Destination in ['CEK','CUN'] or Segments[0].Origin in ['VAR','MOW'] and Segments[0].Destination in ['VAR','MOW'] or Segments[0].Origin in ['ASF','NHA'] and Segments[0].Destination in ['ASF','NHA'] or Segments[0].Origin in ['SKG','OVB'] and Segments[0].Destination in ['SKG','OVB'] or Segments[0].Origin in ['CUN','VOZ'] and Segments[0].Destination in ['CUN','VOZ'] or Segments[0].Origin in ['HRG','OVB'] and Segments[0].Destination in ['HRG','OVB'] or Segments[0].Origin in ['LED','VAR'] and Segments[0].Destination in ['LED','VAR'] or Segments[0].Origin in ['OMS','CUN'] and Segments[0].Destination in ['OMS','CUN'] or Segments[0].Origin in ['PUJ','NOZ'] and Segments[0].Destination in ['PUJ','NOZ'] or Segments[0].Origin in ['CUN','OMS'] and Segments[0].Destination in ['CUN','OMS'] or Segments[0].Origin in ['BAX','NHA'] and Segments[0].Destination in ['BAX','NHA'] or Segments[0].Origin in ['TDX','TJM'] and Segments[0].Destination in ['TDX','TJM'] or Segments[0].Origin in ['BKK','YKS'] and Segments[0].Destination in ['BKK','YKS'] or Segments[0].Origin in ['PUJ','MRV'] and Segments[0].Destination in ['PUJ','MRV'] or Segments[0].Origin in ['KUF','MOW'] and Segments[0].Destination in ['KUF','MOW'] or Segments[0].Origin in ['NHA','YKS'] and Segments[0].Destination in ['NHA','YKS'] or Segments[0].Origin in ['UFA','CUN'] and Segments[0].Destination in ['UFA','CUN'] or Segments[0].Origin in ['MIR','MOW'] and Segments[0].Destination in ['MIR','MOW'] or Segments[0].Origin in ['OVB','PUJ'] and Segments[0].Destination in ['OVB','PUJ'] or Segments[0].Origin in ['SGN','KJA'] and Segments[0].Destination in ['SGN','KJA'] or Segments[0].Origin in ['UTP','CEK'] and Segments[0].Destination in ['UTP','CEK'] or Segments[0].Origin in ['SKG','IEV'] and Segments[0].Destination in ['SKG','IEV'] or Segments[0].Origin in ['PKC','MOW'] and Segments[0].Destination in ['PKC','MOW'] or Segments[0].Origin in ['NHA','OGZ'] and Segments[0].Destination in ['NHA','OGZ'] or Segments[0].Origin in ['USM','UFA'] and Segments[0].Destination in ['USM','UFA'] or Segments[0].Origin in ['KGD','VRA'] and Segments[0].Destination in ['KGD','VRA'] or Segments[0].Origin in ['TDX','KZN'] and Segments[0].Destination in ['TDX','KZN'] or Segments[0].Origin in ['KRR','CUN'] and Segments[0].Destination in ['KRR','CUN'] or Segments[0].Origin in ['DXB','PEE'] and Segments[0].Destination in ['DXB','PEE'] or Segments[0].Origin in ['AER','KUF'] and Segments[0].Destination in ['AER','KUF'] or Segments[0].Origin in ['REN','SSH'] and Segments[0].Destination in ['REN','SSH'] or Segments[0].Origin in ['HKT','NJC'] and Segments[0].Destination in ['HKT','NJC'] or Segments[0].Origin in ['AER','CUN'] and Segments[0].Destination in ['AER','CUN'] or Segments[0].Origin in ['ETH','SVX'] and Segments[0].Destination in ['ETH','SVX'] or Segments[0].Origin in ['SSH','CEK'] and Segments[0].Destination in ['SSH','CEK'] or Segments[0].Origin in ['BKK','UFA'] and Segments[0].Destination in ['BKK','UFA'] or Segments[0].Origin in ['SVX','SKG'] and Segments[0].Destination in ['SVX','SKG'] or Segments[0].Origin in ['BKK','VOG'] and Segments[0].Destination in ['BKK','VOG'] or Segments[0].Origin in ['SKG','MOW'] and Segments[0].Destination in ['SKG','MOW'] or Segments[0].Origin in ['NHA','NOZ'] and Segments[0].Destination in ['NHA','NOZ'] or Segments[0].Origin in ['YKS','OVB'] and Segments[0].Destination in ['YKS','OVB'] or Segments[0].Origin in ['UFA','VRA'] and Segments[0].Destination in ['UFA','VRA'] or Segments[0].Origin in ['MOW','TCI'] and Segments[0].Destination in ['MOW','TCI'] or Segments[0].Origin in ['ASF','PUJ'] and Segments[0].Destination in ['ASF','PUJ'] or Segments[0].Origin in ['GOJ','CUN'] and Segments[0].Destination in ['GOJ','CUN'] or Segments[0].Origin in ['ASF','CUN'] and Segments[0].Destination in ['ASF','CUN'] or Segments[0].Origin in ['SGN','CEK'] and Segments[0].Destination in ['SGN','CEK'] or Segments[0].Origin in ['TJM','SSH'] and Segments[0].Destination in ['TJM','SSH'] or Segments[0].Origin in ['UTP','KZN'] and Segments[0].Destination in ['UTP','KZN'] or Segments[0].Origin in ['HRG','REN'] and Segments[0].Destination in ['HRG','REN'] or Segments[0].Origin in ['HKT','KJA'] and Segments[0].Destination in ['HKT','KJA'] or Segments[0].Origin in ['BEG','MOW'] and Segments[0].Destination in ['BEG','MOW'] or Segments[0].Origin in ['OMS','SSH'] and Segments[0].Destination in ['OMS','SSH'] or Segments[0].Origin in ['MSQ','SKG'] and Segments[0].Destination in ['MSQ','SKG'] or Segments[0].Origin in ['BKK','HTA'] and Segments[0].Destination in ['BKK','HTA'] or Segments[0].Origin in ['TDX','PEE'] and Segments[0].Destination in ['TDX','PEE'] or Segments[0].Origin in ['SKG','MRV'] and Segments[0].Destination in ['SKG','MRV'] or Segments[0].Origin in ['SGN','OVB'] and Segments[0].Destination in ['SGN','OVB'] or Segments[0].Origin in ['SVX','HRG'] and Segments[0].Destination in ['SVX','HRG'] or Segments[0].Origin in ['HKT','AER'] and Segments[0].Destination in ['HKT','AER'] or Segments[0].Origin in ['CEE','CUN'] and Segments[0].Destination in ['CEE','CUN'] or Segments[0].Origin in ['NHA','SVX'] and Segments[0].Destination in ['NHA','SVX'] or Segments[0].Origin in ['CUN','GOJ'] and Segments[0].Destination in ['CUN','GOJ'] or Segments[0].Origin in ['MOW','OGZ'] and Segments[0].Destination in ['MOW','OGZ'] or Segments[0].Origin in ['SCW','SSH'] and Segments[0].Destination in ['SCW','SSH'] or Segments[0].Origin in ['PUJ','PEE'] and Segments[0].Destination in ['PUJ','PEE'] or Segments[0].Origin in ['CUN','ASF'] and Segments[0].Destination in ['CUN','ASF'] or Segments[0].Origin in ['AQJ','SVX'] and Segments[0].Destination in ['AQJ','SVX'] or Segments[0].Origin in ['VRA','IKT'] and Segments[0].Destination in ['VRA','IKT'] or Segments[0].Origin in ['SHJ','SVX'] and Segments[0].Destination in ['SHJ','SVX'] or Segments[0].Origin in ['NBC','VRA'] and Segments[0].Destination in ['NBC','VRA'] or Segments[0].Origin in ['HTA','CUN'] and Segments[0].Destination in ['HTA','CUN'] or Segments[0].Origin in ['MOW','TOF'] and Segments[0].Destination in ['MOW','TOF'] or Segments[0].Origin in ['NJC','CUN'] and Segments[0].Destination in ['NJC','CUN'] or Segments[0].Origin in ['CUN','NOZ'] and Segments[0].Destination in ['CUN','NOZ'] or Segments[0].Origin in ['BTK','NHA'] and Segments[0].Destination in ['BTK','NHA'] or Segments[0].Origin in ['PUJ','OMS'] and Segments[0].Destination in ['PUJ','OMS'] or Segments[0].Origin in ['HTA','OVB'] and Segments[0].Destination in ['HTA','OVB'] or Segments[0].Origin in ['AQJ','KZN'] and Segments[0].Destination in ['AQJ','KZN'] or Segments[0].Origin in ['DXB','VOZ'] and Segments[0].Destination in ['DXB','VOZ'] or Segments[0].Origin in ['NHA','PEE'] and Segments[0].Destination in ['NHA','PEE'] or Segments[0].Origin in ['HKT','OGZ'] and Segments[0].Destination in ['HKT','OGZ'] or Segments[0].Origin in ['KLV','MOW'] and Segments[0].Destination in ['KLV','MOW'] or Segments[0].Origin in ['MRV','SKG'] and Segments[0].Destination in ['MRV','SKG'] or Segments[0].Origin in ['SKG','LED'] and Segments[0].Destination in ['SKG','LED'] or Segments[0].Origin in ['AQJ','MOW'] and Segments[0].Destination in ['AQJ','MOW'] or Segments[0].Origin in ['MOW','NHA'] and Segments[0].Destination in ['MOW','NHA'] or Segments[0].Origin in ['ARH','HRG'] and Segments[0].Destination in ['ARH','HRG'] or Segments[0].Origin in ['SGN','AER'] and Segments[0].Destination in ['SGN','AER'] or Segments[0].Origin in ['VRA','MCX'] and Segments[0].Destination in ['VRA','MCX'] or Segments[0].Origin in ['BKK','OVB'] and Segments[0].Destination in ['BKK','OVB'] or Segments[0].Origin in ['AYT','UFA'] and Segments[0].Destination in ['AYT','UFA'] or Segments[0].Origin in ['SGN','NOZ'] and Segments[0].Destination in ['SGN','NOZ'] or Segments[0].Origin in ['SGN','NBC'] and Segments[0].Destination in ['SGN','NBC'] or Segments[0].Origin in ['MOW','BEG'] and Segments[0].Destination in ['MOW','BEG'] or Segments[0].Origin in ['TDX','BQS'] and Segments[0].Destination in ['TDX','BQS'] or Segments[0].Origin in ['KRR','NHA'] and Segments[0].Destination in ['KRR','NHA'] or Segments[0].Origin in ['NHA','SGC'] and Segments[0].Destination in ['NHA','SGC'] or Segments[0].Origin in ['NHA','UFA'] and Segments[0].Destination in ['NHA','UFA'] or Segments[0].Origin in ['NHA','ARH'] and Segments[0].Destination in ['NHA','ARH'] or Segments[0].Origin in ['EGO','VRA'] and Segments[0].Destination in ['EGO','VRA'] or Segments[0].Origin in ['BCN','MOW'] and Segments[0].Destination in ['BCN','MOW'] or Segments[0].Origin in ['TDX','ROV'] and Segments[0].Destination in ['TDX','ROV'] or Segments[0].Origin in ['TSN','MOW'] and Segments[0].Destination in ['TSN','MOW'] or Segments[0].Origin in ['GOJ','HRG'] and Segments[0].Destination in ['GOJ','HRG'] or Segments[0].Origin in ['BKK','KZN'] and Segments[0].Destination in ['BKK','KZN'] or Segments[0].Origin in ['NHA','ROV'] and Segments[0].Destination in ['NHA','ROV'] or Segments[0].Origin in ['DXB','KJA'] and Segments[0].Destination in ['DXB','KJA'] or Segments[0].Origin in ['PEE','AER'] and Segments[0].Destination in ['PEE','AER'] or Segments[0].Origin in ['DXB','CEK'] and Segments[0].Destination in ['DXB','CEK'] or Segments[0].Origin in ['PUJ','ASF'] and Segments[0].Destination in ['PUJ','ASF'] or Segments[0].Origin in ['KBV','OVB'] and Segments[0].Destination in ['KBV','OVB'] or Segments[0].Origin in ['MOW','EVN'] and Segments[0].Destination in ['MOW','EVN'] or Segments[0].Origin in ['IKT','CUN'] and Segments[0].Destination in ['IKT','CUN'] or Segments[0].Origin in ['KGD','HRG'] and Segments[0].Destination in ['KGD','HRG'] or Segments[0].Origin in ['KBV','PEE'] and Segments[0].Destination in ['KBV','PEE'] or Segments[0].Origin in ['VOG','VRA'] and Segments[0].Destination in ['VOG','VRA'] or Segments[0].Origin in ['MOW','HKT'] and Segments[0].Destination in ['MOW','HKT'] or Segments[0].Origin in ['NHA','ASF'] and Segments[0].Destination in ['NHA','ASF'] or Segments[0].Origin in ['LED','SVX'] and Segments[0].Destination in ['LED','SVX'] or Segments[0].Origin in ['AAQ','CUN'] and Segments[0].Destination in ['AAQ','CUN'] or Segments[0].Origin in ['BKK','KEJ'] and Segments[0].Destination in ['BKK','KEJ'] or Segments[0].Origin in ['BKK','BQS'] and Segments[0].Destination in ['BKK','BQS'] or Segments[0].Origin in ['DXB','IKT'] and Segments[0].Destination in ['DXB','IKT'] or Segments[0].Origin in ['SSH','TJM'] and Segments[0].Destination in ['SSH','TJM'] or Segments[0].Origin in ['PUJ','ROV'] and Segments[0].Destination in ['PUJ','ROV'] or Segments[0].Origin in ['AER','SVX'] and Segments[0].Destination in ['AER','SVX'] or Segments[0].Origin in ['UFA','ETH'] and Segments[0].Destination in ['UFA','ETH'] or Segments[0].Origin in ['BKK','KUF'] and Segments[0].Destination in ['BKK','KUF'] or Segments[0].Origin in ['BKK','VVO'] and Segments[0].Destination in ['BKK','VVO'] or Segments[0].Origin in ['HKT','OVB'] and Segments[0].Destination in ['HKT','OVB'] or Segments[0].Origin in ['ZTH','LED'] and Segments[0].Destination in ['ZTH','LED'] or Segments[0].Origin in ['KZN','NHA'] and Segments[0].Destination in ['KZN','NHA'] or Segments[0].Origin in ['VRA','BAX'] and Segments[0].Destination in ['VRA','BAX'] or Segments[0].Origin in ['RTW','NHA'] and Segments[0].Destination in ['RTW','NHA'] or Segments[0].Origin in ['SKG','DNK'] and Segments[0].Destination in ['SKG','DNK'] or Segments[0].Origin in ['SGN','VOG'] and Segments[0].Destination in ['SGN','VOG'] or Segments[0].Origin in ['KBV','VVO'] and Segments[0].Destination in ['KBV','VVO'] or Segments[0].Origin in ['IEV','CFU'] and Segments[0].Destination in ['IEV','CFU'] or Segments[0].Origin in ['PUJ','TOF'] and Segments[0].Destination in ['PUJ','TOF'] or Segments[0].Origin in ['HKT','KEJ'] and Segments[0].Destination in ['HKT','KEJ'] or Segments[0].Origin in ['PUJ','NJC'] and Segments[0].Destination in ['PUJ','NJC'] or Segments[0].Origin in ['PEE','CUN'] and Segments[0].Destination in ['PEE','CUN'] or Segments[0].Origin in ['HKT','TJM'] and Segments[0].Destination in ['HKT','TJM'] or Segments[0].Origin in ['ETH','KZN'] and Segments[0].Destination in ['ETH','KZN'] or Segments[0].Origin in ['MCX','CUN'] and Segments[0].Destination in ['MCX','CUN'] or Segments[0].Origin in ['HRG','KUF'] and Segments[0].Destination in ['HRG','KUF'] or Segments[0].Origin in ['VRA','VOG'] and Segments[0].Destination in ['VRA','VOG'] or Segments[0].Origin in ['SVX','CUN'] and Segments[0].Destination in ['SVX','CUN'] or Segments[0].Origin in ['VRA','EGO'] and Segments[0].Destination in ['VRA','EGO'] or Segments[0].Origin in ['ROV','CUN'] and Segments[0].Destination in ['ROV','CUN'] or Segments[0].Origin in ['KJA','VRA'] and Segments[0].Destination in ['KJA','VRA'] or Segments[0].Origin in ['VRA','PEE'] and Segments[0].Destination in ['VRA','PEE'] or Segments[0].Origin in ['MOW','SKD'] and Segments[0].Destination in ['MOW','SKD'] or Segments[0].Origin in ['POP','ROV'] and Segments[0].Destination in ['POP','ROV'] or Segments[0].Origin in ['AYT','KZN'] and Segments[0].Destination in ['AYT','KZN'] or Segments[0].Origin in ['ETH','REN'] and Segments[0].Destination in ['ETH','REN'] or Segments[0].Origin in ['ETH','LED'] and Segments[0].Destination in ['ETH','LED'] or Segments[0].Origin in ['CEK','ETH'] and Segments[0].Destination in ['CEK','ETH'] or Segments[0].Origin in ['NHA','VOZ'] and Segments[0].Destination in ['NHA','VOZ'] or Segments[0].Origin in ['SVX','AER'] and Segments[0].Destination in ['SVX','AER'] or Segments[0].Origin in ['FEG','MOW'] and Segments[0].Destination in ['FEG','MOW'] or Segments[0].Origin in ['VRA','KZN'] and Segments[0].Destination in ['VRA','KZN'] or Segments[0].Origin in ['USM','PEE'] and Segments[0].Destination in ['USM','PEE'] or Segments[0].Origin in ['VVO','MOW'] and Segments[0].Destination in ['VVO','MOW'] or Segments[0].Origin in ['SGN','KEJ'] and Segments[0].Destination in ['SGN','KEJ'] or Segments[0].Origin in ['DXB','AER'] and Segments[0].Destination in ['DXB','AER'] or Segments[0].Origin in ['MOW','VOG'] and Segments[0].Destination in ['MOW','VOG'] or Segments[0].Origin in ['SGN','YKS'] and Segments[0].Destination in ['SGN','YKS'] or Segments[0].Origin in ['VRA','NJC'] and Segments[0].Destination in ['VRA','NJC'] or Segments[0].Origin in ['VOG','PUJ'] and Segments[0].Destination in ['VOG','PUJ'] or Segments[0].Origin in ['HKT','MOW'] and Segments[0].Destination in ['HKT','MOW'] or Segments[0].Origin in ['VOG','SKG'] and Segments[0].Destination in ['VOG','SKG'] or Segments[0].Origin in ['OVB','YKS'] and Segments[0].Destination in ['OVB','YKS'] or Segments[0].Origin in ['SGC','SSH'] and Segments[0].Destination in ['SGC','SSH'] or Segments[0].Origin in ['VOZ','NHA'] and Segments[0].Destination in ['VOZ','NHA'] or Segments[0].Origin in ['CUN','NBC'] and Segments[0].Destination in ['CUN','NBC'] or Segments[0].Origin in ['KZN','SSH'] and Segments[0].Destination in ['KZN','SSH'] or Segments[0].Origin in ['HER','MOW'] and Segments[0].Destination in ['HER','MOW'] or Segments[0].Origin in ['TDX','UFA'] and Segments[0].Destination in ['TDX','UFA'] or Segments[0].Origin in ['KZN','ETH'] and Segments[0].Destination in ['KZN','ETH'] or Segments[0].Origin in ['ABA','CUN'] and Segments[0].Destination in ['ABA','CUN'] or Segments[0].Origin in ['PEE','NHA'] and Segments[0].Destination in ['PEE','NHA'] or Segments[0].Origin in ['CUN','TOF'] and Segments[0].Destination in ['CUN','TOF'] or Segments[0].Origin in ['TJM','HRG'] and Segments[0].Destination in ['TJM','HRG'] or Segments[0].Origin in ['EGO','HRG'] and Segments[0].Destination in ['EGO','HRG'] or Segments[0].Origin in ['GOJ','SSH'] and Segments[0].Destination in ['GOJ','SSH'] or Segments[0].Origin in ['HKT','HTA'] and Segments[0].Destination in ['HKT','HTA'] or Segments[0].Origin in ['MOW','ETH'] and Segments[0].Destination in ['MOW','ETH'] or Segments[0].Origin in ['OGZ','VRA'] and Segments[0].Destination in ['OGZ','VRA'] or Segments[0].Origin in ['HKT','NBC'] and Segments[0].Destination in ['HKT','NBC'] or Segments[0].Origin in ['GPA','MSQ'] and Segments[0].Destination in ['GPA','MSQ'] or Segments[0].Origin in ['SGN','TOF'] and Segments[0].Destination in ['SGN','TOF'] or Segments[0].Origin in ['HKT','MCX'] and Segments[0].Destination in ['HKT','MCX'] or Segments[0].Origin in ['KRR','VRA'] and Segments[0].Destination in ['KRR','VRA'] or Segments[0].Origin in ['ROV','PUJ'] and Segments[0].Destination in ['ROV','PUJ'] or Segments[0].Origin in ['CEE','VRA'] and Segments[0].Destination in ['CEE','VRA'] or Segments[0].Origin in ['TJM','NHA'] and Segments[0].Destination in ['TJM','NHA'] or Segments[0].Origin in ['RTW','CUN'] and Segments[0].Destination in ['RTW','CUN'] or Segments[0].Origin in ['AER','KZN'] and Segments[0].Destination in ['AER','KZN'] or Segments[0].Origin in ['MRV','ETH'] and Segments[0].Destination in ['MRV','ETH'] or Segments[0].Origin in ['SGN','VOZ'] and Segments[0].Destination in ['SGN','VOZ'] or Segments[0].Origin in ['USM','BQS'] and Segments[0].Destination in ['USM','BQS'] or Segments[0].Origin in ['USM','SGC'] and Segments[0].Destination in ['USM','SGC'] or Segments[0].Origin in ['HER','SVX'] and Segments[0].Destination in ['HER','SVX'] or Segments[0].Origin in ['DXB','KZN'] and Segments[0].Destination in ['DXB','KZN'] or Segments[0].Origin in ['TDX','KEJ'] and Segments[0].Destination in ['TDX','KEJ'] or Segments[0].Origin in ['HRG','SGC'] and Segments[0].Destination in ['HRG','SGC'] or Segments[0].Origin in ['SOF','LED'] and Segments[0].Destination in ['SOF','LED'] or Segments[0].Origin in ['DXB','UFA'] and Segments[0].Destination in ['DXB','UFA'] or Segments[0].Origin in ['EVN','MOW'] and Segments[0].Destination in ['EVN','MOW'] or Segments[0].Origin in ['HKT','LED'] and Segments[0].Destination in ['HKT','LED'] or Segments[0].Origin in ['SGN','NJC'] and Segments[0].Destination in ['SGN','NJC'] or Segments[0].Origin in ['SHJ','KUF'] and Segments[0].Destination in ['SHJ','KUF'] or Segments[0].Origin in ['AQJ','LED'] and Segments[0].Destination in ['AQJ','LED'] or Segments[0].Origin in ['HRG','GOJ'] and Segments[0].Destination in ['HRG','GOJ'] or Segments[0].Origin in ['PRG','LED'] and Segments[0].Destination in ['PRG','LED'] or Segments[0].Origin in ['NOZ','NHA'] and Segments[0].Destination in ['NOZ','NHA'] or Segments[0].Origin in ['ARH','SSH'] and Segments[0].Destination in ['ARH','SSH'] or Segments[0].Origin in ['SSH','REN'] and Segments[0].Destination in ['SSH','REN'] or Segments[0].Origin in ['AYT','GOJ'] and Segments[0].Destination in ['AYT','GOJ'] or Segments[0].Origin in ['ATH','MSQ'] and Segments[0].Destination in ['ATH','MSQ'] or Segments[0].Origin in ['MOW','VAR'] and Segments[0].Destination in ['MOW','VAR'] or Segments[0].Origin in ['HER','LED'] and Segments[0].Destination in ['HER','LED'] or Segments[0].Origin in ['SIP','KJA'] and Segments[0].Destination in ['SIP','KJA'] or Segments[0].Origin in ['TJM','CUN'] and Segments[0].Destination in ['TJM','CUN'] or Segments[0].Origin in ['PUJ','LED'] and Segments[0].Destination in ['PUJ','LED'] or Segments[0].Origin in ['BKK','SGC'] and Segments[0].Destination in ['BKK','SGC'] or Segments[0].Origin in ['PUJ','KEJ'] and Segments[0].Destination in ['PUJ','KEJ'] or Segments[0].Origin in ['BKK','KJA'] and Segments[0].Destination in ['BKK','KJA'] or Segments[0].Origin in ['DXB','VOG'] and Segments[0].Destination in ['DXB','VOG'] or Segments[0].Origin in ['PUJ','KJA'] and Segments[0].Destination in ['PUJ','KJA'] or Segments[0].Origin in ['RMI','MOW'] and Segments[0].Destination in ['RMI','MOW'] or Segments[0].Origin in ['USM','KEJ'] and Segments[0].Destination in ['USM','KEJ'] or Segments[0].Origin in ['MOW','RVN'] and Segments[0].Destination in ['MOW','RVN'] or Segments[0].Origin in ['VRA','AER'] and Segments[0].Destination in ['VRA','AER'] or Segments[0].Origin in ['SGN','VVO'] and Segments[0].Destination in ['SGN','VVO'] or Segments[0].Origin in ['SIP','MOW'] and Segments[0].Destination in ['SIP','MOW'] or Segments[0].Origin in ['ETH','MRV'] and Segments[0].Destination in ['ETH','MRV'] or Segments[0].Origin in ['VRA','MRV'] and Segments[0].Destination in ['VRA','MRV'] or Segments[0].Origin in ['ROV','MOW'] and Segments[0].Destination in ['ROV','MOW'] or Segments[0].Origin in ['KBV','TJM'] and Segments[0].Destination in ['KBV','TJM'] or Segments[0].Origin in ['PUJ','VOZ'] and Segments[0].Destination in ['PUJ','VOZ'] or Segments[0].Origin in ['LED','AER'] and Segments[0].Destination in ['LED','AER'] or Segments[0].Origin in ['AER','VRA'] and Segments[0].Destination in ['AER','VRA'] or Segments[0].Origin in ['CUN','SVX'] and Segments[0].Destination in ['CUN','SVX'] or Segments[0].Origin in ['HKT','ROV'] and Segments[0].Destination in ['HKT','ROV'] or Segments[0].Origin in ['KUF','NHA'] and Segments[0].Destination in ['KUF','NHA'] or Segments[0].Origin in ['KGD','SKG'] and Segments[0].Destination in ['KGD','SKG'] or Segments[0].Origin in ['DXB','YKS'] and Segments[0].Destination in ['DXB','YKS'] or Segments[0].Origin in ['AER','PEE'] and Segments[0].Destination in ['AER','PEE'] or Segments[0].Origin in ['ROV','CFU'] and Segments[0].Destination in ['ROV','CFU'] or Segments[0].Origin in ['VOG','CUN'] and Segments[0].Destination in ['VOG','CUN'] or Segments[0].Origin in ['PUJ','KZN'] and Segments[0].Destination in ['PUJ','KZN'] or Segments[0].Origin in ['MOW','SZG'] and Segments[0].Destination in ['MOW','SZG'] or Segments[0].Origin in ['GDX','MOW'] and Segments[0].Destination in ['GDX','MOW'] or Segments[0].Origin in ['HKT','VOG'] and Segments[0].Destination in ['HKT','VOG'] or Segments[0].Origin in ['BOJ','MOW'] and Segments[0].Destination in ['BOJ','MOW'] or Segments[0].Origin in ['OVB','HTA'] and Segments[0].Destination in ['OVB','HTA'] or Segments[0].Origin in ['BKK','EGO'] and Segments[0].Destination in ['BKK','EGO'] or Segments[0].Origin in ['ETH','KUF'] and Segments[0].Destination in ['ETH','KUF'] or Segments[0].Origin in ['HRG','ARH'] and Segments[0].Destination in ['HRG','ARH'] or Segments[0].Origin in ['MOW','KGD'] and Segments[0].Destination in ['MOW','KGD'] or Segments[0].Origin in ['HRG','CEK'] and Segments[0].Destination in ['HRG','CEK'] or Segments[0].Origin in ['LED','HER'] and Segments[0].Destination in ['LED','HER'] or Segments[0].Origin in ['USM','IKT'] and Segments[0].Destination in ['USM','IKT'] or Segments[0].Origin in ['CUN','TJM'] and Segments[0].Destination in ['CUN','TJM'] or Segments[0].Origin in ['NHA','UUS'] and Segments[0].Destination in ['NHA','UUS'] or Segments[0].Origin in ['NHA','KZN'] and Segments[0].Destination in ['NHA','KZN'] or Segments[0].Origin in ['NBC','HRG'] and Segments[0].Destination in ['NBC','HRG'] or Segments[0].Origin in ['SKG','SVX'] and Segments[0].Destination in ['SKG','SVX'] or Segments[0].Origin in ['HRG','UFA'] and Segments[0].Destination in ['HRG','UFA'] or Segments[0].Origin in ['TDX','MOW'] and Segments[0].Destination in ['TDX','MOW'] or Segments[0].Origin in ['LED','SKG'] and Segments[0].Destination in ['LED','SKG'] or Segments[0].Origin in ['SGN','SVX'] and Segments[0].Destination in ['SGN','SVX'] or Segments[0].Origin in ['CUN','AER'] and Segments[0].Destination in ['CUN','AER'] or Segments[0].Origin in ['MOW','KUT'] and Segments[0].Destination in ['MOW','KUT'] or Segments[0].Origin in ['VRN','KRR'] and Segments[0].Destination in ['VRN','KRR'] or Segments[0].Origin in ['MSQ','ATH'] and Segments[0].Destination in ['MSQ','ATH'] or Segments[0].Origin in ['PUJ','BAX'] and Segments[0].Destination in ['PUJ','BAX'] or Segments[0].Origin in ['KEJ','CUN'] and Segments[0].Destination in ['KEJ','CUN'] or Segments[0].Origin in ['KUF','PUJ'] and Segments[0].Destination in ['KUF','PUJ'] or Segments[0].Origin in ['VRA','KUF'] and Segments[0].Destination in ['VRA','KUF'] or Segments[0].Origin in ['LED','HRG'] and Segments[0].Destination in ['LED','HRG'] or Segments[0].Origin in ['BKK','ASF'] and Segments[0].Destination in ['BKK','ASF'] or Segments[0].Origin in ['IEV','HER'] and Segments[0].Destination in ['IEV','HER'] or Segments[0].Origin in ['SHJ','ROV'] and Segments[0].Destination in ['SHJ','ROV'] or Segments[0].Origin in ['KUT','MOW'] and Segments[0].Destination in ['KUT','MOW'] or Segments[0].Origin in ['HKT','KRR'] and Segments[0].Destination in ['HKT','KRR'] or Segments[0].Origin in ['AYT','MOW'] and Segments[0].Destination in ['AYT','MOW'] or Segments[0].Origin in ['VRA','MOW'] and Segments[0].Destination in ['VRA','MOW'] or Segments[0].Origin in ['SCW','PUJ'] and Segments[0].Destination in ['SCW','PUJ'] or Segments[0].Origin in ['MOW','TAS'] and Segments[0].Destination in ['MOW','TAS'] or Segments[0].Origin in ['IEV','SKG'] and Segments[0].Destination in ['IEV','SKG'] or Segments[0].Origin in ['LED','BOJ'] and Segments[0].Destination in ['LED','BOJ'] or Segments[0].Origin in ['HKT','SVX'] and Segments[0].Destination in ['HKT','SVX'] or Segments[0].Origin in ['BKK','SVX'] and Segments[0].Destination in ['BKK','SVX'] or Segments[0].Origin in ['SGN','MOW'] and Segments[0].Destination in ['SGN','MOW'] or Segments[0].Origin in ['SVX','ETH'] and Segments[0].Destination in ['SVX','ETH'] or Segments[0].Origin in ['SSH','PEE'] and Segments[0].Destination in ['SSH','PEE'] or Segments[0].Origin in ['NHA','KUF'] and Segments[0].Destination in ['NHA','KUF'] or Segments[0].Origin in ['SSH','KUF'] and Segments[0].Destination in ['SSH','KUF'] or Segments[0].Origin in ['DXB','MOW'] and Segments[0].Destination in ['DXB','MOW'] or Segments[0].Origin in ['PUJ','YKS'] and Segments[0].Destination in ['PUJ','YKS'] or Segments[0].Origin in ['SSH','ARH'] and Segments[0].Destination in ['SSH','ARH'] or Segments[0].Origin in ['AUH','MOW'] and Segments[0].Destination in ['AUH','MOW'] or Segments[0].Origin in ['UTP','IKT'] and Segments[0].Destination in ['UTP','IKT'] or Segments[0].Origin in ['KRR','SSH'] and Segments[0].Destination in ['KRR','SSH'] or Segments[0].Origin in ['HRG','KZN'] and Segments[0].Destination in ['HRG','KZN'] or Segments[0].Origin in ['BKK','ROV'] and Segments[0].Destination in ['BKK','ROV'] or Segments[0].Origin in ['CEK','PUJ'] and Segments[0].Destination in ['CEK','PUJ'] or Segments[0].Origin in ['SGN','KGD'] and Segments[0].Destination in ['SGN','KGD'] or Segments[0].Origin in ['KEJ','PUJ'] and Segments[0].Destination in ['KEJ','PUJ'] or Segments[0].Origin in ['HKT','SCW'] and Segments[0].Destination in ['HKT','SCW'] or Segments[0].Origin in ['BKK','KGD'] and Segments[0].Destination in ['BKK','KGD'] or Segments[0].Origin in ['HKT','SGC'] and Segments[0].Destination in ['HKT','SGC'] or Segments[0].Origin in ['REN','HRG'] and Segments[0].Destination in ['REN','HRG'] or Segments[0].Origin in ['SKG','TSE'] and Segments[0].Destination in ['SKG','TSE'] or Segments[0].Origin in ['BKK','PKC'] and Segments[0].Destination in ['BKK','PKC'] or Segments[0].Origin in ['VRA','KJA'] and Segments[0].Destination in ['VRA','KJA'] or Segments[0].Origin in ['SCW','CUN'] and Segments[0].Destination in ['SCW','CUN'] or Segments[0].Origin in ['SKG','KZN'] and Segments[0].Destination in ['SKG','KZN'] or Segments[0].Origin in ['MOW','GRV'] and Segments[0].Destination in ['MOW','GRV'] or Segments[0].Origin in ['HRG','NBC'] and Segments[0].Destination in ['HRG','NBC'] or Segments[0].Origin in ['SCW','VRA'] and Segments[0].Destination in ['SCW','VRA'] or Segments[0].Origin in ['UFA','HRG'] and Segments[0].Destination in ['UFA','HRG'] or Segments[0].Origin in ['EGO','CUN'] and Segments[0].Destination in ['EGO','CUN'] or Segments[0].Origin in ['KUF','HRG'] and Segments[0].Destination in ['KUF','HRG'] or Segments[0].Origin in ['CUN','ROV'] and Segments[0].Destination in ['CUN','ROV'] or Segments[0].Origin in ['KBV','KEJ'] and Segments[0].Destination in ['KBV','KEJ'] or Segments[0].Origin in ['NHA','IKT'] and Segments[0].Destination in ['NHA','IKT'] or Segments[0].Origin in ['SSH','KRR'] and Segments[0].Destination in ['SSH','KRR'] or Segments[0].Origin in ['CFU','MOW'] and Segments[0].Destination in ['CFU','MOW'] or Segments[0].Origin in ['MSQ','GPA'] and Segments[0].Destination in ['MSQ','GPA'] or Segments[0].Origin in ['ZTH','MOW'] and Segments[0].Destination in ['ZTH','MOW'] or Segments[0].Origin in ['AER','KJA'] and Segments[0].Destination in ['AER','KJA'] or Segments[0].Origin in ['MOW','CFU'] and Segments[0].Destination in ['MOW','CFU'] or Segments[0].Origin in ['BKK','SCW'] and Segments[0].Destination in ['BKK','SCW'] or Segments[0].Origin in ['PUJ','OGZ'] and Segments[0].Destination in ['PUJ','OGZ'] or Segments[0].Origin in ['AMM','MOW'] and Segments[0].Destination in ['AMM','MOW'] or Segments[0].Origin in ['OVB','TOF'] and Segments[0].Destination in ['OVB','TOF'] or Segments[0].Origin in ['SGN','KZN'] and Segments[0].Destination in ['SGN','KZN'] or Segments[0].Origin in ['VOG','AER'] and Segments[0].Destination in ['VOG','AER'] or Segments[0].Origin in ['VRA','SVX'] and Segments[0].Destination in ['VRA','SVX'] or Segments[0].Origin in ['DXB','SVX'] and Segments[0].Destination in ['DXB','SVX'] or Segments[0].Origin in ['HKT','BQS'] and Segments[0].Destination in ['HKT','BQS'] or Segments[0].Origin in ['PUJ','EGO'] and Segments[0].Destination in ['PUJ','EGO'] or Segments[0].Origin in ['DXB','LED'] and Segments[0].Destination in ['DXB','LED'] or Segments[0].Origin in ['ETH','MOW'] and Segments[0].Destination in ['ETH','MOW'] or Segments[0].Origin in ['MOW','KJA'] and Segments[0].Destination in ['MOW','KJA'] or Segments[0].Origin in ['IKT','MOW'] and Segments[0].Destination in ['IKT','MOW'] or Segments[0].Origin in ['KBV','ROV'] and Segments[0].Destination in ['KBV','ROV'] or Segments[0].Origin in ['BKK','REN'] and Segments[0].Destination in ['BKK','REN'] or Segments[0].Origin in ['HKT','PEE'] and Segments[0].Destination in ['HKT','PEE'] or Segments[0].Origin in ['SVX','VRA'] and Segments[0].Destination in ['SVX','VRA'] or Segments[0].Origin in ['BKK','AER'] and Segments[0].Destination in ['BKK','AER'] or Segments[0].Origin in ['ETH','ROV'] and Segments[0].Destination in ['ETH','ROV'] or Segments[0].Origin in ['SGN','SCW'] and Segments[0].Destination in ['SGN','SCW'] or Segments[0].Origin in ['SIP','KUF'] and Segments[0].Destination in ['SIP','KUF'] or Segments[0].Origin in ['CEK','NHA'] and Segments[0].Destination in ['CEK','NHA'] or Segments[0].Origin in ['AQJ','KRR'] and Segments[0].Destination in ['AQJ','KRR'] or Segments[0].Origin in ['KBV','MOW'] and Segments[0].Destination in ['KBV','MOW'] or Segments[0].Origin in ['BHK','MOW'] and Segments[0].Destination in ['BHK','MOW'] or Segments[0].Origin in ['BKK','PEE'] and Segments[0].Destination in ['BKK','PEE'] or Segments[0].Origin in ['MOW','BAX'] and Segments[0].Destination in ['MOW','BAX'] or Segments[0].Origin in ['GPA','MOW'] and Segments[0].Destination in ['GPA','MOW'] or Segments[0].Origin in ['RIX','MOW'] and Segments[0].Destination in ['RIX','MOW'] or Segments[0].Origin in ['DXB','NBC'] and Segments[0].Destination in ['DXB','NBC'] or Segments[0].Origin in ['PUJ','OVB'] and Segments[0].Destination in ['PUJ','OVB'] or Segments[0].Origin in ['ETH','CEK'] and Segments[0].Destination in ['ETH','CEK'] or Segments[0].Origin in ['KRR','ETH'] and Segments[0].Destination in ['KRR','ETH'] or Segments[0].Origin in ['HKT','UUD'] and Segments[0].Destination in ['HKT','UUD'] or Segments[0].Origin in ['TOF','VRA'] and Segments[0].Destination in ['TOF','VRA'] or Segments[0].Origin in ['MOW','SKG'] and Segments[0].Destination in ['MOW','SKG'] or Segments[0].Origin in ['BTK','OVB'] and Segments[0].Destination in ['BTK','OVB'] or Segments[0].Origin in ['KRR','LCA'] and Segments[0].Destination in ['KRR','LCA'] or Segments[0].Origin in ['OGZ','CUN'] and Segments[0].Destination in ['OGZ','CUN'] or Segments[0].Origin in ['PUJ','KGD'] and Segments[0].Destination in ['PUJ','KGD'] or Segments[0].Origin in ['USM','OVB'] and Segments[0].Destination in ['USM','OVB'] or Segments[0].Origin in ['MOW','SHE'] and Segments[0].Destination in ['MOW','SHE'] or Segments[0].Origin in ['RTW','VRA'] and Segments[0].Destination in ['RTW','VRA'] or Segments[0].Origin in ['SHJ','VOZ'] and Segments[0].Destination in ['SHJ','VOZ'] or Segments[0].Origin in ['SSH','VOG'] and Segments[0].Destination in ['SSH','VOG'] or Segments[0].Origin in ['DXB','NOZ'] and Segments[0].Destination in ['DXB','NOZ'] or Segments[0].Origin in ['SGN','SGC'] and Segments[0].Destination in ['SGN','SGC'] or Segments[0].Origin in ['VVO','NHA'] and Segments[0].Destination in ['VVO','NHA'] or Segments[0].Origin in ['CUN','KZN'] and Segments[0].Destination in ['CUN','KZN'] or Segments[0].Origin in ['AYT','SVX'] and Segments[0].Destination in ['AYT','SVX'] or Segments[0].Origin in ['CUN','KGD'] and Segments[0].Destination in ['CUN','KGD'] or Segments[0].Origin in ['KBV','KZN'] and Segments[0].Destination in ['KBV','KZN'] or Segments[0].Origin in ['VRN','MOW'] and Segments[0].Destination in ['VRN','MOW'] or Segments[0].Origin in ['OVB','UUD'] and Segments[0].Destination in ['OVB','UUD'] or Segments[0].Origin in ['USM','TJM'] and Segments[0].Destination in ['USM','TJM'] or Segments[0].Origin in ['HRG','MMK'] and Segments[0].Destination in ['HRG','MMK'] or Segments[0].Origin in ['KUF','SSH'] and Segments[0].Destination in ['KUF','SSH'] or Segments[0].Origin in ['AER','LED'] and Segments[0].Destination in ['AER','LED'] or Segments[0].Origin in ['SGN','ROV'] and Segments[0].Destination in ['SGN','ROV'] or Segments[0].Origin in ['KZN','CUN'] and Segments[0].Destination in ['KZN','CUN'] or Segments[0].Origin in ['VRA','NBC'] and Segments[0].Destination in ['VRA','NBC'] or Segments[0].Origin in ['KUF','CUN'] and Segments[0].Destination in ['KUF','CUN'] or Segments[0].Origin in ['SSH','SGC'] and Segments[0].Destination in ['SSH','SGC'] or Segments[0].Origin in ['VRA','OVB'] and Segments[0].Destination in ['VRA','OVB'] or Segments[0].Origin in ['ODS','SKG'] and Segments[0].Destination in ['ODS','SKG'] or Segments[0].Origin in ['AMM','LED'] and Segments[0].Destination in ['AMM','LED'] or Segments[0].Origin in ['RTW','PUJ'] and Segments[0].Destination in ['RTW','PUJ'] or Segments[0].Origin in ['BKK','NJC'] and Segments[0].Destination in ['BKK','NJC'] or Segments[0].Origin in ['CUN','KRR'] and Segments[0].Destination in ['CUN','KRR'] or Segments[0].Origin in ['MRV','SSH'] and Segments[0].Destination in ['MRV','SSH'] or Segments[0].Origin in ['SGC','HRG'] and Segments[0].Destination in ['SGC','HRG'] or Segments[0].Origin in ['KZN','SKG'] and Segments[0].Destination in ['KZN','SKG'] or Segments[0].Origin in ['UFA','MOW'] and Segments[0].Destination in ['UFA','MOW'] or Segments[0].Origin in ['ROM','MOW'] and Segments[0].Destination in ['ROM','MOW'] or Segments[0].Origin in ['NBC','PUJ'] and Segments[0].Destination in ['NBC','PUJ'] or Segments[0].Origin in ['KHV','MOW'] and Segments[0].Destination in ['KHV','MOW'] or Segments[0].Origin in ['VRA','CEK'] and Segments[0].Destination in ['VRA','CEK'] or Segments[0].Origin in ['VRA','KEJ'] and Segments[0].Destination in ['VRA','KEJ'] or Segments[0].Origin in ['MOW','VVO'] and Segments[0].Destination in ['MOW','VVO'] or Segments[0].Origin in ['TOF','CUN'] and Segments[0].Destination in ['TOF','CUN'] or Segments[0].Origin in ['OVB','SKG'] and Segments[0].Destination in ['OVB','SKG'] or Segments[0].Origin in ['CUN','VOG'] and Segments[0].Destination in ['CUN','VOG'] or Segments[0].Origin in ['BKK','VOZ'] and Segments[0].Destination in ['BKK','VOZ'] or Segments[0].Origin in ['ROV','ETH'] and Segments[0].Destination in ['ROV','ETH'] or Segments[0].Origin in ['HTA','NHA'] and Segments[0].Destination in ['HTA','NHA'] or Segments[0].Origin in ['GOJ','VRA'] and Segments[0].Destination in ['GOJ','VRA'] or Segments[0].Origin in ['MOW','VRN'] and Segments[0].Destination in ['MOW','VRN'] or Segments[0].Origin in ['KZN','HRG'] and Segments[0].Destination in ['KZN','HRG'] or Segments[0].Origin in ['NHA','BAX'] and Segments[0].Destination in ['NHA','BAX'] or Segments[0].Origin in ['VRA','ASF'] and Segments[0].Destination in ['VRA','ASF'] or Segments[0].Origin in ['GOJ','SKG'] and Segments[0].Destination in ['GOJ','SKG'] or Segments[0].Origin in ['SKG','LWO'] and Segments[0].Destination in ['SKG','LWO'] or Segments[0].Origin in ['MRV','CUN'] and Segments[0].Destination in ['MRV','CUN'] or Segments[0].Origin in ['SOF','MOW'] and Segments[0].Destination in ['SOF','MOW'] or Segments[0].Origin in ['BAX','VRA'] and Segments[0].Destination in ['BAX','VRA'] or Segments[0].Origin in ['SSH','MRV'] and Segments[0].Destination in ['SSH','MRV'] or Segments[0].Origin in ['KRR','LED'] and Segments[0].Destination in ['KRR','LED'] or Segments[0].Origin in ['NHA','REN'] and Segments[0].Destination in ['NHA','REN'] or Segments[0].Origin in ['ATH','MOW'] and Segments[0].Destination in ['ATH','MOW'] or Segments[0].Origin in ['KZN','VRA'] and Segments[0].Destination in ['KZN','VRA'] or Segments[0].Origin in ['HRG','VOZ'] and Segments[0].Destination in ['HRG','VOZ'] or Segments[0].Origin in ['SGN','KUF'] and Segments[0].Destination in ['SGN','KUF'] or Segments[0].Origin in ['LED','CFU'] and Segments[0].Destination in ['LED','CFU'] or Segments[0].Origin in ['SGN','MRV'] and Segments[0].Destination in ['SGN','MRV'] or Segments[0].Origin in ['CUN','EGO'] and Segments[0].Destination in ['CUN','EGO'] or Segments[0].Origin in ['KJA','AER'] and Segments[0].Destination in ['KJA','AER'] or Segments[0].Origin in ['VRA','SCW'] and Segments[0].Destination in ['VRA','SCW'] or Segments[0].Origin in ['BQS','NHA'] and Segments[0].Destination in ['BQS','NHA'] or Segments[0].Origin in ['KGD','SSH'] and Segments[0].Destination in ['KGD','SSH'] or Segments[0].Origin in ['BKK','KRR'] and Segments[0].Destination in ['BKK','KRR'] or Segments[0].Origin in ['DXB','OVB'] and Segments[0].Destination in ['DXB','OVB'] or Segments[0].Origin in ['KRR','HRG'] and Segments[0].Destination in ['KRR','HRG'] or Segments[0].Origin in ['VRA','OMS'] and Segments[0].Destination in ['VRA','OMS'] or Segments[0].Origin in ['BKK','MRV'] and Segments[0].Destination in ['BKK','MRV'] or Segments[0].Origin in ['IKT','PUJ'] and Segments[0].Destination in ['IKT','PUJ'] or Segments[0].Origin in ['KZN','PUJ'] and Segments[0].Destination in ['KZN','PUJ'] or Segments[0].Origin in ['BKK','LED'] and Segments[0].Destination in ['BKK','LED'] or Segments[0].Origin in ['SGN','LED'] and Segments[0].Destination in ['SGN','LED'] or Segments[0].Origin in ['NHA','CEK'] and Segments[0].Destination in ['NHA','CEK'] or Segments[0].Origin in ['KJA','SSH'] and Segments[0].Destination in ['KJA','SSH'] or Segments[0].Origin in ['CUN','MOW'] and Segments[0].Destination in ['CUN','MOW'] or Segments[0].Origin in ['UUD','NHA'] and Segments[0].Destination in ['UUD','NHA'] or Segments[0].Origin in ['KUF','ETH'] and Segments[0].Destination in ['KUF','ETH'] or Segments[0].Origin in ['HKT','REN'] and Segments[0].Destination in ['HKT','REN'] or Segments[0].Origin in ['BKK','MOW'] and Segments[0].Destination in ['BKK','MOW'] or Segments[0].Origin in ['BKK','UUD'] and Segments[0].Destination in ['BKK','UUD'] or Segments[0].Origin in ['CUN','OVB'] and Segments[0].Destination in ['CUN','OVB'] or Segments[0].Origin in ['SVX','SSH'] and Segments[0].Destination in ['SVX','SSH'] or Segments[0].Origin in ['LED','ETH'] and Segments[0].Destination in ['LED','ETH'] or Segments[0].Origin in ['MSQ','CFU'] and Segments[0].Destination in ['MSQ','CFU'] or Segments[0].Origin in ['KGD','PUJ'] and Segments[0].Destination in ['KGD','PUJ'] or Segments[0].Origin in ['OVB','AER'] and Segments[0].Destination in ['OVB','AER'] or Segments[0].Origin in ['OMS','NHA'] and Segments[0].Destination in ['OMS','NHA'] or Segments[0].Origin in ['PUJ','GOJ'] and Segments[0].Destination in ['PUJ','GOJ'] or Segments[0].Origin in ['NHA','TOF'] and Segments[0].Destination in ['NHA','TOF'] or Segments[0].Origin in ['TDX','BAX'] and Segments[0].Destination in ['TDX','BAX'] or Segments[0].Origin in ['UTP','KJA'] and Segments[0].Destination in ['UTP','KJA'] or Segments[0].Origin in ['BKK','KHV'] and Segments[0].Destination in ['BKK','KHV'] or Segments[0].Origin in ['NHA','BQS'] and Segments[0].Destination in ['NHA','BQS'] or Segments[0].Origin in ['CMF','MOW'] and Segments[0].Destination in ['CMF','MOW'] or Segments[0].Origin in ['BER','MOW'] and Segments[0].Destination in ['BER','MOW'] or Segments[0].Origin in ['SGN','KHV'] and Segments[0].Destination in ['SGN','KHV'] or Segments[0].Origin in ['DXB','NJC'] and Segments[0].Destination in ['DXB','NJC'] or Segments[0].Origin in ['IKT','VRA'] and Segments[0].Destination in ['IKT','VRA'] or Segments[0].Origin in ['TAS','MOW'] and Segments[0].Destination in ['TAS','MOW'] or Segments[0].Origin in ['GOJ','AYT'] and Segments[0].Destination in ['GOJ','AYT'] or Segments[0].Origin in ['VRA','GOJ'] and Segments[0].Destination in ['VRA','GOJ'] or Segments[0].Origin in ['MOW','BQS'] and Segments[0].Destination in ['MOW','BQS'] or Segments[0].Origin in ['NOZ','VRA'] and Segments[0].Destination in ['NOZ','VRA'] or Segments[0].Origin in ['PUJ','CEK'] and Segments[0].Destination in ['PUJ','CEK'] or Segments[0].Origin in ['USM','BAX'] and Segments[0].Destination in ['USM','BAX'] or Segments[0].Origin in ['ROV','VRN'] and Segments[0].Destination in ['ROV','VRN'] or Segments[0].Origin in ['OVB','CUN'] and Segments[0].Destination in ['OVB','CUN'] or Segments[0].Origin in ['OVB','MOW'] and Segments[0].Destination in ['OVB','MOW'] or Segments[0].Origin in ['SKG','ROV'] and Segments[0].Destination in ['SKG','ROV'] or Segments[0].Origin in ['MOW','BKK'] and Segments[0].Destination in ['MOW','BKK'] or Segments[0].Origin in ['BKK','IKT'] and Segments[0].Destination in ['BKK','IKT'] or Segments[0].Origin in ['TDX','SGC'] and Segments[0].Destination in ['TDX','SGC'] or Segments[0].Origin in ['ROV','VRA'] and Segments[0].Destination in ['ROV','VRA'] or Segments[0].Origin in ['BKK','TOF'] and Segments[0].Destination in ['BKK','TOF'] or Segments[0].Origin in ['CUN','MRV'] and Segments[0].Destination in ['CUN','MRV'] or Segments[0].Origin in ['ZTH','MSQ'] and Segments[0].Destination in ['ZTH','MSQ'] or Segments[0].Origin in ['MOW','CMF'] and Segments[0].Destination in ['MOW','CMF'] or Segments[0].Origin in ['CUN','PEE'] and Segments[0].Destination in ['CUN','PEE'] or Segments[0].Origin in ['CEK','HRG'] and Segments[0].Destination in ['CEK','HRG'] or Segments[0].Origin in ['HRG','KRR'] and Segments[0].Destination in ['HRG','KRR'] or Segments[0].Origin in ['VAR','LED'] and Segments[0].Destination in ['VAR','LED'] or Segments[0].Origin in ['NBC','SSH'] and Segments[0].Destination in ['NBC','SSH'] or Segments[0].Origin in ['PUJ','AER'] and Segments[0].Destination in ['PUJ','AER'] or Segments[0].Origin in ['SIP','SVX'] and Segments[0].Destination in ['SIP','SVX'] or Segments[0].Origin in ['ROV','NHA'] and Segments[0].Destination in ['ROV','NHA'] or Segments[0].Origin in ['CUN','IKT'] and Segments[0].Destination in ['CUN','IKT'] or Segments[0].Origin in ['OVB','VRA'] and Segments[0].Destination in ['OVB','VRA'] or Segments[0].Origin in ['MOW','OVB'] and Segments[0].Destination in ['MOW','OVB'] or Segments[0].Origin in ['UUD','OVB'] and Segments[0].Destination in ['UUD','OVB'] or Segments[0].Origin in ['KRR','OVB'] and Segments[0].Destination in ['KRR','OVB'] or Segments[0].Origin in ['TJM','PUJ'] and Segments[0].Destination in ['TJM','PUJ'] or Segments[0].Origin in ['PEE','HRG'] and Segments[0].Destination in ['PEE','HRG'] or Segments[0].Origin in ['KZN','AYT'] and Segments[0].Destination in ['KZN','AYT'] or Segments[0].Origin in ['GVA','MOW'] and Segments[0].Destination in ['GVA','MOW'] or Segments[0].Origin in ['CUN','OGZ'] and Segments[0].Destination in ['CUN','OGZ'] or Segments[0].Origin in ['MUC','MOW'] and Segments[0].Destination in ['MUC','MOW'] or Segments[0].Origin in ['VOZ','SSH'] and Segments[0].Destination in ['VOZ','SSH'] or Segments[0].Origin in ['AER','OVB'] and Segments[0].Destination in ['AER','OVB'] or Segments[0].Origin in ['HRG','KEJ'] and Segments[0].Destination in ['HRG','KEJ'] or Segments[0].Origin in ['TJM','VRA'] and Segments[0].Destination in ['TJM','VRA'] or Segments[0].Origin in ['HKT','BAX'] and Segments[0].Destination in ['HKT','BAX'] or Segments[0].Origin in ['KUF','AER'] and Segments[0].Destination in ['KUF','AER'] or Segments[0].Origin in ['SGN','HTA'] and Segments[0].Destination in ['SGN','HTA'] or Segments[0].Origin in ['SSH','UFA'] and Segments[0].Destination in ['SSH','UFA'] or Segments[0].Origin in ['SHJ','MOW'] and Segments[0].Destination in ['SHJ','MOW'] or Segments[0].Origin in ['SSH','KZN'] and Segments[0].Destination in ['SSH','KZN'] or Segments[0].Origin in ['SVX','PUJ'] and Segments[0].Destination in ['SVX','PUJ'] or Segments[0].Origin in ['PRG','MOW'] and Segments[0].Destination in ['PRG','MOW'] or Segments[0].Origin in ['VOZ','VRA'] and Segments[0].Destination in ['VOZ','VRA'] or Segments[0].Origin in ['AER','MOW'] and Segments[0].Destination in ['AER','MOW'] or Segments[0].Origin in ['SSH','OMS'] and Segments[0].Destination in ['SSH','OMS'] or Segments[0].Origin in ['SSH','SCW'] and Segments[0].Destination in ['SSH','SCW'] or Segments[0].Origin in ['CUN','MCX'] and Segments[0].Destination in ['CUN','MCX'] or Segments[0].Origin in ['MMK','HRG'] and Segments[0].Destination in ['MMK','HRG'] or Segments[0].Origin in ['LED','SOF'] and Segments[0].Destination in ['LED','SOF'] or Segments[0].Origin in ['KBV','UFA'] and Segments[0].Destination in ['KBV','UFA'] or Segments[0].Origin in ['DJE','MOW'] and Segments[0].Destination in ['DJE','MOW'] or Segments[0].Origin in ['NJC','VRA'] and Segments[0].Destination in ['NJC','VRA'] or Segments[0].Origin in ['YKS','NHA'] and Segments[0].Destination in ['YKS','NHA'] or Segments[0].Origin in ['SSH','MMK'] and Segments[0].Destination in ['SSH','MMK'] or Segments[0].Origin in ['PUJ','TJM'] and Segments[0].Destination in ['PUJ','TJM'] or Segments[0].Origin in ['TOF','NHA'] and Segments[0].Destination in ['TOF','NHA'] or Segments[0].Origin in ['SGN','PEE'] and Segments[0].Destination in ['SGN','PEE'] or Segments[0].Origin in ['NOZ','CUN'] and Segments[0].Destination in ['NOZ','CUN'] or Segments[0].Origin in ['PEE','PUJ'] and Segments[0].Destination in ['PEE','PUJ'] or Segments[0].Origin in ['SVX','NHA'] and Segments[0].Destination in ['SVX','NHA'] or Segments[0].Origin in ['ARH','NHA'] and Segments[0].Destination in ['ARH','NHA'] or Segments[0].Origin in ['SCW','NHA'] and Segments[0].Destination in ['SCW','NHA'] or Segments[0].Origin in ['KEJ','SSH'] and Segments[0].Destination in ['KEJ','SSH'] or Segments[0].Origin in ['AER','UFA'] and Segments[0].Destination in ['AER','UFA'] or Segments[0].Origin in ['NHA','MCX'] and Segments[0].Destination in ['NHA','MCX'] or Segments[0].Origin in ['CUN','LED'] and Segments[0].Destination in ['CUN','LED'] or Segments[0].Origin in ['MOW','FEG'] and Segments[0].Destination in ['MOW','FEG'] or Segments[0].Origin in ['MOW','SVX'] and Segments[0].Destination in ['MOW','SVX'] or Segments[0].Origin in ['KBV','SGC'] and Segments[0].Destination in ['KBV','SGC'] or Segments[0].Origin in ['VRA','KRR'] and Segments[0].Destination in ['VRA','KRR'] or Segments[0].Origin in ['SKG','KRR'] and Segments[0].Destination in ['SKG','KRR'] or Segments[0].Origin in ['NJC','PUJ'] and Segments[0].Destination in ['NJC','PUJ'] or Segments[0].Origin in ['MSQ','ZTH'] and Segments[0].Destination in ['MSQ','ZTH'] or Segments[0].Origin in ['SKG','VOG'] and Segments[0].Destination in ['SKG','VOG'] or Segments[0].Origin in ['KJA','CUN'] and Segments[0].Destination in ['KJA','CUN'] or Segments[0].Origin in ['DXB','GOJ'] and Segments[0].Destination in ['DXB','GOJ'] or Segments[0].Origin in ['SGN','BAX'] and Segments[0].Destination in ['SGN','BAX'] or Segments[0].Origin in ['KUF','AYT'] and Segments[0].Destination in ['KUF','AYT'] or Segments[0].Origin in ['ETH','KRR'] and Segments[0].Destination in ['ETH','KRR'] or Segments[0].Origin in ['IKT','NHA'] and Segments[0].Destination in ['IKT','NHA'] or Segments[0].Origin in ['ROV','HRG'] and Segments[0].Destination in ['ROV','HRG'] or Segments[0].Origin in ['PUJ','IKT'] and Segments[0].Destination in ['PUJ','IKT'] or Segments[0].Origin in ['TIV','MOW'] and Segments[0].Destination in ['TIV','MOW'] or Segments[0].Origin in ['PUJ','MOW'] and Segments[0].Destination in ['PUJ','MOW'] or Segments[0].Origin in ['CEK','VRA'] and Segments[0].Destination in ['CEK','VRA'] or Segments[0].Origin in ['EGO','PUJ'] and Segments[0].Destination in ['EGO','PUJ'] or Segments[0].Origin in ['TDX','IKT'] and Segments[0].Destination in ['TDX','IKT'] or Segments[0].Origin in ['SKG','KGD'] and Segments[0].Destination in ['SKG','KGD'] or Segments[0].Origin in ['SGN','UFA'] and Segments[0].Destination in ['SGN','UFA'] or Segments[0].Origin in ['MOW','BOJ'] and Segments[0].Destination in ['MOW','BOJ'] or Segments[0].Origin in ['NHA','KRR'] and Segments[0].Destination in ['NHA','KRR'] or Segments[0].Origin in ['HKT','KHV'] and Segments[0].Destination in ['HKT','KHV'] or Segments[0].Origin in ['RIX','SKG'] and Segments[0].Destination in ['RIX','SKG'] or Segments[0].Origin in ['SIP','KRR'] and Segments[0].Destination in ['SIP','KRR'] or Segments[0].Origin in ['AAQ','VRA'] and Segments[0].Destination in ['AAQ','VRA'] or Segments[0].Origin in ['VOZ','HRG'] and Segments[0].Destination in ['VOZ','HRG'] or Segments[0].Origin in ['CFU','LED'] and Segments[0].Destination in ['CFU','LED'] or Segments[0].Origin in ['KBV','BQS'] and Segments[0].Destination in ['KBV','BQS'] or Segments[0].Origin in ['BKK','NBC'] and Segments[0].Destination in ['BKK','NBC'] or Segments[0].Origin in ['SSH','GOJ'] and Segments[0].Destination in ['SSH','GOJ'] or Segments[0].Origin in ['LED','OVB'] and Segments[0].Destination in ['LED','OVB'] or Segments[0].Origin in ['NHA','UUD'] and Segments[0].Destination in ['NHA','UUD'] or Segments[0].Origin in ['CUN','UFA'] and Segments[0].Destination in ['CUN','UFA'] or Segments[0].Origin in ['MMK','SSH'] and Segments[0].Destination in ['MMK','SSH'] or Segments[0].Origin in ['MOW','PKC'] and Segments[0].Destination in ['MOW','PKC'] or Segments[0].Origin in ['SKG','ODS'] and Segments[0].Destination in ['SKG','ODS'] or Segments[0].Origin in ['UFA','SKG'] and Segments[0].Destination in ['UFA','SKG'] or Segments[0].Origin in ['UFA','AER'] and Segments[0].Destination in ['UFA','AER'] or Segments[0].Origin in ['VRA','NOZ'] and Segments[0].Destination in ['VRA','NOZ'] or Segments[0].Origin in ['NHA','MOW'] and Segments[0].Destination in ['NHA','MOW'] or Segments[0].Origin in ['HKT','NOZ'] and Segments[0].Destination in ['HKT','NOZ'] or Segments[0].Origin in ['MCX','VRA'] and Segments[0].Destination in ['MCX','VRA'] or Segments[0].Origin in ['SIP','LED'] and Segments[0].Destination in ['SIP','LED'] or Segments[0].Origin in ['MOW','BGY'] and Segments[0].Destination in ['MOW','BGY'] or Segments[0].Origin in ['HKT','EGO'] and Segments[0].Destination in ['HKT','EGO'] or Segments[0].Origin in ['KZN','AER'] and Segments[0].Destination in ['KZN','AER'] or Segments[0].Origin in ['NHA','OVB'] and Segments[0].Destination in ['NHA','OVB'] or Segments[0].Origin in ['VRA','VOZ'] and Segments[0].Destination in ['VRA','VOZ'] or Segments[0].Origin in ['OVB','LED'] and Segments[0].Destination in ['OVB','LED'] or Segments[0].Origin in ['NBC','CUN'] and Segments[0].Destination in ['NBC','CUN'] or Segments[0].Origin in ['VRA','KGD'] and Segments[0].Destination in ['VRA','KGD'] or Segments[0].Origin in ['CUN','CEK'] and Segments[0].Destination in ['CUN','CEK'] or Segments[0].Origin in ['VOZ','CUN'] and Segments[0].Destination in ['VOZ','CUN'] or Segments[0].Origin in ['DYR','MOW'] and Segments[0].Destination in ['DYR','MOW'] or Segments[0].Origin in ['MOW','SOF'] and Segments[0].Destination in ['MOW','SOF'] or Segments[0].Origin in ['LED','PRG'] and Segments[0].Destination in ['LED','PRG'] or Segments[0].Origin in ['PKC','NHA'] and Segments[0].Destination in ['PKC','NHA'] or Segments[0].Origin in ['BKK','TJM'] and Segments[0].Destination in ['BKK','TJM'] or Segments[0].Origin in ['NHA','OMS'] and Segments[0].Destination in ['NHA','OMS'] or Segments[0].Origin in ['DXB','BAX'] and Segments[0].Destination in ['DXB','BAX'] or Segments[0].Origin in ['OVB','HRG'] and Segments[0].Destination in ['OVB','HRG'] or Segments[0].Origin in ['AYT','KUF'] and Segments[0].Destination in ['AYT','KUF'] or Segments[0].Origin in ['HKT','CEK'] and Segments[0].Destination in ['HKT','CEK'] or Segments[0].Origin in ['GRV','MOW'] and Segments[0].Destination in ['GRV','MOW'] or Segments[0].Origin in ['IEV','ATH'] and Segments[0].Destination in ['IEV','ATH'] or Segments[0].Origin in ['OGZ','NHA'] and Segments[0].Destination in ['OGZ','NHA'] or Segments[0].Origin in ['ROV','SSH'] and Segments[0].Destination in ['ROV','SSH'] or Segments[0].Origin in ['SKG','UFA'] and Segments[0].Destination in ['SKG','UFA'] or Segments[0].Origin in ['CUN','BAX'] and Segments[0].Destination in ['CUN','BAX'] or Segments[0].Origin in ['SZG','MOW'] and Segments[0].Destination in ['SZG','MOW'] or Segments[0].Origin in ['HKT','KGD'] and Segments[0].Destination in ['HKT','KGD'] or Segments[0].Origin in ['ROV','SKG'] and Segments[0].Destination in ['ROV','SKG'] or Segments[0].Origin in ['USM','SVX'] and Segments[0].Destination in ['USM','SVX'] or Segments[0].Origin in ['KBV','BAX'] and Segments[0].Destination in ['KBV','BAX'] or Segments[0].Origin in ['BQS','MOW'] and Segments[0].Destination in ['BQS','MOW'] or Segments[0].Origin in ['SSH','KEJ'] and Segments[0].Destination in ['SSH','KEJ'] or Segments[0].Origin in ['SIP','UFA'] and Segments[0].Destination in ['SIP','UFA'] or Segments[0].Origin in ['CUN','YKS'] and Segments[0].Destination in ['CUN','YKS'] or Segments[0].Origin in ['GOJ','NHA'] and Segments[0].Destination in ['GOJ','NHA'] or Segments[0].Origin in ['MOW','PUJ'] and Segments[0].Destination in ['MOW','PUJ'] or Segments[0].Origin in ['NHA','LED'] and Segments[0].Destination in ['NHA','LED'] or Segments[0].Origin in ['HKT','VOZ'] and Segments[0].Destination in ['HKT','VOZ'] or Segments[0].Origin in ['OMS','VRA'] and Segments[0].Destination in ['OMS','VRA'] or Segments[0].Origin in ['OVB','BQS'] and Segments[0].Destination in ['OVB','BQS'] or Segments[0].Origin in ['BKK','GOJ'] and Segments[0].Destination in ['BKK','GOJ'] or Segments[0].Origin in ['HKT','ASF'] and Segments[0].Destination in ['HKT','ASF'] or Segments[0].Origin in ['LED','PUJ'] and Segments[0].Destination in ['LED','PUJ'] or Segments[0].Origin in ['CUN','KUF'] and Segments[0].Destination in ['CUN','KUF'] or Segments[0].Origin in ['MOW','LCA'] and Segments[0].Destination in ['MOW','LCA'] or Segments[0].Origin in ['CUN','KEJ'] and Segments[0].Destination in ['CUN','KEJ'] or Segments[0].Origin in ['LWO','SKG'] and Segments[0].Destination in ['LWO','SKG'] or Segments[0].Origin in ['HRG','SVX'] and Segments[0].Destination in ['HRG','SVX'] or Segments[0].Origin in ['TCI','MOW'] and Segments[0].Destination in ['TCI','MOW'] or Segments[0].Origin in ['SIP','AER'] and Segments[0].Destination in ['SIP','AER'] or Segments[0].Origin in ['SGN','TJM'] and Segments[0].Destination in ['SGN','TJM'] or Segments[0].Origin in ['PUJ','VOG'] and Segments[0].Destination in ['PUJ','VOG'] or Segments[0].Origin in ['UFA','SSH'] and Segments[0].Destination in ['UFA','SSH'] or Segments[0].Origin in ['MIL','MOW'] and Segments[0].Destination in ['MIL','MOW'] or Segments[0].Origin in ['AER','PUJ'] and Segments[0].Destination in ['AER','PUJ'] or Segments[0].Origin in ['NHA','HTA'] and Segments[0].Destination in ['NHA','HTA'] or Segments[0].Origin in ['BQS','OVB'] and Segments[0].Destination in ['BQS','OVB'] or Segments[0].Origin in ['USM','MOW'] and Segments[0].Destination in ['USM','MOW'] or Segments[0].Origin in ['KBV','IKT'] and Segments[0].Destination in ['KBV','IKT'] or Segments[0].Origin in ['HKT','UFA'] and Segments[0].Destination in ['HKT','UFA'] or Segments[0].Origin in ['MOW','KHV'] and Segments[0].Destination in ['MOW','KHV'] or Segments[0].Origin in ['UTP','EGO'] and Segments[0].Destination in ['UTP','EGO'] or Segments[0].Origin in ['DXB','HTA'] and Segments[0].Destination in ['DXB','HTA'] or Segments[0].Origin in ['SGN','OMS'] and Segments[0].Destination in ['SGN','OMS'] or Segments[0].Origin in ['MOW','AER'] and Segments[0].Destination in ['MOW','AER'] or Segments[0].Origin in ['HTA','PUJ'] and Segments[0].Destination in ['HTA','PUJ'] or Segments[0].Origin in ['KJA','NHA'] and Segments[0].Destination in ['KJA','NHA'] or Segments[0].Origin in ['HKT','OMS'] and Segments[0].Destination in ['HKT','OMS'] or Segments[0].Origin in ['OGZ','PUJ'] and Segments[0].Destination in ['OGZ','PUJ'] or Segments[0].Origin in ['PUJ','UFA'] and Segments[0].Destination in ['PUJ','UFA'] or Segments[0].Origin in ['DXB','KUF'] and Segments[0].Destination in ['DXB','KUF'] or Segments[0].Origin in ['BKK','MCX'] and Segments[0].Destination in ['BKK','MCX'] or Segments[0].Origin in ['NHA','PKC'] and Segments[0].Destination in ['NHA','PKC'] or Segments[0].Origin in ['CUN','KJA'] and Segments[0].Destination in ['CUN','KJA'] or Segments[0].Origin in ['KRR','PUJ'] and Segments[0].Destination in ['KRR','PUJ'] or Segments[0].Origin in ['HKT','IKT'] and Segments[0].Destination in ['HKT','IKT'] or Segments[0].Origin in ['DXB','ROV'] and Segments[0].Destination in ['DXB','ROV'] or Segments[0].Origin in ['DXB','TJM'] and Segments[0].Destination in ['DXB','TJM'] or Segments[0].Origin in ['NHA','KJA'] and Segments[0].Destination in ['NHA','KJA'] or Segments[0].Origin in ['USM','OMS'] and Segments[0].Destination in ['USM','OMS'] or Segments[0].Origin in ['KHV','NHA'] and Segments[0].Destination in ['KHV','NHA'] or Segments[0].Origin in ['HRG','KGD'] and Segments[0].Destination in ['HRG','KGD'] or Segments[0].Origin in ['VOG','SSH'] and Segments[0].Destination in ['VOG','SSH'] or Segments[0].Origin in ['MCX','PUJ'] and Segments[0].Destination in ['MCX','PUJ'] or Segments[0].Origin in ['MOW','TIV'] and Segments[0].Destination in ['MOW','TIV'] or Segments[0].Origin in ['DXB','KRR'] and Segments[0].Destination in ['DXB','KRR'] or Segments[0].Origin in ['DNK','SKG'] and Segments[0].Destination in ['DNK','SKG'] or Segments[0].Origin in ['HKT','KZN'] and Segments[0].Destination in ['HKT','KZN'] or Segments[0].Origin in ['USM','LED'] and Segments[0].Destination in ['USM','LED'] or Segments[0].Origin in ['HKT','MRV'] and Segments[0].Destination in ['HKT','MRV'] or Segments[0].Origin in ['HKT','TOF'] and Segments[0].Destination in ['HKT','TOF'] or Segments[0].Origin in ['MOW','UFA'] and Segments[0].Destination in ['MOW','UFA'] or Segments[0].Origin in ['DXB','KEJ'] and Segments[0].Destination in ['DXB','KEJ'] or Segments[0].Origin in ['YKS','CUN'] and Segments[0].Destination in ['YKS','CUN'] or Segments[0].Origin in ['KEJ','HRG'] and Segments[0].Destination in ['KEJ','HRG'] or Segments[0].Origin in ['MCX','NHA'] and Segments[0].Destination in ['MCX','NHA'] or Segments[0].Origin in ['NHA','SCW'] and Segments[0].Destination in ['NHA','SCW'] or Segments[0].Origin in ['DXB','MRV'] and Segments[0].Destination in ['DXB','MRV'] or Segments[0].Origin in ['BKK','OGZ'] and Segments[0].Destination in ['BKK','OGZ'] or Segments[0].Origin in ['UTP','PEE'] and Segments[0].Destination in ['UTP','PEE'] or Segments[0].Origin in ['USM','ROV'] and Segments[0].Destination in ['USM','ROV'] or Segments[0].Origin in ['VRA','YKS'] and Segments[0].Destination in ['VRA','YKS'] or Segments[0].Origin in ['SHE','MOW'] and Segments[0].Destination in ['SHE','MOW'] or Segments[0].Origin in ['MOW','TSN'] and Segments[0].Destination in ['MOW','TSN'] or Segments[0].Origin in ['TOF','OVB'] and Segments[0].Destination in ['TOF','OVB'] or Segments[0].Origin in ['NHA','KEJ'] and Segments[0].Destination in ['NHA','KEJ'] or Segments[0].Origin in ['KGD','CUN'] and Segments[0].Destination in ['KGD','CUN'] or Segments[0].Origin in ['UTP','KUF'] and Segments[0].Destination in ['UTP','KUF'] or Segments[0].Origin in ['SIP','KZN'] and Segments[0].Destination in ['SIP','KZN'] or Segments[0].Origin in ['CUN','SCW'] and Segments[0].Destination in ['CUN','SCW'] or Segments[0].Origin in ['SHJ','REN'] and Segments[0].Destination in ['SHJ','REN'] or Segments[0].Origin in ['SGN','KRR'] and Segments[0].Destination in ['SGN','KRR'] or Segments[0].Origin in ['KEJ','NHA'] and Segments[0].Destination in ['KEJ','NHA'] or Segments[0].Origin in ['CFU','IEV'] and Segments[0].Destination in ['CFU','IEV'] or Segments[0].Origin in ['MOW','CUN'] and Segments[0].Destination in ['MOW','CUN'] or Segments[0].Origin in ['LCA','MOW'] and Segments[0].Destination in ['LCA','MOW'] or Segments[0].Origin in ['SSH','ROV'] and Segments[0].Destination in ['SSH','ROV'] or Segments[0].Origin in ['BUH','MOW'] and Segments[0].Destination in ['BUH','MOW'] or Segments[0].Origin in ['SGN','BQS'] and Segments[0].Destination in ['SGN','BQS'] or Segments[0].Origin in ['KUF','VRA'] and Segments[0].Destination in ['KUF','VRA'] or Segments[0].Origin in ['NHA','KHV'] and Segments[0].Destination in ['NHA','KHV'] or Segments[0].Origin in ['DXB','TOF'] and Segments[0].Destination in ['DXB','TOF'] or Segments[0].Origin in ['HKT','KUF'] and Segments[0].Destination in ['HKT','KUF'] or Segments[0].Origin in ['EGO','NHA'] and Segments[0].Destination in ['EGO','NHA'] or Segments[0].Origin in ['MOW','BCN'] and Segments[0].Destination in ['MOW','BCN'] or Segments[0].Origin in ['SCW','HRG'] and Segments[0].Destination in ['SCW','HRG'] or Segments[0].Origin in ['BAX','CUN'] and Segments[0].Destination in ['BAX','CUN'] or Segments[0].Origin in ['AYT','PEE'] and Segments[0].Destination in ['AYT','PEE'] or Segments[0].Origin in ['BKK','OMS'] and Segments[0].Destination in ['BKK','OMS'] or Segments[0].Origin in ['LCA','KRR'] and Segments[0].Destination in ['LCA','KRR'] or Segments[0].Origin in ['BKK','CEK'] and Segments[0].Destination in ['BKK','CEK'] or Segments[0].Origin in ['MOW','VRA'] and Segments[0].Destination in ['MOW','VRA'] or Segments[0].Origin in ['LED','ZTH'] and Segments[0].Destination in ['LED','ZTH'] or Segments[0].Origin in ['KEJ','VRA'] and Segments[0].Destination in ['KEJ','VRA'] or Segments[0].Origin in ['MOW','DYR'] and Segments[0].Destination in ['MOW','DYR'] or Segments[0].Origin in ['HKT','YKS'] and Segments[0].Destination in ['HKT','YKS'] or Segments[0].Origin in ['MOW','MIR'] and Segments[0].Destination in ['MOW','MIR'] or Segments[0].Origin in ['TRN','MOW'] and Segments[0].Destination in ['TRN','MOW'] or Segments[0].Origin in ['RVN','MOW'] and Segments[0].Destination in ['RVN','MOW'] or Segments[0].Origin in ['CEK','SSH'] and Segments[0].Destination in ['CEK','SSH'] or Segments[0].Origin in ['ETH','UFA'] and Segments[0].Destination in ['ETH','UFA'] or Segments[0].Origin in ['VRA','UFA'] and Segments[0].Destination in ['VRA','UFA'] or Segments[0].Origin in ['MOW','HER'] and Segments[0].Destination in ['MOW','HER'] or Segments[0].Origin in ['DXB','OMS'] and Segments[0].Destination in ['DXB','OMS'] or Segments[0].Origin in ['VRA','ROV'] and Segments[0].Destination in ['VRA','ROV'] or Segments[0].Origin in ['MRV','PUJ'] and Segments[0].Destination in ['MRV','PUJ'] or Segments[0].Origin in ['NHA','EGO'] and Segments[0].Destination in ['NHA','EGO'] or Segments[0].Origin in ['VRA','TOF'] and Segments[0].Destination in ['VRA','TOF'] or Segments[0].Origin in ['BOJ','LED'] and Segments[0].Destination in ['BOJ','LED'] or Segments[0].Origin in ['MOW','BHK'] and Segments[0].Destination in ['MOW','BHK'] or Segments[0].Origin in ['HKT','VVO'] and Segments[0].Destination in ['HKT','VVO'] or Segments[0].Origin in ['TOF','MOW'] and Segments[0].Destination in ['TOF','MOW'] or Segments[0].Origin in ['USM','KZN'] and Segments[0].Destination in ['USM','KZN'] or Segments[0].Origin in ['PUJ','KUF'] and Segments[0].Destination in ['PUJ','KUF'] or Segments[0].Origin in ['VOZ','PUJ'] and Segments[0].Destination in ['VOZ','PUJ'] or Segments[0].Origin in ['OVB','KRR'] and Segments[0].Destination in ['OVB','KRR'] or Segments[0].Origin in ['MOW','IKT'] and Segments[0].Destination in ['MOW','IKT'] or Segments[0].Origin in ['PEE','VRA'] and Segments[0].Destination in ['PEE','VRA'] or Segments[0].Origin in ['CFU','ROV'] and Segments[0].Destination in ['CFU','ROV'] or Segments[0].Origin in ['POP','MOW'] and Segments[0].Destination in ['POP','MOW'] or Segments[0].Origin in ['PUJ','SCW'] and Segments[0].Destination in ['PUJ','SCW'] or Segments[0].Origin in ['BAX','MOW'] and Segments[0].Destination in ['BAX','MOW'] or Segments[0].Origin in ['PUJ','SVX'] and Segments[0].Destination in ['PUJ','SVX'] or Segments[0].Origin in ['CUN','NJC'] and Segments[0].Destination in ['CUN','NJC'] or Segments[0].Origin in ['UTP','LED'] and Segments[0].Destination in ['UTP','LED'] or Segments[0].Origin in ['NHA','TJM'] and Segments[0].Destination in ['NHA','TJM'] or Segments[0].Origin in ['SGN','GOJ'] and Segments[0].Destination in ['SGN','GOJ'] or Segments[0].Origin in ['SSH','NBC'] and Segments[0].Destination in ['SSH','NBC'] or Segments[0].Origin in ['KJA','MOW'] and Segments[0].Destination in ['KJA','MOW'] or Segments[0].Origin in ['MOW','GPA'] and Segments[0].Destination in ['MOW','GPA'] or Segments[0].Origin in ['ATH','IEV'] and Segments[0].Destination in ['ATH','IEV'] or Segments[0].Origin in ['USM','VVO'] and Segments[0].Destination in ['USM','VVO'] or Segments[0].Origin in ['MOW','RMI'] and Segments[0].Destination in ['MOW','RMI'] or Segments[0].Origin in ['CEE','PUJ'] and Segments[0].Destination in ['CEE','PUJ'] or Segments[0].Origin in ['KRR','SKG'] and Segments[0].Destination in ['KRR','SKG'] or Segments[0].Origin in ['CUN','HTA'] and Segments[0].Destination in ['CUN','HTA'] or Segments[0].Origin in ['MRV','VRA'] and Segments[0].Destination in ['MRV','VRA'] or Segments[0].Origin in ['VRA','TJM'] and Segments[0].Destination in ['VRA','TJM'] or Segments[0].Origin in ['SKG','RIX'] and Segments[0].Destination in ['SKG','RIX'] or Segments[0].Origin in ['PRG','SVX'] and Segments[0].Destination in ['PRG','SVX'] or Segments[0].Origin in ['ABA','VRA'] and Segments[0].Destination in ['ABA','VRA'] or Segments[0].Origin in ['SGN','IKT'] and Segments[0].Destination in ['SGN','IKT'] or Segments[0].Origin in ['VOG','HRG'] and Segments[0].Destination in ['VOG','HRG'] or Segments[0].Origin in ['SVX','HER'] and Segments[0].Destination in ['SVX','HER'] or Segments[0].Origin in ['SHJ','VOG'] and Segments[0].Destination in ['SHJ','VOG'] or Segments[0].Origin in ['VRA','OGZ'] and Segments[0].Destination in ['VRA','OGZ'] or Segments[0].Origin in ['MOW','ZTH'] and Segments[0].Destination in ['MOW','ZTH'] or Segments[0].Origin in ['KJA','PUJ'] and Segments[0].Destination in ['KJA','PUJ'] or Segments[0].Origin in ['SSH','KJA'] and Segments[0].Destination in ['SSH','KJA'] or Segments[0].Origin in ['PUJ','NBC'] and Segments[0].Destination in ['PUJ','NBC'] or Segments[0].Origin in ['BKK','BAX'] and Segments[0].Destination in ['BKK','BAX'] or Segments[0].Origin in ['GOJ','HKT'] and Segments[0].Destination in ['GOJ','HKT'] or Segments[0].Origin in ['LED','AYT'] and Segments[0].Destination in ['LED','AYT'] or Segments[0].Origin in ['CEK','USM'] and Segments[0].Destination in ['CEK','USM'] or Segments[0].Origin in ['LED','SHJ'] and Segments[0].Destination in ['LED','SHJ'] or Segments[0].Origin in ['NOZ','BKK'] and Segments[0].Destination in ['NOZ','BKK'] or Segments[0].Origin in ['NOZ','PUJ'] and Segments[0].Destination in ['NOZ','PUJ'] or Segments[0].Origin in ['TJM','TDX'] and Segments[0].Destination in ['TJM','TDX'] or Segments[0].Origin in ['YKS','BKK'] and Segments[0].Destination in ['YKS','BKK'] or Segments[0].Origin in ['MOW','KUF'] and Segments[0].Destination in ['MOW','KUF'] or Segments[0].Origin in ['KJA','SGN'] and Segments[0].Destination in ['KJA','SGN'] or Segments[0].Origin in ['CEK','UTP'] and Segments[0].Destination in ['CEK','UTP'] or Segments[0].Origin in ['UFA','USM'] and Segments[0].Destination in ['UFA','USM'] or Segments[0].Origin in ['KZN','TDX'] and Segments[0].Destination in ['KZN','TDX'] or Segments[0].Origin in ['PEE','DXB'] and Segments[0].Destination in ['PEE','DXB'] or Segments[0].Origin in ['NJC','HKT'] and Segments[0].Destination in ['NJC','HKT'] or Segments[0].Origin in ['UFA','BKK'] and Segments[0].Destination in ['UFA','BKK'] or Segments[0].Origin in ['VOG','BKK'] and Segments[0].Destination in ['VOG','BKK'] or Segments[0].Origin in ['CEK','SGN'] and Segments[0].Destination in ['CEK','SGN'] or Segments[0].Origin in ['KZN','UTP'] and Segments[0].Destination in ['KZN','UTP'] or Segments[0].Origin in ['KJA','HKT'] and Segments[0].Destination in ['KJA','HKT'] or Segments[0].Origin in ['HTA','BKK'] and Segments[0].Destination in ['HTA','BKK'] or Segments[0].Origin in ['PEE','TDX'] and Segments[0].Destination in ['PEE','TDX'] or Segments[0].Origin in ['OVB','SGN'] and Segments[0].Destination in ['OVB','SGN'] or Segments[0].Origin in ['AER','HKT'] and Segments[0].Destination in ['AER','HKT'] or Segments[0].Origin in ['CUN','CEE'] and Segments[0].Destination in ['CUN','CEE'] or Segments[0].Origin in ['OGZ','MOW'] and Segments[0].Destination in ['OGZ','MOW'] or Segments[0].Origin in ['SVX','AQJ'] and Segments[0].Destination in ['SVX','AQJ'] or Segments[0].Origin in ['SVX','SHJ'] and Segments[0].Destination in ['SVX','SHJ'] or Segments[0].Origin in ['NHA','BTK'] and Segments[0].Destination in ['NHA','BTK'] or Segments[0].Origin in ['KZN','AQJ'] and Segments[0].Destination in ['KZN','AQJ'] or Segments[0].Origin in ['VOZ','DXB'] and Segments[0].Destination in ['VOZ','DXB'] or Segments[0].Origin in ['OGZ','HKT'] and Segments[0].Destination in ['OGZ','HKT'] or Segments[0].Origin in ['MOW','KLV'] and Segments[0].Destination in ['MOW','KLV'] or Segments[0].Origin in ['MOW','AQJ'] and Segments[0].Destination in ['MOW','AQJ'] or Segments[0].Origin in ['AER','SGN'] and Segments[0].Destination in ['AER','SGN'] or Segments[0].Origin in ['OVB','BKK'] and Segments[0].Destination in ['OVB','BKK'] or Segments[0].Origin in ['UFA','AYT'] and Segments[0].Destination in ['UFA','AYT'] or Segments[0].Origin in ['NOZ','SGN'] and Segments[0].Destination in ['NOZ','SGN'] or Segments[0].Origin in ['NBC','SGN'] and Segments[0].Destination in ['NBC','SGN'] or Segments[0].Origin in ['BQS','TDX'] and Segments[0].Destination in ['BQS','TDX'] or Segments[0].Origin in ['SGC','NHA'] and Segments[0].Destination in ['SGC','NHA'] or Segments[0].Origin in ['UFA','NHA'] and Segments[0].Destination in ['UFA','NHA'] or Segments[0].Origin in ['ROV','TDX'] and Segments[0].Destination in ['ROV','TDX'] or Segments[0].Origin in ['KZN','BKK'] and Segments[0].Destination in ['KZN','BKK'] or Segments[0].Origin in ['KJA','DXB'] and Segments[0].Destination in ['KJA','DXB'] or Segments[0].Origin in ['CEK','DXB'] and Segments[0].Destination in ['CEK','DXB'] or Segments[0].Origin in ['OVB','KBV'] and Segments[0].Destination in ['OVB','KBV'] or Segments[0].Origin in ['PEE','KBV'] and Segments[0].Destination in ['PEE','KBV'] or Segments[0].Origin in ['SVX','LED'] and Segments[0].Destination in ['SVX','LED'] or Segments[0].Origin in ['CUN','AAQ'] and Segments[0].Destination in ['CUN','AAQ'] or Segments[0].Origin in ['KEJ','BKK'] and Segments[0].Destination in ['KEJ','BKK'] or Segments[0].Origin in ['BQS','BKK'] and Segments[0].Destination in ['BQS','BKK'] or Segments[0].Origin in ['IKT','DXB'] and Segments[0].Destination in ['IKT','DXB'] or Segments[0].Origin in ['KUF','BKK'] and Segments[0].Destination in ['KUF','BKK'] or Segments[0].Origin in ['VVO','BKK'] and Segments[0].Destination in ['VVO','BKK'] or Segments[0].Origin in ['OVB','HKT'] and Segments[0].Destination in ['OVB','HKT'] or Segments[0].Origin in ['NHA','RTW'] and Segments[0].Destination in ['NHA','RTW'] or Segments[0].Origin in ['VOG','SGN'] and Segments[0].Destination in ['VOG','SGN'] or Segments[0].Origin in ['VVO','KBV'] and Segments[0].Destination in ['VVO','KBV'] or Segments[0].Origin in ['TOF','PUJ'] and Segments[0].Destination in ['TOF','PUJ'] or Segments[0].Origin in ['KEJ','HKT'] and Segments[0].Destination in ['KEJ','HKT'] or Segments[0].Origin in ['TJM','HKT'] and Segments[0].Destination in ['TJM','HKT'] or Segments[0].Origin in ['ROV','POP'] and Segments[0].Destination in ['ROV','POP'] or Segments[0].Origin in ['REN','ETH'] and Segments[0].Destination in ['REN','ETH'] or Segments[0].Origin in ['PEE','USM'] and Segments[0].Destination in ['PEE','USM'] or Segments[0].Origin in ['KEJ','SGN'] and Segments[0].Destination in ['KEJ','SGN'] or Segments[0].Origin in ['AER','DXB'] and Segments[0].Destination in ['AER','DXB'] or Segments[0].Origin in ['VOG','MOW'] and Segments[0].Destination in ['VOG','MOW'] or Segments[0].Origin in ['YKS','SGN'] and Segments[0].Destination in ['YKS','SGN'] or Segments[0].Origin in ['UFA','TDX'] and Segments[0].Destination in ['UFA','TDX'] or Segments[0].Origin in ['CUN','ABA'] and Segments[0].Destination in ['CUN','ABA'] or Segments[0].Origin in ['HRG','TJM'] and Segments[0].Destination in ['HRG','TJM'] or Segments[0].Origin in ['HTA','HKT'] and Segments[0].Destination in ['HTA','HKT'] or Segments[0].Origin in ['NBC','HKT'] and Segments[0].Destination in ['NBC','HKT'] or Segments[0].Origin in ['TOF','SGN'] and Segments[0].Destination in ['TOF','SGN'] or Segments[0].Origin in ['MCX','HKT'] and Segments[0].Destination in ['MCX','HKT'] or Segments[0].Origin in ['VRA','CEE'] and Segments[0].Destination in ['VRA','CEE'] or Segments[0].Origin in ['CUN','RTW'] and Segments[0].Destination in ['CUN','RTW'] or Segments[0].Origin in ['VOZ','SGN'] and Segments[0].Destination in ['VOZ','SGN'] or Segments[0].Origin in ['BQS','USM'] and Segments[0].Destination in ['BQS','USM'] or Segments[0].Origin in ['SGC','USM'] and Segments[0].Destination in ['SGC','USM'] or Segments[0].Origin in ['KZN','DXB'] and Segments[0].Destination in ['KZN','DXB'] or Segments[0].Origin in ['KEJ','TDX'] and Segments[0].Destination in ['KEJ','TDX'] or Segments[0].Origin in ['UFA','DXB'] and Segments[0].Destination in ['UFA','DXB'] or Segments[0].Origin in ['LED','HKT'] and Segments[0].Destination in ['LED','HKT'] or Segments[0].Origin in ['NJC','SGN'] and Segments[0].Destination in ['NJC','SGN'] or Segments[0].Origin in ['KUF','SHJ'] and Segments[0].Destination in ['KUF','SHJ'] or Segments[0].Origin in ['LED','AQJ'] and Segments[0].Destination in ['LED','AQJ'] or Segments[0].Origin in ['KJA','SIP'] and Segments[0].Destination in ['KJA','SIP'] or Segments[0].Origin in ['SGC','BKK'] and Segments[0].Destination in ['SGC','BKK'] or Segments[0].Origin in ['KJA','BKK'] and Segments[0].Destination in ['KJA','BKK'] or Segments[0].Origin in ['VOG','DXB'] and Segments[0].Destination in ['VOG','DXB'] or Segments[0].Origin in ['KEJ','USM'] and Segments[0].Destination in ['KEJ','USM'] or Segments[0].Origin in ['VVO','SGN'] and Segments[0].Destination in ['VVO','SGN'] or Segments[0].Origin in ['MOW','SIP'] and Segments[0].Destination in ['MOW','SIP'] or Segments[0].Origin in ['MOW','ROV'] and Segments[0].Destination in ['MOW','ROV'] or Segments[0].Origin in ['TJM','KBV'] and Segments[0].Destination in ['TJM','KBV'] or Segments[0].Origin in ['ROV','HKT'] and Segments[0].Destination in ['ROV','HKT'] or Segments[0].Origin in ['YKS','DXB'] and Segments[0].Destination in ['YKS','DXB'] or Segments[0].Origin in ['MOW','GDX'] and Segments[0].Destination in ['MOW','GDX'] or Segments[0].Origin in ['VOG','HKT'] and Segments[0].Destination in ['VOG','HKT'] or Segments[0].Origin in ['EGO','BKK'] and Segments[0].Destination in ['EGO','BKK'] or Segments[0].Origin in ['KGD','MOW'] and Segments[0].Destination in ['KGD','MOW'] or Segments[0].Origin in ['IKT','USM'] and Segments[0].Destination in ['IKT','USM'] or Segments[0].Origin in ['MOW','TDX'] and Segments[0].Destination in ['MOW','TDX'] or Segments[0].Origin in ['SVX','SGN'] and Segments[0].Destination in ['SVX','SGN'] or Segments[0].Origin in ['KRR','VRN'] and Segments[0].Destination in ['KRR','VRN'] or Segments[0].Origin in ['BAX','PUJ'] and Segments[0].Destination in ['BAX','PUJ'] or Segments[0].Origin in ['HRG','LED'] and Segments[0].Destination in ['HRG','LED'] or Segments[0].Origin in ['ASF','BKK'] and Segments[0].Destination in ['ASF','BKK'] or Segments[0].Origin in ['ROV','SHJ'] and Segments[0].Destination in ['ROV','SHJ'] or Segments[0].Origin in ['KRR','HKT'] and Segments[0].Destination in ['KRR','HKT'] or Segments[0].Origin in ['MOW','AYT'] and Segments[0].Destination in ['MOW','AYT'] or Segments[0].Origin in ['SVX','HKT'] and Segments[0].Destination in ['SVX','HKT'] or Segments[0].Origin in ['SVX','BKK'] and Segments[0].Destination in ['SVX','BKK'] or Segments[0].Origin in ['MOW','SGN'] and Segments[0].Destination in ['MOW','SGN'] or Segments[0].Origin in ['PEE','SSH'] and Segments[0].Destination in ['PEE','SSH'] or Segments[0].Origin in ['MOW','DXB'] and Segments[0].Destination in ['MOW','DXB'] or Segments[0].Origin in ['YKS','PUJ'] and Segments[0].Destination in ['YKS','PUJ'] or Segments[0].Origin in ['MOW','AUH'] and Segments[0].Destination in ['MOW','AUH'] or Segments[0].Origin in ['IKT','UTP'] and Segments[0].Destination in ['IKT','UTP'] or Segments[0].Origin in ['ROV','BKK'] and Segments[0].Destination in ['ROV','BKK'] or Segments[0].Origin in ['KGD','SGN'] and Segments[0].Destination in ['KGD','SGN'] or Segments[0].Origin in ['SCW','HKT'] and Segments[0].Destination in ['SCW','HKT'] or Segments[0].Origin in ['KGD','BKK'] and Segments[0].Destination in ['KGD','BKK'] or Segments[0].Origin in ['SGC','HKT'] and Segments[0].Destination in ['SGC','HKT'] or Segments[0].Origin in ['TSE','SKG'] and Segments[0].Destination in ['TSE','SKG'] or Segments[0].Origin in ['PKC','BKK'] and Segments[0].Destination in ['PKC','BKK'] or Segments[0].Origin in ['KEJ','KBV'] and Segments[0].Destination in ['KEJ','KBV'] or Segments[0].Origin in ['SCW','BKK'] and Segments[0].Destination in ['SCW','BKK'] or Segments[0].Origin in ['MOW','AMM'] and Segments[0].Destination in ['MOW','AMM'] or Segments[0].Origin in ['KZN','SGN'] and Segments[0].Destination in ['KZN','SGN'] or Segments[0].Origin in ['AER','VOG'] and Segments[0].Destination in ['AER','VOG'] or Segments[0].Origin in ['SVX','DXB'] and Segments[0].Destination in ['SVX','DXB'] or Segments[0].Origin in ['BQS','HKT'] and Segments[0].Destination in ['BQS','HKT'] or Segments[0].Origin in ['LED','DXB'] and Segments[0].Destination in ['LED','DXB'] or Segments[0].Origin in ['ROV','KBV'] and Segments[0].Destination in ['ROV','KBV'] or Segments[0].Origin in ['REN','BKK'] and Segments[0].Destination in ['REN','BKK'] or Segments[0].Origin in ['PEE','HKT'] and Segments[0].Destination in ['PEE','HKT'] or Segments[0].Origin in ['AER','BKK'] and Segments[0].Destination in ['AER','BKK'] or Segments[0].Origin in ['SCW','SGN'] and Segments[0].Destination in ['SCW','SGN'] or Segments[0].Origin in ['KUF','SIP'] and Segments[0].Destination in ['KUF','SIP'] or Segments[0].Origin in ['KRR','AQJ'] and Segments[0].Destination in ['KRR','AQJ'] or Segments[0].Origin in ['MOW','KBV'] and Segments[0].Destination in ['MOW','KBV'] or Segments[0].Origin in ['PEE','BKK'] and Segments[0].Destination in ['PEE','BKK'] or Segments[0].Origin in ['NBC','DXB'] and Segments[0].Destination in ['NBC','DXB'] or Segments[0].Origin in ['UUD','HKT'] and Segments[0].Destination in ['UUD','HKT'] or Segments[0].Origin in ['OVB','BTK'] and Segments[0].Destination in ['OVB','BTK'] or Segments[0].Origin in ['OVB','USM'] and Segments[0].Destination in ['OVB','USM'] or Segments[0].Origin in ['VRA','RTW'] and Segments[0].Destination in ['VRA','RTW'] or Segments[0].Origin in ['VOZ','SHJ'] and Segments[0].Destination in ['VOZ','SHJ'] or Segments[0].Origin in ['NOZ','DXB'] and Segments[0].Destination in ['NOZ','DXB'] or Segments[0].Origin in ['SGC','SGN'] and Segments[0].Destination in ['SGC','SGN'] or Segments[0].Origin in ['SVX','AYT'] and Segments[0].Destination in ['SVX','AYT'] or Segments[0].Origin in ['KZN','KBV'] and Segments[0].Destination in ['KZN','KBV'] or Segments[0].Origin in ['TJM','USM'] and Segments[0].Destination in ['TJM','USM'] or Segments[0].Origin in ['ROV','SGN'] and Segments[0].Destination in ['ROV','SGN'] or Segments[0].Origin in ['LED','AMM'] and Segments[0].Destination in ['LED','AMM'] or Segments[0].Origin in ['PUJ','RTW'] and Segments[0].Destination in ['PUJ','RTW'] or Segments[0].Origin in ['NJC','BKK'] and Segments[0].Destination in ['NJC','BKK'] or Segments[0].Origin in ['MOW','ROM'] and Segments[0].Destination in ['MOW','ROM'] or Segments[0].Origin in ['VOZ','BKK'] and Segments[0].Destination in ['VOZ','BKK'] or Segments[0].Origin in ['LED','KRR'] and Segments[0].Destination in ['LED','KRR'] or Segments[0].Origin in ['KUF','SGN'] and Segments[0].Destination in ['KUF','SGN'] or Segments[0].Origin in ['MRV','SGN'] and Segments[0].Destination in ['MRV','SGN'] or Segments[0].Origin in ['SSH','KGD'] and Segments[0].Destination in ['SSH','KGD'] or Segments[0].Origin in ['KRR','BKK'] and Segments[0].Destination in ['KRR','BKK'] or Segments[0].Origin in ['OVB','DXB'] and Segments[0].Destination in ['OVB','DXB'] or Segments[0].Origin in ['MRV','BKK'] and Segments[0].Destination in ['MRV','BKK'] or Segments[0].Origin in ['LED','BKK'] and Segments[0].Destination in ['LED','BKK'] or Segments[0].Origin in ['LED','SGN'] and Segments[0].Destination in ['LED','SGN'] or Segments[0].Origin in ['REN','HKT'] and Segments[0].Destination in ['REN','HKT'] or Segments[0].Origin in ['UUD','BKK'] and Segments[0].Destination in ['UUD','BKK'] or Segments[0].Origin in ['GOJ','PUJ'] and Segments[0].Destination in ['GOJ','PUJ'] or Segments[0].Origin in ['BAX','TDX'] and Segments[0].Destination in ['BAX','TDX'] or Segments[0].Origin in ['KJA','UTP'] and Segments[0].Destination in ['KJA','UTP'] or Segments[0].Origin in ['KHV','BKK'] and Segments[0].Destination in ['KHV','BKK'] or Segments[0].Origin in ['MOW','BER'] and Segments[0].Destination in ['MOW','BER'] or Segments[0].Origin in ['KHV','SGN'] and Segments[0].Destination in ['KHV','SGN'] or Segments[0].Origin in ['NJC','DXB'] and Segments[0].Destination in ['NJC','DXB'] or Segments[0].Origin in ['BAX','USM'] and Segments[0].Destination in ['BAX','USM'] or Segments[0].Origin in ['VRN','ROV'] and Segments[0].Destination in ['VRN','ROV'] or Segments[0].Origin in ['IKT','BKK'] and Segments[0].Destination in ['IKT','BKK'] or Segments[0].Origin in ['SGC','TDX'] and Segments[0].Destination in ['SGC','TDX'] or Segments[0].Origin in ['TOF','BKK'] and Segments[0].Destination in ['TOF','BKK'] or Segments[0].Origin in ['SVX','SIP'] and Segments[0].Destination in ['SVX','SIP'] or Segments[0].Origin in ['HRG','PEE'] and Segments[0].Destination in ['HRG','PEE'] or Segments[0].Origin in ['MOW','GVA'] and Segments[0].Destination in ['MOW','GVA'] or Segments[0].Origin in ['MOW','MUC'] and Segments[0].Destination in ['MOW','MUC'] or Segments[0].Origin in ['BAX','HKT'] and Segments[0].Destination in ['BAX','HKT'] or Segments[0].Origin in ['HTA','SGN'] and Segments[0].Destination in ['HTA','SGN'] or Segments[0].Origin in ['MOW','SHJ'] and Segments[0].Destination in ['MOW','SHJ'] or Segments[0].Origin in ['UFA','KBV'] and Segments[0].Destination in ['UFA','KBV'] or Segments[0].Origin in ['MOW','DJE'] and Segments[0].Destination in ['MOW','DJE'] or Segments[0].Origin in ['PEE','SGN'] and Segments[0].Destination in ['PEE','SGN'] or Segments[0].Origin in ['LED','CUN'] and Segments[0].Destination in ['LED','CUN'] or Segments[0].Origin in ['SVX','MOW'] and Segments[0].Destination in ['SVX','MOW'] or Segments[0].Origin in ['SGC','KBV'] and Segments[0].Destination in ['SGC','KBV'] or Segments[0].Origin in ['GOJ','DXB'] and Segments[0].Destination in ['GOJ','DXB'] or Segments[0].Origin in ['BAX','SGN'] and Segments[0].Destination in ['BAX','SGN'] or Segments[0].Origin in ['HRG','ROV'] and Segments[0].Destination in ['HRG','ROV'] or Segments[0].Origin in ['IKT','TDX'] and Segments[0].Destination in ['IKT','TDX'] or Segments[0].Origin in ['UFA','SGN'] and Segments[0].Destination in ['UFA','SGN'] or Segments[0].Origin in ['KHV','HKT'] and Segments[0].Destination in ['KHV','HKT'] or Segments[0].Origin in ['KRR','SIP'] and Segments[0].Destination in ['KRR','SIP'] or Segments[0].Origin in ['VRA','AAQ'] and Segments[0].Destination in ['VRA','AAQ'] or Segments[0].Origin in ['BQS','KBV'] and Segments[0].Destination in ['BQS','KBV'] or Segments[0].Origin in ['NBC','BKK'] and Segments[0].Destination in ['NBC','BKK'] or Segments[0].Origin in ['NOZ','HKT'] and Segments[0].Destination in ['NOZ','HKT'] or Segments[0].Origin in ['LED','SIP'] and Segments[0].Destination in ['LED','SIP'] or Segments[0].Origin in ['BGY','MOW'] and Segments[0].Destination in ['BGY','MOW'] or Segments[0].Origin in ['EGO','HKT'] and Segments[0].Destination in ['EGO','HKT'] or Segments[0].Origin in ['OVB','NHA'] and Segments[0].Destination in ['OVB','NHA'] or Segments[0].Origin in ['TJM','BKK'] and Segments[0].Destination in ['TJM','BKK'] or Segments[0].Origin in ['BAX','DXB'] and Segments[0].Destination in ['BAX','DXB'] or Segments[0].Origin in ['CEK','HKT'] and Segments[0].Destination in ['CEK','HKT'] or Segments[0].Origin in ['KGD','HKT'] and Segments[0].Destination in ['KGD','HKT'] or Segments[0].Origin in ['SVX','USM'] and Segments[0].Destination in ['SVX','USM'] or Segments[0].Origin in ['BAX','KBV'] and Segments[0].Destination in ['BAX','KBV'] or Segments[0].Origin in ['UFA','SIP'] and Segments[0].Destination in ['UFA','SIP'] or Segments[0].Origin in ['LED','NHA'] and Segments[0].Destination in ['LED','NHA'] or Segments[0].Origin in ['VOZ','HKT'] and Segments[0].Destination in ['VOZ','HKT'] or Segments[0].Origin in ['GOJ','BKK'] and Segments[0].Destination in ['GOJ','BKK'] or Segments[0].Origin in ['ASF','HKT'] and Segments[0].Destination in ['ASF','HKT'] or Segments[0].Origin in ['AER','SIP'] and Segments[0].Destination in ['AER','SIP'] or Segments[0].Origin in ['TJM','SGN'] and Segments[0].Destination in ['TJM','SGN'] or Segments[0].Origin in ['MOW','MIL'] and Segments[0].Destination in ['MOW','MIL'] or Segments[0].Origin in ['MOW','USM'] and Segments[0].Destination in ['MOW','USM'] or Segments[0].Origin in ['IKT','KBV'] and Segments[0].Destination in ['IKT','KBV'] or Segments[0].Origin in ['UFA','HKT'] and Segments[0].Destination in ['UFA','HKT'] or Segments[0].Origin in ['EGO','UTP'] and Segments[0].Destination in ['EGO','UTP'] or Segments[0].Origin in ['HTA','DXB'] and Segments[0].Destination in ['HTA','DXB'] or Segments[0].Origin in ['OMS','SGN'] and Segments[0].Destination in ['OMS','SGN'] or Segments[0].Origin in ['PUJ','HTA'] and Segments[0].Destination in ['PUJ','HTA'] or Segments[0].Origin in ['OMS','HKT'] and Segments[0].Destination in ['OMS','HKT'] or Segments[0].Origin in ['KUF','DXB'] and Segments[0].Destination in ['KUF','DXB'] or Segments[0].Origin in ['MCX','BKK'] and Segments[0].Destination in ['MCX','BKK'] or Segments[0].Origin in ['IKT','HKT'] and Segments[0].Destination in ['IKT','HKT'] or Segments[0].Origin in ['ROV','DXB'] and Segments[0].Destination in ['ROV','DXB'] or Segments[0].Origin in ['TJM','DXB'] and Segments[0].Destination in ['TJM','DXB'] or Segments[0].Origin in ['OMS','USM'] and Segments[0].Destination in ['OMS','USM'] or Segments[0].Origin in ['KRR','DXB'] and Segments[0].Destination in ['KRR','DXB'] or Segments[0].Origin in ['KZN','HKT'] and Segments[0].Destination in ['KZN','HKT'] or Segments[0].Origin in ['LED','USM'] and Segments[0].Destination in ['LED','USM'] or Segments[0].Origin in ['MRV','HKT'] and Segments[0].Destination in ['MRV','HKT'] or Segments[0].Origin in ['TOF','HKT'] and Segments[0].Destination in ['TOF','HKT'] or Segments[0].Origin in ['KEJ','DXB'] and Segments[0].Destination in ['KEJ','DXB'] or Segments[0].Origin in ['MRV','DXB'] and Segments[0].Destination in ['MRV','DXB'] or Segments[0].Origin in ['OGZ','BKK'] and Segments[0].Destination in ['OGZ','BKK'] or Segments[0].Origin in ['PEE','UTP'] and Segments[0].Destination in ['PEE','UTP'] or Segments[0].Origin in ['ROV','USM'] and Segments[0].Destination in ['ROV','USM'] or Segments[0].Origin in ['KUF','UTP'] and Segments[0].Destination in ['KUF','UTP'] or Segments[0].Origin in ['KZN','SIP'] and Segments[0].Destination in ['KZN','SIP'] or Segments[0].Origin in ['REN','SHJ'] and Segments[0].Destination in ['REN','SHJ'] or Segments[0].Origin in ['KRR','SGN'] and Segments[0].Destination in ['KRR','SGN'] or Segments[0].Origin in ['MOW','BUH'] and Segments[0].Destination in ['MOW','BUH'] or Segments[0].Origin in ['BQS','SGN'] and Segments[0].Destination in ['BQS','SGN'] or Segments[0].Origin in ['TOF','DXB'] and Segments[0].Destination in ['TOF','DXB'] or Segments[0].Origin in ['KUF','HKT'] and Segments[0].Destination in ['KUF','HKT'] or Segments[0].Origin in ['HRG','SCW'] and Segments[0].Destination in ['HRG','SCW'] or Segments[0].Origin in ['PEE','AYT'] and Segments[0].Destination in ['PEE','AYT'] or Segments[0].Origin in ['OMS','BKK'] and Segments[0].Destination in ['OMS','BKK'] or Segments[0].Origin in ['CEK','BKK'] and Segments[0].Destination in ['CEK','BKK'] or Segments[0].Origin in ['YKS','HKT'] and Segments[0].Destination in ['YKS','HKT'] or Segments[0].Origin in ['MOW','TRN'] and Segments[0].Destination in ['MOW','TRN'] or Segments[0].Origin in ['OMS','DXB'] and Segments[0].Destination in ['OMS','DXB'] or Segments[0].Origin in ['VVO','HKT'] and Segments[0].Destination in ['VVO','HKT'] or Segments[0].Origin in ['KZN','USM'] and Segments[0].Destination in ['KZN','USM'] or Segments[0].Origin in ['MOW','POP'] and Segments[0].Destination in ['MOW','POP'] or Segments[0].Origin in ['LED','UTP'] and Segments[0].Destination in ['LED','UTP'] or Segments[0].Origin in ['GOJ','SGN'] and Segments[0].Destination in ['GOJ','SGN'] or Segments[0].Origin in ['VVO','USM'] and Segments[0].Destination in ['VVO','USM'] or Segments[0].Origin in ['PUJ','CEE'] and Segments[0].Destination in ['PUJ','CEE'] or Segments[0].Origin in ['SVX','PRG'] and Segments[0].Destination in ['SVX','PRG'] or Segments[0].Origin in ['VRA','ABA'] and Segments[0].Destination in ['VRA','ABA'] or Segments[0].Origin in ['IKT','SGN'] and Segments[0].Destination in ['IKT','SGN'] or Segments[0].Origin in ['VOG','SHJ'] and Segments[0].Destination in ['VOG','SHJ'])`
program, err := expr.Compile(expression, expr.Env(env))
if err != nil {
b.Fatal(err)
}
var out interface{}
for n := 0; n < b.N; n++ {
out, err = vm.Run(program, env)
}
if err != nil {
b.Fatal(err)
}
if !out.(bool) {
b.Fail()
}
}
expr-1.8.9/checker/ 0000775 0000000 0000000 00000000000 13726232776 0014132 5 ustar 00root root 0000000 0000000 expr-1.8.9/checker/checker.go 0000664 0000000 0000000 00000033527 13726232776 0016077 0 ustar 00root root 0000000 0000000 package checker
import (
"fmt"
"reflect"
"github.com/antonmedv/expr/ast"
"github.com/antonmedv/expr/conf"
"github.com/antonmedv/expr/file"
"github.com/antonmedv/expr/parser"
)
func Check(tree *parser.Tree, config *conf.Config) (reflect.Type, error) {
v := &visitor{
collections: make([]reflect.Type, 0),
}
if config != nil {
v.types = config.Types
v.operators = config.Operators
v.expect = config.Expect
v.strict = config.Strict
v.defaultType = config.DefaultType
}
t := v.visit(tree.Node)
if v.expect != reflect.Invalid {
switch v.expect {
case reflect.Int64, reflect.Float64:
if !isNumber(t) {
return nil, fmt.Errorf("expected %v, but got %v", v.expect, t)
}
default:
if t.Kind() != v.expect {
return nil, fmt.Errorf("expected %v, but got %v", v.expect, t)
}
}
}
if v.err != nil {
return t, v.err.Bind(tree.Source)
}
return t, nil
}
type visitor struct {
types conf.TypesTable
operators conf.OperatorsTable
expect reflect.Kind
collections []reflect.Type
strict bool
defaultType reflect.Type
err *file.Error
}
func (v *visitor) visit(node ast.Node) reflect.Type {
var t reflect.Type
switch n := node.(type) {
case *ast.NilNode:
t = v.NilNode(n)
case *ast.IdentifierNode:
t = v.IdentifierNode(n)
case *ast.IntegerNode:
t = v.IntegerNode(n)
case *ast.FloatNode:
t = v.FloatNode(n)
case *ast.BoolNode:
t = v.BoolNode(n)
case *ast.StringNode:
t = v.StringNode(n)
case *ast.UnaryNode:
t = v.UnaryNode(n)
case *ast.BinaryNode:
t = v.BinaryNode(n)
case *ast.MatchesNode:
t = v.MatchesNode(n)
case *ast.PropertyNode:
t = v.PropertyNode(n)
case *ast.IndexNode:
t = v.IndexNode(n)
case *ast.SliceNode:
t = v.SliceNode(n)
case *ast.MethodNode:
t = v.MethodNode(n)
case *ast.FunctionNode:
t = v.FunctionNode(n)
case *ast.BuiltinNode:
t = v.BuiltinNode(n)
case *ast.ClosureNode:
t = v.ClosureNode(n)
case *ast.PointerNode:
t = v.PointerNode(n)
case *ast.ConditionalNode:
t = v.ConditionalNode(n)
case *ast.ArrayNode:
t = v.ArrayNode(n)
case *ast.MapNode:
t = v.MapNode(n)
case *ast.PairNode:
t = v.PairNode(n)
default:
panic(fmt.Sprintf("undefined node type (%T)", node))
}
node.SetType(t)
return t
}
func (v *visitor) error(node ast.Node, format string, args ...interface{}) reflect.Type {
if v.err == nil { // show first error
v.err = &file.Error{
Location: node.Location(),
Message: fmt.Sprintf(format, args...),
}
}
return interfaceType // interface represent undefined type
}
func (v *visitor) NilNode(*ast.NilNode) reflect.Type {
return nilType
}
func (v *visitor) IdentifierNode(node *ast.IdentifierNode) reflect.Type {
if v.types == nil {
return interfaceType
}
if t, ok := v.types[node.Value]; ok {
if t.Ambiguous {
return v.error(node, "ambiguous identifier %v", node.Value)
}
return t.Type
}
if !v.strict {
if v.defaultType != nil {
return v.defaultType
}
return interfaceType
}
return v.error(node, "unknown name %v", node.Value)
}
func (v *visitor) IntegerNode(*ast.IntegerNode) reflect.Type {
return integerType
}
func (v *visitor) FloatNode(*ast.FloatNode) reflect.Type {
return floatType
}
func (v *visitor) BoolNode(*ast.BoolNode) reflect.Type {
return boolType
}
func (v *visitor) StringNode(*ast.StringNode) reflect.Type {
return stringType
}
func (v *visitor) UnaryNode(node *ast.UnaryNode) reflect.Type {
t := v.visit(node.Node)
switch node.Operator {
case "!", "not":
if isBool(t) {
return boolType
}
case "+", "-":
if isNumber(t) {
return t
}
default:
return v.error(node, "unknown operator (%v)", node.Operator)
}
return v.error(node, `invalid operation: %v (mismatched type %v)`, node.Operator, t)
}
func (v *visitor) BinaryNode(node *ast.BinaryNode) reflect.Type {
l := v.visit(node.Left)
r := v.visit(node.Right)
// check operator overloading
if fns, ok := v.operators[node.Operator]; ok {
t, _, ok := conf.FindSuitableOperatorOverload(fns, v.types, l, r)
if ok {
return t
}
}
switch node.Operator {
case "==", "!=":
if isNumber(l) && isNumber(r) {
return boolType
}
if isComparable(l, r) {
return boolType
}
case "or", "||", "and", "&&":
if isBool(l) && isBool(r) {
return boolType
}
case "in", "not in":
if isString(l) && isStruct(r) {
return boolType
}
if isMap(r) {
return boolType
}
if isArray(r) {
return boolType
}
case "<", ">", ">=", "<=":
if isNumber(l) && isNumber(r) {
return boolType
}
if isString(l) && isString(r) {
return boolType
}
case "/", "-", "*":
if isNumber(l) && isNumber(r) {
return combined(l, r)
}
case "**":
if isNumber(l) && isNumber(r) {
return floatType
}
case "%":
if isInteger(l) && isInteger(r) {
return combined(l, r)
}
case "+":
if isNumber(l) && isNumber(r) {
return combined(l, r)
}
if isString(l) && isString(r) {
return stringType
}
case "contains", "startsWith", "endsWith":
if isString(l) && isString(r) {
return boolType
}
case "..":
if isInteger(l) && isInteger(r) {
return reflect.SliceOf(integerType)
}
default:
return v.error(node, "unknown operator (%v)", node.Operator)
}
return v.error(node, `invalid operation: %v (mismatched types %v and %v)`, node.Operator, l, r)
}
func (v *visitor) MatchesNode(node *ast.MatchesNode) reflect.Type {
l := v.visit(node.Left)
r := v.visit(node.Right)
if isString(l) && isString(r) {
return boolType
}
return v.error(node, `invalid operation: matches (mismatched types %v and %v)`, l, r)
}
func (v *visitor) PropertyNode(node *ast.PropertyNode) reflect.Type {
t := v.visit(node.Node)
if t, ok := fieldType(t, node.Property); ok {
return t
}
return v.error(node, "type %v has no field %v", t, node.Property)
}
func (v *visitor) IndexNode(node *ast.IndexNode) reflect.Type {
t := v.visit(node.Node)
i := v.visit(node.Index)
if t, ok := indexType(t); ok {
if !isInteger(i) && !isString(i) {
return v.error(node, "invalid operation: cannot use %v as index to %v", i, t)
}
return t
}
return v.error(node, "invalid operation: type %v does not support indexing", t)
}
func (v *visitor) SliceNode(node *ast.SliceNode) reflect.Type {
t := v.visit(node.Node)
_, isIndex := indexType(t)
if isIndex || isString(t) {
if node.From != nil {
from := v.visit(node.From)
if !isInteger(from) {
return v.error(node.From, "invalid operation: non-integer slice index %v", from)
}
}
if node.To != nil {
to := v.visit(node.To)
if !isInteger(to) {
return v.error(node.To, "invalid operation: non-integer slice index %v", to)
}
}
return t
}
return v.error(node, "invalid operation: cannot slice %v", t)
}
func (v *visitor) FunctionNode(node *ast.FunctionNode) reflect.Type {
if f, ok := v.types[node.Name]; ok {
if fn, ok := isFuncType(f.Type); ok {
inputParamsCount := 1 // for functions
if f.Method {
inputParamsCount = 2 // for methods
}
if !isInterface(fn) &&
fn.IsVariadic() &&
fn.NumIn() == inputParamsCount &&
fn.NumOut() == 1 &&
fn.Out(0).Kind() == reflect.Interface {
rest := fn.In(fn.NumIn() - 1) // function has only one param for functions and two for methods
if rest.Kind() == reflect.Slice && rest.Elem().Kind() == reflect.Interface {
node.Fast = true
}
}
return v.checkFunc(fn, f.Method, node, node.Name, node.Arguments)
}
}
if !v.strict {
if v.defaultType != nil {
return v.defaultType
}
return interfaceType
}
return v.error(node, "unknown func %v", node.Name)
}
func (v *visitor) MethodNode(node *ast.MethodNode) reflect.Type {
t := v.visit(node.Node)
if f, method, ok := methodType(t, node.Method); ok {
if fn, ok := isFuncType(f); ok {
return v.checkFunc(fn, method, node, node.Method, node.Arguments)
}
}
return v.error(node, "type %v has no method %v", t, node.Method)
}
// checkFunc checks func arguments and returns "return type" of func or method.
func (v *visitor) checkFunc(fn reflect.Type, method bool, node ast.Node, name string, arguments []ast.Node) reflect.Type {
if isInterface(fn) {
return interfaceType
}
if fn.NumOut() == 0 {
return v.error(node, "func %v doesn't return value", name)
}
if fn.NumOut() != 1 {
return v.error(node, "func %v returns more then one value", name)
}
numIn := fn.NumIn()
// If func is method on an env, first argument should be a receiver,
// and actual arguments less then numIn by one.
if method {
numIn--
}
if fn.IsVariadic() {
if len(arguments) < numIn-1 {
return v.error(node, "not enough arguments to call %v", name)
}
} else {
if len(arguments) > numIn {
return v.error(node, "too many arguments to call %v", name)
}
if len(arguments) < numIn {
return v.error(node, "not enough arguments to call %v", name)
}
}
offset := 0
// Skip first argument in case of the receiver.
if method {
offset = 1
}
for i, arg := range arguments {
t := v.visit(arg)
var in reflect.Type
if fn.IsVariadic() && i >= numIn-1 {
// For variadic arguments fn(xs ...int), go replaces type of xs (int) with ([]int).
// As we compare arguments one by one, we need underling type.
in = fn.In(fn.NumIn() - 1)
in, _ = indexType(in)
} else {
in = fn.In(i + offset)
}
if isIntegerOrArithmeticOperation(arg) {
t = in
setTypeForIntegers(arg, t)
}
if t == nil {
continue
}
if !t.AssignableTo(in) && t.Kind() != reflect.Interface {
return v.error(arg, "cannot use %v as argument (type %v) to call %v ", t, in, name)
}
}
return fn.Out(0)
}
func (v *visitor) BuiltinNode(node *ast.BuiltinNode) reflect.Type {
switch node.Name {
case "len":
param := v.visit(node.Arguments[0])
if isArray(param) || isMap(param) || isString(param) {
return integerType
}
return v.error(node, "invalid argument for len (type %v)", param)
case "all", "none", "any", "one":
collection := v.visit(node.Arguments[0])
if !isArray(collection) {
return v.error(node.Arguments[0], "builtin %v takes only array (got %v)", node.Name, collection)
}
v.collections = append(v.collections, collection)
closure := v.visit(node.Arguments[1])
v.collections = v.collections[:len(v.collections)-1]
if isFunc(closure) &&
closure.NumOut() == 1 &&
closure.NumIn() == 1 && isInterface(closure.In(0)) {
if !isBool(closure.Out(0)) {
return v.error(node.Arguments[1], "closure should return boolean (got %v)", closure.Out(0).String())
}
return boolType
}
return v.error(node.Arguments[1], "closure should has one input and one output param")
case "filter":
collection := v.visit(node.Arguments[0])
if !isArray(collection) {
return v.error(node.Arguments[0], "builtin %v takes only array (got %v)", node.Name, collection)
}
v.collections = append(v.collections, collection)
closure := v.visit(node.Arguments[1])
v.collections = v.collections[:len(v.collections)-1]
if isFunc(closure) &&
closure.NumOut() == 1 &&
closure.NumIn() == 1 && isInterface(closure.In(0)) {
if !isBool(closure.Out(0)) {
return v.error(node.Arguments[1], "closure should return boolean (got %v)", closure.Out(0).String())
}
if isInterface(collection) {
return arrayType
}
return reflect.SliceOf(collection.Elem())
}
return v.error(node.Arguments[1], "closure should has one input and one output param")
case "map":
collection := v.visit(node.Arguments[0])
if !isArray(collection) {
return v.error(node.Arguments[0], "builtin %v takes only array (got %v)", node.Name, collection)
}
v.collections = append(v.collections, collection)
closure := v.visit(node.Arguments[1])
v.collections = v.collections[:len(v.collections)-1]
if isFunc(closure) &&
closure.NumOut() == 1 &&
closure.NumIn() == 1 && isInterface(closure.In(0)) {
return reflect.SliceOf(closure.Out(0))
}
return v.error(node.Arguments[1], "closure should has one input and one output param")
case "count":
collection := v.visit(node.Arguments[0])
if !isArray(collection) {
return v.error(node.Arguments[0], "builtin %v takes only array (got %v)", node.Name, collection)
}
v.collections = append(v.collections, collection)
closure := v.visit(node.Arguments[1])
v.collections = v.collections[:len(v.collections)-1]
if isFunc(closure) &&
closure.NumOut() == 1 &&
closure.NumIn() == 1 && isInterface(closure.In(0)) {
if !isBool(closure.Out(0)) {
return v.error(node.Arguments[1], "closure should return boolean (got %v)", closure.Out(0).String())
}
return integerType
}
return v.error(node.Arguments[1], "closure should has one input and one output param")
default:
return v.error(node, "unknown builtin %v", node.Name)
}
}
func (v *visitor) ClosureNode(node *ast.ClosureNode) reflect.Type {
t := v.visit(node.Node)
return reflect.FuncOf([]reflect.Type{interfaceType}, []reflect.Type{t}, false)
}
func (v *visitor) PointerNode(node *ast.PointerNode) reflect.Type {
if len(v.collections) == 0 {
return v.error(node, "cannot use pointer accessor outside closure")
}
collection := v.collections[len(v.collections)-1]
if t, ok := indexType(collection); ok {
return t
}
return v.error(node, "cannot use %v as array", collection)
}
func (v *visitor) ConditionalNode(node *ast.ConditionalNode) reflect.Type {
c := v.visit(node.Cond)
if !isBool(c) {
return v.error(node.Cond, "non-bool expression (type %v) used as condition", c)
}
t1 := v.visit(node.Exp1)
t2 := v.visit(node.Exp2)
if t1 == nil && t2 != nil {
return t2
}
if t1 != nil && t2 == nil {
return t1
}
if t1 == nil && t2 == nil {
return nilType
}
if t1.AssignableTo(t2) {
return t1
}
return interfaceType
}
func (v *visitor) ArrayNode(node *ast.ArrayNode) reflect.Type {
for _, node := range node.Nodes {
_ = v.visit(node)
}
return arrayType
}
func (v *visitor) MapNode(node *ast.MapNode) reflect.Type {
for _, pair := range node.Pairs {
v.visit(pair)
}
return mapType
}
func (v *visitor) PairNode(node *ast.PairNode) reflect.Type {
v.visit(node.Key)
v.visit(node.Value)
return nilType
}
expr-1.8.9/checker/checker_test.go 0000664 0000000 0000000 00000027027 13726232776 0017134 0 ustar 00root root 0000000 0000000 package checker_test
import (
"fmt"
"strings"
"testing"
"time"
"github.com/antonmedv/expr"
"github.com/antonmedv/expr/checker"
"github.com/antonmedv/expr/conf"
"github.com/antonmedv/expr/parser"
"github.com/stretchr/testify/assert"
)
func TestCheck_debug(t *testing.T) {
input := `2**3 + 1`
tree, err := parser.Parse(input)
assert.NoError(t, err)
out, err := checker.Check(tree, conf.New(&mockEnv{}))
assert.NoError(t, err)
if err == nil {
assert.Equal(t, "float64", out.Name())
}
}
func TestVisitor_FunctionNode(t *testing.T) {
var err error
env := &mockEnv{}
input := `Set(1, "tag") + Add(2) + Get() + Sub(3) + Any()`
tree, err := parser.Parse(input)
assert.NoError(t, err)
out, err := checker.Check(tree, conf.New(env))
assert.NoError(t, err)
if err == nil {
assert.Equal(t, "int64", out.Name())
}
}
func TestVisitor_MethodNode(t *testing.T) {
var err error
env := &mockEnv{}
input := `Var.Set(1, 0.5)
+ Var.Add(2)
+ Var.Any(true)
+ Var.Get()
+ Var.Sub(3)
+ (Duration.String() == "" ? 1 : 0)
+ Interface.Method(0)
+ Tickets[0].Method(0)`
tree, err := parser.Parse(input)
assert.NoError(t, err)
out, err := checker.Check(tree, conf.New(env))
assert.NoError(t, err)
if err == nil {
assert.Equal(t, "int64", out.Name())
}
}
func TestVisitor_BuiltinNode(t *testing.T) {
var typeTests = []string{
`all(Tickets, {.Price > 0}) && any(map(Tickets, {.Price}), {# < 1000})`,
`filter(map(Tickets, {.Origin}), {len(#) != 3})[0]`,
`none(Any, {#.Any < 1})`,
`none(Any, {.Thing != "awesome"})`,
}
for _, input := range typeTests {
tree, err := parser.Parse(input)
assert.NoError(t, err)
_, err = checker.Check(tree, conf.New(&mockEnv{}))
assert.NoError(t, err)
}
}
func TestCheck(t *testing.T) {
var typeTests = []string{
"!Bool",
"!BoolPtr == Bool",
"'a' == 'b' + 'c'",
"'foo' contains 'bar'",
"'foo' endsWith 'bar'",
"'foo' startsWith 'bar'",
"(1 == 1) || (String matches Any)",
"1 + 2 + Int64",
"1 + 2 == FloatPtr",
"1 + 2 + Float + 3 + 4",
"1 + Int + Float",
"1 < Float",
"1 <= Float",
"1 == 2 and true or Bool",
"1 > Float",
"1 >= Float",
"2**3 + 1",
"[1,2,3]",
"Abc == Float",
"Abc()",
"Any matches Any",
"Any.Thing.Is.Bool",
"ArrayOfAny['string'].next.goes['any thing']",
"ArrayOfFoo[0].Bar.Baz",
"ArrayOfFoo[1].Int64 + 1",
"Bool && Any",
"BoolFn() and BoolFn()",
"EmbedPtr.EmbPtrStr + String",
"EmbPtrStr == ''",
"Float == 1",
"Float == Abc",
"Fn(true, 1, 'str', Any)",
"Foo.Abc()",
"Foo.Bar == Map.id.Bar",
"Foo.Bar.Baz",
"Foo.Fn() or Foo.Fn()",
"Foo.Fn()",
"Foo2p.Bar.Baz",
"Int % Int > 1",
"Int + Int + Int",
"Int == Any",
"Int in Int..Int",
"Int64 % 1",
"IntPtr == Int",
"len([])",
"Map.id.Bar.Baz",
"Map['string'].Bar.Baz",
"Method(Foo.Bar) > 1",
"nil == IntPtr",
"nil == nil",
"nil in ArrayOfFoo",
"nil",
"String + (true ? String : String)",
"String in ArrayOfFoo",
"String in Foo",
"String matches 'ok'",
"String matches Any",
"String not in Foo2p",
"StringPtr == nil",
"Sub.Method(0) + String",
"Sub.SubString",
"SubStr + ''",
"SubString == ''",
"SubSub.SubStr",
"true == false",
"true ? Any : Any",
"{id: Foo.Bar.Baz, 'str': Bool}",
`"a" < "b"`,
"Variadic('', 1, 2) + Variadic('')",
"Foo.Variadic('', 1, 2) + Foo.Variadic('')",
"count(1..30, {# % 3 == 0}) > 0",
"map(1..3, {#}) == [1,2,3]",
"map(filter(ArrayOfFoo, {.Int64 > 0}), {.Bar})",
}
for _, test := range typeTests {
var err error
tree, err := parser.Parse(test)
assert.NoError(t, err, test)
_, err = checker.Check(tree, conf.New(mockEnv2{}))
assert.NoError(t, err, test)
}
}
const errorTests = `
Foo.Bar.Not
type checker_test.bar has no field Not (1:9)
| Foo.Bar.Not
| ........^
Noo
unknown name Noo (1:1)
| Noo
| ^
Foo()
unknown func Foo (1:1)
| Foo()
| ^
Foo['string']
invalid operation: type *checker_test.foo does not support indexing (1:4)
| Foo['string']
| ...^
Foo.Fn(Not)
too many arguments to call Fn (1:5)
| Foo.Fn(Not)
| ....^
Foo.Bar()
type *checker_test.foo has no method Bar (1:5)
| Foo.Bar()
| ....^
Foo.Bar.Not()
type checker_test.bar has no method Not (1:9)
| Foo.Bar.Not()
| ........^
ArrayOfFoo[0].Not
type *checker_test.foo has no field Not (1:15)
| ArrayOfFoo[0].Not
| ..............^
ArrayOfFoo[Not]
unknown name Not (1:12)
| ArrayOfFoo[Not]
| ...........^
Not[0]
unknown name Not (1:1)
| Not[0]
| ^
Not.Bar
unknown name Not (1:1)
| Not.Bar
| ^
ArrayOfFoo.Not
type []*checker_test.foo has no field Not (1:12)
| ArrayOfFoo.Not
| ...........^
Fn(Not)
not enough arguments to call Fn (1:1)
| Fn(Not)
| ^
Map['str'].Not
type *checker_test.foo has no field Not (1:12)
| Map['str'].Not
| ...........^
Bool && IntPtr
invalid operation: && (mismatched types bool and *int) (1:6)
| Bool && IntPtr
| .....^
No ? Any.Bool : Any.Not
unknown name No (1:1)
| No ? Any.Bool : Any.Not
| ^
Any.Cond ? No : Any.Not
unknown name No (1:12)
| Any.Cond ? No : Any.Not
| ...........^
Any.Cond ? Any.Bool : No
unknown name No (1:23)
| Any.Cond ? Any.Bool : No
| ......................^
ManOfAny ? Any : Any
non-bool expression (type map[string]interface {}) used as condition (1:1)
| ManOfAny ? Any : Any
| ^
String matches Int
invalid operation: matches (mismatched types string and int) (1:8)
| String matches Int
| .......^
Int matches String
invalid operation: matches (mismatched types int and string) (1:5)
| Int matches String
| ....^
String contains Int
invalid operation: contains (mismatched types string and int) (1:8)
| String contains Int
| .......^
Int contains String
invalid operation: contains (mismatched types int and string) (1:5)
| Int contains String
| ....^
!Not
unknown name Not (1:2)
| !Not
| .^
Not == Any
unknown name Not (1:1)
| Not == Any
| ^
[Not]
unknown name Not (1:2)
| [Not]
| .^
{id: Not}
unknown name Not (1:6)
| {id: Not}
| .....^
(nil).Foo
type has no field Foo (1:7)
| (nil).Foo
| ......^
(nil)['Foo']
invalid operation: type does not support indexing (1:6)
| (nil)['Foo']
| .....^
1 and false
invalid operation: and (mismatched types int and bool) (1:3)
| 1 and false
| ..^
true or 0
invalid operation: or (mismatched types bool and int) (1:6)
| true or 0
| .....^
not IntPtr
invalid operation: not (mismatched type *int) (1:1)
| not IntPtr
| ^
len(Not)
unknown name Not (1:5)
| len(Not)
| ....^
Int < Bool
invalid operation: < (mismatched types int and bool) (1:5)
| Int < Bool
| ....^
Int > Bool
invalid operation: > (mismatched types int and bool) (1:5)
| Int > Bool
| ....^
Int >= Bool
invalid operation: >= (mismatched types int and bool) (1:5)
| Int >= Bool
| ....^
Int <= Bool
invalid operation: <= (mismatched types int and bool) (1:5)
| Int <= Bool
| ....^
Int + Bool
invalid operation: + (mismatched types int and bool) (1:5)
| Int + Bool
| ....^
Int - Bool
invalid operation: - (mismatched types int and bool) (1:5)
| Int - Bool
| ....^
Int * Bool
invalid operation: * (mismatched types int and bool) (1:5)
| Int * Bool
| ....^
Int / Bool
invalid operation: / (mismatched types int and bool) (1:5)
| Int / Bool
| ....^
Int % Bool
invalid operation: % (mismatched types int and bool) (1:5)
| Int % Bool
| ....^
Int ** Bool
invalid operation: ** (mismatched types int and bool) (1:5)
| Int ** Bool
| ....^
Int .. Bool
invalid operation: .. (mismatched types int and bool) (1:5)
| Int .. Bool
| ....^
NilFn() and BoolFn()
func NilFn doesn't return value (1:1)
| NilFn() and BoolFn()
| ^
'str' in String
invalid operation: in (mismatched types string and string) (1:7)
| 'str' in String
| ......^
1 in Foo
invalid operation: in (mismatched types int and *checker_test.foo) (1:3)
| 1 in Foo
| ..^
1 + ''
invalid operation: + (mismatched types int and string) (1:3)
| 1 + ''
| ..^
all(ArrayOfFoo, {#.Fn() < 0})
invalid operation: < (mismatched types bool and int) (1:25)
| all(ArrayOfFoo, {#.Fn() < 0})
| ........................^
map(Any, {0})[0] + "str"
invalid operation: + (mismatched types int and string) (1:18)
| map(Any, {0})[0] + "str"
| .................^
Variadic()
not enough arguments to call Variadic (1:1)
| Variadic()
| ^
Variadic('', '')
cannot use string as argument (type int) to call Variadic (1:14)
| Variadic('', '')
| .............^
Foo.Variadic()
not enough arguments to call Variadic (1:5)
| Foo.Variadic()
| ....^
Foo.Variadic('', '')
cannot use string as argument (type int) to call Variadic (1:18)
| Foo.Variadic('', '')
| .................^
count(1, {#})
builtin count takes only array (got int) (1:7)
| count(1, {#})
| ......^
count(ArrayOfInt, {#})
closure should return boolean (got int) (1:19)
| count(ArrayOfInt, {#})
| ..................^
all(ArrayOfInt, {# + 1})
closure should return boolean (got int) (1:17)
| all(ArrayOfInt, {# + 1})
| ................^
filter(ArrayOfFoo, {.Int64})
closure should return boolean (got int64) (1:20)
| filter(ArrayOfFoo, {.Int64})
| ...................^
map(1, {2})
builtin map takes only array (got int) (1:5)
| map(1, {2})
| ....^
map(filter(ArrayOfFoo, {.Int64 > 0}), {.Var})
type *checker_test.foo has no field Var (1:41)
| map(filter(ArrayOfFoo, {.Int64 > 0}), {.Var})
| ........................................^
`
func TestCheck_error(t *testing.T) {
tests := strings.Split(strings.Trim(errorTests, "\n"), "\n\n")
for _, test := range tests {
input := strings.SplitN(test, "\n", 2)
if len(input) != 2 {
t.Errorf("syntax error in test: %q", test)
break
}
tree, err := parser.Parse(input[0])
assert.NoError(t, err)
_, err = checker.Check(tree, conf.New(mockEnv2{}))
if err == nil {
err = fmt.Errorf("")
}
assert.Equal(t, input[1], err.Error(), input[0])
}
}
func TestCheck_AsBool(t *testing.T) {
input := `1+2`
tree, err := parser.Parse(input)
assert.NoError(t, err)
config := &conf.Config{}
expr.AsBool()(config)
_, err = checker.Check(tree, config)
assert.Error(t, err)
assert.Equal(t, "expected bool, but got int", err.Error())
}
//
// Mock types
//
type mockEnv struct {
*mockEmbed
Add func(int64) int64
Any interface{}
Var *mockVar
Tickets []mockTicket
Duration time.Duration
Interface mockInterface
}
func (f *mockEnv) Set(v int64, any interface{}) int64 {
return v
}
type mockEmbed struct {
EmbedVar int64
Sub func(int64) int64
}
func (f *mockEmbed) Get() int64 {
return 0
}
type mockVar struct {
*mockEmbed
Add func(int64) int64
Any interface{}
}
func (*mockVar) Set(v int64, f float64) int64 {
return 0
}
type mockInterface interface {
Method(int) int
}
type mockTicket struct {
Price int
Origin string
}
func (t mockTicket) Method(int) int {
return 0
}
type abc interface {
Abc()
}
type bar struct {
Baz string
}
type foo struct {
Int64 int64
Bar bar
Fn func() bool
Abc abc
Variadic func(head string, xs ...int) int
}
type SubSub struct {
SubStr string
}
type Sub struct {
SubSub
SubString string
}
func (p Sub) Method(i int) string {
return ""
}
type EmbedPtr struct {
EmbPtrStr string
}
type mockEnv2 struct {
Sub
*EmbedPtr
Abc abc
Foo *foo
ArrayOfFoo []*foo
Map map[string]*foo
Any interface{}
ArrayOfAny []interface{}
ArrayOfInt []int
ManOfAny map[string]interface{}
Fn func(bool, int, string, interface{}) string
Bool bool
Float float64
Int64 int64
Int int
String string
BoolPtr *bool
FloatPtr *float64
IntPtr *int
StringPtr *string
Foo2p **foo
BoolFn func() bool
NilFn func()
Variadic func(head string, xs ...int) int
}
func (p mockEnv2) Method(_ bar) int {
return 0
}
expr-1.8.9/checker/types.go 0000664 0000000 0000000 00000014573 13726232776 0015637 0 ustar 00root root 0000000 0000000 package checker
import (
"reflect"
"github.com/antonmedv/expr/ast"
)
var (
nilType = reflect.TypeOf(nil)
boolType = reflect.TypeOf(true)
integerType = reflect.TypeOf(int(0))
floatType = reflect.TypeOf(float64(0))
stringType = reflect.TypeOf("")
arrayType = reflect.TypeOf([]interface{}{})
mapType = reflect.TypeOf(map[string]interface{}{})
interfaceType = reflect.TypeOf(new(interface{})).Elem()
)
func typeWeight(t reflect.Type) int {
switch t.Kind() {
case reflect.Uint:
return 1
case reflect.Uint8:
return 2
case reflect.Uint16:
return 3
case reflect.Uint32:
return 4
case reflect.Uint64:
return 5
case reflect.Int:
return 6
case reflect.Int8:
return 7
case reflect.Int16:
return 8
case reflect.Int32:
return 9
case reflect.Int64:
return 10
case reflect.Float32:
return 11
case reflect.Float64:
return 12
default:
return 0
}
}
func combined(a, b reflect.Type) reflect.Type {
if typeWeight(a) > typeWeight(b) {
return a
} else {
return b
}
}
func dereference(t reflect.Type) reflect.Type {
if t == nil {
return nil
}
if t.Kind() == reflect.Ptr {
t = dereference(t.Elem())
}
return t
}
func isComparable(l, r reflect.Type) bool {
l = dereference(l)
r = dereference(r)
if l == nil || r == nil { // It is possible to compare with nil.
return true
}
if l.Kind() == r.Kind() {
return true
}
if isInterface(l) || isInterface(r) {
return true
}
return false
}
func isInterface(t reflect.Type) bool {
t = dereference(t)
if t != nil {
switch t.Kind() {
case reflect.Interface:
return true
}
}
return false
}
func isInteger(t reflect.Type) bool {
t = dereference(t)
if t != nil {
switch t.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
fallthrough
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
return true
case reflect.Interface:
return true
}
}
return false
}
func isFloat(t reflect.Type) bool {
t = dereference(t)
if t != nil {
switch t.Kind() {
case reflect.Float32, reflect.Float64:
return true
case reflect.Interface:
return true
}
}
return false
}
func isNumber(t reflect.Type) bool {
return isInteger(t) || isFloat(t)
}
func isBool(t reflect.Type) bool {
t = dereference(t)
if t != nil {
switch t.Kind() {
case reflect.Bool:
return true
case reflect.Interface:
return true
}
}
return false
}
func isString(t reflect.Type) bool {
t = dereference(t)
if t != nil {
switch t.Kind() {
case reflect.String:
return true
case reflect.Interface:
return true
}
}
return false
}
func isArray(t reflect.Type) bool {
t = dereference(t)
if t != nil {
switch t.Kind() {
case reflect.Slice, reflect.Array:
return true
case reflect.Interface:
return true
}
}
return false
}
func isMap(t reflect.Type) bool {
t = dereference(t)
if t != nil {
switch t.Kind() {
case reflect.Map:
return true
case reflect.Interface:
return true
}
}
return false
}
func isStruct(t reflect.Type) bool {
t = dereference(t)
if t != nil {
switch t.Kind() {
case reflect.Struct:
return true
}
}
return false
}
func isFunc(t reflect.Type) bool {
t = dereference(t)
if t != nil {
switch t.Kind() {
case reflect.Func:
return true
}
}
return false
}
func fieldType(ntype reflect.Type, name string) (reflect.Type, bool) {
ntype = dereference(ntype)
if ntype != nil {
switch ntype.Kind() {
case reflect.Interface:
return interfaceType, true
case reflect.Struct:
// First check all struct's fields.
for i := 0; i < ntype.NumField(); i++ {
f := ntype.Field(i)
if f.Name == name {
return f.Type, true
}
}
// Second check fields of embedded structs.
for i := 0; i < ntype.NumField(); i++ {
f := ntype.Field(i)
if f.Anonymous {
if t, ok := fieldType(f.Type, name); ok {
return t, true
}
}
}
case reflect.Map:
return ntype.Elem(), true
}
}
return nil, false
}
func methodType(t reflect.Type, name string) (reflect.Type, bool, bool) {
if t != nil {
// First, check methods defined on type itself,
// independent of which type it is.
if m, ok := t.MethodByName(name); ok {
if t.Kind() == reflect.Interface {
// In case of interface type method will not have a receiver,
// and to prevent checker decreasing numbers of in arguments
// return method type as not method (second argument is false).
return m.Type, false, true
} else {
return m.Type, true, true
}
}
d := t
if t.Kind() == reflect.Ptr {
d = t.Elem()
}
switch d.Kind() {
case reflect.Interface:
return interfaceType, false, true
case reflect.Struct:
// First, check all struct's fields.
for i := 0; i < d.NumField(); i++ {
f := d.Field(i)
if !f.Anonymous && f.Name == name {
return f.Type, false, true
}
}
// Second, check fields of embedded structs.
for i := 0; i < d.NumField(); i++ {
f := d.Field(i)
if f.Anonymous {
if t, method, ok := methodType(f.Type, name); ok {
return t, method, true
}
}
}
case reflect.Map:
return d.Elem(), false, true
}
}
return nil, false, false
}
func indexType(ntype reflect.Type) (reflect.Type, bool) {
ntype = dereference(ntype)
if ntype == nil {
return nil, false
}
switch ntype.Kind() {
case reflect.Interface:
return interfaceType, true
case reflect.Map, reflect.Array, reflect.Slice:
return ntype.Elem(), true
}
return nil, false
}
func isFuncType(ntype reflect.Type) (reflect.Type, bool) {
ntype = dereference(ntype)
if ntype == nil {
return nil, false
}
switch ntype.Kind() {
case reflect.Interface:
return interfaceType, true
case reflect.Func:
return ntype, true
}
return nil, false
}
func isIntegerOrArithmeticOperation(node ast.Node) bool {
switch n := node.(type) {
case *ast.IntegerNode:
return true
case *ast.UnaryNode:
switch n.Operator {
case "+", "-":
return true
}
case *ast.BinaryNode:
switch n.Operator {
case "+", "/", "-", "*":
return true
}
}
return false
}
func setTypeForIntegers(node ast.Node, t reflect.Type) {
switch n := node.(type) {
case *ast.IntegerNode:
n.SetType(t)
case *ast.UnaryNode:
switch n.Operator {
case "+", "-":
setTypeForIntegers(n.Node, t)
}
case *ast.BinaryNode:
switch n.Operator {
case "+", "/", "-", "*":
setTypeForIntegers(n.Left, t)
setTypeForIntegers(n.Right, t)
}
}
}
expr-1.8.9/cmd/ 0000775 0000000 0000000 00000000000 13726232776 0013271 5 ustar 00root root 0000000 0000000 expr-1.8.9/cmd/exe/ 0000775 0000000 0000000 00000000000 13726232776 0014052 5 ustar 00root root 0000000 0000000 expr-1.8.9/cmd/exe/README.md 0000664 0000000 0000000 00000000605 13726232776 0015332 0 ustar 00root root 0000000 0000000 # Expr Debugger
## Install
```bash
go get github.com/antonmedv/expr/cmd/exe
```
## Usage
Print ast of program.
```bash
echo '1 + 2' | exe -ast
```
Disassemble bytecode to human readable format.
```bash
echo 'map(0..9, {# * 2})' | exe -bytecode
```
Run expression.
```bash
echo '2**8' | exe -run
```
Start interactive debugger.
```bash
echo 'all(1..3, {# > 0})' | exe -debug
```
expr-1.8.9/cmd/exe/debugger.go 0000664 0000000 0000000 00000007763 13726232776 0016202 0 ustar 00root root 0000000 0000000 package main
import (
"fmt"
"sort"
"strconv"
"strings"
"time"
"github.com/antonmedv/expr/checker"
"github.com/antonmedv/expr/compiler"
"github.com/antonmedv/expr/optimizer"
"github.com/antonmedv/expr/parser"
. "github.com/antonmedv/expr/vm"
"github.com/gdamore/tcell"
"github.com/rivo/tview"
"github.com/sanity-io/litter"
)
func debugger() {
tree, err := parser.Parse(input())
check(err)
_, err = checker.Check(tree, nil)
check(err)
if opt {
err = optimizer.Optimize(&tree.Node, nil)
check(err)
}
program, err := compiler.Compile(tree, nil)
check(err)
vm := Debug()
app := tview.NewApplication()
table := tview.NewTable()
stack := tview.NewTable()
stack.
SetBorder(true).
SetTitle("Stack")
scope := tview.NewTable()
scope.
SetBorder(true).
SetTitle("Scope")
sub := tview.NewFlex().
SetDirection(tview.FlexRow).
AddItem(stack, 0, 3, false).
AddItem(scope, 0, 1, false)
flex := tview.NewFlex().
AddItem(table, 0, 1, true).
AddItem(sub, 0, 1, false)
app.SetRoot(flex, true)
go func() {
out, _ := vm.Run(program, nil)
app.QueueUpdateDraw(func() {
sub.RemoveItem(scope)
result := tview.NewTextView()
result.
SetBorder(true).
SetTitle("Output")
result.SetText(litter.Sdump(out))
sub.AddItem(result, 0, 1, false)
})
}()
index := make(map[int]int)
for row, line := range strings.Split(program.Disassemble(), "\n") {
if line == "" {
continue
}
parts := strings.Split(line, "\t")
ip, err := strconv.Atoi(parts[0])
check(err)
index[ip] = row
table.SetCellSimple(row, 0, fmt.Sprintf("% *d", 5, ip))
for col := 1; col < len(parts); col++ {
table.SetCellSimple(row, col, parts[col])
}
for col := len(parts); col < 4; col++ {
table.SetCellSimple(row, col, "")
}
table.SetCell(row, 4, tview.NewTableCell("").SetExpansion(1))
}
draw := func(ip int) {
app.QueueUpdateDraw(func() {
for row := 0; row < table.GetRowCount(); row++ {
for col := 0; col < table.GetColumnCount(); col++ {
table.GetCell(row, col).SetBackgroundColor(tcell.ColorDefault)
}
}
if row, ok := index[ip]; ok {
table.Select(row, 0)
for col := 0; col < 5; col++ {
table.GetCell(row, col).SetBackgroundColor(tcell.ColorMediumBlue)
}
table.SetOffset(row-10, 0)
opcode := table.GetCell(row, 1).Text
if strings.HasPrefix(opcode, "OpJump") {
jump := table.GetCell(row, 3).Text
jump = strings.Trim(jump, "()")
ip, err := strconv.Atoi(jump)
if err == nil {
if row, ok := index[ip]; ok {
for col := 0; col < 5; col++ {
table.GetCell(row, col).SetBackgroundColor(tcell.ColorDimGrey)
}
}
}
}
}
stack.Clear()
for i, value := range vm.Stack() {
stack.SetCellSimple(i, 0, fmt.Sprintf("% *d: ", 2, i))
stack.SetCellSimple(i, 1, fmt.Sprintf("%+v", value))
}
stack.ScrollToEnd()
scope.Clear()
var keys []string
for k := range vm.Scope() {
keys = append(keys, k)
}
sort.Strings(keys)
row := 0
for _, name := range keys {
scope.SetCellSimple(row, 0, fmt.Sprintf("%v: ", name))
scope.SetCellSimple(row, 1, fmt.Sprintf("%v", vm.Scope()[name]))
row++
}
})
}
getSelectedPosition := func() int {
row, _ := table.GetSelection()
ip, err := strconv.Atoi(strings.TrimSpace(table.GetCell(row, 0).Text))
check(err)
return ip
}
autostep := false
var breakpoint int
go func() {
draw(0)
for ip := range vm.Position() {
draw(ip)
if autostep {
if breakpoint != ip {
time.Sleep(20 * time.Millisecond)
vm.Step()
} else {
autostep = false
}
}
}
}()
app.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey {
if event.Key() == tcell.KeyDown || event.Key() == tcell.KeyUp {
table.SetSelectable(true, false)
}
if event.Key() == tcell.KeyEnter {
selectable, _ := table.GetSelectable()
if selectable {
table.SetSelectable(false, false)
breakpoint = getSelectedPosition()
autostep = true
}
vm.Step()
}
return event
})
err = app.Run()
check(err)
}
expr-1.8.9/cmd/exe/dot.go 0000664 0000000 0000000 00000006001 13726232776 0015164 0 ustar 00root root 0000000 0000000 package main
import (
"fmt"
"os"
. "github.com/antonmedv/expr/ast"
)
const format = `digraph {
ranksep=.3;
node [shape=oval, fontname="Helvetica-bold"];
%v
%v}
`
func dotAst(node Node) {
v := &visitor{}
Walk(&node, v)
dot := fmt.Sprintf(format, v.nodes, v.links)
_, _ = fmt.Fprintf(os.Stdout, dot)
}
type visitor struct {
nodes string
links string
index int
stack []int
}
func (v *visitor) push(label string) {
v.index++
v.nodes += fmt.Sprintf(" n%v [label=%q];\n", v.index, label)
v.stack = append(v.stack, v.index)
}
func (v *visitor) pop() int {
node := v.stack[len(v.stack)-1]
v.stack = v.stack[:len(v.stack)-1]
return node
}
func (v *visitor) link(node int) {
v.links += fmt.Sprintf(" n%v -> n%v\n", v.index, node)
}
func (v *visitor) Enter(node *Node) {}
func (v *visitor) Exit(ref *Node) {
switch node := (*ref).(type) {
case *NilNode:
v.push("nil")
case *IdentifierNode:
v.push(node.Value)
case *IntegerNode:
v.push(fmt.Sprintf("%v", node.Value))
case *FloatNode:
v.push(fmt.Sprintf("%v", node.Value))
case *BoolNode:
v.push(fmt.Sprintf("%v", node.Value))
case *StringNode:
v.push(fmt.Sprintf("%q", node.Value))
case *UnaryNode:
n := v.pop()
v.push(node.Operator)
v.link(n)
case *BinaryNode:
b := v.pop()
a := v.pop()
v.push(node.Operator)
v.link(a)
v.link(b)
case *MatchesNode:
b := v.pop()
a := v.pop()
v.push("matches")
v.link(a)
v.link(b)
case *PropertyNode:
a := v.pop()
v.push(fmt.Sprintf(".%v", node.Property))
v.link(a)
case *IndexNode:
b := v.pop()
a := v.pop()
v.push(fmt.Sprintf("%T", node))
v.link(a)
v.link(b)
case *MethodNode:
args := make([]int, 0)
for range node.Arguments {
args = append(args, v.pop())
}
a := v.pop()
v.push(fmt.Sprintf(".%v(...)", node.Method))
v.link(a)
for i := len(args) - 1; i >= 0; i-- {
v.link(args[i])
}
case *FunctionNode:
args := make([]int, 0)
for range node.Arguments {
args = append(args, v.pop())
}
v.push(fmt.Sprintf("%v(...)", node.Name))
for i := len(args) - 1; i >= 0; i-- {
v.link(args[i])
}
case *BuiltinNode:
args := make([]int, 0)
for range node.Arguments {
args = append(args, v.pop())
}
v.push(fmt.Sprintf("%v", node.Name))
for i := len(args) - 1; i >= 0; i-- {
v.link(args[i])
}
case *ClosureNode:
a := v.pop()
v.push(fmt.Sprintf("%T", node))
v.link(a)
case *PointerNode:
v.push("#")
case *ConditionalNode:
e2 := v.pop()
e1 := v.pop()
c := v.pop()
v.push(fmt.Sprintf("%T", node))
v.link(c)
v.link(e1)
v.link(e2)
case *ArrayNode:
n := make([]int, 0)
for range node.Nodes {
n = append(n, v.pop())
}
v.push("[...]")
for i := len(n) - 1; i >= 0; i-- {
v.link(n[i])
}
case *MapNode:
n := make([]int, 0)
for range node.Pairs {
n = append(n, v.pop())
}
v.push("{...}")
for i := len(n) - 1; i >= 0; i-- {
v.link(n[i])
}
case *PairNode:
b := v.pop()
a := v.pop()
v.push(fmt.Sprintf("%T", node))
v.link(a)
v.link(b)
default:
v.push(fmt.Sprintf("%T", node))
}
}
expr-1.8.9/cmd/exe/main.go 0000664 0000000 0000000 00000005167 13726232776 0015336 0 ustar 00root root 0000000 0000000 package main
import (
"bufio"
"flag"
"fmt"
"github.com/antonmedv/expr/ast"
"io/ioutil"
"os"
"github.com/antonmedv/expr"
"github.com/antonmedv/expr/checker"
"github.com/antonmedv/expr/compiler"
"github.com/antonmedv/expr/optimizer"
"github.com/antonmedv/expr/parser"
"github.com/antonmedv/expr/vm"
"github.com/sanity-io/litter"
)
var (
bytecode bool
debug bool
run bool
past bool
dot bool
repl bool
opt bool
typeCheck bool
)
func init() {
flag.BoolVar(&bytecode, "bytecode", false, "disassemble bytecode")
flag.BoolVar(&debug, "debug", false, "debug program")
flag.BoolVar(&run, "run", false, "run program")
flag.BoolVar(&past, "ast", false, "print ast")
flag.BoolVar(&dot, "dot", false, "dot format")
flag.BoolVar(&repl, "repl", false, "start repl")
flag.BoolVar(&opt, "opt", true, "do optimization")
flag.BoolVar(&typeCheck, "type", true, "do a type check")
}
func main() {
flag.Parse()
if past {
printAst()
os.Exit(0)
}
if bytecode {
printDisassemble()
os.Exit(0)
}
if run {
runProgram()
os.Exit(0)
}
if debug {
debugger()
os.Exit(0)
}
if repl {
startRepl()
os.Exit(0)
}
flag.Usage()
os.Exit(2)
}
func input() string {
b, err := ioutil.ReadAll(os.Stdin)
if err != nil {
panic(err)
}
return string(b)
}
func check(err error) {
if err != nil {
_, _ = fmt.Fprintf(os.Stderr, "%v\n", err)
os.Exit(1)
}
}
func printAst() {
tree, err := parser.Parse(input())
check(err)
if typeCheck {
_, err = checker.Check(tree, nil)
check(err)
if opt {
err = optimizer.Optimize(&tree.Node, nil)
check(err)
}
}
if !dot {
fmt.Println(ast.Dump(tree.Node))
return
}
dotAst(tree.Node)
}
func printDisassemble() {
tree, err := parser.Parse(input())
check(err)
if typeCheck {
_, err = checker.Check(tree, nil)
check(err)
if opt {
err = optimizer.Optimize(&tree.Node, nil)
check(err)
}
}
program, err := compiler.Compile(tree, nil)
check(err)
_, _ = fmt.Fprintf(os.Stdout, program.Disassemble())
}
func runProgram() {
tree, err := parser.Parse(input())
check(err)
if typeCheck {
_, err = checker.Check(tree, nil)
check(err)
if opt {
err = optimizer.Optimize(&tree.Node, nil)
check(err)
}
}
program, err := compiler.Compile(tree, nil)
check(err)
out, err := vm.Run(program, nil)
check(err)
litter.Dump(out)
}
func startRepl() {
scanner := bufio.NewScanner(os.Stdin)
fmt.Print("> ")
for scanner.Scan() {
line := scanner.Text()
out, err := expr.Eval(line, nil)
if err != nil {
fmt.Printf("%v\n", err)
goto prompt
}
fmt.Printf("%v\n", litter.Sdump(out))
prompt:
fmt.Print("> ")
}
}
expr-1.8.9/compiler/ 0000775 0000000 0000000 00000000000 13726232776 0014340 5 ustar 00root root 0000000 0000000 expr-1.8.9/compiler/compiler.go 0000664 0000000 0000000 00000030760 13726232776 0016507 0 ustar 00root root 0000000 0000000 package compiler
import (
"encoding/binary"
"fmt"
"math"
"reflect"
"github.com/antonmedv/expr/ast"
"github.com/antonmedv/expr/conf"
"github.com/antonmedv/expr/file"
"github.com/antonmedv/expr/parser"
. "github.com/antonmedv/expr/vm"
)
func Compile(tree *parser.Tree, config *conf.Config) (program *Program, err error) {
defer func() {
if r := recover(); r != nil {
err = fmt.Errorf("%v", r)
}
}()
c := &compiler{
index: make(map[interface{}]uint16),
locations: make(map[int]file.Location),
}
if config != nil {
c.mapEnv = config.MapEnv
c.cast = config.Expect
}
c.compile(tree.Node)
switch c.cast {
case reflect.Int64:
c.emit(OpCast, encode(0)...)
case reflect.Float64:
c.emit(OpCast, encode(1)...)
}
program = &Program{
Source: tree.Source,
Locations: c.locations,
Constants: c.constants,
Bytecode: c.bytecode,
}
return
}
type compiler struct {
locations map[int]file.Location
constants []interface{}
bytecode []byte
index map[interface{}]uint16
mapEnv bool
cast reflect.Kind
nodes []ast.Node
}
func (c *compiler) emit(op byte, b ...byte) int {
c.bytecode = append(c.bytecode, op)
current := len(c.bytecode)
c.bytecode = append(c.bytecode, b...)
var loc file.Location
if len(c.nodes) > 0 {
loc = c.nodes[len(c.nodes)-1].Location()
}
c.locations[current-1] = loc
return current
}
func (c *compiler) emitPush(value interface{}) int {
return c.emit(OpPush, c.makeConstant(value)...)
}
func (c *compiler) makeConstant(i interface{}) []byte {
hashable := true
switch reflect.TypeOf(i).Kind() {
case reflect.Slice, reflect.Map:
hashable = false
}
if hashable {
if p, ok := c.index[i]; ok {
return encode(p)
}
}
c.constants = append(c.constants, i)
if len(c.constants) > math.MaxUint16 {
panic("exceeded constants max space limit")
}
p := uint16(len(c.constants) - 1)
if hashable {
c.index[i] = p
}
return encode(p)
}
func (c *compiler) placeholder() []byte {
return []byte{0xFF, 0xFF}
}
func (c *compiler) patchJump(placeholder int) {
offset := len(c.bytecode) - 2 - placeholder
b := encode(uint16(offset))
c.bytecode[placeholder] = b[0]
c.bytecode[placeholder+1] = b[1]
}
func (c *compiler) calcBackwardJump(to int) []byte {
return encode(uint16(len(c.bytecode) + 1 + 2 - to))
}
func (c *compiler) compile(node ast.Node) {
c.nodes = append(c.nodes, node)
defer func() {
c.nodes = c.nodes[:len(c.nodes)-1]
}()
switch n := node.(type) {
case *ast.NilNode:
c.NilNode(n)
case *ast.IdentifierNode:
c.IdentifierNode(n)
case *ast.IntegerNode:
c.IntegerNode(n)
case *ast.FloatNode:
c.FloatNode(n)
case *ast.BoolNode:
c.BoolNode(n)
case *ast.StringNode:
c.StringNode(n)
case *ast.ConstantNode:
c.ConstantNode(n)
case *ast.UnaryNode:
c.UnaryNode(n)
case *ast.BinaryNode:
c.BinaryNode(n)
case *ast.MatchesNode:
c.MatchesNode(n)
case *ast.PropertyNode:
c.PropertyNode(n)
case *ast.IndexNode:
c.IndexNode(n)
case *ast.SliceNode:
c.SliceNode(n)
case *ast.MethodNode:
c.MethodNode(n)
case *ast.FunctionNode:
c.FunctionNode(n)
case *ast.BuiltinNode:
c.BuiltinNode(n)
case *ast.ClosureNode:
c.ClosureNode(n)
case *ast.PointerNode:
c.PointerNode(n)
case *ast.ConditionalNode:
c.ConditionalNode(n)
case *ast.ArrayNode:
c.ArrayNode(n)
case *ast.MapNode:
c.MapNode(n)
case *ast.PairNode:
c.PairNode(n)
default:
panic(fmt.Sprintf("undefined node type (%T)", node))
}
}
func (c *compiler) NilNode(node *ast.NilNode) {
c.emit(OpNil)
}
func (c *compiler) IdentifierNode(node *ast.IdentifierNode) {
v := c.makeConstant(node.Value)
if c.mapEnv {
c.emit(OpFetchMap, v...)
} else {
c.emit(OpFetch, v...)
}
}
func (c *compiler) IntegerNode(node *ast.IntegerNode) {
t := node.Type()
if t == nil {
c.emitPush(node.Value)
return
}
switch t.Kind() {
case reflect.Float32:
c.emitPush(float32(node.Value))
case reflect.Float64:
c.emitPush(float64(node.Value))
case reflect.Int:
c.emitPush(int(node.Value))
case reflect.Int8:
c.emitPush(int8(node.Value))
case reflect.Int16:
c.emitPush(int16(node.Value))
case reflect.Int32:
c.emitPush(int32(node.Value))
case reflect.Int64:
c.emitPush(int64(node.Value))
case reflect.Uint:
c.emitPush(uint(node.Value))
case reflect.Uint8:
c.emitPush(uint8(node.Value))
case reflect.Uint16:
c.emitPush(uint16(node.Value))
case reflect.Uint32:
c.emitPush(uint32(node.Value))
case reflect.Uint64:
c.emitPush(uint64(node.Value))
default:
c.emitPush(node.Value)
}
}
func (c *compiler) FloatNode(node *ast.FloatNode) {
c.emitPush(node.Value)
}
func (c *compiler) BoolNode(node *ast.BoolNode) {
if node.Value {
c.emit(OpTrue)
} else {
c.emit(OpFalse)
}
}
func (c *compiler) StringNode(node *ast.StringNode) {
c.emitPush(node.Value)
}
func (c *compiler) ConstantNode(node *ast.ConstantNode) {
c.emitPush(node.Value)
}
func (c *compiler) UnaryNode(node *ast.UnaryNode) {
c.compile(node.Node)
switch node.Operator {
case "!", "not":
c.emit(OpNot)
case "+":
// Do nothing
case "-":
c.emit(OpNegate)
default:
panic(fmt.Sprintf("unknown operator (%v)", node.Operator))
}
}
func (c *compiler) BinaryNode(node *ast.BinaryNode) {
l := kind(node.Left)
r := kind(node.Right)
switch node.Operator {
case "==":
c.compile(node.Left)
c.compile(node.Right)
if l == r && l == reflect.Int {
c.emit(OpEqualInt)
} else if l == r && l == reflect.String {
c.emit(OpEqualString)
} else {
c.emit(OpEqual)
}
case "!=":
c.compile(node.Left)
c.compile(node.Right)
c.emit(OpEqual)
c.emit(OpNot)
case "or", "||":
c.compile(node.Left)
end := c.emit(OpJumpIfTrue, c.placeholder()...)
c.emit(OpPop)
c.compile(node.Right)
c.patchJump(end)
case "and", "&&":
c.compile(node.Left)
end := c.emit(OpJumpIfFalse, c.placeholder()...)
c.emit(OpPop)
c.compile(node.Right)
c.patchJump(end)
case "in":
c.compile(node.Left)
c.compile(node.Right)
c.emit(OpIn)
case "not in":
c.compile(node.Left)
c.compile(node.Right)
c.emit(OpIn)
c.emit(OpNot)
case "<":
c.compile(node.Left)
c.compile(node.Right)
c.emit(OpLess)
case ">":
c.compile(node.Left)
c.compile(node.Right)
c.emit(OpMore)
case "<=":
c.compile(node.Left)
c.compile(node.Right)
c.emit(OpLessOrEqual)
case ">=":
c.compile(node.Left)
c.compile(node.Right)
c.emit(OpMoreOrEqual)
case "+":
c.compile(node.Left)
c.compile(node.Right)
c.emit(OpAdd)
case "-":
c.compile(node.Left)
c.compile(node.Right)
c.emit(OpSubtract)
case "*":
c.compile(node.Left)
c.compile(node.Right)
c.emit(OpMultiply)
case "/":
c.compile(node.Left)
c.compile(node.Right)
c.emit(OpDivide)
case "%":
c.compile(node.Left)
c.compile(node.Right)
c.emit(OpModulo)
case "**":
c.compile(node.Left)
c.compile(node.Right)
c.emit(OpExponent)
case "contains":
c.compile(node.Left)
c.compile(node.Right)
c.emit(OpContains)
case "startsWith":
c.compile(node.Left)
c.compile(node.Right)
c.emit(OpStartsWith)
case "endsWith":
c.compile(node.Left)
c.compile(node.Right)
c.emit(OpEndsWith)
case "..":
c.compile(node.Left)
c.compile(node.Right)
c.emit(OpRange)
default:
panic(fmt.Sprintf("unknown operator (%v)", node.Operator))
}
}
func (c *compiler) MatchesNode(node *ast.MatchesNode) {
if node.Regexp != nil {
c.compile(node.Left)
c.emit(OpMatchesConst, c.makeConstant(node.Regexp)...)
return
}
c.compile(node.Left)
c.compile(node.Right)
c.emit(OpMatches)
}
func (c *compiler) PropertyNode(node *ast.PropertyNode) {
c.compile(node.Node)
c.emit(OpProperty, c.makeConstant(node.Property)...)
}
func (c *compiler) IndexNode(node *ast.IndexNode) {
c.compile(node.Node)
c.compile(node.Index)
c.emit(OpIndex)
}
func (c *compiler) SliceNode(node *ast.SliceNode) {
c.compile(node.Node)
if node.To != nil {
c.compile(node.To)
} else {
c.emit(OpLen)
}
if node.From != nil {
c.compile(node.From)
} else {
c.emitPush(0)
}
c.emit(OpSlice)
}
func (c *compiler) MethodNode(node *ast.MethodNode) {
c.compile(node.Node)
for _, arg := range node.Arguments {
c.compile(arg)
}
c.emit(OpMethod, c.makeConstant(Call{Name: node.Method, Size: len(node.Arguments)})...)
}
func (c *compiler) FunctionNode(node *ast.FunctionNode) {
for _, arg := range node.Arguments {
c.compile(arg)
}
op := OpCall
if node.Fast {
op = OpCallFast
}
c.emit(op, c.makeConstant(Call{Name: node.Name, Size: len(node.Arguments)})...)
}
func (c *compiler) BuiltinNode(node *ast.BuiltinNode) {
switch node.Name {
case "len":
c.compile(node.Arguments[0])
c.emit(OpLen)
c.emit(OpRot)
c.emit(OpPop)
case "all":
c.compile(node.Arguments[0])
c.emit(OpBegin)
var loopBreak int
c.emitLoop(func() {
c.compile(node.Arguments[1])
loopBreak = c.emit(OpJumpIfFalse, c.placeholder()...)
c.emit(OpPop)
})
c.emit(OpTrue)
c.patchJump(loopBreak)
c.emit(OpEnd)
case "none":
c.compile(node.Arguments[0])
c.emit(OpBegin)
var loopBreak int
c.emitLoop(func() {
c.compile(node.Arguments[1])
c.emit(OpNot)
loopBreak = c.emit(OpJumpIfFalse, c.placeholder()...)
c.emit(OpPop)
})
c.emit(OpTrue)
c.patchJump(loopBreak)
c.emit(OpEnd)
case "any":
c.compile(node.Arguments[0])
c.emit(OpBegin)
var loopBreak int
c.emitLoop(func() {
c.compile(node.Arguments[1])
loopBreak = c.emit(OpJumpIfTrue, c.placeholder()...)
c.emit(OpPop)
})
c.emit(OpFalse)
c.patchJump(loopBreak)
c.emit(OpEnd)
case "one":
count := c.makeConstant("count")
c.compile(node.Arguments[0])
c.emit(OpBegin)
c.emitPush(0)
c.emit(OpStore, count...)
c.emitLoop(func() {
c.compile(node.Arguments[1])
c.emitCond(func() {
c.emit(OpInc, count...)
})
})
c.emit(OpLoad, count...)
c.emitPush(1)
c.emit(OpEqual)
c.emit(OpEnd)
case "filter":
count := c.makeConstant("count")
c.compile(node.Arguments[0])
c.emit(OpBegin)
c.emitPush(0)
c.emit(OpStore, count...)
c.emitLoop(func() {
c.compile(node.Arguments[1])
c.emitCond(func() {
c.emit(OpInc, count...)
c.emit(OpLoad, c.makeConstant("array")...)
c.emit(OpLoad, c.makeConstant("i")...)
c.emit(OpIndex)
})
})
c.emit(OpLoad, count...)
c.emit(OpEnd)
c.emit(OpArray)
case "map":
c.compile(node.Arguments[0])
c.emit(OpBegin)
size := c.emitLoop(func() {
c.compile(node.Arguments[1])
})
c.emit(OpLoad, size...)
c.emit(OpEnd)
c.emit(OpArray)
case "count":
count := c.makeConstant("count")
c.compile(node.Arguments[0])
c.emit(OpBegin)
c.emitPush(0)
c.emit(OpStore, count...)
c.emitLoop(func() {
c.compile(node.Arguments[1])
c.emitCond(func() {
c.emit(OpInc, count...)
})
})
c.emit(OpLoad, count...)
c.emit(OpEnd)
default:
panic(fmt.Sprintf("unknown builtin %v", node.Name))
}
}
func (c *compiler) emitCond(body func()) {
noop := c.emit(OpJumpIfFalse, c.placeholder()...)
c.emit(OpPop)
body()
jmp := c.emit(OpJump, c.placeholder()...)
c.patchJump(noop)
c.emit(OpPop)
c.patchJump(jmp)
}
func (c *compiler) emitLoop(body func()) []byte {
i := c.makeConstant("i")
size := c.makeConstant("size")
array := c.makeConstant("array")
c.emit(OpLen)
c.emit(OpStore, size...)
c.emit(OpStore, array...)
c.emitPush(0)
c.emit(OpStore, i...)
cond := len(c.bytecode)
c.emit(OpLoad, i...)
c.emit(OpLoad, size...)
c.emit(OpLess)
end := c.emit(OpJumpIfFalse, c.placeholder()...)
c.emit(OpPop)
body()
c.emit(OpInc, i...)
c.emit(OpJumpBackward, c.calcBackwardJump(cond)...)
c.patchJump(end)
c.emit(OpPop)
return size
}
func (c *compiler) ClosureNode(node *ast.ClosureNode) {
c.compile(node.Node)
}
func (c *compiler) PointerNode(node *ast.PointerNode) {
c.emit(OpLoad, c.makeConstant("array")...)
c.emit(OpLoad, c.makeConstant("i")...)
c.emit(OpIndex)
}
func (c *compiler) ConditionalNode(node *ast.ConditionalNode) {
c.compile(node.Cond)
otherwise := c.emit(OpJumpIfFalse, c.placeholder()...)
c.emit(OpPop)
c.compile(node.Exp1)
end := c.emit(OpJump, c.placeholder()...)
c.patchJump(otherwise)
c.emit(OpPop)
c.compile(node.Exp2)
c.patchJump(end)
}
func (c *compiler) ArrayNode(node *ast.ArrayNode) {
for _, node := range node.Nodes {
c.compile(node)
}
c.emitPush(len(node.Nodes))
c.emit(OpArray)
}
func (c *compiler) MapNode(node *ast.MapNode) {
for _, pair := range node.Pairs {
c.compile(pair)
}
c.emitPush(len(node.Pairs))
c.emit(OpMap)
}
func (c *compiler) PairNode(node *ast.PairNode) {
c.compile(node.Key)
c.compile(node.Value)
}
func encode(i uint16) []byte {
b := make([]byte, 2)
binary.LittleEndian.PutUint16(b, i)
return b
}
func kind(node ast.Node) reflect.Kind {
t := node.Type()
if t == nil {
return reflect.Invalid
}
return t.Kind()
}
expr-1.8.9/compiler/compiler_test.go 0000664 0000000 0000000 00000005252 13726232776 0017544 0 ustar 00root root 0000000 0000000 package compiler_test
import (
"math"
"reflect"
"testing"
"github.com/antonmedv/expr/compiler"
"github.com/antonmedv/expr/conf"
"github.com/antonmedv/expr/parser"
"github.com/antonmedv/expr/vm"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestCompile_debug(t *testing.T) {
input := `false && true && true`
tree, err := parser.Parse(input)
require.NoError(t, err)
_, err = compiler.Compile(tree, nil)
require.NoError(t, err)
}
func TestCompile(t *testing.T) {
type test struct {
input string
program vm.Program
}
var tests = []test{
{
`65535`,
vm.Program{
Constants: []interface{}{
int(math.MaxUint16),
},
Bytecode: []byte{
vm.OpPush, 0, 0,
},
},
},
{
`.5`,
vm.Program{
Constants: []interface{}{
float64(.5),
},
Bytecode: []byte{
vm.OpPush, 0, 0,
},
},
},
{
`true`,
vm.Program{
Bytecode: []byte{
vm.OpTrue,
},
},
},
{
`Name`,
vm.Program{
Constants: []interface{}{
"Name",
},
Bytecode: []byte{
vm.OpFetch, 0, 0,
},
},
},
{
`"string"`,
vm.Program{
Constants: []interface{}{
"string",
},
Bytecode: []byte{
vm.OpPush, 0, 0,
},
},
},
{
`"string" == "string"`,
vm.Program{
Constants: []interface{}{
"string",
},
Bytecode: []byte{
vm.OpPush, 0, 0,
vm.OpPush, 0, 0,
vm.OpEqual,
},
},
},
{
`1000000 == 1000000`,
vm.Program{
Constants: []interface{}{
int64(1000000),
},
Bytecode: []byte{
vm.OpPush, 0, 0,
vm.OpPush, 0, 0,
vm.OpEqual,
},
},
},
{
`-1`,
vm.Program{
Constants: []interface{}{1},
Bytecode: []byte{
vm.OpPush, 0, 0,
vm.OpNegate,
},
},
},
{
`true && true || true`,
vm.Program{
Bytecode: []byte{
vm.OpTrue,
vm.OpJumpIfFalse, 2, 0,
vm.OpPop,
vm.OpTrue,
vm.OpJumpIfTrue, 2, 0,
vm.OpPop,
vm.OpTrue,
},
},
},
}
for _, test := range tests {
node, err := parser.Parse(test.input)
require.NoError(t, err)
program, err := compiler.Compile(node, nil)
require.NoError(t, err, test.input)
assert.Equal(t, test.program.Disassemble(), program.Disassemble(), test.input)
}
}
func TestCompile_cast(t *testing.T) {
input := `1`
expected := &vm.Program{
Constants: []interface{}{
1,
},
Bytecode: []byte{
vm.OpPush, 0, 0,
vm.OpCast, 1, 0,
},
}
tree, err := parser.Parse(input)
require.NoError(t, err)
program, err := compiler.Compile(tree, &conf.Config{Expect: reflect.Float64})
require.NoError(t, err)
assert.Equal(t, expected.Disassemble(), program.Disassemble())
}
expr-1.8.9/compiler/patcher.go 0000664 0000000 0000000 00000001641 13726232776 0016317 0 ustar 00root root 0000000 0000000 package compiler
import (
"github.com/antonmedv/expr/ast"
"github.com/antonmedv/expr/conf"
)
type operatorPatcher struct {
ops map[string][]string
types conf.TypesTable
}
func (p *operatorPatcher) Enter(node *ast.Node) {}
func (p *operatorPatcher) Exit(node *ast.Node) {
binaryNode, ok := (*node).(*ast.BinaryNode)
if !ok {
return
}
fns, ok := p.ops[binaryNode.Operator]
if !ok {
return
}
leftType := binaryNode.Left.Type()
rightType := binaryNode.Right.Type()
_, fn, ok := conf.FindSuitableOperatorOverload(fns, p.types, leftType, rightType)
if ok {
newNode := &ast.FunctionNode{
Name: fn,
Arguments: []ast.Node{binaryNode.Left, binaryNode.Right},
}
ast.Patch(node, newNode)
}
}
func PatchOperators(node *ast.Node, config *conf.Config) {
if len(config.Operators) == 0 {
return
}
patcher := &operatorPatcher{ops: config.Operators, types: config.Types}
ast.Walk(node, patcher)
}
expr-1.8.9/conf/ 0000775 0000000 0000000 00000000000 13726232776 0013453 5 ustar 00root root 0000000 0000000 expr-1.8.9/conf/config.go 0000664 0000000 0000000 00000004104 13726232776 0015246 0 ustar 00root root 0000000 0000000 package conf
import (
"fmt"
"reflect"
"github.com/antonmedv/expr/ast"
"github.com/antonmedv/expr/vm"
)
type Config struct {
Env interface{}
MapEnv bool
Types TypesTable
Operators OperatorsTable
Expect reflect.Kind
Optimize bool
Strict bool
DefaultType reflect.Type
ConstExprFns map[string]reflect.Value
Visitors []ast.Visitor
err error
}
func New(env interface{}) *Config {
var mapEnv bool
var mapValueType reflect.Type
if _, ok := env.(map[string]interface{}); ok {
mapEnv = true
} else {
if reflect.ValueOf(env).Kind() == reflect.Map {
mapValueType = reflect.TypeOf(env).Elem()
}
}
return &Config{
Env: env,
MapEnv: mapEnv,
Types: CreateTypesTable(env),
Optimize: true,
Strict: true,
DefaultType: mapValueType,
ConstExprFns: make(map[string]reflect.Value),
}
}
// Check validates the compiler configuration.
func (c *Config) Check() error {
// Check that all functions that define operator overloading
// exist in environment and have correct signatures.
for op, fns := range c.Operators {
for _, fn := range fns {
fnType, ok := c.Types[fn]
if !ok || fnType.Type.Kind() != reflect.Func {
return fmt.Errorf("function %s for %s operator does not exist in environment", fn, op)
}
requiredNumIn := 2
if fnType.Method {
requiredNumIn = 3 // As first argument of method is receiver.
}
if fnType.Type.NumIn() != requiredNumIn || fnType.Type.NumOut() != 1 {
return fmt.Errorf("function %s for %s operator does not have a correct signature", fn, op)
}
}
}
// Check that all ConstExprFns are functions.
for name, fn := range c.ConstExprFns {
if fn.Kind() != reflect.Func {
return fmt.Errorf("const expression %q must be a function", name)
}
}
return c.err
}
func (c *Config) ConstExpr(name string) {
if c.Env == nil {
c.Error(fmt.Errorf("no environment for const expression: %v", name))
return
}
c.ConstExprFns[name] = vm.FetchFn(c.Env, name)
}
func (c *Config) Error(err error) {
if c.err == nil {
c.err = err
}
}
expr-1.8.9/conf/operators_table.go 0000664 0000000 0000000 00000001730 13726232776 0017170 0 ustar 00root root 0000000 0000000 package conf
import "reflect"
// OperatorsTable maps binary operators to corresponding list of functions.
// Functions should be provided in the environment to allow operator overloading.
type OperatorsTable map[string][]string
func FindSuitableOperatorOverload(fns []string, types TypesTable, l, r reflect.Type) (reflect.Type, string, bool) {
for _, fn := range fns {
fnType := types[fn]
firstInIndex := 0
if fnType.Method {
firstInIndex = 1 // As first argument to method is receiver.
}
firstArgType := fnType.Type.In(firstInIndex)
secondArgType := fnType.Type.In(firstInIndex + 1)
firstArgumentFit := l == firstArgType || (firstArgType.Kind() == reflect.Interface && (l == nil || l.Implements(firstArgType)))
secondArgumentFit := r == secondArgType || (secondArgType.Kind() == reflect.Interface && (r == nil || r.Implements(secondArgType)))
if firstArgumentFit && secondArgumentFit {
return fnType.Type.Out(0), fn, true
}
}
return nil, "", false
}
expr-1.8.9/conf/types_table.go 0000664 0000000 0000000 00000004115 13726232776 0016316 0 ustar 00root root 0000000 0000000 package conf
import "reflect"
type Tag struct {
Type reflect.Type
Method bool
Ambiguous bool
}
type TypesTable map[string]Tag
// CreateTypesTable creates types table for type checks during parsing.
// If struct is passed, all fields will be treated as variables,
// as well as all fields of embedded structs and struct itself.
//
// If map is passed, all items will be treated as variables
// (key as name, value as type).
func CreateTypesTable(i interface{}) TypesTable {
if i == nil {
return nil
}
types := make(TypesTable)
v := reflect.ValueOf(i)
t := reflect.TypeOf(i)
d := t
if t.Kind() == reflect.Ptr {
d = t.Elem()
}
switch d.Kind() {
case reflect.Struct:
types = FieldsFromStruct(d)
// Methods of struct should be gathered from original struct with pointer,
// as methods maybe declared on pointer receiver. Also this method retrieves
// all embedded structs methods as well, no need to recursion.
for i := 0; i < t.NumMethod(); i++ {
m := t.Method(i)
types[m.Name] = Tag{Type: m.Type, Method: true}
}
case reflect.Map:
for _, key := range v.MapKeys() {
value := v.MapIndex(key)
if key.Kind() == reflect.String && value.IsValid() && value.CanInterface() {
types[key.String()] = Tag{Type: reflect.TypeOf(value.Interface())}
}
}
// A map may have method too.
for i := 0; i < t.NumMethod(); i++ {
m := t.Method(i)
types[m.Name] = Tag{Type: m.Type, Method: true}
}
}
return types
}
func FieldsFromStruct(t reflect.Type) TypesTable {
types := make(TypesTable)
t = dereference(t)
if t == nil {
return types
}
switch t.Kind() {
case reflect.Struct:
for i := 0; i < t.NumField(); i++ {
f := t.Field(i)
if f.Anonymous {
for name, typ := range FieldsFromStruct(f.Type) {
if _, ok := types[name]; ok {
types[name] = Tag{Ambiguous: true}
} else {
types[name] = typ
}
}
}
types[f.Name] = Tag{Type: f.Type}
}
}
return types
}
func dereference(t reflect.Type) reflect.Type {
if t == nil {
return nil
}
if t.Kind() == reflect.Ptr {
t = dereference(t.Elem())
}
return t
}
expr-1.8.9/docgen/ 0000775 0000000 0000000 00000000000 13726232776 0013765 5 ustar 00root root 0000000 0000000 expr-1.8.9/docgen/README.md 0000664 0000000 0000000 00000001347 13726232776 0015251 0 ustar 00root root 0000000 0000000 # DocGen
This package provides documentation generator with JSON or Markdown output.
## Usage
Create a file and put next code into it.
```go
package main
import (
"encoding/json"
"fmt"
"github.com/antonmedv/expr/docgen"
)
func main() {
// TODO: Replace env with your own types.
doc := docgen.CreateDoc(env)
buf, err := json.MarshalIndent(doc, "", " ")
if err != nil {
panic(err)
}
fmt.Println(string(buf))
}
```
Run `go run your_file.go`. Documentation will be printed in JSON format.
## Markdown
To generate markdown documentation:
```go
package main
import "github.com/antonmedv/expr/docgen"
func main() {
// TODO: Replace env with your own types.
doc := docgen.CreateDoc(env)
print(doc.Markdown())
}
```
expr-1.8.9/docgen/docgen.go 0000664 0000000 0000000 00000013342 13726232776 0015556 0 ustar 00root root 0000000 0000000 package docgen
import (
"reflect"
"regexp"
"strings"
"github.com/antonmedv/expr/conf"
)
// Kind can be any of array, map, struct, func, string, int, float, bool or any.
type Kind string
// Identifier represents variable names and field names.
type Identifier string
// TypeName is a name of type in types map.
type TypeName string
type Context struct {
Variables map[Identifier]*Type `json:"variables"`
Types map[TypeName]*Type `json:"types"`
pkgPath string
}
type Type struct {
Name TypeName `json:"name,omitempty"`
Kind Kind `json:"kind,omitempty"`
Type *Type `json:"type,omitempty"`
Key *Type `json:"key_type,omitempty"`
Fields map[Identifier]*Type `json:"fields,omitempty"`
Arguments []*Type `json:"arguments,omitempty"`
Return *Type `json:"return,omitempty"`
}
var (
Operators = []string{"matches", "contains", "startsWith", "endsWith"}
Builtins = map[Identifier]*Type{
"true": {Kind: "bool"},
"false": {Kind: "bool"},
"len": {Kind: "func", Arguments: []*Type{{Kind: "array", Type: &Type{Kind: "any"}}}, Return: &Type{Kind: "int"}},
"all": {Kind: "func", Arguments: []*Type{{Kind: "array", Type: &Type{Kind: "any"}}, {Kind: "func"}}, Return: &Type{Kind: "bool"}},
"none": {Kind: "func", Arguments: []*Type{{Kind: "array", Type: &Type{Kind: "any"}}, {Kind: "func"}}, Return: &Type{Kind: "bool"}},
"any": {Kind: "func", Arguments: []*Type{{Kind: "array", Type: &Type{Kind: "any"}}, {Kind: "func"}}, Return: &Type{Kind: "bool"}},
"one": {Kind: "func", Arguments: []*Type{{Kind: "array", Type: &Type{Kind: "any"}}, {Kind: "func"}}, Return: &Type{Kind: "bool"}},
"filter": {Kind: "func", Arguments: []*Type{{Kind: "array", Type: &Type{Kind: "any"}}, {Kind: "func"}}, Return: &Type{Kind: "array", Type: &Type{Kind: "any"}}},
"map": {Kind: "func", Arguments: []*Type{{Kind: "array", Type: &Type{Kind: "any"}}, {Kind: "func"}}, Return: &Type{Kind: "array", Type: &Type{Kind: "any"}}},
"count": {Kind: "func", Arguments: []*Type{{Kind: "array", Type: &Type{Kind: "any"}}, {Kind: "func"}}, Return: &Type{Kind: "int"}},
}
)
func CreateDoc(i interface{}) *Context {
c := &Context{
Variables: make(map[Identifier]*Type),
Types: make(map[TypeName]*Type),
pkgPath: dereference(reflect.TypeOf(i)).PkgPath(),
}
for name, t := range conf.CreateTypesTable(i) {
if t.Ambiguous {
continue
}
c.Variables[Identifier(name)] = c.use(t.Type, fromMethod(t.Method))
}
for _, op := range Operators {
c.Variables[Identifier(op)] = &Type{
Kind: "operator",
}
}
for builtin, t := range Builtins {
c.Variables[builtin] = t
}
return c
}
type config struct {
method bool
}
type option func(c *config)
func fromMethod(b bool) option {
return func(c *config) {
c.method = b
}
}
func (c *Context) use(t reflect.Type, ops ...option) *Type {
config := &config{}
for _, op := range ops {
op(config)
}
methods := make([]reflect.Method, 0)
// Methods of struct should be gathered from original struct with pointer,
// as methods maybe declared on pointer receiver. Also this method retrieves
// all embedded structs methods as well, no need to recursion.
for i := 0; i < t.NumMethod(); i++ {
m := t.Method(i)
if isPrivate(m.Name) || isProtobuf(m.Name) {
continue
}
methods = append(methods, m)
}
t = dereference(t)
// Only named types will have methods defined on them.
// It maybe not even struct, but we gonna call then
// structs in appendix anyway.
if len(methods) > 0 {
goto appendix
}
// This switch only for "simple" types.
switch t.Kind() {
case reflect.Bool:
return &Type{Kind: "bool"}
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
fallthrough
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return &Type{Kind: "int"}
case reflect.Float32, reflect.Float64:
return &Type{Kind: "float"}
case reflect.String:
return &Type{Kind: "string"}
case reflect.Interface:
return &Type{Kind: "any"}
case reflect.Array, reflect.Slice:
return &Type{
Kind: "array",
Type: c.use(t.Elem()),
}
case reflect.Map:
return &Type{
Kind: "map",
Key: c.use(t.Key()),
Type: c.use(t.Elem()),
}
case reflect.Struct:
goto appendix
case reflect.Func:
arguments := make([]*Type, 0)
start := 0
if config.method {
start = 1
}
for i := start; i < t.NumIn(); i++ {
arguments = append(arguments, c.use(t.In(i)))
}
f := &Type{
Kind: "func",
Arguments: arguments,
}
if t.NumOut() > 0 {
f.Return = c.use(t.Out(0))
}
return f
}
appendix:
name := TypeName(t.String())
if c.pkgPath == t.PkgPath() {
name = TypeName(t.Name())
}
anonymous := t.Name() == ""
a, ok := c.Types[name]
if !ok {
a = &Type{
Kind: "struct",
Fields: make(map[Identifier]*Type),
}
// baseNode a should be saved before starting recursion, or it will never end.
if !anonymous {
c.Types[name] = a
}
for name, field := range conf.FieldsFromStruct(t) {
if isPrivate(name) || isProtobuf(name) || field.Ambiguous {
continue
}
a.Fields[Identifier(name)] = c.use(field.Type)
}
for _, m := range methods {
if isPrivate(m.Name) || isProtobuf(m.Name) {
continue
}
a.Fields[Identifier(m.Name)] = c.use(m.Type, fromMethod(true))
}
}
if anonymous {
return a
}
return &Type{
Kind: "struct",
Name: name,
}
}
var isCapital = regexp.MustCompile("^[A-Z]")
func isPrivate(s string) bool {
return !isCapital.Match([]byte(s))
}
func isProtobuf(s string) bool {
return strings.HasPrefix(s, "XXX_")
}
func dereference(t reflect.Type) reflect.Type {
if t == nil {
return nil
}
if t.Kind() == reflect.Ptr {
t = dereference(t.Elem())
}
return t
}
expr-1.8.9/docgen/docgen_test.go 0000664 0000000 0000000 00000010454 13726232776 0016616 0 ustar 00root root 0000000 0000000 package docgen_test
import (
. "github.com/antonmedv/expr/docgen"
"github.com/sanity-io/litter"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"math"
"testing"
"time"
)
type Tweet struct {
Size int
Message string
}
type Env struct {
Tweets []Tweet
Config struct {
MaxSize int32
}
Env map[string]interface{}
// NOTE: conflicting type name
TimeWeekday time.Weekday
Weekday Weekday
}
type Weekday int
func (Weekday) String() string {
return ""
}
type Duration int
func (Duration) String() string {
return ""
}
func (*Env) Duration(s string) Duration {
return Duration(0)
}
func TestCreateDoc(t *testing.T) {
Operators = nil
Builtins = nil
doc := CreateDoc(&Env{})
expected := &Context{
Variables: map[Identifier]*Type{
"Tweets": {
Kind: "array",
Type: &Type{
Kind: "struct",
Name: "Tweet",
},
},
"Config": {
Kind: "struct",
Fields: map[Identifier]*Type{
"MaxSize": {Kind: "int"},
},
},
"Env": {
Kind: "map",
Key: &Type{Kind: "string"},
Type: &Type{Kind: "any"},
},
"Duration": {
Kind: "func",
Arguments: []*Type{
{Kind: "string"},
},
Return: &Type{Kind: "struct", Name: "Duration"},
},
"TimeWeekday": {
Name: "time.Weekday",
Kind: "struct",
},
"Weekday": {
Name: "Weekday",
Kind: "struct",
},
},
Types: map[TypeName]*Type{
"Tweet": {
Kind: "struct",
Fields: map[Identifier]*Type{
"Size": {Kind: "int"},
"Message": {Kind: "string"},
},
},
"Duration": {
Kind: "struct",
Fields: map[Identifier]*Type{
"String": {
Kind: "func",
Arguments: []*Type{},
Return: &Type{
Kind: "string",
},
},
},
},
"time.Weekday": {
Kind: "struct",
Fields: map[Identifier]*Type{
"String": {
Kind: "func",
Arguments: []*Type{},
Return: &Type{
Kind: "string",
},
},
},
},
"Weekday": {
Kind: "struct",
Fields: map[Identifier]*Type{
"String": {
Kind: "func",
Arguments: []*Type{},
Return: &Type{
Kind: "string",
},
},
},
},
},
}
assert.Equal(t, litter.Sdump(expected), litter.Sdump(doc))
}
type A struct {
AmbiguousField int
OkField int
}
type B struct {
AmbiguousField string
}
type C struct {
A
B
}
type EnvAmbiguous struct {
A
B
C C
}
func TestCreateDoc_Ambiguous(t *testing.T) {
doc := CreateDoc(&EnvAmbiguous{})
expected := &Context{
Variables: map[Identifier]*Type{
"A": {
Kind: "struct",
Name: "A",
},
"B": {
Kind: "struct",
Name: "B",
},
"OkField": {
Kind: "int",
},
"C": {
Kind: "struct",
Name: "C",
},
},
Types: map[TypeName]*Type{
"A": {
Kind: "struct",
Fields: map[Identifier]*Type{
"AmbiguousField": {Kind: "int"},
"OkField": {Kind: "int"},
},
},
"B": {
Kind: "struct",
Fields: map[Identifier]*Type{
"AmbiguousField": {Kind: "string"},
},
},
"C": {
Kind: "struct",
Fields: map[Identifier]*Type{
"A": {Kind: "struct", Name: "A"},
"B": {Kind: "struct", Name: "B"},
"OkField": {Kind: "int"},
},
},
},
}
assert.Equal(t, litter.Sdump(expected), litter.Sdump(doc))
}
func TestCreateDoc_FromMap(t *testing.T) {
env := map[string]interface{}{
"Tweets": []*Tweet{},
"Config": struct {
MaxSize int
}{},
"Max": math.Max,
}
Operators = nil
Builtins = nil
doc := CreateDoc(env)
expected := &Context{
Variables: map[Identifier]*Type{
"Tweets": {
Kind: "array",
Type: &Type{
Kind: "struct",
Name: "docgen_test.Tweet",
},
},
"Config": {
Kind: "struct",
Fields: map[Identifier]*Type{
"MaxSize": {Kind: "int"},
},
},
"Max": {
Kind: "func",
Arguments: []*Type{
{Kind: "float"},
{Kind: "float"},
},
Return: &Type{Kind: "float"},
},
},
Types: map[TypeName]*Type{
"docgen_test.Tweet": {
Kind: "struct",
Fields: map[Identifier]*Type{
"Size": {Kind: "int"},
"Message": {Kind: "string"},
},
},
},
}
require.Equal(t, litter.Sdump(expected), litter.Sdump(doc))
}
func TestContext_Markdown(t *testing.T) {
doc := CreateDoc(&Env{})
md := doc.Markdown()
require.True(t, len(md) > 0)
}
expr-1.8.9/docgen/markdown.go 0000664 0000000 0000000 00000004457 13726232776 0016150 0 ustar 00root root 0000000 0000000 package docgen
import (
"fmt"
"sort"
"strings"
)
func (c *Context) Markdown() string {
var variables []string
for name := range c.Variables {
variables = append(variables, string(name))
}
var types []string
for name := range c.Types {
types = append(types, string(name))
}
sort.Strings(variables)
sort.Strings(types)
out := `### Variables
| Name | Type |
|------|------|
`
for _, name := range variables {
v := c.Variables[Identifier(name)]
if v.Kind == "func" {
continue
}
if v.Kind == "operator" {
continue
}
out += fmt.Sprintf("| %v | %v |\n", name, link(v))
}
out += `
### Functions
| Name | Return type |
|------|-------------|
`
for _, name := range variables {
v := c.Variables[Identifier(name)]
if v.Kind == "func" {
args := make([]string, len(v.Arguments))
for i, arg := range v.Arguments {
args[i] = link(arg)
}
out += fmt.Sprintf("| %v(%v) | %v |\n", name, strings.Join(args, ", "), link(v.Return))
}
}
out += "\n### Types\n"
for _, name := range types {
t := c.Types[TypeName(name)]
out += fmt.Sprintf("#### %v\n", name)
out += fields(t)
out += "\n"
}
return out
}
func link(t *Type) string {
if t == nil {
return "nil"
}
if t.Name != "" {
return fmt.Sprintf("[%v](#%v)", t.Name, t.Name)
}
if t.Kind == "array" {
return fmt.Sprintf("array(%v)", link(t.Type))
}
if t.Kind == "map" {
return fmt.Sprintf("map(%v => %v)", link(t.Key), link(t.Type))
}
return fmt.Sprintf("`%v`", t.Kind)
}
func fields(t *Type) string {
var fields []string
for field := range t.Fields {
fields = append(fields, string(field))
}
sort.Strings(fields)
out := ""
foundFields := false
for _, name := range fields {
v := t.Fields[Identifier(name)]
if v.Kind != "func" {
if !foundFields {
out += "| Field | Type |\n|---|---|\n"
}
foundFields = true
out += fmt.Sprintf("| %v | %v |\n", name, link(v))
}
}
foundMethod := false
for _, name := range fields {
v := t.Fields[Identifier(name)]
if v.Kind == "func" {
if !foundMethod {
out += "\n| Method | Returns |\n|---|---|\n"
}
foundMethod = true
args := make([]string, len(v.Arguments))
for i, arg := range v.Arguments {
args[i] = link(arg)
}
out += fmt.Sprintf("| %v(%v) | %v |\n", name, strings.Join(args, ", "), link(v.Return))
}
}
return out
}
expr-1.8.9/docs/ 0000775 0000000 0000000 00000000000 13726232776 0013456 5 ustar 00root root 0000000 0000000 expr-1.8.9/docs/Getting-Started.md 0000664 0000000 0000000 00000003763 13726232776 0017016 0 ustar 00root root 0000000 0000000 # Getting Started
**Expr** provides a package for evaluating arbitrary expressions as well as type checking of such expression.
## Evaluate
```go
package main
import (
"fmt"
"github.com/antonmedv/expr"
)
func main() {
env := map[string]interface{}{
"foo": 1,
"bar": 2,
}
out, err := expr.Eval("foo + bar", env)
if err != nil {
panic(err)
}
fmt.Print(out)
}
```
## Compile
Usually we want to compile the code on save (For example, in [web user interface](https://antonmedv.github.io/expr/)).
```go
package main
import (
"fmt"
"github.com/antonmedv/expr"
)
func main() {
env := map[string]interface{}{
"greet": "Hello, %v!",
"names": []string{"world", "you"},
"sprintf": fmt.Sprintf, // You can pass any functions.
}
code := `sprintf(greet, names[0])`
// Compile code into bytecode. This step can be done once and program may be reused.
// Specify environment for type check.
program, err := expr.Compile(code, expr.Env(env))
if err != nil {
panic(err)
}
output, err := expr.Run(program, env)
if err != nil {
panic(err)
}
fmt.Print(output)
}
```
You may use existing types. For example, an environment can be a struct.
```go
package main
import (
"fmt"
"time"
"github.com/antonmedv/expr"
)
type Env struct {
Tweets []Tweet
}
// Methods defined on such struct will be functions.
func (Env) Format(t time.Time) string { return t.Format(time.RFC822) }
type Tweet struct {
Text string
Date time.Time
}
func main() {
code := `map(filter(Tweets, {len(.Text) > 0}), {.Text + Format(.Date)})`
// We can use an empty instance of the struct as an environment.
program, err := expr.Compile(code, expr.Env(Env{}))
if err != nil {
panic(err)
}
env := Env{
Tweets: []Tweet{{"Oh My God!", time.Now()}, {"How you doin?", time.Now()}, {"Could I be wearing any more clothes?", time.Now()}},
}
output, err := expr.Run(program, env)
if err != nil {
panic(err)
}
fmt.Print(output)
}
```
* [Contents](README.md)
* Next: [Operator Override](Operator-Override.md)
expr-1.8.9/docs/Language-Definition.md 0000664 0000000 0000000 00000007023 13726232776 0017613 0 ustar 00root root 0000000 0000000 # Language Definition
**Expr** package uses a specific syntax. In this document, you can find all supported
syntaxes.
## Supported Literals
The package supports:
* **strings** - single and double quotes (e.g. `"hello"`, `'hello'`)
* **numbers** - e.g. `103`, `2.5`, `.5`
* **arrays** - e.g. `[1, 2, 3]`
* **maps** - e.g. `{foo: "bar"}`
* **booleans** - `true` and `false`
* **nil** - `nil`
## Digit separators
Integer literals may contain digit separators to allow digit grouping into more legible forms.
Example:
```
10_000_000_000
```
## Accessing Public Properties
Public properties on structs can be accessed by using the `.` syntax.
If you pass an array into an expression, use the `[]` syntax to access array keys.
```js
foo.Array[0].Value
```
## Functions and Methods
Functions may be called using `()` syntax. The `.` syntax can also be used to call methods on an struct.
```js
price.String()
```
## Supported Operators
The package comes with a lot of operators:
### Arithmetic Operators
* `+` (addition)
* `-` (subtraction)
* `*` (multiplication)
* `/` (division)
* `%` (modulus)
* `**` (pow)
Example:
```js
life + universe + everything
```
### Comparison Operators
* `==` (equal)
* `!=` (not equal)
* `<` (less than)
* `>` (greater than)
* `<=` (less than or equal to)
* `>=` (greater than or equal to)
### Logical Operators
* `not` or `!`
* `and` or `&&`
* `or` or `||`
Example:
```
life < universe || life < everything
```
### String Operators
* `+` (concatenation)
* `matches` (regex match)
* `contains` (string contains)
* `startsWith` (has prefix)
* `endsWith` (has suffix)
To test if a string does *not* match a regex, use the logical `not` operator in combination with the `matches` operator:
```js
not ("foo" matches "^b.+")
```
You must use parenthesis because the unary operator `not` has precedence over the binary operator `matches`.
Example:
```js
'Arthur' + ' ' + 'Dent'
```
Result will be set to `Arthur Dent`.
### Membership Operators
* `in` (contain)
* `not in` (does not contain)
Example:
```js
user.Group in ["human_resources", "marketing"]
```
```js
"foo" in {foo: 1, bar: 2}
```
### Numeric Operators
* `..` (range)
Example:
```js
user.Age in 18..45
```
The range is inclusive:
```js
1..3 == [1, 2, 3]
```
### Ternary Operators
* `foo ? 'yes' : 'no'`
Example:
```js
user.Age > 30 ? "mature" : "immature"
```
## Builtin functions
* `len` (length of array, map or string)
* `all` (will return `true` if all element satisfies the predicate)
* `none` (will return `true` if all element does NOT satisfies the predicate)
* `any` (will return `true` if any element satisfies the predicate)
* `one` (will return `true` if exactly ONE element satisfies the predicate)
* `filter` (filter array by the predicate)
* `map` (map all items with the closure)
* `count` (returns number of elements what satisfies the predicate)
Examples:
Ensure all tweets are less than 280 chars.
```js
all(Tweets, {.Size < 280})
```
Ensure there is exactly one winner.
```js
one(Participants, {.Winner})
```
## Closures
* `{...}` (closure)
Closures allowed only with builtin functions. To access current item use `#` symbol.
```js
map(0..9, {# / 2})
```
If the item of array is struct, it's possible to access fields of struct with omitted `#` symbol (`#.Value` becomes `.Value`).
```js
filter(Tweets, {len(.Value) > 280})
```
## Slices
* `array[:]` (slice)
Slices can work with arrays or strings.
Example:
Variable `array` is `[1,2,3,4,5]`.
```js
array[1:5] == [2,3,4]
array[3:] == [4,5]
array[:4] == [1,2,3]
array[:] == array
```
expr-1.8.9/docs/Operator-Override.md 0000664 0000000 0000000 00000002673 13726232776 0017360 0 ustar 00root root 0000000 0000000 # Operator Override
**Expr** supports operator overriding. For example, you may rewrite such expression:
```js
Now().Sub(CreatedAt)
```
To use `-` operator:
```js
Now() - CreatedAt
```
To override operator use [expr.Operator](https://pkg.go.dev/github.com/antonmedv/expr?tab=doc#Operator):
```go
package main
import (
"fmt"
"time"
"github.com/antonmedv/expr"
)
func main() {
code := `(Now() - CreatedAt).Hours() / 24 / 365`
// We can define options before compiling.
options := []expr.Option{
expr.Env(Env{}),
expr.Operator("-", "Sub"), // Override `-` with function `Sub`.
}
program, err := expr.Compile(code, options...)
if err != nil {
panic(err)
}
env := Env{
CreatedAt: time.Date(1987, time.November, 24, 20, 0, 0, 0, time.UTC),
}
output, err := expr.Run(program, env)
if err != nil {
panic(err)
}
fmt.Print(output)
}
type Env struct {
datetime
CreatedAt time.Time
}
// Functions may be defined on embedded structs as well.
type datetime struct{}
func (datetime) Now() time.Time { return time.Now() }
func (datetime) Sub(a, b time.Time) time.Duration { return a.Sub(b) }
```
**Expr** uses defined functions on `Env` for operator overloading. If types of operands match types of a function,
the operator will be replaced with the function call.
Complete example can be found here: [dates_test.go](examples/dates_test.go).
* [Contents](README.md)
* Next: [Visitor and Patch](Visitor-and-Patch.md)
expr-1.8.9/docs/Optimizations.md 0000664 0000000 0000000 00000003647 13726232776 0016663 0 ustar 00root root 0000000 0000000 # Optimizations
Expr has a bunch of optimization which will produce more optimal program during compile step.
## In array
```js
value in ['foo', 'bar', 'baz']
```
If expr finds an `in` or `not in` expression with an array, it will be transformed into:
```js
value in {"foo": true, "bar": true, "baz": true}
```
## Constant folding
Arithmetic expressions with constants is computed on compile step and replaced with result.
```js
-(2-5)**3-2/(+4-3)+-2
```
Will be compiled to just single number:
```js
23
```
So in expressions it's safe to use some arithmetics for better readability:
```js
percentage > 0.3 * 100
```
As it will be simpified to:
```js
percentage > 30
```
## In range
```js
user.Age in 18..32
```
Will be replaced with binary operator:
```js
18 <= user.Age && user.Age <= 32
```
`not in` operator will also work.
## Const range
```js
1..10_000
```
Ranges computed on compile stage, repleced with preallocated slices.
## Const expr
If some function marked as constant expression with `expr.ConstExpr`. It will be replaced with result
of call, if all arguments are constants.
```go
expr.ConstExpt("fib")
```
```js
fib(42)
```
Will be replaced with result of `fib(42)` on compile step. No need to calculate it during runtime.
[ConstExpr Example](https://pkg.go.dev/github.com/antonmedv/expr?tab=doc#ConstExpr)
## Reuse VM
It is possible to reuse a virtual machine between re-runs on the program.
This adds a small increase in performance (from 4% to 40% depending on a program).
```go
package main
import (
"fmt"
"github.com/antonmedv/expr"
"github.com/antonmedv/expr/vm"
)
func main() {
env := map[string]interface{}{
"foo": 1,
"bar": 2,
}
program, err := expr.Compile("foo + bar", expr.Env(env))
if err != nil {
panic(err)
}
// Reuse this vm instance between runs
v := vm.VM{}
out, err := v.Run(program, env)
if err != nil {
panic(err)
}
fmt.Print(out)
}
```
* [Contents](README.md)
expr-1.8.9/docs/README.md 0000664 0000000 0000000 00000000345 13726232776 0014737 0 ustar 00root root 0000000 0000000 # Documentation
* [Getting Started](Getting-Started.md)
* [Language Definition](Language-Definition.md)
* [Operator Override](Operator-Override.md)
* [Visitor and Patch](Visitor-and-Patch.md)
* [Optimizations](Optimizations.md)
expr-1.8.9/docs/Visitor-and-Patch.md 0000664 0000000 0000000 00000006036 13726232776 0017241 0 ustar 00root root 0000000 0000000 # Visitor and Patch
[ast](https://pkg.go.dev/github.com/antonmedv/expr/ast?tab=doc) package provides `ast.Visitor` interface and `ast.Walk` function.
You can use it for traveling ast tree of compiled program.
For example if you want to collect all variable names:
```go
package main
import (
"fmt"
"github.com/antonmedv/expr/ast"
"github.com/antonmedv/expr/parser"
)
type visitor struct {
identifiers []string
}
func (v *visitor) Enter(node *ast.Node) {}
func (v *visitor) Exit(node *ast.Node) {
if n, ok := (*node).(*ast.IdentifierNode); ok {
v.identifiers = append(v.identifiers, n.Value)
}
}
func main() {
tree, err := parser.Parse("foo + bar")
if err != nil {
panic(err)
}
visitor := &visitor{}
ast.Walk(&tree.Node, visitor)
fmt.Printf("%v", visitor.identifiers) // outputs [foo bar]
}
```
## Patch
Implemented visitor can be applied before compiling AST to bytecode in `expr.Compile` function.
```go
program, err := expr.Compile(code, expr.Patch(&visitor{}))
```
This can be useful for some edge cases, there you want to extend functionality of **Expr** language.
In next example we are going to replace expression `list[-1]` with `list[len(list)-1]`.
```go
package main
import (
"fmt"
"github.com/antonmedv/expr"
"github.com/antonmedv/expr/ast"
)
func main() {
env := map[string]interface{}{
"list": []int{1, 2, 3},
}
code := `list[-1]` // will output 3
program, err := expr.Compile(code, expr.Env(env), expr.Patch(&patcher{}))
if err != nil {
panic(err)
}
output, err := expr.Run(program, env)
if err != nil {
panic(err)
}
fmt.Print(output)
}
type patcher struct{}
func (p *patcher) Enter(_ *ast.Node) {}
func (p *patcher) Exit(node *ast.Node) {
n, ok := (*node).(*ast.IndexNode)
if !ok {
return
}
unary, ok := n.Index.(*ast.UnaryNode)
if !ok {
return
}
if unary.Operator == "-" {
ast.Patch(&n.Index, &ast.BinaryNode{
Operator: "-",
Left: &ast.BuiltinNode{Name: "len", Arguments: []ast.Node{n.Node}},
Right: unary.Node,
})
}
}
```
Type information is also available. Here is an example, there all `fmt.Stringer` interface automatically
converted to `string` type.
```go
package main
import (
"fmt"
"reflect"
"github.com/antonmedv/expr"
"github.com/antonmedv/expr/ast"
)
func main() {
code := `Price == "$100"`
program, err := expr.Compile(code, expr.Env(Env{}), expr.Patch(&stringerPatcher{}))
if err != nil {
panic(err)
}
env := Env{100_00}
output, err := expr.Run(program, env)
if err != nil {
panic(err)
}
fmt.Print(output)
}
type Env struct {
Price Price
}
type Price int
func (p Price) String() string {
return fmt.Sprintf("$%v", int(p)/100)
}
var stringer = reflect.TypeOf((*fmt.Stringer)(nil)).Elem()
type stringerPatcher struct{}
func (p *stringerPatcher) Enter(_ *ast.Node) {}
func (p *stringerPatcher) Exit(node *ast.Node) {
t := (*node).Type()
if t == nil {
return
}
if t.Implements(stringer) {
ast.Patch(node, &ast.MethodNode{
Node: *node,
Method: "String",
})
}
}
```
* [Contents](README.md)
* Next: [Optimizations](Optimizations.md)
expr-1.8.9/docs/examples/ 0000775 0000000 0000000 00000000000 13726232776 0015274 5 ustar 00root root 0000000 0000000 expr-1.8.9/docs/examples/dates_test.go 0000664 0000000 0000000 00000005004 13726232776 0017761 0 ustar 00root root 0000000 0000000 package examples
import (
"testing"
"time"
"github.com/antonmedv/expr"
"github.com/stretchr/testify/require"
)
func TestExamples_dates(t *testing.T) {
code := `
Now() > Date("2020-01-01") &&
Now() - CreatedAt > Duration("24h")
`
options := []expr.Option{
expr.Env(Env{}),
// Operators override for date comprising.
expr.Operator("==", "Equal"),
expr.Operator("<", "Before"),
expr.Operator("<=", "BeforeOrEqual"),
expr.Operator(">", "After"),
expr.Operator(">=", "AfterOrEqual"),
// Time and duration manipulation.
expr.Operator("+", "Add"),
expr.Operator("-", "Sub"),
// Operators override for duration comprising.
expr.Operator("==", "EqualDuration"),
expr.Operator("<", "BeforeDuration"),
expr.Operator("<=", "BeforeOrEqualDuration"),
expr.Operator(">", "AfterDuration"),
expr.Operator(">=", "AfterOrEqualDuration"),
}
program, err := expr.Compile(code, options...)
require.NoError(t, err)
env := Env{
CreatedAt: Env{}.Date("2018-07-14"), // first commit date
}
output, err := expr.Run(program, env)
require.NoError(t, err)
require.Equal(t, true, output)
}
type Env struct {
datetime
CreatedAt time.Time
}
type datetime struct{}
func (datetime) Date(s string) time.Time {
t, err := time.Parse("2006-01-02", s)
if err != nil {
panic(err)
}
return t
}
func (datetime) Duration(s string) time.Duration {
d, err := time.ParseDuration(s)
if err != nil {
panic(err)
}
return d
}
func (datetime) Now() time.Time { return time.Now() }
func (datetime) Equal(a, b time.Time) bool { return a.Equal(b) }
func (datetime) Before(a, b time.Time) bool { return a.Before(b) }
func (datetime) BeforeOrEqual(a, b time.Time) bool { return a.Before(b) || a.Equal(b) }
func (datetime) After(a, b time.Time) bool { return a.After(b) }
func (datetime) AfterOrEqual(a, b time.Time) bool { return a.After(b) || a.Equal(b) }
func (datetime) Add(a time.Time, b time.Duration) time.Time { return a.Add(b) }
func (datetime) Sub(a, b time.Time) time.Duration { return a.Sub(b) }
func (datetime) EqualDuration(a, b time.Duration) bool { return a == b }
func (datetime) BeforeDuration(a, b time.Duration) bool { return a < b }
func (datetime) BeforeOrEqualDuration(a, b time.Duration) bool { return a <= b }
func (datetime) AfterDuration(a, b time.Duration) bool { return a > b }
func (datetime) AfterOrEqualDuration(a, b time.Duration) bool { return a >= b }
expr-1.8.9/docs/images/ 0000775 0000000 0000000 00000000000 13726232776 0014723 5 ustar 00root root 0000000 0000000 expr-1.8.9/docs/images/debug.gif 0000664 0000000 0000000 00015415757 13726232776 0016531 0 ustar 00root root 0000000 0000000 GIF89a`
7% OP klrA< \mo
m%G@ ####$$$$'''(''**+++///33333666999<<<=>>>>AAABACBBBCDCCFFGCFGGGIIIJIJLLLNMNNNPNPTTTXXX[Z[[[]\]^ aaadddddhggiiijjjkkklkkmmmnnnoopppsrssssttttttvuvwwwwwyyyzzzzzz{{{}}}}}怀态恂悂=4狋莎镔뚚"쟟ﱰ4Q#^2 VO!NETSCAPE2.0 ! , ` jTK_zCCB03b(E;HŎ!Kzʑ,?nLf_dSM{JT&ʝ+̦MLJT*ԟVb]J5*ע[jkأc~TYk)gܷ"˂vھl;'ޗz|6aǀ%;E{QLu RӉV}uհ[~-6ۮsm7{-8㾓Wn9ЛG.:γOn;.<ӏWo=/>o? -H'BJ#4"<!"J("NXarb!"jhb#x'b.-",(c8x@#9&i#F($7>dN* R2cKvY^Fi%bySj[_YfddhigrfA'pZhzҩ睍2
}R'>J$Vz$@kvJ**kZ뭳ʺjz*
kֺښ,.l
N-Rkʭ~ۭJ.j:[/V/[.nZpk#V_qoq ,r$lr(r,r0,s4ls8߬s7wvG9W/>;:KO坋^:s8o~뵛:۾N::Þ9+?;z'|__}ڋ=֏oS/>w?_<