Skip to content

Commit

Permalink
Merge branch 'feature/offset' into develop
Browse files Browse the repository at this point in the history
  • Loading branch information
neoneye committed Dec 11, 2024
2 parents d97a130 + 25da4c1 commit fa4d44c
Show file tree
Hide file tree
Showing 11 changed files with 244 additions and 25 deletions.
1 change: 1 addition & 0 deletions rust_project/loda-rust-cli/src/mine/genome.rs
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,7 @@ impl Genome {
}

ParsedProgram {
optional_offset: None,
instruction_vec: instruction_vec
}
}
Expand Down
37 changes: 29 additions & 8 deletions rust_project/loda-rust-cli/src/postmine/format_program.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,12 +62,15 @@ impl FormatProgram {
}

pub fn build(&self) -> anyhow::Result<String> {
let parsed_program: ParsedProgram = match ParsedProgram::parse_program(&self.program_content) {
let parsed_program_with_optional_offset: ParsedProgram = match ParsedProgram::parse_program(&self.program_content) {
Ok(value) => value,
Err(error) => {
return Err(anyhow::anyhow!("Parse program from {:?} error: {:?} content: {:?}", &self.program_path, error, self.program_content));
}
};
let offset: Option<i32> = parsed_program_with_optional_offset.optional_offset;
let parsed_program: ParsedProgram = parsed_program_with_optional_offset.without_offset();


// Don't load dependencies from the file system,
// by pretending that all the dependencies are empty programs
Expand Down Expand Up @@ -125,11 +128,17 @@ impl FormatProgram {
if let Some(loda_submitted_by) = &self.loda_submitted_by {
serializer.append_comment(format!("Submitted by {}", loda_submitted_by));
}

// The initital terms
if let Some(terms) = &self.terms {
serializer.append_comment(terms);
}

// Optional offset
if let Some(offset) = offset {
serializer.append_empty_line();
serializer.append_raw(format!("#offset {}", offset));
}

serializer.append_empty_line();
runner.serialize(&mut serializer);
Expand Down Expand Up @@ -174,7 +183,17 @@ mod tests {
}

#[test]
fn test_40000_format_program_sequence_name() -> Result<(), Box<dyn Error>> {
fn test_40000_format_program_offset() -> Result<(), Box<dyn Error>> {
let program = "#offset -42\nmul $0,-1".to_string();
let mut fp = FormatProgram::new(program);
fp.terms("42,43,44,45,46".to_string());
let formatted_program: String = fp.build()?;
assert_eq!(formatted_program, "; 42,43,44,45,46\n\n#offset -42\n\nmul $0,-1\n");
Ok(())
}

#[test]
fn test_50000_format_program_sequence_name() -> Result<(), Box<dyn Error>> {
let mut oeis_id_name_map = OeisIdStringMap::new();
oeis_id_name_map.insert(OeisId::from(40), "The primes".to_string());
let program = "mul $0,-1".to_string();
Expand All @@ -187,7 +206,7 @@ mod tests {
}

#[test]
fn test_40001_format_program_seq_instructions() -> Result<(), Box<dyn Error>> {
fn test_50001_format_program_seq_instructions() -> Result<(), Box<dyn Error>> {
let mut oeis_id_name_map = OeisIdStringMap::new();
oeis_id_name_map.insert(OeisId::from(45), "Fibonacci".to_string());
let program = "seq $0,45".to_string();
Expand All @@ -199,7 +218,7 @@ mod tests {
}

#[test]
fn test_40002_format_program_without_name_for_program_oeis_id() -> Result<(), Box<dyn Error>> {
fn test_50002_format_program_without_name_for_program_oeis_id() -> Result<(), Box<dyn Error>> {
let oeis_id_name_map = OeisIdStringMap::new();
let program = "mul $0,2".to_string();
let mut fp = FormatProgram::new(program);
Expand All @@ -211,7 +230,7 @@ mod tests {
}

#[test]
fn test_50000_format_program_trim_comments_and_blanks() -> Result<(), Box<dyn Error>> {
fn test_60000_format_program_trim_comments_and_blanks() -> Result<(), Box<dyn Error>> {
let program = "; ignore\n mul $0,-1 ; ignore\n\n; ignore".to_string();
let fp = FormatProgram::new(program);
let formatted_program: String = fp.build()?;
Expand All @@ -220,7 +239,7 @@ mod tests {
}

#[test]
fn test_50000_format_program_parameter_type_indirect() -> Result<(), Box<dyn Error>> {
fn test_70000_format_program_parameter_type_indirect() -> Result<(), Box<dyn Error>> {
let program = "add $$0,1\n\n\nmul $1,$$0".to_string();
let fp = FormatProgram::new(program);
let formatted_program: String = fp.build()?;
Expand All @@ -234,7 +253,7 @@ mod tests {
let mut oeis_id_name_map = OeisIdStringMap::new();
oeis_id_name_map.insert(OeisId::from(40), "The primes".to_string());
oeis_id_name_map.insert(OeisId::from(72677), "a(n) = prime(prime(n)+1)".to_string());
let program = "seq $0,40\nseq $0,40".to_string();
let program = "#offset -123\nseq $0,40\nseq $0,40".to_string();
let mut fp = FormatProgram::new(program);
fp.program_oeis_id(OeisId::from(72677));
fp.oeis_id_name_map(oeis_id_name_map);
Expand All @@ -250,6 +269,8 @@ r#"; A072677: a(n) = prime(prime(n)+1)
; Submitted by Euler
; 5,7,13,19,37,43,61,71,89
#offset -123
seq $0,40 ; The primes
seq $0,40 ; The primes
"#;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -226,7 +226,7 @@ impl DependencyManager {
ExecuteProfile::SmallLimits => NodeCalcSemanticMode::SmallLimits
};
let create_program = CreateProgram::new(node_calc_semantic_mode);
let mut program: Program = match create_program.create_program(&parsed_program.instruction_vec, &self.unofficial_function_registry) {
let mut program: Program = match create_program.create_program(parsed_program, &self.unofficial_function_registry) {
Ok(value) => value,
Err(error) => {
return Err(DependencyManagerError::CreateProgram(error));
Expand Down Expand Up @@ -517,4 +517,11 @@ mod tests {
let runner: Rc::<ProgramRunner> = dm.load(1).unwrap();
assert_eq!(runner.inspect(10), "5,5,5,5,5,5,5,5,5,5");
}

#[test]
fn test_70000_offset_positive() {
let mut dm: DependencyManager = dependency_manager_mock("tests/offset");
let runner: Rc::<ProgramRunner> = dm.load(247).unwrap();
assert_eq!(runner.inspect(10), "0,3,10,25,56,119,246,501,1012,2035");
}
}
21 changes: 17 additions & 4 deletions rust_project/loda-rust-core/src/parser/create_program.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use super::{Instruction, InstructionId, InstructionParameter, ParameterType};
use super::{Instruction, InstructionId, InstructionParameter, ParameterType, ParsedProgram};
use super::validate_loops::*;
use crate::execute::{BoxNode, RegisterIndex, RegisterIndexAndType, RegisterType, Program, LOOP_RANGE_MAX_BITS};
use crate::execute::node_calc::*;
Expand Down Expand Up @@ -337,12 +337,25 @@ impl CreateProgram {
}
}

pub fn create_program(&self, instruction_vec: &Vec<Instruction>, unofficial_function_registry: &UnofficialFunctionRegistry) -> Result<Program, CreateProgramError> {
validate_loops(instruction_vec)?;
pub fn create_program(&self, parsed_program: &ParsedProgram, unofficial_function_registry: &UnofficialFunctionRegistry) -> Result<Program, CreateProgramError> {
validate_loops(&parsed_program.instruction_vec)?;

let mut stack_vec: Vec<(Program, LoopScope)> = vec!();
let mut program = Program::new();
for instruction in instruction_vec {
if let Some(offset) = parsed_program.optional_offset {
// Insert an add instruction at the beginning of the program, if offset is provided.
let instruction = Instruction {
instruction_id: InstructionId::Add,
parameter_vec: vec![
InstructionParameter::new(ParameterType::Direct, 0),
InstructionParameter::new(ParameterType::Constant, offset as i64),
],
line_number: 0,
};
let node = self.create_node_calc(&instruction)?;
program.push_boxed(node);
}
for instruction in &parsed_program.instruction_vec {
let id: InstructionId = instruction.instruction_id.clone();
match id {
InstructionId::LoopBegin => {
Expand Down
114 changes: 114 additions & 0 deletions rust_project/loda-rust-core/src/parser/extract_offset.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
use regex::Regex;
use lazy_static::lazy_static;
use std::fmt;

#[derive(Debug, PartialEq)]
pub enum ExtractOffsetError {
InvalidSyntax(usize),
}

impl fmt::Display for ExtractOffsetError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
Self::InvalidSyntax(line_number) =>
write!(f, "Invalid #offset syntax in line {}", line_number),
}
}
}

/// Remove rows like `#offset -123` from a LODA assembler program, and return the remaining string with the offset value if present.
pub fn extract_offset(input: &str, line_number: usize) -> Result<(String, Option<i32>), ExtractOffsetError> {
lazy_static! {
static ref EXTRACT_OFFSET_RE: Regex = Regex::new(
"^#offset\\s+(0|-?[1-9]\\d*)$"
).unwrap();
}

let input_trimmed: &str = input.trim_start();
if input_trimmed.starts_with("#offset") == false {
return Ok((String::from(input), None));
}

let captures = match EXTRACT_OFFSET_RE.captures(input) {
Some(value) => value,
None => {
return Err(ExtractOffsetError::InvalidSyntax(line_number));
}
};

let capture1: &str = captures.get(1).map_or("", |m| m.as_str());
let offset = match capture1.parse::<i32>() {
Ok(value) => value,
Err(_) => {
return Err(ExtractOffsetError::InvalidSyntax(line_number));
}
};

return Ok((String::from(capture1), Some(offset)));
}

#[cfg(test)]
mod tests {
use super::*;

static INPUT: &'static [&'static str] = &[
// valid offsets
"#offset 0",
"#offset 1",
"#offset\t4",
"#offset -123",
// invalid offsets
"#offset -00000123",
"#offset -0",
"#offset 007",
"#offset 4.5",
"#offset",
"#offset3",
"#offset 3 ; comment",
" #offset 1",
// not offsets
"mov $0,1 ; comment",
"#junk 123",
];

static OUTPUT: &'static [&'static str] = &[
// valid offsets
"<valid-offset 0>",
"<valid-offset 1>",
"<valid-offset 4>",
"<valid-offset -123>",
// invalid offsets
"InvalidSyntax(4)",
"InvalidSyntax(5)",
"InvalidSyntax(6)",
"InvalidSyntax(7)",
"InvalidSyntax(8)",
"InvalidSyntax(9)",
"InvalidSyntax(10)",
"InvalidSyntax(11)",
// not offsets
"mov $0,1 ; comment",
"#junk 123",
];

fn process<S: AsRef<str>>(input: S, line_number: usize) -> String {
let input = input.as_ref();
let (remaining, offset) = match extract_offset(input, line_number) {
Ok(value) => value,
Err(error) => {
return format!("{:?}", error);
}
};
if let Some(offset) = offset {
return format!("<valid-offset {}>", offset);
}
remaining
}

#[test]
fn it_works() {
for (index, input) in INPUT.iter().enumerate() {
assert_eq!(process(input, index), OUTPUT[index]);
}
}
}
2 changes: 2 additions & 0 deletions rust_project/loda-rust-core/src/parser/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
//! Translate from LODA source code to a program instance.
mod create_program;
mod extract_offset;
mod extract_row_re;
mod instruction;
mod instruction_id;
Expand All @@ -12,6 +13,7 @@ mod parse_program;
mod remove_comment;

pub use create_program::CreateProgram;
pub use extract_offset::{extract_offset, ExtractOffsetError};
pub use extract_row_re::EXTRACT_ROW_RE;
pub use instruction::Instruction;
pub use instruction_id::InstructionId;
Expand Down
Loading

0 comments on commit fa4d44c

Please sign in to comment.