mirror of
https://github.com/Noratrieb/dilaria.git
synced 2026-01-16 10:25:02 +01:00
print and more
This commit is contained in:
parent
ebbc675ffd
commit
9e643b8acd
9 changed files with 150 additions and 54 deletions
|
|
@ -39,6 +39,12 @@ let obj = {};
|
||||||
obj.hi = "hi!";
|
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
|
Functions are first class
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
|
|
|
||||||
|
|
@ -31,6 +31,7 @@ pub enum Stmt {
|
||||||
Return(Option<Expr>, Span),
|
Return(Option<Expr>, Span),
|
||||||
Block(Block),
|
Block(Block),
|
||||||
Expr(Expr),
|
Expr(Expr),
|
||||||
|
Print(Expr, Span),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
use crate::errors::Span;
|
||||||
use crate::value::{HashMap, Symbol};
|
use crate::value::{HashMap, Symbol};
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
|
@ -5,6 +6,7 @@ use std::rc::Rc;
|
||||||
pub struct FnBlock {
|
pub struct FnBlock {
|
||||||
pub code: Vec<Instr>,
|
pub code: Vec<Instr>,
|
||||||
pub stack_sizes: Vec<usize>,
|
pub stack_sizes: Vec<usize>,
|
||||||
|
pub spans: Vec<Span>,
|
||||||
pub arity: u8,
|
pub arity: u8,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -32,6 +34,9 @@ pub enum Instr {
|
||||||
CmpLessEq,
|
CmpLessEq,
|
||||||
CmpEq,
|
CmpEq,
|
||||||
CmpNotEq,
|
CmpNotEq,
|
||||||
|
|
||||||
|
/// Println the value on top of the stack
|
||||||
|
Print,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
|
|
||||||
126
src/compile.rs
126
src/compile.rs
|
|
@ -4,16 +4,47 @@ use crate::ast::{
|
||||||
};
|
};
|
||||||
use crate::bytecode::{FnBlock, Instr, Value};
|
use crate::bytecode::{FnBlock, Instr, Value};
|
||||||
use crate::errors::{CompilerError, Span};
|
use crate::errors::{CompilerError, Span};
|
||||||
use crate::value::HashMap;
|
use crate::value::{HashMap, Symbol};
|
||||||
|
use std::cell::RefCell;
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
type CResult<T> = Result<T, CompileError>;
|
type CResult<T> = Result<T, CompileError>;
|
||||||
|
|
||||||
|
#[derive(Debug, Default)]
|
||||||
|
struct Env {
|
||||||
|
locals: HashMap<Symbol, usize>,
|
||||||
|
outer: Option<Rc<RefCell<Env>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Env {
|
||||||
|
fn lookup_local(&self, name: &Ident) -> CResult<usize> {
|
||||||
|
fn lookup_inner(env: &Env, name: &Ident) -> Option<usize> {
|
||||||
|
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<RefCell<Self>>) -> Rc<RefCell<Self>> {
|
||||||
|
Rc::new(RefCell::new(Self {
|
||||||
|
locals: HashMap::default(),
|
||||||
|
outer: Some(outer),
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug, Default)]
|
||||||
struct Compiler {
|
struct Compiler {
|
||||||
blocks: Vec<FnBlock>,
|
blocks: Vec<FnBlock>,
|
||||||
current_block: usize,
|
current_block: usize,
|
||||||
/// the current local variables that are in scope, only needed for compiling
|
/// the current local variables that are in scope, only needed for compiling
|
||||||
locals: Vec<HashMap<Ident, usize>>,
|
env: Rc<RefCell<Env>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn compile(ast: &Program) -> Result<Vec<FnBlock>, CompileError> {
|
pub fn compile(ast: &Program) -> Result<Vec<FnBlock>, CompileError> {
|
||||||
|
|
@ -29,12 +60,11 @@ impl Compiler {
|
||||||
let global_block = FnBlock::default();
|
let global_block = FnBlock::default();
|
||||||
self.blocks.push(global_block);
|
self.blocks.push(global_block);
|
||||||
self.current_block = self.blocks.len() - 1;
|
self.current_block = self.blocks.len() - 1;
|
||||||
self.locals.push(HashMap::default());
|
self.compile_stmts(&ast.0)?;
|
||||||
self.compile_fn_block(&ast.0)?;
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn compile_fn_block(&mut self, stmts: &[Stmt]) -> CResult<()> {
|
fn compile_stmts(&mut self, stmts: &[Stmt]) -> CResult<()> {
|
||||||
for stmt in stmts {
|
for stmt in stmts {
|
||||||
match stmt {
|
match stmt {
|
||||||
Stmt::Declaration(inner) => self.compile_declaration(inner),
|
Stmt::Declaration(inner) => self.compile_declaration(inner),
|
||||||
|
|
@ -45,6 +75,7 @@ impl Compiler {
|
||||||
Stmt::While(inner) => self.compile_while(inner),
|
Stmt::While(inner) => self.compile_while(inner),
|
||||||
Stmt::Break(span) => self.compile_break(*span),
|
Stmt::Break(span) => self.compile_break(*span),
|
||||||
Stmt::Return(expr, span) => self.compile_return(expr, *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::Block(inner) => self.compile_block(inner),
|
||||||
Stmt::Expr(inner) => self.compile_expr(inner),
|
Stmt::Expr(inner) => self.compile_expr(inner),
|
||||||
}?;
|
}?;
|
||||||
|
|
@ -58,7 +89,10 @@ impl Compiler {
|
||||||
self.compile_expr(&declaration.init)?;
|
self.compile_expr(&declaration.init)?;
|
||||||
// Now just remember that the value at this stack location is this variable name
|
// Now just remember that the value at this stack location is this variable name
|
||||||
let stack_pos = self.current_stack_top();
|
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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -68,11 +102,15 @@ impl Compiler {
|
||||||
_ => todo!(),
|
_ => todo!(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let stack_pos = self.lookup_local(local)?;
|
let stack_pos = self.env.borrow().lookup_local(local)?;
|
||||||
|
|
||||||
self.compile_expr(&assignment.rhs)?;
|
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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
@ -101,8 +139,23 @@ impl Compiler {
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn compile_block(&mut self, _: &Block) -> CResult<()> {
|
fn compile_print(&mut self, expr: &Expr, span: Span) -> CResult<()> {
|
||||||
todo!()
|
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<()> {
|
fn compile_expr(&mut self, expr: &Expr) -> CResult<()> {
|
||||||
|
|
@ -116,8 +169,8 @@ impl Compiler {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn compile_expr_ident(&mut self, name: &Ident) -> CResult<()> {
|
fn compile_expr_ident(&mut self, name: &Ident) -> CResult<()> {
|
||||||
let offset = self.lookup_local(name)?;
|
let offset = self.env.borrow().lookup_local(name)?;
|
||||||
self.push_instr(Instr::Load(offset), StackChange::Grow);
|
self.push_instr(Instr::Load(offset), StackChange::Grow, name.span);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -137,26 +190,30 @@ impl Compiler {
|
||||||
Literal::Null(_) => Value::Null,
|
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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn compile_expr_unary(&mut self, inner: &UnaryOp) -> CResult<()> {
|
fn compile_expr_unary(&mut self, unary: &UnaryOp) -> CResult<()> {
|
||||||
self.compile_expr(&inner.expr)?;
|
self.compile_expr(&unary.expr)?;
|
||||||
|
|
||||||
// not and neg compile to the same instruction
|
// 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(())
|
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?
|
// todo: is this the correct ordering?
|
||||||
self.compile_expr(&inner.lhs)?;
|
self.compile_expr(&binary.lhs)?;
|
||||||
self.compile_expr(&inner.rhs)?;
|
self.compile_expr(&binary.rhs)?;
|
||||||
|
|
||||||
let instruction = match inner.kind {
|
let instruction = match binary.kind {
|
||||||
BinaryOpKind::Add => Instr::BinAdd,
|
BinaryOpKind::Add => Instr::BinAdd,
|
||||||
BinaryOpKind::And => Instr::BinAnd,
|
BinaryOpKind::And => Instr::BinAnd,
|
||||||
BinaryOpKind::Or => Instr::BinOr,
|
BinaryOpKind::Or => Instr::BinOr,
|
||||||
|
|
@ -172,7 +229,7 @@ impl Compiler {
|
||||||
BinaryOpKind::Mod => Instr::BinMod,
|
BinaryOpKind::Mod => Instr::BinMod,
|
||||||
};
|
};
|
||||||
|
|
||||||
self.push_instr(instruction, StackChange::Shrink);
|
self.push_instr(instruction, StackChange::Shrink, binary.span);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
@ -181,30 +238,13 @@ impl Compiler {
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn locals(&mut self) -> &mut HashMap<Ident, usize> {
|
|
||||||
self.locals.last_mut().expect("no locals found")
|
|
||||||
}
|
|
||||||
|
|
||||||
fn lookup_local(&self, name: &Ident) -> CResult<usize> {
|
|
||||||
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 {
|
fn current_stack_top(&self) -> usize {
|
||||||
let block = &self.blocks[self.current_block];
|
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 block = &mut self.blocks[self.current_block];
|
||||||
let stack_top = block.stack_sizes.last().copied().unwrap_or(0);
|
let stack_top = block.stack_sizes.last().copied().unwrap_or(0);
|
||||||
let new_stack_top = stack_top as isize + stack_change as isize;
|
let new_stack_top = stack_top as isize + stack_change as isize;
|
||||||
|
|
@ -213,6 +253,10 @@ impl Compiler {
|
||||||
|
|
||||||
block.code.push(instr);
|
block.code.push(instr);
|
||||||
block.stack_sizes.push(new_stack_top);
|
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());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -75,18 +75,12 @@ where
|
||||||
let mut chars = 0;
|
let mut chars = 0;
|
||||||
let lines = source.split_inclusive('\n').enumerate();
|
let lines = source.split_inclusive('\n').enumerate();
|
||||||
for (idx, line) in lines {
|
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;
|
let offset_on_line = error.span().start - chars;
|
||||||
|
|
||||||
println!("{}error: {}{}", RED, error.message(), RESET);
|
println!("{}error: {}{}", RED, error.message(), RESET);
|
||||||
println!(" {}|{}", CYAN, RESET);
|
println!(" {}|{}", CYAN, RESET);
|
||||||
println!(
|
println!("{}{:>5} |{} {}", CYAN, idx + 1, RESET, line);
|
||||||
"{}{:>5} |{} {}",
|
|
||||||
CYAN,
|
|
||||||
idx + 1,
|
|
||||||
RESET,
|
|
||||||
&line[..line.len() - 1]
|
|
||||||
);
|
|
||||||
print!(" {}|{} ", CYAN, RESET);
|
print!(" {}|{} ", CYAN, RESET);
|
||||||
println!(
|
println!(
|
||||||
"{}{}{}{}",
|
"{}{}{}{}",
|
||||||
|
|
|
||||||
10
src/lex.rs
10
src/lex.rs
|
|
@ -32,6 +32,7 @@ impl<'code> Token<'code> {
|
||||||
pub enum TokenType<'code> {
|
pub enum TokenType<'code> {
|
||||||
// keywords
|
// keywords
|
||||||
Let,
|
Let,
|
||||||
|
Print,
|
||||||
Fn,
|
Fn,
|
||||||
If,
|
If,
|
||||||
Else,
|
Else,
|
||||||
|
|
@ -341,6 +342,7 @@ fn keyword_or_ident(name: &str) -> TokenType {
|
||||||
b'a' if len == 3 && bs[1..3] == *b"nd" => TokenType::And,
|
b'a' if len == 3 && bs[1..3] == *b"nd" => TokenType::And,
|
||||||
// or
|
// or
|
||||||
b'o' if len == 2 && bs[1] == b'r' => TokenType::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),
|
_ => TokenType::Ident(name),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -572,9 +574,9 @@ mod test {
|
||||||
#[test]
|
#[test]
|
||||||
fn keywords() {
|
fn keywords() {
|
||||||
lex_test(
|
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![
|
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 ._.
|
let me out ._.
|
||||||
fn world() {
|
fn world() {
|
||||||
if number == 5 or true == false and not false {
|
if number == 5 or true == false and not false {
|
||||||
print("Hello \\ World!")
|
println("Hello \\ World!")
|
||||||
}
|
}
|
||||||
}"#,
|
}"#,
|
||||||
vec![
|
vec![
|
||||||
|
|
@ -670,7 +672,7 @@ mod test {
|
||||||
Not,
|
Not,
|
||||||
False,
|
False,
|
||||||
BraceO,
|
BraceO,
|
||||||
Ident("print"),
|
Ident("println"),
|
||||||
ParenO,
|
ParenO,
|
||||||
String("Hello \\ World!".to_string()),
|
String("Hello \\ World!".to_string()),
|
||||||
ParenC,
|
ParenC,
|
||||||
|
|
|
||||||
|
|
@ -111,6 +111,7 @@ impl<'code> Parser<'code> {
|
||||||
TokenType::While => self.while_stmt(),
|
TokenType::While => self.while_stmt(),
|
||||||
TokenType::Break => self.break_stmt(),
|
TokenType::Break => self.break_stmt(),
|
||||||
TokenType::Return => self.return_stmt(),
|
TokenType::Return => self.return_stmt(),
|
||||||
|
TokenType::Print => self.print_stmt(),
|
||||||
TokenType::BraceO => Ok(Stmt::Block(self.block()?)),
|
TokenType::BraceO => Ok(Stmt::Block(self.block()?)),
|
||||||
_ => {
|
_ => {
|
||||||
let stmt = self.assignment()?;
|
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> {
|
fn assignment(&mut self) -> ParseResult<'code, Stmt> {
|
||||||
enter_parse!(self);
|
enter_parse!(self);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -292,6 +292,28 @@ mod r#if {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mod print {
|
||||||
|
use super::prelude::*;
|
||||||
|
|
||||||
|
fn parse_print(tokens: Vec<Token>) -> 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 {
|
mod r#while {
|
||||||
use super::prelude::*;
|
use super::prelude::*;
|
||||||
|
|
||||||
|
|
|
||||||
9
test.sl
9
test.sl
|
|
@ -1,3 +1,10 @@
|
||||||
let x = 2 * 3;
|
let x = 2 * 3;
|
||||||
|
|
||||||
let tz = x * 5;
|
let tz = x * 5;
|
||||||
|
|
||||||
|
{
|
||||||
|
let uwu = 5;
|
||||||
|
uwu;
|
||||||
|
}
|
||||||
|
|
||||||
|
print 5 + 5;
|
||||||
Loading…
Add table
Add a link
Reference in a new issue