use nom::types::CompleteByteSlice as S;
use std::fs::File;
use std::io::Write;
use std::str::FromStr;
pub struct Config {
pub program: String,
pub output: String,
}
impl Config {
pub fn asm(&self) -> String {
let stdout = String::from("/dev/stdout");
if self.output == stdout {
stdout
} else {
format!("{}.s", self.output)
}
}
}
#[derive(Debug)]
pub struct Error {
message: String,
}
#[derive(Debug, PartialEq)]
pub enum AST {
Nil,
Number(i64),
Boolean(bool),
Char(u8),
Identifier(String),
List(Vec<AST>),
Let {
bindings: Vec<(String, AST)>,
body: Vec<AST>,
},
}
impl From<i64> for AST {
fn from(i: i64) -> Self {
AST::Number(i)
}
}
impl From<bool> for AST {
fn from(b: bool) -> Self {
AST::Boolean(b)
}
}
impl From<char> for AST {
fn from(c: char) -> Self {
AST::Char(c as u8)
}
}
impl From<&str> for AST {
fn from(i: &str) -> Self {
AST::Identifier(String::from(i))
}
}
pub mod parser {
use super::*;
use nom::types::CompleteByteSlice as S;
use nom::{self, *};
use std::str;
named!(identifier <S , String>, alt!(
value!(String::from("+"), tag!("+"))
| value!(String::from("-"), tag!("-"))
| value!(String::from("..."), tag!("..."))
| do_parse!(
i: initial >>
s: many0!(subsequent) >>
(format!("{}{}", i, s.into_iter().collect::<String>())))
));
named!(initial <S, char>, alt!(letter | symbol));
named!(subsequent <S, char>, alt!(initial | digit | one_of!(".+-")));
named!(symbol <S, char>, one_of!("!$%&*/:<=>?~_^"));
named!(letter <S, char>, one_of!("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"));
named!(digit <S, char>, one_of!("0123456789"));
named!(sign <S, i64>, alt!(
tag!("-") => { |_| -1 } |
tag!("+") => { |_| 1 }));
named!(boolean <S, bool>, alt!(
tag!("#t") => { |_| true } |
tag!("#f") => { |_| false }));
named!(ascii <S, u8>, alt!(
value!(9 as u8, tag!(r"#\tab")) |
value!(10 as u8, tag!(r"#\newline")) |
value!(13 as u8, tag!(r"#\return")) |
value!(32 as u8, tag!(r"#\space")) |
preceded!(tag!(r"#\"), map!(take!(1), { |e: S| e.0[0] }))
));
named!(number <S, i64>, do_parse!(
s: opt!(sign) >>
n: map!(take_while1!(is_digit),
{ |e: S| str::from_utf8(e.0)
.expect("Failed to parse string into UTF-8")
.parse::<i64>()
.expect(&format!("Failed to parse digits into i64: `{:?}`\n", e.0)[..])
}) >>
(s.unwrap_or(1) * n)
));
named!(datum <S, AST>, alt!(
value!(AST::Nil, tag!("()")) |
boolean => { |b| AST::Boolean(b) } |
ascii => { |c| AST::Char(c) } |
number => { |i| AST::Number(i) } |
identifier => { |i| AST::Identifier(i) }|
list
));
named!(list <S, AST>, do_parse!(
char!('(') >>
opt!(many0!(space)) >>
d: map!(separated_list!(space, datum),
{|ls: Vec<AST> |
if ls.is_empty() {
AST::Nil
} else {
AST::List(ls)
}}) >>
opt!(many0!(space)) >>
char!(')') >>
(d)));
named!(pub program <S, AST>, do_parse!(
e: alt!(let_syntax | datum) >>
opt!(many0!(space)) >> (e)));
named!(binding <S, (String, AST)>, do_parse!(
opt!(many0!(space)) >>
char!('(') >>
opt!(many0!(space)) >>
name: identifier >>
opt!(many0!(space)) >>
value: datum >>
opt!(many0!(space)) >>
char!(')') >>
opt!(many0!(space)) >>
((name, value))));
named!(let_syntax <S, AST>, do_parse!(
char!('(') >>
opt!(many0!(space)) >>
tag!("let") >>
opt!(many0!(space)) >>
char!('(') >>
b: many0!(binding) >>
char!(')') >>
opt!(many0!(space)) >>
e: many1!(program) >>
opt!(many0!(space)) >>
char!(')') >>
(AST::Let{bindings: b, body: e})));
#[cfg(test)]
mod tests {
use super::*;
const EMPTY: S<'static> = S(b"");
fn ok<T>(t: T) -> Result<(S<'static>, T), nom::Err<S<'static>, u32>> {
partial(EMPTY, t)
}
fn partial<T>(
unconsumed: S<'static>,
t: T,
) -> Result<(S<'_>, T), nom::Err<S<'_>, u32>> {
Ok((unconsumed, t))
}
fn fail<T>(unconsumed: S<'_>) -> Result<(S<'_>, T), nom::Err<S, u32>> {
Err(Err::Error(Context::Code(unconsumed, ErrorKind::Alt)))
}
#[test]
fn assorted() {
assert_eq!(ok(true), boolean(S(b"#t")));
assert_eq!(ok(false), boolean(S(b"#f")));
assert_eq!(fail(S(b"A")), boolean(S(b"A")));
assert_eq!(ok('?'), symbol(S(b"?")));
assert_eq!(ok(42), number(S(b"42")));
assert_eq!(ok(-42), number(S(b"-42")));
assert_eq!(ok('j' as u8), ascii(S(b"#\\j")));
assert_eq!(ok('^' as u8), ascii(S(b"#\\^")));
assert_eq!(fail(S(b"test")), ascii(S(b"test")));
}
#[test]
fn identifiers() {
assert_eq!(ok(String::from("x")), identifier(S(b"x")));
assert_eq!(ok(String::from("one")), identifier(S(b"one")));
assert_eq!(ok(String::from("!bang")), identifier(S(b"!bang")));
assert_eq!(ok(String::from("a->b")), identifier(S(b"a->b")));
assert_eq!(ok(String::from("+")), identifier(S(b"+")));
assert_eq!(ok(String::from("-")), identifier(S(b"-")));
assert_eq!(ok(String::from("i64")), identifier(S(b"i64")));
assert_eq!(
partial(S(b">"), String::from("-")),
identifier(S(b"->"))
);
assert_eq!(
partial(S(b" b"), String::from("a")),
identifier(S(b"a b"))
);
}
#[test]
fn data() {
assert_eq!(ok(AST::Nil), datum(S(b"()")));
assert_eq!(ok("one".into()), datum(S(b"one")));
assert_eq!(ok(42.into()), datum(S(b"42")))
}
#[test]
fn lists() {
assert_eq!(
ok(AST::List(vec!["+".into(), 1.into()])),
list(S(b"(+ 1)"))
);
assert_eq!(
ok(AST::List(vec![
1.into(),
2.into(),
3.into(),
"a".into(),
"b".into(),
"c".into()
])),
list(S(b"(1 2 3 a b c)"))
);
assert_eq!(
ok(AST::List(vec![
"inc".into(),
AST::List(vec!["inc".into(), 42.into()]),
],)),
list(S(b"(inc (inc 42))"))
);
assert_eq!(program(S(b"( + 1 )")), program(S(b"(+ 1)")));
}
#[test]
fn binary() {
assert_eq!(
ok(AST::List(vec!["+".into(), "x".into(), 1776.into()])),
list(S(b"(+ x 1776)"))
);
assert_eq!(
ok(AST::List(vec![
"+".into(),
"x".into(),
AST::List(vec!["*".into(), "a".into(), "b".into()],),
],)),
list(S(b"(+ x (* a b))"))
);
}
#[test]
fn top() {
assert_eq!(ok(true.into()), program(S(b"#t")));
assert_eq!(ok(false.into()), program(S(b"#f")));
assert_eq!(ok('?'.into()), program(S(b"#\\?")));
assert_eq!(ok(42.into()), program(S(b"42")));
assert_eq!(ok((-42).into()), program(S(b"-42")));
assert_eq!(ok('j'.into()), program(S(b"#\\j")));
assert_eq!(ok('^'.into()), program(S(b"#\\^")));
}
#[test]
fn let_binding() {
let prog = S(b"(let ((x 1) (y 2)) (+ x y))");
let exp = AST::Let {
bindings: vec![
("x".to_string(), AST::Number(1)),
("y".to_string(), AST::Number(2)),
],
body: vec![AST::List(vec![
AST::Identifier("+".to_string()),
AST::Identifier("x".to_string()),
AST::Identifier("y".to_string()),
])],
};
assert_eq!(ok(exp), program(prog));
}
}
}
pub mod x86 {
use std::fmt;
use std::ops::{Add, AddAssign};
#[derive(Clone)]
pub struct ASM(pub Vec<Ins>);
pub const WORDSIZE: i64 = 8;
#[derive(Debug, PartialEq, Clone)]
pub enum Register {
RAX,
RBX,
RCX,
RDX,
RSP,
RBP,
}
#[derive(Debug, Clone)]
pub enum Operand {
Const(i64),
Reg(Register),
Stack(i64),
}
#[derive(Debug, Clone)]
pub enum Ins {
Add { r: Register, v: Operand },
And { r: Register, v: Operand },
Call(String),
Cmp { r: Register, with: i64 },
Enter,
Je(String),
Jmp(String),
Label(String),
Leave,
Save { r: Register, si: i64 },
Load { r: Register, si: i64 },
Mov { from: Operand, to: Operand },
Mul { v: Operand },
Pop(Register),
Push(Register),
Ret,
Sar { r: Register, v: i64 },
Sal { r: Register, v: i64 },
Sub { r: Register, v: Operand },
Slice(String),
}
impl From<Ins> for ASM {
fn from(op: Ins) -> Self {
ASM { 0: vec![op] }
}
}
impl From<String> for ASM {
fn from(s: String) -> Self {
ASM { 0: vec![Ins::Slice(s)] }
}
}
impl From<&str> for ASM {
fn from(s: &str) -> Self {
ASM { 0: vec![Ins::Slice(s.to_string())] }
}
}
impl fmt::Display for Register {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", format!("{:?}", self).to_lowercase())
}
}
impl fmt::Display for Operand {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match &self {
Operand::Const(i) => write!(f, "{}", i),
Operand::Reg(r) => write!(f, "{}", r),
Operand::Stack(si) => writeln!(f, "{}", &stack(*si)),
}
}
}
impl fmt::Display for Ins {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Ins::Add { r, v } => writeln!(f, " add {}, {}", r, v),
Ins::And { r, v } => writeln!(f, " and {}, {}", r, v),
Ins::Call(l) => writeln!(f, " call {}", l),
Ins::Cmp { r, with } => writeln!(f, " cmp {}, {}", r, with),
Ins::Enter => {
let op = Ins::Push(Register::RBP)
+ Ins::Mov {
from: Operand::Reg(Register::RSP),
to: Operand::Reg(Register::RBP),
};
write!(f, "{}", op)
}
Ins::Je(l) => writeln!(f, " je {}", l),
Ins::Jmp(l) => writeln!(f, " jmp {}", l),
Ins::Label(l) => writeln!(f, "{}", label(l)),
Ins::Leave => {
let op = Ins::Pop(Register::RBP) + Ins::Ret;
writeln!(f, "{}", op)
}
Ins::Load { r, si } => {
writeln!(f, " mov {}, {}", r, &stack(*si))
}
Ins::Mov { from, to } => {
writeln!(f, " mov {}, {}", to, from)
}
Ins::Mul { v } => writeln!(f, " mul qword ptr {}", v),
Ins::Pop(r) => writeln!(f, " pop {}", r),
Ins::Push(r) => writeln!(f, " push {}", r),
Ins::Ret => writeln!(f, " ret"),
Ins::Save { r, si } => {
writeln!(f, " mov {}, {}", &stack(*si), r)
}
Ins::Sal { r, v } => writeln!(f, " sal {}, {}", r, v),
Ins::Sar { r, v } => writeln!(f, " sar {}, {}", r, v),
Ins::Sub { r, v } => writeln!(f, " sub {}, {}", r, v),
Ins::Slice(s) => write!(f, "{}", s),
}
}
}
impl fmt::Display for ASM {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let mut ctx = String::new();
for op in self.0.iter() {
ctx.push_str(&op.to_string());
}
writeln!(f, "{}", ctx)
}
}
impl AddAssign<Ins> for ASM {
fn add_assign(&mut self, op: Ins) {
self.0.push(op)
}
}
impl Add<Ins> for ASM {
type Output = Self;
fn add(self, op: Ins) -> Self {
let mut t = self.clone();
t.0.push(op);
t
}
}
impl Add<ASM> for ASM {
type Output = Self;
fn add(self, asm: ASM) -> Self {
let mut rhs = self.clone();
let mut lhs = asm.0.clone();
rhs.0.append(&mut lhs);
rhs
}
}
impl Add<Ins> for Ins {
type Output = ASM;
fn add(self, op: Ins) -> ASM {
ASM { 0: vec![self, op] }
}
}
#[cfg(target_os = "macos")]
fn label(label: &str) -> String {
format!("_{}:", label)
}
#[cfg(target_os = "linux")]
fn label(label: &str) -> String {
format!("{}:", label)
}
pub fn stack(si: i64) -> String {
match si {
index if index > 0 => format!("[rbp + {}]", index),
index if index < 0 => format!("[rbp - {}]", (-index)),
_ => panic!("Effective stack index cannot be 0"),
}
}
#[cfg(target_os = "macos")]
pub fn function_header(name: &str) -> ASM {
let mut ctx = String::new();
ctx.push_str(" .section __TEXT,__text\n");
ctx.push_str(" .intel_syntax noprefix\n");
ctx.push_str(&format!(" .globl _{}\n", &name));
ctx.push_str(&Ins::Label(String::from(name)).to_string());
ctx.into()
}
#[cfg(target_os = "linux")]
pub fn function_header(name: &str) -> ASM {
let mut ctx = String::new();
ctx.push_str(" .text\n");
ctx.push_str(" .intel_syntax noprefix\n");
ctx.push_str(&format!(" .globl {}\n", &name));
ctx.push_str(&format!(" .type {}, @function\n", &name));
ctx.push_str(&Ins::Label(String::from(name)).to_string());
ctx.into()
}
#[cfg(test)]
mod tests {
use crate::x86::{Ins::*, Operand::*, Register::*};
#[test]
fn mov() {
assert_eq!(
String::from(" mov rax, 16\n"),
Mov { from: Const(16), to: Reg(RAX) }.to_string()
);
}
}
}
mod state {
use super::x86::{ASM, WORDSIZE};
use std::collections::HashMap;
pub struct State {
pub si: i64,
pub asm: ASM,
env: Env,
}
impl Default for State {
fn default() -> Self {
State { si: -WORDSIZE, asm: ASM(vec![]), env: new() }
}
}
impl State {
pub fn enter(&mut self) {
self.env.enter();
}
pub fn leave(&mut self) {
let unwind = self.env.0.first().expect("unexpected empty env").len()
as i64
* WORDSIZE;
self.si += unwind;
self.env.leave()
}
pub fn get(&mut self, i: &str) -> Option<i64> {
self.env.get(i)
}
pub fn set(&mut self, i: &str, index: i64) {
self.env.set(i, index);
self.alloc();
}
fn alloc(&mut self) {
self.si -= WORDSIZE;
}
}
#[derive(Debug)]
struct Env(Vec<HashMap<String, i64>>);
fn new() -> Env {
Env(vec![HashMap::new()])
}
impl Env {
pub fn enter(&mut self) {
self.0.insert(0, HashMap::new());
}
pub fn leave(&mut self) {
self.0.remove(0);
}
pub fn set(&mut self, i: &str, index: i64) {
self.0
.first_mut()
.map(|binding| binding.insert(i.to_string(), index));
}
pub fn get(&mut self, i: &str) -> Option<i64> {
for bindings in self.0.iter() {
if let Some(t) = bindings.get(i) {
return Some(*t);
}
}
None
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn t() {
let mut e = new();
assert_eq!(e.0.len(), 1);
e.set("x", -8);
assert_eq!(e.get("x"), Some(-8));
e.set("x", -16);
assert_eq!(e.get("x"), Some(-16));
e.enter();
assert_eq!(e.0.len(), 2);
assert_eq!(e.get("x"), Some(-16));
e.set("y", -24);
e.set("x", -32);
assert_eq!(e.get("x"), Some(-32));
e.leave();
assert_eq!(e.0.len(), 1);
assert_eq!(e.get("y"), None);
assert_eq!(e.get("x"), Some(-16));
}
}
}
pub mod emit {
use super::{
immediate,
state::State,
x86::{Ins::*, Operand::*, Register::*, *},
*,
};
pub fn mask() -> Ins {
And { r: RAX, v: Const(immediate::MASK) }
}
pub fn cmp_bool() -> ASM {
(String::from(" sete al \n") +
" movzx rax, al \n" +
&format!(" sal al, {} \n", immediate::SHIFT) +
&format!(" or al, {} \n", immediate::BOOL))
.into()
}
pub fn binding(
s: &mut State,
bindings: &[(String, AST)],
body: &[AST],
) -> ASM {
let mut ctx = String::new();
s.enter();
for (name, expr) in bindings.iter() {
let x = eval(s, expr) + Save { r: RAX, si: s.si };
s.set(name, s.si);
ctx.push_str(&x.to_string());
}
for b in body.iter() {
let x = eval(s, &b);
ctx.push_str(&x.to_string());
}
s.leave();
ctx.into()
}
pub fn eval(s: &mut State, prog: &AST) -> ASM {
match prog {
AST::Identifier(i) => match s.get(i) {
Some(i) => Ins::Load { r: Register::RAX, si: i }.into(),
None => panic!("Undefined variable {}", i),
},
AST::Let { bindings, body } => binding(s, bindings, body),
AST::List(list) => match list.as_slice() {
[AST::Identifier(i), arg] => match &i[..] {
"inc" => primitives::inc(s, arg),
"dec" => primitives::dec(s, arg),
"null?" => primitives::nullp(s, arg),
"zero?" => primitives::zerop(s, arg),
"not" => primitives::not(s, arg),
"fixnum?" => primitives::fixnump(s, arg),
"boolean?" => primitives::booleanp(s, arg),
"char?" => primitives::charp(s, arg),
n => panic!("Unknown unary primitive: {}", n),
},
[AST::Identifier(name), x, y] => match &name[..] {
"+" => primitives::plus(s, x, y),
"-" => primitives::minus(s, x, y),
"*" => primitives::mul(s, x, y),
"/" => primitives::quotient(s, x, y),
"%" => primitives::remainder(s, x, y),
n => panic!("Unknown binary primitive: {}", n),
},
l => panic!("Unknown expression: {:?}", l),
},
_ => Mov {
to: Operand::Reg(RAX),
from: Operand::Const(immediate::to(&prog)),
}
.into(),
}
}
pub fn program(prog: &AST) -> String {
let mut s: State = Default::default();
let gen = x86::function_header("init")
+ Ins::Enter
+ eval(&mut s, prog)
+ Ins::Leave;
gen.to_string()
}
}
pub mod primitives {
use super::state::State;
use super::x86::{Ins::*, Register::*, *};
use super::*;
pub fn inc(s: &mut State, x: &AST) -> ASM {
emit::eval(s, x) + Add { r: RAX, v: Operand::Const(immediate::n(1)) }
}
pub fn dec(s: &mut State, x: &AST) -> ASM {
emit::eval(s, x) + Sub { r: RAX, v: Operand::Const(immediate::n(1)) }
}
pub fn fixnump(s: &mut State, expr: &AST) -> ASM {
emit::eval(s, expr)
+ emit::mask()
+ Cmp { r: RAX, with: immediate::NUM }
+ emit::cmp_bool()
}
pub fn booleanp(s: &mut State, expr: &AST) -> ASM {
emit::eval(s, expr)
+ emit::mask()
+ Cmp { r: RAX, with: immediate::BOOL }
+ emit::cmp_bool()
}
pub fn charp(s: &mut State, expr: &AST) -> ASM {
emit::eval(s, expr)
+ emit::mask()
+ Cmp { r: RAX, with: immediate::CHAR }
+ emit::cmp_bool()
}
pub fn nullp(s: &mut State, expr: &AST) -> ASM {
emit::eval(s, expr)
+ Cmp { r: RAX, with: immediate::NIL }
+ emit::cmp_bool()
}
pub fn zerop(s: &mut State, expr: &AST) -> ASM {
emit::eval(s, expr)
+ Cmp { r: RAX, with: immediate::NUM }
+ emit::cmp_bool()
}
pub fn not(s: &mut State, expr: &AST) -> ASM {
emit::eval(s, expr)
+ Cmp { r: RAX, with: immediate::FALSE }
+ emit::cmp_bool()
}
fn binop(s: &mut State, x: &AST, y: &AST) -> ASM {
emit::eval(s, x) + Save { r: RAX, si: s.si } + emit::eval(s, y)
}
pub fn plus(s: &mut State, x: &AST, y: &AST) -> ASM {
binop(s, &x, &y) + Add { r: RAX, v: Operand::Stack(s.si) }
}
pub fn minus(s: &mut State, x: &AST, y: &AST) -> ASM {
binop(s, &x, &y)
+ Sub { r: RAX, v: Operand::Stack(s.si) }
+ Load { r: RAX, si: s.si }
}
pub fn mul(s: &mut State, x: &AST, y: &AST) -> ASM {
binop(s, &x, &y)
+ Sar { r: RAX, v: immediate::SHIFT }
+ Mul { v: Operand::Stack(s.si) }
}
fn div(s: &mut State, x: &AST, y: &AST) -> ASM {
let mut ctx = String::new();
ctx.push_str(&(emit::eval(s, y).to_string()));
ctx.push_str(
&Ins::Sar { r: Register::RAX, v: immediate::SHIFT }.to_string(),
);
ctx.push_str(" mov rcx, rax \n");
ctx.push_str(&emit::eval(s, x).to_string());
ctx.push_str(
&Ins::Sar { r: Register::RAX, v: immediate::SHIFT }.to_string(),
);
ctx.push_str(" mov rdx, 0 \n");
ctx.push_str(" cqo \n");
ctx.push_str(" idiv rcx \n");
ctx.into()
}
pub fn quotient(s: &mut State, x: &AST, y: &AST) -> ASM {
div(s, x, y) + Sal { r: Register::RAX, v: immediate::SHIFT }
}
pub fn remainder(s: &mut State, x: &AST, y: &AST) -> ASM {
div(s, x, y)
+ Mov {
to: Operand::Reg(Register::RAX),
from: Operand::Reg(Register::RDX),
}
+ Sal { r: Register::RAX, v: immediate::SHIFT }
}
}
pub mod immediate {
use super::*;
pub const NUM: i64 = 0;
pub const BOOL: i64 = 1;
pub const CHAR: i64 = 2;
pub const NIL: i64 = 4;
pub const SHIFT: i64 = 3;
pub const MASK: i64 = 0b0000_0111;
pub const FALSE: i64 = (0 << SHIFT) | BOOL;
pub const TRUE: i64 = (1 << SHIFT) | BOOL;
pub fn to(prog: &AST) -> i64 {
match prog {
AST::Number(i) => (i << SHIFT) | NUM,
AST::Boolean(true) => TRUE,
AST::Boolean(false) => FALSE,
AST::Char(c) => {
(i64::from(*c) << SHIFT) | CHAR
}
AST::Nil => NIL,
AST::Identifier(i) => unimplemented!(
"immediate repr is undefined for identifier {}",
i
),
AST::List(..) => {
unimplemented!("immediate repr is undefined for lists")
}
AST::Let { .. } => {
unimplemented!("immediate repr is undefined for let binding")
}
}
}
pub fn n(i: i64) -> i64 {
(i << SHIFT) | NUM
}
#[cfg(test)]
mod tests {
use super::*;
pub fn from(val: i64) -> AST {
if (val & MASK) == NUM {
return AST::Number(val >> SHIFT);
} else if (val & MASK) == CHAR {
return AST::Char((val >> SHIFT) as u8);
} else if val == TRUE {
return true.into();
} else if val == FALSE {
return false.into();
} else if val == NIL {
return AST::Nil;
} else {
panic!("Oops");
}
}
#[test]
fn numbers() {
assert_eq!(to(&0.into()), 0);
assert_eq!(to(&1.into()), 8);
assert_eq!(from(0), 0.into());
assert_eq!(from(8), 1.into());
}
#[test]
fn chars() {
let expect = (65 << SHIFT) + CHAR;
assert_eq!(to(&('A').into()), expect);
assert_eq!(from(expect), 'A'.into());
}
}
}
impl FromStr for AST {
type Err = Error;
fn from_str(program: &str) -> Result<Self, Error> {
match parser::program(S(program.as_bytes())) {
Ok((_rest, ast)) => Ok(ast),
Err(e) => Err(Error { message: format!("{}", e) }),
}
}
}
pub fn compile(config: &mut Config) -> Result<(), Error> {
let prog: AST = config.program.parse::<AST>()?;
let mut handler = File::create(&config.asm())
.unwrap_or_else(|_| panic!("Failed to create {}", &config.asm()));
match handler.write_all(emit::program(&prog).as_bytes()) {
Ok(_) => Ok(()),
Err(e) => Err(Error {
message: format!("Failed to write generated code: {}", e),
}),
}
}