-
Notifications
You must be signed in to change notification settings - Fork 4
Initial implementation of the Monkey language #1
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -8,3 +8,6 @@ Cargo.lock | |
|
||
# These are backup files generated by rustfmt | ||
**/*.rs.bk | ||
|
||
.idea/ | ||
notes.txt |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
[package] | ||
name = "monkey" | ||
version = "0.1.0" | ||
authors = ["Paul Dix <[email protected]>"] | ||
description = "A rust implementation of Thorsten Ball's Monkey programming language." | ||
license = "MIT" | ||
|
||
[dependencies] |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,291 @@ | ||
use token; | ||
use std::fmt; | ||
use std::collections::HashMap; | ||
use std::hash::{Hash, Hasher}; | ||
|
||
#[derive(Debug)] | ||
pub enum Node { | ||
Program(Box<Program>), | ||
Statement(Box<Statement>), | ||
Expression(Box<Expression>), | ||
} | ||
|
||
#[derive(Hash, Eq, PartialEq, Clone, Debug)] | ||
pub enum Statement { | ||
Let(Box<LetStatement>), | ||
Return(Box<ReturnStatement>), | ||
Expression(Box<ExpressionStatement>), | ||
} | ||
|
||
impl fmt::Display for Statement { | ||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||
let s = match self { | ||
Statement::Let(stmt) => format!("{}", stmt), | ||
Statement::Return(ret) => format!("{}", ret), | ||
Statement::Expression(exp) => format!("{}", exp), | ||
}; | ||
write!(f, "{}", s) | ||
} | ||
} | ||
|
||
#[derive(Debug, PartialEq, Eq, Hash, Clone)] | ||
pub enum Expression { | ||
Identifier(String), | ||
Integer(i64), | ||
Prefix(Box<PrefixExpression>), | ||
Infix(Box<InfixExpression>), | ||
Boolean(bool), | ||
String(String), | ||
If(Box<IfExpression>), | ||
Function(Box<FunctionLiteral>), | ||
Call(Box<CallExpression>), | ||
Array(Box<ArrayLiteral>), | ||
Index(Box<IndexExpression>), | ||
Hash(Box<HashLiteral>), | ||
} | ||
|
||
impl fmt::Display for Expression { | ||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||
let s = match self { | ||
Expression::Identifier(s) => s.clone(), | ||
Expression::Integer(value) => format!("{}", value), | ||
Expression::Prefix(pref) => pref.to_string(), | ||
Expression::Infix(infix) => infix.to_string(), | ||
Expression::Boolean(b) => b.to_string(), | ||
Expression::String(s) => s.clone(), | ||
Expression::If(exp) => exp.to_string(), | ||
Expression::Function(f) => f.to_string(), | ||
Expression::Call(c) => c.to_string(), | ||
Expression::Array(a) => a.to_string(), | ||
Expression::Index(i) => i.to_string(), | ||
Expression::Hash(h) => h.to_string(), | ||
}; | ||
write!(f, "{}", s) | ||
} | ||
} | ||
|
||
#[derive(Eq, PartialEq, Clone, Debug)] | ||
pub struct HashLiteral { | ||
pub pairs: HashMap<Expression,Expression>, | ||
} | ||
|
||
// Had to implement Hash for this because HashMap doesn't. Doesn't matter what this is because | ||
// a HashLiteral isn't a valid expression as a key in a monkey hash. | ||
impl Hash for HashLiteral { | ||
fn hash<H: Hasher>(&self, _state: &mut H) { | ||
panic!("hash not implemented for HashLiteral"); | ||
} | ||
} | ||
|
||
impl fmt::Display for HashLiteral { | ||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||
let pairs: Vec<String> = (&self.pairs).into_iter().map(|(k, v)| format!("{}:{}", k.to_string(), v.to_string())).collect(); | ||
write!(f, "{{{}}}", pairs.join(", ")) | ||
} | ||
} | ||
|
||
#[derive(Hash, Eq, PartialEq, Clone, Debug)] | ||
pub struct IfExpression { | ||
pub condition: Expression, | ||
pub consequence: BlockStatement, | ||
pub alternative: Option<BlockStatement>, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I had some confusion about some spots where it seems like you can use Option in lieu of using a Box. Is that true, what's going on here? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You can use Box of course also differs from Option in that it represents a definitely existing T instead of an optional T, and thus automatically dereferences to its content (via the Btw, I just read your blog post and I'm interested in helping you on your journey of learning Rust and using it in production :) You mentioned that you're also interested in gRPC and web servers. There are several crates for gRPC (when I tried it there was only one but it was working well) and for Web servers I've been using rocket in production (before actix-web existed) and now also actix-web (because it's faster / async (and it allows serving websockets on the same port)). There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Another +1 on actix/actix-web, I'm surprised at just how solid it is and it seems to be taking over in any place I see such programming styles being used. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @Boscop thanks, that's great info. I'll have to read more up on trait objects. Didn't really get a chance to utilize traits in this code, but it's coming up for some future hacking. I'll search through the gRPC crates. I saw in some thread criticism about Rocket because it's not async so I'll probably dig into actix first. |
||
} | ||
|
||
impl fmt::Display for IfExpression { | ||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||
write!(f, "if {} {}", self.condition, self.consequence)?; | ||
|
||
if let Some(ref stmt) = self.alternative { | ||
write!(f, "else {}", stmt)?; | ||
} | ||
Ok(()) | ||
} | ||
} | ||
|
||
#[derive(Hash, Eq, PartialEq, Clone, Debug)] | ||
pub struct ArrayLiteral { | ||
pub elements: Vec<Expression>, | ||
} | ||
|
||
impl fmt::Display for ArrayLiteral { | ||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||
let elements: Vec<String> = (&self.elements).into_iter().map(|e| e.to_string()).collect(); | ||
write!(f, "[{}]", elements.join(", ")) | ||
} | ||
} | ||
|
||
#[derive(Hash, Eq, PartialEq, Clone, Debug)] | ||
pub struct IndexExpression { | ||
pub left: Expression, | ||
pub index: Expression, | ||
} | ||
|
||
impl fmt::Display for IndexExpression { | ||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||
write!(f, "({}[{}])", self.left.to_string(), self.index.to_string()) | ||
} | ||
} | ||
|
||
#[derive(Hash, Eq, PartialEq, Clone, Debug)] | ||
pub struct BlockStatement { | ||
pub statements: Vec<Statement>, | ||
} | ||
|
||
impl fmt::Display for BlockStatement { | ||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||
for stmt in &self.statements { | ||
write!(f, "{}", stmt)?; | ||
} | ||
Ok(()) | ||
} | ||
} | ||
|
||
#[derive(Hash, Eq, PartialEq, Clone, Debug)] | ||
pub struct FunctionLiteral { | ||
pub parameters: Vec<IdentifierExpression>, | ||
pub body: BlockStatement, | ||
} | ||
|
||
impl fmt::Display for FunctionLiteral { | ||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||
let param_list: Vec<String> = (&self.parameters).into_iter().map(|p| p.to_string()).collect(); | ||
write!(f, "({}) {}", param_list.join(", "), self.body) | ||
} | ||
} | ||
|
||
#[derive(Hash, Eq, PartialEq, Clone, Debug)] | ||
pub struct CallExpression { | ||
pub function: Expression, | ||
pub arguments: Vec<Expression>, | ||
} | ||
|
||
impl fmt::Display for CallExpression { | ||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||
let arg_list: Vec<String> = (&self.arguments).into_iter().map(|exp| exp.to_string()).collect(); | ||
write!(f, "{}({})", self.function.to_string(), arg_list.join(", ")) | ||
} | ||
} | ||
|
||
#[derive(Hash, Eq, PartialEq, Clone, Debug)] | ||
pub struct IdentifierExpression { | ||
pub name: String, | ||
} | ||
|
||
impl fmt::Display for IdentifierExpression { | ||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||
write!(f, "{}", self.name) | ||
} | ||
} | ||
|
||
#[derive(Hash, Eq, PartialEq, Clone, Debug)] | ||
pub struct PrefixExpression { | ||
pub operator: token::Token, | ||
pub right: Expression, | ||
} | ||
|
||
impl fmt::Display for PrefixExpression { | ||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||
write!(f, "({}{})", self.operator, self.right) | ||
} | ||
} | ||
|
||
#[derive(Hash, Eq, PartialEq, Clone, Debug)] | ||
pub struct InfixExpression { | ||
pub operator: token::Token, | ||
pub left: Expression, | ||
pub right: Expression, | ||
} | ||
|
||
impl fmt::Display for InfixExpression { | ||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||
write!(f, "({} {} {})", self.left, self.operator, self.right) | ||
} | ||
} | ||
|
||
#[derive(Hash, Eq, PartialEq, Clone, Debug)] | ||
pub struct IntegerLiteral { | ||
pub value: i64, | ||
} | ||
|
||
impl fmt::Display for IntegerLiteral { | ||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||
write!(f, "{}", self.value) | ||
} | ||
} | ||
|
||
#[derive(Hash, Eq, PartialEq, Clone, Debug)] | ||
pub struct LetStatement { | ||
pub name: String, | ||
pub value: Expression, | ||
} | ||
|
||
impl fmt::Display for LetStatement { | ||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||
write!(f, "let {} = {};", self.name, self.value) | ||
} | ||
} | ||
|
||
#[derive(Hash, Eq, PartialEq, Clone, Debug)] | ||
pub struct ReturnStatement { | ||
pub value: Expression, | ||
} | ||
|
||
impl fmt::Display for ReturnStatement { | ||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||
write!(f, "return {};", self.value) | ||
} | ||
} | ||
|
||
#[derive(Hash, Eq, PartialEq, Clone, Debug)] | ||
pub struct ExpressionStatement { | ||
pub expression: Expression | ||
} | ||
|
||
impl fmt::Display for ExpressionStatement { | ||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||
write!(f, "{}", self.expression) | ||
} | ||
} | ||
|
||
#[derive(Debug)] | ||
pub struct Program { | ||
pub statements: Vec<Statement>, | ||
} | ||
|
||
impl Program { | ||
pub fn new() -> Program { | ||
Program { | ||
statements: Vec::new(), | ||
} | ||
} | ||
} | ||
|
||
impl fmt::Display for Program { | ||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||
let statements: Vec<String> = (&self.statements).into_iter().map(|stmt| stmt.to_string()).collect(); | ||
write!(f, "{}", statements.join("")) | ||
} | ||
} | ||
|
||
#[cfg(test)] | ||
mod test { | ||
use super::*; | ||
|
||
#[test] | ||
fn display() { | ||
|
||
let p = Program{ | ||
statements: vec![ | ||
Statement::Let(Box::new( | ||
LetStatement{ | ||
name: "asdf".to_string(), | ||
value: Expression::Identifier("bar".to_string())}))], | ||
}; | ||
|
||
let expected = "let asdf = bar;"; | ||
|
||
if p.to_string() != expected { | ||
panic!("expected {} but got {}", "foo", expected) | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this bad form to panic on a method implemented for a trait? This should never get called.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If executing this code should be a bug (the programmer's fault) it's usually recommended to use
unreachable!()
to signal that this code should never be executed.(In many cases, this also allows the compiler to do more optimizations, also when using zero-sized types like
Never
which can never be instantiated.)There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks!