///|
pub struct CommandOccurrence {
letter_value : Int
code_value : Double
line_value : Int
column_value : Int
} derive(Eq, Debug)
///|
pub fn CommandOccurrence::letter(self : CommandOccurrence) -> Int {
self.letter_value
}
///|
pub fn CommandOccurrence::code(self : CommandOccurrence) -> Double {
self.code_value
}
///|
pub fn CommandOccurrence::line(self : CommandOccurrence) -> Int {
self.line_value
}
///|
pub fn CommandOccurrence::column(self : CommandOccurrence) -> Int {
self.column_value
}
///|
pub struct LetterUsage {
letter_value : Int
count_value : Int
first_line_value : Int
last_line_value : Int
minimum_value : Double
maximum_value : Double
} derive(Eq, Debug)
///|
pub fn LetterUsage::letter(self : LetterUsage) -> Int {
self.letter_value
}
///|
pub fn LetterUsage::count(self : LetterUsage) -> Int {
self.count_value
}
///|
pub fn LetterUsage::first_line(self : LetterUsage) -> Int {
self.first_line_value
}
///|
pub fn LetterUsage::last_line(self : LetterUsage) -> Int {
self.last_line_value
}
///|
pub fn LetterUsage::minimum(self : LetterUsage) -> Double {
self.minimum_value
}
///|
pub fn LetterUsage::maximum(self : LetterUsage) -> Double {
self.maximum_value
}
///|
pub struct SequenceGap {
previous_value : Int
current_value : Int
line_value : Int
missing_value : Int
} derive(Eq, Debug)
///|
pub fn SequenceGap::previous(self : SequenceGap) -> Int {
self.previous_value
}
///|
pub fn SequenceGap::current(self : SequenceGap) -> Int {
self.current_value
}
///|
pub fn SequenceGap::line(self : SequenceGap) -> Int {
self.line_value
}
///|
pub fn SequenceGap::missing(self : SequenceGap) -> Int {
self.missing_value
}
///|
pub struct ProgramIndex {
commands_value : Array[CommandOccurrence]
usage_value : Array[LetterUsage]
executable_lines_value : Array[Int]
comment_lines_value : Array[Int]
sequence_gaps_value : Array[SequenceGap]
} derive(Eq, Debug)
///|
pub fn ProgramIndex::commands(self : ProgramIndex) -> Array[CommandOccurrence] {
self.commands_value.copy()
}
///|
pub fn ProgramIndex::usage(self : ProgramIndex) -> Array[LetterUsage] {
self.usage_value.copy()
}
///|
pub fn ProgramIndex::executable_lines(self : ProgramIndex) -> Array[Int] {
self.executable_lines_value.copy()
}
///|
pub fn ProgramIndex::comment_lines(self : ProgramIndex) -> Array[Int] {
self.comment_lines_value.copy()
}
///|
pub fn ProgramIndex::sequence_gaps(self : ProgramIndex) -> Array[SequenceGap] {
self.sequence_gaps_value.copy()
}
///|
pub fn ProgramIndex::command_count(self : ProgramIndex) -> Int {
self.commands_value.length()
}
///|
pub fn ProgramIndex::find_command(
self : ProgramIndex,
letter : Int,
code : Double,
) -> Array[CommandOccurrence] {
let matches : Array[CommandOccurrence] = []
let upper = ascii_upper(letter)
for occurrence in self.commands_value {
if occurrence.letter() == upper && occurrence.code() == code {
matches.push(occurrence)
}
}
matches
}
///|
pub fn ProgramIndex::usage_for(
self : ProgramIndex,
letter : Int,
) -> LetterUsage? {
let upper = ascii_upper(letter)
for usage in self.usage_value {
if usage.letter() == upper {
return Some(usage)
}
}
None
}
///|
priv struct MutableLetterUsage {
letter : Int
mut count : Int
first_line : Int
mut last_line : Int
mut minimum : Double
mut maximum : Double
}
///|
fn update_usage(
entries : Array[MutableLetterUsage],
word : BlockWord,
line : Int,
) -> Unit {
for entry in entries {
if entry.letter == word.letter() {
entry.count += 1
entry.last_line = line
if word.value() < entry.minimum {
entry.minimum = word.value()
}
if word.value() > entry.maximum {
entry.maximum = word.value()
}
return
}
}
entries.push({
letter: word.letter(),
count: 1,
first_line: line,
last_line: line,
minimum: word.value(),
maximum: word.value(),
})
}
///|
fn sort_usage(entries : Array[MutableLetterUsage]) -> Array[LetterUsage] {
let result : Array[LetterUsage] = []
for letter in 65..<91 {
for entry in entries {
if entry.letter == letter {
result.push({
letter_value: entry.letter,
count_value: entry.count,
first_line_value: entry.first_line,
last_line_value: entry.last_line,
minimum_value: entry.minimum,
maximum_value: entry.maximum,
})
}
}
}
result
}
///|
pub fn index_document(
document : GCodeDocument,
expected_sequence_step? : Int = 0,
) -> ProgramIndex {
let commands : Array[CommandOccurrence] = []
let mutable_usage : Array[MutableLetterUsage] = []
let executable_lines : Array[Int] = []
let comment_lines : Array[Int] = []
let gaps : Array[SequenceGap] = []
let mut previous_sequence : Int? = None
for block in document.blocks() {
if block.word_count() > 0 {
executable_lines.push(block.source_line())
}
if block.comments().length() > 0 {
comment_lines.push(block.source_line())
}
for word in block.words() {
update_usage(mutable_usage, word, block.source_line())
if word.letter() == 71 || word.letter() == 77 {
commands.push({
letter_value: word.letter(),
code_value: word.value(),
line_value: block.source_line(),
column_value: word.column(),
})
}
}
match (previous_sequence, block.sequence()) {
(Some(previous), Some(current)) => {
let actual_step = current - previous
if expected_sequence_step > 0 && actual_step > expected_sequence_step {
gaps.push({
previous_value: previous,
current_value: current,
line_value: block.source_line(),
missing_value: actual_step / expected_sequence_step - 1,
})
}
previous_sequence = Some(current)
}
(_, Some(current)) => previous_sequence = Some(current)
_ => ()
}
}
{
commands_value: commands,
usage_value: sort_usage(mutable_usage),
executable_lines_value: executable_lines,
comment_lines_value: comment_lines,
sequence_gaps_value: gaps,
}
}
///|
pub fn render_program_index(index : ProgramIndex) -> String {
let mut output = "commands=" + index.command_count().to_string()
output = output +
", executable_lines=" +
index.executable_lines().length().to_string()
output = output +
", comment_lines=" +
index.comment_lines().length().to_string()
output = output +
", sequence_gaps=" +
index.sequence_gaps().length().to_string() +
"\n"
for usage in index.usage() {
output = output + usage.letter().unsafe_to_char().to_string()
output = output + " count=" + usage.count().to_string()
output = output +
" lines=" +
usage.first_line().to_string() +
".." +
usage.last_line().to_string()
output = output +
" range=" +
usage.minimum().to_string() +
".." +
usage.maximum().to_string() +
"\n"
}
output
}
///|
pub struct ToolSection {
ordinal_value : Int
tool_value : Int?
start_line_value : Int
end_line_value : Int
blocks_value : Array[GCodeBlock]
} derive(Eq, Debug)
///|
pub fn ToolSection::ordinal(self : ToolSection) -> Int {
self.ordinal_value
}
///|
pub fn ToolSection::tool(self : ToolSection) -> Int? {
self.tool_value
}
///|
pub fn ToolSection::start_line(self : ToolSection) -> Int {
self.start_line_value
}
///|
pub fn ToolSection::end_line(self : ToolSection) -> Int {
self.end_line_value
}
///|
pub fn ToolSection::blocks(self : ToolSection) -> Array[GCodeBlock] {
self.blocks_value.copy()
}
///|
pub fn ToolSection::document(self : ToolSection) -> GCodeDocument {
{
blocks_value: self.blocks_value.copy(),
physical_lines_value: self.blocks_value.length(),
}
}
///|
pub fn split_by_tool(document : GCodeDocument) -> Array[ToolSection] {
let sections : Array[ToolSection] = []
let mut current_blocks : Array[GCodeBlock] = []
let mut current_tool : Int? = None
let mut start_line = 1
let mut ordinal = 0
for block in document.blocks() {
let tool = match block.first_value(84) {
Some(value) => integer_code(value)
None => None
}
if tool is Some(_) && current_blocks.length() > 0 {
sections.push({
ordinal_value: ordinal,
tool_value: current_tool,
start_line_value: start_line,
end_line_value: block.source_line() - 1,
blocks_value: current_blocks,
})
ordinal += 1
current_blocks = []
start_line = block.source_line()
}
match tool {
Some(value) => current_tool = Some(value)
None => ()
}
current_blocks.push(block)
}
if current_blocks.length() > 0 {
sections.push({
ordinal_value: ordinal,
tool_value: current_tool,
start_line_value: start_line,
end_line_value: document.physical_lines(),
blocks_value: current_blocks,
})
}
sections
}
///|
pub(all) enum BlockDifferenceKind {
AddedBlock
RemovedBlock
ChangedBlock
} derive(Eq, Debug)
///|
pub struct BlockDifference {
kind_value : BlockDifferenceKind
line_value : Int
left_value : String?
right_value : String?
} derive(Eq, Debug)
///|
pub fn BlockDifference::kind(self : BlockDifference) -> BlockDifferenceKind {
self.kind_value
}
///|
pub fn BlockDifference::line(self : BlockDifference) -> Int {
self.line_value
}
///|
pub fn BlockDifference::left(self : BlockDifference) -> String? {
self.left_value
}
///|
pub fn BlockDifference::right(self : BlockDifference) -> String? {
self.right_value
}
///|
pub struct DocumentDifference {
differences_value : Array[BlockDifference]
equal_blocks_value : Int
left_lines_value : Int
right_lines_value : Int
} derive(Eq, Debug)
///|
pub fn DocumentDifference::differences(
self : DocumentDifference,
) -> Array[BlockDifference] {
self.differences_value.copy()
}
///|
pub fn DocumentDifference::equal_blocks(self : DocumentDifference) -> Int {
self.equal_blocks_value
}
///|
pub fn DocumentDifference::is_equal(self : DocumentDifference) -> Bool {
self.differences_value.length() == 0
}
///|
pub fn compare_documents(
left : GCodeDocument,
right : GCodeDocument,
ignore_comments? : Bool = false,
) -> DocumentDifference {
let differences : Array[BlockDifference] = []
let left_blocks = left.blocks()
let right_blocks = right.blocks()
let maximum = if left_blocks.length() > right_blocks.length() {
left_blocks.length()
} else {
right_blocks.length()
}
let mut equal = 0
for index in 0..= left_blocks.length() {
differences.push({
kind_value: AddedBlock,
line_value: index + 1,
left_value: None,
right_value: Some(right_blocks[index].source()),
})
} else if index >= right_blocks.length() {
differences.push({
kind_value: RemovedBlock,
line_value: index + 1,
left_value: Some(left_blocks[index].source()),
right_value: None,
})
} else {
let left_text = if ignore_comments {
left_blocks[index].body()
} else {
left_blocks[index].source()
}
let right_text = if ignore_comments {
right_blocks[index].body()
} else {
right_blocks[index].source()
}
if left_text == right_text {
equal += 1
} else {
differences.push({
kind_value: ChangedBlock,
line_value: index + 1,
left_value: Some(left_text),
right_value: Some(right_text),
})
}
}
}
{
differences_value: differences,
equal_blocks_value: equal,
left_lines_value: left.physical_lines(),
right_lines_value: right.physical_lines(),
}
}
///|
pub fn render_document_difference(difference : DocumentDifference) -> String {
let mut output = "equal=" + difference.equal_blocks().to_string()
output = output +
", differences=" +
difference.differences().length().to_string() +
"\n"
for item in difference.differences() {
let kind = match item.kind() {
AddedBlock => "added"
RemovedBlock => "removed"
ChangedBlock => "changed"
}
output = output + kind + " line=" + item.line().to_string()
match item.left() {
Some(value) => output = output + " left=" + value
None => ()
}
match item.right() {
Some(value) => output = output + " right=" + value
None => ()
}
output = output + "\n"
}
output
}