ICFP 2007 Contest: https://web.archive.org/web/20090301164728/https://save-endo.cs.uu.nl/

Compare changes

Choose any two refs to compare.

+2 -2
.gitignore
···
task/endo.dna
-
-
dna2rna/target/
+
.tup/
+
/out/
+64
Tupfile.lua
···
+
function define_rust_rule(name, type, deps)
+
local inputs = {}
+
for _, input in ipairs(tup.glob(name .. "/src/*.rs")) do
+
table.insert(inputs, input)
+
end
+
+
local input_rs
+
local output
+
if type == "bin" then
+
input_rs = name .. "/src/main.rs"
+
output = "out/bin/" .. name
+
else
+
input_rs = name .. "/src/lib.rs"
+
output = "out/lib/lib" .. name .. ".rlib"
+
end
+
+
local externs = {}
+
for _, dep in ipairs(deps) do
+
local rlib = "out/lib/lib" .. dep .. ".rlib"
+
table.insert(inputs, rlib)
+
table.insert(externs, "--extern " .. dep .. "=" .. rlib)
+
end
+
+
local flags = "--edition=2024 -C opt-level=3 -C embed-bitcode=no -C strip=debuginfo --emit=link"
+
local command = "rustc --crate-name " .. name .. " --crate-type=" .. type .. " " .. input_rs .. " -o %o " .. table.concat(externs, " ") .. " " .. flags
+
tup.definerule{inputs = inputs, command = command, outputs = {output}}
+
end
+
+
define_rust_rule("dna_parsing", "lib", {})
+
define_rust_rule("dna2rna", "bin", {"dna_parsing"})
+
define_rust_rule("disassembler", "bin", {"dna_parsing"})
+
define_rust_rule("rna2bmp", "bin", {})
+
define_rust_rule("asm2dna", "bin", {})
+
+
local asm = tup.glob("asm/*.asm")
+
asm.extra_inputs = {"out/bin/asm2dna"}
+
tup.foreach_rule(
+
asm,
+
"./out/bin/asm2dna %f %o",
+
{"out/dna/%B.dna"}
+
)
+
+
local dna = tup.glob("out/dna/*.dna")
+
dna.extra_inputs = {"out/bin/dna2rna"}
+
tup.foreach_rule(
+
dna,
+
"./out/bin/dna2rna %f task/endo.dna %o",
+
{"out/rna/%B.rna"}
+
)
+
+
local rna = tup.glob("out/rna/*.rna")
+
rna.extra_inputs = {"out/bin/rna2bmp"}
+
tup.foreach_rule(
+
rna,
+
"./out/bin/rna2bmp %f %o",
+
{"out/bmp/%B.bmp"}
+
)
+
+
local bmp = tup.glob("out/bmp/*.bmp")
+
tup.foreach_rule(
+
bmp,
+
"convert %f %o",
+
{"out/png/%B.png"}
+
)
asm/empty.asm

This is a binary file and will not be displayed.

+1
asm/gene_list_1.asm
···
+
(?{IFPCFFP})n{0,24} ; {0}n{42,24} ;
+2
asm/gene_list_10.asm
···
+
(?{IFPCFFP})n{0,24} ; {0}n{42,24} ;
+
(?{IFPICFPPCFFPP}!{0x503})n{0,24} ; {0}n{9,24} ;
+2
asm/gene_list_11.asm
···
+
(?{IFPCFFP})n{0,24} ; {0}n{42,24} ;
+
(?{IFPICFPPCFFPP}!{0x503})n{0,24} ; {0}n{10,24} ;
+2
asm/gene_list_12.asm
···
+
(?{IFPCFFP})n{0,24} ; {0}n{42,24} ;
+
(?{IFPICFPPCFFPP}!{0x503})n{0,24} ; {0}n{11,24} ;
+2
asm/gene_list_13.asm
···
+
(?{IFPCFFP})n{0,24} ; {0}n{42,24} ;
+
(?{IFPICFPPCFFPP}!{0x503})n{0,24} ; {0}n{12,24} ;
+2
asm/gene_list_14.asm
···
+
(?{IFPCFFP})n{0,24} ; {0}n{42,24} ;
+
(?{IFPICFPPCFFPP}!{0x503})n{0,24} ; {0}n{13,24} ;
+2
asm/gene_list_2.asm
···
+
(?{IFPCFFP})n{0,24} ; {0}n{42,24} ;
+
(?{IFPICFPPCFFPP}!{0x503})n{0,24} ; {0}n{1,24} ;
+2
asm/gene_list_3.asm
···
+
(?{IFPCFFP})n{0,24} ; {0}n{42,24} ;
+
(?{IFPICFPPCFFPP}!{0x503})n{0,24} ; {0}n{2,24} ;
+2
asm/gene_list_4.asm
···
+
(?{IFPCFFP})n{0,24} ; {0}n{42,24} ;
+
(?{IFPICFPPCFFPP}!{0x503})n{0,24} ; {0}n{3,24} ;
+2
asm/gene_list_5.asm
···
+
(?{IFPCFFP})n{0,24} ; {0}n{42,24} ;
+
(?{IFPICFPPCFFPP}!{0x503})n{0,24} ; {0}n{4,24} ;
+2
asm/gene_list_6.asm
···
+
(?{IFPCFFP})n{0,24} ; {0}n{42,24} ;
+
(?{IFPICFPPCFFPP}!{0x503})n{0,24} ; {0}n{5,24} ;
+2
asm/gene_list_7.asm
···
+
(?{IFPCFFP})n{0,24} ; {0}n{42,24} ;
+
(?{IFPICFPPCFFPP}!{0x503})n{0,24} ; {0}n{6,24} ;
+2
asm/gene_list_8.asm
···
+
(?{IFPCFFP})n{0,24} ; {0}n{42,24} ;
+
(?{IFPICFPPCFFPP}!{0x503})n{0,24} ; {0}n{7,24} ;
+2
asm/gene_list_9.asm
···
+
(?{IFPCFFP})n{0,24} ; {0}n{42,24} ;
+
(?{IFPICFPPCFFPP}!{0x503})n{0,24} ; {0}n{8,24} ;
+1
asm/repair_guide.asm
···
+
(?{IFPCFFP})II ; {0}IC ;
+1
asm/repair_guide_catalog.asm
···
+
(?{IFPCFFP})n{0,24} ; {0}n{1337,24} ;
+1
asm/repair_guide_charset.asm
···
+
(?{IFPCFFP})n{0,24} ; {0}n{10646,24} ;
+1
asm/repair_guide_field_repair.asm
···
+
(?{IFPCFFP})n{0,24} ; {0}n{85,24} ;
+1
asm/repair_guide_genome_structure.asm
···
+
(?{IFPCFFP})n{0,24} ; {0}n{1729,24} ;
+1
asm/repair_guide_l_systems.asm
···
+
(?{IFPCFFP})n{0,24} ; {0}n{5,24} ;
+1
asm/repair_guide_more_genome_structure.asm
···
+
(?{IFPCFFP})n{0,24} ; {0}n{8,24} ;
+1
asm/repair_guide_rna_compression.asm
···
+
(?{IFPCFFP})n{0,24} ; {0}n{123456,24} ;
+1
asm/repair_guide_security.asm
···
+
(?{IFPCFFP})n{0,24} ; {0}n{4405829,24} ;
+1
asm/repair_guide_wanted.asm
···
+
(?{IFPCFFP})n{0,24} ; {0}n{112,24} ;
+1
asm/repair_guide_weird_rna.asm
···
+
(?{IFPCFFP})n{0,24} ; {0}n{2181889,24} ;
+1
asm/startup.asm
···
+
(?{IFPCFFP})I ; {0}C ;
+1
asm/sun.asm
···
+
(?{IFPFI})P ; {0}F ;
+1
asm/task.asm
···
+
(?{IFPP})F ; {0}P ;
+1
asm2dna/.dir-locals.el
···
+
((nil . ((default-directory . "~/repos/endo/asm2dna/"))))
+7
asm2dna/Cargo.lock
···
+
# This file is automatically @generated by Cargo.
+
# It is not intended for manual editing.
+
version = 4
+
+
[[package]]
+
name = "asm2dna"
+
version = "0.1.0"
+6
asm2dna/Cargo.toml
···
+
[package]
+
name = "asm2dna"
+
version = "0.1.0"
+
edition = "2024"
+
+
[dependencies]
+251
asm2dna/src/assembler.rs
···
+
use std::iter::Peekable;
+
use std::str::Chars;
+
+
pub type Error = &'static str;
+
+
pub fn assemble(asm: &str) -> Result<Vec<u8>, Error> {
+
Assembler::new(asm).assemble()
+
}
+
+
struct Assembler<'a> {
+
iter: Peekable<Chars<'a>>,
+
output: Vec<u8>,
+
}
+
+
enum IntPadding {
+
NoPadding,
+
PaddedTo(usize),
+
}
+
+
impl<'a> Assembler<'a> {
+
fn new(buf: &'a str) -> Assembler<'a> {
+
Assembler {
+
iter: buf.chars().peekable(),
+
output: Vec::new(),
+
}
+
}
+
+
fn expect(&mut self, c: char) -> Option<()> {
+
match self.iter.next() {
+
Some(d) => {
+
if c == d {
+
Some(())
+
} else {
+
None
+
}
+
}
+
None => None,
+
}
+
}
+
+
fn emit(&mut self, s: &str) {
+
self.output.extend_from_slice(s.as_bytes());
+
}
+
+
fn read_to(&mut self, any_of: &str) -> Option<String> {
+
let mut s = String::new();
+
loop {
+
if let Some(c) = self.iter.peek()
+
&& any_of.contains(*c)
+
{
+
return Some(s);
+
}
+
+
match self.iter.next() {
+
Some(c) => s.push(c),
+
None => return None,
+
}
+
}
+
}
+
+
fn parse_int(s: &str) -> Option<usize> {
+
let is_hex = s.starts_with("0x");
+
let base = if is_hex { 16 } else { 10 };
+
let mut ret = 0;
+
let mut iter = s.chars();
+
if is_hex {
+
iter.nth(1);
+
}
+
+
for c in iter {
+
ret *= base;
+
ret += c.to_digit(base)?;
+
}
+
+
return Some(ret as usize);
+
}
+
+
fn encode_int(mut n: usize, p: IntPadding) -> String {
+
let mut s = String::new();
+
while n > 0 {
+
if n % 2 == 1 {
+
s += "C";
+
} else {
+
s += "I";
+
}
+
+
n /= 2;
+
}
+
+
if let IntPadding::PaddedTo(p) = p {
+
for _ in s.len()..(p - 1) {
+
s += "I";
+
}
+
}
+
+
s += "P";
+
return s;
+
}
+
+
fn emit_consts(&mut self, s: &str) {
+
for c in s.chars() {
+
match c {
+
'I' => self.emit("C"),
+
'C' => self.emit("F"),
+
'F' => self.emit("P"),
+
'P' => self.emit("IC"),
+
_ => {}
+
}
+
}
+
}
+
+
fn assemble(mut self) -> Result<Vec<u8>, Error> {
+
loop {
+
match self.iter.next() {
+
Some('I') => self.emit("C"),
+
Some('C') => self.emit("F"),
+
Some('F') => self.emit("P"),
+
Some('P') => self.emit("IC"),
+
Some('!') => {
+
self.expect('{').ok_or("Skip followed by non-{")?;
+
let n = self.read_to("}").ok_or("Skip failed to terminate")?;
+
let n = Self::parse_int(&n).ok_or("Invalid int inside skip")?;
+
self.emit("IP");
+
self.emit(&Self::encode_int(n, IntPadding::NoPadding));
+
self.expect('}').ok_or("Skip failed to terminate")?;
+
}
+
Some('?') => {
+
self.expect('{').ok_or("Search followed by non-?")?;
+
let s = self.read_to("}").ok_or("Search failed to terminate")?;
+
self.emit("IFF");
+
self.emit_consts(&s);
+
self.expect('}').ok_or("Search failed to terminate")?;
+
}
+
Some('(') => self.emit("IIP"),
+
Some(')') => self.emit("IIC"),
+
Some(';') => self.emit("IIC"),
+
Some('{') => {
+
let n = self.read_to("},").ok_or("Reference failed terminate")?;
+
let n = Self::parse_int(&n).ok_or("Reference group index invalid")?;
+
let l = match self.iter.next() {
+
Some('}') => String::from("0"),
+
Some(',') => {
+
let l = self.read_to("}").ok_or("Reference failed to terminate")?;
+
self.expect('}');
+
l
+
}
+
_ => return Err("Reference failed to terminate"),
+
};
+
let l = Self::parse_int(&l).ok_or("Reference level invalid")?;
+
self.emit("IP");
+
self.emit(&Self::encode_int(l, IntPadding::NoPadding));
+
self.emit(&Self::encode_int(n, IntPadding::NoPadding));
+
}
+
Some('|') => {
+
let n = self.read_to("|").ok_or("Length failed to terminate")?;
+
let n = Self::parse_int(&n).ok_or("Length group index invalid")?;
+
self.emit("IIP");
+
self.emit(&Self::encode_int(n, IntPadding::NoPadding));
+
self.expect('|').ok_or("Length failed to terminate")?;
+
}
+
Some('n') => {
+
self.expect('{');
+
let n = self
+
.read_to("},")
+
.ok_or("Int constant failed to terminate")?;
+
let n = Self::parse_int(&n).ok_or("Int constant invalid")?;
+
match self.iter.next() {
+
Some('}') => {
+
self.emit(&Self::encode_int(n, IntPadding::NoPadding));
+
}
+
Some(',') => {
+
let pad = self
+
.read_to("}")
+
.ok_or("Int constant failed to terminate")?;
+
let pad =
+
Self::parse_int(&pad).ok_or("Int constant padding invalid")?;
+
self.emit_consts(&Self::encode_int(n, IntPadding::PaddedTo(pad)));
+
self.expect('}').ok_or("Int constant failed to terminate")?;
+
}
+
_ => return Err("Int constant failed to terminate"),
+
};
+
}
+
Some(' ') | Some('\n') => {}
+
None => return Ok(self.output),
+
Some(c) => {
+
println!("{}", c);
+
return Err("Invalid character");
+
}
+
}
+
}
+
}
+
}
+
+
#[cfg(test)]
+
mod tests {
+
use super::*;
+
+
#[test]
+
fn test_assemble_bases() {
+
let asm = assemble(&"ICFP").unwrap();
+
assert_eq!(b"CFPIC", &asm[..]);
+
}
+
+
#[test]
+
fn test_assemble_skip() {
+
let asm = assemble(&"!{1} !{0x10}").unwrap();
+
assert_eq!(b"IPCPIPIIIICP", &asm[..]);
+
}
+
+
#[test]
+
fn test_assemble_search() {
+
let asm = assemble(&"?{ICFP}").unwrap();
+
assert_eq!(b"IFFCFPIC", &asm[..]);
+
}
+
+
#[test]
+
fn test_assemble_groups() {
+
let asm = assemble(&"()()").unwrap();
+
assert_eq!(b"IIPIICIIPIIC", &asm[..]);
+
}
+
+
#[test]
+
fn test_assemble_pattern_template() {
+
let asm = assemble(&"!{2} ; II ;").unwrap();
+
assert_eq!(b"IPICPIICCCIIC", &asm[..]);
+
}
+
+
#[test]
+
fn test_assemble_pattern_quote() {
+
let asm = assemble(&"{2}").unwrap();
+
assert_eq!(b"IPPICP", &asm[..]);
+
+
let asm = assemble(&"{2,3}").unwrap();
+
assert_eq!(b"IPCCPICP", &asm[..]);
+
}
+
+
#[test]
+
fn test_assemble_pattern_length() {
+
let asm = assemble(&"|3|").unwrap();
+
assert_eq!(b"IIPCCP", &asm[..]);
+
}
+
+
#[test]
+
fn test_assemble_pattern_constant() {
+
let asm = assemble(&"n{4,5}").unwrap();
+
assert_eq!(b"CCFCIC", &asm[..]);
+
+
let asm = assemble(&"n{0x123,24}").unwrap();
+
assert_eq!(b"FFCCCFCCFCCCCCCCCCCCCCCIC", &asm[..]);
+
}
+
}
+23
asm2dna/src/main.rs
···
+
mod assembler;
+
+
use std::env;
+
use std::fs;
+
+
fn main() {
+
let args: Vec<String> = env::args().collect();
+
if args.len() < 3 {
+
println!("Usage: {} <asm file> <dna file>", args[0]);
+
return;
+
}
+
+
let Ok(asm) = fs::read_to_string(&args[1]) else {
+
println!("Failed to open asm file at {}", &args[1]);
+
return;
+
};
+
match assembler::assemble(&asm) {
+
Ok(dna) => {
+
let _ = fs::write(&args[2], dna);
+
}
+
Err(err) => println!("{}", err),
+
}
+
}
+1
disassembler/.dir-locals.el
···
+
((nil . ((default-directory . "~/repos/endo/disassembler/"))))
+14
disassembler/Cargo.lock
···
+
# This file is automatically @generated by Cargo.
+
# It is not intended for manual editing.
+
version = 4
+
+
[[package]]
+
name = "disassembler"
+
version = "0.1.0"
+
dependencies = [
+
"dna_parsing",
+
]
+
+
[[package]]
+
name = "dna_parsing"
+
version = "0.1.0"
+7
disassembler/Cargo.toml
···
+
[package]
+
name = "disassembler"
+
version = "0.1.0"
+
edition = "2024"
+
+
[dependencies]
+
dna_parsing = { path = "../dna_parsing" }
+69
disassembler/src/main.rs
···
+
use dna_parsing::base::Base;
+
use dna_parsing::parser::Parser;
+
use std::env;
+
use std::fs;
+
use std::io;
+
use std::io::Write;
+
+
fn main() {
+
let args: Vec<String> = env::args().collect();
+
if args.len() < 3 {
+
println!("Usage: {} <dna file> <offset>", args[0]);
+
return;
+
}
+
+
let Ok(dna) = fs::read_to_string(&args[1]) else {
+
println!("Failed to read dna file at {}", &args[1]);
+
return;
+
};
+
let Ok(offset): Result<usize, _> = args[2].parse() else {
+
println!("Failed to parse offset {}", &args[2]);
+
return;
+
};
+
+
let mut index = 0;
+
loop {
+
print!("> ");
+
let _ = io::stdout().flush();
+
let mut line = String::new();
+
if !io::stdin().read_line(&mut line).is_ok() {
+
println!("Failed to read response line");
+
return;
+
}
+
line = line.trim().to_string();
+
if !line.is_empty() {
+
let Ok(new_index): Result<usize, _> = line.parse() else {
+
println!("Failed to parse {}", line);
+
continue;
+
};
+
index = new_index;
+
}
+
+
let mut parser = Parser::new(
+
dna[index + offset..]
+
.chars()
+
.filter_map(|c| Base::from_char(c)),
+
);
+
let mut rna = Vec::new();
+
let Some(pattern) = parser.pattern(&mut rna) else {
+
println!("Failed to parse pattern");
+
continue;
+
};
+
let Some(template) = parser.template(&mut rna) else {
+
println!("Failed to parse template");
+
continue;
+
};
+
+
if !rna.is_empty() {
+
println!("RNA output:");
+
for r in rna.iter() {
+
let rna_str: String = r.iter().map(|b| b.to_char()).collect();
+
println!(" {}", rna_str);
+
}
+
println!();
+
}
+
println!("{} ; {} ;", pattern, template);
+
index += parser.index();
+
println!("Now at: {}", index);
+
}
+
}
+7
dna2rna/Cargo.lock
···
[[package]]
name = "dna2rna"
version = "0.1.0"
+
dependencies = [
+
"dna_parsing",
+
]
+
+
[[package]]
+
name = "dna_parsing"
+
version = "0.1.0"
+1
dna2rna/Cargo.toml
···
edition = "2024"
[dependencies]
+
dna_parsing = { path = "../dna_parsing" }
+81 -50
dna2rna/src/dna.rs
···
+
use dna_parsing::base::Base;
use std::collections::VecDeque;
use std::ops::Add;
+
use std::ops::AddAssign;
use std::ops::Deref;
use std::ops::Index;
use std::rc::Rc;
-
-
#[derive(Copy, Clone, PartialEq, Debug)]
-
pub enum Base {
-
I,
-
C,
-
F,
-
P,
-
}
#[derive(Clone, Debug)]
pub enum Dna {
···
len: usize,
depth: usize,
children: (DnaRef, DnaRef, DnaRef),
-
}
-
-
impl Base {
-
fn from_char(c: char) -> Option<Base> {
-
match c {
-
'I' => Some(Base::I),
-
'C' => Some(Base::C),
-
'F' => Some(Base::F),
-
'P' => Some(Base::P),
-
_ => None,
-
}
-
}
-
-
#[cfg(test)]
-
fn to_char(&self) -> char {
-
match self {
-
Base::I => 'I',
-
Base::C => 'C',
-
Base::F => 'F',
-
Base::P => 'P',
-
}
-
}
}
impl DnaRef {
···
let (a, b) = &node.children;
if n < a.len() {
let (x, y) = a.split(n);
-
(x, y + b.clone())
+
(x, y + b)
} else if n == a.len() {
(a.clone(), b.clone())
} else {
let (x, y) = b.split(n - a.len());
-
(a.clone() + x, y)
+
(a + x, y)
}
}
Dna::ThreeNode(node) => {
let (a, b, c) = &node.children;
if n < a.len() {
let (x, y) = a.split(n);
-
(x, y + b.clone() + c.clone())
+
(x, y + b + c)
} else if n == a.len() {
-
(a.clone(), b.clone() + c.clone())
+
(a.clone(), b + c)
} else if n - a.len() < b.len() {
let (x, y) = b.split(n - a.len());
-
(a.clone() + x, y + c.clone())
+
(a + x, y + c)
} else if n == a.len() + b.len() {
-
(a.clone() + b.clone(), c.clone())
+
(a + b, c.clone())
} else {
let (x, y) = c.split(n - a.len() - b.len());
-
(a.clone() + b.clone() + x, y)
+
(a + b + x, y)
}
}
}
···
Two(DnaRef, DnaRef),
}
-
fn add_helper(lhs: DnaRef, rhs: DnaRef) -> AddState {
+
fn add_helper(lhs: &DnaRef, rhs: &DnaRef) -> AddState {
if lhs.depth() == rhs.depth() {
-
AddState::Two(lhs, rhs)
+
AddState::Two(lhs.clone(), rhs.clone())
} else if lhs.depth() < rhs.depth() {
-
match &*rhs {
+
match &**rhs {
Dna::Empty => unreachable!(),
-
Dna::Leaf(_) => AddState::One(rhs),
+
Dna::Leaf(_) => AddState::One(rhs.clone()),
Dna::TwoNode(node) => {
let (a, b) = &node.children;
-
match add_helper(lhs, a.clone()) {
+
match add_helper(lhs, a) {
AddState::One(x) => AddState::One(DnaRef::from_two_children(x, b.clone())),
AddState::Two(x, y) => {
AddState::One(DnaRef::from_three_children(x, y, b.clone()))
···
}
Dna::ThreeNode(node) => {
let (a, b, c) = &node.children;
-
match add_helper(lhs, a.clone()) {
+
match add_helper(lhs, a) {
AddState::One(x) => {
AddState::One(DnaRef::from_three_children(x, b.clone(), c.clone()))
}
···
}
}
} else {
-
match &*lhs {
+
match &**lhs {
Dna::Empty => unreachable!(),
Dna::Leaf(_) => AddState::One(lhs.clone()),
Dna::TwoNode(node) => {
let (a, b) = &node.children;
-
match add_helper(b.clone(), rhs) {
+
match add_helper(b, rhs) {
AddState::One(x) => AddState::One(DnaRef::from_two_children(a.clone(), x)),
AddState::Two(x, y) => {
AddState::One(DnaRef::from_three_children(a.clone(), x, y))
···
}
Dna::ThreeNode(node) => {
let (a, b, c) = &node.children;
-
match add_helper(c.clone(), rhs) {
+
match add_helper(c, rhs) {
AddState::One(x) => {
AddState::One(DnaRef::from_three_children(a.clone(), b.clone(), x))
}
···
}
}
-
impl Add for DnaRef {
-
type Output = Self;
+
impl Add<DnaRef> for DnaRef {
+
type Output = DnaRef;
+
+
fn add(self, other: DnaRef) -> DnaRef {
+
match add_helper(&self, &other) {
+
AddState::One(a) => a,
+
AddState::Two(a, b) => DnaRef::from_two_children(a, b),
+
}
+
}
+
}
+
+
impl Add<&DnaRef> for DnaRef {
+
type Output = DnaRef;
+
+
fn add(self, other: &DnaRef) -> DnaRef {
+
match add_helper(&self, other) {
+
AddState::One(a) => a,
+
AddState::Two(a, b) => DnaRef::from_two_children(a, b),
+
}
+
}
+
}
+
+
impl Add<DnaRef> for &DnaRef {
+
type Output = DnaRef;
+
+
fn add(self, other: DnaRef) -> DnaRef {
+
match add_helper(self, &other) {
+
AddState::One(a) => a,
+
AddState::Two(a, b) => DnaRef::from_two_children(a, b),
+
}
+
}
+
}
+
+
impl Add<&DnaRef> for &DnaRef {
+
type Output = DnaRef;
-
fn add(self, other: Self) -> Self {
+
fn add(self, other: &DnaRef) -> DnaRef {
match add_helper(self, other) {
AddState::One(a) => a,
-
AddState::Two(a, b) => Self::from_two_children(a, b),
+
AddState::Two(a, b) => DnaRef::from_two_children(a, b),
+
}
+
}
+
}
+
+
impl AddAssign<DnaRef> for DnaRef {
+
fn add_assign(&mut self, other: DnaRef) {
+
match add_helper(self, &other) {
+
AddState::One(a) => {
+
*self = a;
+
}
+
AddState::Two(a, b) => {
+
*self = Self::from_two_children(a, b);
+
}
+
}
+
}
+
}
+
+
impl AddAssign<&DnaRef> for DnaRef {
+
fn add_assign(&mut self, other: &DnaRef) {
+
match add_helper(self, other) {
+
AddState::One(a) => {
+
*self = a;
+
}
+
AddState::Two(a, b) => {
+
*self = Self::from_two_children(a, b);
+
}
}
}
}
+45 -6
dna2rna/src/main.rs
···
mod dna;
-
mod parser;
-
mod pattern;
-
mod rna;
-
mod template;
+
mod match_replace;
+
+
use std::env;
+
use std::fs;
+
use std::fs::File;
+
use std::io::Write;
fn main() {
-
let dna = dna::DnaRef::from_string("ICFP");
-
println!("Back to string: {}", dna.to_string());
+
let args: Vec<String> = env::args().collect();
+
if args.len() < 4 {
+
println!("Usage: {} <prefix file> <dna file> <rna file>", args[0]);
+
return;
+
}
+
+
let Ok(prefix) = fs::read_to_string(&args[1]) else {
+
println!("Failed to read prefix file at {}", &args[1]);
+
return;
+
};
+
let Ok(dna) = fs::read_to_string(&args[2]) else {
+
println!("Failed to read dna file at {}", &args[2]);
+
return;
+
};
+
+
let mut dna = dna::DnaRef::from_string(&prefix) + dna::DnaRef::from_string(&dna);
+
let Ok(mut rna_file) = File::create(&args[3]) else {
+
println!("Failed to create rna output file at {}", &args[3]);
+
return;
+
};
+
let mut rna = Vec::new();
+
loop {
+
if let Some(new_dna) = match_replace::match_replace(dna, &mut rna) {
+
dna = new_dna;
+
} else {
+
break;
+
}
+
+
for r in rna.iter() {
+
let rna_str: String = r.iter().map(|b| b.to_char()).collect();
+
// Just tolerate a write error.
+
let _ = rna_file.write(rna_str.as_bytes());
+
let _ = rna_file.write(b"\n");
+
}
+
+
rna.clear();
+
}
+
+
return;
}
+26 -19
dna2rna/src/match_replace.rs
···
-
use crate::dna::Base;
use crate::dna::DnaRef;
-
use crate::parser::Parser;
-
use crate::pattern::Pattern;
-
use crate::pattern::PatternItem;
-
use crate::rna::Rna;
-
use crate::template::TemplateItem;
+
use dna_parsing::base::Base;
+
use dna_parsing::parser::Parser;
+
use dna_parsing::pattern::Pattern;
+
use dna_parsing::pattern::PatternItem;
+
use dna_parsing::rna::Rna;
+
use dna_parsing::template::TemplateItem;
struct MatchResult {
end: usize,
groups: Vec<(usize, usize)>,
}
-
fn do_match(dna: &DnaRef, start: usize, pattern: &Pattern) -> Option<MatchResult> {
+
fn do_match(dna: &DnaRef, start: usize, pattern: Pattern) -> Option<MatchResult> {
let mut index = start;
let mut env = Vec::new();
let mut opens = Vec::new();
-
for p in pattern.iter() {
+
for p in pattern.into_iter() {
match p {
PatternItem::Base(b) => {
-
if index >= dna.len() || dna[index] != *b {
+
if index >= dna.len() || dna[index] != b {
return None;
}
···
}
pub fn match_replace(dna: DnaRef, rna: &mut Vec<Rna>) -> Option<DnaRef> {
-
let mut parser = Parser::new(&dna);
+
let mut parser = Parser::new(dna.iter());
let pattern = parser.pattern(rna)?;
let template = parser.template(rna)?;
let index = parser.index();
-
if let Some(matched) = do_match(&dna, index, &pattern) {
+
if let Some(matched) = do_match(&dna, index, pattern) {
let mut replace = DnaRef::new();
for t in template.into_iter() {
match t {
TemplateItem::Base(b) => {
-
// TODO: Implement AddAssign
-
replace = replace + DnaRef::from_base(b);
+
replace += DnaRef::from_base(b);
}
TemplateItem::Ref(n, l) => {
-
let (start, end) = matched.groups[n];
-
let (_, rhs) = dna.split(start);
-
let (group, _) = rhs.split(end - start);
-
replace = replace + protect(group, l);
+
if n < matched.groups.len() {
+
let (start, end) = matched.groups[n];
+
let (_, rhs) = dna.split(start);
+
let (group, _) = rhs.split(end - start);
+
replace += protect(group, l);
+
} else {
+
replace += protect(DnaRef::new(), l);
+
}
}
TemplateItem::Len(n) => {
-
let (start, end) = matched.groups[n];
-
replace = replace + asnat(end - start);
+
if n < matched.groups.len() {
+
let (start, end) = matched.groups[n];
+
replace += asnat(end - start);
+
} else {
+
replace += asnat(0);
+
}
}
}
}
-292
dna2rna/src/parser.rs
···
-
use crate::dna::Base;
-
use crate::dna::DnaRef;
-
use crate::pattern::Pattern;
-
use crate::pattern::PatternItem;
-
use crate::rna::Rna;
-
use crate::template::Template;
-
use crate::template::TemplateItem;
-
-
pub struct Parser<'a> {
-
buf: &'a DnaRef,
-
index: usize,
-
}
-
-
impl<'a> Parser<'a> {
-
pub fn new(buf: &'a DnaRef) -> Parser<'a> {
-
Parser { buf: buf, index: 0 }
-
}
-
-
pub fn index(&self) -> usize {
-
self.index
-
}
-
-
pub fn next_is(&mut self, next: &[Base]) -> bool {
-
if self.index + next.len() > self.buf.len() {
-
return false;
-
}
-
-
for i in 0..next.len() {
-
if self.buf[self.index + i] != next[i] {
-
return false;
-
}
-
}
-
-
return true;
-
}
-
-
pub fn nat(&mut self) -> Option<usize> {
-
let mut ret = 0;
-
let mut bit = 1;
-
loop {
-
if self.next_is(&[Base::P]) {
-
self.index += 1;
-
return Some(ret);
-
} else if self.next_is(&[Base::I]) || self.next_is(&[Base::F]) {
-
self.index += 1;
-
} else if self.next_is(&[Base::C]) {
-
ret += bit;
-
self.index += 1;
-
} else {
-
return None;
-
}
-
-
bit *= 2;
-
}
-
}
-
-
pub fn consts(&mut self) -> Vec<Base> {
-
let mut ret = Vec::new();
-
loop {
-
if self.next_is(&[Base::C]) {
-
self.index += 1;
-
ret.push(Base::I);
-
} else if self.next_is(&[Base::F]) {
-
self.index += 1;
-
ret.push(Base::C);
-
} else if self.next_is(&[Base::P]) {
-
self.index += 1;
-
ret.push(Base::F);
-
} else if self.next_is(&[Base::I, Base::C]) {
-
self.index += 2;
-
ret.push(Base::P);
-
} else {
-
return ret;
-
}
-
}
-
}
-
-
pub fn pattern(&mut self, rna: &mut Vec<Rna>) -> Option<Pattern> {
-
let mut ret = Vec::new();
-
let mut level = 0;
-
loop {
-
if self.next_is(&[Base::C]) {
-
self.index += 1;
-
ret.push(PatternItem::Base(Base::I));
-
} else if self.next_is(&[Base::F]) {
-
self.index += 1;
-
ret.push(PatternItem::Base(Base::C));
-
} else if self.next_is(&[Base::P]) {
-
self.index += 1;
-
ret.push(PatternItem::Base(Base::F));
-
} else if self.next_is(&[Base::I, Base::C]) {
-
self.index += 2;
-
ret.push(PatternItem::Base(Base::P));
-
} else if self.next_is(&[Base::I, Base::P]) {
-
self.index += 2;
-
let n = self.nat()?;
-
ret.push(PatternItem::Skip(n));
-
} else if self.next_is(&[Base::I, Base::F]) {
-
self.index += 3;
-
let s = self.consts();
-
ret.push(PatternItem::Search(s));
-
} else if self.next_is(&[Base::I, Base::I, Base::P]) {
-
self.index += 3;
-
level += 1;
-
ret.push(PatternItem::Open);
-
} else if self.next_is(&[Base::I, Base::I, Base::C])
-
|| self.next_is(&[Base::I, Base::I, Base::F])
-
{
-
self.index += 3;
-
if level == 0 {
-
return Some(ret);
-
}
-
level -= 1;
-
ret.push(PatternItem::Close);
-
} else if self.next_is(&[Base::I, Base::I, Base::I]) {
-
self.index += 3;
-
let mut new_rna = Vec::new();
-
for i in 0..7 {
-
new_rna.push(self.buf[self.index + i]);
-
}
-
self.index += 7;
-
rna.push(new_rna.try_into().unwrap());
-
} else {
-
return None;
-
}
-
}
-
}
-
-
pub fn template(&mut self, rna: &mut Vec<Rna>) -> Option<Template> {
-
let mut ret = Vec::new();
-
loop {
-
if self.next_is(&[Base::C]) {
-
self.index += 1;
-
ret.push(TemplateItem::Base(Base::I));
-
} else if self.next_is(&[Base::F]) {
-
self.index += 1;
-
ret.push(TemplateItem::Base(Base::C));
-
} else if self.next_is(&[Base::P]) {
-
self.index += 1;
-
ret.push(TemplateItem::Base(Base::F));
-
} else if self.next_is(&[Base::I, Base::C]) {
-
self.index += 2;
-
ret.push(TemplateItem::Base(Base::P));
-
} else if self.next_is(&[Base::I, Base::P]) || self.next_is(&[Base::I, Base::F]) {
-
self.index += 2;
-
let l = self.nat()?;
-
let n = self.nat()?;
-
ret.push(TemplateItem::Ref(n, l));
-
} else if self.next_is(&[Base::I, Base::I, Base::C])
-
|| self.next_is(&[Base::I, Base::I, Base::F])
-
{
-
self.index += 3;
-
return Some(ret);
-
} else if self.next_is(&[Base::I, Base::I, Base::P]) {
-
self.index += 3;
-
let n = self.nat()?;
-
ret.push(TemplateItem::Len(n));
-
} else if self.next_is(&[Base::I, Base::I, Base::I]) {
-
self.index += 3;
-
let mut new_rna = Vec::new();
-
for i in 0..7 {
-
new_rna.push(self.buf[self.index + i]);
-
}
-
self.index += 7;
-
rna.push(new_rna.try_into().unwrap());
-
} else {
-
return None;
-
}
-
}
-
}
-
}
-
-
#[cfg(test)]
-
mod tests {
-
use super::*;
-
-
#[test]
-
fn test_base() {
-
let dna = DnaRef::from_string("ICFP");
-
let mut parser = Parser::new(&dna);
-
assert!(parser.next_is(&[Base::I]));
-
assert!(parser.next_is(&[Base::I, Base::C]));
-
assert!(parser.next_is(&[Base::I, Base::C, Base::F]));
-
assert!(parser.next_is(&[Base::I, Base::C, Base::F, Base::P]));
-
}
-
-
#[test]
-
fn test_nat() {
-
let dna = DnaRef::from_string("CP");
-
let mut parser = Parser::new(&dna);
-
assert_eq!(parser.nat(), Some(1));
-
assert_eq!(parser.index, 2);
-
-
let dna = DnaRef::from_string("ICICP");
-
let mut parser = Parser::new(&dna);
-
assert_eq!(parser.nat(), Some(10));
-
assert_eq!(parser.index, 5);
-
-
let dna = DnaRef::from_string("III");
-
let mut parser = Parser::new(&dna);
-
assert_eq!(parser.nat(), None);
-
assert_eq!(parser.index, 3);
-
}
-
-
#[test]
-
fn test_consts() {
-
let dna = DnaRef::from_string("CFPICIIC");
-
let mut parser = Parser::new(&dna);
-
assert_eq!(parser.consts(), &[Base::I, Base::C, Base::F, Base::P]);
-
assert_eq!(parser.index, 5);
-
}
-
-
#[test]
-
fn test_pattern() {
-
let mut rna = Vec::new();
-
-
let dna = DnaRef::from_string("CIIC");
-
let mut parser = Parser::new(&dna);
-
assert_eq!(
-
parser.pattern(&mut rna),
-
Some(vec![PatternItem::Base(Base::I)])
-
);
-
assert_eq!(parser.index, 4);
-
-
let dna = DnaRef::from_string("IIPIPICPIICICIIF");
-
let mut parser = Parser::new(&dna);
-
assert_eq!(
-
parser.pattern(&mut rna),
-
Some(vec![
-
PatternItem::Open,
-
PatternItem::Skip(2),
-
PatternItem::Close,
-
PatternItem::Base(Base::P)
-
])
-
);
-
assert_eq!(parser.index, 16);
-
-
let dna = DnaRef::from_string("IIIPFCICFPIIC");
-
let mut parser = Parser::new(&dna);
-
assert_eq!(parser.pattern(&mut rna), Some(vec![]));
-
assert_eq!(parser.index, 13);
-
assert_eq!(
-
rna,
-
vec![[
-
Base::P,
-
Base::F,
-
Base::C,
-
Base::I,
-
Base::C,
-
Base::F,
-
Base::P
-
]]
-
);
-
}
-
-
#[test]
-
fn test_template() {
-
let mut rna = Vec::new();
-
-
let dna = DnaRef::from_string("CFPICIFCPICPIIPIICPIIC");
-
let mut parser = Parser::new(&dna);
-
assert_eq!(
-
parser.template(&mut rna),
-
Some(vec![
-
TemplateItem::Base(Base::I),
-
TemplateItem::Base(Base::C),
-
TemplateItem::Base(Base::F),
-
TemplateItem::Base(Base::P),
-
TemplateItem::Ref(2, 1),
-
TemplateItem::Len(4)
-
])
-
);
-
assert_eq!(parser.index, 22);
-
-
let dna = DnaRef::from_string("IIIPFCICFPIIC");
-
let mut parser = Parser::new(&dna);
-
assert_eq!(parser.template(&mut rna), Some(vec![]));
-
assert_eq!(parser.index, 13);
-
assert_eq!(
-
rna,
-
vec![[
-
Base::P,
-
Base::F,
-
Base::C,
-
Base::I,
-
Base::C,
-
Base::F,
-
Base::P
-
]]
-
);
-
}
-
}
-12
dna2rna/src/pattern.rs
···
-
use crate::dna::Base;
-
-
#[derive(Debug, PartialEq)]
-
pub enum PatternItem {
-
Base(Base),
-
Skip(usize),
-
Search(Vec<Base>),
-
Open,
-
Close,
-
}
-
-
pub type Pattern = Vec<PatternItem>;
-3
dna2rna/src/rna.rs
···
-
use crate::dna::Base;
-
-
pub type Rna = [Base; 7];
-10
dna2rna/src/template.rs
···
-
use crate::dna::Base;
-
-
#[derive(Debug, PartialEq)]
-
pub enum TemplateItem {
-
Base(Base),
-
Ref(usize, usize),
-
Len(usize),
-
}
-
-
pub type Template = Vec<TemplateItem>;
+1
dna_parsing/.dir-locals.el
···
+
((nil . ((default-directory . "~/repos/endo/dna_parsing/"))))
+7
dna_parsing/Cargo.lock
···
+
# This file is automatically @generated by Cargo.
+
# It is not intended for manual editing.
+
version = 4
+
+
[[package]]
+
name = "dna_parsing"
+
version = "0.1.0"
+6
dna_parsing/Cargo.toml
···
+
[package]
+
name = "dna_parsing"
+
version = "0.1.0"
+
edition = "2024"
+
+
[dependencies]
+28
dna_parsing/src/base.rs
···
+
#[derive(Copy, Clone, PartialEq, Debug)]
+
pub enum Base {
+
I,
+
C,
+
F,
+
P,
+
}
+
+
impl Base {
+
pub fn from_char(c: char) -> Option<Base> {
+
match c {
+
'I' => Some(Base::I),
+
'C' => Some(Base::C),
+
'F' => Some(Base::F),
+
'P' => Some(Base::P),
+
_ => None,
+
}
+
}
+
+
pub fn to_char(&self) -> char {
+
match self {
+
Base::I => 'I',
+
Base::C => 'C',
+
Base::F => 'F',
+
Base::P => 'P',
+
}
+
}
+
}
+5
dna_parsing/src/lib.rs
···
+
pub mod base;
+
pub mod parser;
+
pub mod pattern;
+
pub mod rna;
+
pub mod template;
+315
dna_parsing/src/parser.rs
···
+
use crate::base::Base;
+
use crate::pattern::Pattern;
+
use crate::pattern::PatternItem;
+
use crate::rna::Rna;
+
use crate::template::Template;
+
use crate::template::TemplateItem;
+
use std::mem;
+
+
pub struct Parser<I>
+
where
+
I: Iterator<Item = Base>,
+
{
+
iter: I,
+
peeked: Vec<Base>,
+
index: usize,
+
}
+
+
impl<I> Parser<I>
+
where
+
I: Iterator<Item = Base>,
+
{
+
pub fn new(iter: I) -> Parser<I> {
+
Parser {
+
iter: iter,
+
peeked: Vec::new(),
+
index: 0,
+
}
+
}
+
+
pub fn index(&self) -> usize {
+
self.index
+
}
+
+
fn peek_to(&mut self, n: usize) {
+
while self.peeked.len() < n {
+
if let Some(b) = self.iter.next() {
+
self.peeked.push(b);
+
} else {
+
return;
+
}
+
}
+
}
+
+
pub fn next_is(&mut self, next: &[Base]) -> bool {
+
self.peek_to(next.len());
+
if self.peeked.len() < next.len() {
+
return false;
+
}
+
return &self.peeked[..next.len()] == next;
+
}
+
+
pub fn advance(&mut self, n: usize) {
+
self.peek_to(n);
+
self.index += n;
+
self.peeked.drain(..n);
+
}
+
+
pub fn nat(&mut self) -> Option<usize> {
+
let mut ret = 0;
+
let mut bit = 1;
+
loop {
+
if self.next_is(&[Base::P]) {
+
self.advance(1);
+
return Some(ret);
+
} else if self.next_is(&[Base::I]) || self.next_is(&[Base::F]) {
+
self.advance(1);
+
} else if self.next_is(&[Base::C]) {
+
ret += bit;
+
self.advance(1);
+
} else {
+
return None;
+
}
+
+
bit *= 2;
+
}
+
}
+
+
pub fn consts(&mut self) -> Vec<Base> {
+
let mut ret = Vec::new();
+
loop {
+
if self.next_is(&[Base::C]) {
+
self.advance(1);
+
ret.push(Base::I);
+
} else if self.next_is(&[Base::F]) {
+
self.advance(1);
+
ret.push(Base::C);
+
} else if self.next_is(&[Base::P]) {
+
self.advance(1);
+
ret.push(Base::F);
+
} else if self.next_is(&[Base::I, Base::C]) {
+
self.advance(2);
+
ret.push(Base::P);
+
} else {
+
return ret;
+
}
+
}
+
}
+
+
pub fn pattern(&mut self, rna: &mut Vec<Rna>) -> Option<Pattern> {
+
let mut ret = Vec::new();
+
let mut level = 0;
+
loop {
+
if self.next_is(&[Base::C]) {
+
self.advance(1);
+
ret.push(PatternItem::Base(Base::I));
+
} else if self.next_is(&[Base::F]) {
+
self.advance(1);
+
ret.push(PatternItem::Base(Base::C));
+
} else if self.next_is(&[Base::P]) {
+
self.advance(1);
+
ret.push(PatternItem::Base(Base::F));
+
} else if self.next_is(&[Base::I, Base::C]) {
+
self.advance(2);
+
ret.push(PatternItem::Base(Base::P));
+
} else if self.next_is(&[Base::I, Base::P]) {
+
self.advance(2);
+
let n = self.nat()?;
+
ret.push(PatternItem::Skip(n));
+
} else if self.next_is(&[Base::I, Base::F]) {
+
self.advance(3);
+
let s = self.consts();
+
ret.push(PatternItem::Search(s));
+
} else if self.next_is(&[Base::I, Base::I, Base::P]) {
+
self.advance(3);
+
level += 1;
+
ret.push(PatternItem::Open);
+
} else if self.next_is(&[Base::I, Base::I, Base::C])
+
|| self.next_is(&[Base::I, Base::I, Base::F])
+
{
+
self.advance(3);
+
if level == 0 {
+
return Some(Pattern::new(ret));
+
}
+
level -= 1;
+
ret.push(PatternItem::Close);
+
} else if self.next_is(&[Base::I, Base::I, Base::I]) {
+
self.advance(3);
+
self.peek_to(7);
+
let mut r = Vec::new();
+
mem::swap(&mut r, &mut self.peeked);
+
rna.push(r.try_into().unwrap());
+
self.index += 7;
+
} else {
+
return None;
+
}
+
}
+
}
+
+
pub fn template(&mut self, rna: &mut Vec<Rna>) -> Option<Template> {
+
let mut ret = Vec::new();
+
loop {
+
if self.next_is(&[Base::C]) {
+
self.advance(1);
+
ret.push(TemplateItem::Base(Base::I));
+
} else if self.next_is(&[Base::F]) {
+
self.advance(1);
+
ret.push(TemplateItem::Base(Base::C));
+
} else if self.next_is(&[Base::P]) {
+
self.advance(1);
+
ret.push(TemplateItem::Base(Base::F));
+
} else if self.next_is(&[Base::I, Base::C]) {
+
self.advance(2);
+
ret.push(TemplateItem::Base(Base::P));
+
} else if self.next_is(&[Base::I, Base::P]) || self.next_is(&[Base::I, Base::F]) {
+
self.advance(2);
+
let l = self.nat()?;
+
let n = self.nat()?;
+
ret.push(TemplateItem::Ref(n, l));
+
} else if self.next_is(&[Base::I, Base::I, Base::C])
+
|| self.next_is(&[Base::I, Base::I, Base::F])
+
{
+
self.advance(3);
+
return Some(Template::new(ret));
+
} else if self.next_is(&[Base::I, Base::I, Base::P]) {
+
self.advance(3);
+
let n = self.nat()?;
+
ret.push(TemplateItem::Len(n));
+
} else if self.next_is(&[Base::I, Base::I, Base::I]) {
+
self.advance(3);
+
self.peek_to(7);
+
let mut r = Vec::new();
+
mem::swap(&mut r, &mut self.peeked);
+
rna.push(r.try_into().unwrap());
+
self.index += 7;
+
} else {
+
return None;
+
}
+
}
+
}
+
}
+
+
#[cfg(test)]
+
mod tests {
+
use super::*;
+
+
fn dna_from_str(s: &str) -> Vec<Base> {
+
s.chars().filter_map(|c| Base::from_char(c)).collect()
+
}
+
+
#[test]
+
fn test_base() {
+
let dna = dna_from_str("ICFP").into_iter();
+
let mut parser = Parser::new(dna);
+
assert!(parser.next_is(&[Base::I]));
+
assert!(parser.next_is(&[Base::I, Base::C]));
+
assert!(parser.next_is(&[Base::I, Base::C, Base::F]));
+
assert!(parser.next_is(&[Base::I, Base::C, Base::F, Base::P]));
+
}
+
+
#[test]
+
fn test_nat() {
+
let dna = dna_from_str("CP").into_iter();
+
let mut parser = Parser::new(dna);
+
assert_eq!(parser.nat(), Some(1));
+
assert_eq!(parser.index, 2);
+
+
let dna = dna_from_str("ICICP").into_iter();
+
let mut parser = Parser::new(dna);
+
assert_eq!(parser.nat(), Some(10));
+
assert_eq!(parser.index, 5);
+
+
let dna = dna_from_str("III").into_iter();
+
let mut parser = Parser::new(dna);
+
assert_eq!(parser.nat(), None);
+
assert_eq!(parser.index, 3);
+
}
+
+
#[test]
+
fn test_consts() {
+
let dna = dna_from_str("CFPICIIC").into_iter();
+
let mut parser = Parser::new(dna);
+
assert_eq!(parser.consts(), &[Base::I, Base::C, Base::F, Base::P]);
+
assert_eq!(parser.index, 5);
+
}
+
+
#[test]
+
fn test_pattern() {
+
let mut rna = Vec::new();
+
+
let dna = dna_from_str("CIIC").into_iter();
+
let mut parser = Parser::new(dna);
+
assert_eq!(
+
parser.pattern(&mut rna),
+
Some(Pattern::new(vec![PatternItem::Base(Base::I)]))
+
);
+
assert_eq!(parser.index, 4);
+
+
let dna = dna_from_str("IIPIPICPIICICIIF").into_iter();
+
let mut parser = Parser::new(dna);
+
assert_eq!(
+
parser.pattern(&mut rna),
+
Some(Pattern::new(vec![
+
PatternItem::Open,
+
PatternItem::Skip(2),
+
PatternItem::Close,
+
PatternItem::Base(Base::P)
+
]))
+
);
+
assert_eq!(parser.index, 16);
+
+
let dna = dna_from_str("IIIPFCICFPIIC").into_iter();
+
let mut parser = Parser::new(dna);
+
assert_eq!(parser.pattern(&mut rna), Some(Pattern::new(vec![])));
+
assert_eq!(parser.index, 13);
+
assert_eq!(
+
rna,
+
vec![[
+
Base::P,
+
Base::F,
+
Base::C,
+
Base::I,
+
Base::C,
+
Base::F,
+
Base::P
+
]]
+
);
+
}
+
+
#[test]
+
fn test_template() {
+
let mut rna = Vec::new();
+
+
let dna = dna_from_str("CFPICIFCPICPIIPIICPIIC").into_iter();
+
let mut parser = Parser::new(dna);
+
assert_eq!(
+
parser.template(&mut rna),
+
Some(Template::new(vec![
+
TemplateItem::Base(Base::I),
+
TemplateItem::Base(Base::C),
+
TemplateItem::Base(Base::F),
+
TemplateItem::Base(Base::P),
+
TemplateItem::Ref(2, 1),
+
TemplateItem::Len(4)
+
]))
+
);
+
assert_eq!(parser.index, 22);
+
+
let dna = dna_from_str("IIIPFCICFPIIC").into_iter();
+
let mut parser = Parser::new(dna);
+
assert_eq!(parser.template(&mut rna), Some(Template::new(vec![])));
+
assert_eq!(parser.index, 13);
+
assert_eq!(
+
rna,
+
vec![[
+
Base::P,
+
Base::F,
+
Base::C,
+
Base::I,
+
Base::C,
+
Base::F,
+
Base::P
+
]]
+
);
+
}
+
}
+114
dna_parsing/src/pattern.rs
···
+
use crate::base::Base;
+
use std::fmt;
+
use std::slice;
+
use std::vec;
+
+
#[derive(Debug, PartialEq)]
+
pub enum PatternItem {
+
Base(Base),
+
Skip(usize),
+
Search(Vec<Base>),
+
Open,
+
Close,
+
}
+
+
impl fmt::Display for PatternItem {
+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+
match &self {
+
PatternItem::Base(b) => write!(f, "{}", b.to_char()),
+
PatternItem::Skip(n) => write!(f, "!{{{}}}", n),
+
PatternItem::Search(s) => {
+
write!(f, "?{{")?;
+
for b in s {
+
write!(f, "{}", b.to_char())?;
+
}
+
write!(f, "}}")?;
+
Ok(())
+
}
+
PatternItem::Open => write!(f, "("),
+
PatternItem::Close => write!(f, ")"),
+
}
+
}
+
}
+
+
#[derive(PartialEq, Debug)]
+
pub struct Pattern {
+
pattern: Vec<PatternItem>,
+
}
+
+
impl Pattern {
+
pub fn new(pattern: Vec<PatternItem>) -> Pattern {
+
Pattern { pattern: pattern }
+
}
+
+
pub fn iter<'a>(&'a self) -> slice::Iter<'a, PatternItem> {
+
self.pattern.iter()
+
}
+
+
pub fn into_iter<'a>(self) -> vec::IntoIter<PatternItem> {
+
self.pattern.into_iter()
+
}
+
}
+
+
impl fmt::Display for Pattern {
+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+
for p in self.iter() {
+
write!(f, "{}", p)?;
+
}
+
Ok(())
+
}
+
}
+
+
#[cfg(test)]
+
mod tests {
+
use super::*;
+
+
#[test]
+
fn test_display_base() {
+
assert_eq!(
+
Pattern::new(vec![
+
PatternItem::Base(Base::I),
+
PatternItem::Base(Base::C),
+
PatternItem::Base(Base::F),
+
PatternItem::Base(Base::P)
+
])
+
.to_string(),
+
"ICFP"
+
);
+
}
+
+
#[test]
+
fn test_display_skip() {
+
assert_eq!(Pattern::new(vec![PatternItem::Skip(5)]).to_string(), "!{5}");
+
}
+
+
#[test]
+
fn test_display_search() {
+
assert_eq!(
+
Pattern::new(vec![PatternItem::Search(vec![
+
Base::I,
+
Base::C,
+
Base::F,
+
Base::P
+
])])
+
.to_string(),
+
"?{ICFP}"
+
);
+
}
+
+
#[test]
+
fn test_display_group() {
+
assert_eq!(
+
Pattern::new(vec![
+
PatternItem::Open,
+
PatternItem::Base(Base::I),
+
PatternItem::Base(Base::C),
+
PatternItem::Base(Base::F),
+
PatternItem::Base(Base::P),
+
PatternItem::Close,
+
])
+
.to_string(),
+
"(ICFP)"
+
)
+
}
+
}
+3
dna_parsing/src/rna.rs
···
+
use crate::base::Base;
+
+
pub type Rna = [Base; 7];
+89
dna_parsing/src/template.rs
···
+
use crate::base::Base;
+
use std::fmt;
+
use std::slice;
+
use std::vec;
+
+
#[derive(Debug, PartialEq)]
+
pub enum TemplateItem {
+
Base(Base),
+
Ref(usize, usize),
+
Len(usize),
+
}
+
+
impl fmt::Display for TemplateItem {
+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+
match &self {
+
TemplateItem::Base(b) => write!(f, "{}", b.to_char()),
+
TemplateItem::Ref(n, l) => {
+
if *l == 0 {
+
write!(f, "{{{}}}", n)
+
} else {
+
write!(f, "{{{},{}}}", n, l)
+
}
+
}
+
TemplateItem::Len(n) => {
+
write!(f, "|{}|", n)
+
}
+
}
+
}
+
}
+
+
#[derive(Debug, PartialEq)]
+
pub struct Template {
+
template: Vec<TemplateItem>,
+
}
+
+
impl Template {
+
pub fn new(template: Vec<TemplateItem>) -> Template {
+
Template { template: template }
+
}
+
+
pub fn iter<'a>(&'a self) -> slice::Iter<'a, TemplateItem> {
+
self.template.iter()
+
}
+
+
pub fn into_iter<'a>(self) -> vec::IntoIter<TemplateItem> {
+
self.template.into_iter()
+
}
+
}
+
+
impl fmt::Display for Template {
+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+
for p in self.iter() {
+
write!(f, "{}", p)?;
+
}
+
Ok(())
+
}
+
}
+
+
#[cfg(test)]
+
mod tests {
+
use super::*;
+
+
#[test]
+
fn test_display_base() {
+
assert_eq!(
+
Template::new(vec![
+
TemplateItem::Base(Base::I),
+
TemplateItem::Base(Base::C),
+
TemplateItem::Base(Base::F),
+
TemplateItem::Base(Base::P)
+
])
+
.to_string(),
+
"ICFP"
+
);
+
}
+
+
#[test]
+
fn test_display_ref() {
+
assert_eq!(
+
Template::new(vec![TemplateItem::Ref(3, 2), TemplateItem::Ref(4, 0),]).to_string(),
+
"{3,2}{4}"
+
);
+
}
+
+
#[test]
+
fn test_display_lenf() {
+
assert_eq!(Template::new(vec![TemplateItem::Len(3)]).to_string(), "|3|");
+
}
+
}
+20
generate_crate_rule.py
···
+
import sys
+
+
# 'lib' or 'bin'
+
crate_type = sys.argv[1]
+
crate_name = sys.argv[2]
+
if crate_type == "bin":
+
input_rs = f"{crate_name}/src/main.rs"
+
outfile = f"out/bin/{crate_name}"
+
else:
+
input_rs = f"{crate_name}/src/lib.rs"
+
outfile = f"out/lib/lib{crate_name}.rlib"
+
+
dep_crates = sys.argv[3:]
+
dep_inputs = " ".join([f"out/lib/lib{dep}.rlib" for dep in dep_crates])
+
externs = " ".join([f"--extern {dep}=out/lib/lib{dep}.rlib" for dep in dep_crates])
+
+
flags = "--edition=2024 -C opt-level=3 -C embed-bitcode=no -C strip=debuginfo --emit=link"
+
+
inputs = f"{crate_name}/src/*.rs"
+
print(f": {crate_name}/src/*.rs {dep_inputs} |> rustc --crate-name {crate_name} --crate-type={crate_type} {input_rs} -o %o {externs} {flags} |> {outfile}")
+1
rna2bmp/.dir-locals.el
···
+
((nil . ((default-directory . "~/repos/endo/rna2bmp/"))))
+7
rna2bmp/Cargo.lock
···
+
# This file is automatically @generated by Cargo.
+
# It is not intended for manual editing.
+
version = 4
+
+
[[package]]
+
name = "rna2bmp"
+
version = "0.1.0"
+6
rna2bmp/Cargo.toml
···
+
[package]
+
name = "rna2bmp"
+
version = "0.1.0"
+
edition = "2024"
+
+
[dependencies]
+95
rna2bmp/src/bitmap.rs
···
+
pub const SIZE: usize = 600;
+
+
pub type Point = (usize, usize);
+
pub type Pixel = (u8, u8, u8, u8);
+
// A bitmap is large enough that it will stack overflow. The Box isn't
+
// strictly necessary if I can just be careful everywhere, but it's nice
+
// to have.
+
pub struct Bitmap {
+
data: Box<[[Pixel; SIZE]; SIZE]>,
+
}
+
+
impl Bitmap {
+
pub fn new() -> Bitmap {
+
Bitmap {
+
data: Box::new([[(0, 0, 0, 0); SIZE]; SIZE]),
+
}
+
}
+
+
pub fn pixel_mut(&mut self, p: Point) -> &mut Pixel {
+
let (x, y) = p;
+
&mut self.data[x][y]
+
}
+
+
pub fn pixel(&self, p: Point) -> &Pixel {
+
let (x, y) = p;
+
&self.data[x][y]
+
}
+
+
pub fn to_data(&self) -> Vec<u8> {
+
let mut writer = Writer::new();
+
let row_padding = (4 - (3 * SIZE) % 4) % 4;
+
let pix_size: u32 = (SIZE * (3 * SIZE + row_padding)) as u32;
+
writer.output_u16(0x4d42); // BM
+
writer.output_u32(0x36 + pix_size); // Size
+
writer.output_u16(0x0); // Reserved
+
writer.output_u16(0x0); // Reserved
+
writer.output_u32(0x36); // Offset of pixel array
+
writer.output_u32(0x28); // Length of DIB header
+
writer.output_u32(SIZE as u32); // Width
+
writer.output_u32(SIZE as u32); // Height
+
writer.output_u16(0x1); // Color planes
+
writer.output_u16(0x18); // Bits per pixel
+
writer.output_u32(0x0); // No pixel array compression
+
writer.output_u32(pix_size); // Pixel data size
+
writer.output_u32(0x0); // Horizontal resolution
+
writer.output_u32(0x0); // Vertical resolution
+
writer.output_u32(0x0); // Colors in the palette
+
writer.output_u32(0x0); // All colors are important
+
+
for y in (0..SIZE).rev() {
+
for x in 0..SIZE {
+
let (r, g, b, _) = self.data[x][y];
+
writer.output_u8(b);
+
writer.output_u8(g);
+
writer.output_u8(r);
+
}
+
+
for _ in 0..row_padding {
+
writer.output_u8(0);
+
}
+
}
+
+
return writer.output();
+
}
+
}
+
+
struct Writer {
+
output: Vec<u8>,
+
}
+
+
impl Writer {
+
fn new() -> Writer {
+
Writer { output: Vec::new() }
+
}
+
+
fn output_u8(&mut self, n: u8) {
+
self.output.push(n);
+
}
+
+
fn output_u16(&mut self, n: u16) {
+
self.output.push(n as u8);
+
self.output.push((n >> 8) as u8);
+
}
+
+
fn output_u32(&mut self, n: u32) {
+
self.output.push(n as u8);
+
self.output.push((n >> 8) as u8);
+
self.output.push((n >> 16) as u8);
+
self.output.push((n >> 24) as u8);
+
}
+
+
fn output(self) -> Vec<u8> {
+
self.output
+
}
+
}
+30
rna2bmp/src/main.rs
···
+
mod bitmap;
+
mod rna_processor;
+
+
use crate::rna_processor::RnaProcessor;
+
use std::env;
+
use std::fs;
+
+
fn main() {
+
let args: Vec<String> = env::args().collect();
+
if args.len() < 3 {
+
println!("Usage: {} <rna file> <bmp file>", args[0]);
+
return;
+
}
+
+
let contents = fs::read_to_string(&args[1]);
+
if let Ok(rna) = contents {
+
let mut processor = RnaProcessor::new();
+
processor.process_rna(rna.lines().collect());
+
if let Some(bmp) = processor.bitmap() {
+
if fs::write(&args[2], bmp.to_data()).is_err() {
+
println!("Could not write to {}", args[2]);
+
}
+
} else {
+
println!("No bitmap after RNA processing");
+
}
+
} else {
+
println!("Could not open {}", args[1]);
+
return;
+
}
+
}
+226
rna2bmp/src/rna_processor.rs
···
+
use crate::bitmap::Bitmap;
+
use crate::bitmap::Pixel;
+
use crate::bitmap::Point;
+
use crate::bitmap::SIZE;
+
use std::cmp::max;
+
+
struct Bucket {
+
total_r: usize,
+
total_g: usize,
+
total_b: usize,
+
total_a: usize,
+
count_rgb: usize,
+
count_a: usize,
+
}
+
+
pub struct RnaProcessor {
+
bucket: Bucket,
+
position: Point,
+
mark: Point,
+
dir: Point,
+
bitmaps: Vec<Bitmap>,
+
}
+
+
impl Bucket {
+
fn new() -> Bucket {
+
Bucket {
+
total_r: 0,
+
total_g: 0,
+
total_b: 0,
+
total_a: 0,
+
count_rgb: 0,
+
count_a: 0,
+
}
+
}
+
+
fn current_pixel(&self) -> Pixel {
+
let (r, g, b) = if self.count_rgb == 0 {
+
(0, 0, 0)
+
} else {
+
(
+
self.total_r / self.count_rgb,
+
self.total_g / self.count_rgb,
+
self.total_b / self.count_rgb,
+
)
+
};
+
let a = if self.count_a == 0 {
+
255
+
} else {
+
self.total_a / self.count_a
+
};
+
+
return (
+
(r * a / 255) as u8,
+
(g * a / 255) as u8,
+
(b * a / 255) as u8,
+
a as u8,
+
);
+
}
+
+
fn add_rgb(&mut self, r: usize, g: usize, b: usize) {
+
self.total_r += r;
+
self.total_g += g;
+
self.total_b += b;
+
self.count_rgb += 1;
+
}
+
+
fn add_a(&mut self, a: usize) {
+
self.total_a += a;
+
self.count_a += 1;
+
}
+
+
fn clear(&mut self) {
+
self.total_r = 0;
+
self.total_g = 0;
+
self.total_b = 0;
+
self.total_a = 0;
+
self.count_rgb = 0;
+
self.count_a = 0;
+
}
+
}
+
+
impl RnaProcessor {
+
pub fn new() -> RnaProcessor {
+
RnaProcessor {
+
bucket: Bucket::new(),
+
position: (0, 0),
+
mark: (0, 0),
+
dir: (1, 0),
+
bitmaps: vec![Bitmap::new()],
+
}
+
}
+
+
fn set_pixel(&mut self, p: Point) {
+
if let Some(b) = self.bitmaps.last_mut() {
+
*b.pixel_mut(p) = self.bucket.current_pixel();
+
}
+
}
+
+
pub fn process_rna(&mut self, rna: Vec<&str>) {
+
for r in rna {
+
if r == "PIPIIIC" {
+
self.bucket.add_rgb(0, 0, 0);
+
} else if r == "PIPIIIP" {
+
self.bucket.add_rgb(255, 0, 0);
+
} else if r == "PIPIICC" {
+
self.bucket.add_rgb(0, 255, 0);
+
} else if r == "PIPIICF" {
+
self.bucket.add_rgb(255, 255, 0);
+
} else if r == "PIPIICP" {
+
self.bucket.add_rgb(0, 0, 255);
+
} else if r == "PIPIIFC" {
+
self.bucket.add_rgb(255, 0, 255);
+
} else if r == "PIPIIFF" {
+
self.bucket.add_rgb(0, 255, 255);
+
} else if r == "PIPIIPC" {
+
self.bucket.add_rgb(255, 255, 255);
+
} else if r == "PIPIIPF" {
+
self.bucket.add_a(0);
+
} else if r == "PIPIIPP" {
+
self.bucket.add_a(255);
+
} else if r == "PIIPICP" {
+
self.bucket.clear();
+
} else if r == "PIIIIIP" {
+
let (x, y) = self.position;
+
let (dx, dy) = self.dir;
+
self.position = ((x + dx) % SIZE, (y + dy) % SIZE);
+
} else if r == "PCCCCCP" {
+
let (dx, dy) = self.dir;
+
self.dir = (dy, SIZE - dx);
+
} else if r == "PFFFFFP" {
+
let (dx, dy) = self.dir;
+
self.dir = (SIZE - dy, dx);
+
} else if r == "PCCIFFP" {
+
self.mark = self.position;
+
} else if r == "PFFICCP" {
+
let x0 = self.position.0 as isize;
+
let y0 = self.position.1 as isize;
+
let x1 = self.mark.0 as isize;
+
let y1 = self.mark.1 as isize;
+
let dx = x1 - x0;
+
let dy = y1 - y0;
+
let d = max(dx.abs(), dy.abs());
+
let c = if dx * dy <= 0 { 1 } else { 0 };
+
let mut x = x0 * d + (d - c) / 2;
+
let mut y = y0 * d + (d - c) / 2;
+
for _ in 0..d {
+
self.set_pixel(((x / d) as usize, (y / d) as usize));
+
x += dx;
+
y += dy;
+
}
+
self.set_pixel((x1 as usize, y1 as usize));
+
} else if r == "PIIPIIP" {
+
if let Some(b) = self.bitmaps.last_mut() {
+
let old = *b.pixel(self.position);
+
let new = self.bucket.current_pixel();
+
if new == old {
+
continue;
+
}
+
let mut to_fill = Vec::new();
+
to_fill.push(self.position);
+
while let Some(p) = to_fill.pop() {
+
if *b.pixel(p) == old {
+
*b.pixel_mut(p) = new;
+
+
let (x, y) = p;
+
if x > 0 {
+
to_fill.push((x - 1, y));
+
}
+
if x < SIZE - 1 {
+
to_fill.push((x + 1, y));
+
}
+
if y > 0 {
+
to_fill.push((x, y - 1));
+
}
+
if y < SIZE - 1 {
+
to_fill.push((x, y + 1));
+
}
+
}
+
}
+
}
+
} else if r == "PCCPFFP" {
+
if self.bitmaps.len() < 10 {
+
self.bitmaps.push(Bitmap::new());
+
}
+
} else if r == "PFFPCCP" {
+
if self.bitmaps.len() >= 2 {
+
let bmp0 = self.bitmaps.pop().unwrap();
+
let mut bmp1 = self.bitmaps.pop().unwrap();
+
for x in 0..SIZE {
+
for y in 0..SIZE {
+
let (r0, g0, b0, a0) = *bmp0.pixel((x, y));
+
let (r1, g1, b1, a1) = *bmp1.pixel((x, y));
+
let r = r0 + ((r1 as usize) * (255 - a0 as usize) / 255) as u8;
+
let g = g0 + ((g1 as usize) * (255 - a0 as usize) / 255) as u8;
+
let b = b0 + ((b1 as usize) * (255 - a0 as usize) / 255) as u8;
+
let a = a0 + ((a1 as usize) * (255 - a0 as usize) / 255) as u8;
+
*bmp1.pixel_mut((x, y)) = (r, g, b, a);
+
}
+
}
+
self.bitmaps.push(bmp1);
+
}
+
} else if r == "PFFICCF" {
+
if self.bitmaps.len() >= 2 {
+
let bmp0 = self.bitmaps.pop().unwrap();
+
let mut bmp1 = self.bitmaps.pop().unwrap();
+
for x in 0..SIZE {
+
for y in 0..SIZE {
+
let (_, _, _, a0) = *bmp0.pixel((x, y));
+
let (r1, g1, b1, a1) = *bmp1.pixel((x, y));
+
let r = ((r1 as usize) * (a0 as usize) / 255) as u8;
+
let g = ((g1 as usize) * (a0 as usize) / 255) as u8;
+
let b = ((b1 as usize) * (a0 as usize) / 255) as u8;
+
let a = ((a1 as usize) * (a0 as usize) / 255) as u8;
+
*bmp1.pixel_mut((x, y)) = (r, g, b, a);
+
}
+
}
+
self.bitmaps.push(bmp1);
+
}
+
}
+
}
+
}
+
+
pub fn bitmap(&self) -> Option<&Bitmap> {
+
self.bitmaps.last()
+
}
+
}