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

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

View File

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

View File

@ -4,11 +4,16 @@ pub enum Token {
Float(f64), Float(f64),
String(String), String(String),
Boolean(bool), Boolean(bool),
Identifier(String),
Keyword(String),
Comma,
Plus, Plus,
Minus, Minus,
Star, Star,
Slash, Slash,
Bang, Bang,
Equals,
Semicolon,
Quote(char), Quote(char),
Parenthesis { closing: bool }, Parenthesis { closing: bool },
Whitespace(String), Whitespace(String),
@ -19,8 +24,21 @@ pub enum Token {
pub enum InterpreterError { pub enum InterpreterError {
/// Error parsing source /// Error parsing source
ParseError(String), ParseError(String),
/// Unexpected token
UnexpectedToken,
/// Mismatched types
MismatchedTypes,
/// Type error
TypeError,
/// Unexpected EOF /// Unexpected EOF
UnexpectedEOF, 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> { 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 { if chars_consumed == 0 {
Err(()) Err(())
} else if current_value.contains(".") { } 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 { } 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), ()> { fn tokenize_identifier(chars: &[char]) -> Result<(Token, usize), ()> {
if chars.len() >= 5 && chars[0..5] == ['f', 'a', 'l', 's', 'e'] { let mut current_value = String::new();
Ok((Token::Boolean(false), 5)) let mut chars_consumed = 0;
} else if chars.len() >= 4 && chars[0..4] == ['t', 'r', 'u', 'e'] { if chars[chars_consumed].is_alphabetic() {
Ok((Token::Boolean(true), 4)) current_value.push(chars[chars_consumed]);
} else { } 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), ()> { 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)) Ok((Token::Slash, 1))
} else if chars[0] == '!' { } else if chars[0] == '!' {
Ok((Token::Bang, 1)) Ok((Token::Bang, 1))
} else if chars[0] == '=' {
Ok((Token::Equals, 1))
} else { } else {
Err(()) 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..]) { } else if let Ok((num, chars_consumed)) = tokenize_string(&source[index..]) {
tokens.push(num); tokens.push(num);
index += chars_consumed; 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); tokens.push(num);
index += chars_consumed; index += chars_consumed;
} else if let Ok((operator, chars_consumed)) = tokenize_operator(&source[index..]) { } else if let Ok((operator, chars_consumed)) = tokenize_operator(&source[index..]) {
tokens.push(operator); tokens.push(operator);
index += chars_consumed; 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] == '(' { } else if source[index] == '(' {
tokens.push(Token::Parenthesis { closing: false }); tokens.push(Token::Parenthesis { closing: false });
index += 1; index += 1;
@ -147,18 +192,53 @@ pub enum AstPrimitive {
Float(f64), Float(f64),
String(String), String(String),
Boolean(bool), 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)] #[derive(Clone, PartialEq, Debug)]
pub enum AstNode { pub enum AstNode {
Primitive(AstPrimitive), Primitive(AstPrimitive),
Negate { body: Box<AstNode> }, Negate {
Add { left: Box<AstNode>, right: Box<AstNode> }, body: Box<AstNode>,
Subtract { left: Box<AstNode>, right: Box<AstNode> }, },
Multiply { left: Box<AstNode>, right: Box<AstNode> }, Add {
Divide { left: Box<AstNode>, right: Box<AstNode> }, 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, Null,
} }
pub fn parse(tokens: &[Token]) -> Result<AstNode, InterpreterError> { pub fn parse(tokens: &[Token]) -> Result<AstNode, InterpreterError> {
fn parse_primary_expression(tokens: &[Token]) -> Result<(AstNode, usize), InterpreterError> { fn parse_primary_expression(tokens: &[Token]) -> Result<(AstNode, usize), InterpreterError> {
if tokens.is_empty() { if tokens.is_empty() {
@ -171,8 +251,23 @@ pub fn parse(tokens: &[Token]) -> Result<AstNode, InterpreterError> {
Ok((AstNode::Primitive(AstPrimitive::Boolean(*n)), 1)) Ok((AstNode::Primitive(AstPrimitive::Boolean(*n)), 1))
} else if let Token::String(s) = &tokens[0] { } else if let Token::String(s) = &tokens[0] {
Ok((AstNode::Primitive(AstPrimitive::String(s.clone())), 1)) 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 { } else {
Err(InterpreterError::ParseError("Expected literal".to_owned())) Err(InterpreterError::UnexpectedToken)
}
} else {
Err(InterpreterError::UnexpectedToken)
} }
} }
fn parse_grouped_expression(tokens: &[Token]) -> Result<(AstNode, usize), InterpreterError> { 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; index += tokens_consumed;
// ')' // ')'
if !matches!(tokens[index], Token::Parenthesis { closing: true }) { 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 { } else {
index += 1; index += 1;
} }
@ -205,15 +302,21 @@ pub fn parse(tokens: &[Token]) -> Result<AstNode, InterpreterError> {
index += tokens_consumed; index += tokens_consumed;
Ok(( Ok((
match operation { match operation {
Token::Minus => AstNode::Negate { body: Box::new(body) }, Token::Minus => AstNode::Negate {
Token::Bang => AstNode::Negate { body: Box::new(body) }, body: Box::new(body),
},
Token::Bang => AstNode::Negate {
body: Box::new(body),
},
_ => return Err(InterpreterError::ParseError("Impossible".to_owned())), _ => 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 index = 0;
let (mut value, tokens_consumed) = parse_unary_expression(&tokens[index..])?; let (mut value, tokens_consumed) = parse_unary_expression(&tokens[index..])?;
index += tokens_consumed; index += tokens_consumed;
@ -265,117 +368,208 @@ pub fn parse(tokens: &[Token]) -> Result<AstNode, InterpreterError> {
} }
Ok((value, index)) Ok((value, index))
} }
fn parse_expression(tokens: &[Token]) -> Result<(AstNode, usize), InterpreterError> { fn parse_assign_expression(tokens: &[Token]) -> Result<(AstNode, usize), InterpreterError> {
parse_addition_expression(tokens) let mut index = 0;
} let (identifier, tokens_consumed) = parse_addition_expression(&tokens[index..])?;
let (ast, _) = parse_expression(tokens)?; index += tokens_consumed;
Ok(ast) if index < tokens.len() && tokens[index] == Token::Equals {
} index += 1;
pub fn evaluate(ast: &AstNode) -> Result<AstPrimitive, InterpreterError> {
use std::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 { } else {
Err(InterpreterError::ParseError("Can only negate integers and bools".to_owned())) 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_assign_expression(tokens)
}
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 interpret(ast: &AstNode) -> Result<(), InterpreterError> {
use std::{collections::HashMap, mem::discriminant};
use AstNode::*;
let mut vars: HashMap<String, Option<AstPrimitive>> = HashMap::new();
if let Program { statements } = ast {
for statement in statements {
let _ = interpret_statement(statement, &mut vars)?;
}
}
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 } => { Add { left, right } => {
let left = evaluate(left)?; let left =
let right = evaluate(right)?; interpret_statement(left, vars)?.ok_or(InterpreterError::ExpectedValue)?;
let right =
interpret_statement(right, vars)?.ok_or(InterpreterError::ExpectedValue)?;
if discriminant(&left) != discriminant(&right) { if discriminant(&left) != discriminant(&right) {
Err(InterpreterError::ParseError("Mismatched types".to_owned())) Err(InterpreterError::MismatchedTypes)
} else { } else {
if let AstPrimitive::Integer(left) = left { if let AstPrimitive::Integer(left) = left {
if let AstPrimitive::Integer(right) = right { if let AstPrimitive::Integer(right) = right {
return Ok(AstPrimitive::Integer(left + right)); return Ok(Some(AstPrimitive::Integer(left + right)));
} }
} }
if let AstPrimitive::Float(left) = left { if let AstPrimitive::Float(left) = left {
if let AstPrimitive::Float(right) = right { if let AstPrimitive::Float(right) = right {
return Ok(AstPrimitive::Float(left + right)); return Ok(Some(AstPrimitive::Float(left + right)));
} }
} }
if let AstPrimitive::String(left) = left { if let AstPrimitive::String(left) = left {
if let AstPrimitive::String(right) = right { if let AstPrimitive::String(right) = right {
return Ok(AstPrimitive::String(format!("{}{}", left, right))); return Ok(Some(AstPrimitive::String(format!("{}{}", left, right))));
} }
} }
Err(InterpreterError::ParseError("Can only add integers, strings, and floats".to_owned())) Err(InterpreterError::TypeError)
} }
} }
Subtract { left, right } => { Subtract { left, right } => {
let left = evaluate(left)?; let left =
let right = evaluate(right)?; interpret_statement(left, vars)?.ok_or(InterpreterError::ExpectedValue)?;
let right =
interpret_statement(right, vars)?.ok_or(InterpreterError::ExpectedValue)?;
if discriminant(&left) != discriminant(&right) { if discriminant(&left) != discriminant(&right) {
Err(InterpreterError::ParseError("Mismatched types".to_owned())) Err(InterpreterError::MismatchedTypes)
} else { } else {
if let AstPrimitive::Integer(left) = left { if let AstPrimitive::Integer(left) = left {
if let AstPrimitive::Integer(right) = right { if let AstPrimitive::Integer(right) = right {
return Ok(AstPrimitive::Integer(left - right)); return Ok(Some(AstPrimitive::Integer(left - right)));
} }
} }
if let AstPrimitive::Float(left) = left { if let AstPrimitive::Float(left) = left {
if let AstPrimitive::Float(right) = right { if let AstPrimitive::Float(right) = right {
return Ok(AstPrimitive::Float(left - right)); return Ok(Some(AstPrimitive::Float(left - right)));
} }
} }
Err(InterpreterError::ParseError("Can only subtract integers and floats".to_owned())) Err(InterpreterError::TypeError)
} }
} }
Multiply { left, right } => { Multiply { left, right } => {
let left = evaluate(left)?; let left =
let right = evaluate(right)?; interpret_statement(left, vars)?.ok_or(InterpreterError::ExpectedValue)?;
let right =
interpret_statement(right, vars)?.ok_or(InterpreterError::ExpectedValue)?;
if discriminant(&left) != discriminant(&right) { if discriminant(&left) != discriminant(&right) {
Err(InterpreterError::ParseError("Mismatched types".to_owned())) Err(InterpreterError::MismatchedTypes)
} else { } else {
if let AstPrimitive::Integer(left) = left { if let AstPrimitive::Integer(left) = left {
if let AstPrimitive::Integer(right) = right { if let AstPrimitive::Integer(right) = right {
return Ok(AstPrimitive::Integer(left * right)); return Ok(Some(AstPrimitive::Integer(left * right)));
} }
} }
if let AstPrimitive::Float(left) = left { if let AstPrimitive::Float(left) = left {
if let AstPrimitive::Float(right) = right { if let AstPrimitive::Float(right) = right {
return Ok(AstPrimitive::Float(left * right)); return Ok(Some(AstPrimitive::Float(left * right)));
} }
} }
Err(InterpreterError::ParseError("Can only multiply integers and floats".to_owned())) Err(InterpreterError::TypeError)
} }
} }
Divide { left, right } => { Divide { left, right } => {
let left = evaluate(left)?; let left =
let right = evaluate(right)?; interpret_statement(left, vars)?.ok_or(InterpreterError::ExpectedValue)?;
let right =
interpret_statement(right, vars)?.ok_or(InterpreterError::ExpectedValue)?;
if discriminant(&left) != discriminant(&right) { if discriminant(&left) != discriminant(&right) {
Err(InterpreterError::ParseError("Mismatched types".to_owned())) Err(InterpreterError::MismatchedTypes)
} else { } else {
if let AstPrimitive::Integer(left) = left { if let AstPrimitive::Integer(left) = left {
if let AstPrimitive::Integer(right) = right { if let AstPrimitive::Integer(right) = right {
return Ok(AstPrimitive::Integer(left / right)); return Ok(Some(AstPrimitive::Integer(left / right)));
} }
} }
if let AstPrimitive::Float(left) = left { if let AstPrimitive::Float(left) = left {
if let AstPrimitive::Float(right) = right { if let AstPrimitive::Float(right) = right {
return Ok(AstPrimitive::Float(left / right)); return Ok(Some(AstPrimitive::Float(left / right)));
} }
} }
Err(InterpreterError::ParseError("Can only divide integers and floats".to_owned())) Err(InterpreterError::TypeError)
} }
} }
Null => Err(InterpreterError::ParseError("Cannot evaluate null".to_owned())), _ => Err(InterpreterError::TypeError),
} }
} }
pub fn interpret(source: &str) -> Result<(), InterpreterError> { Ok(())
}
pub fn run(source: &str) -> Result<(), InterpreterError> {
println!("source: {:?}", source); println!("source: {:?}", source);
let tokens = tokenize(source); let tokens = tokenize(source);
println!("tokens: {:?}", tokens); println!("tokens: {:?}", tokens);
let ast = parse(&tokens?); let ast = parse(&tokens?);
println!("ast: {:?}", ast); println!("ast: {:?}", ast);
let value = evaluate(&ast?); let value = interpret(&ast?);
println!("value: {:?}", value); println!("value: {:?}", value);
Ok(()) 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 = `+` | `-` AddOperator = `+` | `-`
AddExpression = MultiplyExpression | AddExpression AddOperator MultiplyExpression AddExpression = MultiplyExpression | AddExpression AddOperator MultiplyExpression
Expression = AddExpression AssignExpression = AddExpression | Identifier `=` AddExpression
Expression = AssignExpression