Browse Source

Basic interpreter done

master
Stephen 6 months ago
parent
commit
8f5d5d8f29
14 changed files with 296 additions and 43 deletions
  1. +9
    -0
      scripts/inherit.lox
  2. +17
    -0
      scripts/inherit2.lox
  3. +19
    -0
      scripts/inherit3.lox
  4. +10
    -0
      scripts/inherit_error.lox
  5. +1
    -0
      src/callable.rs
  6. +5
    -0
      src/environment.rs
  7. +18
    -0
      src/expr.rs
  8. +107
    -24
      src/interpreter.rs
  9. +31
    -6
      src/lox_class.rs
  10. +19
    -4
      src/parser.rs
  11. +50
    -5
      src/resolver.rs
  12. +1
    -1
      src/scanner.rs
  13. +7
    -2
      src/stmt.rs
  14. +2
    -1
      src/visitor.rs

+ 9
- 0
scripts/inherit.lox View File

@ -0,0 +1,9 @@
class Doughnut {
cook() {
print "Fry until golden brown.";
}
}
class BostonCream < Doughnut {}
BostonCream().cook();

+ 17
- 0
scripts/inherit2.lox View File

@ -0,0 +1,17 @@
class Doughnut {
cook() {
print "Fry until golden brown.";
}
}
class BostonCream < Doughnut {
cook() {
super.cook();
print "Pipe full of custard and coat with chocolate.";
}
}
BostonCream().cook();
// Prints:
// Fry until golden brown.
// Pipe full of custard and coat with chocolate.

+ 19
- 0
scripts/inherit3.lox View File

@ -0,0 +1,19 @@
class A {
method() {
print "A method";
}
}
class B < A {
method() {
print "B method";
}
test() {
super.method();
}
}
class C < B {}
C().test();

+ 10
- 0
scripts/inherit_error.lox View File

@ -0,0 +1,10 @@
class A {}
class B < A {}
class Eclair {
cook() {
super.cook(); // Nothing for this super to reference
print "Pipe full of crème pâtissière.";
}
}

+ 1
- 0
src/callable.rs View File

@ -10,6 +10,7 @@ pub trait Callable: DynClone + Display {
interpreter: &mut Interpreter,
arguments: Vec<ExprValue>,
) -> Result<ExprValue, ExprError>;
fn arity(&self) -> usize;
}


+ 5
- 0
src/environment.rs View File

@ -107,6 +107,11 @@ impl Environments {
}))
}
pub fn enclosing(&self, env_id: usize) -> usize {
let env = self.environments.get(&env_id).unwrap();
env.enclosing_id.unwrap()
}
fn ancestor(&self, env_id: usize, dist: usize) -> usize {
let mut id = env_id;
for _ in 0..dist {


+ 18
- 0
src/expr.rs View File

@ -1,4 +1,5 @@
use crate::callable::Callable;
use crate::lox_class::LoxClass;
use crate::lox_instance::LoxInstance;
use crate::tokens::Token;
use crate::visitor::ExprVisitor;
@ -132,6 +133,14 @@ impl Expr {
}
}
pub fn new_super(keyword: Token, method: Token) -> Self {
let id = EXPR_COUNTER.fetch_add(1, Ordering::SeqCst);
Self {
id,
content: ExprContent::Super(SuperExpr { keyword, method }),
}
}
pub fn accept<U, V>(&self, visitor: &mut V) -> U
where
V: ExprVisitor<U>,
@ -148,6 +157,7 @@ impl Expr {
ExprContent::Get(expr) => visitor.visit_get_expr(self.id, expr),
ExprContent::Set(expr) => visitor.visit_set_expr(self.id, expr),
ExprContent::This(expr) => visitor.visit_this_expr(self.id, expr),
ExprContent::Super(expr) => visitor.visit_super_expr(self.id, expr),
}
}
}
@ -165,6 +175,7 @@ pub enum ExprContent {
Logical(LogicalExpr),
Set(SetExpr),
This(ThisExpr),
Super(SuperExpr),
}
#[derive(Clone)]
@ -222,6 +233,11 @@ pub struct SetExpr {
pub struct ThisExpr {
pub keyword: Token,
}
#[derive(Clone)]
pub struct SuperExpr {
pub keyword: Token,
pub method: Token,
}
#[derive(Clone, Debug)]
pub enum ExprValue {
@ -230,6 +246,7 @@ pub enum ExprValue {
Nil,
Boolean(bool),
Callable(Box<dyn Callable>),
Class(Box<LoxClass>),
Instance(Rc<RefCell<LoxInstance>>),
}
@ -241,6 +258,7 @@ impl Display for ExprValue {
Self::Nil => write!(f, "nil"),
Self::Boolean(x) => write!(f, "{}", x),
Self::Callable(x) => write!(f, "{}", x),
Self::Class(x) => write!(f, "{}", x),
Self::Instance(x) => write!(f, "{}", x.borrow()),
}
}


+ 107
- 24
src/interpreter.rs View File

@ -3,8 +3,9 @@ use crate::environment::Environments;
use crate::error_handler::ErrorHandler;
use crate::expr::ExprValue::Callable;
use crate::expr::{
AssignExpr, BinaryExpr, CallExpr, Expr, ExprError, ExprValue, GetExpr, GroupingExpr,
LiteralExpr, LogicalExpr, RealExprError, SetExpr, ThisExpr, UnaryExpr, VariableExpr,
AssignExpr, BinaryExpr, CallExpr, Expr, ExprContent, ExprError, ExprValue, GetExpr,
GroupingExpr, LiteralExpr, LogicalExpr, RealExprError, SetExpr, SuperExpr, ThisExpr, UnaryExpr,
VariableExpr,
};
use crate::lox_class::LoxClass;
use crate::lox_function::LoxFunction;
@ -16,6 +17,7 @@ use crate::stmt::{
use crate::tokens::{Token, TokenType};
use crate::visitor::{ExprVisitor, StmtVisitor};
use std::collections::HashMap;
use std::ops::Deref;
pub struct Interpreter {
pub environments: Environments,
@ -97,6 +99,32 @@ impl<'a> Interpreter {
None => self.environments.get(0, &name),
}
}
fn visit_callable_expr(
&mut self,
e: &CallExpr,
func: &mut Box<dyn crate::callable::Callable>,
) -> Result<ExprValue, ExprError> {
{
let mut arguments: Vec<ExprValue> = Vec::new();
for arg in &e.arguments {
arguments.push(self.evaluate(&arg)?);
}
if arguments.len() != func.arity() {
return Err(ExprError::Error(RealExprError {
token: e.paren.clone(),
msg: format!(
"Expected {} arguments but got {}.",
func.arity(),
arguments.len()
),
}));
}
func.call(self, arguments)
}
}
}
impl Default for Interpreter {
@ -259,29 +287,15 @@ impl ExprVisitor<Result<ExprValue, ExprError>> for Interpreter {
}
fn visit_call_expr(&mut self, _: usize, e: &CallExpr) -> Result<ExprValue, ExprError> {
if let Callable(mut func) = self.evaluate(&e.callee.as_ref())? {
let mut arguments: Vec<ExprValue> = Vec::new();
for arg in &e.arguments {
arguments.push(self.evaluate(&arg)?);
match self.evaluate(&e.callee.as_ref())? {
Callable(mut func) => self.visit_callable_expr(e, &mut func),
ExprValue::Class(func) => {
self.visit_callable_expr(e, &mut (func as Box<dyn crate::callable::Callable>))
}
if arguments.len() != func.arity() {
return Err(ExprError::Error(RealExprError {
token: e.paren.clone(),
msg: format!(
"Expected {} arguments but got {}.",
func.arity(),
arguments.len()
),
}));
}
func.call(self, arguments)
} else {
Err(ExprError::Error(RealExprError {
_ => Err(ExprError::Error(RealExprError {
token: e.paren.clone(),
msg: "Can only call functions and classes.".to_string(),
}))
})),
}
}
@ -314,6 +328,37 @@ impl ExprVisitor<Result<ExprValue, ExprError>> for Interpreter {
fn visit_this_expr(&mut self, id: usize, e: &ThisExpr) -> Result<ExprValue, ExprError> {
self.look_up_variable(&e.keyword, id)
}
fn visit_super_expr(&mut self, id: usize, e: &SuperExpr) -> Result<ExprValue, ExprError> {
let distance = self.locals.get(&id).unwrap();
let superclass = self
.environments
.get_at(self.current_env, *distance, "super")?;
if let ExprValue::Class(superclass) = superclass {
// "this" is always 1 closer than "super"
let instance = self
.environments
.get_at(self.current_env, distance - 1, "this")?;
if let ExprValue::Instance(instance) = instance {
let method = match superclass.find_method(&e.method.lexeme) {
Some(x) => x,
None => {
return Err(ExprError::Error(RealExprError {
token: e.method.clone(),
msg: format!("Undefined property '{}'.", e.method.lexeme),
}))
}
};
Ok(ExprValue::Callable(Box::new(
method.bind(&mut self.environments, instance),
)))
} else {
unreachable!()
}
} else {
unreachable!();
}
}
}
impl StmtVisitor<Result<(), ExprError>> for Interpreter {
@ -380,9 +425,41 @@ impl StmtVisitor<Result<(), ExprError>> for Interpreter {
}
fn visit_class_stmt(&mut self, stmt: &ClassStmt) -> Result<(), ExprError> {
// Retrieve & validate the superclass
let superclass = if let Some(sc) = &stmt.superclass {
// This if statement is always entered
if let ExprContent::Variable(sc_var) = &sc.content {
let sc_val = self.evaluate(sc)?;
match &sc_val {
ExprValue::Class(class) => Some(class.deref().to_owned()),
_ => {
return Err(ExprError::Error(RealExprError {
token: sc_var.name.clone(),
msg: "Superclass must be a class.".to_string(),
}))
}
}
} else {
unreachable!();
}
} else {
None
};
self.environments
.define(self.current_env, stmt.name.lexeme.clone(), ExprValue::Nil);
if let Some(superclass) = &superclass {
self.current_env = self.environments.add_child(self.current_env);
self.environments.define(
self.current_env,
"super".to_string(),
ExprValue::Class(Box::new(superclass.clone())),
);
}
let has_superclass = superclass.is_some();
let methods: HashMap<String, LoxFunction> = stmt
.methods
.iter()
@ -394,11 +471,17 @@ impl StmtVisitor<Result<(), ExprError>> for Interpreter {
})
.collect();
let klass = LoxClass::new(stmt.name.lexeme.clone(), methods);
let klass = LoxClass::new(stmt.name.lexeme.clone(), superclass, methods);
// TODO this leaks memory
if has_superclass {
self.current_env = self.environments.enclosing(self.current_env);
}
self.environments.assign(
self.current_env,
&stmt.name,
&ExprValue::Callable(Box::new(klass)),
&ExprValue::Class(Box::new(klass)),
)?;
Ok(())


+ 31
- 6
src/lox_class.rs View File

@ -6,25 +6,44 @@ use crate::lox_instance::LoxInstance;
use std::cell::RefCell;
use std::collections::HashMap;
use std::fmt;
use std::fmt::{Display, Formatter};
use std::fmt::{Debug, Display, Formatter};
use std::rc::Rc;
#[derive(Clone)]
pub struct LoxClass {
pub name: String,
pub methods: HashMap<String, LoxFunction>,
pub superclass: Option<Box<LoxClass>>,
}
impl LoxClass {
pub fn new(name: String, methods: HashMap<String, LoxFunction>) -> Self {
Self { name, methods }
pub fn new(
name: String,
superclass: Option<LoxClass>,
methods: HashMap<String, LoxFunction>,
) -> Self {
Self {
name,
methods,
superclass: match superclass {
Some(x) => Some(Box::new(x)),
None => None,
},
}
}
pub fn find_method(&self, name: &str) -> Option<&LoxFunction> {
match self.methods.get(name) {
Some(x) => Some(x),
None => None,
if let Some(x) = self.methods.get(name) {
return Some(x);
}
if let Some(sc) = &self.superclass {
if let Some(x) = sc.find_method(name) {
return Some(x);
}
}
None
}
}
@ -58,3 +77,9 @@ impl Display for LoxClass {
write!(f, "{}", self.name)
}
}
impl Debug for LoxClass {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.name)
}
}

+ 19
- 4
src/parser.rs View File

@ -1,7 +1,6 @@
use crate::error_handler::ErrorHandler;
use crate::expr::{Expr, ExprContent, ExprValue};
use crate::stmt::{FunctionStmt, Stmt};
use crate::tokens::TokenType::Identifier;
use crate::tokens::{Token, TokenType};
pub struct Parser<'a> {
@ -98,6 +97,14 @@ impl<'a> Parser<'a> {
fn class_declaration(&mut self) -> Option<Stmt> {
let name = self.consume_token(TokenType::Identifier, "Expected class name")?;
let superclass = if self.match_types(&[TokenType::Less]).is_some() {
self.consume_token(TokenType::Identifier, "Expected superclass name.");
Some(Expr::new_variable(self.previous()))
} else {
None
};
self.consume_token(TokenType::LeftBrace, "Expected '{' before class body.")?;
let mut methods: Vec<FunctionStmt> = Vec::new();
@ -108,9 +115,9 @@ impl<'a> Parser<'a> {
unreachable!();
}
}
self.consume_token(TokenType::RightBrace, "Expect '}' after class body.")?;
self.consume_token(TokenType::RightBrace, "Expected '}' after class body.")?;
Some(Stmt::new_class(name, methods))
Some(Stmt::new_class(name, methods, superclass))
}
fn statement(&mut self) -> Option<Stmt> {
@ -369,7 +376,8 @@ impl<'a> Parser<'a> {
if self.match_types(&[TokenType::LeftParen]).is_some() {
expr = self.finish_call(expr)?;
} else if self.match_types(&[TokenType::Dot]).is_some() {
let name = self.consume_token(Identifier, "Expected property name after '.'.")?;
let name =
self.consume_token(TokenType::Identifier, "Expected property name after '.'.")?;
expr = Expr::new_get(expr, name);
} else {
break;
@ -419,6 +427,13 @@ impl<'a> Parser<'a> {
}
}
TokenType::This => Expr::new_this(self.previous()),
TokenType::Super => {
let keyword = self.previous();
self.consume_token(TokenType::Dot, "Expected '.' after 'super'.")?;
let method =
self.consume_token(TokenType::Identifier, "Expected superclass method name.")?;
Expr::new_super(keyword, method)
}
TokenType::Identifier => Expr::new_variable(self.previous()),
_ => {
self.error_handler


+ 50
- 5
src/resolver.rs View File

@ -1,7 +1,7 @@
use crate::error_handler::ErrorHandler;
use crate::expr::{
AssignExpr, BinaryExpr, CallExpr, Expr, ExprContent, ExprValue, GetExpr, GroupingExpr,
LiteralExpr, LogicalExpr, SetExpr, ThisExpr, UnaryExpr, VariableExpr,
LiteralExpr, LogicalExpr, SetExpr, SuperExpr, ThisExpr, UnaryExpr, VariableExpr,
};
use crate::interpreter::Interpreter;
use crate::stmt::{
@ -175,10 +175,30 @@ impl<'a> StmtVisitor<()> for Resolver<'a> {
self.declare(&stmt.name);
self.define(&stmt.name);
self.begin_scope();
let last_class = self.current_class;
self.current_class = ClassType::Class;
if let Some(x) = &stmt.superclass {
self.current_class = ClassType::Subclass;
if let ExprContent::Variable(y) = &x.content {
if y.name.lexeme == stmt.name.lexeme {
self.error_handler
.error_token(y.name.clone(), "A class cannot inherit from itself.");
}
} else {
unreachable!();
}
self.resolve_expr(x);
self.begin_scope();
self.scopes
.last_mut()
.unwrap()
.insert("super".to_string(), true);
} else {
self.current_class = ClassType::Class;
}
self.begin_scope();
self.scopes
.last_mut()
@ -193,8 +213,13 @@ impl<'a> StmtVisitor<()> for Resolver<'a> {
self.resolve_function(method, declaration);
}
self.current_class = last_class;
self.end_scope();
if stmt.superclass.is_some() {
self.end_scope();
}
self.current_class = last_class;
}
}
@ -281,6 +306,25 @@ impl<'a> ExprVisitor<()> for Resolver<'a> {
&e.keyword,
);
}
fn visit_super_expr(&mut self, id: usize, e: &SuperExpr) {
match self.current_class {
ClassType::None => self
.error_handler
.error_token(e.keyword.clone(), "Cannot use 'super' outside of a class."),
ClassType::Class => self.error_handler.error_token(
e.keyword.clone(),
"Cannot use 'super' in a class with no superclass.",
),
ClassType::Subclass => self.resolve_local(
&Expr {
id,
content: ExprContent::Super(e.clone()),
},
&e.keyword,
),
}
}
}
#[derive(Copy, Clone, Eq, PartialEq)]
@ -295,4 +339,5 @@ enum FunctionType {
enum ClassType {
None,
Class,
Subclass,
}

+ 1
- 1
src/scanner.rs View File

@ -142,7 +142,7 @@ impl Scanner {
}
fn is_at_end(&self) -> bool {
self.current >= self.source.len()
self.current >= self.source.chars().count()
}
fn advance(&mut self) -> char {


+ 7
- 2
src/stmt.rs View File

@ -59,8 +59,12 @@ impl Stmt {
Self::Return(ReturnStmt { keyword, value })
}
pub fn new_class(name: Token, methods: Vec<FunctionStmt>) -> Self {
Self::Class(ClassStmt { name, methods })
pub fn new_class(name: Token, methods: Vec<FunctionStmt>, superclass: Option<Expr>) -> Self {
Self::Class(ClassStmt {
name,
methods,
superclass,
})
}
pub fn accept<U, V>(&self, visitor: &mut V) -> U
@ -132,4 +136,5 @@ pub struct ReturnStmt {
pub struct ClassStmt {
pub name: Token,
pub methods: Vec<FunctionStmt>,
pub superclass: Option<Expr>, // Must be variable
}

+ 2
- 1
src/visitor.rs View File

@ -1,6 +1,6 @@
use crate::expr::{
AssignExpr, BinaryExpr, CallExpr, GetExpr, GroupingExpr, LiteralExpr, LogicalExpr, SetExpr,
ThisExpr, UnaryExpr, VariableExpr,
SuperExpr, ThisExpr, UnaryExpr, VariableExpr,
};
use crate::stmt::{
BlockStmt, ClassStmt, ExpressionStmt, FunctionStmt, IfStmt, PrintStmt, ReturnStmt, VarStmt,
@ -25,6 +25,7 @@ pub trait ExprVisitor<U> {
fn visit_get_expr(&mut self, id: usize, e: &GetExpr) -> U;
fn visit_set_expr(&mut self, id: usize, e: &SetExpr) -> U;
fn visit_this_expr(&mut self, id: usize, e: &ThisExpr) -> U;
fn visit_super_expr(&mut self, id: usize, e: &SuperExpr) -> U;
}
pub trait StmtVisitor<U> {


Loading…
Cancel
Save