Skip to content

Commit d8d1a50

Browse files
authored
Chapter 18: Types of Values (#15)
This implements chapter 18. The biggest difference to the C version in the value type. I used functions instead of macros to check the value type and casting. However, the C-like union is straight from the book. It is a learning exercise. Rust's enums are tagged unions. A production ready implementation should use enums.
1 parent 85e21bd commit d8d1a50

File tree

8 files changed

+234
-39
lines changed

8 files changed

+234
-39
lines changed

Diff for: README.md

+2-1
Original file line numberDiff line numberDiff line change
@@ -22,4 +22,5 @@ Each commit corresponds to one chapter in the book:
2222
## Part III: A Bytecode Virtual Machine
2323
* [Chapter 14: Chunks of Bytecode](https://github.com/jeschkies/lox-rs/commit/bcec748d59b568c3b6ce93d6d07b40b14f44caa0)
2424
* [Chapter 15: A Virtual Machine](https://github.com/jeschkies/lox-rs/commit/5c528b63f0ea4a5cfce3757b6c0a5323cba1abf6)
25-
* [Chapter 16: Compiling Expressions](https://github.com/jeschkies/lox-rs/commit/60563c11311c6c1efc268646305028eeb6024d29)
25+
* [Chapter 16: Scanning on Demand](https://github.com/jeschkies/lox-rs/commit/60563c11311c6c1efc268646305028eeb6024d29)
26+
* [Chapter 17: Compiling Expressions](https://github.com/jeschkies/lox-rs/commit/85e21bd3c9e8d9eb30e3f758051779bb1362b17e)

Diff for: bytecode/src/chunk.rs

+7
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,17 @@ use std::ptr;
77
#[derive(Debug)]
88
pub enum OpCode {
99
OpConstant(usize),
10+
OpNil,
11+
OpTrue,
12+
OpFalse,
13+
OpEqual,
14+
OpGreater,
15+
OpLess,
1016
OpAdd,
1117
OpSubtract,
1218
OpMultiply,
1319
OpDivide,
20+
OpNot,
1421
OpNegate,
1522
OpReturn,
1623
}

Diff for: bytecode/src/compiler.rs

+27-11
Original file line numberDiff line numberDiff line change
@@ -112,31 +112,31 @@ impl<'a> Compiler<'a> {
112112
parse_rule!(m, Semicolon => None, None, None);
113113
parse_rule!(m, Slash => None, Some(Compiler::binary), Factor);
114114
parse_rule!(m, Star => None, Some(Compiler::binary), Factor);
115-
parse_rule!(m, Bang => None, None, None);
116-
parse_rule!(m, BangEqual => None, None, None);
115+
parse_rule!(m, Bang => Some(Compiler::unary), None, None);
116+
parse_rule!(m, BangEqual => None, Some(Compiler::binary), Equality);
117117
parse_rule!(m, Equal => None, None, None);
118-
parse_rule!(m, EqualEqual => None, None, None);
119-
parse_rule!(m, Greater => None, None, None);
120-
parse_rule!(m, GreaterEqual => None, None, None);
121-
parse_rule!(m, Less => None, None, None);
122-
parse_rule!(m, LessEqual => None, None, None);
118+
parse_rule!(m, EqualEqual => None, Some(Compiler::binary), Equality);
119+
parse_rule!(m, Greater => None, Some(Compiler::binary), Comparison);
120+
parse_rule!(m, GreaterEqual => None, Some(Compiler::binary), Comparison);
121+
parse_rule!(m, Less => None, Some(Compiler::binary), Comparison);
122+
parse_rule!(m, LessEqual => None, Some(Compiler::binary), Comparison);
123123
parse_rule!(m, Identifier => None, None, None);
124124
parse_rule!(m, String => None, None, None);
125125
parse_rule!(m, Number => Some(Compiler::number), None, None);
126126
parse_rule!(m, And => None, None, None);
127127
parse_rule!(m, Class => None, None, None);
128128
parse_rule!(m, Else => None, None, None);
129-
parse_rule!(m, False => None, None, None);
129+
parse_rule!(m, False => Some(Compiler::literal), None, None);
130130
parse_rule!(m, For => None, None, None);
131131
parse_rule!(m, Fun => None, None, None);
132132
parse_rule!(m, If => None, None, None);
133-
parse_rule!(m, Nil => None, None, None);
133+
parse_rule!(m, Nil => Some(Compiler::literal), None, None);
134134
parse_rule!(m, Or => None, None, None);
135135
parse_rule!(m, Print => None, None, None);
136136
parse_rule!(m, Return => None, None, None);
137137
parse_rule!(m, Super => None, None, None);
138138
parse_rule!(m, This => None, None, None);
139-
parse_rule!(m, True => None, None, None);
139+
parse_rule!(m, True => Some(Compiler::literal), None, None);
140140
parse_rule!(m, Var => None, None, None);
141141
parse_rule!(m, While => None, None, None);
142142
parse_rule!(m, Error => None, None, None);
@@ -268,6 +268,12 @@ impl<'a> Compiler<'a> {
268268

269269
// Emit the operator instruction.
270270
match operator_type {
271+
TokenType::BangEqual => self.emit_bytes(OpCode::OpEqual, OpCode::OpNot),
272+
TokenType::EqualEqual => self.emit_byte(OpCode::OpEqual),
273+
TokenType::Greater => self.emit_byte(OpCode::OpGreater),
274+
TokenType::GreaterEqual => self.emit_bytes(OpCode::OpLess, OpCode::OpNot),
275+
TokenType::Less => self.emit_byte(OpCode::OpLess),
276+
TokenType::LessEqual => self.emit_bytes(OpCode::OpGreater, OpCode::OpNot),
271277
TokenType::Plus => self.emit_byte(OpCode::OpAdd),
272278
TokenType::Minus => self.emit_byte(OpCode::OpSubtract),
273279
TokenType::Star => self.emit_byte(OpCode::OpMultiply),
@@ -276,14 +282,23 @@ impl<'a> Compiler<'a> {
276282
}
277283
}
278284

285+
fn literal(&mut self) {
286+
match self.parser.previous.typ {
287+
TokenType::False => self.emit_byte(OpCode::OpFalse),
288+
TokenType::Nil => self.emit_byte(OpCode::OpNil),
289+
TokenType::True => self.emit_byte(OpCode::OpTrue),
290+
_ => unreachable!(),
291+
}
292+
}
293+
279294
fn grouping(&mut self) {
280295
self.expression();
281296
self.consume(TokenType::RightParen, "Expect ')' after expression.");
282297
}
283298

284299
fn number(&mut self) {
285300
let value: f64 = self.parser.previous.src.parse().unwrap();
286-
self.emit_constant(value)
301+
self.emit_constant(Value::new_number(value))
287302
}
288303

289304
fn unary(&mut self) {
@@ -294,6 +309,7 @@ impl<'a> Compiler<'a> {
294309

295310
// Emit the operator instruction.
296311
match operator_type {
312+
TokenType::Bang => self.emit_byte(OpCode::OpNot),
297313
TokenType::Minus => self.emit_byte(OpCode::OpNegate),
298314
_ => unreachable!(),
299315
}

Diff for: bytecode/src/debug.rs

+8-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ pub fn disassemble_chunk(chunk: &Chunk, name: &str) {
1111

1212
fn constant_instruction(name: &str, chunk: &Chunk, constant_index: usize) {
1313
print!("{:<16} {:>4} '", name, constant_index);
14-
print_value(chunk.constants[constant_index]);
14+
print_value(&chunk.constants[constant_index]);
1515
println!("'");
1616
}
1717

@@ -32,10 +32,17 @@ pub fn disassemble_instruction(chunk: &Chunk, op_code: &OpCode, i: usize) {
3232
OpCode::OpConstant(constant_index) => {
3333
constant_instruction("OP_CONSTANT", chunk, *constant_index)
3434
}
35+
OpCode::OpNil => simple_instruction("OP_NIL"),
36+
OpCode::OpTrue => simple_instruction("OP_TRUE"),
37+
OpCode::OpFalse => simple_instruction("OP_FALSE"),
38+
OpCode::OpEqual => simple_instruction("OP_EQUAL"),
39+
OpCode::OpGreater => simple_instruction("OP_GREATER"),
40+
OpCode::OpLess => simple_instruction("OP_LESS"),
3541
OpCode::OpAdd => simple_instruction("OP_ADD"),
3642
OpCode::OpSubtract => simple_instruction("OP_SUBTRACT"),
3743
OpCode::OpMultiply => simple_instruction("OP_MULTIPLY"),
3844
OpCode::OpDivide => simple_instruction("OP_DIVIDE"),
45+
OpCode::OpNot => simple_instruction("OP_NOT"),
3946
OpCode::OpNegate => simple_instruction("OP_NEGATE"),
4047
OpCode::OpReturn => simple_instruction("OP_RETURN"),
4148
_ => println!("Unknown opcode {:?}", op_code),

Diff for: bytecode/src/main.rs

+3-4
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ mod value;
88
mod vm;
99

1010
use std::fs;
11-
use std::io::{self, Read};
11+
use std::io;
1212
use std::process::exit;
1313

1414
use error::Error;
@@ -24,14 +24,13 @@ impl Lox {
2424
}
2525

2626
fn repl(&mut self) -> Result<(), Error> {
27-
let mut line = String::new();
2827
let stdin = io::stdin();
29-
let mut handle = stdin.lock();
3028

3129
loop {
3230
print!("> ");
3331

34-
if handle.read_to_string(&mut line)? == 0 {
32+
let mut line = String::new();
33+
if stdin.read_line(&mut line)? == 0 {
3534
println!();
3635
break;
3736
}

Diff for: bytecode/src/scanner.rs

+3-6
Original file line numberDiff line numberDiff line change
@@ -89,15 +89,12 @@ impl<'a> Scanner<'a> {
8989
}
9090
}
9191

92-
/// Return the character at the given position.
92+
/// Return the character at the given position or '\0' if we are out of bound.
9393
///
94-
/// This panics if the given position does not point to a valid char.
94+
/// This simulates a nul-terminated string.
9595
/// This method is copied from the [Rust Regex parse](https://github.com/rust-lang/regex/blob/master/regex-syntax/src/ast/parse.rs#L461).
9696
fn char_at(&self, i: usize) -> char {
97-
self.source[i..]
98-
.chars()
99-
.next()
100-
.unwrap_or_else(|| panic!("expected char at offset {}", i))
97+
self.source[i..].chars().next().unwrap_or_else(|| '\0')
10198
}
10299

103100
/// Returns the current char.

Diff for: bytecode/src/value.rs

+106-4
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,111 @@
1-
pub type Value = f64;
1+
use std::fmt;
2+
3+
#[derive(Debug, Clone, PartialEq, Eq)]
4+
enum ValueType {
5+
Bool,
6+
Nil,
7+
Number,
8+
}
9+
10+
// This basically implements tagged unions aka enums in Rust again.
11+
#[derive(Clone, Copy)]
12+
union V {
13+
boolean: bool,
14+
number: f64,
15+
}
16+
17+
#[derive(Clone)]
18+
pub struct Value {
19+
typ: ValueType,
20+
_as: V,
21+
}
22+
23+
impl fmt::Debug for Value {
24+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
25+
match self {
26+
Value {
27+
typ: ValueType::Bool,
28+
..
29+
} => write!(f, "Value({})", self.as_bool()),
30+
Value {
31+
typ: ValueType::Nil,
32+
..
33+
} => write!(f, "Value(Nil)"),
34+
Value {
35+
typ: ValueType::Number,
36+
..
37+
} => write!(f, "Value({})", self.as_number()),
38+
}
39+
}
40+
}
41+
42+
impl Value {
43+
pub fn new_bool(value: bool) -> Value {
44+
Value {
45+
typ: ValueType::Bool,
46+
_as: V { boolean: value },
47+
}
48+
}
49+
50+
pub fn new_nil() -> Value {
51+
Value {
52+
typ: ValueType::Nil,
53+
_as: V { number: 0.0 },
54+
}
55+
}
56+
57+
pub fn new_number(value: f64) -> Value {
58+
Value {
59+
typ: ValueType::Number,
60+
_as: V { number: value },
61+
}
62+
}
63+
64+
pub fn as_bool(&self) -> bool {
65+
unsafe { self._as.boolean }
66+
}
67+
68+
pub fn as_number(&self) -> f64 {
69+
unsafe { self._as.number }
70+
}
71+
72+
pub fn is_bool(&self) -> bool {
73+
self.typ == ValueType::Bool
74+
}
75+
76+
pub fn is_nil(&self) -> bool {
77+
self.typ == ValueType::Nil
78+
}
79+
80+
pub fn is_number(&self) -> bool {
81+
self.typ == ValueType::Number
82+
}
83+
}
84+
85+
impl PartialEq for Value {
86+
/// This is `valuesEqual` in the book.
87+
fn eq(&self, other: &Self) -> bool {
88+
if self.typ != other.typ {
89+
false
90+
} else {
91+
match self.typ {
92+
ValueType::Bool => self.as_bool() == other.as_bool(),
93+
ValueType::Nil => true,
94+
ValueType::Number => self.as_number() == other.as_number(),
95+
}
96+
}
97+
}
98+
}
99+
100+
impl Eq for Value {}
2101

3102
// We are not repeating the array implementation.
4103
pub type ValueArray = Vec<Value>;
5104

6-
pub fn print_value(value: Value) {
7-
// {} will print 100.0 as 100.
8-
print!("{}", value);
105+
pub fn print_value(value: &Value) {
106+
match value.typ {
107+
ValueType::Bool => print!("{}", value.as_bool()),
108+
ValueType::Nil => print!("nil"),
109+
ValueType::Number => print!("{}", value.as_number()),
110+
}
9111
}

0 commit comments

Comments
 (0)