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

New assembler and moving all the prefixes into asm

+4 -1
Tupfile
···
: dna2rna/src/*.rs |> rustc --crate-name dna2rna dna2rna/src/main.rs $(RUSTC_FLAGS) |> out/bin/dna2rna
: rna2bmp/src/*.rs |> rustc --crate-name rna2bmp rna2bmp/src/main.rs $(RUSTC_FLAGS) |> out/bin/rna2bmp
+
: asm2dna/src/*.rs |> rustc --crate-name asm2dna asm2dna/src/main.rs $(RUSTC_FLAGS) |> out/bin/asm2dna
+
+
: foreach asm/*.asm | out/bin/asm2dna |> ./out/bin/asm2dna %f out/dna/%B.dna |> out/dna/%B.dna
-
: foreach prefixes/*.dna | out/bin/dna2rna |> ./out/bin/dna2rna %f task/endo.dna out/rna/%B.rna |> out/rna/%B.rna
+
: foreach out/dna/*.dna | out/bin/dna2rna |> ./out/bin/dna2rna %f task/endo.dna out/rna/%B.rna |> out/rna/%B.rna
: foreach out/rna/*.rna | out/bin/rna2bmp |> ./out/bin/rna2bmp %f out/bmp/%B.bmp |> out/bmp/%B.bmp
+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/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]
+189
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 {
+
// TODO: advance_by?
+
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(&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('"') => "0",
+
Some(',') => &self.read_to("\",").ok_or("Reference failed to terminate")?,
+
_ => 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));
+
}
+
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");
+
}
+
}
+
}
+
}
+
}
+
+
// TODO: Tests
+21
asm2dna/src/main.rs
···
+
mod assembler;
+
+
use std::env;
+
use std::fs;
+
use std::io;
+
+
fn main() -> io::Result<()> {
+
let args: Vec<String> = env::args().collect();
+
if args.len() < 3 {
+
println!("Usage: {} <asm file> <dna file>", args[0]);
+
return Ok(());
+
}
+
+
// TODO: Better error messages
+
let asm = fs::read_to_string(&args[1])?;
+
match assembler::assemble(&asm) {
+
Ok(dna) => fs::write(&args[2], dna)?,
+
Err(err) => println!("{}", err),
+
}
+
return Ok(());
+
}
prefixes/empty.dna asm/empty.asm
-1
prefixes/repair_guide.dna
···
-
IIPIFFCPICFPPICIICCCIICIPPPCFIIC
-1
prefixes/startup.dna
···
-
IIPIFFCPICFPPICIICCIICIPPPFIIC
-1
prefixes/sun.dna
···
-
IIPIFFCPICPCIICICIICIPPPPIIC
-1
prefixes/task.dna
···
-
IIPIFFCPICICIICPIICIPPPICIIC