Implement variables in the interpreter
This commit is contained in:
parent
746d567554
commit
fc5a50227f
11 changed files with 106 additions and 34 deletions
|
@ -8,6 +8,7 @@ pub enum ExpressionNode {
|
||||||
BinaryExpr(BinaryExpr),
|
BinaryExpr(BinaryExpr),
|
||||||
GroupingExpr(GroupingExpr),
|
GroupingExpr(GroupingExpr),
|
||||||
Literal(Literal),
|
Literal(Literal),
|
||||||
|
Variable(VariableExpr),
|
||||||
UnaryExpr(UnaryExpr),
|
UnaryExpr(UnaryExpr),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -16,7 +17,12 @@ macro_rules! all_variants {
|
||||||
{
|
{
|
||||||
use match_any::match_any;
|
use match_any::match_any;
|
||||||
use $crate::ast::expression::expression_node::*;
|
use $crate::ast::expression::expression_node::*;
|
||||||
match_any!($expr, ExpressionNode::BinaryExpr($val_name) | ExpressionNode::GroupingExpr($val_name) | ExpressionNode::Literal($val_name) | ExpressionNode::UnaryExpr($val_name) => $expr_arm)
|
match_any!($expr,
|
||||||
|
ExpressionNode::BinaryExpr($val_name) |
|
||||||
|
ExpressionNode::GroupingExpr($val_name) |
|
||||||
|
ExpressionNode::Literal($val_name) |
|
||||||
|
ExpressionNode::Variable($val_name) |
|
||||||
|
ExpressionNode::UnaryExpr($val_name) => $expr_arm)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -176,6 +182,22 @@ impl Debug for UnaryExpr {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct VariableExpr {
|
||||||
|
pub var_name: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Debug for VariableExpr {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
write!(f, "(variable({}))", self.var_name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl VariableExpr {
|
||||||
|
pub fn new(var_name: String) -> Self {
|
||||||
|
Self { var_name }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
|
@ -116,6 +116,7 @@ impl<'a, T: Iterator<Item = token::Token<'a>>> Parser<'a, T> {
|
||||||
None => return Err(token.location.wrap(InnerASTParsingError::UnmatchedBrace)),
|
None => return Err(token.location.wrap(InnerASTParsingError::UnmatchedBrace)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
TokenType::Identifier(var_name) => VariableExpr::new(var_name).into(),
|
||||||
a => return Err(token.location.wrap(InnerASTParsingError::IncorrectToken(a))),
|
a => return Err(token.location.wrap(InnerASTParsingError::IncorrectToken(a))),
|
||||||
};
|
};
|
||||||
Ok(node)
|
Ok(node)
|
||||||
|
|
|
@ -1,23 +1,24 @@
|
||||||
use super::Interpret;
|
use super::Interpret;
|
||||||
use super::{types::Value, RuntimeError};
|
use super::{types::Value, RuntimeError};
|
||||||
use crate::ast::expression::expression_node;
|
use crate::ast::expression::expression_node;
|
||||||
|
use crate::interpreter::world::World;
|
||||||
|
|
||||||
impl Interpret for expression_node::ExpressionNode {
|
impl Interpret for expression_node::ExpressionNode {
|
||||||
fn interpret(&self) -> Result<Value, RuntimeError> {
|
fn interpret(&self, w: &mut World) -> Result<Value, RuntimeError> {
|
||||||
expression_node::all_variants!(self, n => n.interpret())
|
expression_node::all_variants!(self, n => n.interpret(w))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Interpret for expression_node::Literal {
|
impl Interpret for expression_node::Literal {
|
||||||
fn interpret(&self) -> Result<Value, RuntimeError> {
|
fn interpret(&self, _: &mut World) -> Result<Value, RuntimeError> {
|
||||||
Ok(self.clone().into())
|
Ok(self.clone().into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Interpret for expression_node::BinaryExpr {
|
impl Interpret for expression_node::BinaryExpr {
|
||||||
fn interpret(&self) -> Result<Value, RuntimeError> {
|
fn interpret(&self, w: &mut World) -> Result<Value, RuntimeError> {
|
||||||
let left_val = self.left.interpret().expect("expected lval");
|
let left_val = self.left.interpret(w).expect("expected lval");
|
||||||
let right_val = self.right.interpret().expect("expected rval");
|
let right_val = self.right.interpret(w).expect("expected rval");
|
||||||
match self.operator {
|
match self.operator {
|
||||||
expression_node::Operator::BangEqual => Ok((left_val != right_val).into()),
|
expression_node::Operator::BangEqual => Ok((left_val != right_val).into()),
|
||||||
expression_node::Operator::Less => Ok((left_val < right_val).into()),
|
expression_node::Operator::Less => Ok((left_val < right_val).into()),
|
||||||
|
@ -31,8 +32,8 @@ impl Interpret for expression_node::BinaryExpr {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Interpret for expression_node::UnaryExpr {
|
impl Interpret for expression_node::UnaryExpr {
|
||||||
fn interpret(&self) -> Result<Value, RuntimeError> {
|
fn interpret(&self, w: &mut World) -> Result<Value, RuntimeError> {
|
||||||
let val = self.right.interpret()?;
|
let val = self.right.interpret(w)?;
|
||||||
match self.operator {
|
match self.operator {
|
||||||
expression_node::UnaryOperator::Bang => Ok(Value::Bool(!val.truthy())),
|
expression_node::UnaryOperator::Bang => Ok(Value::Bool(!val.truthy())),
|
||||||
expression_node::UnaryOperator::Minus => match val {
|
expression_node::UnaryOperator::Minus => match val {
|
||||||
|
@ -45,7 +46,16 @@ impl Interpret for expression_node::UnaryExpr {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Interpret for expression_node::GroupingExpr {
|
impl Interpret for expression_node::GroupingExpr {
|
||||||
fn interpret(&self) -> Result<Value, RuntimeError> {
|
fn interpret(&self, w: &mut World) -> Result<Value, RuntimeError> {
|
||||||
self.0.interpret()
|
self.0.interpret(w)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Interpret for expression_node::VariableExpr {
|
||||||
|
fn interpret(&self, world: &mut World) -> Result<Value, RuntimeError> {
|
||||||
|
match world.get_var(&self.var_name) {
|
||||||
|
Some(v) => Ok(v.clone()),
|
||||||
|
None => Err(RuntimeError),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
mod expression_interpreter;
|
mod expression_interpreter;
|
||||||
mod statement_interpreter;
|
mod statement_interpreter;
|
||||||
use super::types::Value;
|
|
||||||
pub use super::{error::RuntimeError, types};
|
pub use super::{error::RuntimeError, types};
|
||||||
|
use super::{types::Value, world::World};
|
||||||
|
|
||||||
pub trait Interpret {
|
pub trait Interpret {
|
||||||
fn interpret(&self) -> Result<Value, RuntimeError>;
|
fn interpret(&self, world: &mut World) -> Result<Value, RuntimeError>;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,30 +1,33 @@
|
||||||
use super::Interpret;
|
use super::Interpret;
|
||||||
use super::{types::Value, RuntimeError};
|
use super::{types::Value, RuntimeError};
|
||||||
use crate::ast::statement::statement_node;
|
use crate::ast::statement::statement_node;
|
||||||
|
use crate::interpreter::world::World;
|
||||||
|
|
||||||
impl Interpret for statement_node::Statement {
|
impl Interpret for statement_node::Statement {
|
||||||
fn interpret(&self) -> Result<Value, RuntimeError> {
|
fn interpret(&self, w: &mut World) -> Result<Value, RuntimeError> {
|
||||||
statement_node::all_variants!(self, n => n.interpret())
|
statement_node::all_variants!(self, n => n.interpret(w))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Interpret for statement_node::PrintStatement {
|
impl Interpret for statement_node::PrintStatement {
|
||||||
fn interpret(&self) -> Result<Value, RuntimeError> {
|
fn interpret(&self, w: &mut World) -> Result<Value, RuntimeError> {
|
||||||
let res = self.0.interpret()?;
|
let res = self.0.interpret(w)?;
|
||||||
println!("{:?}", res);
|
println!("{:?}", res);
|
||||||
Ok(res)
|
Ok(res)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Interpret for statement_node::ExpressionStatement {
|
impl Interpret for statement_node::ExpressionStatement {
|
||||||
fn interpret(&self) -> Result<Value, RuntimeError> {
|
fn interpret(&self, w: &mut World) -> Result<Value, RuntimeError> {
|
||||||
self.0.interpret()
|
self.0.interpret(w)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Interpret for statement_node::VariableAssignmentStatement {
|
impl Interpret for statement_node::VariableAssignmentStatement {
|
||||||
fn interpret(&self) -> Result<Value, RuntimeError> {
|
fn interpret(&self, w: &mut World) -> Result<Value, RuntimeError> {
|
||||||
let expr_val = self.node.interpret()?;
|
let expr_val = self.node.interpret(w)?;
|
||||||
|
// Clone for now, later this will use a GC and won't need to clone
|
||||||
|
w.set_var(self.var_name.clone(), expr_val.clone());
|
||||||
Ok(expr_val)
|
Ok(expr_val)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
pub mod ast_walker;
|
pub mod ast_walker;
|
||||||
pub mod error;
|
pub mod error;
|
||||||
pub mod types;
|
pub mod types;
|
||||||
|
pub mod world;
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use crate::ast::expression::expression_node;
|
use crate::ast::expression::expression_node;
|
||||||
use from_variants::FromVariants;
|
use from_variants::FromVariants;
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, PartialOrd, FromVariants)]
|
#[derive(Debug, PartialEq, PartialOrd, Clone, FromVariants)]
|
||||||
pub enum Value {
|
pub enum Value {
|
||||||
Int(i32),
|
Int(i32),
|
||||||
Float(f32),
|
Float(f32),
|
||||||
|
|
35
src/interpreter/world.rs
Normal file
35
src/interpreter/world.rs
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
use crate::ast::statement::statement_node::Statement;
|
||||||
|
|
||||||
|
use super::{
|
||||||
|
ast_walker::{Interpret, RuntimeError},
|
||||||
|
types::Value,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct World {
|
||||||
|
variables: HashMap<String, Value>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl World {
|
||||||
|
pub fn set_var(&mut self, name: String, v: Value) -> Option<Value> {
|
||||||
|
self.variables.insert(name, v)
|
||||||
|
}
|
||||||
|
pub fn get_var(&mut self, name: &str) -> Option<&Value> {
|
||||||
|
self.variables.get(name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl World {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self::default()
|
||||||
|
}
|
||||||
|
pub fn exec(&mut self, nodes: Vec<Statement>) -> Result<Value, RuntimeError> {
|
||||||
|
let mut last_res = Value::Nil;
|
||||||
|
for statement in nodes {
|
||||||
|
last_res = statement.interpret(self)?;
|
||||||
|
}
|
||||||
|
Ok(last_res)
|
||||||
|
}
|
||||||
|
}
|
|
@ -8,6 +8,7 @@ use ast::parser::ParseAllResult;
|
||||||
use ast::statement::statement_node::Statement;
|
use ast::statement::statement_node::Statement;
|
||||||
use interpreter::ast_walker::{Interpret, RuntimeError};
|
use interpreter::ast_walker::{Interpret, RuntimeError};
|
||||||
use interpreter::types::Value;
|
use interpreter::types::Value;
|
||||||
|
use interpreter::world::World;
|
||||||
use lexer::{token::Token, Lexer, LexingError};
|
use lexer::{token::Token, Lexer, LexingError};
|
||||||
|
|
||||||
pub fn lex<'a, 'b>(
|
pub fn lex<'a, 'b>(
|
||||||
|
@ -25,8 +26,9 @@ pub fn parse(tokens: Vec<Token>) -> ParseAllResult {
|
||||||
|
|
||||||
pub fn exec(nodes: Vec<Statement>) -> Result<Value, RuntimeError> {
|
pub fn exec(nodes: Vec<Statement>) -> Result<Value, RuntimeError> {
|
||||||
let mut last_res = Value::Nil;
|
let mut last_res = Value::Nil;
|
||||||
|
let mut world = World::new();
|
||||||
for statement in nodes {
|
for statement in nodes {
|
||||||
last_res = statement.interpret()?;
|
last_res = statement.interpret(&mut world)?;
|
||||||
}
|
}
|
||||||
Ok(last_res)
|
Ok(last_res)
|
||||||
}
|
}
|
||||||
|
|
12
src/main.rs
12
src/main.rs
|
@ -1,5 +1,6 @@
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use crftng_intrprtrs::{exec, lex, parse};
|
use crftng_intrprtrs::interpreter::world::World;
|
||||||
|
use crftng_intrprtrs::{lex, parse};
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::{fs, io};
|
use std::{fs, io};
|
||||||
use tracing::Level;
|
use tracing::Level;
|
||||||
|
@ -16,7 +17,7 @@ struct Args {
|
||||||
no_run: bool,
|
no_run: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run(code: &str, args: &Args) {
|
fn run(code: &str, args: &Args, world: &mut World) {
|
||||||
let src_file = args.file.as_ref().map(|f| f.to_string_lossy().to_string());
|
let src_file = args.file.as_ref().map(|f| f.to_string_lossy().to_string());
|
||||||
let tokens = match lex(code, src_file.as_deref()) {
|
let tokens = match lex(code, src_file.as_deref()) {
|
||||||
Ok(v) => v,
|
Ok(v) => v,
|
||||||
|
@ -44,19 +45,20 @@ fn run(code: &str, args: &Args) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if !args.no_run {
|
if !args.no_run {
|
||||||
println!("{:?}", exec(ast));
|
println!("{:?}", world.exec(ast));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run_file(args: &Args) -> Result<(), io::Error> {
|
fn run_file(args: &Args) -> Result<(), io::Error> {
|
||||||
let src = fs::read_to_string(args.file.as_ref().unwrap())?;
|
let src = fs::read_to_string(args.file.as_ref().unwrap())?;
|
||||||
run(&src, args);
|
run(&src, args, &mut World::new());
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run_repl(args: &Args) {
|
fn run_repl(args: &Args) {
|
||||||
assert!(args.file.is_none());
|
assert!(args.file.is_none());
|
||||||
|
|
||||||
|
let mut world = World::new();
|
||||||
let mut line_buf = String::new();
|
let mut line_buf = String::new();
|
||||||
loop {
|
loop {
|
||||||
match io::stdin().read_line(&mut line_buf) {
|
match io::stdin().read_line(&mut line_buf) {
|
||||||
|
@ -68,7 +70,7 @@ fn run_repl(args: &Args) {
|
||||||
|
|
||||||
let line = line_buf.trim();
|
let line = line_buf.trim();
|
||||||
if !line.is_empty() {
|
if !line.is_empty() {
|
||||||
run(&line_buf, args);
|
run(&line_buf, args, &mut world);
|
||||||
line_buf.clear();
|
line_buf.clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
8
test.lox
8
test.lox
|
@ -1,6 +1,2 @@
|
||||||
12 == 1;
|
asdf = 1;
|
||||||
print 1;
|
print asdf;
|
||||||
print 1.90 == 1.90;
|
|
||||||
print 1.90;
|
|
||||||
print 2;
|
|
||||||
nil;
|
|
||||||
|
|
Loading…
Reference in a new issue