Recursive-descent parser over a token stream produced by
Linx.NFT.Tokenizer. Builds a small internal AST that
Linx.NFT.Compiler later walks and translates into calls on the
Linx.Netfilter.Ruleset validator-setter surface (the same
surface the pipeline DSL uses — no parallel validation layer).
Mirrors the shape of Phoenix.LiveView.TagEngine.Parser: a
function per non-terminal, a token list as the threaded state,
consume helpers that raise Linx.NFT.ParseError on mismatch
with the token's {file, line, column} and source snippet.
AST shape
Top-level items (the result of parse/1):
{:table, family, name, body, meta}
{:include, path, meta}
{:define, name, value, meta}Inside a table body:
{:chain, name, opts, stmts, meta}
{:set, name, opts, meta}
{:map, name, opts, meta}
{:vmap, name, opts, meta}
{:object, kind, name, opts, meta}
{:flowtable, name, opts, meta}Inside a chain stmts:
{:rule, exprs, rule_opts, meta}Inside a rule's exprs (one node per source-level statement,
whether a match clause, a verdict, or an action):
{:match, lhs, op, rhs, meta}
{:verdict, kind, meta} # :accept, :drop, {:jump, "chain"}, ...
{:counter, opts, meta}
{:log, opts, meta}
{:limit, rate, opts, meta}
{:nat, kind, target, opts, meta} # kind: :dnat | :snat | :masquerade | :redirect
{:meta_set, field, value, meta} # `meta mark set 0xdead`
{:reject, opts, meta}
{:queue, opts, meta}LHS (left-hand side of a match):
{:payload, header, field, meta} # `tcp dport`, `ip saddr`
{:meta, field, meta} # `meta iif`
{:ct, field, meta} # `ct state`
{:set_ref, name, meta} # bare `@blocklist` as predicate
{:not, inner_lhs, meta} # `not @blocklist`RHS values (the value-position grammar):
{:integer, n, meta} | {:string, s, meta}
{:address, kind, raw, meta} # :ipv4 / :ipv6 / :mac / :cidr_v4 / :cidr_v6
{:identifier, name, meta} # bare identifier (e.g. `established`)
{:set_inline, [vals], meta} # `{ 22, 80, 443 }`
{:set_ref, name, meta} # `@blocklist`
{:range, lo, hi, meta} # `22-25`
{:list, [vals], meta} # `22, 80, 443` (no braces)
{:elixir_expr, raw, meta} # `#{...}` interpolation
{:wildcard, meta} # `*` (e.g. `iifname "eth*"`)Scope notes
The parser covers the structural shape and the slice of the
grammar needed for the canonical ~NFT examples in
docs/netfilter/EXAMPLES.md. The set of recognised statement
/ lhs / rhs shapes will grow as the compiler and long-tail
extensions add callers (see docs/netfilter/DESIGN.md). The
architectural commitments — recursive descent, raise-on-mismatch,
file:line:column on every AST node — are finalised here.
Summary
Functions
Parses a token list into a list of top-level AST items.
Types
@type ast() :: tuple()
Functions
@spec parse( [tuple()], keyword() ) :: {:ok, [ast()]} | {:error, Linx.NFT.ParseError.t()}
Parses a token list into a list of top-level AST items.
Options
:file— source filename for error messages (default"nofile").:source— original source binary for snippet rendering (default"").
Returns {:ok, ast_items} or {:error, %Linx.NFT.ParseError{}}.