From 8d03964f76b53f2ec57691b8c7983495768b1514 Mon Sep 17 00:00:00 2001 From: nils <48135649+Nilstrieb@users.noreply.github.com> Date: Mon, 4 Jul 2022 12:13:00 +0200 Subject: [PATCH] d o c --- doc/src/05_building_block_view.adoc | 7 ++++--- doc/src/06_runtime_view.adoc | 13 ------------- doc/src/08_concepts.adoc | 25 +++++++++++++------------ doc/src/09_architecture_decisions.adoc | 6 +++++- doc/src/10_quality_requirements.adoc | 22 +++++++++++++++++++--- doc/src/config.adoc | 2 ++ src/error.rs | 3 +++ src/interpret.rs | 4 +--- src/ir.rs | 4 ++-- src/main.rs | 4 +++- src/parser.rs | 16 ++++++++-------- 11 files changed, 60 insertions(+), 46 deletions(-) diff --git a/doc/src/05_building_block_view.adoc b/doc/src/05_building_block_view.adoc index 6965d54..3c4fdff 100644 --- a/doc/src/05_building_block_view.adoc +++ b/doc/src/05_building_block_view.adoc @@ -2,18 +2,19 @@ [[section-building-block-view]] - == Building Block View ==== Parser `parser.rs` -Lexes the source code, and then parses those tokens into an abstract syntax tree. +Tokenizes the source code, and then parses those tokens into an abstract syntax tree. [source,rust] ---- -include::{sourcedir}/parser.rs[tag=parse] +include::{sourcedir}/parser.rs[tag=stmt] ---- +The AST accepts arbitrary expressions as arguments to instructions. This allows it to generate better diagnostics later. + ==== Compiler and IR `ir.rs` diff --git a/doc/src/06_runtime_view.adoc b/doc/src/06_runtime_view.adoc index 9b3ead9..63dd6ea 100644 --- a/doc/src/06_runtime_view.adoc +++ b/doc/src/06_runtime_view.adoc @@ -16,16 +16,3 @@ The interpreter follows a classic interpreter architecture. First, the source is Then, a handwritten recursive descent parser parses the token stream. The abstract syntax tree is then given to a small compiler, that compiles it down to a smaller and more limited IR. It also resolves jump labels to offsets. The interpreter then executes this lower level IR. - - -=== - -* __ -* __ - -=== - -=== ... - -=== diff --git a/doc/src/08_concepts.adoc b/doc/src/08_concepts.adoc index a62ef16..5b0f657 100644 --- a/doc/src/08_concepts.adoc +++ b/doc/src/08_concepts.adoc @@ -1,22 +1,23 @@ +:sourcedir: ../../src + [[section-concepts]] == Cross-cutting Concepts +=== Version control +This project uses git and Github for version control. +=== Error handling +A single type is used for handling invalid input. The error has a single message, a span that shows the location in the source code, and optionally some notes or a help message. This message is then printed using the `ariadne` crate. -=== __ +[source,rust] +---- +include::{sourcedir}/error.rs[tag=error] +---- -__ +If the interpreter enters an invalid state, it panics. +=== Unsafe code - -=== __ - -__ - -... - -=== __ - -__ +Currently, `#![forbid(unsafe_code)]` is used in the crate, but unsafe code may be used for performance improvements in the future. diff --git a/doc/src/09_architecture_decisions.adoc b/doc/src/09_architecture_decisions.adoc index be77748..5b24774 100644 --- a/doc/src/09_architecture_decisions.adoc +++ b/doc/src/09_architecture_decisions.adoc @@ -15,4 +15,8 @@ It would be simpler to simply walk over the AST during interpretation, but this * There would still have to be a second pass over the AST to resolve labels. -Therefore, an IR is used to resolve the labels and interpret it. \ No newline at end of file +Therefore, an IR is used to resolve the labels and interpret it. + +=== Depend on `logos` for the lexer + +Don't write a lexer by hand, let `logos` generate it. This saves development time and makes it easier to add new features to the lexer. \ No newline at end of file diff --git a/doc/src/10_quality_requirements.adoc b/doc/src/10_quality_requirements.adoc index e1135b0..52c041e 100644 --- a/doc/src/10_quality_requirements.adoc +++ b/doc/src/10_quality_requirements.adoc @@ -1,13 +1,29 @@ [[section-quality-scenarios]] == Quality Requirements - - +* Parser tests +* Idiomatic Rust code +* Good diagnostics === Quality Tree +[plantuml] +---- +left to right direction +(Quality) --> (maintainability) +(maintainability) --> (1 parser tests) +(maintainability) --> (2 idiomatic rust code) +(Quality) --> (usability) +(usability) --> (3 diagnostics) +---- === Quality Scenarios - +[cols="e,4e" options="header"] +|=== +|ID|Scenario +|1|A developer wants to add a new feature to the parser. It should be ensured that they don't break existing functionality. +|2|A new developer that is already familiar with rust wants to get started contributing to the project. The project should be familiar to them. +|3|Someone wants to use crapderive, but they have syntax errors since they aren't familiar with the language yet. The compiler should help them find the issues and fix them. +|=== \ No newline at end of file diff --git a/doc/src/config.adoc b/doc/src/config.adoc index 334d529..64ef5b3 100644 --- a/doc/src/config.adoc +++ b/doc/src/config.adoc @@ -7,3 +7,5 @@ // where are images located? :imagesdir: ./images + +:sourcedir: ../../src diff --git a/src/error.rs b/src/error.rs index 3de05d9..f3d6b25 100644 --- a/src/error.rs +++ b/src/error.rs @@ -3,12 +3,15 @@ use dbg_pls::DebugPls; use logos::Span; #[derive(Debug, DebugPls)] +// tag::error[] pub struct CompilerError { pub msg: String, pub span: Span, pub notes: Vec<(String, Span)>, pub help: Option, } +// end::error[] + impl CompilerError { pub fn new(msg: String, span: Span, notes: Vec<(String, Span)>, help: Option) -> Self { Self { diff --git a/src/interpret.rs b/src/interpret.rs index 1f501a4..36a0a46 100644 --- a/src/interpret.rs +++ b/src/interpret.rs @@ -212,9 +212,7 @@ impl InterpretCtx { fn write_addr(&mut self, addr: usize, value: u64) { assert!(addr + 7 < self.memory.len()); let bytes = value.to_le_bytes(); - for i in 0..8 { - self.memory[addr + i] = bytes[i]; - } + self.memory[addr..(addr + 8)].copy_from_slice(&bytes[..8]); } fn reg(&self, reg: Register) -> u64 { diff --git a/src/ir.rs b/src/ir.rs index fee4e62..c1de493 100644 --- a/src/ir.rs +++ b/src/ir.rs @@ -157,7 +157,7 @@ impl CompileCtx { nested.span, "save the first result in a temporary register".to_string(), )), - ExprKind::Symbol(_) => return Err(CompilerError::simple( + ExprKind::Symbol(_) => Err(CompilerError::simple( "symbol not allowed here".to_owned(), expr.span, )) @@ -169,7 +169,7 @@ impl CompileCtx { match expr.kind { ExprKind::Number(n) => Ok(Value::Literal(n)), ExprKind::Symbol(_) => { - return Err(CompilerError::simple( + Err(CompilerError::simple( "symbol not allowed here".to_owned(), expr.span, )) diff --git a/src/main.rs b/src/main.rs index b6f7961..f326528 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,3 +1,5 @@ +#![forbid(unsafe_code)] + use std::{io, process}; use crate::error::CompilerError; @@ -22,6 +24,6 @@ fn main() -> Result<(), io::Error> { } fn report_and_exit(file: &str, error: CompilerError) -> ! { - error::report(error, "test.at", &file); + error::report(error, "test.at", file); process::exit(1); } diff --git a/src/parser.rs b/src/parser.rs index 28c2c5e..de492a4 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -63,6 +63,7 @@ impl DebugPls for Stmt { } #[derive(Debug, PartialEq, Eq, DebugPls)] +// tag::stmt[] pub enum StmtKind { Mov { to: Expr, from: Expr }, Movb { to: Expr, from: Expr }, @@ -76,6 +77,7 @@ pub enum StmtKind { Cmp { lhs: Expr, rhs: Expr }, Label { name: String }, } +// end::stmt[] #[derive(Debug, PartialEq, Eq)] pub struct Expr { @@ -145,7 +147,7 @@ where { fn program(&mut self) -> Result> { let mut stmts = Vec::new(); - while let Ok(_) = self.peek() { + while self.peek().is_ok() { let stmt = self.stmt()?; stmts.push(stmt); } @@ -220,7 +222,7 @@ where } Token::Label(name) => { let name = name - .strip_suffix(":") + .strip_suffix(':') .expect("lexer produced invalid label") .to_owned(); stmt(span, StmtKind::Label { name }) @@ -232,7 +234,7 @@ where Token::Word(word) => { return Err(CompilerError::new( "{word}".to_string(), - span.clone(), + span, vec![], Some(format!("Consider using a label instead: `{}:`", word)), )) @@ -253,7 +255,7 @@ where } Token::Number(n) => expr(ExprKind::Number(n), span), Token::Word(name) => { - if let Some(r_number) = name.strip_prefix("r") { + if let Some(r_number) = name.strip_prefix('r') { if let Ok(n) = r_number.parse::() { if n > 15 { return Err(CompilerError::new( @@ -286,17 +288,15 @@ where } fn peek(&mut self) -> Result<&(Token<'a>, Span)> { - self.iter.peek().ok_or(CompilerError::eof()) + self.iter.peek().ok_or_else(CompilerError::eof) } fn next(&mut self) -> Result<(Token<'a>, Span)> { - self.iter.next().ok_or(CompilerError::eof()) + self.iter.next().ok_or_else(CompilerError::eof) } } -// tag::parse[] pub fn parse(src: &str) -> Result> { - // end::parse[] let lexer = lex(src).spanned(); let mut parser = Parser { iter: lexer.peekable(),