Skip to content

Commit

Permalink
Merge pull request #352 from mumuki/feature-calls-primitive
Browse files Browse the repository at this point in the history
Feature calls primitive
  • Loading branch information
flbulgarelli authored Feb 4, 2023
2 parents 27827f1 + d19531d commit aa3cbd2
Show file tree
Hide file tree
Showing 23 changed files with 262 additions and 85 deletions.
32 changes: 18 additions & 14 deletions docs/edlspec.md
Original file line number Diff line number Diff line change
Expand Up @@ -205,15 +205,6 @@ expectation "`tell` must be called with value 10 and true":
expectation "`play` must be called with this":
%% equivalent to * Calls:play:WithSelf
calls `play` with self;
expectation "uses a repeat with a non-literal amount of iterations":
%% matches repeat blocks where the repeat count expression is a non-literal
%% and the loop body is anything
uses repeat with (nonliteral, anything);
expectation "uses a repeat with a non-literal amount of iterations":
%% shorter version of previous example
uses repeat with nonliteral;
```

Most of the matchers are designed to perform literal queries, but some of them allow more complex matching:
Expand All @@ -230,6 +221,18 @@ expectation "a method that performs boolean operations must be declared":
expectation "`getAge` must not return a hardcoded value":
%% equivalent to Intransitive:getAge Returns:WithNonliteral
within `getAge` returns with nonliteral;
expectation "uses a repeat with a non-literal amount of iterations":
%% matches repeat blocks where the repeat count expression is a non-literal
%% and the loop body is anything
uses repeat with (nonliteral, anything);
expectation "uses a repeat with a non-literal amount of iterations":
%% shorter version of previous example
uses repeat with nonliteral;
expectation "uses items[0] to get first item":
calls get at with (&`items`, 0);
```

As you can see in previous examples, many of the simplest matchers can also be used in the standard expectation syntax. However, EDL also supports the `that` matcher,
Expand Down Expand Up @@ -333,10 +336,11 @@ This is the complete list of inspections that support matchers:
### Supported matchers

* `(<matcher1>, <matcher2>.., <matcherN>)`: matches a tuple of expressions, like a callable's arguments or a control structure parts. If less elements than required are passed, the list is padded with `anything` matchers.
* `<character>`: matches a single character
* `<number>`: matches a number literal
* `<string>`: matches a single string
* `<symbol>`: matches a symbol literal
* `'<character>'`: matches a single character - e.g. `'h'`
* `<number>`: matches a number literal - e.g. `5` or `10.9`
* `"<string>"`: matches a string literal - e.g. `"hello"`
* `` `<symbol>` ``: matches a symbol literal - e.g. `` `id` ``
* `` &`<identifier>` ``: matches a reference - e.g. `` &`counter` ``
* `anything`: matches anything
* `false` and `true`: matches the `true` and `false` literals
* `literal`: matches any literal
Expand Down Expand Up @@ -428,7 +432,7 @@ expectation "pacakge `vet` must declare a class, enum or interface named `Pet`":
expectation "`Pet` must declare `eat` and `Owner` must send it":
%% however in most cases, it is better to declare two different, separate
%% expectations
(within `Pet` declares `eat`) and (within `Owner` sends `eat`);
(within `Pet` declares `eat`) and (within `Owner` calls `eat`);
```

# ⚠️ Caveats
Expand Down
33 changes: 32 additions & 1 deletion docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -159,23 +159,54 @@ Finally, you can provide a _matcher_ to many of the available expectations, that
> code.expect 'DeclaresAttribute:WithLiteral'
=> true # because weight is initialized with a literal value
> code.expect 'DeclaresAttribute:WithNil'
=> false # because no attribute is initialied with null
=> false # because no attribute is initialized with null
> code.expect 'DeclaresVariable:aPlace:WithReference:buenosAires'
=> true # because the reference buenosAires is assigned to aPlace
> code.expect 'DeclaresVariable:aBird:WithReference:aPlace'
=> false # because the reference aPlace is NOT assigned to aBird...
> code.expect 'aBird', 'Uses:aPlace'
=> true # ...eventhough it is used
```

The complete list of supported matchers is the following:

* `WithAnything`
* `WithFalse`
* `WithLiteral`
* `WithLogic`
* `WithMath`
* `WithNil`
* `WithNonliteral`
* `WithTrue`
* `WithReference:value`
* `WithChar:'value'`
* `WithSymbol:value`
* `WithNumber:value`
* `WithString:"value"`

## Custom expectations

Finally, Mulang support custom expectations, defined using the [EDL]((./edlspec)) language:

```ruby
> code = Mulang::Code.native "JavaScript", %q{
aBird['name'] = 'Norita';
aBird['energy'] = 100;
aBird.fly(rosario);
}
> code.custom_expect %q{
expectation "sets `aBird`'s name": calls set at with (&`aBird`, "name");
expectation "sets `'Pepita'` as `aBird`'s name": calls set at with (&`aBird`, "name", "Pepita");
expectation "assigns `100` to `aBird`'s energy ": calls set at with (&`aBird`, "energy", 100);
expectation "makes `aBird`'s fly to `rosario` ": calls `fly` with (&`aBird`, &`rosario`);
}
=> {
"sets `aBird`'s name"=>true,
"sets `'Pepita'` as `aBird`'s name"=>false,
"assigns `100` to `aBird`'s energy "=>true,
"makes `aBird`'s fly to `rosario` "=>true
}
```

# Contributors

Expand Down
107 changes: 56 additions & 51 deletions docs/inspections.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,57 +74,62 @@ The power of Mulang is grounded on more than 120 different kind of inspections
> ⚠️ Please notice that the operators inspections are the preferred and most reliable way of checking
> usage of language primitives. For example, prefer `UsesPlus` over `Uses:+`
| Inspection | Meaning
|----------------------------------|-----------------------
| `UsesAbsolute` | is the numeric `abs`-like absolute operator used?
| `UsesAllSatisfy` | is the collection `all`-like / `every`-like operator used?
| `UsesAnd` | is the `&&`-like and operator used?
| `UsesAnySatisfy` | is the collection `any`-like / `some`-like operator used?
| `UsesBackwardComposition` | is the `.`-like functional backward composition operator used?
| `UsesBitwiseAnd` | is the bit-level `&`-like and operator used?
| `UsesBitwiseLeftShift` | is the bit-level left `<<`-like shift operator used?
| `UsesBitwiseOr` | is the bit-level `|`-like or operator used?
| `UsesBitwiseRightShift` | is the bit-level right `>>`-like shift operator used?
| `UsesBitwiseXor` | is the bit-level `^`-like xor operator used?
| `UsesCeil` | is the numeric `ceil`-like ceiling operator used?
| `UsesCollect` | is the collection `map`-like operator used?
| `UsesCount` | is the collection `count`-like operator used?
| `UsesDetect` | is the collection `find`-like search operator used?
| `UsesDetectMax` | is the collection `max`-like maximum operator used?
| `UsesDetectMin` | is the collection `min`-like minumum operator used?
| `UsesDivide` | is the numeric `/` operator used?
| `UsesEqual` | is the `===`-like equal operator used?
| `UsesFlatten` | is the collection `flatten`-like operator used?
| `UsesFloor` | is the numeric `ceil`-like floor operator used?
| `UsesForwardComposition` | is the `>>`-like functional forward composition operator used?
| `UsesGather` | is the collection `flatmap`-like operator used?
| `UsesGetAt` | is the collection `[]`-like operator used?
| `UsesGreaterOrEqualThan` | is the `>=` operator used?
| `UsesGreaterThan` | is the `>` operator used?
| `UsesHash` | is the `hashcode` operator used?
| `UsesInject` | is the collection `reduce`-like / `fold`-like operator used?
| `UsesLessOrEqualThan` | is the `<=` operator used?
| `UsesLessThan` | is the `<` operator used?
| `UsesMax` | is the `max`-like maximum value binary operator used?
| `UsesMin` | is the `min`-like minimum value binary operator used?
| `UsesMinus` | is the numeric `-` operator used?
| `UsesModulo` | is the numeric `%-like` modulo operator used?
| `UsesMultiply` | is the numeric `*` operator used?
| `UsesNegation` | is the `!`-like not operator used?
| `UsesNotEqual` | is the `!==`-like distinct operator used?
| `UsesNotSame` | is the not reference-identical operator used?
| `UsesNotSimilar` | is the not equal-ignoring-type operator used?
| `UsesOr` | is the `||`-like or operator used?
| `UsesOtherwise` | is the guard's otherwise operator used?
| `UsesPlus` | is the numeric `+` operator used?
| `UsesPush` | is the collection `insertAtEnd`-like operator used?
| `UsesRound` | is the numeric `round`-like round operator used?
| `UsesSame` | is the reference-identical operator used?
| `UsesSelect` | is the collection `filter`-like operator used?
| `UsesSetAt` | is the collection `[]=`-like operator used?
| `UsesSimilar` | is the equal-ignoring-type operator used?
| `UsesSize` | is the collection `length`-like size operator used?

Primitive operators inspections are provided in two flavors:

* `Uses`: check whether the operator is referred
* `Calls`: check whether the operator is actually called within a function, procedure or method call. `Calls` inspections support matchers!

| `Uses` Inspection | `Calls` Inspection | Meaning
|----------------------------------|-----------------------------------|-----------------------
| `UsesAbsolute` | `CallsAbsolute` | is the numeric `abs`-like absolute operator used/called?
| `UsesAllSatisfy` | `CallsAllSatisfy` | is the collection `all`-like / `every`-like operator used/called?
| `UsesAnd` | `CallsAnd` | is the `&&`-like and operator used/called?
| `UsesAnySatisfy` | `CallsAnySatisfy` | is the collection `any`-like / `some`-like operator used/called?
| `UsesBackwardComposition` | `CallsBackwardComposition` | is the `.`-like functional backward composition operator used/called?
| `UsesBitwiseAnd` | `CallsBitwiseAnd` | is the bit-level `&`-like and operator used/called?
| `UsesBitwiseLeftShift` | `CallsBitwiseLeftShift` | is the bit-level left `<<`-like shift operator used/called?
| `UsesBitwiseOr` | `CallsBitwiseOr` | is the bit-level `|`-like or operator used/called?
| `UsesBitwiseRightShift` | `CallsBitwiseRightShift` | is the bit-level right `>>`-like shift operator used/called?
| `UsesBitwiseXor` | `CallsBitwiseXor` | is the bit-level `^`-like xor operator used/called?
| `UsesCeil` | `CallsCeil` | is the numeric `ceil`-like ceiling operator used/called?
| `UsesCollect` | `CallsCollect` | is the collection `map`-like operator used/called?
| `UsesCount` | `CallsCount` | is the collection `count`-like operator used/called?
| `UsesDetect` | `CallsDetect` | is the collection `find`-like search operator used/called?
| `UsesDetectMax` | `CallsDetectMax` | is the collection `max`-like maximum operator used/called?
| `UsesDetectMin` | `CallsDetectMin` | is the collection `min`-like minumum operator used/called?
| `UsesDivide` | `CallsDivide` | is the numeric `/` operator used/called?
| `UsesEqual` | `CallsEqual` | is the `===`-like equal operator used/called?
| `UsesFlatten` | `CallsFlatten` | is the collection `flatten`-like operator used/called?
| `UsesFloor` | `CallsFloor` | is the numeric `ceil`-like floor operator used/called?
| `UsesForwardComposition` | `CallsForwardComposition` | is the `>>`-like functional forward composition operator used/called?
| `UsesGather` | `CallsGather` | is the collection `flatmap`-like operator used/called?
| `UsesGetAt` | `CallsGetAt` | is the collection `[]`-like operator used/called?
| `UsesGreaterOrEqualThan` | `CallsGreaterOrEqualThan` | is the `>=` operator used/called?
| `UsesGreaterThan` | `CallsGreaterThan` | is the `>` operator used/called?
| `UsesHash` | `CallsHash` | is the `hashcode` operator used/called?
| `UsesInject` | `CallsInject` | is the collection `reduce`-like / `fold`-like operator used/called?
| `UsesLessOrEqualThan` | `CallsLessOrEqualThan` | is the `<=` operator used/called?
| `UsesLessThan` | `CallsLessThan` | is the `<` operator used/called?
| `UsesMax` | `CallsMax` | is the `max`-like maximum value binary operator used/called?
| `UsesMin` | `CallsMin` | is the `min`-like minimum value binary operator used/called?
| `UsesMinus` | `CallsMinus` | is the numeric `-` operator used/called?
| `UsesModulo` | `CallsModulo` | is the numeric `%-like` modulo operator used/called?
| `UsesMultiply` | `CallsMultiply` | is the numeric `*` operator used/called?
| `UsesNegation` | `CallsNegation` | is the `!`-like not operator used/called?
| `UsesNotEqual` | `CallsNotEqual` | is the `!==`-like distinct operator used/called?
| `UsesNotSame` | `CallsNotSame` | is the not reference-identical operator used/called?
| `UsesNotSimilar` | `CallsNotSimilar` | is the not equal-ignoring-type operator used/called?
| `UsesOr` | `CallsOr` | is the `||`-like or operator used/called?
| `UsesOtherwise` | `CallsOtherwise` | is the guard's otherwise operator used/called?
| `UsesPlus` | `CallsPlus` | is the numeric `+` operator used/called?
| `UsesPush` | `CallsPush` | is the collection `insertAtEnd`-like operator used/called?
| `UsesRound` | `CallsRound` | is the numeric `round`-like round operator used/called?
| `UsesSame` | `CallsSame` | is the reference-identical operator used/called?
| `UsesSelect` | `CallsSelect` | is the collection `filter`-like operator used/called?
| `UsesSetAt` | `CallsSetAt` | is the collection `[]=`-like operator used/called?
| `UsesSimilar` | `CallsSimilar` | is the equal-ignoring-type operator used/called?
| `UsesSize` | `CallsSize` | is the collection `length`-like size operator used/called?
| `UsesSlice` | `CallsSlice` | is the slicing operator - like Ruby's `[..]` or Python's `[:]` - used/called?

## Imperative Inspections

Expand Down
4 changes: 3 additions & 1 deletion gem/lib/locales/en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,15 @@ en:
must_not: must not
must: must
solution: solution
with_anything: ' with some expression'
with_char: ' with character <code>%{value}</code>'
with_false: ' with value <code>%{keyword_False}</code>'
with_literal: ' with a literal value'
with_logic: ' with a boolean expression'
with_math: ' with a math expression'
with_nil: ' with <code>%{keyword_Nil}</code>'
with_nonliteral: ' with a non-literal expresson'
with_reference: ' with <code>%{value}</code>'
with_nonliteral: ' with a non-literal expression'
with_number: ' with number <code>%{value}</code>'
with_string: ' with string <code>%{value}</code>'
with_symbol: ' with symbol <code>%{value}</code>'
Expand Down
2 changes: 2 additions & 0 deletions gem/lib/locales/es.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,14 @@ es:
must_not: no debe
must: debe
solution: la solución
with_anything: ' con alguna expresión'
with_char: ' con el carácter <code>%{value}</code>'
with_false: ' con el valor <code>%{keyword_False}</code>'
with_literal: ' con un valor literal'
with_logic: ' con una expresión booleana'
with_math: 'con una expresión matemática'
with_nil: ' con <code>%{keyword_Nil}</code>'
with_reference: ' con <code>%{value}</code>'
with_nonliteral: ' con una expresión no literal'
with_number: ' con el número <code>%{value}</code>'
with_string: ' con la cadena <code>%{value}</code>'
Expand Down
2 changes: 2 additions & 0 deletions gem/lib/locales/pt.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,14 @@ pt:
must_not: 'não deve'
must: 'deve'
solution: 'a solução'
with_anything: ' com alguma expressão'
with_char: ' com o caractere <code>%{value}</code>'
with_false: ' com o valor <code>%{keyword_False}</code>'
with_literal: ' com um valor literal'
with_logic: ' com uma expressão booleana'
with_math: ' com uma expressão matemática'
with_nil: ' com <code>%{keyword_Nil}</code>'
with_reference: ' com <code>%{value}</code>'
with_nonliteral: ' com uma expressão não literal'
with_number: ' com o número <code>%{value}</code>'
with_string: ' com a string <code>%{value}</code>'
Expand Down
4 changes: 2 additions & 2 deletions gem/lib/mulang/inspection.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ def as_json(*args)
^(?<negation>Not:)?
(?<type>[^:]+)
(
:(?<matcher>WithLiteral|WithNonliteral|WithLogic|WithMath|WithFalse|WithNil|WithTrue) |
:(?<matcher>WithChar|WithNumber|WithString|WithSymbol):(?<value>[^:]+) |
:(?<matcher>WithAnything|WithLiteral|WithNonliteral|WithLogic|WithMath|WithFalse|WithNil|WithTrue) |
:(?<matcher>WithReference|WithChar|WithNumber|WithString|WithSymbol):(?<value>[^:]+) |
:(?<target>[^:]+)(:(?<matcher>[^:]+)(:(?<value>[^:]+))?)?
)?$}.gsub(/\s/, '')

Expand Down
6 changes: 5 additions & 1 deletion gem/lib/mulang/inspection/matcher.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
class Mulang::Inspection::Matcher
include Mulang::Inspection::Compacted

TYPES = %w(WithChar WithFalse WithLiteral WithLogic WithMath WithNil WithNonliteral WithNumber WithString WithSymbol WithTrue)
TYPES = %w(
WithAnything WithChar WithFalse WithLiteral
WithLogic WithMath WithNil WithNonliteral
WithNumber WithReference WithString
WithSymbol WithTrue)

attr_accessor :type, :value

Expand Down
Loading

0 comments on commit aa3cbd2

Please sign in to comment.