Variables
This commit is contained in:
parent
643b4c8538
commit
0be64e5eb3
@ -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);
|
||||
|
@ -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)?;
|
||||
|
324
src/parse.rs
324
src/parse.rs
@ -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 ¤t_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::ParseError("Expected literal".to_owned()))
|
||||
Err(InterpreterError::UnexpectedToken)
|
||||
}
|
||||
} else {
|
||||
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_expression(tokens: &[Token]) -> Result<(AstNode, usize), InterpreterError> {
|
||||
parse_addition_expression(tokens)
|
||||
}
|
||||
let (ast, _) = parse_expression(tokens)?;
|
||||
Ok(ast)
|
||||
}
|
||||
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))
|
||||
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 {
|
||||
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 } => {
|
||||
let left = evaluate(left)?;
|
||||
let right = evaluate(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::ParseError("Mismatched types".to_owned()))
|
||||
Err(InterpreterError::MismatchedTypes)
|
||||
} else {
|
||||
if let AstPrimitive::Integer(left) = left {
|
||||
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(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(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 } => {
|
||||
let left = evaluate(left)?;
|
||||
let right = evaluate(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::ParseError("Mismatched types".to_owned()))
|
||||
Err(InterpreterError::MismatchedTypes)
|
||||
} else {
|
||||
if let AstPrimitive::Integer(left) = left {
|
||||
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(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 } => {
|
||||
let left = evaluate(left)?;
|
||||
let right = evaluate(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::ParseError("Mismatched types".to_owned()))
|
||||
Err(InterpreterError::MismatchedTypes)
|
||||
} else {
|
||||
if let AstPrimitive::Integer(left) = left {
|
||||
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(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 } => {
|
||||
let left = evaluate(left)?;
|
||||
let right = evaluate(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::ParseError("Mismatched types".to_owned()))
|
||||
Err(InterpreterError::MismatchedTypes)
|
||||
} else {
|
||||
if let AstPrimitive::Integer(left) = left {
|
||||
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(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);
|
||||
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(())
|
||||
}
|
4
test.txt
4
test.txt
@ -10,4 +10,6 @@ MultiplyExpression = UnaryExpression | MultiplyExpression MultiplyOperator Unary
|
||||
AddOperator = `+` | `-`
|
||||
AddExpression = MultiplyExpression | AddExpression AddOperator MultiplyExpression
|
||||
|
||||
Expression = AddExpression
|
||||
AssignExpression = AddExpression | Identifier `=` AddExpression
|
||||
|
||||
Expression = AssignExpression
|
Loading…
x
Reference in New Issue
Block a user