Add another token type; Add ranges
This commit is contained in:
parent
755fe5d899
commit
0ba54e9717
@ -174,10 +174,16 @@ impl Lexer {
|
|||||||
(Token::Comma, (self.position - 1, self.position))
|
(Token::Comma, (self.position - 1, self.position))
|
||||||
}
|
}
|
||||||
'.' => {
|
'.' => {
|
||||||
|
if let Some('.') = self.peek_second_char(source) {
|
||||||
|
self.position += 2;
|
||||||
|
|
||||||
|
(Token::DoubleDot, (self.position - 2, self.position))
|
||||||
|
} else {
|
||||||
self.position += 1;
|
self.position += 1;
|
||||||
|
|
||||||
(Token::Dot, (self.position - 1, self.position))
|
(Token::Dot, (self.position - 1, self.position))
|
||||||
}
|
}
|
||||||
|
}
|
||||||
'>' => {
|
'>' => {
|
||||||
if let Some('=') = self.peek_second_char(source) {
|
if let Some('=') = self.peek_second_char(source) {
|
||||||
self.position += 2;
|
self.position += 2;
|
||||||
@ -484,6 +490,21 @@ impl Display for LexError {
|
|||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn range() {
|
||||||
|
let input = "0..42";
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
lex(input),
|
||||||
|
Ok(vec![
|
||||||
|
(Token::Integer("0"), (0, 1)),
|
||||||
|
(Token::DoubleDot, (1, 3)),
|
||||||
|
(Token::Integer("42"), (3, 5)),
|
||||||
|
(Token::Eof, (5, 5))
|
||||||
|
])
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn negate_expression() {
|
fn negate_expression() {
|
||||||
let input = "x = -42; -x";
|
let input = "x = -42; -x";
|
||||||
|
@ -23,7 +23,7 @@ pub use identifier::Identifier;
|
|||||||
pub use lexer::{lex, LexError, Lexer};
|
pub use lexer::{lex, LexError, Lexer};
|
||||||
pub use parser::{parse, ParseError, Parser};
|
pub use parser::{parse, ParseError, Parser};
|
||||||
pub use r#type::Type;
|
pub use r#type::Type;
|
||||||
pub use token::{Token, TokenOwned};
|
pub use token::{Token, TokenKind, TokenOwned};
|
||||||
pub use value::{Value, ValueError};
|
pub use value::{Value, ValueError};
|
||||||
pub use vm::{run, Vm, VmError};
|
pub use vm::{run, Vm, VmError};
|
||||||
|
|
||||||
|
@ -13,7 +13,7 @@ use std::{
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
AbstractSyntaxTree, BinaryOperator, BuiltInFunction, DustError, Identifier, LexError, Lexer,
|
AbstractSyntaxTree, BinaryOperator, BuiltInFunction, DustError, Identifier, LexError, Lexer,
|
||||||
Node, Span, Statement, Token, TokenOwned, UnaryOperator, Value,
|
Node, Span, Statement, Token, TokenKind, TokenOwned, UnaryOperator, Value,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Parses the input into an abstract syntax tree.
|
/// Parses the input into an abstract syntax tree.
|
||||||
@ -225,14 +225,37 @@ impl<'src> Parser<'src> {
|
|||||||
self.next_token()?;
|
self.next_token()?;
|
||||||
|
|
||||||
let integer = text
|
let integer = text
|
||||||
.parse()
|
.parse::<i64>()
|
||||||
.map_err(|error| ParseError::IntegerError { error, position })?;
|
.map_err(|error| ParseError::IntegerError { error, position })?;
|
||||||
|
|
||||||
|
if let Token::DoubleDot = self.current.0 {
|
||||||
|
self.next_token()?;
|
||||||
|
|
||||||
|
if let Token::Integer(range_end) = self.current.0 {
|
||||||
|
self.next_token()?;
|
||||||
|
|
||||||
|
let range_end = range_end
|
||||||
|
.parse::<i64>()
|
||||||
|
.map_err(|error| ParseError::IntegerError { error, position })?;
|
||||||
|
|
||||||
|
Ok(Node::new(
|
||||||
|
Statement::Constant(Value::range(integer..range_end)),
|
||||||
|
(position.0, self.current.1 .1),
|
||||||
|
))
|
||||||
|
} else {
|
||||||
|
Err(ParseError::ExpectedToken {
|
||||||
|
expected: TokenKind::Integer,
|
||||||
|
actual: self.current.0.to_owned(),
|
||||||
|
position: (position.0, self.current.1 .1),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
} else {
|
||||||
Ok(Node::new(
|
Ok(Node::new(
|
||||||
Statement::Constant(Value::integer(integer)),
|
Statement::Constant(Value::integer(integer)),
|
||||||
position,
|
position,
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
}
|
||||||
(Token::Identifier(text), position) => {
|
(Token::Identifier(text), position) => {
|
||||||
self.next_token()?;
|
self.next_token()?;
|
||||||
|
|
||||||
@ -433,7 +456,7 @@ impl<'src> Parser<'src> {
|
|||||||
Ok(Node::new(node.inner, (left_position.0, right_position.1)))
|
Ok(Node::new(node.inner, (left_position.0, right_position.1)))
|
||||||
} else {
|
} else {
|
||||||
Err(ParseError::ExpectedToken {
|
Err(ParseError::ExpectedToken {
|
||||||
expected: TokenOwned::RightParenthesis,
|
expected: TokenKind::RightParenthesis,
|
||||||
actual: self.current.0.to_owned(),
|
actual: self.current.0.to_owned(),
|
||||||
position: self.current.1,
|
position: self.current.1,
|
||||||
})
|
})
|
||||||
@ -464,7 +487,7 @@ impl<'src> Parser<'src> {
|
|||||||
nodes.push(instruction);
|
nodes.push(instruction);
|
||||||
} else {
|
} else {
|
||||||
return Err(ParseError::ExpectedToken {
|
return Err(ParseError::ExpectedToken {
|
||||||
expected: TokenOwned::RightSquareBrace,
|
expected: TokenKind::RightSquareBrace,
|
||||||
actual: self.current.0.to_owned(),
|
actual: self.current.0.to_owned(),
|
||||||
position: self.current.1,
|
position: self.current.1,
|
||||||
});
|
});
|
||||||
@ -496,7 +519,7 @@ impl<'src> Parser<'src> {
|
|||||||
self.next_token()?;
|
self.next_token()?;
|
||||||
} else {
|
} else {
|
||||||
return Err(ParseError::ExpectedToken {
|
return Err(ParseError::ExpectedToken {
|
||||||
expected: TokenOwned::LeftParenthesis,
|
expected: TokenKind::LeftParenthesis,
|
||||||
actual: self.current.0.to_owned(),
|
actual: self.current.0.to_owned(),
|
||||||
position: self.current.1,
|
position: self.current.1,
|
||||||
});
|
});
|
||||||
@ -523,7 +546,7 @@ impl<'src> Parser<'src> {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return Err(ParseError::ExpectedToken {
|
return Err(ParseError::ExpectedToken {
|
||||||
expected: TokenOwned::RightParenthesis,
|
expected: TokenKind::RightParenthesis,
|
||||||
actual: self.current.0.to_owned(),
|
actual: self.current.0.to_owned(),
|
||||||
position: self.current.1,
|
position: self.current.1,
|
||||||
});
|
});
|
||||||
@ -547,7 +570,7 @@ impl<'src> Parser<'src> {
|
|||||||
if let Token::LeftCurlyBrace = self.current.0 {
|
if let Token::LeftCurlyBrace = self.current.0 {
|
||||||
} else {
|
} else {
|
||||||
return Err(ParseError::ExpectedToken {
|
return Err(ParseError::ExpectedToken {
|
||||||
expected: TokenOwned::LeftCurlyBrace,
|
expected: TokenKind::LeftCurlyBrace,
|
||||||
actual: self.current.0.to_owned(),
|
actual: self.current.0.to_owned(),
|
||||||
position: self.current.1,
|
position: self.current.1,
|
||||||
});
|
});
|
||||||
@ -707,7 +730,7 @@ impl<'src> Parser<'src> {
|
|||||||
self.next_token()?;
|
self.next_token()?;
|
||||||
} else {
|
} else {
|
||||||
return Err(ParseError::ExpectedToken {
|
return Err(ParseError::ExpectedToken {
|
||||||
expected: TokenOwned::LeftCurlyBrace,
|
expected: TokenKind::LeftCurlyBrace,
|
||||||
actual: self.current.0.to_owned(),
|
actual: self.current.0.to_owned(),
|
||||||
position: self.current.1,
|
position: self.current.1,
|
||||||
});
|
});
|
||||||
@ -749,7 +772,7 @@ pub enum ParseError {
|
|||||||
position: Span,
|
position: Span,
|
||||||
},
|
},
|
||||||
ExpectedToken {
|
ExpectedToken {
|
||||||
expected: TokenOwned,
|
expected: TokenKind,
|
||||||
actual: TokenOwned,
|
actual: TokenOwned,
|
||||||
position: Span,
|
position: Span,
|
||||||
},
|
},
|
||||||
@ -822,6 +845,18 @@ mod tests {
|
|||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn range() {
|
||||||
|
let input = "0..42";
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
parse(input),
|
||||||
|
Ok(AbstractSyntaxTree {
|
||||||
|
nodes: [Node::new(Statement::Constant(Value::range(0..42)), (0, 5))].into()
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn negate_variable() {
|
fn negate_variable() {
|
||||||
let input = "a = 1; -a";
|
let input = "a = 1; -a";
|
||||||
|
@ -32,6 +32,7 @@ pub enum Token<'src> {
|
|||||||
Comma,
|
Comma,
|
||||||
Dot,
|
Dot,
|
||||||
DoubleAmpersand,
|
DoubleAmpersand,
|
||||||
|
DoubleDot,
|
||||||
DoubleEqual,
|
DoubleEqual,
|
||||||
DoublePipe,
|
DoublePipe,
|
||||||
Equal,
|
Equal,
|
||||||
@ -62,6 +63,7 @@ impl<'src> Token<'src> {
|
|||||||
Token::Comma => TokenOwned::Comma,
|
Token::Comma => TokenOwned::Comma,
|
||||||
Token::Dot => TokenOwned::Dot,
|
Token::Dot => TokenOwned::Dot,
|
||||||
Token::DoubleAmpersand => TokenOwned::DoubleAmpersand,
|
Token::DoubleAmpersand => TokenOwned::DoubleAmpersand,
|
||||||
|
Token::DoubleDot => TokenOwned::DoubleDot,
|
||||||
Token::DoubleEqual => TokenOwned::DoubleEqual,
|
Token::DoubleEqual => TokenOwned::DoubleEqual,
|
||||||
Token::DoublePipe => TokenOwned::DoublePipe,
|
Token::DoublePipe => TokenOwned::DoublePipe,
|
||||||
Token::Else => TokenOwned::Else,
|
Token::Else => TokenOwned::Else,
|
||||||
@ -110,6 +112,7 @@ impl<'src> Token<'src> {
|
|||||||
Token::Comma => ",",
|
Token::Comma => ",",
|
||||||
Token::Dot => ".",
|
Token::Dot => ".",
|
||||||
Token::DoubleAmpersand => "&&",
|
Token::DoubleAmpersand => "&&",
|
||||||
|
Token::DoubleDot => "..",
|
||||||
Token::DoubleEqual => "==",
|
Token::DoubleEqual => "==",
|
||||||
Token::DoublePipe => "||",
|
Token::DoublePipe => "||",
|
||||||
Token::Else => "else",
|
Token::Else => "else",
|
||||||
@ -144,6 +147,51 @@ impl<'src> Token<'src> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn kind(&self) -> TokenKind {
|
||||||
|
match self {
|
||||||
|
Token::Bang => TokenKind::Bang,
|
||||||
|
Token::Boolean(_) => TokenKind::Boolean,
|
||||||
|
Token::Comma => TokenKind::Comma,
|
||||||
|
Token::Dot => TokenKind::Dot,
|
||||||
|
Token::DoubleAmpersand => TokenKind::DoubleAmpersand,
|
||||||
|
Token::DoubleDot => TokenKind::DoubleDot,
|
||||||
|
Token::DoubleEqual => TokenKind::DoubleEqual,
|
||||||
|
Token::DoublePipe => TokenKind::DoublePipe,
|
||||||
|
Token::Else => TokenKind::Else,
|
||||||
|
Token::Eof => TokenKind::Eof,
|
||||||
|
Token::Equal => TokenKind::Equal,
|
||||||
|
Token::Float(_) => TokenKind::Float,
|
||||||
|
Token::Greater => TokenKind::Greater,
|
||||||
|
Token::GreaterEqual => TokenKind::GreaterOrEqual,
|
||||||
|
Token::Identifier(_) => TokenKind::Identifier,
|
||||||
|
Token::If => TokenKind::If,
|
||||||
|
Token::Integer(_) => TokenKind::Integer,
|
||||||
|
Token::IsEven => TokenKind::IsEven,
|
||||||
|
Token::IsOdd => TokenKind::IsOdd,
|
||||||
|
Token::LeftCurlyBrace => TokenKind::LeftCurlyBrace,
|
||||||
|
Token::LeftParenthesis => TokenKind::LeftParenthesis,
|
||||||
|
Token::LeftSquareBrace => TokenKind::LeftSquareBrace,
|
||||||
|
Token::Length => TokenKind::Length,
|
||||||
|
Token::Less => TokenKind::Less,
|
||||||
|
Token::LessEqual => TokenKind::LessOrEqual,
|
||||||
|
Token::Minus => TokenKind::Minus,
|
||||||
|
Token::Percent => TokenKind::Percent,
|
||||||
|
Token::Plus => TokenKind::Plus,
|
||||||
|
Token::PlusEqual => TokenKind::PlusEqual,
|
||||||
|
Token::ReadLine => TokenKind::ReadLine,
|
||||||
|
Token::RightCurlyBrace => TokenKind::RightCurlyBrace,
|
||||||
|
Token::RightParenthesis => TokenKind::RightParenthesis,
|
||||||
|
Token::RightSquareBrace => TokenKind::RightSquareBrace,
|
||||||
|
Token::Semicolon => TokenKind::Semicolon,
|
||||||
|
Token::Star => TokenKind::Star,
|
||||||
|
Token::Slash => TokenKind::Slash,
|
||||||
|
Token::String(_) => TokenKind::String,
|
||||||
|
Token::ToString => TokenKind::ToString,
|
||||||
|
Token::While => TokenKind::While,
|
||||||
|
Token::WriteLine => TokenKind::WriteLine,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn is_eof(&self) -> bool {
|
pub fn is_eof(&self) -> bool {
|
||||||
matches!(self, Token::Eof)
|
matches!(self, Token::Eof)
|
||||||
}
|
}
|
||||||
@ -162,7 +210,7 @@ impl<'src> Token<'src> {
|
|||||||
Token::DoubleAmpersand => 4,
|
Token::DoubleAmpersand => 4,
|
||||||
Token::DoublePipe => 3,
|
Token::DoublePipe => 3,
|
||||||
Token::Equal | Token::PlusEqual => 2,
|
Token::Equal | Token::PlusEqual => 2,
|
||||||
Token::Semicolon => 1,
|
Token::DoubleDot | Token::Semicolon => 1,
|
||||||
_ => 0,
|
_ => 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -198,6 +246,7 @@ impl<'src> PartialEq for Token<'src> {
|
|||||||
(Token::Comma, Token::Comma) => true,
|
(Token::Comma, Token::Comma) => true,
|
||||||
(Token::Dot, Token::Dot) => true,
|
(Token::Dot, Token::Dot) => true,
|
||||||
(Token::DoubleAmpersand, Token::DoubleAmpersand) => true,
|
(Token::DoubleAmpersand, Token::DoubleAmpersand) => true,
|
||||||
|
(Token::DoubleDot, Token::DoubleDot) => true,
|
||||||
(Token::DoubleEqual, Token::DoubleEqual) => true,
|
(Token::DoubleEqual, Token::DoubleEqual) => true,
|
||||||
(Token::DoublePipe, Token::DoublePipe) => true,
|
(Token::DoublePipe, Token::DoublePipe) => true,
|
||||||
(Token::Else, Token::Else) => true,
|
(Token::Else, Token::Else) => true,
|
||||||
@ -267,6 +316,7 @@ pub enum TokenOwned {
|
|||||||
Comma,
|
Comma,
|
||||||
Dot,
|
Dot,
|
||||||
DoubleAmpersand,
|
DoubleAmpersand,
|
||||||
|
DoubleDot,
|
||||||
DoubleEqual,
|
DoubleEqual,
|
||||||
DoublePipe,
|
DoublePipe,
|
||||||
Equal,
|
Equal,
|
||||||
@ -297,6 +347,7 @@ impl Display for TokenOwned {
|
|||||||
TokenOwned::Comma => Token::Comma.fmt(f),
|
TokenOwned::Comma => Token::Comma.fmt(f),
|
||||||
TokenOwned::Dot => Token::Dot.fmt(f),
|
TokenOwned::Dot => Token::Dot.fmt(f),
|
||||||
TokenOwned::DoubleAmpersand => Token::DoubleAmpersand.fmt(f),
|
TokenOwned::DoubleAmpersand => Token::DoubleAmpersand.fmt(f),
|
||||||
|
TokenOwned::DoubleDot => Token::DoubleDot.fmt(f),
|
||||||
TokenOwned::DoubleEqual => Token::DoubleEqual.fmt(f),
|
TokenOwned::DoubleEqual => Token::DoubleEqual.fmt(f),
|
||||||
TokenOwned::DoublePipe => Token::DoublePipe.fmt(f),
|
TokenOwned::DoublePipe => Token::DoublePipe.fmt(f),
|
||||||
TokenOwned::Else => Token::Else.fmt(f),
|
TokenOwned::Else => Token::Else.fmt(f),
|
||||||
@ -334,3 +385,102 @@ impl Display for TokenOwned {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Token representation that holds no data.
|
||||||
|
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
|
||||||
|
pub enum TokenKind {
|
||||||
|
Eof,
|
||||||
|
|
||||||
|
Identifier,
|
||||||
|
|
||||||
|
// Hard-coded values
|
||||||
|
Boolean,
|
||||||
|
Float,
|
||||||
|
Integer,
|
||||||
|
String,
|
||||||
|
|
||||||
|
// Keywords
|
||||||
|
Else,
|
||||||
|
If,
|
||||||
|
IsEven,
|
||||||
|
IsOdd,
|
||||||
|
Length,
|
||||||
|
ReadLine,
|
||||||
|
ToString,
|
||||||
|
While,
|
||||||
|
WriteLine,
|
||||||
|
|
||||||
|
// Symbols
|
||||||
|
Bang,
|
||||||
|
Comma,
|
||||||
|
Dot,
|
||||||
|
DoubleAmpersand,
|
||||||
|
DoubleDot,
|
||||||
|
DoubleEqual,
|
||||||
|
DoublePipe,
|
||||||
|
Equal,
|
||||||
|
Greater,
|
||||||
|
GreaterOrEqual,
|
||||||
|
LeftCurlyBrace,
|
||||||
|
LeftParenthesis,
|
||||||
|
LeftSquareBrace,
|
||||||
|
Less,
|
||||||
|
LessOrEqual,
|
||||||
|
Minus,
|
||||||
|
Percent,
|
||||||
|
Plus,
|
||||||
|
PlusEqual,
|
||||||
|
RightCurlyBrace,
|
||||||
|
RightParenthesis,
|
||||||
|
RightSquareBrace,
|
||||||
|
Semicolon,
|
||||||
|
Star,
|
||||||
|
Slash,
|
||||||
|
}
|
||||||
|
|
||||||
|
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::Comma => Token::Comma.fmt(f),
|
||||||
|
TokenKind::Dot => Token::Dot.fmt(f),
|
||||||
|
TokenKind::DoubleAmpersand => Token::DoubleAmpersand.fmt(f),
|
||||||
|
TokenKind::DoubleDot => Token::DoubleDot.fmt(f),
|
||||||
|
TokenKind::DoubleEqual => Token::DoubleEqual.fmt(f),
|
||||||
|
TokenKind::DoublePipe => Token::DoublePipe.fmt(f),
|
||||||
|
TokenKind::Else => Token::Else.fmt(f),
|
||||||
|
TokenKind::Eof => Token::Eof.fmt(f),
|
||||||
|
TokenKind::Equal => Token::Equal.fmt(f),
|
||||||
|
TokenKind::Float => write!(f, "float"),
|
||||||
|
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::IsEven => Token::IsEven.fmt(f),
|
||||||
|
TokenKind::IsOdd => Token::IsOdd.fmt(f),
|
||||||
|
TokenKind::LeftCurlyBrace => Token::LeftCurlyBrace.fmt(f),
|
||||||
|
TokenKind::LeftParenthesis => Token::LeftParenthesis.fmt(f),
|
||||||
|
TokenKind::LeftSquareBrace => Token::LeftSquareBrace.fmt(f),
|
||||||
|
TokenKind::Length => Token::Length.fmt(f),
|
||||||
|
TokenKind::Less => Token::Less.fmt(f),
|
||||||
|
TokenKind::LessOrEqual => Token::LessEqual.fmt(f),
|
||||||
|
TokenKind::Minus => Token::Minus.fmt(f),
|
||||||
|
TokenKind::Percent => Token::Percent.fmt(f),
|
||||||
|
TokenKind::Plus => Token::Plus.fmt(f),
|
||||||
|
TokenKind::PlusEqual => Token::PlusEqual.fmt(f),
|
||||||
|
TokenKind::ReadLine => Token::ReadLine.fmt(f),
|
||||||
|
TokenKind::RightCurlyBrace => Token::RightCurlyBrace.fmt(f),
|
||||||
|
TokenKind::RightParenthesis => Token::RightParenthesis.fmt(f),
|
||||||
|
TokenKind::RightSquareBrace => Token::RightSquareBrace.fmt(f),
|
||||||
|
TokenKind::Semicolon => Token::Semicolon.fmt(f),
|
||||||
|
TokenKind::Star => Token::Star.fmt(f),
|
||||||
|
TokenKind::Slash => Token::Slash.fmt(f),
|
||||||
|
TokenKind::String => write!(f, "string"),
|
||||||
|
TokenKind::ToString => Token::ToString.fmt(f),
|
||||||
|
TokenKind::While => Token::While.fmt(f),
|
||||||
|
TokenKind::WriteLine => Token::WriteLine.fmt(f),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user