Skip to content

Commit

Permalink
Adding support for print statement
Browse files Browse the repository at this point in the history
The print statement will be removed later on. It just simplifies things as long as we don't have a standard library.
The parser now also supports multiline statements and is prepared for adding declarations.
  • Loading branch information
patbuc committed Jul 13, 2024
1 parent a108db0 commit 949fd14
Show file tree
Hide file tree
Showing 6 changed files with 96 additions and 9 deletions.
9 changes: 8 additions & 1 deletion src/compiler/compiler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,14 @@ impl Compiler {

parser.start();
parser.advance();
parser.expression();

loop {
if parser.match_token(TokenType::Eof) {
break;
}
parser.declaration();
}

parser.consume(TokenType::Eof, "Expect end of expression");
parser.end();

Expand Down
38 changes: 38 additions & 0 deletions src/compiler/parser/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,32 @@ impl Parser {
}
}

pub(in crate::compiler) fn match_token(&mut self, token_type: TokenType) -> bool {
if !self.check(token_type) {
return false;
}
self.advance();
return true;
}

fn check(&mut self, token_type: TokenType) -> bool {
return self.current_token.token_type == token_type;
}

#[cfg_attr(feature = "disassemble", instrument(skip(self)))]
pub(in crate::compiler) fn declaration(&mut self) {
self.statement();
}

#[cfg_attr(feature = "disassemble", instrument(skip(self)))]
pub(in crate::compiler) fn statement(&mut self) {
if self.match_token(TokenType::Print) {
self.print_statement();
} else {
self.expression_statement();
}
}

#[cfg_attr(feature = "disassemble", instrument(skip(self)))]
pub(in crate::compiler) fn expression(&mut self) {
self.parse_precedence(Precedence::Assignment);
Expand Down Expand Up @@ -109,6 +135,7 @@ impl Parser {
}
}

#[cfg_attr(feature = "disassemble", instrument(skip(self)))]
fn literal(&mut self) {
match self.previous_token.token_type {
TokenType::False => self.emit_op_code(OpCode::False),
Expand Down Expand Up @@ -195,4 +222,15 @@ impl Parser {
self.blocks.last_mut().unwrap()
}

fn print_statement(&mut self) {
self.expression();
self.consume(TokenType::Semicolon, "Expecting ';' at end of statement.");
self.emit_op_code(OpCode::Print);
}

fn expression_statement(&mut self) {
self.expression();
self.consume(TokenType::Semicolon, "Expecting ';' at end of expression.");
self.emit_op_code(OpCode::Pop);
}
}
3 changes: 2 additions & 1 deletion src/neon.n
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
((10 + 2) * (6 / 2) - (5 - 3)) < 29
print "Hello" + " " + "World!";
print 42 * 3.14;
4 changes: 3 additions & 1 deletion src/vm/block/disassembler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ impl Block {
OpCode::Less => self.simple_instruction(OpCode::Less, offset),
OpCode::Not => self.simple_instruction(OpCode::Not, offset),
OpCode::String => self.string_instruction(instruction, offset),
OpCode::Print => self.simple_instruction(OpCode::Print, offset),
OpCode::Pop => self.simple_instruction(OpCode::Pop, offset),
};
}

Expand All @@ -65,7 +67,7 @@ impl Block {
}

let (index, offset_shift) = get_constant_index(self, &op_code, offset + 1);
let constant = self.constants.read_value(index);
let constant = self.read_constant(index);
println!("{:?} {:02} '{}'", op_code, index, as_number!(constant));
offset + 1 + offset_shift
}
Expand Down
2 changes: 2 additions & 0 deletions src/vm/opcodes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ pub(crate) enum OpCode {
Less = 0x0E,
Not = 0x0F,
String = 0x10,
Print = 0x11,
Pop = 0x12,
}

impl OpCode {
Expand Down
49 changes: 43 additions & 6 deletions src/vm/virtual_machine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,6 @@ impl VirtualMachine {
loop {
match OpCode::from_u8(block.read_u8(self.ip)) {
OpCode::Return => {
let value = self.pop();
println!("{}", value);
return Result::Ok;
}
OpCode::Constant => {
Expand Down Expand Up @@ -137,6 +135,13 @@ impl VirtualMachine {
self.push(string);
self.ip += 1;
}
OpCode::Print => {
let value = self.pop();
println!("{}", value);
}
OpCode::Pop => {
self.pop();
}
}
self.ip += 1;
}
Expand Down Expand Up @@ -207,10 +212,6 @@ mod tests {
block.write_op_code(super::OpCode::Subtract, 0);
block.write_constant(number!(2.0), 0);
block.write_op_code(super::OpCode::Divide, 0);

// Pushing throw away value to the stack.
// This is needed because the Return OpCode will pop a value from the stack and print it.
block.write_constant(number!(0.0), 0);
block.write_op_code(super::OpCode::Return, 0);

let mut vm = super::VirtualMachine {
Expand All @@ -223,4 +224,40 @@ mod tests {
assert_eq!(super::Result::Ok, result);
assert_eq!(3.5, as_number!(vm.pop()));
}

#[test]
fn can_print_hello_world() {
let program = r#"
print "Hello, World!";
"#;

let mut vm = super::VirtualMachine::new();
let result = vm.interpret(program.to_string());

assert_eq!(super::Result::Ok, result);
}

#[test]
fn can_print_the_answer_to_everything_times_pi() {
let program = r#"
print 42 * 3.14;
"#;

let mut vm = super::VirtualMachine::new();
let result = vm.interpret(program.to_string());

assert_eq!(super::Result::Ok, result);
}
#[test]
fn can_run_multi_line_statements() {
let program = r#"
print "Hello, World!";
print 42 * 3.14;
"#;

let mut vm = super::VirtualMachine::new();
let result = vm.interpret(program.to_string());

assert_eq!(super::Result::Ok, result);
}
}

0 comments on commit 949fd14

Please sign in to comment.