Compare commits
3 Commits
f50b765c1e
...
2ae75dcdd0
Author | SHA1 | Date | |
---|---|---|---|
2ae75dcdd0 | |||
9ea203f419 | |||
e1b04328d5 |
@ -1,12 +1,12 @@
|
|||||||
//! In-memory representation of a Dust program.
|
//! In-memory representation of a Dust program.
|
||||||
use std::{
|
use std::{
|
||||||
collections::{BTreeMap, HashMap, VecDeque},
|
collections::{BTreeMap, VecDeque},
|
||||||
fmt::{self, Display, Formatter},
|
fmt::{self, Display, Formatter},
|
||||||
};
|
};
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::{BuiltInFunction, Identifier, Span, Type, Value};
|
use crate::{BuiltInFunction, Context, Identifier, Span, Type, Value};
|
||||||
|
|
||||||
/// In-memory representation of a Dust program.
|
/// In-memory representation of a Dust program.
|
||||||
#[derive(Debug, Clone, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
|
||||||
@ -75,18 +75,16 @@ pub enum Statement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Statement {
|
impl Statement {
|
||||||
pub fn expected_type(&self, variables: &HashMap<Identifier, Value>) -> Option<Type> {
|
pub fn expected_type(&self, context: &Context) -> Option<Type> {
|
||||||
match self {
|
match self {
|
||||||
Statement::Block(nodes) => nodes.last().unwrap().inner.expected_type(variables),
|
Statement::Block(nodes) => nodes.last().unwrap().inner.expected_type(context),
|
||||||
Statement::BinaryOperation { left, .. } => left.inner.expected_type(variables),
|
Statement::BinaryOperation { left, .. } => left.inner.expected_type(context),
|
||||||
Statement::BuiltInFunctionCall { function, .. } => function.expected_return_type(),
|
Statement::BuiltInFunctionCall { function, .. } => function.expected_return_type(),
|
||||||
Statement::Constant(value) => Some(value.r#type(variables)),
|
Statement::Constant(value) => Some(value.r#type(context)),
|
||||||
Statement::FunctionCall { function, .. } => function.inner.expected_type(variables),
|
Statement::FunctionCall { function, .. } => function.inner.expected_type(context),
|
||||||
Statement::Identifier(identifier) => variables
|
Statement::Identifier(identifier) => context.get_type(identifier).cloned(),
|
||||||
.get(identifier)
|
|
||||||
.map(|value| value.r#type(variables)),
|
|
||||||
Statement::List(nodes) => {
|
Statement::List(nodes) => {
|
||||||
let item_type = nodes.first().unwrap().inner.expected_type(variables)?;
|
let item_type = nodes.first().unwrap().inner.expected_type(context)?;
|
||||||
|
|
||||||
Some(Type::List {
|
Some(Type::List {
|
||||||
length: nodes.len(),
|
length: nodes.len(),
|
||||||
@ -98,7 +96,7 @@ impl Statement {
|
|||||||
|
|
||||||
for (identifier, item) in nodes {
|
for (identifier, item) in nodes {
|
||||||
if let Statement::Identifier(identifier) = &identifier.inner {
|
if let Statement::Identifier(identifier) = &identifier.inner {
|
||||||
types.insert(identifier.clone(), item.inner.expected_type(variables)?);
|
types.insert(identifier.clone(), item.inner.expected_type(context)?);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,14 +6,13 @@
|
|||||||
//! - `analyze` convenience function
|
//! - `analyze` convenience function
|
||||||
//! - `Analyzer` struct
|
//! - `Analyzer` struct
|
||||||
use std::{
|
use std::{
|
||||||
collections::HashMap,
|
|
||||||
error::Error,
|
error::Error,
|
||||||
fmt::{self, Display, Formatter},
|
fmt::{self, Display, Formatter},
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
abstract_tree::BinaryOperator, AbstractSyntaxTree, BuiltInFunction, Identifier, Node, Span,
|
abstract_tree::BinaryOperator, AbstractSyntaxTree, BuiltInFunction, Context, Node, Span,
|
||||||
Statement, Type, Value,
|
Statement, Type,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Analyzes the abstract syntax tree for errors.
|
/// Analyzes the abstract syntax tree for errors.
|
||||||
@ -31,9 +30,9 @@ use crate::{
|
|||||||
/// ```
|
/// ```
|
||||||
pub fn analyze(
|
pub fn analyze(
|
||||||
abstract_tree: &AbstractSyntaxTree,
|
abstract_tree: &AbstractSyntaxTree,
|
||||||
variables: &HashMap<Identifier, Value>,
|
context: &mut Context,
|
||||||
) -> Result<(), AnalyzerError> {
|
) -> Result<(), AnalyzerError> {
|
||||||
let analyzer = Analyzer::new(abstract_tree, variables);
|
let mut analyzer = Analyzer::new(abstract_tree, context);
|
||||||
|
|
||||||
analyzer.analyze()
|
analyzer.analyze()
|
||||||
}
|
}
|
||||||
@ -53,21 +52,18 @@ pub fn analyze(
|
|||||||
/// assert!(result.is_err());
|
/// assert!(result.is_err());
|
||||||
pub struct Analyzer<'a> {
|
pub struct Analyzer<'a> {
|
||||||
abstract_tree: &'a AbstractSyntaxTree,
|
abstract_tree: &'a AbstractSyntaxTree,
|
||||||
variables: &'a HashMap<Identifier, Value>,
|
context: &'a mut Context,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Analyzer<'a> {
|
impl<'a> Analyzer<'a> {
|
||||||
pub fn new(
|
pub fn new(abstract_tree: &'a AbstractSyntaxTree, context: &'a mut Context) -> Self {
|
||||||
abstract_tree: &'a AbstractSyntaxTree,
|
|
||||||
variables: &'a HashMap<Identifier, Value>,
|
|
||||||
) -> Self {
|
|
||||||
Self {
|
Self {
|
||||||
abstract_tree,
|
abstract_tree,
|
||||||
variables,
|
context,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn analyze(&self) -> Result<(), AnalyzerError> {
|
pub fn analyze(&mut self) -> Result<(), AnalyzerError> {
|
||||||
for node in &self.abstract_tree.nodes {
|
for node in &self.abstract_tree.nodes {
|
||||||
self.analyze_node(node)?;
|
self.analyze_node(node)?;
|
||||||
}
|
}
|
||||||
@ -75,17 +71,27 @@ impl<'a> Analyzer<'a> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn analyze_node(&self, node: &Node<Statement>) -> Result<(), AnalyzerError> {
|
fn analyze_node(&mut self, node: &Node<Statement>) -> Result<(), AnalyzerError> {
|
||||||
match &node.inner {
|
match &node.inner {
|
||||||
Statement::BinaryOperation {
|
Statement::BinaryOperation {
|
||||||
left,
|
left,
|
||||||
operator,
|
operator,
|
||||||
right,
|
right,
|
||||||
} => {
|
} => {
|
||||||
if let BinaryOperator::AddAssign | BinaryOperator::Assign = operator.inner {
|
if let BinaryOperator::Assign = operator.inner {
|
||||||
if let Statement::Identifier(_) = left.inner {
|
if let Statement::Identifier(identifier) = &left.inner {
|
||||||
self.analyze_node(right)?;
|
self.analyze_node(right)?;
|
||||||
|
|
||||||
|
let right_type = right.inner.expected_type(self.context);
|
||||||
|
|
||||||
|
self.context.set_type(
|
||||||
|
identifier.clone(),
|
||||||
|
right_type.ok_or(AnalyzerError::ExpectedValue {
|
||||||
|
actual: right.as_ref().clone(),
|
||||||
|
position: right.position,
|
||||||
|
})?,
|
||||||
|
);
|
||||||
|
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -93,8 +99,8 @@ impl<'a> Analyzer<'a> {
|
|||||||
self.analyze_node(left)?;
|
self.analyze_node(left)?;
|
||||||
self.analyze_node(right)?;
|
self.analyze_node(right)?;
|
||||||
|
|
||||||
let left_type = left.inner.expected_type(self.variables);
|
let left_type = left.inner.expected_type(self.context);
|
||||||
let right_type = right.inner.expected_type(self.variables);
|
let right_type = right.inner.expected_type(self.context);
|
||||||
|
|
||||||
if let BinaryOperator::Add
|
if let BinaryOperator::Add
|
||||||
| BinaryOperator::Subtract
|
| BinaryOperator::Subtract
|
||||||
@ -154,7 +160,7 @@ impl<'a> Analyzer<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
Statement::Identifier(identifier) => {
|
Statement::Identifier(identifier) => {
|
||||||
if !self.variables.contains_key(identifier) {
|
if !self.context.contains(identifier) {
|
||||||
return Err(AnalyzerError::UndefinedVariable {
|
return Err(AnalyzerError::UndefinedVariable {
|
||||||
identifier: node.clone(),
|
identifier: node.clone(),
|
||||||
});
|
});
|
||||||
@ -184,7 +190,7 @@ impl<'a> Analyzer<'a> {
|
|||||||
|
|
||||||
if let Statement::BuiltInFunctionCall { function, .. } = &right.inner {
|
if let Statement::BuiltInFunctionCall { function, .. } = &right.inner {
|
||||||
if function == &BuiltInFunction::IsEven || function == &BuiltInFunction::IsOdd {
|
if function == &BuiltInFunction::IsEven || function == &BuiltInFunction::IsOdd {
|
||||||
if let Some(Type::Integer) = left.inner.expected_type(self.variables) {
|
if let Some(Type::Integer) = left.inner.expected_type(self.context) {
|
||||||
} else {
|
} else {
|
||||||
return Err(AnalyzerError::ExpectedIntegerOrFloat {
|
return Err(AnalyzerError::ExpectedIntegerOrFloat {
|
||||||
actual: left.as_ref().clone(),
|
actual: left.as_ref().clone(),
|
||||||
@ -348,8 +354,8 @@ mod tests {
|
|||||||
)]
|
)]
|
||||||
.into(),
|
.into(),
|
||||||
};
|
};
|
||||||
let variables = HashMap::new();
|
let mut context = Context::new();
|
||||||
let analyzer = Analyzer::new(&abstract_tree, &variables);
|
let mut analyzer = Analyzer::new(&abstract_tree, &mut context);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
analyzer.analyze(),
|
analyzer.analyze(),
|
||||||
@ -373,8 +379,8 @@ mod tests {
|
|||||||
)]
|
)]
|
||||||
.into(),
|
.into(),
|
||||||
};
|
};
|
||||||
let variables = HashMap::new();
|
let mut context = Context::new();
|
||||||
let analyzer = Analyzer::new(&abstract_tree, &variables);
|
let mut analyzer = Analyzer::new(&abstract_tree, &mut context);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
analyzer.analyze(),
|
analyzer.analyze(),
|
||||||
@ -404,8 +410,8 @@ mod tests {
|
|||||||
)]
|
)]
|
||||||
.into(),
|
.into(),
|
||||||
};
|
};
|
||||||
let variables = HashMap::new();
|
let mut context = Context::new();
|
||||||
let analyzer = Analyzer::new(&abstract_tree, &variables);
|
let mut analyzer = Analyzer::new(&abstract_tree, &mut context);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
analyzer.analyze(),
|
analyzer.analyze(),
|
||||||
@ -434,8 +440,8 @@ mod tests {
|
|||||||
)]
|
)]
|
||||||
.into(),
|
.into(),
|
||||||
};
|
};
|
||||||
let variables = HashMap::new();
|
let mut context = Context::new();
|
||||||
let analyzer = Analyzer::new(&abstract_tree, &variables);
|
let mut analyzer = Analyzer::new(&abstract_tree, &mut context);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
analyzer.analyze(),
|
analyzer.analyze(),
|
||||||
@ -455,8 +461,8 @@ mod tests {
|
|||||||
)]
|
)]
|
||||||
.into(),
|
.into(),
|
||||||
};
|
};
|
||||||
let variables = HashMap::new();
|
let mut context = Context::new();
|
||||||
let analyzer = Analyzer::new(&abstract_tree, &variables);
|
let mut analyzer = Analyzer::new(&abstract_tree, &mut context);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
analyzer.analyze(),
|
analyzer.analyze(),
|
||||||
|
84
dust-lang/src/context.rs
Normal file
84
dust-lang/src/context.rs
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
//! Garbage-collecting context for variables.
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
use crate::{Identifier, Type, Value};
|
||||||
|
|
||||||
|
/// Garbage-collecting context for variables.
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct Context {
|
||||||
|
pub variables: HashMap<Identifier, (VariableData, UsageData)>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Context {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
variables: HashMap::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn contains(&self, identifier: &Identifier) -> bool {
|
||||||
|
self.variables.contains_key(identifier)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get(&self, identifier: &Identifier) -> Option<&(VariableData, UsageData)> {
|
||||||
|
self.variables.get(identifier)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_type(&self, identifier: &Identifier) -> Option<&Type> {
|
||||||
|
match self.variables.get(identifier) {
|
||||||
|
Some((VariableData::Type(r#type), _)) => Some(r#type),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_value(&self, identifier: &Identifier) -> Option<&Value> {
|
||||||
|
match self.variables.get(identifier) {
|
||||||
|
Some((VariableData::Value(value), _)) => Some(value),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_variable_data(&self, identifier: &Identifier) -> Option<&VariableData> {
|
||||||
|
match self.variables.get(identifier) {
|
||||||
|
Some((variable_data, _)) => Some(variable_data),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_type(&mut self, identifier: Identifier, r#type: Type) {
|
||||||
|
self.variables.insert(
|
||||||
|
identifier,
|
||||||
|
(VariableData::Type(r#type), UsageData::default()),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_value(&mut self, identifier: Identifier, value: Value) {
|
||||||
|
self.variables.insert(
|
||||||
|
identifier,
|
||||||
|
(VariableData::Value(value), UsageData::default()),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn collect_garbage(&mut self) {
|
||||||
|
self.variables
|
||||||
|
.retain(|_, (_, usage_data)| usage_data.used < usage_data.allowed_uses);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Context {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub enum VariableData {
|
||||||
|
Value(Value),
|
||||||
|
Type(Type),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default, Debug, Clone)]
|
||||||
|
pub struct UsageData {
|
||||||
|
pub allowed_uses: u16,
|
||||||
|
pub used: u16,
|
||||||
|
}
|
@ -228,7 +228,10 @@ impl Lexer {
|
|||||||
} else {
|
} else {
|
||||||
self.position += 1;
|
self.position += 1;
|
||||||
|
|
||||||
return Err(LexError::UnexpectedCharacter(c));
|
return Err(LexError::UnexpectedCharacter {
|
||||||
|
character: c,
|
||||||
|
position: self.position,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
';' => {
|
';' => {
|
||||||
@ -244,13 +247,19 @@ impl Lexer {
|
|||||||
} else {
|
} else {
|
||||||
self.position += 1;
|
self.position += 1;
|
||||||
|
|
||||||
return Err(LexError::UnexpectedCharacter(c));
|
return Err(LexError::UnexpectedCharacter {
|
||||||
|
character: c,
|
||||||
|
position: self.position,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
self.position += 1;
|
self.position += 1;
|
||||||
|
|
||||||
return Err(LexError::UnexpectedCharacter(c));
|
return Err(LexError::UnexpectedCharacter {
|
||||||
|
character: c,
|
||||||
|
position: self.position,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -433,13 +442,21 @@ impl Default for Lexer {
|
|||||||
|
|
||||||
#[derive(Debug, PartialEq, Clone)]
|
#[derive(Debug, PartialEq, Clone)]
|
||||||
pub enum LexError {
|
pub enum LexError {
|
||||||
UnexpectedCharacter(char),
|
UnexpectedCharacter { character: char, position: usize },
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LexError {
|
||||||
|
pub fn position(&self) -> Span {
|
||||||
|
match self {
|
||||||
|
Self::UnexpectedCharacter { position, .. } => (*position, *position),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Error for LexError {
|
impl Error for LexError {
|
||||||
fn source(&self) -> Option<&(dyn Error + 'static)> {
|
fn source(&self) -> Option<&(dyn Error + 'static)> {
|
||||||
match self {
|
match self {
|
||||||
Self::UnexpectedCharacter(_) => None,
|
Self::UnexpectedCharacter { .. } => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -447,7 +464,7 @@ impl Error for LexError {
|
|||||||
impl Display for LexError {
|
impl Display for LexError {
|
||||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
Self::UnexpectedCharacter(character) => {
|
Self::UnexpectedCharacter { character, .. } => {
|
||||||
write!(f, "Unexpected character: '{}'", character)
|
write!(f, "Unexpected character: '{}'", character)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
pub mod abstract_tree;
|
pub mod abstract_tree;
|
||||||
pub mod analyzer;
|
pub mod analyzer;
|
||||||
pub mod built_in_function;
|
pub mod built_in_function;
|
||||||
|
pub mod context;
|
||||||
pub mod dust_error;
|
pub mod dust_error;
|
||||||
pub mod identifier;
|
pub mod identifier;
|
||||||
pub mod lex;
|
pub mod lex;
|
||||||
@ -13,15 +14,16 @@ pub mod r#type;
|
|||||||
pub mod value;
|
pub mod value;
|
||||||
pub mod vm;
|
pub mod vm;
|
||||||
|
|
||||||
pub use abstract_tree::{AbstractSyntaxTree, Node, Statement};
|
pub use abstract_tree::{AbstractSyntaxTree, BinaryOperator, Node, Statement};
|
||||||
pub use analyzer::{analyze, Analyzer, AnalyzerError};
|
pub use analyzer::{analyze, Analyzer, AnalyzerError};
|
||||||
pub use built_in_function::{BuiltInFunction, BuiltInFunctionError};
|
pub use built_in_function::{BuiltInFunction, BuiltInFunctionError};
|
||||||
|
pub use context::{Context, UsageData, VariableData};
|
||||||
pub use dust_error::DustError;
|
pub use dust_error::DustError;
|
||||||
pub use identifier::Identifier;
|
pub use identifier::Identifier;
|
||||||
pub use lex::{lex, LexError, Lexer};
|
pub use lex::{lex, LexError, Lexer};
|
||||||
pub use parse::{parse, ParseError, Parser};
|
pub use parse::{parse, ParseError, Parser};
|
||||||
pub use r#type::Type;
|
pub use r#type::Type;
|
||||||
pub use token::Token;
|
pub use token::{Token, TokenOwned};
|
||||||
pub use value::{Value, ValueError};
|
pub use value::{Value, ValueError};
|
||||||
pub use vm::{run, Vm, VmError};
|
pub use vm::{run, Vm, VmError};
|
||||||
|
|
||||||
|
@ -12,8 +12,8 @@ use std::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
abstract_tree::BinaryOperator, built_in_function::BuiltInFunction, token::TokenOwned,
|
AbstractSyntaxTree, BinaryOperator, BuiltInFunction, Identifier, LexError, Lexer, Node, Span,
|
||||||
AbstractSyntaxTree, Identifier, LexError, Lexer, Node, Span, Statement, Token, Value,
|
Statement, Token, TokenOwned, Value,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Parses the input into an abstract syntax tree.
|
/// Parses the input into an abstract syntax tree.
|
||||||
@ -121,44 +121,32 @@ impl<'src> Parser<'src> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse(&mut self) -> Result<Node<Statement>, ParseError> {
|
|
||||||
self.parse_node(0)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn current(&self) -> &(Token, Span) {
|
pub fn current(&self) -> &(Token, Span) {
|
||||||
&self.current
|
&self.current
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn parse(&mut self) -> Result<Node<Statement>, ParseError> {
|
||||||
|
self.parse_statement(0)
|
||||||
|
}
|
||||||
|
|
||||||
fn next_token(&mut self) -> Result<(), ParseError> {
|
fn next_token(&mut self) -> Result<(), ParseError> {
|
||||||
let next = self.lexer.next_token(self.source);
|
self.current = self.lexer.next_token(self.source)?;
|
||||||
|
|
||||||
self.current = match next {
|
|
||||||
Ok((token, position)) => (token, position),
|
|
||||||
Err(lex_error) => {
|
|
||||||
let position = {
|
|
||||||
self.next_token()?;
|
|
||||||
|
|
||||||
self.current.1
|
|
||||||
};
|
|
||||||
|
|
||||||
return Err(ParseError::LexError {
|
|
||||||
error: lex_error,
|
|
||||||
position,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_node(&mut self, precedence: u8) -> Result<Node<Statement>, ParseError> {
|
fn parse_statement(&mut self, precedence: u8) -> Result<Node<Statement>, ParseError> {
|
||||||
let left = self.parse_primary()?;
|
let mut left = self.parse_primary()?;
|
||||||
|
|
||||||
if precedence < self.current_precedence() {
|
while precedence < self.current.0.precedence() {
|
||||||
self.parse_infix(left)
|
if self.current.0.is_postfix() {
|
||||||
} else {
|
left = self.parse_postfix(left)?;
|
||||||
Ok(left)
|
} else {
|
||||||
|
left = self.parse_infix(left)?;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Ok(left)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_primary(&mut self) -> Result<Node<Statement>, ParseError> {
|
fn parse_primary(&mut self) -> Result<Node<Statement>, ParseError> {
|
||||||
@ -241,7 +229,7 @@ impl<'src> Parser<'src> {
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
let next_node = self.parse_node(0)?;
|
let next_node = self.parse_statement(0)?;
|
||||||
|
|
||||||
// If the next node is an assignment, this might be a map
|
// If the next node is an assignment, this might be a map
|
||||||
if let Statement::BinaryOperation {
|
if let Statement::BinaryOperation {
|
||||||
@ -264,20 +252,20 @@ impl<'src> Parser<'src> {
|
|||||||
if let Statement::Map(map_properties) =
|
if let Statement::Map(map_properties) =
|
||||||
statement.get_or_insert_with(|| Statement::Map(Vec::new()))
|
statement.get_or_insert_with(|| Statement::Map(Vec::new()))
|
||||||
{
|
{
|
||||||
// Ignore commas after properties
|
|
||||||
if let Token::Comma = self.current.0 {
|
|
||||||
self.next_token()?;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add the new property to the map
|
// Add the new property to the map
|
||||||
map_properties.push((*left, *right));
|
map_properties.push((*left, *right));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Ignore commas after properties
|
||||||
|
if let Token::Comma = self.current.0 {
|
||||||
|
self.next_token()?;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// Otherwise, the new statement is a block
|
// Otherwise, the new statement is a block
|
||||||
} else if let Statement::Block(statements) =
|
} else if let Statement::Block(statements) =
|
||||||
statement.get_or_insert_with(|| Statement::Block(Vec::new()))
|
statement.get_or_insert_with(|| Statement::Block(Vec::new()))
|
||||||
{
|
{
|
||||||
// Add the assignment statement to the block
|
// Add the statement to the block
|
||||||
statements.push(next_node);
|
statements.push(next_node);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -285,7 +273,7 @@ impl<'src> Parser<'src> {
|
|||||||
(Token::LeftParenthesis, left_position) => {
|
(Token::LeftParenthesis, left_position) => {
|
||||||
self.next_token()?;
|
self.next_token()?;
|
||||||
|
|
||||||
let node = self.parse_node(0)?;
|
let node = self.parse_statement(0)?;
|
||||||
|
|
||||||
if let (Token::RightParenthesis, right_position) = self.current {
|
if let (Token::RightParenthesis, right_position) = self.current {
|
||||||
self.next_token()?;
|
self.next_token()?;
|
||||||
@ -320,7 +308,7 @@ impl<'src> Parser<'src> {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Ok(instruction) = self.parse_node(0) {
|
if let Ok(instruction) = self.parse_statement(0) {
|
||||||
nodes.push(instruction);
|
nodes.push(instruction);
|
||||||
} else {
|
} else {
|
||||||
return Err(ParseError::ExpectedToken {
|
return Err(ParseError::ExpectedToken {
|
||||||
@ -369,7 +357,7 @@ impl<'src> Parser<'src> {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Ok(node) = self.parse_node(0) {
|
if let Ok(node) = self.parse_statement(0) {
|
||||||
if let Some(ref mut arguments) = value_arguments {
|
if let Some(ref mut arguments) = value_arguments {
|
||||||
arguments.push(node);
|
arguments.push(node);
|
||||||
} else {
|
} else {
|
||||||
@ -403,59 +391,51 @@ impl<'src> Parser<'src> {
|
|||||||
fn parse_infix(&mut self, left: Node<Statement>) -> Result<Node<Statement>, ParseError> {
|
fn parse_infix(&mut self, left: Node<Statement>) -> Result<Node<Statement>, ParseError> {
|
||||||
let left_start = left.position.0;
|
let left_start = left.position.0;
|
||||||
|
|
||||||
// Postfix operations
|
if let Token::Dot = &self.current.0 {
|
||||||
if let Token::Semicolon = &self.current.0 {
|
|
||||||
self.next_token()?;
|
self.next_token()?;
|
||||||
|
|
||||||
let right_end = self.current.1 .1;
|
let right = self.parse_statement(Token::Dot.precedence() + 1)?;
|
||||||
|
let right_end = right.position.1;
|
||||||
|
|
||||||
return Ok(Node::new(
|
return Ok(Node::new(
|
||||||
Statement::Nil(Box::new(left)),
|
Statement::PropertyAccess(Box::new(left), Box::new(right)),
|
||||||
(left_start, right_end),
|
(left_start, right_end),
|
||||||
));
|
));
|
||||||
};
|
}
|
||||||
|
|
||||||
// Infix operations
|
let binary_operator = match &self.current.0 {
|
||||||
let binary_operator = match &self.current {
|
Token::DoubleAmpersand => Node::new(BinaryOperator::And, self.current.1),
|
||||||
(Token::Dot, _) => {
|
Token::DoubleEqual => Node::new(BinaryOperator::Equal, self.current.1),
|
||||||
self.next_token()?;
|
Token::DoublePipe => Node::new(BinaryOperator::Or, self.current.1),
|
||||||
|
Token::Equal => Node::new(BinaryOperator::Assign, self.current.1),
|
||||||
let right = self.parse_node(0)?;
|
Token::Greater => Node::new(BinaryOperator::Greater, self.current.1),
|
||||||
let right_end = right.position.1;
|
Token::GreaterEqual => Node::new(BinaryOperator::GreaterOrEqual, self.current.1),
|
||||||
|
Token::Less => Node::new(BinaryOperator::Less, self.current.1),
|
||||||
return Ok(Node::new(
|
Token::LessEqual => Node::new(BinaryOperator::LessOrEqual, self.current.1),
|
||||||
Statement::PropertyAccess(Box::new(left), Box::new(right)),
|
Token::Minus => Node::new(BinaryOperator::Subtract, self.current.1),
|
||||||
(left_start, right_end),
|
Token::Plus => Node::new(BinaryOperator::Add, self.current.1),
|
||||||
));
|
Token::PlusEqual => Node::new(BinaryOperator::AddAssign, self.current.1),
|
||||||
}
|
Token::Star => Node::new(BinaryOperator::Multiply, self.current.1),
|
||||||
(Token::DoubleAmpersand, _) => Node::new(BinaryOperator::And, self.current.1),
|
Token::Slash => Node::new(BinaryOperator::Divide, self.current.1),
|
||||||
(Token::DoubleEqual, _) => Node::new(BinaryOperator::Equal, self.current.1),
|
Token::Percent => Node::new(BinaryOperator::Modulo, self.current.1),
|
||||||
(Token::DoublePipe, _) => Node::new(BinaryOperator::Or, self.current.1),
|
|
||||||
(Token::Equal, _) => Node::new(BinaryOperator::Assign, self.current.1),
|
|
||||||
(Token::Greater, _) => Node::new(BinaryOperator::Greater, self.current.1),
|
|
||||||
(Token::GreaterEqual, _) => Node::new(BinaryOperator::GreaterOrEqual, self.current.1),
|
|
||||||
(Token::Less, _) => Node::new(BinaryOperator::Less, self.current.1),
|
|
||||||
(Token::LessEqual, _) => Node::new(BinaryOperator::LessOrEqual, self.current.1),
|
|
||||||
(Token::Minus, _) => Node::new(BinaryOperator::Subtract, self.current.1),
|
|
||||||
(Token::Plus, _) => Node::new(BinaryOperator::Add, self.current.1),
|
|
||||||
(Token::PlusEqual, _) => Node::new(BinaryOperator::AddAssign, self.current.1),
|
|
||||||
(Token::Star, _) => Node::new(BinaryOperator::Multiply, self.current.1),
|
|
||||||
(Token::Slash, _) => Node::new(BinaryOperator::Divide, self.current.1),
|
|
||||||
(Token::Percent, _) => Node::new(BinaryOperator::Modulo, self.current.1),
|
|
||||||
_ => {
|
_ => {
|
||||||
self.next_token()?;
|
|
||||||
|
|
||||||
return Err(ParseError::UnexpectedToken {
|
return Err(ParseError::UnexpectedToken {
|
||||||
actual: self.current.0.to_owned(),
|
actual: self.current.0.to_owned(),
|
||||||
position: self.current.1,
|
position: self.current.1,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
let operator_precedence = self.current.0.precedence()
|
||||||
|
- if self.current.0.is_right_associative() {
|
||||||
|
1
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
};
|
||||||
|
|
||||||
self.next_token()?;
|
self.next_token()?;
|
||||||
|
|
||||||
let left_start = left.position.0;
|
let left_start = left.position.0;
|
||||||
let right = self.parse_node(0)?;
|
let right = self.parse_statement(operator_precedence)?;
|
||||||
let right_end = right.position.1;
|
let right_end = right.position.1;
|
||||||
|
|
||||||
Ok(Node::new(
|
Ok(Node::new(
|
||||||
@ -468,33 +448,21 @@ impl<'src> Parser<'src> {
|
|||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn current_precedence(&self) -> u8 {
|
fn parse_postfix(&mut self, left: Node<Statement>) -> Result<Node<Statement>, ParseError> {
|
||||||
match self.current.0 {
|
if let Token::Semicolon = &self.current.0 {
|
||||||
Token::Semicolon => 10,
|
self.next_token()?;
|
||||||
Token::Equal | Token::PlusEqual => 8,
|
|
||||||
Token::DoubleEqual => 7,
|
let left_start = left.position.0;
|
||||||
Token::DoubleAmpersand | Token::DoublePipe => 6,
|
let operator_end = self.current.1 .1;
|
||||||
Token::Greater | Token::GreaterEqual | Token::Less | Token::LessEqual => 5,
|
|
||||||
Token::Dot => 4,
|
Ok(Node::new(
|
||||||
Token::Percent => 3,
|
Statement::Nil(Box::new(left)),
|
||||||
Token::Star => 2,
|
(left_start, operator_end),
|
||||||
Token::Slash => 2,
|
))
|
||||||
Token::Plus => 1,
|
} else {
|
||||||
Token::Minus => 1,
|
Ok(left)
|
||||||
_ => 0,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn peek_token(&mut self) -> Token {
|
|
||||||
self.lexer
|
|
||||||
.peek_token(self.source)
|
|
||||||
.map(|(token, _)| token)
|
|
||||||
.unwrap_or(Token::Eof)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn next_is_postfix(&mut self) -> bool {
|
|
||||||
matches!(self.peek_token(), Token::Semicolon)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Clone)]
|
#[derive(Debug, PartialEq, Clone)]
|
||||||
@ -503,10 +471,7 @@ pub enum ParseError {
|
|||||||
error: ParseBoolError,
|
error: ParseBoolError,
|
||||||
position: Span,
|
position: Span,
|
||||||
},
|
},
|
||||||
LexError {
|
LexError(LexError),
|
||||||
error: LexError,
|
|
||||||
position: Span,
|
|
||||||
},
|
|
||||||
ExpectedIdentifier {
|
ExpectedIdentifier {
|
||||||
actual: TokenOwned,
|
actual: TokenOwned,
|
||||||
position: Span,
|
position: Span,
|
||||||
@ -530,6 +495,12 @@ pub enum ParseError {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<LexError> for ParseError {
|
||||||
|
fn from(v: LexError) -> Self {
|
||||||
|
Self::LexError(v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl ParseError {
|
impl ParseError {
|
||||||
pub fn position(&self) -> Span {
|
pub fn position(&self) -> Span {
|
||||||
match self {
|
match self {
|
||||||
@ -538,7 +509,7 @@ impl ParseError {
|
|||||||
ParseError::ExpectedToken { position, .. } => *position,
|
ParseError::ExpectedToken { position, .. } => *position,
|
||||||
ParseError::FloatError { position, .. } => *position,
|
ParseError::FloatError { position, .. } => *position,
|
||||||
ParseError::IntegerError { position, .. } => *position,
|
ParseError::IntegerError { position, .. } => *position,
|
||||||
ParseError::LexError { position, .. } => *position,
|
ParseError::LexError(error) => error.position(),
|
||||||
ParseError::UnexpectedToken { position, .. } => *position,
|
ParseError::UnexpectedToken { position, .. } => *position,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -547,7 +518,7 @@ impl ParseError {
|
|||||||
impl Error for ParseError {
|
impl Error for ParseError {
|
||||||
fn source(&self) -> Option<&(dyn Error + 'static)> {
|
fn source(&self) -> Option<&(dyn Error + 'static)> {
|
||||||
match self {
|
match self {
|
||||||
Self::LexError { error, .. } => Some(error),
|
Self::LexError(error) => Some(error),
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -565,7 +536,7 @@ impl Display for ParseError {
|
|||||||
} => write!(f, "Expected token {expected}, found {actual}"),
|
} => write!(f, "Expected token {expected}, found {actual}"),
|
||||||
Self::FloatError { error, .. } => write!(f, "{}", error),
|
Self::FloatError { error, .. } => write!(f, "{}", error),
|
||||||
Self::IntegerError { error, .. } => write!(f, "{}", error),
|
Self::IntegerError { error, .. } => write!(f, "{}", error),
|
||||||
Self::LexError { error, .. } => write!(f, "{}", error),
|
Self::LexError(error) => write!(f, "{}", error),
|
||||||
Self::UnexpectedToken { actual, .. } => write!(f, "Unexpected token {actual}"),
|
Self::UnexpectedToken { actual, .. } => write!(f, "Unexpected token {actual}"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -127,6 +127,40 @@ impl<'src> Token<'src> {
|
|||||||
Token::WriteLine => "write_line",
|
Token::WriteLine => "write_line",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn is_eof(&self) -> bool {
|
||||||
|
matches!(self, Token::Eof)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn precedence(&self) -> u8 {
|
||||||
|
match self {
|
||||||
|
Token::Equal | Token::PlusEqual => 8,
|
||||||
|
Token::Semicolon => 7,
|
||||||
|
Token::DoubleAmpersand | Token::DoublePipe => 6,
|
||||||
|
Token::Greater
|
||||||
|
| Token::GreaterEqual
|
||||||
|
| Token::Less
|
||||||
|
| Token::LessEqual
|
||||||
|
| Token::DoubleEqual => 5,
|
||||||
|
Token::Dot => 4,
|
||||||
|
Token::Percent => 3,
|
||||||
|
Token::Star | Token::Slash => 2,
|
||||||
|
Token::Plus | Token::Minus => 1,
|
||||||
|
_ => 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_left_associative(&self) -> bool {
|
||||||
|
!self.is_right_associative()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_right_associative(&self) -> bool {
|
||||||
|
matches!(self, Token::Semicolon)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_postfix(&self) -> bool {
|
||||||
|
matches!(self, Token::Semicolon)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'src> Display for Token<'src> {
|
impl<'src> Display for Token<'src> {
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
//! Dust value representation
|
//! Dust value representation
|
||||||
use std::{
|
use std::{
|
||||||
cmp::Ordering,
|
cmp::Ordering,
|
||||||
collections::{BTreeMap, HashMap},
|
collections::BTreeMap,
|
||||||
error::Error,
|
error::Error,
|
||||||
fmt::{self, Display, Formatter},
|
fmt::{self, Display, Formatter},
|
||||||
ops::Range,
|
ops::Range,
|
||||||
@ -14,7 +14,7 @@ use serde::{
|
|||||||
Deserialize, Deserializer, Serialize,
|
Deserialize, Deserializer, Serialize,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{identifier::Identifier, AbstractSyntaxTree, Type, Vm, VmError};
|
use crate::{identifier::Identifier, AbstractSyntaxTree, Context, Type, Vm, VmError};
|
||||||
|
|
||||||
/// Dust value representation
|
/// Dust value representation
|
||||||
///
|
///
|
||||||
@ -89,8 +89,8 @@ impl Value {
|
|||||||
Value(Arc::new(ValueInner::String(to_string.to_string())))
|
Value(Arc::new(ValueInner::String(to_string.to_string())))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn r#type(&self, variables: &HashMap<Identifier, Value>) -> Type {
|
pub fn r#type(&self, context: &Context) -> Type {
|
||||||
self.0.r#type(variables)
|
self.0.r#type(context)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn as_boolean(&self) -> Option<bool> {
|
pub fn as_boolean(&self) -> Option<bool> {
|
||||||
@ -626,18 +626,18 @@ pub enum ValueInner {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl ValueInner {
|
impl ValueInner {
|
||||||
pub fn r#type(&self, variables: &HashMap<Identifier, Value>) -> Type {
|
pub fn r#type(&self, context: &Context) -> Type {
|
||||||
match self {
|
match self {
|
||||||
ValueInner::Boolean(_) => Type::Boolean,
|
ValueInner::Boolean(_) => Type::Boolean,
|
||||||
ValueInner::Float(_) => Type::Float,
|
ValueInner::Float(_) => Type::Float,
|
||||||
ValueInner::Function(function) => Type::Function {
|
ValueInner::Function(function) => Type::Function {
|
||||||
type_parameters: function.type_parameters.clone(),
|
type_parameters: function.type_parameters.clone(),
|
||||||
value_parameters: function.value_parameters.clone(),
|
value_parameters: function.value_parameters.clone(),
|
||||||
return_type: function.return_type(variables).map(Box::new),
|
return_type: function.return_type(context).map(Box::new),
|
||||||
},
|
},
|
||||||
ValueInner::Integer(_) => Type::Integer,
|
ValueInner::Integer(_) => Type::Integer,
|
||||||
ValueInner::List(values) => {
|
ValueInner::List(values) => {
|
||||||
let item_type = values.first().unwrap().r#type(variables);
|
let item_type = values.first().unwrap().r#type(context);
|
||||||
|
|
||||||
Type::List {
|
Type::List {
|
||||||
length: values.len(),
|
length: values.len(),
|
||||||
@ -648,7 +648,7 @@ impl ValueInner {
|
|||||||
let mut type_map = BTreeMap::new();
|
let mut type_map = BTreeMap::new();
|
||||||
|
|
||||||
for (identifier, value) in value_map {
|
for (identifier, value) in value_map {
|
||||||
let r#type = value.r#type(variables);
|
let r#type = value.r#type(context);
|
||||||
|
|
||||||
type_map.insert(identifier.clone(), r#type);
|
type_map.insert(identifier.clone(), r#type);
|
||||||
}
|
}
|
||||||
@ -715,24 +715,24 @@ impl Function {
|
|||||||
self,
|
self,
|
||||||
_type_arguments: Option<Vec<Type>>,
|
_type_arguments: Option<Vec<Type>>,
|
||||||
value_arguments: Option<Vec<Value>>,
|
value_arguments: Option<Vec<Value>>,
|
||||||
variables: &HashMap<Identifier, Value>,
|
context: &mut Context,
|
||||||
) -> Result<Option<Value>, VmError> {
|
) -> Result<Option<Value>, VmError> {
|
||||||
let mut new_variables = variables.clone();
|
let mut new_context = context.clone();
|
||||||
|
|
||||||
if let (Some(value_parameters), Some(value_arguments)) =
|
if let (Some(value_parameters), Some(value_arguments)) =
|
||||||
(self.value_parameters, value_arguments)
|
(self.value_parameters, value_arguments)
|
||||||
{
|
{
|
||||||
for ((identifier, _), value) in value_parameters.into_iter().zip(value_arguments) {
|
for ((identifier, _), value) in value_parameters.into_iter().zip(value_arguments) {
|
||||||
new_variables.insert(identifier, value);
|
new_context.set_value(identifier, value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut vm = Vm::new(self.body);
|
let mut vm = Vm::new(self.body);
|
||||||
|
|
||||||
vm.run(&mut new_variables)
|
vm.run(&mut new_context)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn return_type(&self, variables: &HashMap<Identifier, Value>) -> Option<Type> {
|
pub fn return_type(&self, variables: &Context) -> Option<Type> {
|
||||||
self.body
|
self.body
|
||||||
.nodes
|
.nodes
|
||||||
.iter()
|
.iter()
|
||||||
|
@ -1,27 +1,24 @@
|
|||||||
//! Virtual machine for running the abstract syntax tree.
|
//! Virtual machine for running the abstract syntax tree.
|
||||||
use std::{
|
use std::{
|
||||||
collections::{BTreeMap, HashMap},
|
collections::BTreeMap,
|
||||||
error::Error,
|
error::Error,
|
||||||
fmt::{self, Display, Formatter},
|
fmt::{self, Display, Formatter},
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
abstract_tree::BinaryOperator, parse, AbstractSyntaxTree, Analyzer, AnalyzerError,
|
abstract_tree::BinaryOperator, parse, AbstractSyntaxTree, Analyzer, AnalyzerError,
|
||||||
BuiltInFunctionError, Identifier, Node, ParseError, Span, Statement, Value, ValueError,
|
BuiltInFunctionError, Context, Node, ParseError, Span, Statement, Value, ValueError,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn run(
|
pub fn run(input: &str, context: &mut Context) -> Result<Option<Value>, VmError> {
|
||||||
input: &str,
|
|
||||||
variables: &mut HashMap<Identifier, Value>,
|
|
||||||
) -> Result<Option<Value>, VmError> {
|
|
||||||
let abstract_syntax_tree = parse(input)?;
|
let abstract_syntax_tree = parse(input)?;
|
||||||
let analyzer = Analyzer::new(&abstract_syntax_tree, variables);
|
let mut analyzer = Analyzer::new(&abstract_syntax_tree, context);
|
||||||
|
|
||||||
analyzer.analyze()?;
|
analyzer.analyze()?;
|
||||||
|
|
||||||
let mut vm = Vm::new(abstract_syntax_tree);
|
let mut vm = Vm::new(abstract_syntax_tree);
|
||||||
|
|
||||||
vm.run(variables)
|
vm.run(context)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Vm {
|
pub struct Vm {
|
||||||
@ -33,14 +30,11 @@ impl Vm {
|
|||||||
Self { abstract_tree }
|
Self { abstract_tree }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn run(
|
pub fn run(&mut self, context: &mut Context) -> Result<Option<Value>, VmError> {
|
||||||
&mut self,
|
|
||||||
variables: &mut HashMap<Identifier, Value>,
|
|
||||||
) -> Result<Option<Value>, VmError> {
|
|
||||||
let mut previous_value = None;
|
let mut previous_value = None;
|
||||||
|
|
||||||
while let Some(node) = self.abstract_tree.nodes.pop_front() {
|
while let Some(node) = self.abstract_tree.nodes.pop_front() {
|
||||||
previous_value = self.run_node(node, variables)?;
|
previous_value = self.run_node(node, context)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(previous_value)
|
Ok(previous_value)
|
||||||
@ -49,7 +43,7 @@ impl Vm {
|
|||||||
fn run_node(
|
fn run_node(
|
||||||
&self,
|
&self,
|
||||||
node: Node<Statement>,
|
node: Node<Statement>,
|
||||||
variables: &mut HashMap<Identifier, Value>,
|
context: &mut Context,
|
||||||
) -> Result<Option<Value>, VmError> {
|
) -> Result<Option<Value>, VmError> {
|
||||||
match node.inner {
|
match node.inner {
|
||||||
Statement::BinaryOperation {
|
Statement::BinaryOperation {
|
||||||
@ -68,7 +62,7 @@ impl Vm {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
let value = if let Some(value) = self.run_node(*right, variables)? {
|
let value = if let Some(value) = self.run_node(*right, context)? {
|
||||||
value
|
value
|
||||||
} else {
|
} else {
|
||||||
return Err(VmError::ExpectedValue {
|
return Err(VmError::ExpectedValue {
|
||||||
@ -76,9 +70,9 @@ impl Vm {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
variables.insert(identifier, value.clone());
|
context.set_value(identifier, value);
|
||||||
|
|
||||||
return Ok(Some(value));
|
return Ok(None);
|
||||||
}
|
}
|
||||||
|
|
||||||
if let BinaryOperator::AddAssign = operator.inner {
|
if let BinaryOperator::AddAssign = operator.inner {
|
||||||
@ -89,25 +83,21 @@ impl Vm {
|
|||||||
position: left.position,
|
position: left.position,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
let right_value = if let Some(value) = self.run_node(*right, context)? {
|
||||||
let right_value = if let Some(value) = self.run_node(*right, variables)? {
|
|
||||||
value
|
value
|
||||||
} else {
|
} else {
|
||||||
return Err(VmError::ExpectedValue {
|
return Err(VmError::ExpectedValue {
|
||||||
position: right_position,
|
position: right_position,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
let left_value = context.get_value(&identifier).ok_or_else(|| {
|
||||||
let left_value =
|
VmError::UndefinedVariable {
|
||||||
variables
|
identifier: Node::new(
|
||||||
.get(&identifier)
|
Statement::Identifier(identifier.clone()),
|
||||||
.ok_or_else(|| VmError::UndefinedVariable {
|
left.position,
|
||||||
identifier: Node::new(
|
),
|
||||||
Statement::Identifier(identifier.clone()),
|
}
|
||||||
left.position,
|
})?;
|
||||||
),
|
|
||||||
})?;
|
|
||||||
|
|
||||||
let new_value = left_value.add(&right_value).map_err(|value_error| {
|
let new_value = left_value.add(&right_value).map_err(|value_error| {
|
||||||
VmError::ValueError {
|
VmError::ValueError {
|
||||||
error: value_error,
|
error: value_error,
|
||||||
@ -115,21 +105,20 @@ impl Vm {
|
|||||||
}
|
}
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
variables.insert(identifier, new_value.clone());
|
context.set_value(identifier, new_value);
|
||||||
|
|
||||||
return Ok(Some(new_value));
|
return Ok(None);
|
||||||
}
|
}
|
||||||
|
|
||||||
let left_position = left.position;
|
let left_position = left.position;
|
||||||
let left_value = if let Some(value) = self.run_node(*left, variables)? {
|
let left_value = if let Some(value) = self.run_node(*left, context)? {
|
||||||
value
|
value
|
||||||
} else {
|
} else {
|
||||||
return Err(VmError::ExpectedValue {
|
return Err(VmError::ExpectedValue {
|
||||||
position: left_position,
|
position: left_position,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
let right_value = if let Some(value) = self.run_node(*right, context)? {
|
||||||
let right_value = if let Some(value) = self.run_node(*right, variables)? {
|
|
||||||
value
|
value
|
||||||
} else {
|
} else {
|
||||||
return Err(VmError::ExpectedValue {
|
return Err(VmError::ExpectedValue {
|
||||||
@ -164,7 +153,7 @@ impl Vm {
|
|||||||
let mut previous_value = None;
|
let mut previous_value = None;
|
||||||
|
|
||||||
for statement in statements {
|
for statement in statements {
|
||||||
previous_value = self.run_node(statement, variables)?;
|
previous_value = self.run_node(statement, context)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(previous_value)
|
Ok(previous_value)
|
||||||
@ -179,7 +168,7 @@ impl Vm {
|
|||||||
|
|
||||||
for node in nodes {
|
for node in nodes {
|
||||||
let position = node.position;
|
let position = node.position;
|
||||||
let value = if let Some(value) = self.run_node(node, variables)? {
|
let value = if let Some(value) = self.run_node(node, context)? {
|
||||||
value
|
value
|
||||||
} else {
|
} else {
|
||||||
return Err(VmError::ExpectedValue { position });
|
return Err(VmError::ExpectedValue { position });
|
||||||
@ -209,14 +198,13 @@ impl Vm {
|
|||||||
value_arguments: value_parameter_nodes,
|
value_arguments: value_parameter_nodes,
|
||||||
} => {
|
} => {
|
||||||
let function_position = function_node.position;
|
let function_position = function_node.position;
|
||||||
let function_value =
|
let function_value = if let Some(value) = self.run_node(*function_node, context)? {
|
||||||
if let Some(value) = self.run_node(*function_node, variables)? {
|
value
|
||||||
value
|
} else {
|
||||||
} else {
|
return Err(VmError::ExpectedValue {
|
||||||
return Err(VmError::ExpectedValue {
|
position: function_position,
|
||||||
position: function_position,
|
});
|
||||||
});
|
};
|
||||||
};
|
|
||||||
let function = if let Some(function) = function_value.as_function() {
|
let function = if let Some(function) = function_value.as_function() {
|
||||||
function
|
function
|
||||||
} else {
|
} else {
|
||||||
@ -231,7 +219,7 @@ impl Vm {
|
|||||||
|
|
||||||
for node in value_nodes {
|
for node in value_nodes {
|
||||||
let position = node.position;
|
let position = node.position;
|
||||||
let value = if let Some(value) = self.run_node(node, variables)? {
|
let value = if let Some(value) = self.run_node(node, context)? {
|
||||||
value
|
value
|
||||||
} else {
|
} else {
|
||||||
return Err(VmError::ExpectedValue { position });
|
return Err(VmError::ExpectedValue { position });
|
||||||
@ -245,10 +233,10 @@ impl Vm {
|
|||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(function.clone().call(None, value_parameters, variables)?)
|
Ok(function.clone().call(None, value_parameters, context)?)
|
||||||
}
|
}
|
||||||
Statement::Identifier(identifier) => {
|
Statement::Identifier(identifier) => {
|
||||||
if let Some(value) = variables.get(&identifier) {
|
if let Some(value) = context.get_value(&identifier) {
|
||||||
Ok(Some(value.clone()))
|
Ok(Some(value.clone()))
|
||||||
} else {
|
} else {
|
||||||
Err(VmError::UndefinedVariable {
|
Err(VmError::UndefinedVariable {
|
||||||
@ -261,7 +249,7 @@ impl Vm {
|
|||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|node| {
|
.map(|node| {
|
||||||
let span = node.position;
|
let span = node.position;
|
||||||
if let Some(value) = self.run_node(node, variables)? {
|
if let Some(value) = self.run_node(node, context)? {
|
||||||
Ok(value)
|
Ok(value)
|
||||||
} else {
|
} else {
|
||||||
Err(VmError::ExpectedValue { position: span })
|
Err(VmError::ExpectedValue { position: span })
|
||||||
@ -283,7 +271,7 @@ impl Vm {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
let position = value_node.position;
|
let position = value_node.position;
|
||||||
let value = if let Some(value) = self.run_node(value_node, variables)? {
|
let value = if let Some(value) = self.run_node(value_node, context)? {
|
||||||
value
|
value
|
||||||
} else {
|
} else {
|
||||||
return Err(VmError::ExpectedValue { position });
|
return Err(VmError::ExpectedValue { position });
|
||||||
@ -295,13 +283,13 @@ impl Vm {
|
|||||||
Ok(Some(Value::map(values)))
|
Ok(Some(Value::map(values)))
|
||||||
}
|
}
|
||||||
Statement::Nil(node) => {
|
Statement::Nil(node) => {
|
||||||
let _return = self.run_node(*node, variables)?;
|
let _return = self.run_node(*node, context)?;
|
||||||
|
|
||||||
Ok(None)
|
Ok(None)
|
||||||
}
|
}
|
||||||
Statement::PropertyAccess(left, right) => {
|
Statement::PropertyAccess(left, right) => {
|
||||||
let left_span = left.position;
|
let left_span = left.position;
|
||||||
let left_value = if let Some(value) = self.run_node(*left, variables)? {
|
let left_value = if let Some(value) = self.run_node(*left, context)? {
|
||||||
value
|
value
|
||||||
} else {
|
} else {
|
||||||
return Err(VmError::ExpectedValue {
|
return Err(VmError::ExpectedValue {
|
||||||
@ -336,7 +324,7 @@ impl Vm {
|
|||||||
if let Some(value_nodes) = value_argument_nodes {
|
if let Some(value_nodes) = value_argument_nodes {
|
||||||
for node in value_nodes {
|
for node in value_nodes {
|
||||||
let position = node.position;
|
let position = node.position;
|
||||||
let value = if let Some(value) = self.run_node(node, variables)? {
|
let value = if let Some(value) = self.run_node(node, context)? {
|
||||||
value
|
value
|
||||||
} else {
|
} else {
|
||||||
return Err(VmError::ExpectedValue { position });
|
return Err(VmError::ExpectedValue { position });
|
||||||
@ -495,7 +483,7 @@ mod tests {
|
|||||||
fn add_assign() {
|
fn add_assign() {
|
||||||
let input = "x = 1; x += 1; x";
|
let input = "x = 1; x += 1; x";
|
||||||
|
|
||||||
assert_eq!(run(input, &mut HashMap::new()), Ok(Some(Value::integer(2))));
|
assert_eq!(run(input, &mut Context::new()), Ok(Some(Value::integer(2))));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -503,7 +491,7 @@ mod tests {
|
|||||||
let input = "true || false";
|
let input = "true || false";
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
run(input, &mut HashMap::new()),
|
run(input, &mut Context::new()),
|
||||||
Ok(Some(Value::boolean(true)))
|
Ok(Some(Value::boolean(true)))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -513,7 +501,7 @@ mod tests {
|
|||||||
let input = "{ y = 'foo', } == { y = 'foo', }";
|
let input = "{ y = 'foo', } == { y = 'foo', }";
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
run(input, &mut HashMap::new()),
|
run(input, &mut Context::new()),
|
||||||
Ok(Some(Value::boolean(true)))
|
Ok(Some(Value::boolean(true)))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -523,7 +511,7 @@ mod tests {
|
|||||||
let input = "42 == 42";
|
let input = "42 == 42";
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
run(input, &mut HashMap::new()),
|
run(input, &mut Context::new()),
|
||||||
Ok(Some(Value::boolean(true)))
|
Ok(Some(Value::boolean(true)))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -532,7 +520,7 @@ mod tests {
|
|||||||
fn modulo() {
|
fn modulo() {
|
||||||
let input = "42 % 2";
|
let input = "42 % 2";
|
||||||
|
|
||||||
assert_eq!(run(input, &mut HashMap::new()), Ok(Some(Value::integer(0))));
|
assert_eq!(run(input, &mut Context::new()), Ok(Some(Value::integer(0))));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -540,7 +528,7 @@ mod tests {
|
|||||||
let input = "42 / 2";
|
let input = "42 / 2";
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
run(input, &mut HashMap::new()),
|
run(input, &mut Context::new()),
|
||||||
Ok(Some(Value::integer(21)))
|
Ok(Some(Value::integer(21)))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -550,7 +538,7 @@ mod tests {
|
|||||||
let input = "2 < 3";
|
let input = "2 < 3";
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
run(input, &mut HashMap::new()),
|
run(input, &mut Context::new()),
|
||||||
Ok(Some(Value::boolean(true)))
|
Ok(Some(Value::boolean(true)))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -560,7 +548,7 @@ mod tests {
|
|||||||
let input = "42 <= 42";
|
let input = "42 <= 42";
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
run(input, &mut HashMap::new()),
|
run(input, &mut Context::new()),
|
||||||
Ok(Some(Value::boolean(true)))
|
Ok(Some(Value::boolean(true)))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -570,7 +558,7 @@ mod tests {
|
|||||||
let input = "2 > 3";
|
let input = "2 > 3";
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
run(input, &mut HashMap::new()),
|
run(input, &mut Context::new()),
|
||||||
Ok(Some(Value::boolean(false)))
|
Ok(Some(Value::boolean(false)))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -580,7 +568,7 @@ mod tests {
|
|||||||
let input = "42 >= 42";
|
let input = "42 >= 42";
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
run(input, &mut HashMap::new()),
|
run(input, &mut Context::new()),
|
||||||
Ok(Some(Value::boolean(true)))
|
Ok(Some(Value::boolean(true)))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -590,7 +578,7 @@ mod tests {
|
|||||||
let input = "9223372036854775807 + 1";
|
let input = "9223372036854775807 + 1";
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
run(input, &mut HashMap::new()),
|
run(input, &mut Context::new()),
|
||||||
Ok(Some(Value::integer(i64::MAX)))
|
Ok(Some(Value::integer(i64::MAX)))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -600,7 +588,7 @@ mod tests {
|
|||||||
let input = "-9223372036854775808 - 1";
|
let input = "-9223372036854775808 - 1";
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
run(input, &mut HashMap::new()),
|
run(input, &mut Context::new()),
|
||||||
Ok(Some(Value::integer(i64::MIN)))
|
Ok(Some(Value::integer(i64::MIN)))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -609,7 +597,7 @@ mod tests {
|
|||||||
fn multiply() {
|
fn multiply() {
|
||||||
let input = "2 * 3";
|
let input = "2 * 3";
|
||||||
|
|
||||||
assert_eq!(run(input, &mut HashMap::new()), Ok(Some(Value::integer(6))));
|
assert_eq!(run(input, &mut Context::new()), Ok(Some(Value::integer(6))));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -617,7 +605,7 @@ mod tests {
|
|||||||
let input = "true";
|
let input = "true";
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
run(input, &mut HashMap::new()),
|
run(input, &mut Context::new()),
|
||||||
Ok(Some(Value::boolean(true)))
|
Ok(Some(Value::boolean(true)))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -627,7 +615,7 @@ mod tests {
|
|||||||
let input = "42.is_even()";
|
let input = "42.is_even()";
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
run(input, &mut HashMap::new()),
|
run(input, &mut Context::new()),
|
||||||
Ok(Some(Value::boolean(true)))
|
Ok(Some(Value::boolean(true)))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -637,7 +625,7 @@ mod tests {
|
|||||||
let input = "42.is_odd()";
|
let input = "42.is_odd()";
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
run(input, &mut HashMap::new()),
|
run(input, &mut Context::new()),
|
||||||
Ok(Some(Value::boolean(false)))
|
Ok(Some(Value::boolean(false)))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -646,27 +634,27 @@ mod tests {
|
|||||||
fn length() {
|
fn length() {
|
||||||
let input = "[1, 2, 3].length()";
|
let input = "[1, 2, 3].length()";
|
||||||
|
|
||||||
assert_eq!(run(input, &mut HashMap::new()), Ok(Some(Value::integer(3))));
|
assert_eq!(run(input, &mut Context::new()), Ok(Some(Value::integer(3))));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn list_access() {
|
fn list_access() {
|
||||||
let input = "[1, 2, 3].1";
|
let input = "[1, 2, 3].1";
|
||||||
|
|
||||||
assert_eq!(run(input, &mut HashMap::new()), Ok(Some(Value::integer(2))));
|
assert_eq!(run(input, &mut Context::new()), Ok(Some(Value::integer(2))));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn add() {
|
fn add() {
|
||||||
let input = "1 + 2";
|
let input = "1 + 2";
|
||||||
|
|
||||||
assert_eq!(run(input, &mut HashMap::new()), Ok(Some(Value::integer(3))));
|
assert_eq!(run(input, &mut Context::new()), Ok(Some(Value::integer(3))));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn add_multiple() {
|
fn add_multiple() {
|
||||||
let input = "1 + 2 + 3";
|
let input = "1 + 2 + 3";
|
||||||
|
|
||||||
assert_eq!(run(input, &mut HashMap::new()), Ok(Some(Value::integer(6))));
|
assert_eq!(run(input, &mut Context::new()), Ok(Some(Value::integer(6))));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
use std::{collections::HashMap, fs::read_to_string};
|
use std::fs::read_to_string;
|
||||||
|
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use dust_lang::{run, DustError, Identifier, Value};
|
use dust_lang::{run, Context, DustError};
|
||||||
|
|
||||||
#[derive(Parser)]
|
#[derive(Parser)]
|
||||||
struct Cli {
|
struct Cli {
|
||||||
@ -13,20 +13,20 @@ struct Cli {
|
|||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let args = Cli::parse();
|
let args = Cli::parse();
|
||||||
let mut variables = HashMap::new();
|
let mut context = Context::new();
|
||||||
|
|
||||||
if let Some(command) = &args.command {
|
if let Some(command) = &args.command {
|
||||||
run_and_display_errors(command, &mut variables);
|
run_and_display_errors(command, &mut context);
|
||||||
} else if let Some(path) = &args.path {
|
} else if let Some(path) = &args.path {
|
||||||
let source = read_to_string(path).expect("Failed to read file");
|
let source = read_to_string(path).expect("Failed to read file");
|
||||||
|
|
||||||
run_and_display_errors(&source, &mut variables)
|
run_and_display_errors(&source, &mut context)
|
||||||
} else {
|
} else {
|
||||||
panic!("No command or path provided");
|
panic!("No command or path provided");
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run_and_display_errors(source: &str, variables: &mut HashMap<Identifier, Value>) {
|
fn run_and_display_errors(source: &str, variables: &mut Context) {
|
||||||
match run(source, variables) {
|
match run(source, variables) {
|
||||||
Ok(return_value) => {
|
Ok(return_value) => {
|
||||||
if let Some(value) = return_value {
|
if let Some(value) = return_value {
|
||||||
|
Loading…
Reference in New Issue
Block a user