github.com/expr-lang/expr@v1.16.9/docs/getting-started.md (about)

     1  # Getting Started
     2  
     3  **Expr** is a simple, fast and extensible expression language for Go. It is
     4  designed to be easy to use and integrate into your Go application. Let's delve
     5  deeper into its core features:
     6  
     7  - **Memory safe** - Designed to prevent vulnerabilities like buffer overflows and memory leaks.
     8  - **Type safe** - Enforces strict type rules, aligning with Go's type system.
     9  - **Terminating** - Ensures every expression evaluation cannot loop indefinitely.
    10  - **Side effects free** - Evaluations won't modify global states or variables.
    11  
    12  Let's start with a simple example:
    13  
    14  ```go
    15  program, err := expr.Compile(`2 + 2`)
    16  if err != nil {
    17      panic(err)
    18  }
    19  
    20  output, err := expr.Run(program, nil)
    21  if err != nil {
    22      panic(err)
    23  }
    24  
    25  fmt.Print(output) // 4
    26  ```
    27  
    28  Expr compiles the expression `2 + 2` into a bytecode program. Then we run
    29  the program and get the output.
    30  
    31  :::tip
    32  In performance-critical applications, you can reuse the compiled program. Compiled programs are safe for concurrent use.
    33  **Compile once** and run **multiple** times.
    34  :::
    35  
    36  The `expr.Compile` function returns a `*vm.Program` and an error. The `expr.Run` function takes a program and an
    37  environment. The environment is a map of variables that can be used in the expression. In this example, we use `nil` as
    38  an environment because we don't need any variables.
    39  
    40  
    41  Now let's pass some variables to the expression:
    42  
    43  ```go
    44  env := map[string]any{
    45      "foo": 100,
    46      "bar": 200,
    47  }
    48  
    49  program, err := expr.Compile(`foo + bar`, expr.Env(env))
    50  if err != nil {
    51      panic(err)
    52  }
    53  
    54  output, err := expr.Run(program, env)
    55  if err != nil {
    56      panic(err)
    57  }
    58  
    59  fmt.Print(output) // 300
    60  ```
    61  
    62  Why do we need to pass the environment to the `expr.Compile` function? Expr can be used as a type-safe language. 
    63  Expr can infer the type of the expression and check it against the environment. 
    64  
    65  Here is an example:
    66  
    67  ```go
    68  env := map[string]any{
    69      "name": "Anton",
    70      "age": 35,
    71  }
    72  
    73  program, err := expr.Compile(`name + age`, expr.Env(env))
    74  if err != nil {
    75      panic(err) // Will panic with "invalid operation: string + int"
    76  }
    77  ```
    78  
    79  Expr can work with any Go types:
    80  
    81  ```go
    82  env := map[string]any{
    83      "greet":   "Hello, %v!",
    84      "names":   []string{"world", "you"},
    85      "sprintf": fmt.Sprintf,
    86  }
    87  
    88  code := `sprintf(greet, names[0])`
    89  
    90  program, err := expr.Compile(code, expr.Env(env))
    91  if err != nil {
    92      panic(err)
    93  }
    94  
    95  output, err := expr.Run(program, env)
    96  if err != nil {
    97      panic(err)
    98  }
    99  
   100  fmt.Print(output) // Hello, world!
   101  ```
   102  
   103  Also, Expr can use a struct as an environment. Methods defined on the struct become functions.
   104  The struct fields can be renamed with the `expr` tag. 
   105  
   106  Here is an example:
   107  
   108  ```go
   109  type Env struct {
   110      Posts []Post `expr:"posts"`
   111  }
   112  
   113  func (Env) Format(t time.Time) string { 
   114      return t.Format(time.RFC822) 
   115  }
   116  
   117  type Post struct {
   118      Body string
   119      Date time.Time
   120  }
   121  
   122  func main() {
   123      code := `map(posts, Format(.Date) + ": " + .Body)`
   124      
   125      program, err := expr.Compile(code, expr.Env(Env{}))
   126      if err != nil {
   127          panic(err)
   128      }
   129  
   130      env := Env{
   131          Posts: []Post{
   132              {"Oh My God!", time.Now()}, 
   133              {"How you doin?", time.Now()}, 
   134              {"Could I be wearing any more clothes?", time.Now()},
   135          },
   136      }
   137  
   138      output, err := expr.Run(program, env)
   139      if err != nil {
   140          panic(err)
   141      }
   142  
   143      fmt.Print(output)
   144  }
   145  ```
   146  
   147  The compiled program can be reused between runs.
   148  
   149  ```go
   150  type Env struct {
   151      X int
   152      Y int
   153  }
   154  
   155  program, err := expr.Compile(`X + Y`, expr.Env(Env{}))
   156  if err != nil {
   157      panic(err)
   158  }
   159  
   160  output, err := expr.Run(program, Env{1, 2})
   161  if err != nil {
   162      panic(err)
   163  }
   164  
   165  fmt.Print(output) // 3
   166  
   167  output, err = expr.Run(program, Env{3, 4})
   168  if err != nil {
   169      panic(err)
   170  }
   171  
   172  fmt.Print(output) // 7
   173  ```
   174  
   175  :::tip
   176  For one-off expressions, you can use the `expr.Eval` function. It compiles and runs the expression in one step.
   177  ```go
   178  output, err := expr.Eval(`2 + 2`, env)
   179  ```
   180  :::