1
0

Fix dot notations precedence; Add some miscellaneous expansions

This commit is contained in:
Jeff 2024-08-12 15:02:04 -04:00
parent a61c1756f2
commit 0fb0b63a97
8 changed files with 384 additions and 152 deletions

View File

@ -62,10 +62,6 @@ pub enum Statement {
value_arguments: Option<Vec<Node<Statement>>>,
},
// Property access expression
// TODO: This should be a binary operation
PropertyAccess(Box<Node<Statement>>, Box<Node<Statement>>),
// Loops
While {
condition: Box<Node<Statement>>,
@ -113,7 +109,11 @@ impl Statement {
pub fn expected_type(&self, context: &Context) -> Option<Type> {
match self {
Statement::Block(nodes) => nodes.last().unwrap().inner.expected_type(context),
Statement::BinaryOperation { left, operator, .. } => match operator.inner {
Statement::BinaryOperation {
left,
operator,
right,
} => match operator.inner {
BinaryOperator::Add
| BinaryOperator::Divide
| BinaryOperator::Modulo
@ -129,6 +129,21 @@ impl Statement {
| BinaryOperator::Or => Some(Type::Boolean),
BinaryOperator::Assign | BinaryOperator::AddAssign => None,
BinaryOperator::FieldAccess => {
let left_type = left.inner.expected_type(context)?;
if let Type::Map(properties) = left_type {
let key = match &right.inner {
Statement::Identifier(identifier) => identifier,
_ => return None,
};
properties.get(key).cloned()
} else {
None
}
}
},
Statement::BuiltInFunctionCall { function, .. } => function.expected_return_type(),
Statement::Constant(value) => Some(value.r#type(context)),
@ -157,7 +172,6 @@ impl Statement {
Some(Type::Map(types))
}
Statement::Nil(_) => None,
Statement::PropertyAccess(_, _) => None,
Statement::UnaryOperation { operator, operand } => match operator.inner {
UnaryOperator::Negate => Some(operand.inner.expected_type(context)?),
UnaryOperator::Not => Some(Type::Boolean),
@ -202,7 +216,11 @@ impl Display for Statement {
operator,
right,
} => {
if let BinaryOperator::FieldAccess = operator.inner {
write!(f, "{left}{operator}{right}")
} else {
write!(f, "{left} {operator} {right}")
}
}
Statement::BuiltInFunctionCall {
function,
@ -340,7 +358,6 @@ impl Display for Statement {
write!(f, "}}")
}
Statement::Nil(node) => write!(f, "{node};"),
Statement::PropertyAccess(left, right) => write!(f, "{left}.{right}"),
Statement::UnaryOperation { operator, operand } => {
write!(f, "{operator}{operand}")
}
@ -353,6 +370,8 @@ impl Display for Statement {
#[derive(Debug, Clone, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
pub enum BinaryOperator {
FieldAccess,
// Math
Add,
Divide,
@ -385,6 +404,7 @@ impl Display for BinaryOperator {
BinaryOperator::And => write!(f, "&&"),
BinaryOperator::Divide => write!(f, "/"),
BinaryOperator::Equal => write!(f, "=="),
BinaryOperator::FieldAccess => write!(f, "."),
BinaryOperator::Greater => write!(f, ">"),
BinaryOperator::GreaterOrEqual => write!(f, ">="),
BinaryOperator::Less => write!(f, "<"),

View File

@ -96,6 +96,44 @@ impl<'a> Analyzer<'a> {
}
}
if let BinaryOperator::FieldAccess = operator.inner {
self.analyze_statement(left)?;
if let Statement::Identifier(_) = right.inner {
// Do not expect a value for property accessors
} else {
self.analyze_statement(right)?;
}
if let Some(Type::List { .. }) = left.inner.expected_type(self.context) {
let right_type = right.inner.expected_type(self.context);
if let Some(Type::Integer) = right_type {
// Allow indexing lists with integers
} else if let Some(Type::Range) = right_type {
// Allow indexing lists with ranges
} else {
return Err(AnalyzerError::ExpectedIntegerOrRange {
actual: right.as_ref().clone(),
});
}
}
if let Some(Type::Map { .. }) = left.inner.expected_type(self.context) {
if let Some(Type::String) = right.inner.expected_type(self.context) {
// Allow indexing maps with strings
} else if let Statement::Identifier(_) = right.inner {
// Allow indexing maps with identifiers
} else {
return Err(AnalyzerError::ExpectedIdentifierOrString {
actual: right.as_ref().clone(),
});
}
}
return Ok(());
}
self.analyze_statement(left)?;
self.analyze_statement(right)?;
@ -324,41 +362,6 @@ impl<'a> Analyzer<'a> {
Statement::Nil(node) => {
self.analyze_statement(node)?;
}
Statement::PropertyAccess(left, right) => {
self.analyze_statement(left)?;
if let Statement::Identifier(_) = right.inner {
// Do not expect a value for property accessors
} else {
self.analyze_statement(right)?;
}
if let Some(Type::List { .. }) = left.inner.expected_type(self.context) {
let right_type = right.inner.expected_type(self.context);
if let Some(Type::Integer) = right_type {
// Allow indexing lists with integers
} else if let Some(Type::Range) = right_type {
// Allow indexing lists with ranges
} else {
return Err(AnalyzerError::ExpectedIntegerOrRange {
actual: right.as_ref().clone(),
});
}
}
if let Some(Type::Map { .. }) = left.inner.expected_type(self.context) {
if let Some(Type::String) = right.inner.expected_type(self.context) {
// Allow indexing maps with strings
} else if let Statement::Identifier(_) = right.inner {
// Allow indexing maps with identifiers
} else {
return Err(AnalyzerError::ExpectedIdentifierOrString {
actual: right.as_ref().clone(),
});
}
}
}
Statement::UnaryOperation { operator, operand } => {
self.analyze_statement(operand)?;

View File

@ -264,6 +264,11 @@ impl Lexer {
(Token::Bang, (self.position - 1, self.position))
}
':' => {
self.position += 1;
(Token::Colon, (self.position - 1, self.position))
}
_ => {
self.position += 1;
@ -408,13 +413,17 @@ impl Lexer {
let token = match string {
"Infinity" => Token::Float("Infinity"),
"NaN" => Token::Float("NaN"),
"bool" => Token::Bool,
"else" => Token::Else,
"false" => Token::Boolean("false"),
"float" => Token::FloatKeyword,
"if" => Token::If,
"int" => Token::Int,
"is_even" => Token::IsEven,
"is_odd" => Token::IsOdd,
"length" => Token::Length,
"read_line" => Token::ReadLine,
"struct" => Token::Struct,
"to_string" => Token::ToString,
"true" => Token::Boolean("true"),
"while" => Token::While,

View File

@ -604,6 +604,8 @@ impl<'src> Parser<'src> {
};
if let Token::Dot = &self.current.0 {
let operator_position = self.current.1;
self.next_token()?;
let right = self.parse_statement(operator_precedence)?;
@ -658,7 +660,11 @@ impl<'src> Parser<'src> {
}
return Ok(Node::new(
Statement::PropertyAccess(Box::new(left), Box::new(right)),
Statement::BinaryOperation {
left: Box::new(left),
operator: Node::new(BinaryOperator::FieldAccess, operator_position),
right: Box::new(right),
},
(left_start, right_end),
));
}
@ -852,47 +858,48 @@ mod tests {
assert_eq!(
parse(input),
Ok(AbstractSyntaxTree {
nodes: [Node {
inner: Statement::PropertyAccess(
Box::new(Node {
inner: Statement::Map(vec![(
Node {
inner: Statement::Identifier(Identifier::new("x")),
position: (2, 3)
},
Node {
inner: Statement::Map(vec![(
Node {
inner: Statement::Identifier(Identifier::new("y")),
position: (8, 9)
},
Node {
inner: Statement::Constant(Value::integer(42)),
position: (12, 14)
}
)]),
position: (6, 16)
}
)]),
position: (0, 18)
}),
Box::new(Node {
inner: Statement::PropertyAccess(
Box::new(Node {
inner: Statement::Identifier(Identifier::new("x")),
position: (19, 20)
}),
Box::new(Node {
inner: Statement::Identifier(Identifier::new("y")),
position: (21, 22)
})
nodes: [Node::new(
Statement::BinaryOperation {
left: Box::new(Node::new(
Statement::BinaryOperation {
left: Box::new(Node::new(
Statement::Map(vec![(
Node::new(
Statement::Identifier(Identifier::new("x")),
(2, 3)
),
position: (19, 22)
})
Node::new(
Statement::Map(vec![(
Node::new(
Statement::Identifier(Identifier::new("y")),
(8, 9)
),
position: (0, 22)
}]
Node::new(
Statement::Constant(Value::integer(42)),
(12, 14)
)
)]),
(6, 16)
)
)]),
(0, 18)
)),
operator: Node::new(BinaryOperator::FieldAccess, (18, 19)),
right: Box::new(Node::new(
Statement::Identifier(Identifier::new("x")),
(19, 20)
))
},
(0, 20)
)),
operator: Node::new(BinaryOperator::FieldAccess, (20, 21)),
right: Box::new(Node::new(
Statement::Identifier(Identifier::new("y")),
(21, 22)
))
},
(0, 22)
)]
.into()
})
)
@ -1444,7 +1451,7 @@ mod tests {
}
#[test]
fn map_with_two_properties() {
fn map_with_two_fields() {
let input = "{ x = 42, y = 'foobar' }";
assert_eq!(
@ -1469,8 +1476,8 @@ mod tests {
}
#[test]
fn map_with_one_property() {
let input = "{ x = 42, }";
fn map_with_one_field() {
let input = "{ x = 42 }";
assert_eq!(
parse(input),
@ -1480,7 +1487,7 @@ mod tests {
Node::new(Statement::Identifier(Identifier::new("x")), (2, 3)),
Node::new(Statement::Constant(Value::integer(42)), (6, 8))
)]),
(0, 11)
(0, 10)
)]
.into()
})
@ -1732,8 +1739,8 @@ mod tests {
parse(input),
Ok(AbstractSyntaxTree {
nodes: [Node::new(
Statement::PropertyAccess(
Box::new(Node::new(
Statement::BinaryOperation {
left: Box::new(Node::new(
Statement::List(vec![
Node::new(Statement::Constant(Value::integer(1)), (1, 2)),
Node::new(Statement::Constant(Value::integer(2)), (4, 5)),
@ -1741,8 +1748,12 @@ mod tests {
]),
(0, 9)
)),
Box::new(Node::new(Statement::Constant(Value::integer(0)), (10, 11))),
),
operator: Node::new(BinaryOperator::FieldAccess, (9, 10)),
right: Box::new(Node::new(
Statement::Constant(Value::integer(0)),
(10, 11)
)),
},
(0, 11),
)]
.into()
@ -1758,16 +1769,17 @@ mod tests {
parse(input),
Ok(AbstractSyntaxTree {
nodes: [Node::new(
Statement::PropertyAccess(
Box::new(Node::new(
Statement::BinaryOperation {
left: Box::new(Node::new(
Statement::Identifier(Identifier::new("a")),
(0, 1)
)),
Box::new(Node::new(
operator: Node::new(BinaryOperator::FieldAccess, (1, 2)),
right: Box::new(Node::new(
Statement::Identifier(Identifier::new("b")),
(2, 3)
)),
),
},
(0, 3),
)]
.into()

View File

@ -17,18 +17,24 @@ pub enum Token<'src> {
String(&'src str),
// Keywords
Bool,
Else,
FloatKeyword,
If,
Int,
IsEven,
IsOdd,
Length,
ReadLine,
Str,
Struct,
ToString,
While,
WriteLine,
// Symbols
Bang,
Colon,
Comma,
Dot,
DoubleAmpersand,
@ -59,7 +65,9 @@ impl<'src> Token<'src> {
pub fn to_owned(&self) -> TokenOwned {
match self {
Token::Bang => TokenOwned::Bang,
Token::Bool => TokenOwned::Bool,
Token::Boolean(boolean) => TokenOwned::Boolean(boolean.to_string()),
Token::Colon => TokenOwned::Colon,
Token::Comma => TokenOwned::Comma,
Token::Dot => TokenOwned::Dot,
Token::DoubleAmpersand => TokenOwned::DoubleAmpersand,
@ -70,10 +78,12 @@ impl<'src> Token<'src> {
Token::Eof => TokenOwned::Eof,
Token::Equal => TokenOwned::Equal,
Token::Float(float) => TokenOwned::Float(float.to_string()),
Token::FloatKeyword => TokenOwned::FloatKeyword,
Token::Greater => TokenOwned::Greater,
Token::GreaterEqual => TokenOwned::GreaterOrEqual,
Token::Identifier(text) => TokenOwned::Identifier(text.to_string()),
Token::If => TokenOwned::If,
Token::Int => TokenOwned::Int,
Token::Integer(integer) => TokenOwned::Integer(integer.to_string()),
Token::IsEven => TokenOwned::IsEven,
Token::IsOdd => TokenOwned::IsOdd,
@ -95,6 +105,8 @@ impl<'src> Token<'src> {
Token::Star => TokenOwned::Star,
Token::Slash => TokenOwned::Slash,
Token::String(text) => TokenOwned::String(text.to_string()),
Token::Str => TokenOwned::Str,
Token::Struct => TokenOwned::Struct,
Token::ToString => TokenOwned::ToString,
Token::While => TokenOwned::While,
Token::WriteLine => TokenOwned::WriteLine,
@ -104,11 +116,14 @@ impl<'src> Token<'src> {
pub fn as_str(&self) -> &str {
match self {
Token::Boolean(boolean_text) => boolean_text,
Token::Float(float_text) => float_text,
Token::Identifier(text) => text,
Token::Integer(integer_text) => integer_text,
Token::String(text) => text,
Token::Bang => "!",
Token::Bool => "bool",
Token::Colon => ":",
Token::Comma => ",",
Token::Dot => ".",
Token::DoubleAmpersand => "&&",
@ -118,10 +133,11 @@ impl<'src> Token<'src> {
Token::Else => "else",
Token::Eof => "EOF",
Token::Equal => "=",
Token::Float(_) => "float",
Token::FloatKeyword => "float",
Token::Greater => ">",
Token::GreaterEqual => ">=",
Token::If => "if",
Token::Int => "int",
Token::IsEven => "is_even",
Token::IsOdd => "is_odd",
Token::LeftCurlyBrace => "{",
@ -141,6 +157,8 @@ impl<'src> Token<'src> {
Token::Semicolon => ";",
Token::Star => "*",
Token::Slash => "/",
Token::Str => "str",
Token::Struct => "struct",
Token::ToString => "to_string",
Token::While => "while",
Token::WriteLine => "write_line",
@ -150,7 +168,9 @@ impl<'src> Token<'src> {
pub fn kind(&self) -> TokenKind {
match self {
Token::Bang => TokenKind::Bang,
Token::Bool => TokenKind::Bool,
Token::Boolean(_) => TokenKind::Boolean,
Token::Colon => TokenKind::Colon,
Token::Comma => TokenKind::Comma,
Token::Dot => TokenKind::Dot,
Token::DoubleAmpersand => TokenKind::DoubleAmpersand,
@ -161,10 +181,12 @@ impl<'src> Token<'src> {
Token::Eof => TokenKind::Eof,
Token::Equal => TokenKind::Equal,
Token::Float(_) => TokenKind::Float,
Token::FloatKeyword => TokenKind::FloatKeyword,
Token::Greater => TokenKind::Greater,
Token::GreaterEqual => TokenKind::GreaterOrEqual,
Token::Identifier(_) => TokenKind::Identifier,
Token::If => TokenKind::If,
Token::Int => TokenKind::Int,
Token::Integer(_) => TokenKind::Integer,
Token::IsEven => TokenKind::IsEven,
Token::IsOdd => TokenKind::IsOdd,
@ -185,7 +207,9 @@ impl<'src> Token<'src> {
Token::Semicolon => TokenKind::Semicolon,
Token::Star => TokenKind::Star,
Token::Slash => TokenKind::Slash,
Token::Str => TokenKind::Str,
Token::String(_) => TokenKind::String,
Token::Struct => TokenKind::Struct,
Token::ToString => TokenKind::ToString,
Token::While => TokenKind::While,
Token::WriteLine => TokenKind::WriteLine,
@ -301,18 +325,23 @@ pub enum TokenOwned {
String(String),
// Keywords
Bool,
Else,
FloatKeyword,
If,
Int,
IsEven,
IsOdd,
Length,
ReadLine,
Str,
ToString,
While,
WriteLine,
// Symbols
Bang,
Colon,
Comma,
Dot,
DoubleAmpersand,
@ -336,6 +365,7 @@ pub enum TokenOwned {
RightSquareBrace,
Semicolon,
Star,
Struct,
Slash,
}
@ -343,7 +373,9 @@ impl Display for TokenOwned {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match self {
TokenOwned::Bang => Token::Bang.fmt(f),
TokenOwned::Bool => write!(f, "bool"),
TokenOwned::Boolean(boolean) => write!(f, "{boolean}"),
TokenOwned::Colon => Token::Colon.fmt(f),
TokenOwned::Comma => Token::Comma.fmt(f),
TokenOwned::Dot => Token::Dot.fmt(f),
TokenOwned::DoubleAmpersand => Token::DoubleAmpersand.fmt(f),
@ -354,10 +386,12 @@ impl Display for TokenOwned {
TokenOwned::Eof => Token::Eof.fmt(f),
TokenOwned::Equal => Token::Equal.fmt(f),
TokenOwned::Float(float) => write!(f, "{float}"),
TokenOwned::FloatKeyword => write!(f, "float"),
TokenOwned::Greater => Token::Greater.fmt(f),
TokenOwned::GreaterOrEqual => Token::GreaterEqual.fmt(f),
TokenOwned::Identifier(text) => write!(f, "{text}"),
TokenOwned::If => Token::If.fmt(f),
TokenOwned::Int => write!(f, "int"),
TokenOwned::Integer(integer) => write!(f, "{integer}"),
TokenOwned::IsEven => Token::IsEven.fmt(f),
TokenOwned::IsOdd => Token::IsOdd.fmt(f),
@ -378,7 +412,9 @@ impl Display for TokenOwned {
TokenOwned::Semicolon => Token::Semicolon.fmt(f),
TokenOwned::Star => Token::Star.fmt(f),
TokenOwned::Slash => Token::Slash.fmt(f),
TokenOwned::Str => write!(f, "str"),
TokenOwned::String(string) => write!(f, "{string}"),
TokenOwned::Struct => Token::Struct.fmt(f),
TokenOwned::ToString => Token::ToString.fmt(f),
TokenOwned::While => Token::While.fmt(f),
TokenOwned::WriteLine => Token::WriteLine.fmt(f),
@ -400,18 +436,23 @@ pub enum TokenKind {
String,
// Keywords
Bool,
Else,
FloatKeyword,
If,
Int,
IsEven,
IsOdd,
Length,
ReadLine,
Str,
ToString,
While,
WriteLine,
// Symbols
Bang,
Colon,
Comma,
Dot,
DoubleAmpersand,
@ -435,6 +476,7 @@ pub enum TokenKind {
RightSquareBrace,
Semicolon,
Star,
Struct,
Slash,
}
@ -442,7 +484,9 @@ impl Display for TokenKind {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match self {
TokenKind::Bang => Token::Bang.fmt(f),
TokenKind::Boolean => write!(f, "boolean"),
TokenKind::Bool => Token::Bool.fmt(f),
TokenKind::Boolean => write!(f, "boolean value"),
TokenKind::Colon => Token::Colon.fmt(f),
TokenKind::Comma => Token::Comma.fmt(f),
TokenKind::Dot => Token::Dot.fmt(f),
TokenKind::DoubleAmpersand => Token::DoubleAmpersand.fmt(f),
@ -452,12 +496,14 @@ impl Display for TokenKind {
TokenKind::Else => Token::Else.fmt(f),
TokenKind::Eof => Token::Eof.fmt(f),
TokenKind::Equal => Token::Equal.fmt(f),
TokenKind::Float => write!(f, "float"),
TokenKind::Float => write!(f, "float value"),
TokenKind::FloatKeyword => Token::FloatKeyword.fmt(f),
TokenKind::Greater => Token::Greater.fmt(f),
TokenKind::GreaterOrEqual => Token::GreaterEqual.fmt(f),
TokenKind::Identifier => write!(f, "identifier"),
TokenKind::If => Token::If.fmt(f),
TokenKind::Integer => write!(f, "integer"),
TokenKind::Int => Token::Int.fmt(f),
TokenKind::Integer => write!(f, "integer value"),
TokenKind::IsEven => Token::IsEven.fmt(f),
TokenKind::IsOdd => Token::IsOdd.fmt(f),
TokenKind::LeftCurlyBrace => Token::LeftCurlyBrace.fmt(f),
@ -476,11 +522,85 @@ impl Display for TokenKind {
TokenKind::RightSquareBrace => Token::RightSquareBrace.fmt(f),
TokenKind::Semicolon => Token::Semicolon.fmt(f),
TokenKind::Star => Token::Star.fmt(f),
TokenKind::Str => write!(f, "str"),
TokenKind::Slash => Token::Slash.fmt(f),
TokenKind::String => write!(f, "string"),
TokenKind::String => write!(f, "string value"),
TokenKind::Struct => Token::Struct.fmt(f),
TokenKind::ToString => Token::ToString.fmt(f),
TokenKind::While => Token::While.fmt(f),
TokenKind::WriteLine => Token::WriteLine.fmt(f),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
fn all_tokens<'src>() -> [Token<'src>; 42] {
[
Token::Bang,
Token::Boolean("true"),
Token::Colon,
Token::Comma,
Token::Dot,
Token::DoubleAmpersand,
Token::DoubleDot,
Token::DoubleEqual,
Token::DoublePipe,
Token::Else,
Token::Eof,
Token::Equal,
Token::Float("42.0"),
Token::Greater,
Token::GreaterEqual,
Token::Identifier("foobar"),
Token::If,
Token::Integer("42"),
Token::IsEven,
Token::IsOdd,
Token::LeftCurlyBrace,
Token::LeftParenthesis,
Token::LeftSquareBrace,
Token::Length,
Token::Less,
Token::LessEqual,
Token::Minus,
Token::Percent,
Token::Plus,
Token::PlusEqual,
Token::ReadLine,
Token::RightCurlyBrace,
Token::RightParenthesis,
Token::RightSquareBrace,
Token::Semicolon,
Token::Star,
Token::Slash,
Token::String("foobar"),
Token::Struct,
Token::ToString,
Token::While,
Token::WriteLine,
]
}
#[test]
fn token_displays() {
for token in all_tokens().iter() {
let display = token.to_string();
assert_eq!(display, token.to_owned().to_string());
if let Token::Boolean(_)
| Token::Float(_)
| Token::Identifier(_)
| Token::Integer(_)
| Token::String(_) = token
{
continue;
} else {
assert_eq!(display, token.kind().to_string());
}
}
}
}

View File

@ -31,6 +31,7 @@ pub struct TypeConflict {
pub enum Type {
Any,
Boolean,
Defined(Identifier),
Enum {
name: Identifier,
type_parameters: Option<Vec<Type>>,
@ -216,6 +217,7 @@ impl Display for Type {
match self {
Type::Any => write!(f, "any"),
Type::Boolean => write!(f, "bool"),
Type::Defined(identifier) => write!(f, "{identifier}"),
Type::Enum { variants, .. } => {
write!(f, "enum ")?;

View File

@ -348,6 +348,7 @@ impl Display for Value {
}
ValueInner::Range(range) => write!(f, "{}..{}", range.start, range.end),
ValueInner::String(string) => write!(f, "{string}"),
ValueInner::Struct(r#struct) => write!(f, "{struct}"),
}
}
}
@ -403,6 +404,7 @@ impl Serialize for Value {
tuple_ser.end()
}
ValueInner::String(string) => serializer.serialize_str(string),
ValueInner::Struct(r#struct) => r#struct.serialize(serializer),
}
}
}
@ -659,6 +661,7 @@ pub enum ValueInner {
Map(BTreeMap<Identifier, Value>),
Range(Range<i64>),
String(String),
Struct(Struct),
}
impl ValueInner {
@ -692,6 +695,7 @@ impl ValueInner {
}
ValueInner::Range(_) => Type::Range,
ValueInner::String(_) => Type::String,
ValueInner::Struct(r#struct) => Type::Defined(r#struct.name().clone()),
}
}
@ -747,6 +751,8 @@ impl Ord for ValueInner {
(Range(_), _) => Ordering::Greater,
(String(left), String(right)) => left.cmp(right),
(String(_), _) => Ordering::Greater,
(Struct(left), Struct(right)) => left.cmp(right),
(Struct(_), _) => Ordering::Greater,
}
}
}
@ -832,6 +838,65 @@ impl Display for Function {
}
}
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
pub enum Struct {
Unit {
name: Identifier,
},
Tuple {
name: Identifier,
fields: Vec<Value>,
},
Fields {
name: Identifier,
fields: Vec<(Identifier, Value)>,
},
}
impl Struct {
pub fn name(&self) -> &Identifier {
match self {
Struct::Unit { name } => name,
Struct::Tuple { name, .. } => name,
Struct::Fields { name, .. } => name,
}
}
}
impl Display for Struct {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
match self {
Struct::Unit { name } => write!(f, "{}", name),
Struct::Tuple { name, fields } => {
write!(f, "{}(", name)?;
for (index, field) in fields.iter().enumerate() {
if index > 0 {
write!(f, ", ")?;
}
write!(f, "{}", field)?;
}
write!(f, ")")
}
Struct::Fields { name, fields } => {
write!(f, "{} {{", name)?;
for (index, (identifier, r#type)) in fields.iter().enumerate() {
if index > 0 {
write!(f, ", ")?;
}
write!(f, "{}: {}", identifier, r#type)?;
}
write!(f, "}}")
}
}
}
}
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub enum ValueError {
CannotAdd(Value, Value),

View File

@ -137,6 +137,58 @@ impl Vm {
return Ok(None);
}
if let BinaryOperator::FieldAccess = operator.inner {
let left_span = left.position;
let left_value = if let Some(value) = self.run_statement(*left)? {
value
} else {
return Err(VmError::ExpectedValue {
position: left_span,
});
};
let right_span = right.position;
if let (Some(list), Statement::Constant(value)) =
(left_value.as_list(), &right.inner)
{
if let Some(index) = value.as_integer() {
let value = list.get(index as usize).cloned();
return Ok(value);
}
if let Some(range) = value.as_range() {
let range = range.start as usize..range.end as usize;
if let Some(items) = list.get(range) {
return Ok(Some(Value::list(items.to_vec())));
}
}
}
if let Some(map) = left_value.as_map() {
if let Statement::Identifier(identifier) = right.inner {
let value = map.get(&identifier).cloned();
return Ok(value);
}
if let Some(value) = self.run_statement(*right)? {
if let Some(string) = value.as_string() {
let identifier = Identifier::new(string);
let value = map.get(&identifier).cloned();
return Ok(value);
}
}
}
return Err(VmError::ExpectedIdentifierIntegerOrRange {
position: right_span,
});
}
let left_position = left.position;
let left_value = if let Some(value) = self.run_statement(*left)? {
value
@ -466,57 +518,6 @@ impl Vm {
Ok(None)
}
Statement::PropertyAccess(left, right) => {
let left_span = left.position;
let left_value = if let Some(value) = self.run_statement(*left)? {
value
} else {
return Err(VmError::ExpectedValue {
position: left_span,
});
};
let right_span = right.position;
if let (Some(list), Statement::Constant(value)) =
(left_value.as_list(), &right.inner)
{
if let Some(index) = value.as_integer() {
let value = list.get(index as usize).cloned();
return Ok(value);
}
if let Some(range) = value.as_range() {
let range = range.start as usize..range.end as usize;
if let Some(items) = list.get(range) {
return Ok(Some(Value::list(items.to_vec())));
}
}
}
if let Some(map) = left_value.as_map() {
if let Statement::Identifier(identifier) = right.inner {
let value = map.get(&identifier).cloned();
return Ok(value);
}
if let Some(value) = self.run_statement(*right)? {
if let Some(string) = value.as_string() {
let identifier = Identifier::new(string);
let value = map.get(&identifier).cloned();
return Ok(value);
}
}
}
Err(VmError::ExpectedIdentifierIntegerOrRange {
position: right_span,
})
}
Statement::UnaryOperation { operator, operand } => {
let position = operand.position;
let value = if let Some(value) = self.run_statement(*operand)? {