similarity

AST similarity caliculator by APTED / Tree Edit Distance

moon add mizchi/similarity@0.2.1
Download zip
Author
Version
0.2.1
License
Apache-2.0
Last updated
5 months ago
Downloads
56K
README

#similarity.mbt

Code similarity detection for MoonBit using AST-based comparison (TSED - Tree Structure Edit Distance).

#Features

  • AST-based comparison - Uses moonbitlang/parser to parse MoonBit code
  • TSED Algorithm - Tree Structure Edit Distance for accurate structural similarity
  • Function extraction - Extracts functions, methods, and tests from MoonBit source
  • Cross-file detection - Compare functions across multiple files

#Installation

#CLI Tool

git clone https://github.com/mizchi/similarity.mbt cd similarity.mbt make install # Installs to ~/.local/bin/similarity-mbt

Make sure ~/.local/bin is in your PATH.

#As a Library

Add to your moon.mod.json:

{ "deps": { "mizchi/similarity": "0.1.0" } }

#Usage

#CLI

# Scan all .mbt files in current directory similarity-mbt # Scan specific files similarity-mbt src/*.mbt # Adjust threshold (default: 0.88) similarity-mbt -t 0.95 *.mbt # Exclude test files similarity-mbt --no-tests # Show help similarity-mbt -h

#Basic similarity detection (Library)

let source =
#|fn add(a : Int, b : Int) -> Int { a + b }
#|fn sum(x : Int, y : Int) -> Int { x + y }

let options = @similarity.DetectorOptions::default()
let results = @similarity.detect_similarities(source, options)

for result in results {
println(result) // add <-> sum (similarity: 95%, score: 2.8)
}

#Custom options

///|
let options : @similarity.DetectorOptions = {
threshold: 0.80, // Minimum similarity (0.0-1.0)
min_lines: 3, // Minimum lines per function
size_penalty: true, // Penalize size differences
}

#Cross-file comparison

let files = [
("file1.mbt", source1),
("file2.mbt", source2),
]
let results = @similarity.detect_cross_file_similarities(files, options)

for result in results {
let (file1, file2, sim) = result
println("\{file1}:\{sim.func1.name} <-> \{file2}:\{sim.func2.name}")
}

#Direct tree comparison

///|
let functions = @similarity.extract_functions(source)

///|
let tsed_options = @similarity.TSEDOptions::default()

///|
let similarity = @similarity.calculate_tsed(
functions[0].tree,
functions[1].tree,
tsed_options,
)

#API

#Types

  • DetectorOptions - Configuration for similarity detection
  • FunctionInfo - Extracted function with name, line info, and AST tree
  • SimilarityResult - Comparison result with similarity score
  • TreeNode - AST tree node for TSED comparison

#Functions

  • detect_similarities(source, options) - Detect similar functions in source
  • detect_cross_file_similarities(sources, options) - Cross-file comparison
  • extract_functions(source) - Extract functions from source code
  • calculate_tsed(tree1, tree2, options) - Calculate tree edit distance

#Algorithm

Based on APTED (All Path Tree Edit Distance) with TSED normalization:

  1. Parse - Convert MoonBit source to AST using moonbitlang/parser
  2. Extract - Extract function bodies as tree structures
  3. Compare - Calculate edit distance between trees
  4. Normalize - Apply size penalties and normalization

#License

Apache-2.0

#
APTEDOptions

pub(all) struct APTEDOptions {
rename_cost : Double
delete_cost : Double
insert_cost : Double
compare_values : Bool
}

#
APTEDOptions::default

fn APTEDOptions::default() -> APTEDOptions

#
DetectorOptions

pub(all) struct DetectorOptions {
threshold : Double
min_lines : Int
size_penalty : Bool
}

#
DetectorOptions::default

#
FunctionInfo

pub(all) struct FunctionInfo {
name : String
start_line : Int
end_line : Int
tree : TreeNode
}

#
SimilarityResult

pub(all) struct SimilarityResult {
func1 : FunctionInfo
func2 : FunctionInfo
similarity : Double
score : Double
}

#
TSEDOptions

pub(all) struct TSEDOptions {
apted_options : APTEDOptions
min_lines : Int
min_tokens : Int?
size_penalty : Bool
}

#
TSEDOptions::default

fn TSEDOptions::default() -> TSEDOptions

#
TreeNode

pub(all) struct TreeNode {
label : String
value : String
children : Array[TreeNode]
id : Int
cached_size : Int
}

impl Show for TreeNode

#
TreeNode::add_child

fn TreeNode::add_child(self : TreeNode, child : TreeNode) -> Unit

#
TreeNode::finalize_labels

fn TreeNode::finalize_labels(self : TreeNode) -> Unit

Add children count to labels for better structural comparison e.g., "For" -> "For_3" if the node has 3 children

#
TreeNode::get_subtree_size

fn TreeNode::get_subtree_size(self : TreeNode) -> Int

#
TreeNode::new

fn TreeNode::new(label : String, value : String, id : Int) -> TreeNode

#
calculate_tsed

fn calculate_tsed(tree1 : TreeNode, tree2 : TreeNode, options : TSEDOptions) -> Double

#
compute_edit_distance

fn compute_edit_distance(tree1 : TreeNode, tree2 : TreeNode, options : APTEDOptions) -> Double

#
detect_cross_file_similarities

fn detect_cross_file_similarities(sources : Array[(String, String)], options : DetectorOptions) -> Array[(String, String, SimilarityResult)]

#
detect_similarities

fn detect_similarities(source : String, options : DetectorOptions) -> Array[SimilarityResult]

#
extract_functions

fn extract_functions(source : String) -> Array[FunctionInfo]

Powered by MoonBit

Site sourceReport issuePackagesBuild queueSkillsStatistics

© 2026 mooncakes.io