From 9e643b8acdb1cdab27264f069139457a010bf992 Mon Sep 17 00:00:00 2001 From: Nilstrieb <48135649+Nilstrieb@users.noreply.github.com> Date: Wed, 29 Dec 2021 20:12:45 +0100 Subject: [PATCH] print and more --- README.md | 6 +++ src/ast.rs | 1 + src/bytecode.rs | 5 ++ src/compile.rs | 126 +++++++++++++++++++++++++++++++--------------- src/errors.rs | 10 +--- src/lex.rs | 10 ++-- src/parse/mod.rs | 15 ++++++ src/parse/test.rs | 22 ++++++++ test.sl | 9 +++- 9 files changed, 150 insertions(+), 54 deletions(-) diff --git a/README.md b/README.md index d13cb1e..39df086 100644 --- a/README.md +++ b/README.md @@ -39,6 +39,12 @@ let obj = {}; obj.hi = "hi!"; ``` +There is the `print` statement to print a value, but this will be removed +```rust +let name = "nils"; +print name; +``` + Functions are first class ```rust diff --git a/src/ast.rs b/src/ast.rs index 5eb8192..ab65863 100644 --- a/src/ast.rs +++ b/src/ast.rs @@ -31,6 +31,7 @@ pub enum Stmt { Return(Option, Span), Block(Block), Expr(Expr), + Print(Expr, Span), } #[derive(Debug, Clone, PartialEq)] diff --git a/src/bytecode.rs b/src/bytecode.rs index ce67516..7842ad2 100644 --- a/src/bytecode.rs +++ b/src/bytecode.rs @@ -1,3 +1,4 @@ +use crate::errors::Span; use crate::value::{HashMap, Symbol}; use std::rc::Rc; @@ -5,6 +6,7 @@ use std::rc::Rc; pub struct FnBlock { pub code: Vec, pub stack_sizes: Vec, + pub spans: Vec, pub arity: u8, } @@ -32,6 +34,9 @@ pub enum Instr { CmpLessEq, CmpEq, CmpNotEq, + + /// Println the value on top of the stack + Print, } #[derive(Debug)] diff --git a/src/compile.rs b/src/compile.rs index de042a2..a95c72a 100644 --- a/src/compile.rs +++ b/src/compile.rs @@ -4,16 +4,47 @@ use crate::ast::{ }; use crate::bytecode::{FnBlock, Instr, Value}; use crate::errors::{CompilerError, Span}; -use crate::value::HashMap; +use crate::value::{HashMap, Symbol}; +use std::cell::RefCell; +use std::rc::Rc; type CResult = Result; +#[derive(Debug, Default)] +struct Env { + locals: HashMap, + outer: Option>>, +} + +impl Env { + fn lookup_local(&self, name: &Ident) -> CResult { + fn lookup_inner(env: &Env, name: &Ident) -> Option { + env.locals.get(&name.sym).copied().or_else(|| { + env.outer + .as_ref() + .map(|outer| lookup_inner(&outer.borrow(), name)) + .flatten() + }) + } + + lookup_inner(self, name) + .ok_or_else(|| CompileError::new(name.span, format!("variable {} not found", name.sym))) + } + + fn new_inner(outer: Rc>) -> Rc> { + Rc::new(RefCell::new(Self { + locals: HashMap::default(), + outer: Some(outer), + })) + } +} + #[derive(Debug, Default)] struct Compiler { blocks: Vec, current_block: usize, /// the current local variables that are in scope, only needed for compiling - locals: Vec>, + env: Rc>, } pub fn compile(ast: &Program) -> Result, CompileError> { @@ -29,12 +60,11 @@ impl Compiler { let global_block = FnBlock::default(); self.blocks.push(global_block); self.current_block = self.blocks.len() - 1; - self.locals.push(HashMap::default()); - self.compile_fn_block(&ast.0)?; + self.compile_stmts(&ast.0)?; Ok(()) } - fn compile_fn_block(&mut self, stmts: &[Stmt]) -> CResult<()> { + fn compile_stmts(&mut self, stmts: &[Stmt]) -> CResult<()> { for stmt in stmts { match stmt { Stmt::Declaration(inner) => self.compile_declaration(inner), @@ -45,6 +75,7 @@ impl Compiler { Stmt::While(inner) => self.compile_while(inner), Stmt::Break(span) => self.compile_break(*span), Stmt::Return(expr, span) => self.compile_return(expr, *span), + Stmt::Print(expr, span) => self.compile_print(expr, *span), Stmt::Block(inner) => self.compile_block(inner), Stmt::Expr(inner) => self.compile_expr(inner), }?; @@ -58,7 +89,10 @@ impl Compiler { self.compile_expr(&declaration.init)?; // Now just remember that the value at this stack location is this variable name let stack_pos = self.current_stack_top(); - self.locals().insert(declaration.name.clone(), stack_pos); + self.env + .borrow_mut() + .locals + .insert(declaration.name.sym.clone(), stack_pos); Ok(()) } @@ -68,11 +102,15 @@ impl Compiler { _ => todo!(), }; - let stack_pos = self.lookup_local(local)?; + let stack_pos = self.env.borrow().lookup_local(local)?; self.compile_expr(&assignment.rhs)?; - self.push_instr(Instr::Store(stack_pos), StackChange::Shrink); + self.push_instr( + Instr::Store(stack_pos), + StackChange::Shrink, + assignment.span, + ); Ok(()) } @@ -101,8 +139,23 @@ impl Compiler { todo!() } - fn compile_block(&mut self, _: &Block) -> CResult<()> { - todo!() + fn compile_print(&mut self, expr: &Expr, span: Span) -> CResult<()> { + self.compile_expr(expr)?; + + self.push_instr(Instr::Print, StackChange::Shrink, span); + + Ok(()) + } + + fn compile_block(&mut self, block: &Block) -> CResult<()> { + let next_env = Env::new_inner(self.env.clone()); + self.env = next_env; + + self.compile_stmts(&block.stmts)?; + + let outer = self.env.borrow().outer.clone().expect("outer env got lost"); + self.env = outer; + Ok(()) } fn compile_expr(&mut self, expr: &Expr) -> CResult<()> { @@ -116,8 +169,8 @@ impl Compiler { } fn compile_expr_ident(&mut self, name: &Ident) -> CResult<()> { - let offset = self.lookup_local(name)?; - self.push_instr(Instr::Load(offset), StackChange::Grow); + let offset = self.env.borrow().lookup_local(name)?; + self.push_instr(Instr::Load(offset), StackChange::Grow, name.span); Ok(()) } @@ -137,26 +190,30 @@ impl Compiler { Literal::Null(_) => Value::Null, }; - self.push_instr(Instr::PushVal(Box::new(value)), StackChange::Grow); + self.push_instr( + Instr::PushVal(Box::new(value)), + StackChange::Grow, + lit.span(), + ); Ok(()) } - fn compile_expr_unary(&mut self, inner: &UnaryOp) -> CResult<()> { - self.compile_expr(&inner.expr)?; + fn compile_expr_unary(&mut self, unary: &UnaryOp) -> CResult<()> { + self.compile_expr(&unary.expr)?; // not and neg compile to the same instruction - self.push_instr(Instr::Neg, StackChange::None); + self.push_instr(Instr::Neg, StackChange::None, unary.span); Ok(()) } - fn compile_expr_binary(&mut self, inner: &BinaryOp) -> CResult<()> { + fn compile_expr_binary(&mut self, binary: &BinaryOp) -> CResult<()> { // todo: is this the correct ordering? - self.compile_expr(&inner.lhs)?; - self.compile_expr(&inner.rhs)?; + self.compile_expr(&binary.lhs)?; + self.compile_expr(&binary.rhs)?; - let instruction = match inner.kind { + let instruction = match binary.kind { BinaryOpKind::Add => Instr::BinAdd, BinaryOpKind::And => Instr::BinAnd, BinaryOpKind::Or => Instr::BinOr, @@ -172,7 +229,7 @@ impl Compiler { BinaryOpKind::Mod => Instr::BinMod, }; - self.push_instr(instruction, StackChange::Shrink); + self.push_instr(instruction, StackChange::Shrink, binary.span); Ok(()) } @@ -181,30 +238,13 @@ impl Compiler { todo!() } - fn locals(&mut self) -> &mut HashMap { - self.locals.last_mut().expect("no locals found") - } - - fn lookup_local(&self, name: &Ident) -> CResult { - for locals in self.locals.iter().rev() { - dbg!("searching"); - if let Some(&position) = locals.get(name) { - return Ok(position); - } - } - - Err(CompileError::new( - name.span, - format!("variable {} not found", name.sym), - )) - } - fn current_stack_top(&self) -> usize { let block = &self.blocks[self.current_block]; - *block.stack_sizes.last().expect("empty stack") + // we want the stack position, not the size, so the `- 1` + *block.stack_sizes.last().expect("empty stack") - 1 } - fn push_instr(&mut self, instr: Instr, stack_change: StackChange) { + fn push_instr(&mut self, instr: Instr, stack_change: StackChange, span: Span) { let block = &mut self.blocks[self.current_block]; let stack_top = block.stack_sizes.last().copied().unwrap_or(0); let new_stack_top = stack_top as isize + stack_change as isize; @@ -213,6 +253,10 @@ impl Compiler { block.code.push(instr); block.stack_sizes.push(new_stack_top); + block.spans.push(span); + + debug_assert_eq!(block.code.len(), block.stack_sizes.len()); + debug_assert_eq!(block.code.len(), block.spans.len()); } } diff --git a/src/errors.rs b/src/errors.rs index f6fc60a..ecee830 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -75,18 +75,12 @@ where let mut chars = 0; let lines = source.split_inclusive('\n').enumerate(); for (idx, line) in lines { - if chars + line.len() + 1 > error.span().start { + if chars + line.len() > error.span().start { let offset_on_line = error.span().start - chars; println!("{}error: {}{}", RED, error.message(), RESET); println!(" {}|{}", CYAN, RESET); - println!( - "{}{:>5} |{} {}", - CYAN, - idx + 1, - RESET, - &line[..line.len() - 1] - ); + println!("{}{:>5} |{} {}", CYAN, idx + 1, RESET, line); print!(" {}|{} ", CYAN, RESET); println!( "{}{}{}{}", diff --git a/src/lex.rs b/src/lex.rs index 40e3c30..bb5b32c 100644 --- a/src/lex.rs +++ b/src/lex.rs @@ -32,6 +32,7 @@ impl<'code> Token<'code> { pub enum TokenType<'code> { // keywords Let, + Print, Fn, If, Else, @@ -341,6 +342,7 @@ fn keyword_or_ident(name: &str) -> TokenType { b'a' if len == 3 && bs[1..3] == *b"nd" => TokenType::And, // or b'o' if len == 2 && bs[1] == b'r' => TokenType::Or, + b'p' if len == 5 && bs[1..5] == *b"rint" => TokenType::Print, _ => TokenType::Ident(name), } } @@ -572,9 +574,9 @@ mod test { #[test] fn keywords() { lex_test( - "let fn if else loop while break for true false null and not or", + "let fn if else loop while break for true false null and not or print", vec![ - Let, Fn, If, Else, Loop, While, Break, For, True, False, Null, And, Not, Or, + Let, Fn, If, Else, Loop, While, Break, For, True, False, Null, And, Not, Or, Print, ], ) } @@ -635,7 +637,7 @@ mod test { let me out ._. fn world() { if number == 5 or true == false and not false { - print("Hello \\ World!") + println("Hello \\ World!") } }"#, vec![ @@ -670,7 +672,7 @@ mod test { Not, False, BraceO, - Ident("print"), + Ident("println"), ParenO, String("Hello \\ World!".to_string()), ParenC, diff --git a/src/parse/mod.rs b/src/parse/mod.rs index 3d222c2..6480473 100644 --- a/src/parse/mod.rs +++ b/src/parse/mod.rs @@ -111,6 +111,7 @@ impl<'code> Parser<'code> { TokenType::While => self.while_stmt(), TokenType::Break => self.break_stmt(), TokenType::Return => self.return_stmt(), + TokenType::Print => self.print_stmt(), TokenType::BraceO => Ok(Stmt::Block(self.block()?)), _ => { let stmt = self.assignment()?; @@ -291,6 +292,20 @@ impl<'code> Parser<'code> { } } + fn print_stmt(&mut self) -> ParseResult<'code, Stmt> { + enter_parse!(self); + + let print_span = self.expect(TokenType::Print)?.span; + + let expr = self.expression()?; + + let semi_span = self.expect(TokenType::Semi)?.span; + + exit_parse!(self); + + Ok(Stmt::Print(expr, print_span.extend(semi_span))) + } + fn assignment(&mut self) -> ParseResult<'code, Stmt> { enter_parse!(self); diff --git a/src/parse/test.rs b/src/parse/test.rs index 4bf72df..2374ac4 100644 --- a/src/parse/test.rs +++ b/src/parse/test.rs @@ -292,6 +292,28 @@ mod r#if { } } +mod print { + use super::prelude::*; + + fn parse_print(tokens: Vec) -> Stmt { + let mut parser = parser(tokens); + parser.print_stmt().unwrap() + } + + #[test] + fn print_true() { + let tokens = [Print, True].map(token).into(); + let ast = parse_print(tokens); + assert_eq!( + Stmt::Print( + Expr::Literal(Literal::Boolean(true, Span::dummy())), + Span::dummy() + ), + ast + ); + } +} + mod r#while { use super::prelude::*; diff --git a/test.sl b/test.sl index 8ac0832..cad3cf0 100644 --- a/test.sl +++ b/test.sl @@ -1,3 +1,10 @@ let x = 2 * 3; -let tz = x * 5; \ No newline at end of file +let tz = x * 5; + +{ + let uwu = 5; + uwu; +} + +print 5 + 5; \ No newline at end of file