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]
|
#[macro_use]
|
||||||
mod lexer;
|
mod lexer;
|
||||||
|
mod code;
|
||||||
|
mod compiler;
|
||||||
mod parser;
|
mod parser;
|
||||||
mod repl;
|
mod repl;
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user