Rule Language Reference

A quick reference to all the builtin functions, operators and actions.

  • Functions are used in predicates (i.e. in the where clause) of matchers.
  • Operators can be used to combine multiple expressions within predicates.
  • Actions can be taken in the body of match.
  • AST Providers can also export Functions and Actions.
  • Variables & Conditions can be used for expressing pre-conditions
  • Scoped Matchers can be used to restrict a matcher’s breadth and depth.

Example:

Function where not(Public) && Override {
  fail("")
}

In the sample matcher above, not is a function, && is an operator and fail is an action.


Functions

match(Property, RegExp)

Tries to convert the value of Property to a string, and matches it against the regular expression in RegExp.

not(Expression)

Converts the value of Expression to a boolean value and performs a logical negation.

count(List)

Converts the value of List to a list and returns the number of elements in the list. Returns 0 if List is not an actual list.


Operators

Expression1 == Expression2

Structural equality operator.

Expression1 != Expression2

Negation of Expression1 == Expression2

Expression1 && Expression2

Converts both Expression1 and Expression2 to boolean values and performs a boolean “and”.

Expression1 || Expression2

Converts both Expression1 and Expression2 to boolean values and performs a boolean “or”.

node.property

Access property of the AST node. If the node is actually a list of nodes, the property access works like flatMap: the property access is computed for every node in the list and then compacted into a new list.


Actions

fail(Explanation)

Reports that an error was found in the current node being matched. Explanation should be a string explaining why is this is a violation and, ideally, how to correct it.

warn(Explanation)

Similar to fail(Explanation), but reports a warning instead of an error.


AST Providers

Swift

The Swift provider only exports one convenience function:

inheritsFrom(ClassName)

This function assumes that a class is being matched and will return true if the string ClassName appears in the inheritance clause of the class declaration. It returns false if either the current node does not have a TypeInheritanceClause (i.e. it’s not a ClassDeclaration) or if the string ClassName does not appear in the inheritance clause.

Python

The Python provider currently does not export any function or action.


Variables & Conditions

NEAL allows you to declare boolean variables inside a matcher. Those variables can be mutated from nested matchers and used in where clauses and conditions.

Declaring a new variable

Variable declarations must be prefix with var and have a initial value assigned to it.

Provider::Matcher {
  var is_valid := true
  var did_match := false
}

Mutating a variable

Variables are block-scoped and can be mutated from nested matchers at any depth.

Provider::Matcher {
  var is_valid := true
  var did_match := false

  NestedMatcher {
    did_match := true
  }

  OtherMatcher {
    is_valid := false
  }
}

Conditions

Conditions are evaluated when exiting the node and can be used in conjunction with variables to check that a series of pre-conditions within that node.

Provider::Matcher {
  var is_valid := true
  var did_match := false

  condition (is_valid && did_match) {
    fail("....")
  }

  NestedMatcher where is_valid {
    did_match := true
  }

  OtherMatcher {
    is_valid := false
  }
}

Scoped Matchers

Any matcher can be scoped to a given property of it’s parent matcher, thus restricting in breadth: instead of searching through all the properties of the current AST node, restrict it to a single property. The choice of the operator used for scoping (> vs >>) can be used to restrict in depth: > will match only immediate children nodes, >> will nodes at any depth.

For example, the following matcher will match class C { var x: String = "" } but not class C { func f() { var x: String = "" } }

Swift::Class {
  ClassBody > VariableDeclaration {
    fail("...")
  }
}

Replacing the > operator with >> would lead to matching both cases.