From 6dd22f5e4efff18995b6cb9df23d7b4debf0eeaa Mon Sep 17 00:00:00 2001 From: Shautvast Date: Tue, 21 Oct 2025 16:27:26 +0200 Subject: [PATCH] print statements --- src/chunk.rs | 6 ++- src/compiler.rs | 131 +++++++++++++++++++++++++++++++++++++----------- src/main.rs | 2 +- src/scanner.rs | 1 + src/tokens.rs | 5 +- src/value.rs | 25 +++++++++ src/vm.rs | 16 ++++-- 7 files changed, 148 insertions(+), 38 deletions(-) diff --git a/src/chunk.rs b/src/chunk.rs index 9b1e7e0..889e762 100644 --- a/src/chunk.rs +++ b/src/chunk.rs @@ -3,7 +3,7 @@ use crate::value::Value; use crate::vm::{ OP_ADD, OP_BITAND, OP_BITOR, OP_BITXOR, OP_CONSTANT, OP_DIVIDE, OP_FALSE, OP_MULTIPLY, OP_NEGATE, OP_RETURN, OP_SUBTRACT, OP_TRUE, OP_NOT, OP_SHL, OP_SHR, OP_LESS, OP_LESS_EQUAL, - OP_GREATER, OP_GREATER_EQUAL, OP_EQUAL + OP_GREATER, OP_GREATER_EQUAL, OP_EQUAL, OP_PRINT, OP_POP, }; pub struct Chunk { @@ -71,6 +71,8 @@ impl Chunk { OP_GREATER => self.simple_inst("GT", offset), OP_GREATER_EQUAL => self.simple_inst("GTE", offset), OP_EQUAL => self.simple_inst("EQ", offset), + OP_PRINT => self.simple_inst("PRT", offset), + OP_POP => self.simple_inst("POP", offset), _ => { println!("Unknown instruction"); offset + 1 @@ -85,7 +87,7 @@ impl Chunk { fn constant_inst(&self, name: &str, offset: usize) -> usize { let constant = self.code[offset + 1]; - debug!("{} {} ", name, constant); + print!("{} {} ", name, constant); self.print_value(&self.constants[constant as usize]); offset + 2 } diff --git a/src/compiler.rs b/src/compiler.rs index 5f1f49d..e90c732 100644 --- a/src/compiler.rs +++ b/src/compiler.rs @@ -2,7 +2,7 @@ use crate::chunk::Chunk; use crate::scanner::scan; use crate::tokens::{Token, TokenType}; use crate::value::Value; -use crate::vm::{OP_ADD, OP_BITAND, OP_BITOR, OP_BITXOR, OP_CONSTANT, OP_DIVIDE, OP_FALSE, OP_MULTIPLY, OP_NEGATE, OP_NOT, OP_RETURN, OP_SHR, OP_SHL, OP_SUBTRACT, OP_TRUE, OP_EQUAL, OP_GREATER, OP_GREATER_EQUAL, OP_LESS, OP_LESS_EQUAL}; +use crate::vm::{OP_ADD, OP_BITAND, OP_BITOR, OP_BITXOR, OP_CONSTANT, OP_DIVIDE, OP_EQUAL, OP_FALSE, OP_GREATER, OP_GREATER_EQUAL, OP_LESS, OP_LESS_EQUAL, OP_MULTIPLY, OP_NEGATE, OP_NOT, OP_POP, OP_PRINT, OP_RETURN, OP_SHL, OP_SHR, OP_SUBTRACT, OP_TRUE}; use anyhow::anyhow; use std::collections::HashMap; use std::sync::LazyLock; @@ -36,12 +36,44 @@ struct Compiler<'a> { impl<'a> Compiler<'a> { fn compile(mut self) -> anyhow::Result { - self.expression()?; - self.consume(TokenType::Eof, "Expect end of expression.")?; + + + while !self.match_token(TokenType::Eof) { + self.declaration()?; + } + + // self.expression()?; + + // self.consume(TokenType::Eof, "Expect end of expression.")?; self.emit_byte(OP_RETURN); Ok(self.chunk) } + fn declaration(&mut self) -> anyhow::Result<()> { + self.statement() + } + + fn statement(&mut self) -> anyhow::Result<()> { + if self.match_token(TokenType::Print) { + self.print_statement() + } else { + self.expression_statement() + } + } + + fn expression_statement(&mut self) -> anyhow::Result<()>{ + self.expression()?; + self.emit_byte(OP_POP); + Ok(()) + } + + fn print_statement(&mut self) -> anyhow::Result<()> { + self.expression()?; + // self.consume(TokenType::Semicolon, "Expect ';' after value.")?; + self.emit_byte(OP_PRINT); + Ok(()) + } + fn advance(&mut self) -> anyhow::Result<()> { if self.current < self.tokens.len() - 1 { self.previous = self.current; @@ -49,7 +81,7 @@ impl<'a> Compiler<'a> { self.current += 1; self.current_token = &self.tokens[self.current]; } - if let TokenType::Error = self.current_token.tokentype { + if let TokenType::Error = self.current_token.token_type { self.had_error = true; Err(anyhow!( "Error at {} on line {}", @@ -62,13 +94,26 @@ impl<'a> Compiler<'a> { } fn consume(&mut self, token_type: TokenType, message: &str) -> anyhow::Result<()> { - if token_type == self.current_token.tokentype { + if token_type == self.current_token.token_type { self.advance() } else { Err(anyhow!("{}", message)) } } + fn match_token(&mut self, token_type: TokenType) -> bool { + if !self.check(token_type) { + false + } else { + self.advance().expect("token expected"); + true + } + } + + fn check(&mut self, token_type: TokenType) -> bool { + self.current_token.token_type == token_type + } + fn expression(&mut self) -> anyhow::Result<()> { self.parse_precedence(PREC_ASSIGNMENT)?; Ok(()) @@ -76,19 +121,19 @@ impl<'a> Compiler<'a> { fn parse_precedence(&mut self, precedence: usize) -> anyhow::Result<()> { self.advance()?; - let rule = get_rule(&self.previous_token.tokentype); - debug!("Precedence rule: {:?}",rule); + let rule = get_rule(&self.previous_token.token_type); + debug!("Precedence rule: {:?}", rule); if let Some(prefix) = rule.prefix { prefix(self)?; - while precedence <= get_rule(&self.current_token.tokentype).precedence { + while precedence <= get_rule(&self.current_token.token_type).precedence { self.advance()?; - let infix_rule = get_rule(&self.previous_token.tokentype).infix; + let infix_rule = get_rule(&self.previous_token.token_type).infix; if let Some(infix) = infix_rule { infix(self)?; } } } else { - return Err(anyhow!("Expect expression.")); + return Err(anyhow!("Expect expression.")); } Ok(()) } @@ -128,17 +173,15 @@ impl Rule { } fn number(s: &mut Compiler) -> anyhow::Result<()> { - let tt = s.previous_token.tokentype; - let value = s.previous_token.lexeme.clone(); - s.emit_constant(match tt { - TokenType::Number => Value::F64(value.parse().unwrap()), + s.emit_constant(match s.previous_token.token_type { + TokenType::Number => Value::F64(s.previous_token.lexeme.parse()?), _ => unimplemented!(), // TODO numeric types }); Ok(()) } fn literal(s: &mut Compiler) -> anyhow::Result<()> { - match s.previous_token.tokentype { + match s.previous_token.token_type { TokenType::False => s.emit_byte(OP_FALSE), TokenType::True => s.emit_byte(OP_TRUE), TokenType::String => s.emit_constant(Value::String(s.previous_token.lexeme.clone())), @@ -153,7 +196,7 @@ fn grouping(s: &mut Compiler) -> anyhow::Result<()> { } fn unary(s: &mut Compiler) -> anyhow::Result<()> { - let operator_type = s.previous_token.tokentype; + let operator_type = s.previous_token.token_type; s.parse_precedence(PREC_UNARY)?; @@ -170,8 +213,8 @@ fn unary(s: &mut Compiler) -> anyhow::Result<()> { } fn binary(s: &mut Compiler) -> anyhow::Result<()> { - let operator_type = &s.previous_token.tokentype; - debug!("operator {:?}",operator_type); + let operator_type = &s.previous_token.token_type; + debug!("operator {:?}", operator_type); let rule = get_rule(operator_type); s.parse_precedence(rule.precedence + 1)?; match operator_type { @@ -221,23 +264,53 @@ static RULES: LazyLock> = LazyLock::new(|| { rules.insert(TokenType::Slash, Rule::new(None, Some(binary), PREC_FACTOR)); rules.insert(TokenType::Star, Rule::new(None, Some(binary), PREC_FACTOR)); rules.insert(TokenType::Bang, Rule::new(Some(unary), None, PREC_UNARY)); - rules.insert(TokenType::BangEqual, Rule::new(None, None, PREC_NONE)); + rules.insert(TokenType::BangEqual, Rule::new(None, None, PREC_EQUALITY)); rules.insert(TokenType::Equal, Rule::new(None, None, PREC_NONE)); - rules.insert(TokenType::EqualEqual, Rule::new(None, Some(binary), PREC_COMPARISON)); - rules.insert(TokenType::Greater, Rule::new(None, Some(binary), PREC_COMPARISON)); - rules.insert(TokenType::GreaterEqual, Rule::new(None, Some(binary), PREC_COMPARISON)); - rules.insert(TokenType::GreaterGreater, Rule::new(None, Some(binary), PREC_BITSHIFT)); - rules.insert(TokenType::Less, Rule::new(None, Some(binary), PREC_COMPARISON)); - rules.insert(TokenType::LessEqual, Rule::new(None, Some(binary), PREC_COMPARISON)); - rules.insert(TokenType::LessLess, Rule::new(None, Some(binary), PREC_BITSHIFT)); + rules.insert( + TokenType::EqualEqual, + Rule::new(None, Some(binary), PREC_EQUALITY), + ); + rules.insert( + TokenType::Greater, + Rule::new(None, Some(binary), PREC_COMPARISON), + ); + rules.insert( + TokenType::GreaterEqual, + Rule::new(None, Some(binary), PREC_COMPARISON), + ); + rules.insert( + TokenType::GreaterGreater, + Rule::new(None, Some(binary), PREC_BITSHIFT), + ); + rules.insert( + TokenType::Less, + Rule::new(None, Some(binary), PREC_COMPARISON), + ); + rules.insert( + TokenType::LessEqual, + Rule::new(None, Some(binary), PREC_COMPARISON), + ); + rules.insert( + TokenType::LessLess, + Rule::new(None, Some(binary), PREC_BITSHIFT), + ); rules.insert(TokenType::Identifier, Rule::new(None, None, PREC_NONE)); rules.insert(TokenType::String, Rule::new(Some(literal), None, PREC_NONE)); rules.insert(TokenType::Number, Rule::new(Some(number), None, PREC_NONE)); - rules.insert(TokenType::LogicalAnd, Rule::new(None, Some(binary), PREC_AND)); + rules.insert( + TokenType::LogicalAnd, + Rule::new(None, Some(binary), PREC_AND), + ); rules.insert(TokenType::LogicalOr, Rule::new(None, Some(binary), PREC_OR)); - rules.insert(TokenType::BitAnd, Rule::new(None, Some(binary), PREC_BITAND)); + rules.insert( + TokenType::BitAnd, + Rule::new(None, Some(binary), PREC_BITAND), + ); rules.insert(TokenType::BitOr, Rule::new(None, Some(binary), PREC_BITOR)); - rules.insert(TokenType::BitXor, Rule::new(None, Some(binary), PREC_BITXOR)); + rules.insert( + TokenType::BitXor, + Rule::new(None, Some(binary), PREC_BITXOR), + ); rules.insert(TokenType::Fn, Rule::new(None, None, PREC_NONE)); rules.insert(TokenType::Struct, Rule::new(None, None, PREC_NONE)); rules.insert(TokenType::Else, Rule::new(None, None, PREC_NONE)); diff --git a/src/main.rs b/src/main.rs index fd17330..7d5ab10 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,7 +2,7 @@ fn main() -> anyhow::Result<()> { tracing_subscriber::fmt::init(); - let chunk = crudlang::compiler::compile("\"1\"+\"2\"")?; + let chunk = crudlang::compiler::compile("\"hello \" + 42")?; chunk.disassemble(); let result = crudlang::vm::interpret(chunk); diff --git a/src/scanner.rs b/src/scanner.rs index 5a2c1f4..0d3efd3 100644 --- a/src/scanner.rs +++ b/src/scanner.rs @@ -50,6 +50,7 @@ impl Scanner { '-' => self.add_token(TokenType::Minus), '+' => self.add_token(TokenType::Plus), ':' => self.add_token(TokenType::Colon), + ';' => self.add_token(TokenType::Semicolon), '*' => self.add_token(TokenType::Star), '!' => { let t = if self.match_next('=') { diff --git a/src/tokens.rs b/src/tokens.rs index 26eda9b..1bd429e 100644 --- a/src/tokens.rs +++ b/src/tokens.rs @@ -1,6 +1,6 @@ #[derive(Debug)] pub struct Token { - pub tokentype: TokenType, + pub token_type: TokenType, pub lexeme: String, pub line: usize, } @@ -8,7 +8,7 @@ pub struct Token { impl Token { pub(crate) fn new(tokentype: TokenType, lexeme: String, line: usize) -> Self { Self { - tokentype, + token_type: tokentype, lexeme, line, } @@ -30,6 +30,7 @@ pub(crate) enum TokenType { LeftBracket, RightBracket, Colon, + Semicolon, Comma, Dot, Star, diff --git a/src/value.rs b/src/value.rs index 0b56c67..3d36e00 100644 --- a/src/value.rs +++ b/src/value.rs @@ -2,6 +2,7 @@ use anyhow::anyhow; use chrono::{DateTime, Utc}; use std::cmp::Ordering; use std::collections::HashMap; +use std::fmt::{write, Display, Formatter}; use std::hash::{Hash, Hasher}; use std::ops::{Add, BitAnd, BitOr, BitXor, Div, Mul, Neg, Not, Shl, Shr, Sub}; @@ -22,6 +23,7 @@ pub enum Value { List(Vec), Map(HashMap), Error(String), + Void } impl Into for i32 { @@ -95,6 +97,29 @@ impl Into for HashMap { } } +impl Display for Value { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + match &self { + &Value::U32(v) => write!(f, "{}", v), + &Value::U64(v) => write!(f, "{}", v), + &Value::String(v) => write!(f, "{}", v), + &Value::Bool(v) => write!(f, "{}", v), + &Value::I32(v) => write!(f, "{}", v), + &Value::I64(v) => write!(f, "{}", v), + &Value::F32(v) => write!(f, "{}", v), + &Value::F64(v) => write!(f, "{}", v), + &Value::Char(v) => write!(f, "{}", v), + &Value::Date(v) => write!(f, "{}", v), + &Value::Enum => write!(f, "enum"), + &Value::Struct => write!(f, "struct"), + &Value::List(v) => write!(f, "{:?}", v), + &Value::Map(v) => write!(f, "map"), + &Value::Error(v) => write!(f, "{}", v), + &Value::Void => write!(f, "()"), + } + } +} + impl Neg for &Value { type Output = anyhow::Result; diff --git a/src/vm.rs b/src/vm.rs index 99ab180..d8ef60b 100644 --- a/src/vm.rs +++ b/src/vm.rs @@ -1,7 +1,7 @@ -use anyhow::anyhow; -use tracing::debug; use crate::chunk::Chunk; use crate::value::Value; +use anyhow::anyhow; +use tracing::debug; pub fn interpret(chunk: Chunk) -> Result { let mut vm = Vm { @@ -60,8 +60,11 @@ impl Vm { OP_BITXOR => binary_op(self, |a, b| a ^ b), OP_NEGATE => unary_op(self, |a| -a), OP_RETURN => { - // println!("{:?}", self.pop()); - return Result::Ok(self.pop()); + return if self.stack.is_empty() { + Result::Ok(Value::Void) + } else { + return Result::Ok(self.pop()); + }; } OP_SHL => binary_op(self, |a, b| a << b), OP_SHR => binary_op(self, |a, b| a >> b), @@ -70,6 +73,10 @@ impl Vm { OP_GREATER_EQUAL => binary_op(self, |a, b| Ok(Value::Bool(a >= b))), OP_LESS => binary_op(self, |a, b| Ok(Value::Bool(a < b))), OP_LESS_EQUAL => binary_op(self, |a, b| Ok(Value::Bool(a <= b))), + OP_PRINT => { + let v = self.pop(); + println!("{}", v); + } _ => {} } } @@ -141,3 +148,4 @@ pub const OP_BITOR: u16 = 21; pub const OP_BITXOR: u16 = 22; pub const OP_SHR: u16 = 23; pub const OP_SHL: u16 = 24; +pub const OP_POP: u16 = 25;