Command Line Tool
You can also use Mulang from the Command Line, without having to interact with Haskell code. This tool allows to perform most common analysis out of the box by using a JSON spec. It supports five different kinds of analysis:
- Expectation analysis: you can pass inspections that will be tested against the provied program. Expectations answer questions like: does the function X call the function Y? or does the program use if's?.
- Smell analysis: instead of asking explcit questions to the program, the smells analysis implicitly runs specific inspections - that denote bad code - in orden to know if any of them is matched.
- Intermediate Language analysis: you can ask the tool to generate the Mulang AST for a given source code.
- Signature analysis: report the signatures of the computations present in source code.
- Test analysis: run basic unit-like tests over the code.
Examples
Let's see some usage samples:
With intransitive expectations
$ mulang '
{
"sample" : {
"tag" : "CodeSample",
"language" : "Haskell",
"content" : "x = z + 1"
},
"spec" : {
"expectations" : [
{
"binding" : "Intransitive:x",
"inspection" : "Uses:z"
}
]
}
}
' | json_pp
{
"tag" : "AnalysisCompleted",
"expectationResults" : [
{
"expectation" : {
"binding" : "Intransitive:x",
"inspection" : "Uses:z"
},
"result" : true
}
]
}
With unscoped expectations
$ mulang '
{
"sample" : {
"tag" : "CodeSample",
"language" : "Haskell",
"content" : "x = 1"
},
"spec" : {
"expectations" : [
{
"binding" : "*",
"inspection" : "Declares:x"
}
]
}
}
' | json_pp
{
"tag" : "AnalysisCompleted",
"expectationResults" : [
{
"result" : true,
"expectation" : {
"binding" : "*",
"inspection" : "Declares:x"
}
}
]
}
With expectations with matchers
$ mulang '
{
"sample" : {
"tag" : "CodeSample",
"language" : "Python3",
"content" : "print(\"bye\")\nexit(1)"
},
"spec" : {
"expectations" : [
{
"binding" : "*",
"inspection" : "Calls:exit:WithNumber:0"
},
{
"binding" : "*",
"inspection" : "Prints:WithString:\"bye\""
}
]
}
}
' | json_pp
{
"tag" : "AnalysisCompleted",
"expectationResults" : [
{
"expectation" : {
"inspection" : "Calls:exit:WithNumber:0",
"binding" : "*"
},
"result" : false
},
{
"expectation" : {
"inspection" : "Prints:WithString:\"bye\"",
"binding" : "*"
},
"result" : true
}
]
}
With custom expectations
For more information about EDL syntax see EDL Spec
$ mulang '
{
"sample" : {
"tag" : "CodeSample",
"language" : "JavaScript",
"content" : "function plusOne(x) { return x + 1 }"
},
"spec" : {
"customExpectations" : "expectation: declares function `plusOne` that (returns with math);\nexpectation: !declares variable with literal"
}
}
' | json_pp
{
"tag" : "AnalysisCompleted",
"expectationResults" : [
{
"result" : true,
"expectation" : {
"binding" : "*",
"inspection" : "Declares:x"
}
}
]
}
With signature analysis
$ mulang '
{
"sample" : {
"tag" : "CodeSample",
"language" : "JavaScript",
"content" : "function foo(x, y) { return x + y; }"
},
"spec" : {
"signatureAnalysisType" : {
"tag" : "StyledSignatures",
"style" : "HaskellStyle"
}
}
}
' | json_pp
{
"tag" : "AnalysisCompleted",
"expectationResults" : [
{
"expectation" : {
"binding" : "<<custom>>",
"inspection" : "E0"
},
"result" : true
},
{
"expectation" : {
"inspection" : "E1",
"binding" : "<<custom>>"
},
"result" : true
}
]
}
With broken input
$ mulang '
{
"sample" : {
"tag" : "CodeSample",
"language" : "JavaScript",
"content" : "function foo(x, y { return x + y; }"
},
"spec" : {
"signatureAnalysisType" : {
"tag" : "StyledSignatures",
"style" : "HaskellStyle"
}
}
}' | json_pp
{
"tag" : "AnalysisFailed",
"reason" : "Sample code parsing error"
}
With AST as input
$ mulang '
{
"sample" : {
"tag" : "MulangSample",
"ast" : {
"tag" : "Sequence",
"contents" : [
{
"tag" : "Variable",
"contents" : [
"x",
{ "tag" : "MuNumber", "contents" : 1 }
]
},
{
"tag" : "Variable",
"contents" : [
"y",
{ "tag" : "MuNumber", "contents" : 2 }
]
}
]
}
},
"spec" : {
"signatureAnalysisType" : {
"tag" : "StyledSignatures",
"style" : "HaskellStyle"
}
}
}
' | json_pp
{
"tag" : "AnalysisCompleted",
"signatures" : [
"-- x",
"-- y"
]
}
With smell analysis, by inclusion
$ mulang '
{
"sample" : {
"tag" : "CodeSample",
"language" : "JavaScript",
"content" : "function foo(x, y) { return null; }"
},
"spec" : {
"smellsSet" : {
"tag" : "NoSmells",
"include" : [
"ReturnsNil",
"DoesNullTest"
]
}
}
}
' | json_pp
{
"tag" : "AnalysisCompleted",
"smells" : [
{
"binding" : "foo",
"inspection" : "ReturnsNil"
}
]
}
With smell analysis, by exclusion
$ mulang '
{
"sample" : {
"tag" : "CodeSample",
"language" : "JavaScript",
"content" : "function foo(x, y) { return null; }"
},
"spec" : {
"smellsSet" : {
"tag" : "AllSmells",
"exclude" : [
"ReturnsNil"
]
}
}
}
' | json_pp
{
"tag" : "AnalysisCompleted",
"smells" : [],
}
With expressiveness smells
Expressivnes smells are like other smells - they can be included or excluded using the smellsSet
settings. However, their behaviour is also controlled
by the domainLanguage
setting, which you can configure:
$ mulang '
{
"sample" : {
"tag" : "CodeSample",
"language" : "Prolog",
"content" : "son(Parent, Son):-parentOf(Son, Parent).parentOf(bart, homer)."
},
"spec" : {
"smellsSet" : { "tag" : "AllSmells" },
"domainLanguage" : {
"caseStyle" : "SnakeCase",
"minimumIdentifierSize" : 4,
"jargon" : ["id"]
}
}
}' | json_pp
{
"tag" : "AnalysisCompleted",
"smells" : [
{
"inspection" : "HasTooShortIdentifiers",
"binding" : "son"
},
{
"binding" : "parentOf",
"inspection" : "HasWrongCaseIdentifiers"
}
]
}
Also, if you want to use HasMisspelledIdentifiers
smell, you need to specify a dictionary - with must be ordered, downcased and with unique words only:
$ mulang '
{
"sample" : {
"tag" : "CodeSample",
"language" : "JavaScript",
"content" : "function foo(x, y) { return null; }"
},
"spec" : {
"smellsSet" : { "tag" : "AllSmells" },
"domainLanguage" : { "dictionaryFilePath" : "/usr/share/dict/words" }
}
}' | json_pp
{
"tag" : "AnalysisCompleted",
"smells" : [
{
"inspection" : "ReturnsNil",
"binding" : "foo"
},
{
"inspection" : "HasMisspelledIdentifiers",
"binding" : "foo"
}
]
}
With typos smells
Smell typos are special since they interact with some expectations:
HasDeclarationTypos
is a smell that will be activated after aDeclares
- or any similar expectation likeDeclaresProcedure
orDelegates
- expectation is set.HasUsageTypos
will be activated after aUses
expectation is set.
mulang '
{
"sample": {
"tag": "CodeSample",
"language": "JavaScript",
"content": "function pls(x, y) { return x + y; }"
},
"spec": {
"expectations": [
{ "binding": "*", "inspection": "Declares:plus" }
],
"smellsSet": {
"tag": "NoSmells",
"include": [ "HasDeclarationTypos" ]
}
}
}
' | json_pp
{
"outputAst" : null,
"tag" : "AnalysisCompleted",
"expectationResults" : [
{
"expectation" : {
"inspection" : "Declares:plus",
"binding" : "*"
},
"result" : false
}
],
"smells" : [
{
"inspection" : "HasDeclarationTypos:plus",
"binding" : "pls"
}
],
"testResults" : [],
"signatures" : []
}
With intermediate language generation
$ mulang '
{
"sample" : {
"tag" : "CodeSample",
"language" : "JavaScript",
"content" : "function foo(x, y) { return null; }"
},
"spec" : {
"includeOutputAst" : true
}
}
' | json_pp
{
"tag" : "AnalysisCompleted",
"outputAst" : {
"tag" : "Function",
"contents" : [
"foo",
[
[
[
{
"tag" : "VariablePattern",
"contents" : "x"
},
{
"tag" : "VariablePattern",
"contents" : "y"
}
],
{
"tag" : "UnguardedBody",
"contents" : {
"contents" : {
"tag" : "MuNil"
},
"tag" : "Return"
}
}
]
]
]
}
}
With normalization options
$ mulang '
{
"sample": {
"tag": "MulangSample",
"normalizationOptions": {
"insertImplicitReturn": true
},
"ast": {
"tag": "Procedure",
"contents": [
"foo",
[
[
[
{
"tag": "VariablePattern",
"contents": "x"
}
],
{
"tag": "UnguardedBody",
"contents": {
"tag": "Application",
"contents": [
{
"tag": "Primitive",
"contents": "Multiply"
},
[
{
"tag": "MuNumber",
"contents": 2
},
{
"tag": "Reference",
"contents": "x"
}
]
]
}
}
]
]
]
}
},
"spec": {
"includeOutputAst": true
}
}
' | json_pp
{
"tag" : "AnalysisCompleted",
"signatures" : [],
"smells" : [],
"expectationResults" : [],
"testResults" : [],
"outputAst" : {
"tag" : "Procedure",
"contents" : [
"foo",
[
[
[
{
"contents" : "x",
"tag" : "VariablePattern"
}
],
{
"contents" : {
"tag" : "Return",
"contents" : {
"contents" : [
{
"tag" : "Primitive",
"contents" : "Multiply"
},
[
{
"tag" : "MuNumber",
"contents" : 2
},
{
"contents" : "x",
"tag" : "Reference"
}
]
],
"tag" : "Application"
}
},
"tag" : "UnguardedBody"
}
]
]
]
}
}
With test running
mulang '{
"sample" : {
"tag" : "CodeSample",
"language" : "JavaScript",
"content" : "function f(x) { return x + 1 }"
},
"spec" : {
"testAnalysisType" : {
"tag" : "ExternalTests",
"test" : {
"tag" : "CodeSample",
"language" : "JavaScript",
"content" : "it(\"f increments by one\", function() { assert.equals(f(1), 2) })"
}
}
}
}' | json_pp
{
"tag" : "AnalysisCompleted",
"testResults" : [
{
"description" : [
"f increments by one"
],
"status" : {
"tag" : "Success"
}
}
]
}
For further detail on this spec, see Code Execution
Code Execution
As of v4.4.0, mulang provides basic support for executing its AST.
This feature can accessed through a testAnalysisType
spec, such as the one shown in this section.
Currently, support is given for executing the following AST elements:
- Application
- Assert
- Assignment
- ForLoop
- If
- Lambda
- MuBool
- MuList
- MuNil
- MuNumber
- MuString
- Raise
- Reference
- Return
- Sequence
- Function
- Procedure
- Method
- Variable
- While
Examples
mulang '{
"sample" : {
"tag" : "CodeSample",
"language" : "JavaScript",
"content" : "
function f(x) {
return x + 1
}"
},
"spec" : {
"testAnalysisType" : {
"tag" : "ExternalTests",
"test" : {
"tag" : "CodeSample",
"language" : "JavaScript",
"content" : "
it(\"f increments by one\", function() {
assert.equals(f(1), 2)
})"
}
}
}
}' | json_pp
{
"tag" : "AnalysisCompleted",
"testResults" : [
{
"status" : {
"tag" : "Success"
},
"description" : [
"f increments by one"
]
}
]
}
Since both the code and tests are parsed to and run as an AST, the two of them needn't be in the same language:
mulang '{
"sample" : {
"tag" : "CodeSample",
"language" : "Python",
"content" : "def f():
x = 0
while x < 10:
x += 1
return x"
},
"spec" : {
"testAnalysisType" : {
"tag" : "ExternalTests",
"test" : {
"tag" : "CodeSample",
"language" : "JavaScript",
"content" : "
it(\"f returns 10\", function() {
assert.equals(f(), 10)
})"
}
}
}
}' | json_pp
{
"tag" : "AnalysisCompleted",
"testResults" : [
{
"status" : {
"tag" : "Success"
},
"description" : [
"f returns 10"
]
}
]
}