mirror of
https://github.com/MorizzG/rlox.git
synced 2025-12-06 04:12:42 +00:00
Chapter 10 done
This commit is contained in:
parent
956c4d0f28
commit
46f1030207
16 changed files with 1173 additions and 201 deletions
52
src/error.rs
52
src/error.rs
|
|
@ -25,8 +25,8 @@ pub enum LexerError {
|
||||||
pub enum ParserError {
|
pub enum ParserError {
|
||||||
#[error("Token stream ended unexpectedly.")]
|
#[error("Token stream ended unexpectedly.")]
|
||||||
TokenStreamEnded,
|
TokenStreamEnded,
|
||||||
#[error("Unexpected token {token} at {0}.", token.code_pos)]
|
#[error("Expected a primary expression, but found a {token} token instead at {0}.", token.code_pos)]
|
||||||
UnexpectedToken { token: Token },
|
ExpectedPrimary { token: Token },
|
||||||
#[error("Missing semicolon at {code_pos}")]
|
#[error("Missing semicolon at {code_pos}")]
|
||||||
MissingSemicolon { code_pos: CodePos },
|
MissingSemicolon { code_pos: CodePos },
|
||||||
#[error("Expected variable name at {0}, got {token} instead", token.code_pos)]
|
#[error("Expected variable name at {0}, got {token} instead", token.code_pos)]
|
||||||
|
|
@ -36,7 +36,23 @@ pub enum ParserError {
|
||||||
#[error("Missing closing curly brace at {code_pos}")]
|
#[error("Missing closing curly brace at {code_pos}")]
|
||||||
MissingRightBrace { code_pos: CodePos },
|
MissingRightBrace { code_pos: CodePos },
|
||||||
#[error("Missing closing parenthesis at {code_pos}")]
|
#[error("Missing closing parenthesis at {code_pos}")]
|
||||||
MissingRightParenthesis { code_pos: CodePos },
|
MissingRightParen { code_pos: CodePos },
|
||||||
|
#[error("Missing parenthesis after if at {code_pos}")]
|
||||||
|
MissingParenAfterIf { code_pos: CodePos },
|
||||||
|
#[error("Missing parenthesis after while at {code_pos}")]
|
||||||
|
MissingParenAfterWhile { code_pos: CodePos },
|
||||||
|
#[error("Missing parenthesis after for at {code_pos}")]
|
||||||
|
MissingParenAfterFor { code_pos: CodePos },
|
||||||
|
#[error("Call at {code_pos} has too many arguments")]
|
||||||
|
TooManyArguments { code_pos: CodePos },
|
||||||
|
#[error("{msg} at {code_pos}")]
|
||||||
|
MissingIdentifier { msg: String, code_pos: CodePos },
|
||||||
|
#[error("Missing arguments to function declaration at {code_pos}")]
|
||||||
|
MissingFunctionArgs { code_pos: CodePos },
|
||||||
|
#[error("Missing body to function declaration at {code_pos}")]
|
||||||
|
MissingFunctionBody { code_pos: CodePos },
|
||||||
|
#[error("Function declaration at {code_pos} has too many parameters")]
|
||||||
|
TooManyParams { code_pos: CodePos },
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Error, Debug)]
|
#[derive(Error, Debug)]
|
||||||
|
|
@ -49,19 +65,34 @@ pub enum RuntimeError {
|
||||||
DivisionByZero,
|
DivisionByZero,
|
||||||
#[error("Name {name} is not defined")]
|
#[error("Name {name} is not defined")]
|
||||||
NameNotDefined { name: String },
|
NameNotDefined { name: String },
|
||||||
|
#[error("{callee} is not callable")]
|
||||||
|
NotCallable { callee: Value },
|
||||||
|
#[error("{name}() takes {arity} args, but {given} were given.")]
|
||||||
|
WrongArity { name: String, arity: usize, given: usize },
|
||||||
|
#[error("Extern function call to {name} failed: {msg}")]
|
||||||
|
ExtFunCallFailed { name: String, msg: String },
|
||||||
|
#[error("Uncaught break statement")]
|
||||||
|
Break,
|
||||||
|
#[error("Uncaught return statement")]
|
||||||
|
Return { value: Value },
|
||||||
|
#[error("Exit with exit code {exit_code}")]
|
||||||
|
Exit { exit_code: i32 },
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Error, Debug)]
|
#[derive(Error, Debug)]
|
||||||
|
#[allow(clippy::enum_variant_names)]
|
||||||
pub enum LoxError {
|
pub enum LoxError {
|
||||||
#[error("{0}", format_errors(inner))]
|
#[error("{0}", format_multiple_errors(inner))]
|
||||||
LexerError { inner: Vec<LexerError> },
|
LexerError { inner: Vec<LexerError> },
|
||||||
#[error("{0}", format_errors(inner))]
|
#[error("{0}", format_multiple_errors(inner))]
|
||||||
ParserError { inner: Vec<ParserError> },
|
ParserError { inner: Vec<ParserError> },
|
||||||
#[error("{inner}")]
|
#[error("{inner}")]
|
||||||
EvalError { inner: RuntimeError },
|
RuntimeError { inner: RuntimeError },
|
||||||
|
#[error("Called exit() with exit code {exit_code}")]
|
||||||
|
Exit { exit_code: i32 },
|
||||||
}
|
}
|
||||||
|
|
||||||
fn format_errors(errs: &Vec<impl std::error::Error>) -> String {
|
fn format_multiple_errors(errs: &Vec<impl std::error::Error>) -> String {
|
||||||
let msg = if errs.len() == 1 {
|
let msg = if errs.len() == 1 {
|
||||||
format!("{}", errs[0])
|
format!("{}", errs[0])
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -85,7 +116,10 @@ impl From<Vec<ParserError>> for LoxError {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<RuntimeError> for LoxError {
|
impl From<RuntimeError> for LoxError {
|
||||||
fn from(eval_err: RuntimeError) -> Self {
|
fn from(runtime_err: RuntimeError) -> Self {
|
||||||
LoxError::EvalError { inner: eval_err }
|
match runtime_err {
|
||||||
|
RuntimeError::Exit { exit_code } => LoxError::Exit { exit_code },
|
||||||
|
_ => LoxError::RuntimeError { inner: runtime_err },
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,67 +1,146 @@
|
||||||
|
use std::borrow::Borrow;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use crate::error::RuntimeError;
|
use crate::error::RuntimeError;
|
||||||
|
|
||||||
|
use super::lox_std::init_std;
|
||||||
|
use super::value::HeapValue;
|
||||||
use super::Value;
|
use super::Value;
|
||||||
|
|
||||||
type Scope = HashMap<String, Value>;
|
pub type Scope = HashMap<String, HeapValue>;
|
||||||
|
|
||||||
pub struct Environment {
|
pub struct Environment {
|
||||||
globals: HashMap<String, Value>,
|
globals: Scope,
|
||||||
|
|
||||||
scopes: Vec<Scope>,
|
// scopes: Vec<Scope>,
|
||||||
|
frames: Vec<Frame>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Environment {
|
impl Environment {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Environment {
|
let mut env = Environment {
|
||||||
globals: HashMap::new(),
|
globals: HashMap::new(),
|
||||||
scopes: Vec::new(),
|
frames: vec![Frame::new_global()],
|
||||||
|
};
|
||||||
|
|
||||||
|
init_std(&mut env);
|
||||||
|
|
||||||
|
env
|
||||||
|
}
|
||||||
|
|
||||||
|
fn current_frame(&self) -> &Frame {
|
||||||
|
self.frames.last().unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn current_frame_mut(&mut self) -> &mut Frame {
|
||||||
|
self.frames.last_mut().unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn globals(&self) -> &Scope {
|
||||||
|
&self.globals
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get(&self, name: &str) -> Result<Value, RuntimeError> {
|
||||||
|
if let Some(value) = self.current_frame().get(name) {
|
||||||
|
return Ok(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
match self.globals.get(name) {
|
||||||
|
Some(heap_value) => Ok(heap_value.get_clone()),
|
||||||
|
None => Err(RuntimeError::NameNotDefined { name: name.to_owned() }),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get(&self, name: &str) -> Result<&Value, RuntimeError> {
|
pub fn define(&mut self, name: impl Into<String> + Borrow<str>, value: Value) {
|
||||||
for scope in self.scopes.iter().rev() {
|
if let Err(value) = self.current_frame_mut().try_define(name.borrow(), value) {
|
||||||
if let Some(value) = scope.get(name) {
|
self.globals.insert(name.into(), HeapValue::new(value));
|
||||||
return Ok(value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
self.globals
|
|
||||||
.get(name)
|
|
||||||
.ok_or(RuntimeError::NameNotDefined { name: name.to_owned() })
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn define(&mut self, name: String, value: Value) {
|
|
||||||
if let Some(scope) = self.scopes.last_mut() {
|
|
||||||
scope.insert(name, value);
|
|
||||||
} else {
|
|
||||||
self.globals.insert(name, value);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn assign(&mut self, name: &str, value: Value) -> Result<(), RuntimeError> {
|
pub fn assign(&mut self, name: &str, value: Value) -> Result<(), RuntimeError> {
|
||||||
for scope in self.scopes.iter_mut().rev() {
|
let value = match self.current_frame_mut().try_assign(name, value) {
|
||||||
if let Some(var) = scope.get_mut(name) {
|
Ok(()) => return Ok(()),
|
||||||
*var = value;
|
Err(value) => value,
|
||||||
return Ok(());
|
};
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(var) = self.globals.get_mut(name) {
|
if let Some(var) = self.globals.get_mut(name) {
|
||||||
*var = value;
|
// *var = value;
|
||||||
|
var.replace(value);
|
||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
let name = name.to_owned();
|
Err(RuntimeError::NameNotDefined { name: name.to_owned() })
|
||||||
Err(RuntimeError::NameNotDefined { name })
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn enter_scope(&mut self) {
|
pub fn enter_scope(&mut self) {
|
||||||
self.scopes.push(Scope::new());
|
self.current_frame_mut().enter_scope();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn exit_scope(&mut self) {
|
pub fn exit_scope(&mut self) {
|
||||||
self.scopes.pop().expect("Tried to exit scope, but no scope to exit");
|
self.current_frame_mut().exit_scope();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn push_frame(&mut self, base_scope: Scope) {
|
||||||
|
self.frames.push(Frame::new(base_scope));
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn pop_frame(&mut self) {
|
||||||
|
self.frames.pop().expect("Tried to pop global frame");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*====================================================================================================================*/
|
||||||
|
|
||||||
|
struct Frame {
|
||||||
|
scopes: Vec<Scope>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Frame {
|
||||||
|
fn new(base_scope: Scope) -> Self {
|
||||||
|
Frame {
|
||||||
|
scopes: vec![base_scope],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn new_global() -> Self {
|
||||||
|
Frame { scopes: Vec::new() }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn enter_scope(&mut self) {
|
||||||
|
self.scopes.push(Scope::new());
|
||||||
|
}
|
||||||
|
|
||||||
|
fn exit_scope(&mut self) {
|
||||||
|
self.scopes.pop().expect("Tried to exit scope, but no scope to exit");
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get(&self, name: &str) -> Option<Value> {
|
||||||
|
for scope in self.scopes.iter().rev() {
|
||||||
|
if let Some(heap_value) = scope.get(name) {
|
||||||
|
return Some(heap_value.get_clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
fn try_define(&mut self, name: impl Into<String> + Borrow<str>, value: Value) -> Result<(), Value> {
|
||||||
|
if let Some(scope) = self.scopes.last_mut() {
|
||||||
|
scope.insert(name.into(), HeapValue::new(value));
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn try_assign(&mut self, name: &str, value: Value) -> Result<(), Value> {
|
||||||
|
for scope in self.scopes.iter_mut().rev() {
|
||||||
|
if let Some(heap_value) = scope.get_mut(name) {
|
||||||
|
heap_value.replace(value);
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Err(value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
104
src/interpreter/function.rs
Normal file
104
src/interpreter/function.rs
Normal file
|
|
@ -0,0 +1,104 @@
|
||||||
|
use std::fmt::{Debug, Display};
|
||||||
|
|
||||||
|
use crate::parser::Stmt;
|
||||||
|
|
||||||
|
use super::environment::{Environment, Scope};
|
||||||
|
use super::interpret::EvalResult;
|
||||||
|
use super::Value;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct LoxFunction {
|
||||||
|
name: String,
|
||||||
|
closure: Scope,
|
||||||
|
param_names: Vec<String>,
|
||||||
|
body: Box<Stmt>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LoxFunction {
|
||||||
|
pub fn new(name: impl Into<String>, param_names: Vec<String>, body: Stmt) -> Self {
|
||||||
|
let name = name.into();
|
||||||
|
let body = Box::new(body);
|
||||||
|
LoxFunction {
|
||||||
|
name,
|
||||||
|
closure: Scope::new(),
|
||||||
|
param_names,
|
||||||
|
body,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn arity(&self) -> usize {
|
||||||
|
self.param_names.len()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn name(&self) -> &str {
|
||||||
|
&self.name
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn closure(&self) -> &Scope {
|
||||||
|
&self.closure
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn param_names(&self) -> impl Iterator<Item = &String> {
|
||||||
|
self.param_names.iter()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn body(&self) -> &Stmt {
|
||||||
|
&self.body
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for LoxFunction {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
write!(f, "<fun {}>", self.name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*====================================================================================================================*/
|
||||||
|
|
||||||
|
pub type ExternFunClosure = fn(Vec<Value>, &mut Environment) -> EvalResult<Value>;
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct LoxExternFunction {
|
||||||
|
name: String,
|
||||||
|
|
||||||
|
closure: ExternFunClosure,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LoxExternFunction {
|
||||||
|
pub fn new(name: impl Into<String>, closure: ExternFunClosure) -> Self {
|
||||||
|
let name = name.into();
|
||||||
|
|
||||||
|
LoxExternFunction { name, closure }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn register(self, env: &mut Environment) {
|
||||||
|
let name = self.name.clone();
|
||||||
|
let fun = Value::extern_function(self);
|
||||||
|
env.define(name, fun);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn call(&self, args: Vec<Value>, env: &mut Environment) -> EvalResult<Value> {
|
||||||
|
(self.closure)(args, env)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for LoxExternFunction {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
write!(f, "<extern fun {}>", self.name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Debug for LoxExternFunction {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
f.debug_struct("LoxExternFunction")
|
||||||
|
.field("name", &self.name)
|
||||||
|
.field("closure", &"<closure>")
|
||||||
|
.finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialEq for LoxExternFunction {
|
||||||
|
fn eq(&self, other: &Self) -> bool {
|
||||||
|
self.name == other.name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,46 +1,17 @@
|
||||||
use std::fmt::Display;
|
use std::rc::Rc;
|
||||||
|
|
||||||
use crate::error::RuntimeError;
|
use crate::error::RuntimeError;
|
||||||
use crate::parser::{BinaryOp, Expr, Literal, Stmt, UnaryOp};
|
use crate::parser::{BinaryOp, Expr, Literal, LogicalOp, Stmt, UnaryOp};
|
||||||
|
|
||||||
use super::environment::Environment;
|
use super::environment::Environment;
|
||||||
|
use super::{LoxFunction, Value};
|
||||||
|
|
||||||
pub type EvalResult<T> = Result<T, RuntimeError>;
|
pub type EvalResult<T> = Result<T, RuntimeError>;
|
||||||
|
|
||||||
/*====================================================================================================================*/
|
/*====================================================================================================================*/
|
||||||
|
|
||||||
pub fn execute(statement: Stmt, env: &mut Environment) -> Result<(), RuntimeError> {
|
pub fn execute(statement: Stmt, env: &mut Environment) -> Result<(), RuntimeError> {
|
||||||
statement.eval(env)
|
statement.execute(env)
|
||||||
}
|
|
||||||
|
|
||||||
/*====================================================================================================================*/
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
|
||||||
pub enum Value {
|
|
||||||
String(String),
|
|
||||||
Number(f64),
|
|
||||||
Bool(bool),
|
|
||||||
Nil,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Value {
|
|
||||||
fn is_truthy(&self) -> bool {
|
|
||||||
match self {
|
|
||||||
Value::Bool(false) | Value::Nil => false,
|
|
||||||
_ => true,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Display for Value {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
match self {
|
|
||||||
Value::String(s) => write!(f, "\"{s}\""),
|
|
||||||
Value::Number(num) => write!(f, "{num}"),
|
|
||||||
Value::Bool(b) => write!(f, "{b}"),
|
|
||||||
Value::Nil => write!(f, "nil"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*====================================================================================================================*/
|
/*====================================================================================================================*/
|
||||||
|
|
@ -50,24 +21,25 @@ impl Display for Value {
|
||||||
} */
|
} */
|
||||||
|
|
||||||
impl Literal {
|
impl Literal {
|
||||||
fn eval(self, _env: &mut Environment) -> EvalResult<Value> {
|
fn eval(&self, env: &mut Environment) -> EvalResult<Value> {
|
||||||
|
let _ = env;
|
||||||
match self {
|
match self {
|
||||||
Literal::String(s) => Ok(Value::String(s)),
|
Literal::String(s) => Ok(Value::String(Rc::clone(s))),
|
||||||
Literal::Number(num) => Ok(Value::Number(num)),
|
Literal::Number(num) => Ok(Value::Number(*num)),
|
||||||
Literal::Bool(b) => Ok(Value::Bool(b)),
|
Literal::Bool(b) => Ok(Value::Bool(*b)),
|
||||||
Literal::Nil => Ok(Value::Nil),
|
Literal::Nil => Ok(Value::Nil),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Expr {
|
impl Expr {
|
||||||
fn eval(self, env: &mut Environment) -> EvalResult<Value> {
|
fn eval(&self, env: &mut Environment) -> EvalResult<Value> {
|
||||||
match self {
|
match self {
|
||||||
Expr::Literal { literal } => literal.eval(env),
|
Expr::Literal { literal } => literal.eval(env),
|
||||||
Expr::Unary { op, expr } => {
|
Expr::Unary { op, expr } => {
|
||||||
let arg = expr.eval(env)?;
|
let arg = expr.eval(env)?;
|
||||||
|
|
||||||
match (op, arg) {
|
match (*op, arg) {
|
||||||
(UnaryOp::Negate, Value::Number(num)) => Ok(Value::Number(-num)),
|
(UnaryOp::Negate, Value::Number(num)) => Ok(Value::Number(-num)),
|
||||||
(UnaryOp::Not, Value::Bool(b)) => Ok(Value::Bool(!b)),
|
(UnaryOp::Not, Value::Bool(b)) => Ok(Value::Bool(!b)),
|
||||||
(UnaryOp::Not, primitive) => Ok(Value::Bool(!primitive.is_truthy())),
|
(UnaryOp::Not, primitive) => Ok(Value::Bool(!primitive.is_truthy())),
|
||||||
|
|
@ -80,7 +52,7 @@ impl Expr {
|
||||||
let left = left.eval(env)?;
|
let left = left.eval(env)?;
|
||||||
let right = right.eval(env)?;
|
let right = right.eval(env)?;
|
||||||
|
|
||||||
match (left, op, right) {
|
match (left, *op, right) {
|
||||||
(Number(left), BinaryOp::Add, Number(right)) => Ok(Number(left + right)),
|
(Number(left), BinaryOp::Add, Number(right)) => Ok(Number(left + right)),
|
||||||
(Number(left), BinaryOp::Subtract, Number(right)) => Ok(Number(left - right)),
|
(Number(left), BinaryOp::Subtract, Number(right)) => Ok(Number(left - right)),
|
||||||
(Number(left), BinaryOp::Multiply, Number(right)) => Ok(Number(left * right)),
|
(Number(left), BinaryOp::Multiply, Number(right)) => Ok(Number(left * right)),
|
||||||
|
|
@ -91,7 +63,12 @@ impl Expr {
|
||||||
Ok(Number(left / right))
|
Ok(Number(left / right))
|
||||||
}
|
}
|
||||||
|
|
||||||
(String(left), BinaryOp::Add, String(right)) => Ok(String(left + &right)),
|
(String(left), BinaryOp::Add, String(right)) => {
|
||||||
|
let mut s = std::string::String::with_capacity(left.capacity() + right.capacity());
|
||||||
|
s += &left;
|
||||||
|
s += &right;
|
||||||
|
Ok(String(Rc::new(s)))
|
||||||
|
}
|
||||||
|
|
||||||
(left, BinaryOp::Equal, right) => Ok(Bool(left == right)),
|
(left, BinaryOp::Equal, right) => Ok(Bool(left == right)),
|
||||||
(left, BinaryOp::NotEqual, right) => Ok(Bool(left != right)),
|
(left, BinaryOp::NotEqual, right) => Ok(Bool(left != right)),
|
||||||
|
|
@ -109,26 +86,59 @@ impl Expr {
|
||||||
(left, op, right) => Err(RuntimeError::BinaryOpInvalidArguments { left, op, right }),
|
(left, op, right) => Err(RuntimeError::BinaryOpInvalidArguments { left, op, right }),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Expr::Logical { left, op, right } => {
|
||||||
|
let left = left.eval(env)?;
|
||||||
|
match op {
|
||||||
|
LogicalOp::Or => {
|
||||||
|
if left.is_truthy() {
|
||||||
|
return Ok(left);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
LogicalOp::And => {
|
||||||
|
if !left.is_truthy() {
|
||||||
|
return Ok(left);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let right = right.eval(env)?;
|
||||||
|
Ok(right)
|
||||||
|
}
|
||||||
Expr::Grouping { expr } => expr.eval(env),
|
Expr::Grouping { expr } => expr.eval(env),
|
||||||
Expr::Variable { name } => env.get(&name).cloned(),
|
Expr::Variable { name } => env.get(name),
|
||||||
Expr::Assignment { name, value } => {
|
Expr::Assignment { name, value } => {
|
||||||
let value = value.eval(env)?;
|
let value = value.eval(env)?;
|
||||||
env.assign(&name, value.clone())?;
|
env.assign(name, value.clone())?;
|
||||||
Ok(value)
|
Ok(value)
|
||||||
}
|
}
|
||||||
|
Expr::Call { callee, args } => {
|
||||||
|
let callee = callee.eval(env)?;
|
||||||
|
let args = args
|
||||||
|
.iter()
|
||||||
|
.map(|arg| arg.eval(env))
|
||||||
|
.collect::<EvalResult<Vec<Value>>>()?;
|
||||||
|
|
||||||
|
match callee {
|
||||||
|
Value::Function(fun) => fun.call(args, env),
|
||||||
|
Value::ExternFunction(ext_fun) => ext_fun.call(args, env),
|
||||||
|
_ => Err(RuntimeError::NotCallable { callee }),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Expr::Function {
|
||||||
|
name,
|
||||||
|
param_names,
|
||||||
|
body,
|
||||||
|
} => Ok(Value::function(LoxFunction::new(
|
||||||
|
name,
|
||||||
|
param_names.clone(),
|
||||||
|
body.as_ref().clone(),
|
||||||
|
))),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Stmt {
|
impl Stmt {
|
||||||
fn eval(self, env: &mut Environment) -> EvalResult<()> {
|
fn execute(&self, env: &mut Environment) -> EvalResult<()> {
|
||||||
match self {
|
match self {
|
||||||
Stmt::ExprStmt { expr } => {
|
|
||||||
// expr.eval(env)?;
|
|
||||||
// Ok(Value::Nil)
|
|
||||||
expr.eval(env)?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
Stmt::Print { expr } => {
|
Stmt::Print { expr } => {
|
||||||
match expr.eval(env)? {
|
match expr.eval(env)? {
|
||||||
// special case: when printing a string, drop the surrounding ""
|
// special case: when printing a string, drop the surrounding ""
|
||||||
|
|
@ -138,20 +148,85 @@ impl Stmt {
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
Stmt::IfStmt {
|
||||||
|
condition,
|
||||||
|
then_branch,
|
||||||
|
else_branch,
|
||||||
|
} => {
|
||||||
|
let condition = condition.eval(env)?;
|
||||||
|
if condition.is_truthy() {
|
||||||
|
then_branch.execute(env)
|
||||||
|
} else if let Some(else_branch) = else_branch {
|
||||||
|
else_branch.execute(env)
|
||||||
|
} else {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Stmt::While { condition, body } => {
|
||||||
|
while condition.eval(env)?.is_truthy() {
|
||||||
|
match body.execute(env) {
|
||||||
|
Ok(_) => {}
|
||||||
|
Err(RuntimeError::Break) => break,
|
||||||
|
Err(err) => return Err(err),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
Stmt::VarDecl { name, initializer } => {
|
Stmt::VarDecl { name, initializer } => {
|
||||||
let initializer = initializer.eval(env)?;
|
let initializer = initializer.eval(env)?;
|
||||||
env.define(name, initializer);
|
env.define(name.as_ref(), initializer);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
Stmt::Block { statements } => {
|
Stmt::Block { statements } => {
|
||||||
env.enter_scope();
|
env.enter_scope();
|
||||||
for statement in statements {
|
for statement in statements {
|
||||||
// on error the current frame gets destroyed anyways, so no need to exit scope
|
// on error the current frame gets destroyed anyways, so no need to exit scope
|
||||||
statement.eval(env)?;
|
statement.execute(env)?;
|
||||||
}
|
}
|
||||||
env.exit_scope();
|
env.exit_scope();
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
Stmt::ExprStmt { expr } => {
|
||||||
|
// expr.eval(env)?;
|
||||||
|
// Ok(Value::Nil)
|
||||||
|
expr.eval(env)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
Stmt::Break => Err(RuntimeError::Break),
|
||||||
|
Stmt::Return { expr } => {
|
||||||
|
let value = expr.eval(env)?;
|
||||||
|
Err(RuntimeError::Return { value })
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*====================================================================================================================*/
|
||||||
|
|
||||||
|
impl LoxFunction {
|
||||||
|
pub fn call(&self, args: Vec<Value>, env: &mut Environment) -> EvalResult<Value> {
|
||||||
|
if args.len() != self.arity() {
|
||||||
|
return Err(RuntimeError::WrongArity {
|
||||||
|
name: self.name().to_owned(),
|
||||||
|
arity: self.arity(),
|
||||||
|
given: args.len(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
env.push_frame(self.closure().clone());
|
||||||
|
|
||||||
|
for (name, value) in std::iter::zip(self.param_names(), args) {
|
||||||
|
env.define(name.as_ref(), value);
|
||||||
|
}
|
||||||
|
|
||||||
|
let ret_val = match self.body().execute(env) {
|
||||||
|
Ok(()) => Value::Nil,
|
||||||
|
Err(RuntimeError::Return { value }) => value,
|
||||||
|
Err(err) => return Err(err),
|
||||||
|
};
|
||||||
|
|
||||||
|
env.pop_frame();
|
||||||
|
|
||||||
|
Ok(ret_val)
|
||||||
|
}
|
||||||
|
}
|
||||||
99
src/interpreter/lox_std.rs
Normal file
99
src/interpreter/lox_std.rs
Normal file
|
|
@ -0,0 +1,99 @@
|
||||||
|
use crate::error::RuntimeError;
|
||||||
|
|
||||||
|
use super::environment::Environment;
|
||||||
|
use super::function::{ExternFunClosure, LoxExternFunction};
|
||||||
|
use super::value::HeapValue;
|
||||||
|
use super::Value;
|
||||||
|
|
||||||
|
pub fn init_std(env: &mut Environment) {
|
||||||
|
input().register(env);
|
||||||
|
clock().register(env);
|
||||||
|
print_globals().register(env);
|
||||||
|
exit().register(env);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn input() -> LoxExternFunction {
|
||||||
|
let closure: ExternFunClosure = |args, _env| match *args {
|
||||||
|
[] => {
|
||||||
|
let mut buf = String::new();
|
||||||
|
std::io::stdin()
|
||||||
|
.read_line(&mut buf)
|
||||||
|
.map_err(|err| RuntimeError::ExtFunCallFailed {
|
||||||
|
name: "input".to_owned(),
|
||||||
|
msg: format!("input() failed to read from stdin: {err}"),
|
||||||
|
})?;
|
||||||
|
|
||||||
|
assert_eq!(buf.pop(), Some('\n'));
|
||||||
|
|
||||||
|
Ok(Value::string(buf))
|
||||||
|
}
|
||||||
|
_ => Err(RuntimeError::ExtFunCallFailed {
|
||||||
|
name: "input".to_owned(),
|
||||||
|
msg: "input() takes no arguments".to_owned(),
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
|
||||||
|
LoxExternFunction::new("input", closure)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn clock() -> LoxExternFunction {
|
||||||
|
let closure: ExternFunClosure = |args, _env| match *args {
|
||||||
|
[] => {
|
||||||
|
let time = std::time::SystemTime::now()
|
||||||
|
.duration_since(std::time::UNIX_EPOCH)
|
||||||
|
.unwrap()
|
||||||
|
.as_secs_f64();
|
||||||
|
|
||||||
|
Ok(Value::Number(time))
|
||||||
|
}
|
||||||
|
_ => Err(RuntimeError::ExtFunCallFailed {
|
||||||
|
name: "clock".to_owned(),
|
||||||
|
msg: "clock() takes no arguments".to_owned(),
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
|
||||||
|
LoxExternFunction::new("clock", closure)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn print_globals() -> LoxExternFunction {
|
||||||
|
let closure: ExternFunClosure = |args, env| match *args {
|
||||||
|
[] => {
|
||||||
|
let mut globals: Vec<(&String, &HeapValue)> = env.globals().iter().collect();
|
||||||
|
|
||||||
|
globals.sort_by_key(|&(name, _value)| name);
|
||||||
|
|
||||||
|
for (name, value) in globals {
|
||||||
|
println!("{name}: {value}");
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(Value::Nil)
|
||||||
|
}
|
||||||
|
_ => Err(RuntimeError::ExtFunCallFailed {
|
||||||
|
name: "print_globals".to_owned(),
|
||||||
|
msg: "print_globals() takes no arguments".to_owned(),
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
|
||||||
|
LoxExternFunction::new("print_globals", closure)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn exit() -> LoxExternFunction {
|
||||||
|
let closure: ExternFunClosure = |args, _env| {
|
||||||
|
match &*args {
|
||||||
|
[] => return Err(RuntimeError::Exit { exit_code: 0 }),
|
||||||
|
[Value::Number(exit_code)] => {
|
||||||
|
if exit_code.fract() == 0.0 {
|
||||||
|
let exit_code = *exit_code as i32;
|
||||||
|
return Err(RuntimeError::Exit { exit_code });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
Err(RuntimeError::ExtFunCallFailed {
|
||||||
|
name: "exit".to_owned(),
|
||||||
|
msg: "Arguments to exit() must an integer or nothing for exit code 0".to_owned(),
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
|
LoxExternFunction::new("exit", closure)
|
||||||
|
}
|
||||||
|
|
@ -1,6 +1,10 @@
|
||||||
mod environment;
|
mod environment;
|
||||||
mod eval;
|
mod function;
|
||||||
|
mod interpret;
|
||||||
|
mod lox_std;
|
||||||
mod run;
|
mod run;
|
||||||
|
mod value;
|
||||||
|
|
||||||
pub use eval::Value;
|
pub use function::LoxFunction;
|
||||||
pub use run::interpreter_main;
|
pub use run::interpreter_main;
|
||||||
|
pub use value::Value;
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,7 @@
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
|
|
||||||
use crate::error::LoxError;
|
use crate::error::LoxError;
|
||||||
use crate::interpreter::eval::execute;
|
use crate::interpreter::interpret::execute;
|
||||||
use crate::interpreter::Value;
|
|
||||||
use crate::lexer::{scan_tokens, Token};
|
use crate::lexer::{scan_tokens, Token};
|
||||||
use crate::parser::parse_tokens;
|
use crate::parser::parse_tokens;
|
||||||
|
|
||||||
|
|
@ -34,11 +33,15 @@ fn run_file(script_path: &str) {
|
||||||
|
|
||||||
let mut env = Environment::new();
|
let mut env = Environment::new();
|
||||||
|
|
||||||
if let Err(err) = run(&source_code, &mut env) {
|
match run(&source_code, &mut env) {
|
||||||
eprintln!("{err}");
|
Ok(()) => std::process::exit(0),
|
||||||
match err {
|
Err(err) => {
|
||||||
LoxError::LexerError { .. } | LoxError::ParserError { .. } => std::process::exit(65),
|
eprintln!("{err}");
|
||||||
LoxError::EvalError { .. } => std::process::exit(70),
|
match err {
|
||||||
|
LoxError::LexerError { .. } | LoxError::ParserError { .. } => std::process::exit(65),
|
||||||
|
LoxError::RuntimeError { .. } => std::process::exit(70),
|
||||||
|
LoxError::Exit { exit_code } => std::process::exit(exit_code),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -48,7 +51,7 @@ fn run_repl() {
|
||||||
|
|
||||||
let mut env = Environment::new();
|
let mut env = Environment::new();
|
||||||
|
|
||||||
'outer: loop {
|
loop {
|
||||||
let mut input_buf = String::new();
|
let mut input_buf = String::new();
|
||||||
|
|
||||||
print!("> ");
|
print!("> ");
|
||||||
|
|
@ -60,7 +63,7 @@ fn run_repl() {
|
||||||
std::process::exit(66);
|
std::process::exit(66);
|
||||||
});
|
});
|
||||||
|
|
||||||
/* let num_open_braces = (input_buf.matches('{').count() as i64) - (input_buf.matches('}').count() as i64);
|
let num_open_braces = (input_buf.matches('{').count() as i64) - (input_buf.matches('}').count() as i64);
|
||||||
let num_open_parens = (input_buf.matches('(').count() as i64) - (input_buf.matches(')').count() as i64);
|
let num_open_parens = (input_buf.matches('(').count() as i64) - (input_buf.matches(')').count() as i64);
|
||||||
let num_open_brackets = (input_buf.matches('[').count() as i64) - (input_buf.matches(']').count() as i64);
|
let num_open_brackets = (input_buf.matches('[').count() as i64) - (input_buf.matches(']').count() as i64);
|
||||||
|
|
||||||
|
|
@ -72,22 +75,21 @@ fn run_repl() {
|
||||||
// any braces/parens/brackets more closing than opening => break (will be parse error)
|
// any braces/parens/brackets more closing than opening => break (will be parse error)
|
||||||
if num_open_braces < 0 || num_open_parens < 0 || num_open_brackets < 0 {
|
if num_open_braces < 0 || num_open_parens < 0 || num_open_brackets < 0 {
|
||||||
break 'inner;
|
break 'inner;
|
||||||
} */
|
}
|
||||||
|
|
||||||
break 'inner;
|
|
||||||
|
|
||||||
print!("< ");
|
print!("< ");
|
||||||
std::io::stdout().flush().unwrap();
|
std::io::stdout().flush().unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
input_buf = input_buf.trim().to_owned();
|
let input_buf = input_buf.trim();
|
||||||
|
|
||||||
if input_buf.is_empty() || input_buf == "exit" || input_buf == "quit" {
|
if input_buf.is_empty() || input_buf == "exit" || input_buf == "quit" {
|
||||||
break 'outer;
|
std::process::exit(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
match run(&input_buf, &mut env) {
|
match run(input_buf, &mut env) {
|
||||||
Ok(()) => {}
|
Ok(()) => {}
|
||||||
|
Err(LoxError::Exit { exit_code }) => std::process::exit(exit_code),
|
||||||
Err(err) => eprintln!("{}", err),
|
Err(err) => eprintln!("{}", err),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -106,7 +108,9 @@ fn run(code_string: &str, env: &mut Environment) -> Result<(), LoxError> {
|
||||||
|
|
||||||
let statements = parse_tokens(tokens)?;
|
let statements = parse_tokens(tokens)?;
|
||||||
|
|
||||||
// println!("{expr}");
|
/* for statement in statements.iter() {
|
||||||
|
println!("{statement}");
|
||||||
|
} */
|
||||||
|
|
||||||
// let mut result = Value::Nil;
|
// let mut result = Value::Nil;
|
||||||
|
|
||||||
|
|
|
||||||
106
src/interpreter/value.rs
Normal file
106
src/interpreter/value.rs
Normal file
|
|
@ -0,0 +1,106 @@
|
||||||
|
use std::cell::UnsafeCell;
|
||||||
|
use std::fmt::{Debug, Display};
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
use super::function::LoxExternFunction;
|
||||||
|
use super::LoxFunction;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub enum Value {
|
||||||
|
Function(Rc<LoxFunction>),
|
||||||
|
ExternFunction(Rc<LoxExternFunction>),
|
||||||
|
String(Rc<String>),
|
||||||
|
Number(f64),
|
||||||
|
Bool(bool),
|
||||||
|
Nil,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Value {
|
||||||
|
pub fn function(fun: LoxFunction) -> Self {
|
||||||
|
let fun = Rc::new(fun);
|
||||||
|
Value::Function(fun)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn extern_function(fun: LoxExternFunction) -> Self {
|
||||||
|
let fun = Rc::new(fun);
|
||||||
|
Value::ExternFunction(fun)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn string(s: impl Into<String>) -> Self {
|
||||||
|
let s = Rc::new(s.into());
|
||||||
|
Value::String(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_truthy(&self) -> bool {
|
||||||
|
!matches!(self, Value::Bool(false) | Value::Nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for Value {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
match self {
|
||||||
|
Value::Function(fun) => write!(f, "{fun}"),
|
||||||
|
Value::ExternFunction(ext_fun) => write!(f, "{ext_fun}"),
|
||||||
|
Value::String(s) => write!(f, "\"{s}\""),
|
||||||
|
Value::Number(num) => write!(f, "{num}"),
|
||||||
|
Value::Bool(b) => write!(f, "{b}"),
|
||||||
|
Value::Nil => write!(f, "nil"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialEq for Value {
|
||||||
|
fn eq(&self, other: &Value) -> bool {
|
||||||
|
match (self, other) {
|
||||||
|
(Value::Function(l0), Value::Function(r0)) => Rc::ptr_eq(l0, r0),
|
||||||
|
(Value::ExternFunction(l0), Value::ExternFunction(r0)) => Rc::ptr_eq(l0, r0),
|
||||||
|
(Value::String(l0), Value::String(r0)) => l0 == r0,
|
||||||
|
(Value::Number(l0), Value::Number(r0)) => l0 == r0,
|
||||||
|
(Value::Bool(l0), Value::Bool(r0)) => l0 == r0,
|
||||||
|
(Value::Nil, Value::Nil) => true,
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*====================================================================================================================*/
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct HeapValue {
|
||||||
|
inner: Rc<UnsafeCell<Value>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl HeapValue {
|
||||||
|
pub fn new(value: Value) -> Self {
|
||||||
|
let inner = Rc::new(UnsafeCell::new(value));
|
||||||
|
HeapValue { inner }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_clone(&self) -> Value {
|
||||||
|
unsafe {
|
||||||
|
let ptr = self.inner.get();
|
||||||
|
let value = &*ptr;
|
||||||
|
value.clone()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn replace(&mut self, value: Value) {
|
||||||
|
let mut value = value;
|
||||||
|
unsafe {
|
||||||
|
let ptr_mut = self.inner.get();
|
||||||
|
std::ptr::swap(&mut value, ptr_mut);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Debug for HeapValue {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
f.debug_struct("HeapValue").field("inner", &self.get_clone()).finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for HeapValue {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
write!(f, "{}", self.get_clone())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -9,9 +9,10 @@ use super::{Token, TokenType};
|
||||||
|
|
||||||
static KEYWORDS: phf::Map<&'static str, TokenType> = phf_map! {
|
static KEYWORDS: phf::Map<&'static str, TokenType> = phf_map! {
|
||||||
"and" => TokenType::And,
|
"and" => TokenType::And,
|
||||||
|
"break" => TokenType::Break,
|
||||||
"class" => TokenType::Class,
|
"class" => TokenType::Class,
|
||||||
"else" => TokenType::Else,
|
"else" => TokenType::Else,
|
||||||
"false" => TokenType::Else,
|
"false" => TokenType::False,
|
||||||
"for" => TokenType::For,
|
"for" => TokenType::For,
|
||||||
"fun" => TokenType::Fun,
|
"fun" => TokenType::Fun,
|
||||||
"if" => TokenType::If,
|
"if" => TokenType::If,
|
||||||
|
|
@ -232,7 +233,7 @@ impl Lexer {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn push_token(&mut self, token_type: TokenType) {
|
fn push_token(&mut self, token_type: TokenType) {
|
||||||
let lexeme: String = self.source[self.start..self.current].iter().collect();
|
// let lexeme: String = self.source[self.start..self.current].iter().collect();
|
||||||
|
|
||||||
self.tokens.push(Token::new(token_type, self.code_pos));
|
self.tokens.push(Token::new(token_type, self.code_pos));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
use crate::misc::CodePos;
|
use crate::misc::CodePos;
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code, clippy::upper_case_acronyms)]
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
#[rustfmt::skip]
|
#[rustfmt::skip]
|
||||||
pub enum TokenType {
|
pub enum TokenType {
|
||||||
|
|
@ -20,12 +20,13 @@ pub enum TokenType {
|
||||||
Number(f64),
|
Number(f64),
|
||||||
|
|
||||||
// Keywords
|
// Keywords
|
||||||
And, Class, Else, False, Fun, For, If, Nil, Or,
|
And, Break, Class, Else, False, Fun, For, If, Nil, Or,
|
||||||
Print, Return, Super, This, True, Var, While,
|
Print, Return, Super, This, True, Var, While,
|
||||||
|
|
||||||
EOF
|
EOF
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
pub struct Token {
|
pub struct Token {
|
||||||
pub token_type: TokenType,
|
pub token_type: TokenType,
|
||||||
// pub lexeme: String,
|
// pub lexeme: String,
|
||||||
|
|
|
||||||
|
|
@ -23,3 +23,12 @@ impl Debug for CodePos {
|
||||||
write!(f, "{}:{}", self.line, self.col)
|
write!(f, "{}:{}", self.line, self.col)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*====================================================================================================================*/
|
||||||
|
|
||||||
|
pub fn indent(s: String) -> String {
|
||||||
|
s.split('\n')
|
||||||
|
.map(|line| format!("\t{line}"))
|
||||||
|
.collect::<Vec<String>>()
|
||||||
|
.join("\n")
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,18 +1,44 @@
|
||||||
program -> statement* EOF ;
|
program -> statement* EOF ;
|
||||||
|
|
||||||
statement -> exprStmt | printStmt | declaration | block ;
|
statement -> if_stmt
|
||||||
|
| print_stmt
|
||||||
|
| while_stmt
|
||||||
|
| for_stmt
|
||||||
|
| declaration
|
||||||
|
| block
|
||||||
|
| expr_stmt
|
||||||
|
| break
|
||||||
|
| return_stmt ;
|
||||||
|
|
||||||
exprStmt -> expression ";" ;
|
if_stmt -> "if" "(" expression ")" statement ( "else" statement )? ;
|
||||||
printStmt -> "print" expression ";" ;
|
print_stmt -> "print" expression ";" ;
|
||||||
declaration -> "var" IDENTIFIER ( "=" expression )? ";" ;
|
while_stmt -> "while" "(" expression ")" statement ;
|
||||||
|
for_stmt -> "for" "(" (declaration | expr_stmt | ";") ";" expression? ";" expression ";" ")" statement ;
|
||||||
|
declaration -> var_decl | fun_decl ;
|
||||||
block -> "{" statement* "}" ;
|
block -> "{" statement* "}" ;
|
||||||
|
expr_Stmt -> expression ";" ;
|
||||||
|
break -> "break" ";" ;
|
||||||
|
return -> "return" expression? ";" ;
|
||||||
|
|
||||||
|
var_decl -> "var" IDENTIFIER ( "=" expression )? ";"
|
||||||
|
fun_decl -> "fun" IDENTIFIER "(" parameters ")" block ;
|
||||||
|
|
||||||
expression -> assignment
|
expression -> assignment
|
||||||
|
|
||||||
assignment -> IDENTIFIER "=" assignment | equality ;
|
assignment -> IDENTIFIER "=" assignment | logic_or ;
|
||||||
|
logic_or -> logic_and ( "or" logic_and )* ;
|
||||||
|
logic_and -> equality ( "and" equality )* ;
|
||||||
equality -> comparison ( ( "==" | "!=" ) comparison )* ;
|
equality -> comparison ( ( "==" | "!=" ) comparison )* ;
|
||||||
comparison -> term ( ">" | ">=" | "<" | "<=" term )* ;
|
comparison -> term ( ">" | ">=" | "<" | "<=" term )* ;
|
||||||
term -> factor ( ( "+" | "-" ) factor )*
|
term -> factor ( ( "+" | "-" ) factor )*
|
||||||
factor -> unary ( ( "*" | "/" ) unary )* ;
|
factor -> unary ( ( "*" | "/" ) unary )* ;
|
||||||
unary -> ( "!" | "-" ) unary | primary ;
|
unary -> ( "!" | "-" ) unary | call ;
|
||||||
primary -> "(" expression ")" | IDENTIFIER | NUMBER | STRING | "true" | "false" | "nil" ;
|
|
||||||
|
call -> primary ( "(" arguments? ")" )* ;
|
||||||
|
arguments -> expression ( "," expression )* ;
|
||||||
|
|
||||||
|
primary -> "(" expression ")" | IDENTIFIER | fun_expr | NUMBER | STRING | "true" | "false" | "nil" ;
|
||||||
|
|
||||||
|
fun_expr -> "fun" "(" parameters ")" block ;
|
||||||
|
|
||||||
|
parameters -> ( IDENTIFIER ( "," IDENTIFIER )* )? ;
|
||||||
|
|
@ -1,6 +1,9 @@
|
||||||
use std::fmt::Display;
|
use std::fmt::Display;
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
#[derive(Debug)]
|
use super::Stmt;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
pub enum Expr {
|
pub enum Expr {
|
||||||
Literal {
|
Literal {
|
||||||
literal: Literal,
|
literal: Literal,
|
||||||
|
|
@ -14,6 +17,11 @@ pub enum Expr {
|
||||||
op: BinaryOp,
|
op: BinaryOp,
|
||||||
right: Box<Expr>,
|
right: Box<Expr>,
|
||||||
},
|
},
|
||||||
|
Logical {
|
||||||
|
left: Box<Expr>,
|
||||||
|
op: LogicalOp,
|
||||||
|
right: Box<Expr>,
|
||||||
|
},
|
||||||
Grouping {
|
Grouping {
|
||||||
expr: Box<Expr>,
|
expr: Box<Expr>,
|
||||||
},
|
},
|
||||||
|
|
@ -24,12 +32,22 @@ pub enum Expr {
|
||||||
name: String,
|
name: String,
|
||||||
value: Box<Expr>,
|
value: Box<Expr>,
|
||||||
},
|
},
|
||||||
|
Call {
|
||||||
|
callee: Box<Expr>,
|
||||||
|
args: Vec<Expr>,
|
||||||
|
},
|
||||||
|
Function {
|
||||||
|
name: String,
|
||||||
|
param_names: Vec<String>,
|
||||||
|
body: Box<Stmt>,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Expr {
|
impl Expr {
|
||||||
pub fn string(s: String) -> Self {
|
pub fn string(s: impl Into<String>) -> Self {
|
||||||
|
let s = s.into();
|
||||||
Expr::Literal {
|
Expr::Literal {
|
||||||
literal: Literal::String(s),
|
literal: Literal::String(Rc::new(s)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -60,15 +78,36 @@ impl Expr {
|
||||||
Expr::Binary { left, op, right }
|
Expr::Binary { left, op, right }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn logical(left: Expr, op: LogicalOp, right: Expr) -> Self {
|
||||||
|
let left = Box::new(left);
|
||||||
|
let right = Box::new(right);
|
||||||
|
Expr::Logical { left, op, right }
|
||||||
|
}
|
||||||
|
|
||||||
pub fn grouping(expr: Expr) -> Self {
|
pub fn grouping(expr: Expr) -> Self {
|
||||||
let expr = Box::new(expr);
|
let expr = Box::new(expr);
|
||||||
Expr::Grouping { expr }
|
Expr::Grouping { expr }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn assignment(name: String, value: Expr) -> Self {
|
pub fn assignment(name: impl Into<String>, value: Expr) -> Self {
|
||||||
|
let name = name.into();
|
||||||
let value = Box::new(value);
|
let value = Box::new(value);
|
||||||
Expr::Assignment { name, value }
|
Expr::Assignment { name, value }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn call(callee: Expr, args: Vec<Expr>) -> Self {
|
||||||
|
let callee = Box::new(callee);
|
||||||
|
Expr::Call { callee, args }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn function(name: String, param_names: Vec<String>, body: Stmt) -> Self {
|
||||||
|
let body = Box::new(body);
|
||||||
|
Self::Function {
|
||||||
|
name,
|
||||||
|
param_names,
|
||||||
|
body,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Display for Expr {
|
impl Display for Expr {
|
||||||
|
|
@ -77,18 +116,36 @@ impl Display for Expr {
|
||||||
Expr::Literal { literal } => write!(f, "{literal}"),
|
Expr::Literal { literal } => write!(f, "{literal}"),
|
||||||
Expr::Unary { op, expr } => write!(f, "({op} {expr})"),
|
Expr::Unary { op, expr } => write!(f, "({op} {expr})"),
|
||||||
Expr::Binary { left, op, right } => write!(f, "({op} {left} {right})"),
|
Expr::Binary { left, op, right } => write!(f, "({op} {left} {right})"),
|
||||||
|
Expr::Logical { left, op, right } => {
|
||||||
|
write!(f, "({op} {left} {right})")
|
||||||
|
}
|
||||||
Expr::Grouping { expr } => write!(f, "(group {expr})"),
|
Expr::Grouping { expr } => write!(f, "(group {expr})"),
|
||||||
Expr::Variable { name } => write!(f, "{name}"),
|
Expr::Variable { name } => write!(f, "{name}"),
|
||||||
Expr::Assignment { name, value } => write!(f, "{name} = {value}"),
|
Expr::Assignment { name, value } => write!(f, "{name} = {value}"),
|
||||||
|
Expr::Call { callee, args } => write!(
|
||||||
|
f,
|
||||||
|
"({callee} {})",
|
||||||
|
args.iter()
|
||||||
|
.map(|arg| format!("{arg}"))
|
||||||
|
.collect::<Vec<String>>()
|
||||||
|
.join(" ")
|
||||||
|
),
|
||||||
|
Expr::Function {
|
||||||
|
name,
|
||||||
|
param_names,
|
||||||
|
body,
|
||||||
|
} => {
|
||||||
|
write!(f, "fun {name}({}) {body}", param_names.join(", "))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*====================================================================================================================*/
|
/*====================================================================================================================*/
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum Literal {
|
pub enum Literal {
|
||||||
String(String),
|
String(Rc<String>),
|
||||||
Number(f64),
|
Number(f64),
|
||||||
Bool(bool),
|
Bool(bool),
|
||||||
Nil,
|
Nil,
|
||||||
|
|
@ -107,7 +164,7 @@ impl Display for Literal {
|
||||||
|
|
||||||
/*====================================================================================================================*/
|
/*====================================================================================================================*/
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
pub enum UnaryOp {
|
pub enum UnaryOp {
|
||||||
Negate,
|
Negate,
|
||||||
Not,
|
Not,
|
||||||
|
|
@ -124,7 +181,7 @@ impl Display for UnaryOp {
|
||||||
|
|
||||||
/*====================================================================================================================*/
|
/*====================================================================================================================*/
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
#[rustfmt::skip]
|
#[rustfmt::skip]
|
||||||
pub enum BinaryOp {
|
pub enum BinaryOp {
|
||||||
// arithmetic
|
// arithmetic
|
||||||
|
|
@ -154,3 +211,18 @@ impl Display for BinaryOp {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
pub enum LogicalOp {
|
||||||
|
Or,
|
||||||
|
And,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for LogicalOp {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
match self {
|
||||||
|
LogicalOp::Or => write!(f, "or"),
|
||||||
|
LogicalOp::And => write!(f, "and"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,6 @@ mod expr;
|
||||||
mod parser;
|
mod parser;
|
||||||
mod stmt;
|
mod stmt;
|
||||||
|
|
||||||
pub use expr::{BinaryOp, Expr, Literal, UnaryOp};
|
pub use expr::{BinaryOp, Expr, Literal, LogicalOp, UnaryOp};
|
||||||
pub use parser::parse_tokens;
|
pub use parser::parse_tokens;
|
||||||
pub use stmt::Stmt;
|
pub use stmt::Stmt;
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ use crate::lexer::{Token, TokenType};
|
||||||
use crate::parser::expr::BinaryOp;
|
use crate::parser::expr::BinaryOp;
|
||||||
|
|
||||||
use super::expr::{Expr, UnaryOp};
|
use super::expr::{Expr, UnaryOp};
|
||||||
use super::Stmt;
|
use super::{LogicalOp, Stmt};
|
||||||
|
|
||||||
/*====================================================================================================================*/
|
/*====================================================================================================================*/
|
||||||
|
|
||||||
|
|
@ -60,12 +60,15 @@ impl Iterator for TokenIter {
|
||||||
|
|
||||||
struct Parser {
|
struct Parser {
|
||||||
token_iter: TokenIter,
|
token_iter: TokenIter,
|
||||||
|
|
||||||
|
parse_errors: Vec<ParserError>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Parser {
|
impl Parser {
|
||||||
pub fn new(tokens: Vec<Token>) -> Self {
|
pub fn new(tokens: Vec<Token>) -> Self {
|
||||||
Parser {
|
Parser {
|
||||||
token_iter: TokenIter::new(tokens),
|
token_iter: TokenIter::new(tokens),
|
||||||
|
parse_errors: Vec::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -73,7 +76,7 @@ impl Parser {
|
||||||
let mut me = self;
|
let mut me = self;
|
||||||
|
|
||||||
let mut statements = Vec::new();
|
let mut statements = Vec::new();
|
||||||
let mut parse_errors = Vec::new();
|
// let mut parse_errors = Vec::new();
|
||||||
|
|
||||||
while !me.token_iter.is_empty() && me.peek_token().token_type != TokenType::EOF {
|
while !me.token_iter.is_empty() && me.peek_token().token_type != TokenType::EOF {
|
||||||
// statements.push(me.statement()?);
|
// statements.push(me.statement()?);
|
||||||
|
|
@ -82,16 +85,18 @@ impl Parser {
|
||||||
statements.push(stmt);
|
statements.push(stmt);
|
||||||
}
|
}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
parse_errors.push(err);
|
me.parse_errors.push(err);
|
||||||
|
// println!("Synchronising...");
|
||||||
me.synchronise();
|
me.synchronise();
|
||||||
|
// println!("Synchronised")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// me.consume_token(TokenType::EOF).unwrap();
|
// me.consume_token(TokenType::EOF).unwrap();
|
||||||
|
|
||||||
if !parse_errors.is_empty() {
|
if !me.parse_errors.is_empty() {
|
||||||
Err(parse_errors)
|
Err(me.parse_errors)
|
||||||
} else {
|
} else {
|
||||||
Ok(statements)
|
Ok(statements)
|
||||||
}
|
}
|
||||||
|
|
@ -126,18 +131,133 @@ impl Parser {
|
||||||
|
|
||||||
// no sync point: discard token
|
// no sync point: discard token
|
||||||
let _ = self.next_token();
|
let _ = self.next_token();
|
||||||
|
// println!("Discarding {} token", self.next_token());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn statement(&mut self) -> ParserResult<Stmt> {
|
fn statement(&mut self) -> ParserResult<Stmt> {
|
||||||
match self.peek_token().token_type {
|
match self.peek_token().token_type {
|
||||||
TokenType::Print => self.print_statement(),
|
TokenType::Print => self.print_statement(),
|
||||||
|
TokenType::If => self.if_statement(),
|
||||||
|
TokenType::While => self.while_statement(),
|
||||||
|
TokenType::For => self.for_statement(),
|
||||||
TokenType::Var => self.var_declaration(),
|
TokenType::Var => self.var_declaration(),
|
||||||
|
TokenType::Fun => self.fun_declaration(),
|
||||||
TokenType::LeftBrace => self.block(),
|
TokenType::LeftBrace => self.block(),
|
||||||
|
TokenType::Break => {
|
||||||
|
assert_eq!(self.next_token().token_type, TokenType::Break);
|
||||||
|
self.semicolon()?;
|
||||||
|
Ok(Stmt::Break)
|
||||||
|
}
|
||||||
|
TokenType::Return => {
|
||||||
|
assert_eq!(self.next_token().token_type, TokenType::Return);
|
||||||
|
let expr = match self.peek_token().token_type {
|
||||||
|
TokenType::Semicolon => Expr::nil(),
|
||||||
|
_ => self.expression()?,
|
||||||
|
};
|
||||||
|
self.semicolon()?;
|
||||||
|
Ok(Stmt::return_stmt(expr))
|
||||||
|
}
|
||||||
_ => self.expression_statement(),
|
_ => self.expression_statement(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn if_statement(&mut self) -> ParserResult<Stmt> {
|
||||||
|
assert_eq!(self.next_token().token_type, TokenType::If);
|
||||||
|
|
||||||
|
self.consume_token(TokenType::LeftParen, |token| ParserError::MissingParenAfterIf {
|
||||||
|
code_pos: token.code_pos,
|
||||||
|
})?;
|
||||||
|
|
||||||
|
let condition = self.expression()?;
|
||||||
|
|
||||||
|
self.consume_token(TokenType::RightParen, |token| ParserError::MissingRightParen {
|
||||||
|
code_pos: token.code_pos,
|
||||||
|
})?;
|
||||||
|
|
||||||
|
let then_branch = self.statement()?;
|
||||||
|
|
||||||
|
let else_branch = if self.peek_token().token_type == TokenType::Else {
|
||||||
|
// consume else token
|
||||||
|
let _ = self.next_token();
|
||||||
|
|
||||||
|
Some(self.statement()?)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(Stmt::if_stmt(condition, then_branch, else_branch))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn while_statement(&mut self) -> ParserResult<Stmt> {
|
||||||
|
assert_eq!(self.next_token().token_type, TokenType::While);
|
||||||
|
|
||||||
|
self.consume_token(TokenType::LeftParen, |token| ParserError::MissingParenAfterWhile {
|
||||||
|
code_pos: token.code_pos,
|
||||||
|
})?;
|
||||||
|
|
||||||
|
let condition = self.expression()?;
|
||||||
|
|
||||||
|
self.consume_token(TokenType::RightParen, |token| ParserError::MissingRightParen {
|
||||||
|
code_pos: token.code_pos,
|
||||||
|
})?;
|
||||||
|
|
||||||
|
let body = self.statement()?;
|
||||||
|
|
||||||
|
Ok(Stmt::while_stmt(condition, body))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn for_statement(&mut self) -> ParserResult<Stmt> {
|
||||||
|
assert_eq!(self.next_token().token_type, TokenType::For);
|
||||||
|
|
||||||
|
self.consume_token(TokenType::LeftParen, |token| ParserError::MissingParenAfterFor {
|
||||||
|
code_pos: token.code_pos,
|
||||||
|
})?;
|
||||||
|
|
||||||
|
let initializer = match self.peek_token().token_type {
|
||||||
|
TokenType::Semicolon => {
|
||||||
|
assert_eq!(self.next_token().token_type, TokenType::Semicolon);
|
||||||
|
None
|
||||||
|
}
|
||||||
|
TokenType::Var => Some(self.var_declaration()?),
|
||||||
|
_ => Some(self.expression_statement()?),
|
||||||
|
};
|
||||||
|
|
||||||
|
let condition = match self.peek_token().token_type {
|
||||||
|
TokenType::Semicolon => Expr::bool(true),
|
||||||
|
_ => self.expression()?,
|
||||||
|
};
|
||||||
|
|
||||||
|
self.semicolon()?;
|
||||||
|
|
||||||
|
let increment = match self.peek_token().token_type {
|
||||||
|
TokenType::RightParen => None,
|
||||||
|
_ => Some(self.expression()?),
|
||||||
|
};
|
||||||
|
|
||||||
|
self.consume_token(TokenType::RightParen, |token| ParserError::MissingRightParen {
|
||||||
|
code_pos: token.code_pos,
|
||||||
|
})?;
|
||||||
|
|
||||||
|
let mut body = self.statement()?;
|
||||||
|
|
||||||
|
if let Some(increment) = increment {
|
||||||
|
body = Stmt::Block {
|
||||||
|
statements: vec![body, Stmt::expr_stmt(increment)],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut for_stmt = Stmt::while_stmt(condition, body);
|
||||||
|
|
||||||
|
if let Some(initializer) = initializer {
|
||||||
|
for_stmt = Stmt::Block {
|
||||||
|
statements: vec![initializer, for_stmt],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(for_stmt)
|
||||||
|
}
|
||||||
|
|
||||||
fn print_statement(&mut self) -> ParserResult<Stmt> {
|
fn print_statement(&mut self) -> ParserResult<Stmt> {
|
||||||
// self.consume_token(TokenType::Print)?;
|
// self.consume_token(TokenType::Print)?;
|
||||||
assert_eq!(self.next_token().token_type, TokenType::Print);
|
assert_eq!(self.next_token().token_type, TokenType::Print);
|
||||||
|
|
@ -175,6 +295,74 @@ impl Parser {
|
||||||
Ok(Stmt::var_decl(name, initializer))
|
Ok(Stmt::var_decl(name, initializer))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn fun_declaration(&mut self) -> ParserResult<Stmt> {
|
||||||
|
assert_eq!(self.next_token().token_type, TokenType::Fun);
|
||||||
|
|
||||||
|
let name = self.identifier("Missing function name")?;
|
||||||
|
|
||||||
|
let fun = self.fun_params_and_body(name.clone())?;
|
||||||
|
|
||||||
|
Ok(Stmt::var_decl(name, fun))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn fun_params_and_body(&mut self, name: impl Into<String>) -> ParserResult<Expr> {
|
||||||
|
// <Fun> token has already been eaten by primary or fun_declaration
|
||||||
|
// assert_eq!(self.next_token().token_type, TokenType::Fun);
|
||||||
|
|
||||||
|
if self.peek_token().token_type != TokenType::LeftParen {
|
||||||
|
return Err(ParserError::MissingFunctionArgs {
|
||||||
|
code_pos: self.peek_token().code_pos,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
let params_code_pos = self.peek_token().code_pos;
|
||||||
|
|
||||||
|
let param_names = self.collect_params()?;
|
||||||
|
|
||||||
|
if param_names.len() > 255 {
|
||||||
|
self.parse_errors.push(ParserError::TooManyParams {
|
||||||
|
code_pos: params_code_pos,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.peek_token().token_type != TokenType::LeftBrace {
|
||||||
|
return Err(ParserError::MissingFunctionBody {
|
||||||
|
code_pos: self.peek_token().code_pos,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
let body = self.block()?;
|
||||||
|
|
||||||
|
let name = name.into();
|
||||||
|
Ok(Expr::function(name, param_names, body))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn collect_params(&mut self) -> ParserResult<Vec<String>> {
|
||||||
|
assert_eq!(self.next_token().token_type, TokenType::LeftParen);
|
||||||
|
|
||||||
|
if self.peek_token().token_type == TokenType::RightParen {
|
||||||
|
assert_eq!(self.next_token().token_type, TokenType::RightParen);
|
||||||
|
|
||||||
|
return Ok(Vec::new());
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut param_names = Vec::new();
|
||||||
|
|
||||||
|
param_names.push(self.identifier("Expected parameter name")?);
|
||||||
|
|
||||||
|
while self.peek_token().token_type == TokenType::Comma {
|
||||||
|
assert_eq!(self.next_token().token_type, TokenType::Comma);
|
||||||
|
|
||||||
|
param_names.push(self.identifier("Expected parameter name")?);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.consume_token(TokenType::RightParen, |token| ParserError::MissingRightParen {
|
||||||
|
code_pos: token.code_pos,
|
||||||
|
})?;
|
||||||
|
|
||||||
|
Ok(param_names)
|
||||||
|
}
|
||||||
|
|
||||||
fn block(&mut self) -> ParserResult<Stmt> {
|
fn block(&mut self) -> ParserResult<Stmt> {
|
||||||
// self.consume_token(TokenType::LeftBrace)?;
|
// self.consume_token(TokenType::LeftBrace)?;
|
||||||
assert_eq!(self.next_token().token_type, TokenType::LeftBrace);
|
assert_eq!(self.next_token().token_type, TokenType::LeftBrace);
|
||||||
|
|
@ -215,7 +403,7 @@ impl Parser {
|
||||||
fn assignment(&mut self) -> ParserResult<Expr> {
|
fn assignment(&mut self) -> ParserResult<Expr> {
|
||||||
let code_pos = self.peek_token().code_pos;
|
let code_pos = self.peek_token().code_pos;
|
||||||
|
|
||||||
let expr = self.equality()?;
|
let expr = self.logical_or()?;
|
||||||
|
|
||||||
if self.peek_token().token_type != TokenType::Equal {
|
if self.peek_token().token_type != TokenType::Equal {
|
||||||
return Ok(expr);
|
return Ok(expr);
|
||||||
|
|
@ -232,6 +420,36 @@ impl Parser {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn logical_or(&mut self) -> ParserResult<Expr> {
|
||||||
|
let mut expr = self.logical_and()?;
|
||||||
|
|
||||||
|
if self.peek_token().token_type == TokenType::Or {
|
||||||
|
// consume or
|
||||||
|
let _ = self.next_token();
|
||||||
|
|
||||||
|
let right = self.logical_or()?;
|
||||||
|
|
||||||
|
expr = Expr::logical(expr, LogicalOp::Or, right);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(expr)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn logical_and(&mut self) -> ParserResult<Expr> {
|
||||||
|
let mut expr = self.equality()?;
|
||||||
|
|
||||||
|
if self.peek_token().token_type == TokenType::And {
|
||||||
|
// consume and
|
||||||
|
let _ = self.next_token();
|
||||||
|
|
||||||
|
let right = self.logical_and()?;
|
||||||
|
|
||||||
|
expr = Expr::logical(expr, LogicalOp::And, right);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(expr)
|
||||||
|
}
|
||||||
|
|
||||||
fn equality(&mut self) -> ParserResult<Expr> {
|
fn equality(&mut self) -> ParserResult<Expr> {
|
||||||
let mut expr = self.comparison()?;
|
let mut expr = self.comparison()?;
|
||||||
|
|
||||||
|
|
@ -329,10 +547,62 @@ impl Parser {
|
||||||
let _ = self.next_token();
|
let _ = self.next_token();
|
||||||
Ok(Expr::unary(UnaryOp::Negate, self.unary()?))
|
Ok(Expr::unary(UnaryOp::Negate, self.unary()?))
|
||||||
}
|
}
|
||||||
_ => self.primary(),
|
_ => self.call(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn call(&mut self) -> ParserResult<Expr> {
|
||||||
|
let mut expr = self.primary()?;
|
||||||
|
|
||||||
|
loop {
|
||||||
|
match self.peek_token().token_type {
|
||||||
|
TokenType::LeftParen => {
|
||||||
|
let args_code_pos = self.peek_token().code_pos;
|
||||||
|
|
||||||
|
let args = self.collect_args()?;
|
||||||
|
|
||||||
|
if args.len() > 255 {
|
||||||
|
self.parse_errors.push(ParserError::TooManyArguments {
|
||||||
|
code_pos: args_code_pos,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
expr = Expr::call(expr, args);
|
||||||
|
}
|
||||||
|
TokenType::Dot => todo!(),
|
||||||
|
_ => break,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(expr)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn collect_args(&mut self) -> ParserResult<Vec<Expr>> {
|
||||||
|
assert_eq!(self.next_token().token_type, TokenType::LeftParen);
|
||||||
|
|
||||||
|
if self.peek_token().token_type == TokenType::RightParen {
|
||||||
|
assert_eq!(self.next_token().token_type, TokenType::RightParen);
|
||||||
|
|
||||||
|
return Ok(Vec::new());
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut args = Vec::new();
|
||||||
|
|
||||||
|
args.push(self.expression()?);
|
||||||
|
|
||||||
|
while self.peek_token().token_type == TokenType::Comma {
|
||||||
|
assert_eq!(self.next_token().token_type, TokenType::Comma);
|
||||||
|
|
||||||
|
args.push(self.expression()?);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.consume_token(TokenType::RightParen, |token| ParserError::MissingRightParen {
|
||||||
|
code_pos: token.code_pos,
|
||||||
|
})?;
|
||||||
|
|
||||||
|
Ok(args)
|
||||||
|
}
|
||||||
|
|
||||||
fn primary(&mut self) -> ParserResult<Expr> {
|
fn primary(&mut self) -> ParserResult<Expr> {
|
||||||
if self.peek_token().token_type == TokenType::EOF {
|
if self.peek_token().token_type == TokenType::EOF {
|
||||||
return Err(ParserError::TokenStreamEnded);
|
return Err(ParserError::TokenStreamEnded);
|
||||||
|
|
@ -341,6 +611,7 @@ impl Parser {
|
||||||
let token = self.next_token();
|
let token = self.next_token();
|
||||||
|
|
||||||
match token.token_type {
|
match token.token_type {
|
||||||
|
TokenType::Fun => Ok(self.fun_params_and_body("<lambda>")?),
|
||||||
TokenType::Number(num) => Ok(Expr::number(num)),
|
TokenType::Number(num) => Ok(Expr::number(num)),
|
||||||
TokenType::String(s) => Ok(Expr::string(s)),
|
TokenType::String(s) => Ok(Expr::string(s)),
|
||||||
TokenType::False => Ok(Expr::bool(false)),
|
TokenType::False => Ok(Expr::bool(false)),
|
||||||
|
|
@ -348,56 +619,54 @@ impl Parser {
|
||||||
TokenType::Nil => Ok(Expr::nil()),
|
TokenType::Nil => Ok(Expr::nil()),
|
||||||
TokenType::LeftParen => {
|
TokenType::LeftParen => {
|
||||||
let expr = self.expression()?;
|
let expr = self.expression()?;
|
||||||
// self.consume_token(TokenType::RightParen)?;
|
|
||||||
let peek_token = self.peek_token();
|
|
||||||
if peek_token.token_type != TokenType::RightParen {
|
|
||||||
return Err(ParserError::MissingRightParenthesis {
|
|
||||||
code_pos: token.code_pos,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
let _ = self.next_token();
|
self.consume_token(TokenType::RightParen, |token| ParserError::MissingRightParen {
|
||||||
|
code_pos: token.code_pos,
|
||||||
|
})?;
|
||||||
|
|
||||||
Ok(Expr::grouping(expr))
|
Ok(Expr::grouping(expr))
|
||||||
}
|
}
|
||||||
TokenType::Identifier(name) => Ok(Expr::Variable { name }),
|
TokenType::Identifier(name) => Ok(Expr::Variable { name }),
|
||||||
_ => Err(ParserError::UnexpectedToken { token }),
|
_ => Err(ParserError::ExpectedPrimary { token }),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn semicolon(&mut self) -> ParserResult<()> {
|
fn semicolon(&mut self) -> ParserResult<()> {
|
||||||
/* match self.next_token() {
|
self.consume_token(TokenType::Semicolon, |token| ParserError::MissingSemicolon {
|
||||||
token if token.token_type == TokenType::Semicolon => Ok(()),
|
code_pos: token.code_pos,
|
||||||
token => Err(ParserError::MissingSemicolon {
|
})
|
||||||
code_pos: token.code_pos,
|
}
|
||||||
|
|
||||||
|
fn identifier(&mut self, msg: &str) -> ParserResult<String> {
|
||||||
|
match self.peek_token().token_type {
|
||||||
|
TokenType::Identifier(_) => {
|
||||||
|
if let TokenType::Identifier(name) = self.next_token().token_type {
|
||||||
|
Ok(name)
|
||||||
|
} else {
|
||||||
|
unreachable!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => Err(ParserError::MissingIdentifier {
|
||||||
|
msg: msg.to_owned(),
|
||||||
|
code_pos: self.peek_token().code_pos,
|
||||||
}),
|
}),
|
||||||
// None => Err(ParserError::TokenStreamEnded),
|
|
||||||
} */
|
|
||||||
|
|
||||||
let peek_token = self.peek_token();
|
|
||||||
|
|
||||||
if peek_token.token_type == TokenType::Semicolon {
|
|
||||||
self.next_token();
|
|
||||||
Ok(())
|
|
||||||
} else {
|
|
||||||
Err(ParserError::MissingSemicolon {
|
|
||||||
code_pos: peek_token.code_pos,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn next_token(&mut self) -> Token {
|
fn next_token(&mut self) -> Token {
|
||||||
/* let next = self.token_iter.next();
|
/* let token = self.token_iter.next().unwrap();
|
||||||
|
|
||||||
// println!("Next token: {next:?}");
|
// println!("Next token: {next:?}");
|
||||||
|
|
||||||
if let Some(ref token) = next {
|
if token.token_type == TokenType::EOF {
|
||||||
if token.token_type == TokenType::EOF {
|
panic!("Someone ate a EOF token");
|
||||||
panic!("Someone ate a EOF token");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
next.ok_or(ParserError::TokenStreamEnded) */
|
// if token.token_type == TokenType::Print {
|
||||||
|
// panic!("Found the print");
|
||||||
|
// }
|
||||||
|
|
||||||
|
token */
|
||||||
|
|
||||||
self.token_iter.next().unwrap() // .ok_or(ParserError::TokenStreamEnded)
|
self.token_iter.next().unwrap() // .ok_or(ParserError::TokenStreamEnded)
|
||||||
}
|
}
|
||||||
|
|
@ -406,16 +675,25 @@ impl Parser {
|
||||||
self.token_iter.peek().unwrap() // .ok_or(ParserError::TokenStreamEnded)
|
self.token_iter.peek().unwrap() // .ok_or(ParserError::TokenStreamEnded)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* fn consume_token(&mut self, token_type: TokenType) -> ParserResult<bool> {
|
fn consume_token<F>(&mut self, token_type: TokenType, err_fn: F) -> ParserResult<()>
|
||||||
/* self.next_token().and_then(|token| {
|
where
|
||||||
if token.token_type == token_type {
|
F: Fn(Token) -> ParserError,
|
||||||
Some(token)
|
{
|
||||||
} else {
|
/* let token = self.next_token();
|
||||||
// Err(ParserError::UnexpectedToken { token })
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}) */
|
|
||||||
|
|
||||||
Ok(self.next_token().token_type == token_type)
|
if token.token_type == token_type {
|
||||||
} */
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(err_fn(token))
|
||||||
|
} */
|
||||||
|
|
||||||
|
match &self.peek_token().token_type {
|
||||||
|
tt if tt == &token_type => {
|
||||||
|
let _ = self.next_token();
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
TokenType::EOF => Err(err_fn(self.peek_token().clone())),
|
||||||
|
_ => Err(err_fn(self.next_token())),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,49 +1,129 @@
|
||||||
use std::fmt::Display;
|
use std::fmt::Display;
|
||||||
|
|
||||||
|
use crate::misc::indent;
|
||||||
|
|
||||||
use super::Expr;
|
use super::Expr;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
pub enum Stmt {
|
pub enum Stmt {
|
||||||
ExprStmt { expr: Box<Expr> },
|
Print {
|
||||||
Print { expr: Box<Expr> },
|
expr: Box<Expr>,
|
||||||
VarDecl { name: String, initializer: Box<Expr> },
|
},
|
||||||
Block { statements: Vec<Stmt> },
|
IfStmt {
|
||||||
|
condition: Box<Expr>,
|
||||||
|
then_branch: Box<Stmt>,
|
||||||
|
else_branch: Option<Box<Stmt>>,
|
||||||
|
},
|
||||||
|
While {
|
||||||
|
condition: Box<Expr>,
|
||||||
|
body: Box<Stmt>,
|
||||||
|
},
|
||||||
|
VarDecl {
|
||||||
|
name: String,
|
||||||
|
initializer: Box<Expr>,
|
||||||
|
},
|
||||||
|
Block {
|
||||||
|
statements: Vec<Stmt>,
|
||||||
|
},
|
||||||
|
ExprStmt {
|
||||||
|
expr: Box<Expr>,
|
||||||
|
},
|
||||||
|
Break,
|
||||||
|
Return {
|
||||||
|
expr: Box<Expr>,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Stmt {
|
impl Stmt {
|
||||||
pub fn expr_stmt(expr: Expr) -> Self {
|
|
||||||
let expr = Box::new(expr);
|
|
||||||
Stmt::ExprStmt { expr }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn print_stmt(expr: Expr) -> Self {
|
pub fn print_stmt(expr: Expr) -> Self {
|
||||||
let expr = Box::new(expr);
|
let expr = Box::new(expr);
|
||||||
Stmt::Print { expr }
|
Stmt::Print { expr }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn var_decl(name: String, initializer: Expr) -> Self {
|
pub fn if_stmt(
|
||||||
let initializer = Box::new(initializer);
|
condition: impl Into<Box<Expr>>,
|
||||||
|
then_branch: impl Into<Box<Stmt>>,
|
||||||
|
else_branch: Option<impl Into<Box<Stmt>>>,
|
||||||
|
) -> Self {
|
||||||
|
let condition = condition.into();
|
||||||
|
let then_branch = then_branch.into();
|
||||||
|
let else_branch = else_branch.map(|stmt| stmt.into());
|
||||||
|
|
||||||
|
Stmt::IfStmt {
|
||||||
|
condition,
|
||||||
|
then_branch,
|
||||||
|
else_branch,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn while_stmt(condition: impl Into<Box<Expr>>, body: impl Into<Box<Stmt>>) -> Self {
|
||||||
|
let condition = condition.into();
|
||||||
|
let body = body.into();
|
||||||
|
|
||||||
|
Stmt::While { condition, body }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn var_decl(name: impl Into<String>, initializer: impl Into<Box<Expr>>) -> Self {
|
||||||
|
let name = name.into();
|
||||||
|
let initializer = initializer.into();
|
||||||
Stmt::VarDecl { name, initializer }
|
Stmt::VarDecl { name, initializer }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn expr_stmt(expr: impl Into<Box<Expr>>) -> Self {
|
||||||
|
let expr = expr.into();
|
||||||
|
Stmt::ExprStmt { expr }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn return_stmt(expr: impl Into<Box<Expr>>) -> Self {
|
||||||
|
let expr = expr.into();
|
||||||
|
Stmt::Return { expr }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Display for Stmt {
|
impl Display for Stmt {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
Stmt::ExprStmt { expr } => write!(f, "{expr}"),
|
Stmt::Print { expr } => write!(f, "print {expr};"),
|
||||||
Stmt::Print { expr } => write!(f, "print {expr}"),
|
Stmt::IfStmt {
|
||||||
Stmt::VarDecl { name, initializer } => write!(f, "var {name} = {initializer}"),
|
condition,
|
||||||
|
then_branch,
|
||||||
|
else_branch,
|
||||||
|
} => {
|
||||||
|
writeln!(f, "if {condition}")?;
|
||||||
|
match then_branch.as_ref() {
|
||||||
|
Stmt::Block { .. } => write!(f, "{then_branch}")?,
|
||||||
|
_ => write!(f, "{}", indent(format!("{then_branch}")))?,
|
||||||
|
}
|
||||||
|
if let Some(else_branch) = else_branch {
|
||||||
|
writeln!(f, "\nelse")?;
|
||||||
|
match else_branch.as_ref() {
|
||||||
|
Stmt::Block { .. } => write!(f, "{else_branch}")?,
|
||||||
|
_ => write!(f, "{}", indent(format!("{else_branch}")))?,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
Stmt::While { condition, body } => {
|
||||||
|
writeln!(f, "{condition}")?;
|
||||||
|
match body.as_ref() {
|
||||||
|
Stmt::Block { .. } => write!(f, "{body}")?,
|
||||||
|
_ => write!(f, "{}", indent(format!("{body}")))?,
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
Stmt::VarDecl { name, initializer } => write!(f, "var {name} = {initializer};"),
|
||||||
Stmt::Block { statements } => {
|
Stmt::Block { statements } => {
|
||||||
writeln!(f, "{{")?;
|
writeln!(f, "{{")?;
|
||||||
for statement in statements {
|
for statement in statements {
|
||||||
// for each statement: statement to string, split by lines, preprend each line with tab, print
|
write!(f, "{}", indent(format!("{statement}")))?;
|
||||||
// return first error or, on success, "collect" the resulting () into a Vec (ZST, so no allocation)
|
|
||||||
format!("{statement}")
|
|
||||||
.split("\n")
|
|
||||||
.map(|line| writeln!(f, "\t{line}"))
|
|
||||||
.collect::<Result<Vec<()>, std::fmt::Error>>()?;
|
|
||||||
}
|
}
|
||||||
write!(f, "}}")
|
write!(f, "}}")
|
||||||
}
|
}
|
||||||
|
Stmt::ExprStmt { expr } => write!(f, "{expr};"),
|
||||||
|
Stmt::Break => write!(f, "break;"),
|
||||||
|
Stmt::Return { expr } => {
|
||||||
|
write!(f, "return {expr};")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue