~cdv/loxen

bba9ce3300c5bae29961fe8617fc0d173c430afe — Chris Vittal 6 months ago 2978e5f
[relox] Many changes, notably, strings.
M rs/src/bin/relox/main.rs => rs/src/bin/relox/main.rs +4 -2
@@ 32,7 32,8 @@ fn run_file(path: &str) -> i32 {
        }
    };

    match relox::vm::interpret(&source) {
    let mut vm = relox::VM::default();
    match vm.interpret(&source) {
        Ok(_) => 0,
        Err(Error::Compile) => 65,
        Err(Error::Runtime) => 70,


@@ 41,8 42,9 @@ fn run_file(path: &str) -> i32 {

fn repl() -> rustyline::Result<()> {
    let mut rl = rustyline::Editor::<()>::new();
    let mut vm = relox::VM::default();
    loop {
        let line = rl.readline(">> ")?;
        let _ = relox::vm::interpret(&line);
        let _ = vm.interpret(&line);
    }
}

M rs/src/chunk.rs => rs/src/chunk.rs +26 -24
@@ 1,5 1,3 @@
use std::convert::TryFrom;

use crate::value::Value;

#[derive(Debug, Default)]


@@ 24,7 22,7 @@ impl Chunk {
        self.code.push(b);
    }

    pub fn write_op(&mut self, op: OpCode, line: u32) {
    pub fn write_op(&mut self, op: Op, line: u32) {
        self.write(op as u8, line);
    }



@@ 34,10 32,10 @@ impl Chunk {
            return Err("too many constants in chunk");
        }
        if ind < u8::MAX as usize {
            self.write_op(OpCode::Constant, line);
            self.write_op(Op::Constant, line);
            self.write(ind as u8, line);
        } else {
            self.write_op(OpCode::ConstantLong, line);
            self.write_op(Op::ConstantLong, line);
            self.write(ind as u8, line);
            self.write((ind >> 8) as u8, line);
            self.write((ind >> 16) as u8, line);


@@ 50,12 48,14 @@ impl Chunk {
        self.constants.len() - 1
    }

    #[cfg(debug_assertions)]
    pub fn disassemble(&self, name: &str) {
        use std::convert::TryFrom;
        eprintln!("=== {} (begin) ===", name);
        let mut off = 0;
        let mut prev_line = u32::MAX;
        while off < self.code.len() {
            match OpCode::try_from(self.code[off]) {
            match Op::try_from(self.code[off]) {
                Ok(v) => {
                    off = v.disassemble(&self, off, &mut prev_line);
                }


@@ 68,7 68,7 @@ impl Chunk {
        eprintln!("=== {} ( end ) ===", name);
    }

    fn get_line(&self, offset: usize) -> u32 {
    pub fn get_line(&self, offset: usize) -> u32 {
        let mut addr: usize = 0;
        let mut line = self.lines.first_line as i32;
        for &LineOffset { byte_off, line_off } in &self.lines.array {


@@ 94,36 94,36 @@ macro_rules! op_codes {
    ($($name:ident, $const:ident, $pretty:expr;)*) => {
#[derive(Debug,Clone,Copy,PartialEq,Eq)]
#[repr(u8)]
pub enum OpCode {
pub enum Op {
    $($name,)*
}

#[derive(Debug)]
pub struct InvalidInstruction;

$(pub const $const: u8 = OpCode::$name as u8;)*
$(pub const $const: u8 = Op::$name as u8;)*

impl ::std::convert::TryFrom<u8> for OpCode {
impl ::std::convert::TryFrom<u8> for Op {
    type Error = InvalidInstruction;
    fn try_from(b: u8) -> ::std::result::Result<Self, Self::Error> {
        match b {
            $($const => Ok(OpCode::$name),)*
            $($const => Ok(Op::$name),)*
            _ => Err(InvalidInstruction)
        }
    }
}

impl Into<u8> for OpCode {
impl Into<u8> for Op {
    #[inline(always)]
    fn into(self) -> u8 {
        self as u8
    }
}

impl ::std::fmt::Display for OpCode {
impl ::std::fmt::Display for Op {
    fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
        match self {
            $(OpCode::$name => f.pad($pretty),)*
            $(Op::$name => f.pad($pretty),)*
        }
    }
}}


@@ 133,23 133,24 @@ op_codes!(
    Return, OP_RETURN, "RETURN";
    Constant, OP_CONSTANT, "CONSTANT";
    ConstantLong, OP_CONSTANT_LONG, "CONSTANT_LONG";
//    Nil, OP_NIL, "NIL";
//    True, OP_TRUE, "TRUE";
//    False, OP_FALSE, "FALSE";
//    Equal, OP_EQUAL, "EQUAL";
//    Greater, OP_GREATER, "GREATER";
//    Less, OP_LESS, "LESS";
    Nil, OP_NIL, "NIL";
    True, OP_TRUE, "TRUE";
    False, OP_FALSE, "FALSE";
    Equal, OP_EQUAL, "EQUAL";
    Greater, OP_GREATER, "GREATER";
    Less, OP_LESS, "LESS";
    Add, OP_ADD, "ADD";
    Sub, OP_SUBTRACT, "SUBTRACT";
    Mul, OP_MULTIPLY, "MULTIPLY";
    Div, OP_DIVIDE, "DIVIDE";
//    Not, OP_NOT, "NOT";
    Not, OP_NOT, "NOT";
    Negate, OP_NEGATE, "NEGATE";
);

impl OpCode {
impl Op {
    #[cfg(debug_assertions)]
    pub fn disassemble(&self, c: &Chunk, off: usize, prev_line: &mut u32) -> usize {
        use OpCode::*;
        use Op::*;
        let line = c.get_line(off);
        eprint!("{:04} ", off);
        if line == *prev_line {


@@ 159,7 160,8 @@ impl OpCode {
            *prev_line = line;
        }
        match self {
            Return | Add | Sub | Mul | Div | Negate => {
            Return | Nil | True | False | Equal | Greater | Less | Add | Sub | Mul | Div
            | Negate | Not => {
                eprintln!("{:16}", self);
                off + 1
            }

M rs/src/compiler.rs => rs/src/compiler.rs +62 -36
@@ 1,26 1,29 @@
use crate::{
    chunk::*,
    object::Heap,
    scanner::{Scanner, Token, TokenType},
    value::Value,
    Error,
};

pub fn compile(source: &str) -> crate::Result<Box<Chunk>> {
    let mut c = Compiler::new(source);
pub fn compile(source: &str, heap: &mut Heap) -> crate::Result<Box<Chunk>> {
    let mut c = Compiler::new(source, heap);
    c.expression();
    c.consume(TokenType::Eof, "Expect end of expression.");
    c.end()
}

struct Compiler<'s> {
struct Compiler<'s, 'vm> {
    p: Parser<'s>,
    heap: &'vm mut Heap,
    chunk: Box<Chunk>,
}

impl<'s> Compiler<'s> {
    fn new(source: &'s str) -> Self {
impl<'s, 'vm> Compiler<'s, 'vm> {
    fn new(source: &'s str, heap: &'vm mut Heap) -> Self {
        Self {
            p: Parser::new(source),
            heap,
            chunk: Default::default(),
        }
    }


@@ 48,22 51,39 @@ impl<'s> Compiler<'s> {
        self.expression();

        match ty {
            TokenType::Minus => self.emit_op(OpCode::Negate),
            TokenType::Bang => self.emit_op(Op::Not),
            TokenType::Minus => self.emit_op(Op::Negate),
            _ => unreachable!(),
        }
    }

    fn binary(&mut self) {
        use TokenType::*;
        let ty = self.p.prev.ty;

        let rule = ParseRule::get(ty);
        self.parse_prec(rule.prec.next());

        match ty {
            TokenType::Plus => self.emit_op(OpCode::Add),
            TokenType::Minus => self.emit_op(OpCode::Sub),
            TokenType::Star => self.emit_op(OpCode::Mul),
            TokenType::Slash => self.emit_op(OpCode::Div),
            BangEq => self.emit_op2(Op::Equal, Op::Not),
            EqualEq => self.emit_op(Op::Equal),
            Greater => self.emit_op(Op::Greater),
            GreaterEq => self.emit_op2(Op::Less, Op::Not),
            Less => self.emit_op(Op::Less),
            LessEq => self.emit_op2(Op::Greater, Op::Not),
            Plus => self.emit_op(Op::Add),
            Minus => self.emit_op(Op::Sub),
            Star => self.emit_op(Op::Mul),
            Slash => self.emit_op(Op::Div),
            _ => unreachable!(),
        }
    }

    fn literal(&mut self) {
        match self.p.prev.ty {
            TokenType::False => self.emit_op(Op::False),
            TokenType::Nil => self.emit_op(Op::Nil),
            TokenType::True => self.emit_op(Op::True),
            _ => unreachable!(),
        }
    }


@@ 78,7 98,13 @@ impl<'s> Compiler<'s> {
            .s()
            .parse::<f64>()
            .expect("should only call number on doubles");
        self.emit_constant(n);
        self.emit_constant(n.into());
    }

    fn string(&mut self) {
        let s = &self.p.prev.s[1..self.p.prev.s.len() - 1];
        let oid = self.heap.clone_str(s);
        self.emit_constant(Value::Str(oid));
    }

    fn parse_prec(&mut self, prec: Prec) {


@@ 101,7 127,7 @@ impl<'s> Compiler<'s> {
    }

    fn emit_return(&mut self) {
        self.emit_op(OpCode::Return);
        self.emit_op(Op::Return);
    }

    fn emit_constant(&mut self, v: Value) {


@@ 110,13 136,13 @@ impl<'s> Compiler<'s> {
        }
    }

    fn emit_op(&mut self, op: OpCode) {
    fn emit_op(&mut self, op: Op) {
        self.emit(op as u8);
    }

    fn _emit_op2(&mut self, op: OpCode, b: u8) {
        self.emit(op as u8);
        self.emit(b);
    fn emit_op2(&mut self, op1: Op, op2: Op) {
        self.emit(op1 as u8);
        self.emit(op2 as u8);
    }

    fn _emit2(&mut self, b1: u8, b2: u8) {


@@ 125,11 151,11 @@ impl<'s> Compiler<'s> {
    }

    fn emit(&mut self, b: u8) {
        self.chunk.write(b, self.p.prev.line);
        self.chunk.write(b, self.line());
    }
}

impl<'s> std::ops::Deref for Compiler<'s> {
impl<'s> std::ops::Deref for Compiler<'s, '_> {
    type Target = Parser<'s>;

    fn deref(&self) -> &Self::Target {


@@ 137,7 163,7 @@ impl<'s> std::ops::Deref for Compiler<'s> {
    }
}

impl<'s> std::ops::DerefMut for Compiler<'s> {
impl<'s> std::ops::DerefMut for Compiler<'s, '_> {
    fn deref_mut(&mut self) -> &mut Self::Target {
        &mut self.p
    }


@@ 261,16 287,16 @@ impl Prec {
    }
}

struct ParseRule<'s> {
    pre: Option<fn(&mut Compiler<'s>)>,
    inf: Option<fn(&mut Compiler<'s>)>,
struct ParseRule<'s, 'vm> {
    pre: Option<fn(&mut Compiler<'s, 'vm>)>,
    inf: Option<fn(&mut Compiler<'s, 'vm>)>,
    prec: Prec,
}

impl<'s> ParseRule<'s> {
impl<'s, 'vm> ParseRule<'s, 'vm> {
    fn new(
        pre: Option<fn(&mut Compiler<'s>)>,
        inf: Option<fn(&mut Compiler<'s>)>,
        pre: Option<fn(&mut Compiler<'s, 'vm>)>,
        inf: Option<fn(&mut Compiler<'s, 'vm>)>,
        prec: Prec,
    ) -> Self {
        Self { pre, inf, prec }


@@ 292,33 318,33 @@ impl<'s> ParseRule<'s> {
            Slash => Self::new(None, Some(Compiler::binary), Prec::Factor),
            Star => Self::new(None, Some(Compiler::binary), Prec::Factor),

            Bang => Self::new(None, None, Prec::None),
            BangEq => Self::new(None, None, Prec::None),
            Bang => Self::new(Some(Compiler::unary), None, Prec::None),
            BangEq => Self::new(None, Some(Compiler::binary), Prec::Eq),
            Equal => Self::new(None, None, Prec::None),
            EqualEq => Self::new(None, None, Prec::None),
            Greater => Self::new(None, None, Prec::None),
            GreaterEq => Self::new(None, None, Prec::None),
            Less => Self::new(None, None, Prec::None),
            LessEq => Self::new(None, None, Prec::None),
            EqualEq => Self::new(None, Some(Compiler::binary), Prec::Eq),
            Greater => Self::new(None, Some(Compiler::binary), Prec::Cmp),
            GreaterEq => Self::new(None, Some(Compiler::binary), Prec::Cmp),
            Less => Self::new(None, Some(Compiler::binary), Prec::Cmp),
            LessEq => Self::new(None, Some(Compiler::binary), Prec::Cmp),

            Ident => Self::new(None, None, Prec::None),
            Str => Self::new(None, None, Prec::None),
            Str => Self::new(Some(Compiler::string), None, Prec::None),
            Num => Self::new(Some(Compiler::number), None, Prec::None),

            And => Self::new(None, None, Prec::None),
            Class => Self::new(None, None, Prec::None),
            Else => Self::new(None, None, Prec::None),
            False => Self::new(None, None, Prec::None),
            False => Self::new(Some(Compiler::literal), None, Prec::None),
            For => Self::new(None, None, Prec::None),
            Fun => Self::new(None, None, Prec::None),
            If => Self::new(None, None, Prec::None),
            Nil => Self::new(None, None, Prec::None),
            Nil => Self::new(Some(Compiler::literal), None, Prec::None),
            Or => Self::new(None, None, Prec::None),
            Print => Self::new(None, None, Prec::None),
            Return => Self::new(None, None, Prec::None),
            Super => Self::new(None, None, Prec::None),
            This => Self::new(None, None, Prec::None),
            True => Self::new(None, None, Prec::None),
            True => Self::new(Some(Compiler::literal), None, Prec::None),
            Var => Self::new(None, None, Prec::None),
            While => Self::new(None, None, Prec::None),


M rs/src/lib.rs => rs/src/lib.rs +4 -1
@@ 1,8 1,11 @@
mod chunk;
mod compiler;
mod object;
mod scanner;
mod value;
pub mod vm;
mod vm;

pub use vm::VM;

type Result<T> = std::result::Result<T, Error>;


A rs/src/object.rs => rs/src/object.rs +93 -0
@@ 0,0 1,93 @@
use std::{collections::hash_map::HashMap, fmt, rc::Rc};

pub enum Obj {
    Str(Rc<str>),
    #[allow(unused)]
    None,
}

#[repr(transparent)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Oid(usize);

#[derive(Default)]
pub struct Heap {
    objs: Vec<Obj>,
    strings: HashMap<Rc<str>, Oid>,
}

impl std::ops::Index<Oid> for Heap {
    type Output = Obj;

    #[inline]
    fn index(&self, idx: Oid) -> &Self::Output {
        &self.objs[idx.0]
    }
}

impl std::ops::IndexMut<Oid> for Heap {
    #[inline]
    fn index_mut(&mut self, idx: Oid) -> &mut Self::Output {
        &mut self.objs[idx.0]
    }
}

impl From<&'_ str> for Obj {
    fn from(s: &'_ str) -> Self {
        Obj::Str(Rc::from(s))
    }
}

impl From<String> for Obj {
    fn from(s: String) -> Self {
        Obj::Str(s.into())
    }
}

impl Heap {
    pub fn clone_str(&mut self, s: &str) -> Oid {
        match self.strings.get(s) {
            Some(&oid) => oid,
            None => {
                let oid = self.create_obj(s);
                if let Obj::Str(obs) = &self[oid] {
                    let obs = obs.clone();
                    self.strings.insert(obs, oid);
                    oid
                } else {
                    unreachable!()
                }
            }
        }
    }

    pub fn take_string(&mut self, s: String) -> Oid {
        // rc, already copies bytes out of a string, wish this wasn't
        // quite the case
        self.clone_str(&s)
    }

    fn create_obj<O: Into<Obj>>(&mut self, obj: O) -> Oid {
        self.objs.push(obj.into());
        Oid(self.objs.len() - 1)
    }

    pub fn _destroy(&mut self, oid: Oid) {
        self[oid] = Obj::None;
    }
}

impl fmt::LowerHex for Oid {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        <usize as fmt::LowerHex>::fmt(&self.0, f)
    }
}

impl fmt::Display for Obj {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            Obj::Str(s) => <Rc<str> as fmt::Display>::fmt(s, f),
            Obj::None => f.pad("nullobj"),
        }
    }
}

M rs/src/value.rs => rs/src/value.rs +45 -5
@@ 1,18 1,58 @@
pub type Value = f64;
/*
use std::fmt;

#[derive(Debug, Clone, Copy)]
use crate::object::*;

#[derive(Debug, Clone, Copy, PartialEq)]
pub enum Value {
    Bool(bool),
    Nil,
    Num(f64),

    // should match the object hierarchy, with exception of None
    Str(Oid),
}

impl Value {
    pub fn is_num(&self) -> bool {
        matches!(self, Value::Num(_))
    }

    pub fn _is_bool(&self) -> bool {
        matches!(self, Value::Bool(_))
    }

    pub fn _is_str(&self) -> bool {
        matches!(self, Value::Str(_))
    }

    pub fn is_nil(&self) -> bool {
        matches!(self, Value::Nil)
    }

    pub fn is_falsey(&self) -> bool {
        self.is_nil() || matches!(self, Value::Bool(false))
    }
}

impl From<bool> for Value {
    fn from(p: bool) -> Self {
        Self::Bool(p)
    }
}

impl From<f64> for Value {
    fn from(n: f64) -> Self {
        Value::Num(n)
    }
}

impl fmt::Display for Value {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            Self::Bool(b) => <bool as fmt::Display>::fmt(b, f),
            Self::Nil => f.pad("nil"),
            Self::Str(oid) => write!(f, "str {:#06x}", oid),
            Self::Num(n) => <f64 as fmt::Display>::fmt(n, f),
        }
    }
}

*/

M rs/src/vm.rs => rs/src/vm.rs +113 -30
@@ 1,41 1,67 @@
use std::convert::TryFrom;
use std::{convert::TryFrom, fmt};

use byteorder::{ReadBytesExt, LE};

use crate::chunk::*;
use crate::value::Value;
use crate::Error;
use crate::{chunk::*, object::*, value::Value, Error};

use OpCode::*;

pub fn interpret(source: &str) -> Result<(), Error> {
    let chunk = crate::compiler::compile(source)?;
    VM::interpret(chunk)
}
use Op::*;

#[derive(Default)]
pub struct VM {
    chunk: Box<Chunk>,
    stack: Vec<Value>,
    heap: Heap,
    ip: usize,
}

impl VM {
    fn interpret(chunk: Box<Chunk>) -> Result<(), Error> {
        let mut vm = Self {
            chunk,
            stack: Vec::with_capacity(65536),
            ip: 0,
        };
    pub fn interpret(&mut self, source: &str) -> Result<(), Error> {
        let chunk = crate::compiler::compile(source, &mut self.heap)?;
        self.interpret_chunk(chunk)
    }

    fn interpret_chunk(&mut self, mut chunk: Box<Chunk>) -> Result<(), Error> {
        std::mem::swap(&mut self.chunk, &mut chunk);
        self.ip = 0;
        self.stack.clear();

        vm.run()
        self.run()
    }

    fn runtime_error(&self, err: fmt::Arguments) -> Result<(), Error> {
        eprintln!("{}", err);
        eprintln!("[line {}] in script", self.chunk.get_line(self.ip));
        Err(Error::Runtime)
    }

    fn concatenate(&mut self, sa: Oid, sb: Oid) {
        let new_s = match (&self.heap[sa], &self.heap[sb]) {
            (Obj::Str(ref a), Obj::Str(ref b)) => {
                let mut a = String::from(a.as_ref());
                a += &b;
                a
            }
            _ => unreachable!(),
        };
        self.pop();
        self.pop();
        let oid = self.heap.take_string(new_s);
        self.push(Value::Str(oid));
    }

    fn run(&mut self) -> Result<(), Error> {
        macro_rules! binary_op {
            ($op:tt) => {
                if !self.peek(0).is_num() || !self.peek(1).is_num() {
                    self.runtime_error(format_args!("Operands must be numbers"))?;
                }

                let b = self.pop();
                let a = self.pop();
                self.push(a $op b);
                match (a, b) {
                    (Value::Num(a), Value::Num(b)) => self.push(a $op b),
                    _ => unreachable!()
                }
            }
        }
        #[cfg(debug_assertions)]


@@ 48,7 74,7 @@ impl VM {
                    print!("          ");
                }
                for v in &self.stack {
                    print!("[ {} ]", v);
                    print!("[ {} ]", self.vfmt(v));
                }
                println!();
                op.disassemble(&self.chunk, self.ip - 1, &mut prev_line);


@@ 58,13 84,29 @@ impl VM {
                    let v = self.read_constant(op);
                    self.push(v);
                }
                Negate => {
                    let v = self.pop();
                    self.push(-v);
                Nil => self.push(Value::Nil),
                True => self.push(true),
                False => self.push(false),
                Equal => {
                    let b = self.pop();
                    let a = self.pop();
                    self.push(a == b);
                }
                Greater => {
                    binary_op!(>);
                }
                Add => {
                    binary_op!(+);
                Less => {
                    binary_op!(<);
                }
                Add => match (self.peek(1), self.peek(0)) {
                    (&Value::Str(sa), &Value::Str(sb)) => self.concatenate(sa, sb),
                    (&Value::Num(a), &Value::Num(b)) => {
                        self.pop();
                        self.pop();
                        self.push(a + b);
                    }
                    _ => todo!(),
                },
                Sub => {
                    binary_op!(-);
                }


@@ 74,15 116,31 @@ impl VM {
                Div => {
                    binary_op!(/);
                }
                Not => {
                    let v = self.pop();
                    self.push(v.is_falsey())
                }
                Negate => {
                    if !self.peek(0).is_num() {
                        self.runtime_error(format_args!("Operand must be a number"))?;
                    }

                    if let Value::Num(v) = self.pop() {
                        self.push(-v);
                    } else {
                        unreachable!();
                    }
                }
                Return => {
                    println!("{}", self.pop());
                    let v = self.pop();
                    println!("{}", self.vfmt(&v));
                    return Ok(());
                }
            }
        }
    }

    fn read_constant(&mut self, op: OpCode) -> Value {
    fn read_constant(&mut self, op: Op) -> Value {
        match op {
            Constant => {
                let cid = self.read_byte() as usize;


@@ 105,15 163,40 @@ impl VM {
        self.chunk[self.ip - 1]
    }

    fn read_op(&mut self) -> OpCode {
        OpCode::try_from(self.read_byte()).expect("Invalid Opcode")
    fn read_op(&mut self) -> Op {
        Op::try_from(self.read_byte()).expect("Invalid Opcode")
    }

    fn push(&mut self, v: Value) {
        self.stack.push(v);
    fn push<V: Into<Value>>(&mut self, v: V) {
        self.stack.push(v.into());
    }

    fn pop(&mut self) -> Value {
        self.stack.pop().expect("Attempted to pop from empty stack")
    }

    fn peek(&self, dist: usize) -> &Value {
        &self.stack[self.stack.len() - dist - 1]
    }

    fn vfmt<'vm>(&'vm self, v: &'vm Value) -> VRef {
        VRef {
            heap: &self.heap,
            value: v,
        }
    }
}

struct VRef<'vm> {
    heap: &'vm Heap,
    value: &'vm Value,
}

impl fmt::Display for VRef<'_> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self.value {
            Value::Str(oid) => write!(f, "{}", self.heap[*oid]),
            v => write!(f, "{}", v),
        }
    }
}