# `Feel.Syntax.SFeel`
[🔗](https://github.com/FeeJai/BDM/blob/feel-v0.1.0/apps/feel/lib/feel/syntax/s_feel.ex#L1)

The check that an expression is in S-FEEL, the simple subset of FEEL (DMN 1.6, clause 9; D-49).

S-FEEL restricts the **syntax**. Within it the semantics are those of FEEL, which clause 9 states
to be equivalent, so an expression that passes is run as any other, and nothing here is a
runtime matter. `Feel.parse/2` and `Feel.prepare/2` call `check/4` after the parser when the dialect
is `:s_feel`; the parser itself reads FEEL, since the grammar of S-FEEL has no construct that the
grammar of FEEL lacks.

An expression that is not in S-FEEL is a syntax error with the code `:outside_s_feel`, one
diagnostic for each construct, in source order.

## What is accepted

For an expression (`entry: :expression`, grammar rule 3 of clause 9):

  * literals: numbers (with no exponent), strings (with the escapes `\'`, `\"`, `\\`, `\n`, `\r`,
    `\t` and `\u` with four hex digits, but not `\U`), `true` and `false`;
  * names and qualified names (`a`, `first name`, `applicant.age`);
  * `date("...")`, `time("...")` and `duration("...")` with one string literal, and no other call;
  * arithmetic negation and `+ - * / **`;
  * the comparisons `= != < <= > >=`;
  * the operands of those are expressions again, as the grammar has it.

For a list of unary tests (`entry: :unary_tests`, rule 12):

  * `-`, and `not(...)` of a list of tests;
  * a test is a value, `<`, `<=`, `>` or `>=` followed by a value, or an interval. A value is a
    literal (a number can have a sign), a qualified name, or one of the three date and time
    literals. `= x` and `!= x` as tests are not there: a value alone is the equality.

Not in S-FEEL: `and`, `or`, `if`, `for`, `some`, `every`, `between`, `in`, `instance of`, lists,
contexts, filters, paths on anything but a name, function definitions, other calls (`not(x)` and
`abs(x)` too), `@"..."` literals, intervals as values, generalized unary tests (`? > 5`), exponents,
`\U` escapes, and comments.

## Two grammars

The grammar as printed has no parentheses for grouping and no `null` literal. The specification
notes (08, 2.7) take both for omissions, as a decision table tests for missing data all the time.
`grammar: :practical` (the default) accepts them. `grammar: :literal` follows the printed grammar
and reports them. Nothing else differs: a path is still names and dots, so a path on a group,
`(a).b`, is reported in both.

## What is not checked

The types of S-FEEL, which have no `date and time`, list, context, range or function, and the
roles it is required in (the allowed values of an item definition, the input expressions and
entries and the output entries of a decision table, D-49). Those are about the model and not
about the text.

    iex> {:ok, _ast} = Feel.parse("(1 + 2) * 3", dialect: :s_feel)
    iex> {:error, error} = Feel.parse("if x then 1 else 2", dialect: :s_feel)
    iex> {error.kind, error.code, error.span}
    {:syntax, :outside_s_feel, {0, 18}}
    iex> {:error, error} = Feel.parse("(1 + 2) * 3", dialect: :s_feel, s_feel_grammar: :literal)
    iex> hd(error.diagnostics).details.construct
    :grouping

# `grammar`

```elixir
@type grammar() :: :practical | :literal
```

`:practical` accepts grouping parentheses and `null`; `:literal` reports them.

# `check`

```elixir
@spec check(String.t(), [Feel.Syntax.Token.t()], Feel.AST.t(), [{:grammar, grammar()}]) ::
  :ok | {:error, [Feel.Diagnostic.t(), ...]}
```

Checks the tokens and the syntax tree of a parsed S-FEEL expression or list of unary tests.

`source` and `tokens` are what the lexer read, `ast` what the parser made of them. Returns `:ok`,
or every construct that is not in S-FEEL as a list of diagnostics with the code `:outside_s_feel`,
ordered by position. Raises `ArgumentError` for an unknown option.

## Options

  * `:grammar` - `:practical` (default) or `:literal`; see the module documentation.

# `validate_grammar!`

```elixir
@spec validate_grammar!(term()) :: grammar()
```

Returns the grammar, or raises `ArgumentError` when it is not `:practical` or `:literal`.

    iex> Feel.Syntax.SFeel.validate_grammar!(:literal)
    :literal

---

*Consult [api-reference.md](api-reference.md) for complete listing*
