Sign in

    moongrep

    Experimental MoonBit structural search and taint-analysis tool

    linter
    grep
    refactoring
    static-analysis
    Download zip
    Version
    0.1.14
    License
    Apache-2.0
    Last updated
    3 months ago
    Downloads
    227

    #moongrep

    moongrep is an experimental structural search and taint-analysis tool for MoonBit.

    #Quick Start

    The simplest way to use moongrep is to run the scan command from the root of a MoonBit project. By default, it scans recursively and skips directories generated by Git and the MoonBit toolchain. Specify an expression pattern to match with the --pattern option. For example, the following command matches a typical expression that uses match on an Option value:

    moongrep scan --pattern 'match $(value:exp) { Some($(some:id)) => $(some_body:exp); None => $(none_body:exp) }'

    #Expression Patterns and Metavariables

    moongrep parses both the expression pattern and the MoonBit code being scanned into abstract syntax trees (ASTs), then compares the structures of the two trees. Ordinary MoonBit syntax in a pattern represents fixed structure. Metavariables represent syntax nodes to match and capture. Formatting differences such as line breaks and indentation generally do not affect matching.

    Metavariables use the following format:

    $(name:kind)

    name is the metavariable name. When a match succeeds, the code at that position is recorded under this name. kind specifies the category of syntax node that may be matched.

    Common kind values include:

    • exp: matches a complete expression, such as a variable, function call, field access, or if expression;
    • id: matches an identifier, such as a variable name, parameter name, or a name bound in a pattern;
    • const: matches a literal constant, such as an integer, string, or Boolean value;
    • arg: matches a complete function-call argument;
    • pat: matches a complete pattern;
    • type: matches a complete type.

    The following pattern matches a match expression with both Some and None branches:

    match $(value:exp) { Some($(some:id)) => $(some_body:exp) None => $(none_body:exp) }

    In this pattern:

    • $(value:exp) captures the expression examined by match;
    • $(some:id) captures the identifier bound by the Some pattern;
    • $(some_body:exp) captures the expression in the Some branch;
    • $(none_body:exp) captures the expression in the None branch.

    This pattern can match:

    match load_user() {
    Some(user) => display(user)
    None => show_error()
    }

    The match produces the following captures:

    value = load_user() some = user some_body = display(user) none_body = show_error()

    The match, Some, and None syntax and the positions of the two branches are fixed structure in the pattern. An expression with Ok and Err branches does not match it. Some(1) does not match Some($(some:id)) either, because 1 is syntactically a constant. $(some:id) requires an identifier in that position.

    The same named metavariable may appear multiple times in a pattern. Every occurrence must capture a structurally identical syntax node. For example:

    $(value:exp) == $(value:exp)

    This pattern can match:

    user.name == user.name

    The following expression does not match the pattern:

    user.name == other.name

    Repeated captures are compared by syntax-tree structure; source locations are not considered.

    $_ is a discard placeholder. It matches any content at its position without recording a capture. Multiple $_ placeholders in the same pattern are independent of one another.

    #Structured Output

    By default, moongrep produces a report intended for human readers. To make its output suitable for a coding agent, add the --output-json option:

    moongrep scan --pattern 'inspect($_, content="true")' --output-json

    #Pattern Guards

    Use --guard to add filters to an expression pattern. The filters apply to the contents captured by metavariables in the pattern.

    A short, practical example is to use --guard to find inspect calls on numbers. Such calls are usually not good coding practice and should preferably be rewritten to use assert_eq or another assertion.

    moongrep scan --pattern 'inspect($_, content=$(str:const))' --guard '{$str: "^-?(0|[1-9][0-9]*)(\\\\.[0-9]+)?$"}'

    The argument to --guard is a YAML map and normally immediately follows the --pattern it filters:

    --pattern '...$(name:id)...$(value:const)...' \ --guard '{$name: "regular expression", $value: "regular expression"}'

    Each key must be a named metavariable declared in the pattern and must retain the $ prefix. Each value must be a regular-expression string. Enclose the entire YAML map in single quotes to prevent the shell from expanding $name, and enclose each regular expression in double quotes. Backslashes in a double-quoted YAML string must be escaped; for example, write \. in a regular expression as \\..

    Guards currently support only id and const captures. They cannot filter exp, arg, pat, type, ellipsis captures, or $_. For an id capture, the regular expression is matched against the normalized identifier, such as name or @pkg.name. For a const capture, it is matched against the constant value produced by the parser. For example, the string literal "raw" produces raw, the number 42 produces 42, and the Boolean value true produces true.

    Regular expressions use substring matching by default. For example, "raw" also matches "draw". To match the entire captured value, use ^ and $, as in "^raw$". All conditions in a map must match, so a guard can constrain both a function name and its argument:

    moongrep scan --pattern '$(callee:id)($(value:const))' --guard '{$callee: "^@html\\.render$", $value: "^(danger|raw)$"}'

    Each --pattern accepts at most one --guard. To scan multiple guarded patterns, provide --pattern and --guard in pairs. To apply multiple conditions to one pattern, put them in the same YAML map.

    #Dump AST

    MoonBit untyped_ast debug dumps are available through the dump subcommand:

    moon runwasm moonbit-community/moongrep -- dump --impl 'fn answer { 42 }' moon runwasm moonbit-community/moongrep -- dump --expr 'x + 1'

    Command-line synopsis:

    moon runwasm moonbit-community/moongrep -- dump (--impl <impl> | --expr <expr>)

    Use --impl <impl> to parse a MoonBit top-level implementation item and print its untyped_ast debug output. Use --expr <expr> to parse a MoonBit expression and print its untyped_ast debug output. These options are mutually exclusive, and one of them is required.

    Usage errors, including omitting both dump modes or combining --impl with --expr, print a message and exit with code 2. Parse or lexical failures print a message and exit with code 1.

    #Documentation

    Embedded documentation is available through the docs subcommand:

    moon runwasm moonbit-community/moongrep -- docs --list moon runwasm moonbit-community/moongrep -- docs RuleSpec

    Source Files

    Powered by MoonBit

    Site sourceReport issuePackagesBuild queueSkillsStatistics

    © 2026 mooncakes.io