Feel.Syntax.SFeel (feel v0.1.0)

Copy Markdown View Source

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

Summary

Types

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

Functions

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

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

Types

grammar()

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

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

Functions

check(source, tokens, ast, options \\ [])

@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!(grammar)

@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