498 lines
20 KiB
C
498 lines
20 KiB
C
/*
|
|
* repro_grammar_functional.c -- Per-grammar INVARIANT battery for the
|
|
* FUNCTIONAL language family.
|
|
*
|
|
* One TEST() per language so per-language RED/GREEN shows on the bug-repro
|
|
* board. Each test runs the same battery against a tiny idiomatic fixture for
|
|
* that language (a named function/definition whose body calls another named
|
|
* function). The shared single_file_battery() + pipeline_battery() helpers
|
|
* below are a direct mirror of those in repro_grammar_core.c.
|
|
*
|
|
* Languages covered (13) and the CBM_LANG_* enum each uses:
|
|
* Haskell -> CBM_LANG_HASKELL
|
|
* OCaml -> CBM_LANG_OCAML
|
|
* F# -> CBM_LANG_FSHARP
|
|
* Elixir -> CBM_LANG_ELIXIR
|
|
* Erlang -> CBM_LANG_ERLANG
|
|
* Elm -> CBM_LANG_ELM
|
|
* Clojure -> CBM_LANG_CLOJURE
|
|
* Scheme -> CBM_LANG_SCHEME
|
|
* Racket -> CBM_LANG_RACKET
|
|
* Common Lisp -> CBM_LANG_COMMONLISP
|
|
* Emacs Lisp -> CBM_LANG_EMACSLISP (note: not ELISP)
|
|
* Lean 4 -> CBM_LANG_LEAN
|
|
* Gleam -> CBM_LANG_GLEAM
|
|
*
|
|
* BATTERY DIMENSIONS (mirror of repro_grammar_core.c)
|
|
* -----------------------------------------------------
|
|
* SINGLE-FILE (cbm_extract_file, via inv_rx + inv_count_* helpers):
|
|
* 1. extract-clean : inv_extract_clean(src,lang,file) == 1
|
|
* 2. labels-valid : inv_count_bad_labels(r) == 0
|
|
* 3. fqn-wellformed : inv_count_bad_fqns(r) == 0
|
|
* 4. ranges-valid : inv_count_bad_ranges(r) == 0
|
|
* 5. defs-present : inv_count_label(r, expect_label) > 0
|
|
* 6. calls-extracted : inv_has_call(r, callee) == 1
|
|
*
|
|
* FULL-PIPELINE (rh_index_files -> cbm_store_t*, via inv_count_* store helpers):
|
|
* 7. callable-sourcing : module_sourced == 0 AND callable_sourced >= 1
|
|
* 8. no-dangling : inv_count_dangling_edges(store, project, "CALLS") == 0
|
|
*
|
|
* KNOWN GAPS (the point of this file)
|
|
* -------------------------------------
|
|
* Dimension 6 (calls-extracted) is RED for Elm: the scripting-callee path does
|
|
* not yield a call name for Elm's function_call nodes on current code.
|
|
*
|
|
* Dimension 7 (callable-sourcing) is RED for all functional languages on current
|
|
* code. cbm_enclosing_func_qn falls back to the module QN when
|
|
* cbm_find_enclosing_func cannot match tree-sitter node types to
|
|
* func_kinds_for_lang for the language (the same gap documented in
|
|
* QUALITY_ANALYSIS.md section 6 / enclosing-func drift). Only ~3.69% of CALLS
|
|
* edges are callable-sourced in the real graph; functional languages are not in
|
|
* the known-GREEN set (Go/CUDA/D).
|
|
*
|
|
* RED rows ARE the deliverable: they document extraction gaps and serve as
|
|
* permanent regression guards until the gaps are fixed.
|
|
*
|
|
* Coding rule: inline comments are line comments only (no block comments inside
|
|
* block comments).
|
|
*/
|
|
|
|
#include "test_framework.h"
|
|
#include "repro_invariant_lib.h"
|
|
#include <store/store.h>
|
|
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
|
|
/* -- Shared single-file battery (dimensions 1-6) --------------------------
|
|
*
|
|
* Runs the six single-file invariants against one fixture. Returns 0 when all
|
|
* pass, 1 otherwise (printing a per-dimension FAIL line). lang_tag is for
|
|
* diagnostics only. expect_label is the def label the fixture is guaranteed to
|
|
* produce (e.g. "Function"); callee is the in-body callee name that must
|
|
* appear in the extracted calls.
|
|
*/
|
|
static int single_file_battery(const char *lang_tag, const char *src,
|
|
CBMLanguage lang, const char *file,
|
|
const char *expect_label,
|
|
const char *callee) {
|
|
const char *RED = tf_red();
|
|
const char *RST = tf_reset();
|
|
int fails = 0;
|
|
|
|
/* 1. extract-clean -- must hold before anything else is meaningful. */
|
|
if (inv_extract_clean(src, lang, file) != 1) {
|
|
printf(" %sFAIL%s [%s] extract-clean: NULL result or has_error set\n",
|
|
RED, RST, lang_tag);
|
|
return 1; /* nothing else can be trusted */
|
|
}
|
|
|
|
CBMFileResult *r = inv_rx(src, lang, file);
|
|
if (!r) {
|
|
printf(" %sFAIL%s [%s] inv_rx returned NULL after clean extract\n",
|
|
RED, RST, lang_tag);
|
|
return 1;
|
|
}
|
|
|
|
/* 2. labels-valid */
|
|
int bad_labels = inv_count_bad_labels(r);
|
|
if (bad_labels != 0) {
|
|
printf(" %sFAIL%s [%s] labels-valid: %d def(s) with invalid label\n",
|
|
RED, RST, lang_tag, bad_labels);
|
|
fails++;
|
|
}
|
|
|
|
/* 3. fqn-wellformed */
|
|
int bad_fqns = inv_count_bad_fqns(r);
|
|
if (bad_fqns != 0) {
|
|
printf(" %sFAIL%s [%s] fqn-wellformed: %d def(s) with malformed QN\n",
|
|
RED, RST, lang_tag, bad_fqns);
|
|
fails++;
|
|
}
|
|
|
|
/* 4. ranges-valid */
|
|
int bad_ranges = inv_count_bad_ranges(r);
|
|
if (bad_ranges != 0) {
|
|
printf(" %sFAIL%s [%s] ranges-valid: %d def(s) with invalid range\n",
|
|
RED, RST, lang_tag, bad_ranges);
|
|
fails++;
|
|
}
|
|
|
|
/* 5. defs-present -- the function/definition the fixture wrote must be extracted. */
|
|
if (expect_label && inv_count_label(r, expect_label) < 1) {
|
|
printf(" %sFAIL%s [%s] defs-present: no def labelled \"%s\"\n",
|
|
RED, RST, lang_tag, expect_label);
|
|
fails++;
|
|
}
|
|
|
|
/* 6. calls-extracted -- the in-body call must be captured. */
|
|
if (inv_has_call(r, callee) != 1) {
|
|
printf(" %sFAIL%s [%s] calls-extracted: no call to \"%s\" found"
|
|
" -- known extraction gap\n",
|
|
RED, RST, lang_tag, callee);
|
|
fails++;
|
|
}
|
|
|
|
cbm_free_result(r);
|
|
return fails ? 1 : 0;
|
|
}
|
|
|
|
/* -- Shared full-pipeline battery (dimensions 7-8) ------------------------
|
|
*
|
|
* Indexes the single-file fixture through the production pipeline and asserts
|
|
* callable-sourcing (no Module-sourced in-body CALLS) and no dangling CALLS
|
|
* edges. Returns 0 on PASS, 1 on FAIL. Dimension 7 is RED for all functional
|
|
* languages on current code -- that is the intended signal.
|
|
*/
|
|
static int pipeline_battery(const char *lang_tag, const char *filename,
|
|
const char *src) {
|
|
const char *RED = tf_red();
|
|
const char *RST = tf_reset();
|
|
|
|
RFile files[1];
|
|
files[0].name = filename;
|
|
files[0].content = src;
|
|
|
|
RProj lp;
|
|
cbm_store_t *store = rh_index_files(&lp, files, 1);
|
|
if (!store) {
|
|
printf(" %sFAIL%s [%s] pipeline: rh_index_files returned NULL\n",
|
|
RED, RST, lang_tag);
|
|
return 1;
|
|
}
|
|
|
|
int fails = 0;
|
|
|
|
/* 7. callable-sourcing -- mod must be 0; we also require >=1 callable-sourced
|
|
* edge so a fixture that produced zero CALLS edges cannot vacuously pass. */
|
|
int module_sourced = 0;
|
|
int callable_sourced = 0;
|
|
inv_count_calls_by_source(store, lp.project, &module_sourced,
|
|
&callable_sourced);
|
|
if (module_sourced != 0) {
|
|
printf(" %sFAIL%s [%s] callable-sourcing: %d in-body CALLS sourced at "
|
|
"Module (callable=%d) -- known enclosing-func gap\n",
|
|
RED, RST, lang_tag, module_sourced, callable_sourced);
|
|
fails++;
|
|
} else if (callable_sourced < 1) {
|
|
printf(" %sFAIL%s [%s] callable-sourcing: 0 CALLS edges (fixture "
|
|
"produced no in-body call edge to attribute)\n",
|
|
RED, RST, lang_tag);
|
|
fails++;
|
|
}
|
|
|
|
/* 8. no-dangling -- every CALLS edge endpoint must resolve. */
|
|
int dangling = inv_count_dangling_edges(store, lp.project, "CALLS");
|
|
if (dangling != 0) {
|
|
printf(" %sFAIL%s [%s] no-dangling: %d dangling CALLS endpoint(s)\n",
|
|
RED, RST, lang_tag, dangling);
|
|
fails++;
|
|
}
|
|
|
|
rh_cleanup(&lp, store);
|
|
return fails ? 1 : 0;
|
|
}
|
|
|
|
/* -- Haskell ---------------------------------------------------------------
|
|
* Idiomatic: module header, a helper function, a caller function whose body
|
|
* applies the helper. Haskell function application is juxtaposition: `add x y`
|
|
* inside the body of `compute` is the call. The tree-sitter-haskell grammar
|
|
* emits `function` and `apply` nodes; extract_fp_callee handles `apply`.
|
|
* Expected: dims 1-6 + 8 GREEN, dim 7 RED (no cross-LSP rescue for Haskell;
|
|
* func_kinds_for_lang drift causes enclosing-func walk to fall back to Module).
|
|
*/
|
|
TEST(repro_grammar_functional_haskell) {
|
|
static const char src[] =
|
|
"module Calc where\n"
|
|
"\n"
|
|
"add :: Int -> Int -> Int\n"
|
|
"add a b = a + b\n"
|
|
"\n"
|
|
"compute :: Int -> Int\n"
|
|
"compute x = add x 1\n";
|
|
if (single_file_battery("Haskell", src, CBM_LANG_HASKELL, "Calc.hs",
|
|
"Function", "add") != 0)
|
|
return 1;
|
|
return pipeline_battery("Haskell", "Calc.hs", src);
|
|
}
|
|
|
|
/* -- OCaml -----------------------------------------------------------------
|
|
* Idiomatic: two `let` bindings at module top level; the second binding's body
|
|
* calls the first. OCaml `let f x = expr` is a `value_definition` node;
|
|
* extract_fp_callee handles `application_expression`. Labels: "Function".
|
|
* Expected: dims 1-6 + 8 GREEN, dim 7 RED (same enclosing-func gap).
|
|
*/
|
|
TEST(repro_grammar_functional_ocaml) {
|
|
static const char src[] =
|
|
"let add a b = a + b\n"
|
|
"\n"
|
|
"let compute x = add x 1\n";
|
|
if (single_file_battery("OCaml", src, CBM_LANG_OCAML, "calc.ml",
|
|
"Function", "add") != 0)
|
|
return 1;
|
|
return pipeline_battery("OCaml", "calc.ml", src);
|
|
}
|
|
|
|
/* -- F# --------------------------------------------------------------------
|
|
* Idiomatic: two `let` bindings; the second calls the first inside its body.
|
|
* F# `let f x = ...` is a `function_or_value_defn` node (or `value_declaration`
|
|
* depending on grammar version); extract_fsharp_callee handles
|
|
* `application_expression`. Labels: "Function".
|
|
* Expected: dims 1-6 + 8 GREEN, dim 7 RED (enclosing-func gap applies;
|
|
* no dedicated F# cross-LSP rescue).
|
|
*/
|
|
TEST(repro_grammar_functional_fsharp) {
|
|
static const char src[] =
|
|
"let add a b = a + b\n"
|
|
"\n"
|
|
"let compute x = add x 1\n";
|
|
if (single_file_battery("F#", src, CBM_LANG_FSHARP, "Calc.fs",
|
|
"Function", "add") != 0)
|
|
return 1;
|
|
return pipeline_battery("F#", "Calc.fs", src);
|
|
}
|
|
|
|
/* -- Elixir ----------------------------------------------------------------
|
|
* Idiomatic: a module with two `def` clauses; the caller's body invokes the
|
|
* helper. Elixir `def` is extracted as a "call" node by tree-sitter-elixir;
|
|
* extract_calls.c has a special Elixir branch for "call" nodes that extracts
|
|
* the callee. Labels: "Function" (elixir_func_types includes "call").
|
|
* Expected: dims 1-6 + 8 GREEN, dim 7 RED (enclosing-func gap).
|
|
*/
|
|
TEST(repro_grammar_functional_elixir) {
|
|
static const char src[] =
|
|
"defmodule Calc do\n"
|
|
" def add(a, b), do: a + b\n"
|
|
"\n"
|
|
" def compute(x) do\n"
|
|
" add(x, 1)\n"
|
|
" end\n"
|
|
"end\n";
|
|
if (single_file_battery("Elixir", src, CBM_LANG_ELIXIR, "calc.ex",
|
|
"Function", "add") != 0)
|
|
return 1;
|
|
return pipeline_battery("Elixir", "calc.ex", src);
|
|
}
|
|
|
|
/* -- Erlang ----------------------------------------------------------------
|
|
* Idiomatic: a module attribute, an exported function, and a helper function.
|
|
* The exported function's body calls the helper. Erlang function clauses are
|
|
* `function_clause` nodes; extract_erlang_callee handles `call` nodes.
|
|
* Labels: "Function" (erlang_func_types = {"function_clause"}).
|
|
* Expected: dims 1-6 + 8 GREEN, dim 7 RED (enclosing-func gap applies;
|
|
* Erlang is not in the known-GREEN callable-sourcing set).
|
|
*/
|
|
TEST(repro_grammar_functional_erlang) {
|
|
static const char src[] =
|
|
"-module(calc).\n"
|
|
"-export([compute/1]).\n"
|
|
"\n"
|
|
"add(A, B) -> A + B.\n"
|
|
"\n"
|
|
"compute(X) ->\n"
|
|
" add(X, 1).\n";
|
|
if (single_file_battery("Erlang", src, CBM_LANG_ERLANG, "calc.erl",
|
|
"Function", "add") != 0)
|
|
return 1;
|
|
return pipeline_battery("Erlang", "calc.erl", src);
|
|
}
|
|
|
|
/* -- Elm ------------------------------------------------------------------
|
|
* Idiomatic: a module declaration, a helper function, and a caller function
|
|
* whose body applies the helper. Elm `f x = body` is a `value_declaration`
|
|
* node; elm_call_types = {"function_call", "function_call_expr"}. The call
|
|
* extractor reaches extract_scripting_callee for Elm but currently does NOT
|
|
* yield a callee name for Elm's function_call node -- dim 6 is RED.
|
|
* Labels: "Function" (elm_func_types = {"value_declaration", ...}).
|
|
* Expected: dims 1-5 + 8 GREEN, dim 6 RED (calls extraction gap -- this RED
|
|
* assertion documents the gap), dim 7 RED (enclosing-func gap).
|
|
*/
|
|
TEST(repro_grammar_functional_elm) {
|
|
static const char src[] =
|
|
"module Calc exposing (compute)\n"
|
|
"\n"
|
|
"add : Int -> Int -> Int\n"
|
|
"add a b =\n"
|
|
" a + b\n"
|
|
"\n"
|
|
"compute : Int -> Int\n"
|
|
"compute x =\n"
|
|
" add x 1\n";
|
|
if (single_file_battery("Elm", src, CBM_LANG_ELM, "Calc.elm",
|
|
"Function", "add") != 0)
|
|
return 1;
|
|
return pipeline_battery("Elm", "Calc.elm", src);
|
|
}
|
|
|
|
/* -- Clojure ---------------------------------------------------------------
|
|
* Idiomatic: two `defn` forms; the second's body calls the first. In Clojure
|
|
* both forms are `list_lit` nodes; `extract_lisp_def` labels them "Function".
|
|
* `extract_lisp_callee` extracts the callee from the head of a `list_lit`.
|
|
* Expected: dims 1-6 + 8 GREEN, dim 7 RED (enclosing-func gap; Clojure is not
|
|
* in the known-GREEN callable-sourcing set).
|
|
*/
|
|
TEST(repro_grammar_functional_clojure) {
|
|
static const char src[] =
|
|
"(defn add [a b]\n"
|
|
" (+ a b))\n"
|
|
"\n"
|
|
"(defn compute [x]\n"
|
|
" (add x 1))\n";
|
|
if (single_file_battery("Clojure", src, CBM_LANG_CLOJURE, "calc.clj",
|
|
"Function", "add") != 0)
|
|
return 1;
|
|
return pipeline_battery("Clojure", "calc.clj", src);
|
|
}
|
|
|
|
/* -- Scheme ----------------------------------------------------------------
|
|
* Idiomatic: two `define` forms; the second's body calls the first. In
|
|
* tree-sitter-scheme both forms are `list` nodes; `extract_lisp_def` (triggered
|
|
* by SCHEME in walk_defs) labels them "Function".
|
|
* NOTE: CBM_LANG_SCHEME has func_types = empty_types, so extract_func_def is
|
|
* never triggered; definitions only appear via extract_lisp_def. The callee
|
|
* is extracted by extract_lisp_callee (SCHEME is in the lisp group).
|
|
* Expected: dims 1-6 + 8 GREEN, dim 7 RED (enclosing-func gap -- SCHEME not
|
|
* in func_kinds_for_lang known-GREEN set).
|
|
*/
|
|
TEST(repro_grammar_functional_scheme) {
|
|
static const char src[] =
|
|
"(define (add a b)\n"
|
|
" (+ a b))\n"
|
|
"\n"
|
|
"(define (compute x)\n"
|
|
" (add x 1))\n";
|
|
if (single_file_battery("Scheme", src, CBM_LANG_SCHEME, "calc.scm",
|
|
"Function", "add") != 0)
|
|
return 1;
|
|
return pipeline_battery("Scheme", "calc.scm", src);
|
|
}
|
|
|
|
/* -- Racket ----------------------------------------------------------------
|
|
* Idiomatic: a `#lang racket` reader directive, two `define` forms; the
|
|
* second's body calls the first. tree-sitter-racket emits `list` nodes;
|
|
* `extract_lisp_def` (triggered by RACKET in walk_defs) labels them "Function".
|
|
* NOTE: CBM_LANG_RACKET has func_types = empty_types, so definitions only
|
|
* appear via extract_lisp_def. extract_lisp_callee handles RACKET.
|
|
* Expected: dims 1-6 + 8 GREEN, dim 7 RED (enclosing-func gap -- RACKET not
|
|
* in the known-GREEN callable-sourcing set).
|
|
*/
|
|
TEST(repro_grammar_functional_racket) {
|
|
static const char src[] =
|
|
"#lang racket\n"
|
|
"\n"
|
|
"(define (add a b)\n"
|
|
" (+ a b))\n"
|
|
"\n"
|
|
"(define (compute x)\n"
|
|
" (add x 1))\n";
|
|
if (single_file_battery("Racket", src, CBM_LANG_RACKET, "calc.rkt",
|
|
"Function", "add") != 0)
|
|
return 1;
|
|
return pipeline_battery("Racket", "calc.rkt", src);
|
|
}
|
|
|
|
/* -- Common Lisp -----------------------------------------------------------
|
|
* Idiomatic: two `defun` forms; the second's body calls the first. In
|
|
* tree-sitter-commonlisp `defun` is the node kind; `commonlisp_func_types =
|
|
* {"defun"}` triggers extract_func_def which labels it "Function".
|
|
* extract_lisp_callee handles COMMONLISP.
|
|
* Expected: dims 1-6 + 8 GREEN, dim 7 RED (enclosing-func gap -- COMMONLISP
|
|
* not in the known-GREEN callable-sourcing set).
|
|
*/
|
|
TEST(repro_grammar_functional_commonlisp) {
|
|
static const char src[] =
|
|
"(defun add (a b)\n"
|
|
" (+ a b))\n"
|
|
"\n"
|
|
"(defun compute (x)\n"
|
|
" (add x 1))\n";
|
|
if (single_file_battery("Common Lisp", src, CBM_LANG_COMMONLISP, "calc.lisp",
|
|
"Function", "add") != 0)
|
|
return 1;
|
|
return pipeline_battery("Common Lisp", "calc.lisp", src);
|
|
}
|
|
|
|
/* -- Emacs Lisp ------------------------------------------------------------
|
|
* Idiomatic: two `defun` forms; the second's body calls the first. In
|
|
* tree-sitter-elisp `defun` is a `list` node with head "defun";
|
|
* `elisp_func_types = {"function_definition", "macro_definition"}` triggers
|
|
* extract_func_def. extract_lisp_callee handles EMACSLISP (in the lisp group).
|
|
* Note: the enum is CBM_LANG_EMACSLISP (not ELISP).
|
|
* Expected: dims 1-6 + 8 GREEN, dim 7 RED (enclosing-func gap -- EMACSLISP
|
|
* not in the known-GREEN callable-sourcing set).
|
|
*/
|
|
TEST(repro_grammar_functional_emacslisp) {
|
|
static const char src[] =
|
|
"(defun add (a b)\n"
|
|
" (+ a b))\n"
|
|
"\n"
|
|
"(defun compute (x)\n"
|
|
" (add x 1))\n";
|
|
if (single_file_battery("Emacs Lisp", src, CBM_LANG_EMACSLISP, "calc.el",
|
|
"Function", "add") != 0)
|
|
return 1;
|
|
return pipeline_battery("Emacs Lisp", "calc.el", src);
|
|
}
|
|
|
|
/* -- Lean 4 ----------------------------------------------------------------
|
|
* Idiomatic: two `def` declarations; the second's body calls the first.
|
|
* `lean_func_types = {"def", "theorem", "instance", "abbrev"}` triggers
|
|
* extract_func_def which labels the definitions "Function". extract_calls.c
|
|
* has a Lean-specific guard (lean_is_in_type_position) for `apply` nodes.
|
|
* Expected: dims 1-6 + 8 GREEN, dim 7 RED (enclosing-func gap -- Lean is not
|
|
* in the known-GREEN callable-sourcing set).
|
|
*/
|
|
TEST(repro_grammar_functional_lean) {
|
|
static const char src[] =
|
|
"def add (a b : Nat) : Nat := a + b\n"
|
|
"\n"
|
|
"def compute (x : Nat) : Nat :=\n"
|
|
" add x 1\n";
|
|
if (single_file_battery("Lean", src, CBM_LANG_LEAN, "Calc.lean",
|
|
"Function", "add") != 0)
|
|
return 1;
|
|
return pipeline_battery("Lean", "Calc.lean", src);
|
|
}
|
|
|
|
/* -- Gleam ----------------------------------------------------------------
|
|
* Idiomatic: two `fn` declarations; the second's body calls the first.
|
|
* `gleam_func_types = {"function", "anonymous_function", "external_function",
|
|
* ...}` triggers extract_func_def which labels them "Function".
|
|
* Call extraction reaches extract_scripting_callee (no gleam-specific branch in
|
|
* extract_callee_lang_specific); gleam_call_types = {"function_call"}.
|
|
* Expected: dims 1-6 + 8 GREEN, dim 7 RED (enclosing-func gap -- Gleam not
|
|
* in the known-GREEN callable-sourcing set).
|
|
*/
|
|
TEST(repro_grammar_functional_gleam) {
|
|
static const char src[] =
|
|
"fn add(a: Int, b: Int) -> Int {\n"
|
|
" a + b\n"
|
|
"}\n"
|
|
"\n"
|
|
"fn compute(x: Int) -> Int {\n"
|
|
" add(x, 1)\n"
|
|
"}\n";
|
|
if (single_file_battery("Gleam", src, CBM_LANG_GLEAM, "calc.gleam",
|
|
"Function", "add") != 0)
|
|
return 1;
|
|
return pipeline_battery("Gleam", "calc.gleam", src);
|
|
}
|
|
|
|
/* -- Suite ---------------------------------------------------------------- */
|
|
|
|
SUITE(repro_grammar_functional) {
|
|
RUN_TEST(repro_grammar_functional_haskell);
|
|
RUN_TEST(repro_grammar_functional_ocaml);
|
|
RUN_TEST(repro_grammar_functional_fsharp);
|
|
RUN_TEST(repro_grammar_functional_elixir);
|
|
RUN_TEST(repro_grammar_functional_erlang);
|
|
RUN_TEST(repro_grammar_functional_elm);
|
|
RUN_TEST(repro_grammar_functional_clojure);
|
|
RUN_TEST(repro_grammar_functional_scheme);
|
|
RUN_TEST(repro_grammar_functional_racket);
|
|
RUN_TEST(repro_grammar_functional_commonlisp);
|
|
RUN_TEST(repro_grammar_functional_emacslisp);
|
|
RUN_TEST(repro_grammar_functional_lean);
|
|
RUN_TEST(repro_grammar_functional_gleam);
|
|
}
|