wip: compiler backend
This commit is contained in:
parent
8bd701c03c
commit
faac9c70c9
179
src/code.rs
Normal file
179
src/code.rs
Normal file
|
@ -0,0 +1,179 @@
|
|||
use std::{
|
||||
fmt::{Display, Formatter, Result as FmtResult},
|
||||
ops::{Deref, DerefMut},
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq, PartialOrd, Ord, Hash)]
|
||||
pub struct Instructions(pub Vec<u8>);
|
||||
|
||||
impl Display for Instructions {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
|
||||
let mut i = 0;
|
||||
|
||||
while i < self.0.len() {
|
||||
let opcode = OpCode::lookup(self.0[i]);
|
||||
let def = opcode.definition();
|
||||
let (operands, read) = read_operands(&def, Instructions(self.0[i + 1..].to_vec()));
|
||||
|
||||
f.write_fmt(format_args!("{:04} {}\n", i, def.name))?;
|
||||
}
|
||||
|
||||
f.write_str("")
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for Instructions {
|
||||
type Target = Vec<u8>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl DerefMut for Instructions {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Vec<u8>> for Instructions {
|
||||
fn from(value: Vec<u8>) -> Self {
|
||||
Self(value)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub enum OpCode {
|
||||
Constant = 1,
|
||||
}
|
||||
|
||||
impl OpCode {
|
||||
pub fn lookup(c: u8) -> Self {
|
||||
match c {
|
||||
1 => Self::Constant,
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for OpCode {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
|
||||
f.write_str(match self {
|
||||
OpCode::Constant => "OpConstant",
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
struct Definition {
|
||||
name: String,
|
||||
operand_widths: Vec<i32>,
|
||||
}
|
||||
|
||||
impl OpCode {
|
||||
fn definition(&self) -> Definition {
|
||||
match self {
|
||||
OpCode::Constant => Definition {
|
||||
name: "Constant".to_string(),
|
||||
operand_widths: vec![2],
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn make(op: OpCode, operands: Vec<i32>) -> Instructions {
|
||||
let definition = op.definition();
|
||||
|
||||
let mut instruction_len = 1;
|
||||
for op in definition.operand_widths.iter() {
|
||||
instruction_len += op;
|
||||
}
|
||||
|
||||
let mut instruction = Vec::with_capacity(instruction_len as usize);
|
||||
instruction.push(op as u8);
|
||||
|
||||
for (i, operand) in operands.into_iter().enumerate() {
|
||||
let width = definition.operand_widths[i];
|
||||
|
||||
match width {
|
||||
2 => {
|
||||
instruction.extend(&(operand as u16).to_be_bytes());
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
Instructions(instruction)
|
||||
}
|
||||
|
||||
fn read_operands(def: &Definition, ins: Instructions) -> (Vec<i32>, i32) {
|
||||
let mut operands = vec![];
|
||||
|
||||
let mut offset = 0i32;
|
||||
for (i, width) in def.operand_widths.iter().enumerate() {
|
||||
match width {
|
||||
2 => {
|
||||
operands[i] =
|
||||
u16::from_be_bytes([ins.0[offset as usize], ins.0[offset as usize + 1]]) as i32
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
offset += width;
|
||||
}
|
||||
|
||||
(operands, offset)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::code::{make, read_operands};
|
||||
|
||||
use super::{Instructions, OpCode};
|
||||
|
||||
#[test]
|
||||
fn make_test() {
|
||||
let tests = vec![(
|
||||
OpCode::Constant,
|
||||
vec![65534],
|
||||
Instructions(vec![OpCode::Constant as u8, 255, 254]),
|
||||
)];
|
||||
|
||||
for (op, operands, expected) in tests {
|
||||
let instruction = make(op, operands);
|
||||
|
||||
assert_eq!(instruction, expected);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn read_operands_test() {
|
||||
let tests = [(OpCode::Constant, vec![65535], 2)];
|
||||
|
||||
for test in tests {
|
||||
let instruction = make(test.0, test.1.clone());
|
||||
let definition = test.0.definition();
|
||||
|
||||
let (operands_read, n) =
|
||||
read_operands(&definition, Instructions(instruction[1..].to_vec()));
|
||||
assert_eq!(n, test.2);
|
||||
assert_eq!(operands_read, test.1);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn instructions_string() {
|
||||
let instructions: Instructions = vec![
|
||||
make(OpCode::Constant, vec![1]),
|
||||
make(OpCode::Constant, vec![2]),
|
||||
make(OpCode::Constant, vec![65535]),
|
||||
]
|
||||
.into_iter()
|
||||
.flat_map(|x| x.0)
|
||||
.collect::<Vec<u8>>()
|
||||
.into();
|
||||
let expected = "0000 OpConstant 1
|
||||
0003 OpConstant 2
|
||||
0006 OpConstant 65535";
|
||||
|
||||
assert_eq!(instructions.to_string(), expected);
|
||||
}
|
||||
}
|
81
src/compiler.rs
Normal file
81
src/compiler.rs
Normal file
|
@ -0,0 +1,81 @@
|
|||
use crate::{code::Instructions, evaluator::Object, parser::ast};
|
||||
|
||||
pub struct Compiler {
|
||||
instructions: Instructions,
|
||||
constants: Vec<Object>,
|
||||
}
|
||||
|
||||
impl Compiler {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
instructions: Instructions(vec![]),
|
||||
constants: vec![],
|
||||
}
|
||||
}
|
||||
|
||||
pub fn compile(&self, node: ast::Node) -> Result<(), String> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn bytecode(&self) -> ByteCode {
|
||||
ByteCode {
|
||||
instructions: self.instructions.clone(),
|
||||
constants: self.constants.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct ByteCode {
|
||||
instructions: Instructions,
|
||||
constants: Vec<Object>,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use crate::{
|
||||
code::{self, Instructions, OpCode},
|
||||
compiler::Compiler,
|
||||
evaluator::Object,
|
||||
lexer::Lexer,
|
||||
parser::{ast::Node, Parser},
|
||||
};
|
||||
|
||||
fn run_compiler_tests(
|
||||
(input, constants, instructions): (&'static str, Vec<Object>, Vec<Instructions>),
|
||||
) {
|
||||
let lexer = Lexer::new(input);
|
||||
let mut parser = Parser::new(lexer);
|
||||
let program = parser.parse_program();
|
||||
assert!(program.is_some());
|
||||
let program = program.unwrap();
|
||||
let compiler = Compiler::new();
|
||||
compiler.compile(Node::Program(program)).unwrap();
|
||||
let bytecode = compiler.bytecode();
|
||||
|
||||
assert_eq!(
|
||||
bytecode.instructions,
|
||||
instructions
|
||||
.into_iter()
|
||||
.flat_map(|x| x.0)
|
||||
.collect::<Vec<u8>>()
|
||||
.into()
|
||||
);
|
||||
assert_eq!(bytecode.constants, constants);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn integer_arithmetic() {
|
||||
let tests = [(
|
||||
"1+2",
|
||||
vec![Object::Integer(1), Object::Integer(2)],
|
||||
vec![
|
||||
code::make(OpCode::Constant, vec![0]),
|
||||
code::make(OpCode::Constant, vec![1]),
|
||||
],
|
||||
)];
|
||||
|
||||
for test in tests {
|
||||
run_compiler_tests(test);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -8,6 +8,8 @@ mod evaluator;
|
|||
|
||||
#[macro_use]
|
||||
mod lexer;
|
||||
mod code;
|
||||
mod compiler;
|
||||
mod parser;
|
||||
mod repl;
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user