first documentation run

This commit is contained in:
Sebastian Hugentobler 2025-05-26 12:24:35 +02:00
parent e065108f58
commit f59e6a5fd5
Signed by: shu
SSH key fingerprint: SHA256:ppcx6MlixdNZd5EUM1nkHOKoyQYoJwzuQKXM6J/t66M
21 changed files with 64 additions and 23 deletions

View file

@ -13,7 +13,7 @@ pub enum CallingError {
CallInterpretationFailed(String),
}
/// Implementations can be called.
/// Interface for callable objects in Lox (functions, methods, classes).
pub trait Callable {
fn name(&self) -> String;
fn arity(&self) -> usize;

View file

@ -21,6 +21,7 @@ impl Display for Class {
}
impl Class {
/// Create a new class with the given name, optional superclass, and methods.
pub fn new(
name: String,
superclass: Option<Class>,
@ -35,6 +36,8 @@ impl Class {
}
}
/// Find a method by name in this class or any of its superclasses.
/// This is method inheritance.
pub fn find_method(&self, name: &str) -> Option<&Function> {
self.methods.get(name).or_else(|| {
if let Some(superclass) = &self.superclass {

View file

@ -13,6 +13,7 @@ pub struct Cli {
pub command: Commands,
}
/// Configuration for running a Lox source file
#[derive(Args, Clone)]
pub struct RunConfig {
/// Path to Lox source file.
@ -20,6 +21,7 @@ pub struct RunConfig {
pub source: PathBuf,
}
/// Available commands for the Lox interpreter
#[derive(Subcommand)]
pub enum Commands {
/// Run a Lox source file.

View file

@ -12,8 +12,11 @@ pub enum EnvironmentError {
InvalidDistance,
}
/// Environment mapping variable names to their respective values. Can have an optional enclosing
/// environment. The outermost (global) environment has no enclosing one.
/// Environment mapping variable names to their respective values.
/// Can have an optional enclosing environment. The outermost (global)
/// environment has no enclosing one.
///
/// This is for lexical scoping.
#[derive(Default, Debug, Clone)]
pub struct Environment {
values: HashMap<String, Value>,
@ -52,6 +55,8 @@ impl Environment {
}
}
/// Assign a value to a variable at a specific lexical distance (number of scopes away).
/// Used by the resolver to handle closures correctly.
pub fn assign_at(
&mut self,
distance: usize,
@ -79,6 +84,8 @@ impl Environment {
}
}
/// Get a variable's value from an environment at a specific lexical distance.
/// Used by the resolver to handle closures correctly.
pub fn get_at(&self, distance: usize, token: &Token) -> Result<Value, EnvironmentError> {
if let Some(v) = self.ancestor(distance)?.values.get(token.lexeme.as_str()) {
Ok(v.clone())
@ -90,6 +97,7 @@ impl Environment {
}
}
/// Find an environment at a specific lexical distance from the current one.
fn ancestor(&self, distance: usize) -> Result<Environment, EnvironmentError> {
let mut environment = self.clone();

View file

@ -1,6 +1,6 @@
use crate::token::{self, Token};
/// Enumeration of all types of expressions.
/// Enumeration of all types of expressions in the Lox language.
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum Expression {
Assign {

View file

@ -10,7 +10,7 @@ use crate::{
use core::fmt::Debug;
use std::{cell::RefCell, fmt::Display, rc::Rc};
/// A lox function.
/// A Lox function with name, parameters, body, and closure environment.
#[derive(Debug, Clone)]
pub struct Function {
pub name: Token,
@ -62,6 +62,8 @@ impl Callable for Function {
}
impl Function {
/// Bind a method to an instance, creating a new function with "this" defined in its environment.
/// This allows methods to access the instance they belong to.
pub fn bind(&self, instance: Rc<RefCell<Instance>>) -> Self {
let mut env = Environment::with_enclosing(self.closure.clone());
env.define("this".to_string(), Value::Instance(instance));

View file

@ -14,6 +14,7 @@ pub enum InstanceError {
NoSuchProperty(usize, String),
}
/// An instance of a Lox class, containing fields and access to methods.
#[derive(Debug, Clone, PartialEq)]
pub struct Instance {
class: Class,
@ -27,6 +28,7 @@ impl Display for Instance {
}
impl Instance {
/// Create a new instance of a class with empty fields.
pub fn new(class: Class) -> Rc<RefCell<Self>> {
Rc::new(RefCell::new(Self {
class,
@ -34,6 +36,8 @@ impl Instance {
}))
}
/// Get a property from this instance. First check for the name in fields, then methods.
/// If a method is found, bind it to this instance.
pub fn get(&self, name: &Token, instance: Rc<RefCell<Self>>) -> Result<Value, InstanceError> {
if let Some(field) = self.fields.get(name.lexeme.as_str()) {
Ok(field.clone())
@ -50,6 +54,7 @@ impl Instance {
}
}
/// Set a field on this instance to the given value.
pub fn set(&mut self, name: &Token, value: Value) {
self.fields.insert(name.lexeme.clone(), value);
}

View file

@ -46,7 +46,7 @@ pub enum InterpreterError {
EnvironmentError(#[from] crate::environment::EnvironmentError),
}
/// Interpreter for the Lox language.
/// Interpreter for the Lox language that executes statements and evaluates expressions.
#[derive(Debug)]
pub struct Interpreter {
pub globals: Rc<RefCell<Environment>>,
@ -126,7 +126,7 @@ impl Interpreter {
}
/// Execute all statements within a block, using a new environment (with the old one as the
/// enclosing one). Immediately return the value if a Return Value is encountered after
/// enclosing one). Immediately return the value of a Return Value when it is encountered after
/// execution of a statement.
pub fn block(
&mut self,

View file

@ -11,6 +11,7 @@ use crate::{
value::{CallableType, Value},
};
/// Native clock function that returns the current time in seconds since the Unix epoch
struct Clock;
impl Callable for Clock {
@ -22,6 +23,7 @@ impl Callable for Clock {
0
}
/// Return the current system time in seconds since the Unix epoch
fn call(
&self,
_interpreter: &mut Interpreter,
@ -36,6 +38,7 @@ impl Callable for Clock {
}
}
/// Return all native functions available to the Lox interpreter
pub fn all() -> Vec<(String, Value)> {
vec![(
"clock".into(),

View file

@ -71,7 +71,7 @@ pub enum ParserError {
SuperclassMethodNameExpected(usize),
}
/// Parse the Lox language.
/// Parse the Lox language tokens into an abstract syntax tree.
#[derive(Debug, Clone)]
struct Parser {
current: usize,
@ -655,7 +655,8 @@ impl Parser {
}
}
/// Try to parse the provided tokens into an AST.
/// Try to parse the provided tokens into an Abstract Syntax Tree (AST).
/// Return a list of statements that can be executed by the interpreter.
pub fn ast(tokens: Vec<Token>) -> Result<Vec<Statement>, ParserError> {
let mut parser = Parser::new(tokens)?;
parser.run()

View file

@ -37,6 +37,7 @@ enum ClassType {
Subclass,
}
/// Resolve variable references and performs static analysis before interpretation.
#[derive(Debug)]
pub struct Resolver<'a> {
scopes: Vec<HashMap<String, bool>>,

View file

@ -1,6 +1,6 @@
use crate::{expression::Expression, token::Token};
/// Enumeration of all types of statements.
/// Enumeration of all types of statements in the Lox language.
#[derive(Debug, Clone, PartialEq)]
pub enum Statement {
Block(Vec<Statement>),

View file

@ -56,12 +56,13 @@ pub enum TokenType {
Eof,
}
/// Literal value.
/// Literal value in the Lox language.
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub enum Literal {
/// String literal.
String(String),
// Number literal, represented as f64 (thus it can be decimal).
/// Number literal, represented as f64 (thus it can be decimal).
/// Must be Ord, otherwise I need to rewrite the locals implementation in the interpreter.
Number(OrderedFloat<f64>),
/// Boolean literal.
Boolean(bool),
@ -78,7 +79,7 @@ pub struct Token {
pub lexeme: String,
/// Literal value of the token, if any.
pub literal: Option<Literal>,
/// Starting line on which the token was oonsumed from the source.
/// Starting line on which the token was consumed from the source.
pub line: usize,
}

View file

@ -5,10 +5,10 @@ use crate::token::Token;
use super::interface::Tokenizer;
/// Consume comments.
/// Consume comments in the source code.
///
/// A comment starts with '//' and runs until the end of the line.
/// If only one '/' is seen, it is consumed as a Slash token.
/// If only one '/' is seen, it is consumed as a Slash token instead.
pub struct Comment;
impl Tokenizer for Comment {
fn run(

View file

@ -8,8 +8,8 @@ use super::interface::Tokenizer;
/// Consume an identifier which also might be a keyword.
///
/// An identifier starts with an alphabetic character and goes on consuming alphanumeric and
/// underscore characters until the first different one.
/// An identifier starts with an alphabetic character or underscore and continues with
/// alphanumeric and underscore characters until the first different character.
pub struct Identifier;
impl Tokenizer for Identifier {
fn run(

View file

@ -2,7 +2,8 @@ use std::{iter::Peekable, str::CharIndices};
use crate::token::Token;
/// Interface to implement by a tokenizer.
/// Interface to implement by a tokenizer. Each tokenizer handles a specific type of token
/// in the Lox language.
pub trait Tokenizer: Send + Sync {
/// Take a tuple consisting of the index of a char and the char itself, the whole source code
/// iterator, the source itself and the current line. Return None if you can not handle the

View file

@ -63,6 +63,7 @@ lazy_static! {
}
/// Consume lexemes that consist of exactly one or two characters.
/// Handles operators like !=, ==, <=, and >=.
pub struct Lookahead;
impl Tokenizer for Lookahead {
fn run(

View file

@ -5,7 +5,8 @@ use tracing::error;
use super::interface::Tokenizer;
/// Consume a number literal. Numbers can have one decimal point.
/// Consume a number literal from the source code.
/// Numbers can have one decimal point and consist of one or more digits.
pub struct Number;
impl Tokenizer for Number {
fn run(

View file

@ -3,9 +3,10 @@ use crate::token::{Literal, Token, TokenType};
use std::{iter::Peekable, str::CharIndices};
use tracing::error;
/// Consume a string literal.
/// Consume a string literal from the source code.
///
/// A string literal consists of everything between two '"' and can stretch across multiple lines.
/// A string literal consists of everything between two double quotes (")
/// and can stretch across multiple lines.
pub struct String;
impl Tokenizer for String {
fn run(

View file

@ -2,7 +2,7 @@ use super::interface::Tokenizer;
use crate::token::Token;
use std::{iter::Peekable, str::CharIndices};
/// Consume and ignore whitespace characters.
/// Consume and ignore whitespace characters (spaces, tabs, carriage returns).
pub struct Whitespace;
impl Tokenizer for Whitespace {
fn run(

View file

@ -4,21 +4,31 @@ use ordered_float::OrderedFloat;
use crate::{callable::Callable, instance, token::Literal};
/// Represents the type of a callable value in Lox.
#[derive(Clone, Debug, PartialEq)]
pub enum CallableType {
/// A regular function or method
Function,
/// A class (which is callable to create instances)
Class(crate::class::Class),
}
/// Concrete value in the interpreter.
/// Concrete runtime value in the Lox interpreter.
#[derive(Clone, Debug, PartialEq)]
pub enum Value {
/// Special value for function returns
Return(Box<Value>),
/// Function, method, or class
Callable((Rc<dyn Callable>, CallableType)),
/// String value
String(String),
/// Numeric value
Number(OrderedFloat<f64>),
/// Boolean value
Boolean(bool),
/// Class instance
Instance(Rc<RefCell<instance::Instance>>),
/// Nil value (null)
Nil,
}
@ -37,6 +47,7 @@ impl Display for Value {
}
impl Value {
/// Determine if a value is "truthy" according to Lox rules.
/// Return false for nil or a false boolean, true otherwise.
pub fn is_truthy(&self) -> bool {
match self {