mulang 5.1.0.2 → 6.0.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -9,16 +9,65 @@ module Mulang
9
9
  def self.bin_path
10
10
  File.join(__dir__, '..', 'bin', 'mulang')
11
11
  end
12
- def self.analyse(analysis)
13
- Open3.popen2(bin_path, '-s') do |input, output, _thread|
14
- input.puts analysis.to_json
12
+ def self.analyse(analysis, **options)
13
+ arg, mode = Mulang::RunMode.for analysis, options
14
+ Open3.popen2(bin_path, arg) do |input, output, _thread|
15
+ input.puts mode.input(analysis).to_json
15
16
  input.close
16
- JSON.parse output.read
17
+ result = JSON.parse output.read
18
+ mode.output result
19
+ end
20
+ end
21
+
22
+ private
23
+
24
+ module RunMode
25
+ def self.for(analysis, options)
26
+ serialization = options[:serialization]
27
+ many = analysis.is_a?(Array)
28
+ if many
29
+ [encode_serialization(serialization), Mulang::RunMode::Natural]
30
+ elsif serialization
31
+ [encode_serialization(serialization), Mulang::RunMode::ForcedMany]
32
+ else
33
+ ["-s", Mulang::RunMode::Natural]
34
+ end
35
+ end
36
+
37
+ def self.encode_serialization(option)
38
+ case option
39
+ when nil then '-S'
40
+ when :bracket then '-B'
41
+ when :brace then '-C'
42
+ else raise "Unsupported serialization #{option}"
43
+ end
44
+ end
45
+
46
+ module Natural
47
+ def self.input(analysis)
48
+ analysis
49
+ end
50
+
51
+ def self.output(result)
52
+ result
53
+ end
54
+ end
55
+
56
+ module ForcedMany
57
+ def self.input(analysis)
58
+ [analysis]
59
+ end
60
+
61
+ def self.output(result)
62
+ result[0]
63
+ end
17
64
  end
18
65
  end
19
66
  end
20
67
 
68
+ require_relative './mulang/tokens'
21
69
  require_relative './mulang/inspection'
22
70
  require_relative './mulang/expectation'
23
71
  require_relative './mulang/code'
24
72
  require_relative './mulang/language'
73
+ require_relative './mulang/sexp'
@@ -1,12 +1,29 @@
1
1
  module Mulang
2
2
  class Code
3
+ attr_accessor :language, :content
3
4
  def initialize(language, content)
4
5
  @language = language
5
6
  @content = content
6
7
  end
7
8
 
8
- def ast
9
- @language.ast @content
9
+ def identifiers(**options)
10
+ @language.identifiers @content, **options
11
+ end
12
+
13
+ def ast(**options)
14
+ @language.ast @content, **options
15
+ end
16
+
17
+ def ast_analysis(**options)
18
+ @language.ast_analysis @content, **options
19
+ end
20
+
21
+ def transformed_asts(operations, **options)
22
+ @language.transformed_asts @content, operations, **options
23
+ end
24
+
25
+ def transformed_asts_analysis(operations, **options)
26
+ @language.transformed_asts_analysis @content, operations, **options
10
27
  end
11
28
 
12
29
  def sample
@@ -17,8 +34,8 @@ module Mulang
17
34
  { sample: sample, spec: spec }
18
35
  end
19
36
 
20
- def analyse(spec)
21
- Mulang.analyse analysis(spec)
37
+ def analyse(spec, **options)
38
+ Mulang.analyse analysis(spec), **options
22
39
  end
23
40
 
24
41
  def expect(binding='*', inspection)
@@ -43,12 +60,25 @@ module Mulang
43
60
  native(*args).tap { |it| it.expect('Parses') }
44
61
  end
45
62
 
46
- def self.external(content, &tool)
47
- new Mulang::Language::External.new(&tool), content
63
+ def self.external(language_name = nil, content, &tool)
64
+ new Mulang::Language::External.new(language_name, &tool), content
65
+ end
66
+
67
+ def self.analyse_many(codes, spec, **options)
68
+ run_many(codes, **options) { |it| it.analysis(spec) }
69
+ end
70
+
71
+ def self.ast_many(codes, **options)
72
+ run_many(codes, key: 'outputAst', **options) { |it| it.ast_analysis(**options) }
73
+ end
74
+
75
+ def self.transformed_asts_many(codes, operations, **options)
76
+ run_many(codes, key: 'transformedAsts', **options) { |it| it.transformed_asts_analysis(operations, **options) }
48
77
  end
49
78
 
50
- def self.ast(ast)
51
- new Mulang::Language::External.new, ast
79
+ def self.run_many(codes, key: nil, **options)
80
+ result = Mulang.analyse(codes.map { |it| yield it }, **options)
81
+ key ? result.map { |it| it[key] } : result
52
82
  end
53
83
 
54
84
  private
@@ -1,12 +1,73 @@
1
1
  module Mulang::Expectation
2
- SMELLS = %w(DiscardsExceptions DoesConsolePrint DoesNilTest DoesNullTest DoesTypeTest
3
- HasAssignmentReturn HasCodeDuplication HasEmptyIfBranches HasRedundantRepeat HasLongParameterList
4
- HasMisspelledBindings HasMisspelledIdentifiers
5
- HasRedundantBooleanComparison HasRedundantGuards HasRedundantIf
6
- HasRedundantLambda HasRedundantLocalVariableReturn HasRedundantParameter
7
- HasRedundantReduction HasTooManyMethods HasTooShortBindings HasTooShortIdentifiers HasUnreachableCode
8
- HasWrongCaseBinding HasWrongCaseIdentifiers IsLongCode OverridesEqualOrHashButNotBoth
9
- ReturnsNil ReturnsNull UsesCut UsesFail UsesUnificationOperator)
2
+ LOGIC_SMELLS = %w(
3
+ HasRedundantReduction
4
+ UsesCut
5
+ UsesFail
6
+ UsesUnificationOperator
7
+ )
8
+
9
+ FUNCTIONAL_SMELLS = %w(
10
+ HasRedundantGuards
11
+ HasRedundantParameter
12
+ ShouldUseOtherwise
13
+ )
14
+
15
+ OBJECT_ORIENTED_SMELLS = %w(
16
+ DoesNilTest
17
+ DoesNullTest
18
+ HasTooManyMethods
19
+ OverridesEqualOrHashButNotBoth
20
+ ReturnsNil
21
+ ReturnsNull
22
+ DoesTypeTest
23
+ )
24
+
25
+ IMPERATIVE_SMELLS = %w(
26
+ HasAssignmentCondition
27
+ HasAssignmentReturn
28
+ HasEmptyRepeat
29
+ HasRedundantRepeat
30
+ )
31
+
32
+ EXPRESSIVENESS_SMELLS = %w(
33
+ HasMisspelledBindings
34
+ HasMisspelledIdentifiers
35
+ HasTooShortIdentifiers
36
+ HasWrongCaseBinding
37
+ HasWrongCaseIdentifiers
38
+ )
39
+
40
+ GENERIC_SMELLS = %w(
41
+ DiscardsExceptions
42
+ DoesConsolePrint
43
+ HasCodeDuplication
44
+ HasDeclarationTypos
45
+ HasEmptyIfBranches
46
+ HasEqualIfBranches
47
+ HasLongParameterList
48
+ HasRedundantBooleanComparison
49
+ HasRedundantLocalVariableReturn
50
+ HasRedundantIf
51
+ HasRedundantLambda
52
+ HasTooShortBindings
53
+ HasUnreachableCode
54
+ HasUsageTypos
55
+ IsLongCode
56
+ ShouldInvertIfCondition
57
+ ShouldUseStrictComparators
58
+ )
59
+
60
+ JAVA_SCRIPT_SMELLS = %w(
61
+ JavaScript#UsesVarInsteadOfLet
62
+ )
63
+
64
+ SMELLS = GENERIC_SMELLS +
65
+ EXPRESSIVENESS_SMELLS +
66
+ IMPERATIVE_SMELLS +
67
+ OBJECT_ORIENTED_SMELLS +
68
+ FUNCTIONAL_SMELLS +
69
+ LOGIC_SMELLS +
70
+ JAVA_SCRIPT_SMELLS
10
71
 
11
72
  def self.guess_type(expectation)
12
73
  if expectation[:binding] == '<<custom>>'
@@ -19,7 +80,7 @@ module Mulang::Expectation
19
80
  end
20
81
 
21
82
  def self.has_smell?(smell)
22
- SMELLS.include? smell
83
+ SMELLS.include? smell.split(':').first
23
84
  end
24
85
 
25
86
  def self.parse(expectation)
@@ -1,45 +1,27 @@
1
1
  module Mulang::Expectation::I18n
2
2
  class << self
3
- DEFAULT_KEYWORDS = {
4
- keyword_fail: :fail,
5
- keyword_false: :false,
6
- keyword_findall: :findall,
7
- keyword_for: :for,
8
- keyword_forall: :forall,
9
- keyword_foreach: :foreach,
10
- keyword_if: :if,
11
- keyword_is: :is,
12
- keyword_not: :not,
13
- keyword_null: :null,
14
- keyword_repeat: :repeat,
15
- keyword_switch: :switch,
16
- keyword_true: :true,
17
- keyword_while: :while,
18
- keyword_yield: :yield
19
- }
20
-
21
- def translate(e, keywords = nil)
22
- translate!(e, keywords)
3
+ def translate(e, tokens = nil)
4
+ translate!(e, tokens)
23
5
  rescue
24
6
  '<unknown expectation>'
25
7
  end
26
8
 
27
- def translate!(e, keywords = nil)
9
+ def translate!(e, tokens = nil)
28
10
  e = e.as_v2
29
11
  key = key_for e.binding, e.inspection
30
- ::I18n.t key, translation_params(e, keywords)
12
+ ::I18n.t key, translation_params(e, tokens)
31
13
  end
32
14
 
33
15
  alias t translate
34
16
 
35
17
  private
36
18
 
37
- def translation_params(e, keywords)
38
- with_keywords keywords,
19
+ def translation_params(e, tokens)
20
+ with_tokens tokens,
39
21
  binding: t_binding(e.binding),
40
22
  target: t_target(e.inspection),
41
23
  must: t_must(e.inspection),
42
- matching: t_matching(keywords, e.inspection)
24
+ matching: t_matching(tokens, e.inspection)
43
25
  end
44
26
 
45
27
  def key_for(binding, inspection)
@@ -47,7 +29,7 @@ module Mulang::Expectation::I18n
47
29
  end
48
30
 
49
31
  def t_binding(binding)
50
- binding == '*' ? ::I18n.t("mulang.expectation.solution") : "<strong>#{Mulang::Inspection.parse_binding_name binding}</strong>"
32
+ binding == '*' ? ::I18n.t("mulang.expectation.solution") : "<code>#{Mulang::Inspection.parse_binding_name binding}</code>"
51
33
  end
52
34
 
53
35
  def t_must(parsed)
@@ -55,15 +37,23 @@ module Mulang::Expectation::I18n
55
37
  end
56
38
 
57
39
  def t_target(parsed)
58
- "<strong>#{parsed.target.value}</strong>" if parsed.target
40
+ "<code>#{parsed.target.value}</code>" if parsed.target
59
41
  end
60
42
 
61
- def t_matching(keywords, parsed)
62
- ::I18n.t("mulang.expectation.#{parsed.matcher.type}", with_keywords(keywords, value: parsed.matcher.value)) if parsed.matcher
43
+ def t_matching(tokens, parsed)
44
+ ::I18n.t("mulang.expectation.#{parsed.matcher.type}", with_tokens(tokens, value: parsed.matcher.value)) if parsed.matcher
63
45
  end
64
46
 
65
- def with_keywords(keywords, params)
66
- params.merge(DEFAULT_KEYWORDS).merge(keywords || {})
47
+ def with_tokens(tokens, params)
48
+ hash = if tokens.nil?
49
+ {}
50
+ elsif tokens.is_a?(Hash)
51
+ tokens
52
+ else
53
+ params.merge(Mulang::Tokens::TOKENS.indifferent_get(tokens))
54
+ end
55
+
56
+ params.merge(Mulang::Tokens::DEFAULT_TOKENS.merge(hash))
67
57
  end
68
58
  end
69
59
  end
@@ -11,12 +11,12 @@ class Mulang::Expectation::Standard
11
11
  raise "Wrong inspection #{to_h}" unless inspection?
12
12
  end
13
13
 
14
- def translate(keywords = nil)
15
- Mulang::Expectation::I18n.translate self, keywords
14
+ def translate(tokens = nil)
15
+ Mulang::Expectation::I18n.translate self, tokens
16
16
  end
17
17
 
18
- def translate!(keywords = nil)
19
- Mulang::Expectation::I18n.translate! self, keywords
18
+ def translate!(tokens = nil)
19
+ Mulang::Expectation::I18n.translate! self, tokens
20
20
  end
21
21
 
22
22
  def to_h
@@ -72,7 +72,7 @@ class Mulang::Expectation::V0 < Mulang::Expectation::Standard
72
72
  end
73
73
 
74
74
  def as_v2_use
75
- Mulang::Expectation::V2.new binding, new_inspection(inspection.type.gsub('Has', 'Uses'), nil)
75
+ Mulang::Expectation::V2.new binding, new_inspection(inspection.type.gsub(/^Has/, 'Uses'), nil)
76
76
  end
77
77
 
78
78
  def as_v2_declare(simple_type)
@@ -1,43 +1,95 @@
1
1
  module Mulang::Language
2
- class Native
3
- def initialize(language)
4
- @language = language
2
+ class Base
3
+ def identifiers(content, **options)
4
+ Mulang.analyse(identifiers_analysis(content, **options), **options)['outputIdentifiers'] rescue nil
5
5
  end
6
6
 
7
- def ast(content)
8
- Mulang.analyse(ast_analysis(content))['intermediateLanguage'] rescue nil
7
+ def identifiers_analysis(content, **options)
8
+ base_analysis content, {includeOutputIdentifiers: true}, **options
9
9
  end
10
10
 
11
- def ast_analysis(content)
11
+ def transformed_asts(content, operations, **options)
12
+ Mulang.analyse(transformed_asts_analysis(content, operations, **options), **options)['transformedAsts'] rescue nil
13
+ end
14
+
15
+ def transformed_asts_analysis(content, operations, **options)
16
+ base_analysis content, {transformationSpecs: operations}, **options
17
+ end
18
+
19
+ def normalization_options(**options)
20
+ options.except(:serialization).presence
21
+ end
22
+
23
+ def ast(content, **options)
24
+ Mulang.analyse(ast_analysis(content, **options), **options)['outputAst'] rescue nil
25
+ end
26
+
27
+ def ast_analysis(content, **options)
28
+ base_analysis content, {includeOutputAst: true}, **options
29
+ end
30
+
31
+ private
32
+
33
+ def base_analysis(content, spec, **options)
12
34
  {
13
- sample: { tag: 'CodeSample', language: @language, content: content },
14
- spec: { expectations: [], smellsSet: { tag: 'NoSmells' }, includeIntermediateLanguage: true }
35
+ sample: sample(content),
36
+ spec: {
37
+ expectations: [],
38
+ smellsSet: { tag: 'NoSmells' },
39
+ includeOutputAst: false,
40
+ normalizationOptions: normalization_options(options)
41
+ }.merge(spec).compact
15
42
  }
16
43
  end
44
+ end
45
+
46
+ class Native < Base
47
+ attr_accessor :name
48
+
49
+ def initialize(language_name)
50
+ @name = language_name
51
+ end
17
52
 
18
53
  def sample(content)
19
54
  {
20
55
  tag: 'CodeSample',
21
- language: @language,
56
+ language: @name,
22
57
  content: content
23
58
  }
24
59
  end
25
60
  end
26
61
 
27
- class External
28
- def initialize(&tool)
62
+ class External < Base
63
+ attr_accessor :name
64
+
65
+ def initialize(language_name = nil, &tool)
66
+ @name = language_name
29
67
  @tool = block_given? ? tool : proc { |it| it }
30
68
  end
31
69
 
32
- def ast(content)
33
- @tool.call(content) rescue nil
70
+ def ast(content, **args)
71
+ if args[:serialization]
72
+ super
73
+ else
74
+ call_tool content
75
+ end
34
76
  end
35
77
 
36
78
  def sample(content)
37
79
  {
38
80
  tag: 'MulangSample',
39
- ast: ast(content)
81
+ ast: call_tool(content)
40
82
  }
41
83
  end
84
+
85
+ def base_analysis(*)
86
+ super.deep_merge(spec: {originalLanguage: @name}.compact)
87
+ end
88
+
89
+ private
90
+
91
+ def call_tool(content)
92
+ @tool.call(content) rescue nil
93
+ end
42
94
  end
43
95
  end