Variables

This commit is contained in:
Garen Tyler 2022-03-25 23:08:43 -06:00
parent 643b4c8538
commit 0be64e5eb3
No known key found for this signature in database
GPG Key ID: E3BF83D66394FD92
6 changed files with 322 additions and 125 deletions

View File

@ -192,4 +192,4 @@ impl AstNode {
pub fn null() -> AstNode {
AstNode::Null
}
}
}

View File

@ -10,7 +10,7 @@ pub fn compile(source: &str) -> Vec<u8> {
pub fn compile_wat(source: &str) -> String {
// let mut s = SymbolGenerator::new();
let ast = parse::interpret(source);
let ast = parse::run(source);
// println!("{:?}", ast);
unimplemented!()
// let wasm = ast.emit(&mut s);

View File

@ -5,7 +5,7 @@ fn main() -> std::io::Result<()> {
// Read the source from a file.
let source = std::fs::read_to_string("test.pvt").unwrap();
// Compile it
let _value = pivot::parse::interpret(&source);
let _value = pivot::parse::run(&source);
// let binary = pivot::compile(&source);
// Write it to a file.
// File::create("out.wasm")?.write_all(&binary)?;

View File

@ -4,11 +4,16 @@ pub enum Token {
Float(f64),
String(String),
Boolean(bool),
Identifier(String),
Keyword(String),
Comma,
Plus,
Minus,
Star,
Slash,
Bang,
Equals,
Semicolon,
Quote(char),
Parenthesis { closing: bool },
Whitespace(String),
@ -19,8 +24,21 @@ pub enum Token {
pub enum InterpreterError {
/// Error parsing source
ParseError(String),
/// Unexpected token
UnexpectedToken,
/// Mismatched types
MismatchedTypes,
/// Type error
TypeError,
/// Unexpected EOF
UnexpectedEOF,
/// Expected value
ExpectedValue,
}
impl<T> From<Option<T>> for InterpreterError {
fn from(value: Option<T>) -> InterpreterError {
InterpreterError::ExpectedValue
}
}
pub fn tokenize(source: &str) -> Result<Vec<Token>, InterpreterError> {
@ -37,18 +55,37 @@ pub fn tokenize(source: &str) -> Result<Vec<Token>, InterpreterError> {
if chars_consumed == 0 {
Err(())
} else if current_value.contains(".") {
Ok((Token::Float(current_value.parse::<f64>().unwrap()), chars_consumed))
Ok((
Token::Float(current_value.parse::<f64>().unwrap()),
chars_consumed,
))
} else {
Ok((Token::Integer(current_value.parse::<i64>().unwrap()), chars_consumed))
Ok((
Token::Integer(current_value.parse::<i64>().unwrap()),
chars_consumed,
))
}
}
fn tokenize_bool(chars: &[char]) -> Result<(Token, usize), ()> {
if chars.len() >= 5 && chars[0..5] == ['f', 'a', 'l', 's', 'e'] {
Ok((Token::Boolean(false), 5))
} else if chars.len() >= 4 && chars[0..4] == ['t', 'r', 'u', 'e'] {
Ok((Token::Boolean(true), 4))
fn tokenize_identifier(chars: &[char]) -> Result<(Token, usize), ()> {
let mut current_value = String::new();
let mut chars_consumed = 0;
if chars[chars_consumed].is_alphabetic() {
current_value.push(chars[chars_consumed]);
} else {
Err(())
return Err(());
}
chars_consumed += 1;
while chars_consumed < chars.len()
&& (chars[chars_consumed].is_alphanumeric() || chars[chars_consumed] == '_')
{
current_value.push(chars[chars_consumed]);
chars_consumed += 1;
}
match &current_value[..] {
"true" => Ok((Token::Boolean(true), 4)),
"false" => Ok((Token::Boolean(false), 5)),
"let" => Ok((Token::Keyword(current_value), chars_consumed)),
_ => Ok((Token::Identifier(current_value), chars_consumed)),
}
}
fn tokenize_string(chars: &[char]) -> Result<(Token, usize), ()> {
@ -103,6 +140,8 @@ pub fn tokenize(source: &str) -> Result<Vec<Token>, InterpreterError> {
Ok((Token::Slash, 1))
} else if chars[0] == '!' {
Ok((Token::Bang, 1))
} else if chars[0] == '=' {
Ok((Token::Equals, 1))
} else {
Err(())
}
@ -121,12 +160,18 @@ pub fn tokenize(source: &str) -> Result<Vec<Token>, InterpreterError> {
} else if let Ok((num, chars_consumed)) = tokenize_string(&source[index..]) {
tokens.push(num);
index += chars_consumed;
} else if let Ok((num, chars_consumed)) = tokenize_bool(&source[index..]) {
} else if let Ok((num, chars_consumed)) = tokenize_identifier(&source[index..]) {
tokens.push(num);
index += chars_consumed;
} else if let Ok((operator, chars_consumed)) = tokenize_operator(&source[index..]) {
tokens.push(operator);
index += chars_consumed;
} else if source[index] == ',' {
tokens.push(Token::Comma);
index += 1;
} else if source[index] == ';' {
tokens.push(Token::Semicolon);
index += 1;
} else if source[index] == '(' {
tokens.push(Token::Parenthesis { closing: false });
index += 1;
@ -147,18 +192,53 @@ pub enum AstPrimitive {
Float(f64),
String(String),
Boolean(bool),
Identifier(String),
Null,
}
impl std::hash::Hash for AstPrimitive {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
match self {
AstPrimitive::Float(f) => format!("{}", f).hash(state),
_ => self.hash(state),
}
}
}
#[derive(Clone, PartialEq, Debug)]
pub enum AstNode {
Primitive(AstPrimitive),
Negate { body: Box<AstNode> },
Add { left: Box<AstNode>, right: Box<AstNode> },
Subtract { left: Box<AstNode>, right: Box<AstNode> },
Multiply { left: Box<AstNode>, right: Box<AstNode> },
Divide { left: Box<AstNode>, right: Box<AstNode> },
Negate {
body: Box<AstNode>,
},
Add {
left: Box<AstNode>,
right: Box<AstNode>,
},
Subtract {
left: Box<AstNode>,
right: Box<AstNode>,
},
Multiply {
left: Box<AstNode>,
right: Box<AstNode>,
},
Divide {
left: Box<AstNode>,
right: Box<AstNode>,
},
Declare {
identifier: String,
},
Assign {
left: Box<AstNode>,
right: Box<AstNode>,
},
Program {
statements: Vec<AstNode>,
},
Null,
}
pub fn parse(tokens: &[Token]) -> Result<AstNode, InterpreterError> {
fn parse_primary_expression(tokens: &[Token]) -> Result<(AstNode, usize), InterpreterError> {
if tokens.is_empty() {
@ -171,8 +251,23 @@ pub fn parse(tokens: &[Token]) -> Result<AstNode, InterpreterError> {
Ok((AstNode::Primitive(AstPrimitive::Boolean(*n)), 1))
} else if let Token::String(s) = &tokens[0] {
Ok((AstNode::Primitive(AstPrimitive::String(s.clone())), 1))
} else if let Token::Identifier(s) = &tokens[0] {
Ok((AstNode::Primitive(AstPrimitive::Identifier(s.clone())), 1))
} else if tokens[0] == Token::Keyword("let".to_owned()) {
if tokens.len() < 2 {
Err(InterpreterError::UnexpectedEOF)
} else if let Token::Identifier(s) = &tokens[1] {
Ok((
AstNode::Declare {
identifier: s.clone(),
},
2,
))
} else {
Err(InterpreterError::UnexpectedToken)
}
} else {
Err(InterpreterError::ParseError("Expected literal".to_owned()))
Err(InterpreterError::UnexpectedToken)
}
}
fn parse_grouped_expression(tokens: &[Token]) -> Result<(AstNode, usize), InterpreterError> {
@ -188,7 +283,9 @@ pub fn parse(tokens: &[Token]) -> Result<AstNode, InterpreterError> {
index += tokens_consumed;
// ')'
if !matches!(tokens[index], Token::Parenthesis { closing: true }) {
return Err(InterpreterError::ParseError("No closing parenthesis".to_owned()));
return Err(InterpreterError::ParseError(
"No closing parenthesis".to_owned(),
));
} else {
index += 1;
}
@ -205,15 +302,21 @@ pub fn parse(tokens: &[Token]) -> Result<AstNode, InterpreterError> {
index += tokens_consumed;
Ok((
match operation {
Token::Minus => AstNode::Negate { body: Box::new(body) },
Token::Bang => AstNode::Negate { body: Box::new(body) },
Token::Minus => AstNode::Negate {
body: Box::new(body),
},
Token::Bang => AstNode::Negate {
body: Box::new(body),
},
_ => return Err(InterpreterError::ParseError("Impossible".to_owned())),
},
index
index,
))
}
}
fn parse_multiplication_expression(tokens: &[Token]) -> Result<(AstNode, usize), InterpreterError> {
fn parse_multiplication_expression(
tokens: &[Token],
) -> Result<(AstNode, usize), InterpreterError> {
let mut index = 0;
let (mut value, tokens_consumed) = parse_unary_expression(&tokens[index..])?;
index += tokens_consumed;
@ -265,117 +368,208 @@ pub fn parse(tokens: &[Token]) -> Result<AstNode, InterpreterError> {
}
Ok((value, index))
}
fn parse_assign_expression(tokens: &[Token]) -> Result<(AstNode, usize), InterpreterError> {
let mut index = 0;
let (identifier, tokens_consumed) = parse_addition_expression(&tokens[index..])?;
index += tokens_consumed;
if index < tokens.len() && tokens[index] == Token::Equals {
index += 1;
} else {
return Ok((identifier, index));
}
let (value, tokens_consumed) = parse_addition_expression(&tokens[index..])?;
index += tokens_consumed;
Ok((
AstNode::Assign {
left: Box::new(identifier),
right: Box::new(value),
},
index,
))
}
fn parse_expression(tokens: &[Token]) -> Result<(AstNode, usize), InterpreterError> {
parse_addition_expression(tokens)
parse_assign_expression(tokens)
}
let (ast, _) = parse_expression(tokens)?;
Ok(ast)
let mut statements = vec![];
let mut index = 0;
loop {
if index >= tokens.len() {
break;
}
let statement = parse_expression(&tokens[index..]);
if let Ok((statement, tokens_consumed)) = statement {
statements.push(statement);
index += tokens_consumed;
} else {
break;
}
}
Ok(AstNode::Program { statements })
}
pub fn evaluate(ast: &AstNode) -> Result<AstPrimitive, InterpreterError> {
use std::mem::discriminant;
pub fn interpret(ast: &AstNode) -> Result<(), InterpreterError> {
use std::{collections::HashMap, mem::discriminant};
use AstNode::*;
match ast {
Primitive(p) => Ok(p.clone()),
Negate { body } => {
if let AstPrimitive::Integer(body) = evaluate(body)? {
Ok(AstPrimitive::Integer(body * -1))
} else if let AstPrimitive::Boolean(body) = evaluate(body)? {
Ok(AstPrimitive::Boolean(!body))
}else {
Err(InterpreterError::ParseError("Can only negate integers and bools".to_owned()))
}
},
Add { left, right } => {
let left = evaluate(left)?;
let right = evaluate(right)?;
if discriminant(&left) != discriminant(&right) {
Err(InterpreterError::ParseError("Mismatched types".to_owned()))
} else {
if let AstPrimitive::Integer(left) = left {
if let AstPrimitive::Integer(right) = right {
return Ok(AstPrimitive::Integer(left + right));
}
}
if let AstPrimitive::Float(left) = left {
if let AstPrimitive::Float(right) = right {
return Ok(AstPrimitive::Float(left + right));
}
}
if let AstPrimitive::String(left) = left {
if let AstPrimitive::String(right) = right {
return Ok(AstPrimitive::String(format!("{}{}", left, right)));
}
}
Err(InterpreterError::ParseError("Can only add integers, strings, and floats".to_owned()))
}
let mut vars: HashMap<String, Option<AstPrimitive>> = HashMap::new();
if let Program { statements } = ast {
for statement in statements {
let _ = interpret_statement(statement, &mut vars)?;
}
Subtract { left, right } => {
let left = evaluate(left)?;
let right = evaluate(right)?;
if discriminant(&left) != discriminant(&right) {
Err(InterpreterError::ParseError("Mismatched types".to_owned()))
} else {
if let AstPrimitive::Integer(left) = left {
if let AstPrimitive::Integer(right) = right {
return Ok(AstPrimitive::Integer(left - right));
}
}
if let AstPrimitive::Float(left) = left {
if let AstPrimitive::Float(right) = right {
return Ok(AstPrimitive::Float(left - right));
}
}
Err(InterpreterError::ParseError("Can only subtract integers and floats".to_owned()))
}
}
Multiply { left, right } => {
let left = evaluate(left)?;
let right = evaluate(right)?;
if discriminant(&left) != discriminant(&right) {
Err(InterpreterError::ParseError("Mismatched types".to_owned()))
} else {
if let AstPrimitive::Integer(left) = left {
if let AstPrimitive::Integer(right) = right {
return Ok(AstPrimitive::Integer(left * right));
}
}
if let AstPrimitive::Float(left) = left {
if let AstPrimitive::Float(right) = right {
return Ok(AstPrimitive::Float(left * right));
}
}
Err(InterpreterError::ParseError("Can only multiply integers and floats".to_owned()))
}
}
Divide { left, right } => {
let left = evaluate(left)?;
let right = evaluate(right)?;
if discriminant(&left) != discriminant(&right) {
Err(InterpreterError::ParseError("Mismatched types".to_owned()))
} else {
if let AstPrimitive::Integer(left) = left {
if let AstPrimitive::Integer(right) = right {
return Ok(AstPrimitive::Integer(left / right));
}
}
if let AstPrimitive::Float(left) = left {
if let AstPrimitive::Float(right) = right {
return Ok(AstPrimitive::Float(left / right));
}
}
Err(InterpreterError::ParseError("Can only divide integers and floats".to_owned()))
}
}
Null => Err(InterpreterError::ParseError("Cannot evaluate null".to_owned())),
}
fn interpret_statement(
ast: &AstNode,
vars: &mut HashMap<String, Option<AstPrimitive>>,
) -> Result<Option<AstPrimitive>, InterpreterError> {
match ast {
Primitive(p) => {
if let AstPrimitive::Identifier(id) = p {
if let Some(val) = vars.get(id) {
if let Some(val) = val {
Ok(Some(val.clone()))
} else {
Err(InterpreterError::ParseError(
"Variable used before definition".to_owned(),
))
}
} else {
Err(InterpreterError::ParseError(
"Variable used before declaration".to_owned(),
))
}
} else {
Ok(Some(p.clone()))
}
}
Negate { body } => {
if let Some(AstPrimitive::Integer(body)) = interpret_statement(body, vars)? {
Ok(Some(AstPrimitive::Integer(body * -1)))
} else if let Some(AstPrimitive::Boolean(body)) = interpret_statement(body, vars)? {
Ok(Some(AstPrimitive::Boolean(!body)))
} else {
Err(InterpreterError::TypeError)
}
}
Declare { identifier } => {
vars.insert(identifier.clone(), None);
Ok(None)
}
Assign { left, right } => {
if let AstNode::Declare { identifier } = Box::leak(left.clone()) {
let _ = interpret(left)?;
let value = interpret_statement(right, vars)?;
vars.insert(identifier.clone(), value);
Ok(Some(vars.get(identifier).unwrap().clone().unwrap().clone()))
} else if let AstNode::Primitive(AstPrimitive::Identifier(id)) =
Box::leak(left.clone())
{
let id = id.clone();
let value = interpret_statement(right, vars)?;
vars.insert(id.clone(), value);
Ok(Some(vars.get(&id).unwrap().clone().unwrap().clone()))
} else {
Err(InterpreterError::TypeError)
}
}
Add { left, right } => {
let left =
interpret_statement(left, vars)?.ok_or(InterpreterError::ExpectedValue)?;
let right =
interpret_statement(right, vars)?.ok_or(InterpreterError::ExpectedValue)?;
if discriminant(&left) != discriminant(&right) {
Err(InterpreterError::MismatchedTypes)
} else {
if let AstPrimitive::Integer(left) = left {
if let AstPrimitive::Integer(right) = right {
return Ok(Some(AstPrimitive::Integer(left + right)));
}
}
if let AstPrimitive::Float(left) = left {
if let AstPrimitive::Float(right) = right {
return Ok(Some(AstPrimitive::Float(left + right)));
}
}
if let AstPrimitive::String(left) = left {
if let AstPrimitive::String(right) = right {
return Ok(Some(AstPrimitive::String(format!("{}{}", left, right))));
}
}
Err(InterpreterError::TypeError)
}
}
Subtract { left, right } => {
let left =
interpret_statement(left, vars)?.ok_or(InterpreterError::ExpectedValue)?;
let right =
interpret_statement(right, vars)?.ok_or(InterpreterError::ExpectedValue)?;
if discriminant(&left) != discriminant(&right) {
Err(InterpreterError::MismatchedTypes)
} else {
if let AstPrimitive::Integer(left) = left {
if let AstPrimitive::Integer(right) = right {
return Ok(Some(AstPrimitive::Integer(left - right)));
}
}
if let AstPrimitive::Float(left) = left {
if let AstPrimitive::Float(right) = right {
return Ok(Some(AstPrimitive::Float(left - right)));
}
}
Err(InterpreterError::TypeError)
}
}
Multiply { left, right } => {
let left =
interpret_statement(left, vars)?.ok_or(InterpreterError::ExpectedValue)?;
let right =
interpret_statement(right, vars)?.ok_or(InterpreterError::ExpectedValue)?;
if discriminant(&left) != discriminant(&right) {
Err(InterpreterError::MismatchedTypes)
} else {
if let AstPrimitive::Integer(left) = left {
if let AstPrimitive::Integer(right) = right {
return Ok(Some(AstPrimitive::Integer(left * right)));
}
}
if let AstPrimitive::Float(left) = left {
if let AstPrimitive::Float(right) = right {
return Ok(Some(AstPrimitive::Float(left * right)));
}
}
Err(InterpreterError::TypeError)
}
}
Divide { left, right } => {
let left =
interpret_statement(left, vars)?.ok_or(InterpreterError::ExpectedValue)?;
let right =
interpret_statement(right, vars)?.ok_or(InterpreterError::ExpectedValue)?;
if discriminant(&left) != discriminant(&right) {
Err(InterpreterError::MismatchedTypes)
} else {
if let AstPrimitive::Integer(left) = left {
if let AstPrimitive::Integer(right) = right {
return Ok(Some(AstPrimitive::Integer(left / right)));
}
}
if let AstPrimitive::Float(left) = left {
if let AstPrimitive::Float(right) = right {
return Ok(Some(AstPrimitive::Float(left / right)));
}
}
Err(InterpreterError::TypeError)
}
}
_ => Err(InterpreterError::TypeError),
}
}
Ok(())
}
pub fn interpret(source: &str) -> Result<(), InterpreterError> {
pub fn run(source: &str) -> Result<(), InterpreterError> {
println!("source: {:?}", source);
let tokens = tokenize(source);
println!("tokens: {:?}", tokens);
let ast = parse(&tokens?);
println!("ast: {:?}", ast);
let value = evaluate(&ast?);
let value = interpret(&ast?);
println!("value: {:?}", value);
Ok(())
}
}

View File

@ -1 +1,2 @@
"a" + "b"
let asdf = 4
asdf + 1

View File

@ -10,4 +10,6 @@ MultiplyExpression = UnaryExpression | MultiplyExpression MultiplyOperator Unary
AddOperator = `+` | `-`
AddExpression = MultiplyExpression | AddExpression AddOperator MultiplyExpression
Expression = AddExpression
AssignExpression = AddExpression | Identifier `=` AddExpression
Expression = AssignExpression