first documentation run
This commit is contained in:
parent
e065108f58
commit
f59e6a5fd5
21 changed files with 64 additions and 23 deletions
|
@ -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;
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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(),
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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>>,
|
||||
|
|
|
@ -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>),
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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(
|
||||
|
|
13
src/value.rs
13
src/value.rs
|
@ -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 {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue