fix(users/tazjin): rustfmt code with non-default settings

rustfmt only sometimes detects path-based nested config
files (probably some kind of race?), so my users folder uses a
separate formatting check for rustfmt to avoid flaky CI. Enough flakes
around already ...

Change-Id: Ifd862f9974f071b3a256643dd8e56c019116156a
Reviewed-on: https://cl.tvl.fyi/c/depot/+/5242
Reviewed-by: tazjin <tazjin@tvl.su>
Autosubmit: tazjin <tazjin@tvl.su>
Tested-by: BuildkiteCI
This commit is contained in:
Vincent Ambo 2022-02-07 19:29:52 +03:00 committed by clbot
parent 8b8c98380e
commit 0d0b43ed88
16 changed files with 348 additions and 421 deletions

16
users/tazjin/default.nix Normal file
View file

@ -0,0 +1,16 @@
# //users/tazjin-specific CI configuration.
{ pkgs, ... }:
let
rustfmt = pkgs.writeShellScript "rustfmt-tazjin" ''
${pkgs.fd}/bin/fd -e rs | \
${pkgs.ripgrep}/bin/rg 'users/tazjin' | \
xargs ${pkgs.rustfmt}/bin/rustfmt --check --config-path users/tazjin
'';
in
rustfmt.overrideAttrs (_: {
meta.ci.extraSteps.rustfmt = {
command = rustfmt;
};
})

View file

@ -38,8 +38,8 @@
//! //!
//! * an event type representing all possible events in the machine //! * an event type representing all possible events in the machine
//! //!
//! * an action type representing a description of all possible //! * an action type representing a description of all possible side-effects
//! side-effects of the machine //! of the machine
//! //!
//! Using the definition above we can now say that a transition in a //! Using the definition above we can now say that a transition in a
//! state-machine, involving these three types, takes an initial state //! state-machine, involving these three types, takes an initial state
@ -92,14 +92,13 @@
//! //!
//! * `finito`: Core components and classes of Finito //! * `finito`: Core components and classes of Finito
//! //!
//! * `finito-in-mem`: In-memory implementation of state machines //! * `finito-in-mem`: In-memory implementation of state machines that do not
//! that do not need to live longer than an application using //! need to live longer than an application using standard library
//! standard library concurrency primitives. //! concurrency primitives.
//! //!
//! * `finito-postgres`: Postgres-backed, persistent implementation //! * `finito-postgres`: Postgres-backed, persistent implementation of state
//! of state machines that, well, do need to live longer. Uses //! machines that, well, do need to live longer. Uses Postgres for
//! Postgres for concurrency synchronisation, so keep that in //! concurrency synchronisation, so keep that in mind.
//! mind.
//! //!
//! Which should cover most use-cases. Okay, enough prose, lets dive //! Which should cover most use-cases. Okay, enough prose, lets dive
//! in. //! in.
@ -110,8 +109,8 @@
extern crate serde; extern crate serde;
use serde::Serialize;
use serde::de::DeserializeOwned; use serde::de::DeserializeOwned;
use serde::Serialize;
use std::fmt::Debug; use std::fmt::Debug;
use std::mem; use std::mem;
@ -120,7 +119,10 @@ use std::mem;
/// ///
/// This trait is used to implement transition logic and to "tie the /// This trait is used to implement transition logic and to "tie the
/// room together", with the room being our triplet of types. /// room together", with the room being our triplet of types.
pub trait FSM where Self: Sized { pub trait FSM
where
Self: Sized,
{
/// A human-readable string uniquely describing what this FSM /// A human-readable string uniquely describing what this FSM
/// models. This is used in log messages, database tables and /// models. This is used in log messages, database tables and
/// various other things throughout Finito. /// various other things throughout Finito.
@ -166,7 +168,7 @@ pub trait FSM where Self: Sized {
/// `act` interprets and executes FSM actions. This is the only /// `act` interprets and executes FSM actions. This is the only
/// part of an FSM in which side-effects are allowed. /// part of an FSM in which side-effects are allowed.
fn act(Self::Action, &Self::State) -> Result<Vec<Self::Event>, Self::Error>; fn act(action: Self::Action, state: &Self::State) -> Result<Vec<Self::Event>, Self::Error>;
} }
/// This function is the primary function used to advance a state /// This function is the primary function used to advance a state
@ -223,11 +225,13 @@ pub trait FSMBackend<S: 'static> {
/// Insert a new state-machine into the backend's storage and /// Insert a new state-machine into the backend's storage and
/// return its newly allocated key. /// return its newly allocated key.
fn insert_machine<F>(&self, initial: F) -> Result<Self::Key, Self::Error> fn insert_machine<F>(&self, initial: F) -> Result<Self::Key, Self::Error>
where F: FSM + Serialize + DeserializeOwned; where
F: FSM + Serialize + DeserializeOwned;
/// Retrieve the current state of an FSM by its key. /// Retrieve the current state of an FSM by its key.
fn get_machine<F: FSM>(&self, key: Self::Key) -> Result<F, Self::Error> fn get_machine<F: FSM>(&self, key: Self::Key) -> Result<F, Self::Error>
where F: FSM + Serialize + DeserializeOwned; where
F: FSM + Serialize + DeserializeOwned;
/// Advance a state machine by applying an event and persisting it /// Advance a state machine by applying an event and persisting it
/// as well as any resulting actions. /// as well as any resulting actions.
@ -236,8 +240,9 @@ pub trait FSMBackend<S: 'static> {
/// on the backend used. Please consult the backend's /// on the backend used. Please consult the backend's
/// documentation for details. /// documentation for details.
fn advance<'a, F: FSM>(&'a self, key: Self::Key, event: F::Event) -> Result<F, Self::Error> fn advance<'a, F: FSM>(&'a self, key: Self::Key, event: F::Event) -> Result<F, Self::Error>
where F: FSM + Serialize + DeserializeOwned, where
F::State: From<&'a S>, F: FSM + Serialize + DeserializeOwned,
F::Event: Serialize + DeserializeOwned, F::State: From<&'a S>,
F::Action: Serialize + DeserializeOwned; F::Event: Serialize + DeserializeOwned,
F::Action: Serialize + DeserializeOwned;
} }

View file

@ -27,15 +27,15 @@
//! The door can only be locked if it is closed. Oh, and it has a few //! The door can only be locked if it is closed. Oh, and it has a few
//! extra features: //! extra features:
//! //!
//! * whenever the door's state changes, an IRC channel receives a //! * whenever the door's state changes, an IRC channel receives a message about
//! message about that //! that
//! //!
//! * the door calls the police if the code is intered incorrectly more //! * the door calls the police if the code is intered incorrectly more than a
//! than a specified number of times (mhm, lets say, three) //! specified number of times (mhm, lets say, three)
//! //!
//! * if the police is called the door can not be interacted with //! * if the police is called the door can not be interacted with anymore (and
//! anymore (and honestly, for the sake of this example, we don't //! honestly, for the sake of this example, we don't care how its
//! care how its functionality is restored) //! functionality is restored)
//! //!
//! ## The Door - Visualized //! ## The Door - Visualized
//! //!
@ -71,7 +71,8 @@
//! //!
//! Alright, enough foreplay, lets dive in! //! Alright, enough foreplay, lets dive in!
#[macro_use] extern crate serde_derive; #[macro_use]
extern crate serde_derive;
extern crate failure; extern crate failure;
extern crate finito; extern crate finito;
@ -292,11 +293,13 @@ mod tests {
use finito::advance; use finito::advance;
fn test_fsm<S: FSM>(initial: S, events: Vec<S::Event>) -> (S, Vec<S::Action>) { fn test_fsm<S: FSM>(initial: S, events: Vec<S::Event>) -> (S, Vec<S::Action>) {
events.into_iter().fold((initial, vec![]), |(state, mut actions), event| { events
let (new_state, mut new_actions) = advance(state, event); .into_iter()
actions.append(&mut new_actions); .fold((initial, vec![]), |(state, mut actions), event| {
(new_state, actions) let (new_state, mut new_actions) = advance(state, event);
}) actions.append(&mut new_actions);
(new_state, actions)
})
} }
#[test] #[test]
@ -313,7 +316,10 @@ mod tests {
]; ];
let (final_state, actions) = test_fsm(initial, events); let (final_state, actions) = test_fsm(initial, events);
assert_eq!(final_state, DoorState::Locked { code: 4567, attempts: 2 }); assert_eq!(final_state, DoorState::Locked {
code: 4567,
attempts: 2
});
assert_eq!(actions, vec![ assert_eq!(actions, vec![
DoorAction::NotifyIRC("door was closed".into()), DoorAction::NotifyIRC("door was closed".into()),
DoorAction::NotifyIRC("door was opened".into()), DoorAction::NotifyIRC("door was opened".into()),

View file

@ -1,10 +1,9 @@
//! This module defines error types and conversions for issue that can //! This module defines error types and conversions for issue that can
//! occur while dealing with persisted state machines. //! occur while dealing with persisted state machines.
use std::result;
use std::fmt;
use uuid::Uuid;
use std::error::Error as StdError; use std::error::Error as StdError;
use std::{fmt, result};
use uuid::Uuid;
// errors to chain: // errors to chain:
use postgres::Error as PgError; use postgres::Error as PgError;
@ -41,20 +40,15 @@ impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use ErrorKind::*; use ErrorKind::*;
let msg = match &self.kind { let msg = match &self.kind {
Serialization(err) => Serialization(err) => format!("JSON serialization error: {}", err),
format!("JSON serialization error: {}", err),
Database(err) => Database(err) => format!("PostgreSQL error: {}", err),
format!("PostgreSQL error: {}", err),
DBPool(err) => DBPool(err) => format!("Database connection pool error: {}", err),
format!("Database connection pool error: {}", err),
FSMNotFound(id) => FSMNotFound(id) => format!("FSM with ID {} not found", id),
format!("FSM with ID {} not found", id),
ActionNotFound(id) => ActionNotFound(id) => format!("Action with ID {} not found", id),
format!("Action with ID {} not found", id),
}; };
match &self.context { match &self.context {
@ -66,7 +60,7 @@ impl fmt::Display for Error {
impl StdError for Error {} impl StdError for Error {}
impl <E: Into<ErrorKind>> From<E> for Error { impl<E: Into<ErrorKind>> From<E> for Error {
fn from(err: E) -> Error { fn from(err: E) -> Error {
Error { Error {
kind: err.into(), kind: err.into(),
@ -99,11 +93,11 @@ pub trait ResultExt<T> {
fn context<C: fmt::Display>(self, ctx: C) -> Result<T>; fn context<C: fmt::Display>(self, ctx: C) -> Result<T>;
} }
impl <T, E: Into<Error>> ResultExt<T> for result::Result<T, E> { impl<T, E: Into<Error>> ResultExt<T> for result::Result<T, E> {
fn context<C: fmt::Display>(self, ctx: C) -> Result<T> { fn context<C: fmt::Display>(self, ctx: C) -> Result<T> {
self.map_err(|err| Error { self.map_err(|err| Error {
context: Some(format!("{}", ctx)), context: Some(format!("{}", ctx)),
.. err.into() ..err.into()
}) })
} }
} }

View file

@ -4,8 +4,10 @@
//! //!
//! TODO: events & actions should have `SERIAL` keys //! TODO: events & actions should have `SERIAL` keys
#[macro_use] extern crate postgres; #[macro_use]
#[macro_use] extern crate postgres_derive; extern crate postgres;
#[macro_use]
extern crate postgres_derive;
extern crate chrono; extern crate chrono;
extern crate finito; extern crate finito;
@ -14,23 +16,25 @@ extern crate serde;
extern crate serde_json; extern crate serde_json;
extern crate uuid; extern crate uuid;
#[cfg(test)] mod tests; #[cfg(test)]
#[cfg(test)] extern crate finito_door; mod tests;
#[cfg(test)]
extern crate finito_door;
mod error; mod error;
pub use error::{Result, Error, ErrorKind}; pub use error::{Error, ErrorKind, Result};
use chrono::prelude::{DateTime, Utc}; use chrono::prelude::{DateTime, Utc};
use error::ResultExt; use error::ResultExt;
use finito::{FSM, FSMBackend}; use finito::{FSMBackend, FSM};
use postgres::transaction::Transaction; use postgres::transaction::Transaction;
use postgres::GenericConnection; use postgres::GenericConnection;
use serde::Serialize; use r2d2_postgres::{r2d2, PostgresConnectionManager};
use serde::de::DeserializeOwned; use serde::de::DeserializeOwned;
use serde::Serialize;
use serde_json::Value; use serde_json::Value;
use std::marker::PhantomData; use std::marker::PhantomData;
use uuid::Uuid; use uuid::Uuid;
use r2d2_postgres::{r2d2, PostgresConnectionManager};
type DBPool = r2d2::Pool<PostgresConnectionManager>; type DBPool = r2d2::Pool<PostgresConnectionManager>;
type DBConn = r2d2::PooledConnection<PostgresConnectionManager>; type DBConn = r2d2::PooledConnection<PostgresConnectionManager>;
@ -112,15 +116,13 @@ pub struct FinitoPostgres<S> {
db_pool: DBPool, db_pool: DBPool,
} }
impl <S> FinitoPostgres<S> { impl<S> FinitoPostgres<S> {
pub fn new(state: S, db_pool: DBPool, _pool_size: usize) -> Self { pub fn new(state: S, db_pool: DBPool, _pool_size: usize) -> Self {
FinitoPostgres { FinitoPostgres { state, db_pool }
state, db_pool,
}
} }
} }
impl <State: 'static> FSMBackend<State> for FinitoPostgres<State> { impl<State: 'static> FSMBackend<State> for FinitoPostgres<State> {
type Key = Uuid; type Key = Uuid;
type Error = Error; type Error = Error;
@ -134,10 +136,11 @@ impl <State: 'static> FSMBackend<State> for FinitoPostgres<State> {
let fsm = S::FSM_NAME.to_string(); let fsm = S::FSM_NAME.to_string();
let state = serde_json::to_value(initial).context("failed to serialise FSM")?; let state = serde_json::to_value(initial).context("failed to serialise FSM")?;
self.conn()?.execute(query, &[&id, &fsm, &state]).context("failed to insert FSM")?; self.conn()?
.execute(query, &[&id, &fsm, &state])
.context("failed to insert FSM")?;
return Ok(id); return Ok(id);
} }
fn get_machine<S: FSM + DeserializeOwned>(&self, key: Uuid) -> Result<S> { fn get_machine<S: FSM + DeserializeOwned>(&self, key: Uuid) -> Result<S> {
@ -156,10 +159,12 @@ impl <State: 'static> FSMBackend<State> for FinitoPostgres<State> {
/// processing is finished as running actions may result in additional /// processing is finished as running actions may result in additional
/// transitions. /// transitions.
fn advance<'a, S>(&'a self, key: Uuid, event: S::Event) -> Result<S> fn advance<'a, S>(&'a self, key: Uuid, event: S::Event) -> Result<S>
where S: FSM + Serialize + DeserializeOwned, where
S::State: From<&'a State>, S: FSM + Serialize + DeserializeOwned,
S::Event: Serialize + DeserializeOwned, S::State: From<&'a State>,
S::Action: Serialize + DeserializeOwned { S::Event: Serialize + DeserializeOwned,
S::Action: Serialize + DeserializeOwned,
{
let conn = self.conn()?; let conn = self.conn()?;
let tx = conn.transaction().context("could not begin transaction")?; let tx = conn.transaction().context("could not begin transaction")?;
let state = get_machine_internal(&tx, key, true)?; let state = get_machine_internal(&tx, key, true)?;
@ -187,16 +192,18 @@ impl <State: 'static> FSMBackend<State> for FinitoPostgres<State> {
} }
} }
impl <State: 'static> FinitoPostgres<State> { impl<State: 'static> FinitoPostgres<State> {
/// Execute several actions at the same time, each in a separate /// Execute several actions at the same time, each in a separate
/// thread. Note that actions returning further events, causing /// thread. Note that actions returning further events, causing
/// further transitions, returning further actions and so on will /// further transitions, returning further actions and so on will
/// potentially cause multiple threads to get created. /// potentially cause multiple threads to get created.
fn run_actions<'a, S>(&'a self, fsm_id: Uuid, action_ids: Vec<Uuid>) where fn run_actions<'a, S>(&'a self, fsm_id: Uuid, action_ids: Vec<Uuid>)
where
S: FSM + Serialize + DeserializeOwned, S: FSM + Serialize + DeserializeOwned,
S::Event: Serialize + DeserializeOwned, S::Event: Serialize + DeserializeOwned,
S::Action: Serialize + DeserializeOwned, S::Action: Serialize + DeserializeOwned,
S::State: From<&'a State> { S::State: From<&'a State>,
{
let state: S::State = (&self.state).into(); let state: S::State = (&self.state).into();
let conn = self.conn().expect("TODO"); let conn = self.conn().expect("TODO");
@ -214,17 +221,19 @@ impl <State: 'static> FinitoPostgres<State> {
/// Retrieve a single connection from the database connection pool. /// Retrieve a single connection from the database connection pool.
fn conn(&self) -> Result<DBConn> { fn conn(&self) -> Result<DBConn> {
self.db_pool.get().context("failed to retrieve connection from pool") self.db_pool
.get()
.context("failed to retrieve connection from pool")
} }
} }
/// Insert a single state-machine into the database and return its /// Insert a single state-machine into the database and return its
/// newly allocated, random UUID. /// newly allocated, random UUID.
pub fn insert_machine<C, S>(conn: &C, initial: S) -> Result<Uuid> where pub fn insert_machine<C, S>(conn: &C, initial: S) -> Result<Uuid>
where
C: GenericConnection, C: GenericConnection,
S: FSM + Serialize { S: FSM + Serialize,
{
let query = r#" let query = r#"
INSERT INTO machines (id, fsm, state) INSERT INTO machines (id, fsm, state)
VALUES ($1, $2, $3) VALUES ($1, $2, $3)
@ -240,13 +249,12 @@ pub fn insert_machine<C, S>(conn: &C, initial: S) -> Result<Uuid> where
} }
/// Insert a single event into the database and return its UUID. /// Insert a single event into the database and return its UUID.
fn insert_event<C, S>(conn: &C, fn insert_event<C, S>(conn: &C, fsm_id: Uuid, event: &S::Event) -> Result<Uuid>
fsm_id: Uuid,
event: &S::Event) -> Result<Uuid>
where where
C: GenericConnection, C: GenericConnection,
S: FSM, S: FSM,
S::Event: Serialize { S::Event: Serialize,
{
let query = r#" let query = r#"
INSERT INTO events (id, fsm, fsm_id, event) INSERT INTO events (id, fsm, fsm_id, event)
VALUES ($1, $2, $3, $4) VALUES ($1, $2, $3, $4)
@ -254,21 +262,19 @@ where
let id = Uuid::new_v4(); let id = Uuid::new_v4();
let fsm = S::FSM_NAME.to_string(); let fsm = S::FSM_NAME.to_string();
let event_value = serde_json::to_value(event) let event_value = serde_json::to_value(event).context("failed to serialize event")?;
.context("failed to serialize event")?;
conn.execute(query, &[&id, &fsm, &fsm_id, &event_value])?; conn.execute(query, &[&id, &fsm, &fsm_id, &event_value])?;
return Ok(id) return Ok(id);
} }
/// Insert a single action into the database and return its UUID. /// Insert a single action into the database and return its UUID.
fn insert_action<C, S>(conn: &C, fn insert_action<C, S>(conn: &C, fsm_id: Uuid, event_id: Uuid, action: &S::Action) -> Result<Uuid>
fsm_id: Uuid, where
event_id: Uuid,
action: &S::Action) -> Result<Uuid> where
C: GenericConnection, C: GenericConnection,
S: FSM, S: FSM,
S::Action: Serialize { S::Action: Serialize,
{
let query = r#" let query = r#"
INSERT INTO actions (id, fsm, fsm_id, event_id, content, status) INSERT INTO actions (id, fsm, fsm_id, event_id, content, status)
VALUES ($1, $2, $3, $4, $5, $6) VALUES ($1, $2, $3, $4, $5, $6)
@ -276,23 +282,26 @@ fn insert_action<C, S>(conn: &C,
let id = Uuid::new_v4(); let id = Uuid::new_v4();
let fsm = S::FSM_NAME.to_string(); let fsm = S::FSM_NAME.to_string();
let action_value = serde_json::to_value(action) let action_value = serde_json::to_value(action).context("failed to serialize action")?;
.context("failed to serialize action")?;
conn.execute( conn.execute(query, &[
query, &id,
&[&id, &fsm, &fsm_id, &event_id, &action_value, &ActionStatus::Pending] &fsm,
)?; &fsm_id,
&event_id,
&action_value,
&ActionStatus::Pending,
])?;
return Ok(id) return Ok(id);
} }
/// Update the state of a specified machine. /// Update the state of a specified machine.
fn update_state<C, S>(conn: &C, fn update_state<C, S>(conn: &C, fsm_id: Uuid, state: &S) -> Result<()>
fsm_id: Uuid, where
state: &S) -> Result<()> where
C: GenericConnection, C: GenericConnection,
S: FSM + Serialize { S: FSM + Serialize,
{
let query = r#" let query = r#"
UPDATE machines SET state = $1 WHERE id = $2 UPDATE machines SET state = $1 WHERE id = $2
"#; "#;
@ -312,23 +321,28 @@ fn update_state<C, S>(conn: &C,
fn alter_for_update(alter: bool, query: &str) -> String { fn alter_for_update(alter: bool, query: &str) -> String {
match alter { match alter {
false => query.to_string(), false => query.to_string(),
true => format!("{} FOR UPDATE", query), true => format!("{} FOR UPDATE", query),
} }
} }
/// Retrieve the current state of a state machine from the database, /// Retrieve the current state of a state machine from the database,
/// optionally locking the machine state for the duration of some /// optionally locking the machine state for the duration of some
/// enclosing transaction. /// enclosing transaction.
fn get_machine_internal<C, S>(conn: &C, fn get_machine_internal<C, S>(conn: &C, id: Uuid, for_update: bool) -> Result<S>
id: Uuid, where
for_update: bool) -> Result<S> where
C: GenericConnection, C: GenericConnection,
S: FSM + DeserializeOwned { S: FSM + DeserializeOwned,
let query = alter_for_update(for_update, r#" {
let query = alter_for_update(
for_update,
r#"
SELECT state FROM machines WHERE id = $1 SELECT state FROM machines WHERE id = $1
"#); "#,
);
let rows = conn.query(&query, &[&id]).context("failed to retrieve FSM")?; let rows = conn
.query(&query, &[&id])
.context("failed to retrieve FSM")?;
if let Some(row) = rows.into_iter().next() { if let Some(row) = rows.into_iter().next() {
Ok(serde_json::from_value(row.get(0)).context("failed to deserialize FSM")?) Ok(serde_json::from_value(row.get(0)).context("failed to deserialize FSM")?)
@ -339,20 +353,25 @@ fn get_machine_internal<C, S>(conn: &C,
/// Retrieve an action from the database, optionally locking it for /// Retrieve an action from the database, optionally locking it for
/// the duration of some enclosing transaction. /// the duration of some enclosing transaction.
fn get_action<C, S>(conn: &C, id: Uuid) -> Result<(ActionStatus, S::Action)> where fn get_action<C, S>(conn: &C, id: Uuid) -> Result<(ActionStatus, S::Action)>
where
C: GenericConnection, C: GenericConnection,
S: FSM, S: FSM,
S::Action: DeserializeOwned { S::Action: DeserializeOwned,
let query = alter_for_update(true, r#" {
let query = alter_for_update(
true,
r#"
SELECT status, content FROM actions SELECT status, content FROM actions
WHERE id = $1 AND fsm = $2 WHERE id = $1 AND fsm = $2
"#); "#,
);
let rows = conn.query(&query, &[&id, &S::FSM_NAME])?; let rows = conn.query(&query, &[&id, &S::FSM_NAME])?;
if let Some(row) = rows.into_iter().next() { if let Some(row) = rows.into_iter().next() {
let action = serde_json::from_value(row.get(1)) let action =
.context("failed to deserialize FSM action")?; serde_json::from_value(row.get(1)).context("failed to deserialize FSM action")?;
Ok((row.get(0), action)) Ok((row.get(0), action))
} else { } else {
Err(ErrorKind::ActionNotFound(id).into()) Err(ErrorKind::ActionNotFound(id).into())
@ -360,13 +379,17 @@ fn get_action<C, S>(conn: &C, id: Uuid) -> Result<(ActionStatus, S::Action)> whe
} }
/// Update the status of an action after an attempt to run it. /// Update the status of an action after an attempt to run it.
fn update_action_status<C, S>(conn: &C, fn update_action_status<C, S>(
id: Uuid, conn: &C,
status: ActionStatus, id: Uuid,
error: Option<String>, status: ActionStatus,
_fsm: PhantomData<S>) -> Result<()> where error: Option<String>,
_fsm: PhantomData<S>,
) -> Result<()>
where
C: GenericConnection, C: GenericConnection,
S: FSM { S: FSM,
{
let query = r#" let query = r#"
UPDATE actions SET status = $1, error = $2 UPDATE actions SET status = $1, error = $2
WHERE id = $3 AND fsm = $4 WHERE id = $3 AND fsm = $4
@ -389,10 +412,16 @@ fn update_action_status<C, S>(conn: &C,
/// panic), the error will be persisted. Should it fail by panicking /// panic), the error will be persisted. Should it fail by panicking
/// (which developers should never do explicitly in action /// (which developers should never do explicitly in action
/// interpreters) its status will not be changed. /// interpreters) its status will not be changed.
fn run_action<S>(tx: Transaction, id: Uuid, state: &S::State, _fsm: PhantomData<S>) fn run_action<S>(
-> Result<Vec<S::Event>> where tx: Transaction,
id: Uuid,
state: &S::State,
_fsm: PhantomData<S>,
) -> Result<Vec<S::Event>>
where
S: FSM, S: FSM,
S::Action: DeserializeOwned { S::Action: DeserializeOwned,
{
let (status, action) = get_action::<Transaction, S>(&tx, id)?; let (status, action) = get_action::<Transaction, S>(&tx, id)?;
let result = match status { let result = match status {
@ -401,29 +430,25 @@ fn run_action<S>(tx: Transaction, id: Uuid, state: &S::State, _fsm: PhantomData<
// If the action succeeded, update its status to // If the action succeeded, update its status to
// completed and return the created events. // completed and return the created events.
Ok(events) => { Ok(events) => {
update_action_status( update_action_status(&tx, id, ActionStatus::Completed, None, PhantomData::<S>)?;
&tx, id, ActionStatus::Completed, None, PhantomData::<S>
)?;
events events
}, }
// If the action failed, persist the debug message and // If the action failed, persist the debug message and
// return nothing. // return nothing.
Err(err) => { Err(err) => {
let msg = Some(format!("{:?}", err)); let msg = Some(format!("{:?}", err));
update_action_status( update_action_status(&tx, id, ActionStatus::Failed, msg, PhantomData::<S>)?;
&tx, id, ActionStatus::Failed, msg, PhantomData::<S>
)?;
vec![] vec![]
}, }
} }
}, }
_ => { _ => {
// TODO: Currently only pending actions are run because // TODO: Currently only pending actions are run because
// retryable actions are not yet implemented. // retryable actions are not yet implemented.
vec![] vec![]
}, }
}; };
tx.commit().context("failed to commit transaction")?; tx.commit().context("failed to commit transaction")?;

View file

@ -16,7 +16,11 @@ fn test_insert_machine() {
let door = insert_machine(&conn, initial).expect("Failed to insert door"); let door = insert_machine(&conn, initial).expect("Failed to insert door");
let result = get_machine(&conn, &door, false).expect("Failed to fetch door"); let result = get_machine(&conn, &door, false).expect("Failed to fetch door");
assert_eq!(result, DoorState::Opened, "Inserted door state should match"); assert_eq!(
result,
DoorState::Opened,
"Inserted door state should match"
);
} }
#[test] #[test]
@ -41,7 +45,10 @@ fn test_advance() {
} }
let result = get_machine(&conn, &door, false).expect("Failed to fetch door"); let result = get_machine(&conn, &door, false).expect("Failed to fetch door");
let expected = DoorState::Locked { code: 4567, attempts: 2 }; let expected = DoorState::Locked {
code: 4567,
attempts: 2,
};
assert_eq!(result, expected, "Advanced door state should match"); assert_eq!(result, expected, "Advanced door state should match");
} }

View file

@ -63,9 +63,11 @@ enum Precedence {
Equality, // == != Equality, // == !=
Comparison, // < > <= >= Comparison, // < > <= >=
Term, // + - Term, // + -
Factor, // * / Factor, //
Unary, // ! - //
Call, // . () // * /
Unary, // ! -
Call, // . ()
Primary, Primary,
} }
@ -78,11 +80,7 @@ struct ParseRule<T: Iterator<Item = Token>> {
} }
impl<T: Iterator<Item = Token>> ParseRule<T> { impl<T: Iterator<Item = Token>> ParseRule<T> {
fn new( fn new(prefix: Option<ParseFn<T>>, infix: Option<ParseFn<T>>, precedence: Precedence) -> Self {
prefix: Option<ParseFn<T>>,
infix: Option<ParseFn<T>>,
precedence: Precedence,
) -> Self {
ParseRule { ParseRule {
prefix, prefix,
infix, infix,
@ -105,18 +103,16 @@ impl Precedence {
Precedence::Factor => Precedence::Unary, Precedence::Factor => Precedence::Unary,
Precedence::Unary => Precedence::Call, Precedence::Unary => Precedence::Call,
Precedence::Call => Precedence::Primary, Precedence::Call => Precedence::Primary,
Precedence::Primary => panic!( Precedence::Primary => {
"invalid parser state: no higher precedence than Primary" panic!("invalid parser state: no higher precedence than Primary")
), }
} }
} }
} }
fn rule_for<T: Iterator<Item = Token>>(token: &TokenKind) -> ParseRule<T> { fn rule_for<T: Iterator<Item = Token>>(token: &TokenKind) -> ParseRule<T> {
match token { match token {
TokenKind::LeftParen => { TokenKind::LeftParen => ParseRule::new(Some(Compiler::grouping), None, Precedence::None),
ParseRule::new(Some(Compiler::grouping), None, Precedence::None)
}
TokenKind::Minus => ParseRule::new( TokenKind::Minus => ParseRule::new(
Some(Compiler::unary), Some(Compiler::unary),
@ -124,57 +120,33 @@ fn rule_for<T: Iterator<Item = Token>>(token: &TokenKind) -> ParseRule<T> {
Precedence::Term, Precedence::Term,
), ),
TokenKind::Plus => { TokenKind::Plus => ParseRule::new(None, Some(Compiler::binary), Precedence::Term),
ParseRule::new(None, Some(Compiler::binary), Precedence::Term)
}
TokenKind::Slash => { TokenKind::Slash => ParseRule::new(None, Some(Compiler::binary), Precedence::Factor),
ParseRule::new(None, Some(Compiler::binary), Precedence::Factor)
}
TokenKind::Star => { TokenKind::Star => ParseRule::new(None, Some(Compiler::binary), Precedence::Factor),
ParseRule::new(None, Some(Compiler::binary), Precedence::Factor)
}
TokenKind::Number(_) => { TokenKind::Number(_) => ParseRule::new(Some(Compiler::number), None, Precedence::None),
ParseRule::new(Some(Compiler::number), None, Precedence::None)
}
TokenKind::True => { TokenKind::True => ParseRule::new(Some(Compiler::literal), None, Precedence::None),
ParseRule::new(Some(Compiler::literal), None, Precedence::None)
}
TokenKind::False => { TokenKind::False => ParseRule::new(Some(Compiler::literal), None, Precedence::None),
ParseRule::new(Some(Compiler::literal), None, Precedence::None)
}
TokenKind::Nil => { TokenKind::Nil => ParseRule::new(Some(Compiler::literal), None, Precedence::None),
ParseRule::new(Some(Compiler::literal), None, Precedence::None)
}
TokenKind::Bang => { TokenKind::Bang => ParseRule::new(Some(Compiler::unary), None, Precedence::None),
ParseRule::new(Some(Compiler::unary), None, Precedence::None)
}
TokenKind::BangEqual => { TokenKind::BangEqual => ParseRule::new(None, Some(Compiler::binary), Precedence::Equality),
ParseRule::new(None, Some(Compiler::binary), Precedence::Equality)
}
TokenKind::EqualEqual => { TokenKind::EqualEqual => ParseRule::new(None, Some(Compiler::binary), Precedence::Equality),
ParseRule::new(None, Some(Compiler::binary), Precedence::Equality)
}
TokenKind::Greater => { TokenKind::Greater => ParseRule::new(None, Some(Compiler::binary), Precedence::Comparison),
ParseRule::new(None, Some(Compiler::binary), Precedence::Comparison)
}
TokenKind::GreaterEqual => { TokenKind::GreaterEqual => {
ParseRule::new(None, Some(Compiler::binary), Precedence::Comparison) ParseRule::new(None, Some(Compiler::binary), Precedence::Comparison)
} }
TokenKind::Less => { TokenKind::Less => ParseRule::new(None, Some(Compiler::binary), Precedence::Comparison),
ParseRule::new(None, Some(Compiler::binary), Precedence::Comparison)
}
TokenKind::LessEqual => { TokenKind::LessEqual => {
ParseRule::new(None, Some(Compiler::binary), Precedence::Comparison) ParseRule::new(None, Some(Compiler::binary), Precedence::Comparison)
@ -184,9 +156,7 @@ fn rule_for<T: Iterator<Item = Token>>(token: &TokenKind) -> ParseRule<T> {
ParseRule::new(Some(Compiler::variable), None, Precedence::None) ParseRule::new(Some(Compiler::variable), None, Precedence::None)
} }
TokenKind::String(_) => { TokenKind::String(_) => ParseRule::new(Some(Compiler::string), None, Precedence::None),
ParseRule::new(Some(Compiler::string), None, Precedence::None)
}
_ => ParseRule::new(None, None, Precedence::None), _ => ParseRule::new(None, None, Precedence::None),
} }
@ -236,9 +206,7 @@ impl<T: Iterator<Item = Token>> Compiler<T> {
fn define_variable(&mut self, var: Option<ConstantIdx>) -> LoxResult<()> { fn define_variable(&mut self, var: Option<ConstantIdx>) -> LoxResult<()> {
if self.locals.scope_depth == 0 { if self.locals.scope_depth == 0 {
self.emit_op(OpCode::OpDefineGlobal( self.emit_op(OpCode::OpDefineGlobal(var.expect("should be global")));
var.expect("should be global"),
));
} else { } else {
self.locals self.locals
.locals .locals
@ -305,9 +273,7 @@ impl<T: Iterator<Item = Token>> Compiler<T> {
} }
fn block(&mut self) -> LoxResult<()> { fn block(&mut self) -> LoxResult<()> {
while !self.check(&TokenKind::RightBrace) while !self.check(&TokenKind::RightBrace) && !self.check(&TokenKind::Eof) {
&& !self.check(&TokenKind::Eof)
{
self.declaration()?; self.declaration()?;
} }
@ -712,9 +678,8 @@ impl<T: Iterator<Item = Token>> Compiler<T> {
pub fn compile(code: &str) -> Result<(Interner, Chunk), Vec<Error>> { pub fn compile(code: &str) -> Result<(Interner, Chunk), Vec<Error>> {
let chars = code.chars().collect::<Vec<char>>(); let chars = code.chars().collect::<Vec<char>>();
let tokens = scanner::scan(&chars).map_err(|errors| { let tokens = scanner::scan(&chars)
errors.into_iter().map(Into::into).collect::<Vec<Error>>() .map_err(|errors| errors.into_iter().map(Into::into).collect::<Vec<Error>>())?;
})?;
let mut compiler = Compiler { let mut compiler = Compiler {
tokens: tokens.into_iter().peekable(), tokens: tokens.into_iter().peekable(),

View file

@ -23,10 +23,7 @@ impl crate::Lox for Interpreter {
Interpreter {} Interpreter {}
} }
fn interpret( fn interpret(&mut self, code: String) -> Result<Self::Value, Vec<Self::Error>> {
&mut self,
code: String,
) -> Result<Self::Value, Vec<Self::Error>> {
let (strings, chunk) = compiler::compile(&code)?; let (strings, chunk) = compiler::compile(&code)?;
vm::interpret(strings, chunk).map_err(|e| vec![e]) vm::interpret(strings, chunk).map_err(|e| vec![e])
} }

View file

@ -118,12 +118,7 @@ impl VM {
OpCode::OpNegate => { OpCode::OpNegate => {
let v = self.pop(); let v = self.pop();
with_type!( with_type!(self, v, Value::Number(num), self.push(Value::Number(-num)));
self,
v,
Value::Number(num),
self.push(Value::Number(-num))
);
} }
OpCode::OpSubtract => binary_op!(self, Number, -), OpCode::OpSubtract => binary_op!(self, Number, -),
@ -141,15 +136,18 @@ impl VM {
self.push(Value::String(new_s.into())); self.push(Value::String(new_s.into()));
} }
(Value::Number(n_a), Value::Number(n_b)) => (Value::Number(n_a), Value::Number(n_b)) => {
self.push(Value::Number(n_a + n_b)), self.push(Value::Number(n_a + n_b))
}
_ => return Err(Error { _ => {
line: self.chunk.get_line(self.ip - 1), return Err(Error {
kind: ErrorKind::TypeError( line: self.chunk.get_line(self.ip - 1),
"'+' operator only works on strings and numbers".into() kind: ErrorKind::TypeError(
), "'+' operator only works on strings and numbers".into(),
}) ),
})
}
} }
} }
@ -205,8 +203,7 @@ impl VM {
self.stack.len() > local_idx.0, self.stack.len() > local_idx.0,
"stack is not currently large enough for local" "stack is not currently large enough for local"
); );
self.stack[local_idx.0] = self.stack[local_idx.0] = self.stack.last().unwrap().clone();
self.stack.last().unwrap().clone();
} }
OpCode::OpJumpPlaceholder(_) => { OpCode::OpJumpPlaceholder(_) => {
@ -255,9 +252,7 @@ impl VM {
fn print_value(&self, val: Value) -> String { fn print_value(&self, val: Value) -> String {
match val { match val {
Value::String(LoxString::Heap(s)) => s, Value::String(LoxString::Heap(s)) => s,
Value::String(LoxString::Interned(id)) => { Value::String(LoxString::Interned(id)) => self.strings.lookup(id).into(),
self.strings.lookup(id).into()
}
_ => format!("{:?}", val), _ => format!("{:?}", val),
} }
} }

View file

@ -1,8 +1,5 @@
use std::env;
use std::fs;
use std::io;
use std::io::Write; use std::io::Write;
use std::process; use std::{env, fs, io, process};
mod bytecode; mod bytecode;
mod scanner; mod scanner;
@ -15,10 +12,7 @@ pub trait Lox {
type Error: std::fmt::Display; type Error: std::fmt::Display;
fn create() -> Self; fn create() -> Self;
fn interpret( fn interpret(&mut self, source: String) -> Result<Self::Value, Vec<Self::Error>>;
&mut self,
source: String,
) -> Result<Self::Value, Vec<Self::Error>>;
} }
fn main() { fn main() {
@ -29,9 +23,7 @@ fn main() {
} }
match env::var("LOX_INTERPRETER").as_ref().map(String::as_str) { match env::var("LOX_INTERPRETER").as_ref().map(String::as_str) {
Ok("treewalk") => { Ok("treewalk") => pick::<treewalk::interpreter::Interpreter>(args.nth(1)),
pick::<treewalk::interpreter::Interpreter>(args.nth(1))
}
_ => pick::<bytecode::Interpreter>(args.nth(1)), _ => pick::<bytecode::Interpreter>(args.nth(1)),
} }
} }
@ -46,8 +38,7 @@ fn pick<I: Lox>(file_arg: Option<String>) {
// Run Lox code from a file and print results to stdout // Run Lox code from a file and print results to stdout
fn run_file<I: Lox>(file: &str) { fn run_file<I: Lox>(file: &str) {
let contents = let contents = fs::read_to_string(file).expect("failed to read the input file");
fs::read_to_string(file).expect("failed to read the input file");
let mut lox = I::create(); let mut lox = I::create();
run(&mut lox, contents); run(&mut lox, contents);
} }

View file

@ -106,15 +106,9 @@ impl<'a> Scanner<'a> {
// possible multi-character tokens // possible multi-character tokens
'!' => self.add_if_next('=', TokenKind::BangEqual, TokenKind::Bang), '!' => self.add_if_next('=', TokenKind::BangEqual, TokenKind::Bang),
'=' => { '=' => self.add_if_next('=', TokenKind::EqualEqual, TokenKind::Equal),
self.add_if_next('=', TokenKind::EqualEqual, TokenKind::Equal)
}
'<' => self.add_if_next('=', TokenKind::LessEqual, TokenKind::Less), '<' => self.add_if_next('=', TokenKind::LessEqual, TokenKind::Less),
'>' => self.add_if_next( '>' => self.add_if_next('=', TokenKind::GreaterEqual, TokenKind::Greater),
'=',
TokenKind::GreaterEqual,
TokenKind::Greater,
),
'/' => { '/' => {
// support comments until EOL by discarding characters // support comments until EOL by discarding characters
@ -234,8 +228,7 @@ impl<'a> Scanner<'a> {
self.advance(); self.advance();
} }
let ident: String = let ident: String = self.source[self.start..self.current].iter().collect();
self.source[self.start..self.current].iter().collect();
// Determine whether this is an identifier, or a keyword: // Determine whether this is an identifier, or a keyword:
let token_kind = match ident.as_str() { let token_kind = match ident.as_str() {

View file

@ -34,11 +34,7 @@ impl Callable {
} }
} }
fn call( fn call(&self, lox: &mut Interpreter, args: Vec<Value>) -> Result<Value, Error> {
&self,
lox: &mut Interpreter,
args: Vec<Value>,
) -> Result<Value, Error> {
match self { match self {
Callable::Builtin(builtin) => builtin.call(args), Callable::Builtin(builtin) => builtin.call(args),
@ -50,10 +46,8 @@ impl Callable {
fn_env.define(param, value)?; fn_env.define(param, value)?;
} }
let result = lox.interpret_block_with_env( let result =
Some(Rc::new(RwLock::new(fn_env))), lox.interpret_block_with_env(Some(Rc::new(RwLock::new(fn_env))), &func.body);
&func.body,
);
match result { match result {
// extract returned values if applicable // extract returned values if applicable
@ -109,22 +103,13 @@ pub struct Environment {
} }
impl Environment { impl Environment {
fn define( fn define(&mut self, name: &scanner::Token, value: Value) -> Result<(), Error> {
&mut self,
name: &scanner::Token,
value: Value,
) -> Result<(), Error> {
let ident = identifier_str(name)?; let ident = identifier_str(name)?;
self.values.insert(ident.into(), value); self.values.insert(ident.into(), value);
Ok(()) Ok(())
} }
fn get( fn get(&self, ident: &str, line: usize, depth: usize) -> Result<Value, Error> {
&self,
ident: &str,
line: usize,
depth: usize,
) -> Result<Value, Error> {
if depth > 0 { if depth > 0 {
match &self.enclosing { match &self.enclosing {
None => { None => {
@ -137,9 +122,7 @@ impl Environment {
}) })
} }
Some(parent) => { Some(parent) => {
let env = parent let env = parent.read().expect("fatal: environment lock poisoned");
.read()
.expect("fatal: environment lock poisoned");
return env.get(ident, line, depth - 1); return env.get(ident, line, depth - 1);
} }
} }
@ -154,11 +137,7 @@ impl Environment {
}) })
} }
fn assign( fn assign(&mut self, name: &scanner::Token, value: Value) -> Result<(), Error> {
&mut self,
name: &scanner::Token,
value: Value,
) -> Result<(), Error> {
let ident = identifier_str(name)?; let ident = identifier_str(name)?;
match self.values.get_mut(ident) { match self.values.get_mut(ident) {
@ -242,22 +221,14 @@ impl Lox for Interpreter {
impl Interpreter { impl Interpreter {
// Environment modification helpers // Environment modification helpers
fn define_var( fn define_var(&mut self, name: &scanner::Token, value: Value) -> Result<(), Error> {
&mut self,
name: &scanner::Token,
value: Value,
) -> Result<(), Error> {
self.env self.env
.write() .write()
.expect("environment lock is poisoned") .expect("environment lock is poisoned")
.define(name, value) .define(name, value)
} }
fn assign_var( fn assign_var(&mut self, name: &scanner::Token, value: Value) -> Result<(), Error> {
&mut self,
name: &scanner::Token,
value: Value,
) -> Result<(), Error> {
self.env self.env
.write() .write()
.expect("environment lock is poisoned") .expect("environment lock is poisoned")
@ -271,11 +242,10 @@ impl Interpreter {
kind: ErrorKind::UndefinedVariable(ident.into()), kind: ErrorKind::UndefinedVariable(ident.into()),
})?; })?;
self.env.read().expect("environment lock is poisoned").get( self.env
ident, .read()
var.name.line, .expect("environment lock is poisoned")
depth, .get(ident, var.name.line, depth)
)
} }
/// Interpret the block in the supplied environment. If no /// Interpret the block in the supplied environment. If no
@ -324,16 +294,10 @@ impl Interpreter {
Value::Literal(Literal::String(output)) Value::Literal(Literal::String(output))
} }
Statement::Var(var) => return self.interpret_var(var), Statement::Var(var) => return self.interpret_var(var),
Statement::Block(block) => { Statement::Block(block) => return self.interpret_block_with_env(None, block),
return self.interpret_block_with_env(None, block)
}
Statement::If(if_stmt) => return self.interpret_if(if_stmt), Statement::If(if_stmt) => return self.interpret_if(if_stmt),
Statement::While(while_stmt) => { Statement::While(while_stmt) => return self.interpret_while(while_stmt),
return self.interpret_while(while_stmt) Statement::Function(func) => return self.interpret_function(func.clone()),
}
Statement::Function(func) => {
return self.interpret_function(func.clone())
}
Statement::Return(ret) => { Statement::Return(ret) => {
return Err(Error { return Err(Error {
line: 0, line: 0,
@ -348,9 +312,7 @@ impl Interpreter {
fn interpret_var(&mut self, var: &parser::Var) -> Result<Value, Error> { fn interpret_var(&mut self, var: &parser::Var) -> Result<Value, Error> {
let init = var.initialiser.as_ref().ok_or_else(|| Error { let init = var.initialiser.as_ref().ok_or_else(|| Error {
line: var.name.line, line: var.name.line,
kind: ErrorKind::InternalError( kind: ErrorKind::InternalError("missing variable initialiser".into()),
"missing variable initialiser".into(),
),
})?; })?;
let value = self.eval(init)?; let value = self.eval(init)?;
self.define_var(&var.name, value.clone())?; self.define_var(&var.name, value.clone())?;
@ -369,10 +331,7 @@ impl Interpreter {
} }
} }
fn interpret_while( fn interpret_while(&mut self, stmt: &parser::While) -> Result<Value, Error> {
&mut self,
stmt: &parser::While,
) -> Result<Value, Error> {
let mut value = Value::Literal(Literal::Nil); let mut value = Value::Literal(Literal::Nil);
while eval_truthy(&self.eval(&stmt.condition)?) { while eval_truthy(&self.eval(&stmt.condition)?) {
value = self.interpret_stmt(&stmt.body)?; value = self.interpret_stmt(&stmt.body)?;
@ -381,10 +340,7 @@ impl Interpreter {
Ok(value) Ok(value)
} }
fn interpret_function( fn interpret_function(&mut self, func: Rc<parser::Function>) -> Result<Value, Error> {
&mut self,
func: Rc<parser::Function>,
) -> Result<Value, Error> {
let name = func.name.clone(); let name = func.name.clone();
let value = Value::Callable(Callable::Function { let value = Value::Callable(Callable::Function {
func, func,
@ -414,9 +370,7 @@ impl Interpreter {
(TokenKind::Minus, Value::Literal(Literal::Number(num))) => { (TokenKind::Minus, Value::Literal(Literal::Number(num))) => {
Ok(Literal::Number(-num).into()) Ok(Literal::Number(-num).into())
} }
(TokenKind::Bang, right) => { (TokenKind::Bang, right) => Ok(Literal::Boolean(!eval_truthy(&right)).into()),
Ok(Literal::Boolean(!eval_truthy(&right)).into())
}
(op, right) => Err(Error { (op, right) => Err(Error {
line: expr.operator.line, line: expr.operator.line,
@ -478,10 +432,7 @@ impl Interpreter {
Ok(value) Ok(value)
} }
fn eval_logical( fn eval_logical(&mut self, logical: &parser::Logical) -> Result<Value, Error> {
&mut self,
logical: &parser::Logical,
) -> Result<Value, Error> {
let left = eval_truthy(&self.eval(&logical.left)?); let left = eval_truthy(&self.eval(&logical.left)?);
let right = eval_truthy(&self.eval(&logical.right)?); let right = eval_truthy(&self.eval(&logical.right)?);
@ -490,10 +441,7 @@ impl Interpreter {
TokenKind::Or => Ok(Literal::Boolean(left || right).into()), TokenKind::Or => Ok(Literal::Boolean(left || right).into()),
kind => Err(Error { kind => Err(Error {
line: logical.operator.line, line: logical.operator.line,
kind: ErrorKind::InternalError(format!( kind: ErrorKind::InternalError(format!("Invalid logical operator: {:?}", kind)),
"Invalid logical operator: {:?}",
kind
)),
}), }),
} }
} }
@ -504,10 +452,7 @@ impl Interpreter {
Value::Literal(v) => { Value::Literal(v) => {
return Err(Error { return Err(Error {
line: call.paren.line, line: call.paren.line,
kind: ErrorKind::RuntimeError(format!( kind: ErrorKind::RuntimeError(format!("not callable: {:?}", v)),
"not callable: {:?}",
v
)),
}) })
} }
}; };
@ -546,10 +491,7 @@ fn eval_truthy(lit: &Value) -> bool {
} }
} }
fn set_enclosing_env( fn set_enclosing_env(this: &RwLock<Environment>, parent: Rc<RwLock<Environment>>) {
this: &RwLock<Environment>,
parent: Rc<RwLock<Environment>>,
) {
this.write() this.write()
.expect("environment lock is poisoned") .expect("environment lock is poisoned")
.enclosing = Some(parent); .enclosing = Some(parent);

View file

@ -124,56 +124,54 @@ pub enum Statement {
// Parser // Parser
/* // program → declaration* EOF ;
program declaration* EOF ; //
// declaration → funDecl
declaration funDecl // | varDecl
| varDecl // | statement ;
| statement ; //
// funDecl → "fun" function ;
funDecl "fun" function ; // function → IDENTIFIER "(" parameters? ")" block ;
function IDENTIFIER "(" parameters? ")" block ; // parameters → IDENTIFIER ( "," IDENTIFIER )* ;
parameters IDENTIFIER ( "," IDENTIFIER )* ; //
//
// statement → exprStmt
statement exprStmt // | forStmt
| forStmt // | ifStmt
| ifStmt // | printStmt
| printStmt // | returnStmt
| returnStmt // | whileStmt
| whileStmt // | block ;
| block ; //
// forStmt → "for" "(" ( varDecl | exprStmt | ";" )
forStmt "for" "(" ( varDecl | exprStmt | ";" ) // expression? ";"
expression? ";" // expression? ")" statement ;
expression? ")" statement ; //
// returnStmt → "return" expression? ";" ;
returnStmt "return" expression? ";" ; //
// whileStmt → "while" "(" expression ")" statement ;
whileStmt "while" "(" expression ")" statement ; //
// exprStmt → expression ";" ;
exprStmt expression ";" ; //
// ifStmt → "if" "(" expression ")" statement
ifStmt "if" "(" expression ")" statement // ( "else" statement )? ;
( "else" statement )? ; //
// printStmt → "print" expression ";" ;
printStmt "print" expression ";" ; //
// expression → assignment ;
expression assignment ; // assignment → IDENTIFIER "=" assignment
assignment IDENTIFIER "=" assignment // | logic_or ;
| logic_or ; // logic_or → logic_and ( "or" logic_and )* ;
logic_or logic_and ( "or" logic_and )* ; // logic_and → equality ( "and" equality )* ;
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 | call ;
unary ( "!" | "-" ) unary | call ; // call → primary ( "(" arguments? ")" )* ;
call primary ( "(" arguments? ")" )* ; // arguments → expression ( "," expression )* ;
arguments expression ( "," expression )* ; // primary → NUMBER | STRING | "true" | "false" | "nil"
primary NUMBER | STRING | "true" | "false" | "nil" // | "(" expression ")" ;
| "(" expression ")" ;
*/
struct Parser { struct Parser {
tokens: Vec<Token>, tokens: Vec<Token>,
@ -213,9 +211,7 @@ impl Parser {
if params.len() >= 255 { if params.len() >= 255 {
return Err(Error { return Err(Error {
line: self.peek().line, line: self.peek().line,
kind: ErrorKind::InternalError( kind: ErrorKind::InternalError("255 parameter limit exceeded.".into()),
"255 parameter limit exceeded.".into(),
),
}); });
} }
@ -429,10 +425,7 @@ impl Parser {
return Err(Error { return Err(Error {
line: equals.line, line: equals.line,
kind: ErrorKind::InvalidAssignmentTarget(format!( kind: ErrorKind::InvalidAssignmentTarget(format!("{:?}", equals)),
"{:?}",
equals
)),
}); });
} }
@ -495,9 +488,7 @@ impl Parser {
} }
fn unary(&mut self) -> ExprResult { fn unary(&mut self) -> ExprResult {
if self.match_token(&TokenKind::Bang) if self.match_token(&TokenKind::Bang) || self.match_token(&TokenKind::Minus) {
|| self.match_token(&TokenKind::Minus)
{
return Ok(Expr::Unary(Unary { return Ok(Expr::Unary(Unary {
operator: self.previous().clone(), operator: self.previous().clone(),
right: Box::new(self.unary()?), right: Box::new(self.unary()?),
@ -557,10 +548,7 @@ impl Parser {
TokenKind::LeftParen => { TokenKind::LeftParen => {
let expr = self.expression()?; let expr = self.expression()?;
self.consume( self.consume(&TokenKind::RightParen, ErrorKind::UnmatchedParens)?;
&TokenKind::RightParen,
ErrorKind::UnmatchedParens,
)?;
return Ok(Expr::Grouping(Grouping(Box::new(expr)))); return Ok(Expr::Grouping(Grouping(Box::new(expr))));
} }
@ -632,11 +620,7 @@ impl Parser {
&self.tokens[self.current - 1] &self.tokens[self.current - 1]
} }
fn consume( fn consume(&mut self, kind: &TokenKind, err: ErrorKind) -> Result<Token, Error> {
&mut self,
kind: &TokenKind,
err: ErrorKind,
) -> Result<Token, Error> {
if self.check_token(kind) { if self.check_token(kind) {
return Ok(self.advance()); return Ok(self.advance());
} }

View file

@ -56,13 +56,14 @@ impl<'a> Resolver<'a> {
// The resolver does not clone references, so unless // The resolver does not clone references, so unless
// the interpreter is called before the resolver this // the interpreter is called before the resolver this
// case should never happen. // case should never happen.
None => return Err(Error { None => {
line: 0, return Err(Error {
kind: ErrorKind::InternalError( line: 0,
"multiple function references before interpretation" kind: ErrorKind::InternalError(
.into(), "multiple function references before interpretation".into(),
), ),
}), })
}
}, },
} }
} }
@ -79,10 +80,7 @@ impl<'a> Resolver<'a> {
Ok(()) Ok(())
} }
fn resolve_function( fn resolve_function(&mut self, func: &'a mut parser::Function) -> Result<(), Error> {
&mut self,
func: &'a mut parser::Function,
) -> Result<(), Error> {
self.declare(&func.name.lexeme); self.declare(&func.name.lexeme);
self.define(&func.name.lexeme); self.define(&func.name.lexeme);
@ -123,17 +121,13 @@ impl<'a> Resolver<'a> {
} }
} }
fn resolve_variable( fn resolve_variable(&mut self, var: &'a mut parser::Variable) -> Result<(), Error> {
&mut self,
var: &'a mut parser::Variable,
) -> Result<(), Error> {
if let Some(scope) = self.scopes.last_mut() { if let Some(scope) = self.scopes.last_mut() {
if let Some(false) = scope.get(var.name.lexeme.as_str()) { if let Some(false) = scope.get(var.name.lexeme.as_str()) {
return Err(Error { return Err(Error {
line: var.name.line, line: var.name.line,
kind: ErrorKind::StaticError( kind: ErrorKind::StaticError(
"can't read local variable in its own initialiser" "can't read local variable in its own initialiser".into(),
.into(),
), ),
}); });
} }
@ -143,10 +137,7 @@ impl<'a> Resolver<'a> {
Ok(()) Ok(())
} }
fn resolve_assign( fn resolve_assign(&mut self, assign: &'a mut parser::Assign) -> Result<(), Error> {
&mut self,
assign: &'a mut parser::Assign,
) -> Result<(), Error> {
self.resolve_expr(&mut assign.value)?; self.resolve_expr(&mut assign.value)?;
assign.depth = self.resolve_local(&assign.name); assign.depth = self.resolve_local(&assign.name);
Ok(()) Ok(())
@ -162,10 +153,7 @@ impl<'a> Resolver<'a> {
None None
} }
fn resolve_call( fn resolve_call(&mut self, call: &'a mut parser::Call) -> Result<(), Error> {
&mut self,
call: &'a mut parser::Call,
) -> Result<(), Error> {
self.resolve_expr(&mut call.callee)?; self.resolve_expr(&mut call.callee)?;
for arg in call.args.iter_mut() { for arg in call.args.iter_mut() {
@ -198,10 +186,7 @@ impl<'a> Resolver<'a> {
} }
} }
pub fn resolve( pub fn resolve(globals: &[String], block: &mut parser::Block) -> Result<(), Error> {
globals: &[String],
block: &mut parser::Block,
) -> Result<(), Error> {
let mut resolver: Resolver = Default::default(); let mut resolver: Resolver = Default::default();
// Scope for static globals only starts, never ends. // Scope for static globals only starts, never ends.

22
users/tazjin/rustfmt.toml Normal file
View file

@ -0,0 +1,22 @@
edition = "2021"
newline_style = "Unix"
# Default code with is 100 characters, comments should follow
# suit.
wrap_comments = true
# The default of this option creates hard-to-read nesting of
# conditionals, turn it off.
combine_control_expr = false
# Group imports by module, but no higher. This avoids hard-to-read
# nested use statements.
imports_granularity = "Module"
# Avoid vertical visual clutter by unnecessarily exploding
# block-like arguments.
overflow_delimited_expr = true
# Miscellaneous
format_code_in_doc_comments = true
normalize_comments = true