2024-03-23 15:24:25 +00:00
|
|
|
use std::{
|
|
|
|
f64::{INFINITY, NAN, NEG_INFINITY},
|
|
|
|
fmt::{self, Display, Formatter},
|
|
|
|
};
|
2024-02-29 02:04:38 +00:00
|
|
|
|
2024-03-20 15:43:47 +00:00
|
|
|
use chumsky::prelude::*;
|
2024-02-25 18:49:26 +00:00
|
|
|
|
2024-04-21 21:00:08 +00:00
|
|
|
use crate::error::Error;
|
2024-02-25 18:49:26 +00:00
|
|
|
|
2024-03-09 01:30:26 +00:00
|
|
|
#[derive(Copy, Clone, Debug, PartialEq)]
|
2024-02-25 18:49:26 +00:00
|
|
|
pub enum Token<'src> {
|
|
|
|
Boolean(bool),
|
2024-04-27 06:22:26 +00:00
|
|
|
Comment(&'src str),
|
2024-02-25 18:49:26 +00:00
|
|
|
Integer(i64),
|
|
|
|
Float(f64),
|
|
|
|
String(&'src str),
|
|
|
|
Identifier(&'src str),
|
2024-03-07 10:37:26 +00:00
|
|
|
Operator(Operator),
|
2024-03-07 11:57:33 +00:00
|
|
|
Control(Control),
|
2024-03-20 15:43:47 +00:00
|
|
|
Keyword(Keyword),
|
|
|
|
}
|
|
|
|
|
2024-03-23 15:24:25 +00:00
|
|
|
impl<'src> Display for Token<'src> {
|
|
|
|
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
|
|
|
match self {
|
|
|
|
Token::Boolean(boolean) => write!(f, "{boolean}"),
|
2024-04-27 06:22:26 +00:00
|
|
|
Token::Comment(comment) => write!(f, "# {comment}"),
|
2024-03-23 15:24:25 +00:00
|
|
|
Token::Integer(integer) => write!(f, "{integer}"),
|
|
|
|
Token::Float(float) => write!(f, "{float}"),
|
|
|
|
Token::String(string) => write!(f, "{string}"),
|
|
|
|
Token::Identifier(string) => write!(f, "{string}"),
|
|
|
|
Token::Operator(operator) => write!(f, "{operator}"),
|
|
|
|
Token::Control(control) => write!(f, "{control}"),
|
|
|
|
Token::Keyword(keyword) => write!(f, "{keyword}"),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-03-20 15:43:47 +00:00
|
|
|
#[derive(Copy, Clone, Debug, PartialEq)]
|
|
|
|
pub enum Keyword {
|
|
|
|
Any,
|
2024-05-21 21:07:12 +00:00
|
|
|
As,
|
2024-03-20 21:05:37 +00:00
|
|
|
Async,
|
2024-03-20 15:43:47 +00:00
|
|
|
Bool,
|
|
|
|
Break,
|
|
|
|
Else,
|
|
|
|
Float,
|
2024-03-24 13:10:49 +00:00
|
|
|
Fn,
|
2024-03-20 15:43:47 +00:00
|
|
|
Int,
|
|
|
|
If,
|
2024-06-04 18:47:15 +00:00
|
|
|
JsonParse,
|
|
|
|
Length,
|
2024-03-20 15:43:47 +00:00
|
|
|
List,
|
|
|
|
Map,
|
|
|
|
None,
|
|
|
|
Range,
|
2024-05-25 15:48:43 +00:00
|
|
|
ReadFile,
|
2024-04-21 21:00:08 +00:00
|
|
|
ReadLine,
|
|
|
|
Sleep,
|
2024-03-20 15:43:47 +00:00
|
|
|
Struct,
|
|
|
|
Str,
|
2024-03-24 13:10:49 +00:00
|
|
|
Type,
|
2024-03-20 15:43:47 +00:00
|
|
|
Loop,
|
|
|
|
While,
|
2024-04-21 21:00:08 +00:00
|
|
|
WriteLine,
|
2024-03-20 15:43:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Display for Keyword {
|
|
|
|
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
|
|
|
match self {
|
|
|
|
Keyword::Any => write!(f, "any"),
|
2024-05-21 21:07:12 +00:00
|
|
|
Keyword::As => write!(f, "as"),
|
2024-03-20 21:05:37 +00:00
|
|
|
Keyword::Async => write!(f, "async"),
|
2024-03-20 15:43:47 +00:00
|
|
|
Keyword::Bool => write!(f, "bool"),
|
|
|
|
Keyword::Break => write!(f, "break"),
|
|
|
|
Keyword::Else => write!(f, "else"),
|
|
|
|
Keyword::Float => write!(f, "float"),
|
2024-03-24 13:10:49 +00:00
|
|
|
Keyword::Fn => write!(f, "fn"),
|
2024-03-20 15:43:47 +00:00
|
|
|
Keyword::Int => write!(f, "int"),
|
|
|
|
Keyword::If => write!(f, "if"),
|
|
|
|
Keyword::List => write!(f, "list"),
|
|
|
|
Keyword::Map => write!(f, "map"),
|
|
|
|
Keyword::None => write!(f, "none"),
|
|
|
|
Keyword::Range => write!(f, "range"),
|
|
|
|
Keyword::Struct => write!(f, "struct"),
|
|
|
|
Keyword::Str => write!(f, "str"),
|
|
|
|
Keyword::Loop => write!(f, "loop"),
|
|
|
|
Keyword::While => write!(f, "while"),
|
2024-03-24 13:10:49 +00:00
|
|
|
Keyword::Type => write!(f, "type"),
|
2024-06-04 18:47:15 +00:00
|
|
|
Keyword::JsonParse => write!(f, "JSON_PARSE"),
|
|
|
|
Keyword::Length => write!(f, "LENGTH"),
|
2024-05-25 15:48:43 +00:00
|
|
|
Keyword::ReadFile => write!(f, "READ_FILE"),
|
2024-04-21 21:00:08 +00:00
|
|
|
Keyword::ReadLine => write!(f, "READ_LINE"),
|
|
|
|
Keyword::Sleep => write!(f, "SLEEP"),
|
|
|
|
Keyword::WriteLine => write!(f, "WRITE_LINE"),
|
2024-03-20 15:43:47 +00:00
|
|
|
}
|
|
|
|
}
|
2024-02-29 02:04:38 +00:00
|
|
|
}
|
|
|
|
|
2024-03-09 01:30:26 +00:00
|
|
|
#[derive(Copy, Clone, Debug, PartialEq)]
|
2024-03-07 10:37:26 +00:00
|
|
|
pub enum Operator {
|
|
|
|
Add,
|
|
|
|
AddAssign,
|
|
|
|
And,
|
|
|
|
Assign,
|
|
|
|
Divide,
|
|
|
|
Equal,
|
|
|
|
Greater,
|
|
|
|
GreaterOrEqual,
|
|
|
|
Less,
|
|
|
|
LessOrEqual,
|
|
|
|
Modulo,
|
|
|
|
Multiply,
|
|
|
|
Not,
|
|
|
|
NotEqual,
|
|
|
|
Or,
|
|
|
|
SubAssign,
|
|
|
|
Subtract,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Display for Operator {
|
|
|
|
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
|
|
|
match self {
|
|
|
|
Operator::Add => write!(f, "+"),
|
|
|
|
Operator::AddAssign => write!(f, "+="),
|
|
|
|
Operator::And => write!(f, "&&"),
|
|
|
|
Operator::Assign => write!(f, "="),
|
|
|
|
Operator::Divide => write!(f, "="),
|
|
|
|
Operator::Equal => write!(f, "=="),
|
|
|
|
Operator::Greater => write!(f, ">"),
|
|
|
|
Operator::GreaterOrEqual => write!(f, ">="),
|
|
|
|
Operator::Less => write!(f, "<"),
|
|
|
|
Operator::LessOrEqual => write!(f, "<="),
|
|
|
|
Operator::Modulo => write!(f, "%"),
|
|
|
|
Operator::Multiply => write!(f, "*"),
|
|
|
|
Operator::Not => write!(f, "!"),
|
|
|
|
Operator::NotEqual => write!(f, "!="),
|
|
|
|
Operator::Or => write!(f, "||"),
|
|
|
|
Operator::SubAssign => write!(f, "-="),
|
|
|
|
Operator::Subtract => write!(f, "-"),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-03-09 01:30:26 +00:00
|
|
|
#[derive(Copy, Clone, Debug, PartialEq)]
|
2024-03-07 11:57:33 +00:00
|
|
|
pub enum Control {
|
|
|
|
CurlyOpen,
|
|
|
|
CurlyClose,
|
|
|
|
SquareOpen,
|
|
|
|
SquareClose,
|
|
|
|
ParenOpen,
|
|
|
|
ParenClose,
|
2024-03-24 13:10:49 +00:00
|
|
|
Pipe,
|
2024-03-07 11:57:33 +00:00
|
|
|
Comma,
|
|
|
|
DoubleColon,
|
|
|
|
Colon,
|
2024-03-23 13:35:24 +00:00
|
|
|
Dollar,
|
2024-03-07 11:57:33 +00:00
|
|
|
Dot,
|
2024-03-09 01:30:26 +00:00
|
|
|
DoubleDot,
|
2024-03-07 11:57:33 +00:00
|
|
|
Semicolon,
|
2024-03-24 13:10:49 +00:00
|
|
|
SkinnyArrow,
|
|
|
|
FatArrow,
|
2024-04-21 21:00:08 +00:00
|
|
|
DoubleUnderscore,
|
2024-03-07 11:57:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Display for Control {
|
|
|
|
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
|
|
|
match self {
|
|
|
|
Control::CurlyOpen => write!(f, "{{"),
|
|
|
|
Control::CurlyClose => write!(f, "}}"),
|
2024-03-23 13:35:24 +00:00
|
|
|
Control::Dollar => write!(f, "$"),
|
2024-03-07 11:57:33 +00:00
|
|
|
Control::SquareOpen => write!(f, "["),
|
|
|
|
Control::SquareClose => write!(f, "]"),
|
|
|
|
Control::ParenOpen => write!(f, "("),
|
|
|
|
Control::ParenClose => write!(f, ")"),
|
2024-03-24 13:10:49 +00:00
|
|
|
Control::Pipe => write!(f, "|"),
|
2024-03-07 11:57:33 +00:00
|
|
|
Control::Comma => write!(f, ","),
|
|
|
|
Control::DoubleColon => write!(f, "::"),
|
|
|
|
Control::Colon => write!(f, ":"),
|
|
|
|
Control::Dot => write!(f, "."),
|
|
|
|
Control::Semicolon => write!(f, ";"),
|
2024-03-11 21:58:26 +00:00
|
|
|
Control::DoubleDot => write!(f, ".."),
|
2024-03-24 13:10:49 +00:00
|
|
|
Control::SkinnyArrow => write!(f, "->"),
|
|
|
|
Control::FatArrow => write!(f, "=>"),
|
2024-04-21 21:00:08 +00:00
|
|
|
Control::DoubleUnderscore => write!(f, "__"),
|
2024-03-07 11:57:33 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-03-06 20:36:58 +00:00
|
|
|
pub fn lex<'src>(source: &'src str) -> Result<Vec<(Token<'src>, SimpleSpan)>, Vec<Error>> {
|
2024-02-25 18:49:26 +00:00
|
|
|
lexer()
|
|
|
|
.parse(source)
|
|
|
|
.into_result()
|
2024-03-06 20:36:58 +00:00
|
|
|
.map_err(|errors| errors.into_iter().map(|error| error.into()).collect())
|
2024-02-25 18:49:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn lexer<'src>() -> impl Parser<
|
|
|
|
'src,
|
|
|
|
&'src str,
|
|
|
|
Vec<(Token<'src>, SimpleSpan<usize>)>,
|
|
|
|
extra::Err<Rich<'src, char, SimpleSpan<usize>>>,
|
|
|
|
> {
|
2024-04-27 06:22:26 +00:00
|
|
|
let line_comment = just("//")
|
|
|
|
.ignore_then(
|
|
|
|
none_of('\n')
|
|
|
|
.repeated()
|
|
|
|
.to_slice()
|
|
|
|
.map(|text: &str| Token::Comment(text.trim())),
|
|
|
|
)
|
|
|
|
.then_ignore(just('\n').or_not());
|
|
|
|
|
|
|
|
let multi_line_comment = just("/*")
|
|
|
|
.ignore_then(
|
|
|
|
none_of('*')
|
|
|
|
.repeated()
|
|
|
|
.to_slice()
|
|
|
|
.map(|text: &str| Token::Comment(text.trim())),
|
|
|
|
)
|
|
|
|
.then_ignore(just("*/"));
|
|
|
|
|
2024-03-07 00:45:41 +00:00
|
|
|
let boolean = choice((
|
2024-03-20 15:43:47 +00:00
|
|
|
just("true").to(Token::Boolean(true)),
|
|
|
|
just("false").to(Token::Boolean(false)),
|
2024-03-07 00:45:41 +00:00
|
|
|
));
|
2024-02-25 18:49:26 +00:00
|
|
|
|
|
|
|
let float_numeric = just('-')
|
|
|
|
.or_not()
|
|
|
|
.then(text::int(10))
|
|
|
|
.then(just('.').then(text::digits(10)))
|
2024-03-09 00:05:17 +00:00
|
|
|
.then(just('e').then(text::digits(10)).or_not())
|
2024-02-25 18:49:26 +00:00
|
|
|
.to_slice()
|
|
|
|
.map(|text: &str| Token::Float(text.parse().unwrap()));
|
|
|
|
|
2024-03-23 15:24:25 +00:00
|
|
|
let float = choice((
|
|
|
|
float_numeric,
|
|
|
|
just("Infinity").to(Token::Float(INFINITY)),
|
|
|
|
just("-Infinity").to(Token::Float(NEG_INFINITY)),
|
|
|
|
just("NaN").to(Token::Float(NAN)),
|
|
|
|
));
|
2024-02-25 18:49:26 +00:00
|
|
|
|
|
|
|
let integer = just('-')
|
|
|
|
.or_not()
|
2024-02-28 23:16:25 +00:00
|
|
|
.then(text::int(10))
|
2024-02-25 18:49:26 +00:00
|
|
|
.to_slice()
|
|
|
|
.map(|text: &str| {
|
2024-03-23 15:24:25 +00:00
|
|
|
let integer = text.parse().unwrap();
|
2024-02-25 18:49:26 +00:00
|
|
|
|
|
|
|
Token::Integer(integer)
|
|
|
|
});
|
|
|
|
|
|
|
|
let delimited_string = |delimiter| {
|
|
|
|
just(delimiter)
|
|
|
|
.then(none_of(delimiter).repeated())
|
|
|
|
.then(just(delimiter))
|
|
|
|
.to_slice()
|
|
|
|
.map(|text: &str| Token::String(&text[1..text.len() - 1]))
|
|
|
|
};
|
|
|
|
|
|
|
|
let string = choice((
|
|
|
|
delimited_string('\''),
|
|
|
|
delimited_string('"'),
|
|
|
|
delimited_string('`'),
|
|
|
|
));
|
|
|
|
|
2024-05-20 21:15:05 +00:00
|
|
|
let identifier_and_keyword = text::ident().map(|text: &str| match text {
|
|
|
|
"any" => Token::Keyword(Keyword::Any),
|
|
|
|
"async" => Token::Keyword(Keyword::Async),
|
2024-05-21 23:27:33 +00:00
|
|
|
"as" => Token::Keyword(Keyword::As),
|
2024-05-20 21:15:05 +00:00
|
|
|
"bool" => Token::Keyword(Keyword::Bool),
|
|
|
|
"break" => Token::Keyword(Keyword::Break),
|
|
|
|
"else" => Token::Keyword(Keyword::Else),
|
|
|
|
"float" => Token::Keyword(Keyword::Float),
|
|
|
|
"fn" => Token::Keyword(Keyword::Fn),
|
|
|
|
"int" => Token::Keyword(Keyword::Int),
|
|
|
|
"if" => Token::Keyword(Keyword::If),
|
|
|
|
"list" => Token::Keyword(Keyword::List),
|
|
|
|
"map" => Token::Keyword(Keyword::Map),
|
|
|
|
"none" => Token::Keyword(Keyword::None),
|
|
|
|
"range" => Token::Keyword(Keyword::Range),
|
|
|
|
"struct" => Token::Keyword(Keyword::Struct),
|
|
|
|
"str" => Token::Keyword(Keyword::Str),
|
|
|
|
"type" => Token::Keyword(Keyword::Type),
|
|
|
|
"loop" => Token::Keyword(Keyword::Loop),
|
|
|
|
"while" => Token::Keyword(Keyword::While),
|
2024-06-04 18:47:15 +00:00
|
|
|
"JSON_PARSE" => Token::Keyword(Keyword::JsonParse),
|
|
|
|
"LENGTH" => Token::Keyword(Keyword::Length),
|
2024-05-25 15:48:43 +00:00
|
|
|
"READ_FILE" => Token::Keyword(Keyword::ReadFile),
|
2024-05-20 21:15:05 +00:00
|
|
|
"READ_LINE" => Token::Keyword(Keyword::ReadLine),
|
|
|
|
"SLEEP" => Token::Keyword(Keyword::Sleep),
|
|
|
|
"WRITE_LINE" => Token::Keyword(Keyword::WriteLine),
|
|
|
|
_ => Token::Identifier(text),
|
|
|
|
});
|
2024-02-25 18:49:26 +00:00
|
|
|
|
|
|
|
let operator = choice((
|
2024-03-07 10:37:26 +00:00
|
|
|
// logic
|
2024-03-20 15:43:47 +00:00
|
|
|
just("&&").to(Operator::And),
|
|
|
|
just("==").to(Operator::Equal),
|
|
|
|
just("!=").to(Operator::NotEqual),
|
|
|
|
just(">=").to(Operator::GreaterOrEqual),
|
|
|
|
just("<=").to(Operator::LessOrEqual),
|
|
|
|
just(">").to(Operator::Greater),
|
|
|
|
just("<").to(Operator::Less),
|
|
|
|
just("!").to(Operator::Not),
|
|
|
|
just("!=").to(Operator::NotEqual),
|
|
|
|
just("||").to(Operator::Or),
|
2024-03-07 11:33:54 +00:00
|
|
|
// assignment
|
2024-03-20 15:43:47 +00:00
|
|
|
just("=").to(Operator::Assign),
|
|
|
|
just("+=").to(Operator::AddAssign),
|
|
|
|
just("-=").to(Operator::SubAssign),
|
2024-03-07 10:37:26 +00:00
|
|
|
// math
|
2024-03-20 15:43:47 +00:00
|
|
|
just("+").to(Operator::Add),
|
|
|
|
just("-").to(Operator::Subtract),
|
|
|
|
just("*").to(Operator::Multiply),
|
|
|
|
just("/").to(Operator::Divide),
|
|
|
|
just("%").to(Operator::Modulo),
|
2024-02-25 18:49:26 +00:00
|
|
|
))
|
|
|
|
.map(Token::Operator);
|
|
|
|
|
2024-02-28 22:49:46 +00:00
|
|
|
let control = choice((
|
2024-03-24 13:10:49 +00:00
|
|
|
just("->").to(Control::SkinnyArrow),
|
|
|
|
just("=>").to(Control::FatArrow),
|
2024-03-20 15:43:47 +00:00
|
|
|
just("{").to(Control::CurlyOpen),
|
|
|
|
just("}").to(Control::CurlyClose),
|
|
|
|
just("[").to(Control::SquareOpen),
|
|
|
|
just("]").to(Control::SquareClose),
|
|
|
|
just("(").to(Control::ParenOpen),
|
|
|
|
just(")").to(Control::ParenClose),
|
2024-03-24 13:10:49 +00:00
|
|
|
just("|").to(Control::Pipe),
|
2024-03-20 15:43:47 +00:00
|
|
|
just(",").to(Control::Comma),
|
|
|
|
just(";").to(Control::Semicolon),
|
|
|
|
just("::").to(Control::DoubleColon),
|
|
|
|
just(":").to(Control::Colon),
|
|
|
|
just("..").to(Control::DoubleDot),
|
|
|
|
just(".").to(Control::Dot),
|
2024-03-23 13:35:24 +00:00
|
|
|
just("$").to(Control::Dollar),
|
2024-04-21 21:00:08 +00:00
|
|
|
just("__").to(Control::DoubleUnderscore),
|
2024-02-28 22:49:46 +00:00
|
|
|
))
|
|
|
|
.map(Token::Control);
|
2024-02-25 18:49:26 +00:00
|
|
|
|
|
|
|
choice((
|
2024-04-27 06:22:26 +00:00
|
|
|
line_comment,
|
|
|
|
multi_line_comment,
|
|
|
|
boolean,
|
|
|
|
float,
|
|
|
|
integer,
|
|
|
|
string,
|
2024-05-20 21:15:05 +00:00
|
|
|
identifier_and_keyword,
|
2024-04-27 06:22:26 +00:00
|
|
|
control,
|
|
|
|
operator,
|
2024-02-25 18:49:26 +00:00
|
|
|
))
|
|
|
|
.map_with(|token, state| (token, state.span()))
|
|
|
|
.padded()
|
|
|
|
.repeated()
|
|
|
|
.collect()
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
|
|
|
use super::*;
|
|
|
|
|
2024-04-27 06:22:26 +00:00
|
|
|
#[test]
|
|
|
|
fn line_comment() {
|
|
|
|
assert_eq!(
|
|
|
|
lex("// 42").unwrap(),
|
|
|
|
vec![(Token::Comment("42"), (0..5).into())]
|
|
|
|
);
|
|
|
|
|
|
|
|
assert_eq!(
|
|
|
|
lex("1// 42//2").unwrap(),
|
|
|
|
vec![
|
|
|
|
(Token::Integer(1), (0..1).into()),
|
|
|
|
(Token::Comment("42//2"), (1..9).into()),
|
|
|
|
]
|
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
lex("
|
|
|
|
1
|
|
|
|
// 42
|
|
|
|
2
|
|
|
|
")
|
|
|
|
.unwrap(),
|
|
|
|
vec![
|
|
|
|
(Token::Integer(1), (17..18).into()),
|
|
|
|
(Token::Comment("42"), (35..41).into()),
|
|
|
|
(Token::Integer(2), (57..58).into()),
|
|
|
|
]
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn multi_line_comment() {
|
|
|
|
assert_eq!(
|
|
|
|
lex("/* 42 */").unwrap(),
|
|
|
|
vec![(Token::Comment("42"), (0..8).into())]
|
|
|
|
);
|
|
|
|
|
|
|
|
assert_eq!(
|
|
|
|
lex("1/* 42//2 */").unwrap(),
|
|
|
|
vec![
|
|
|
|
(Token::Integer(1), (0..1).into()),
|
|
|
|
(Token::Comment("42//2"), (1..12).into()),
|
|
|
|
]
|
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
lex("
|
|
|
|
1
|
|
|
|
/*
|
|
|
|
42
|
|
|
|
*/
|
|
|
|
2
|
|
|
|
")
|
|
|
|
.unwrap(),
|
|
|
|
vec![
|
|
|
|
(Token::Integer(1), (17..18).into()),
|
|
|
|
(Token::Comment("42"), (35..79).into()),
|
|
|
|
(Token::Integer(2), (96..97).into()),
|
|
|
|
]
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2024-03-09 01:30:26 +00:00
|
|
|
#[test]
|
|
|
|
fn range() {
|
|
|
|
assert_eq!(
|
|
|
|
lex("1..10").unwrap(),
|
|
|
|
vec![
|
|
|
|
(Token::Integer(1), (0..1).into()),
|
|
|
|
(Token::Control(Control::DoubleDot), (1..3).into()),
|
|
|
|
(Token::Integer(10), (3..5).into())
|
|
|
|
]
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2024-03-07 11:33:54 +00:00
|
|
|
#[test]
|
|
|
|
fn math_operators() {
|
|
|
|
assert_eq!(
|
|
|
|
lex("1 + 1").unwrap(),
|
|
|
|
vec![
|
|
|
|
(Token::Integer(1), (0..1).into()),
|
2024-03-20 15:43:47 +00:00
|
|
|
(Token::Operator(Operator::Add), (2..3).into()),
|
2024-03-07 11:33:54 +00:00
|
|
|
(Token::Integer(1), (4..5).into())
|
|
|
|
]
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2024-02-29 02:04:38 +00:00
|
|
|
#[test]
|
|
|
|
fn keywords() {
|
2024-03-20 15:43:47 +00:00
|
|
|
assert_eq!(lex("int").unwrap()[0].0, Token::Keyword(Keyword::Int))
|
2024-02-29 02:04:38 +00:00
|
|
|
}
|
|
|
|
|
2024-02-25 18:49:26 +00:00
|
|
|
#[test]
|
|
|
|
fn identifier() {
|
|
|
|
assert_eq!(lex("x").unwrap()[0].0, Token::Identifier("x"));
|
|
|
|
assert_eq!(lex("foobar").unwrap()[0].0, Token::Identifier("foobar"));
|
|
|
|
assert_eq!(lex("HELLO").unwrap()[0].0, Token::Identifier("HELLO"));
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn r#true() {
|
|
|
|
assert_eq!(lex("true").unwrap()[0].0, Token::Boolean(true));
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn r#false() {
|
|
|
|
assert_eq!(lex("false").unwrap()[0].0, Token::Boolean(false));
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn positive_float() {
|
|
|
|
assert_eq!(lex("0.0").unwrap()[0].0, Token::Float(0.0));
|
|
|
|
assert_eq!(lex("42.0").unwrap()[0].0, Token::Float(42.0));
|
|
|
|
|
|
|
|
let max_float = f64::MAX.to_string() + ".0";
|
|
|
|
|
|
|
|
assert_eq!(lex(&max_float).unwrap()[0].0, Token::Float(f64::MAX));
|
|
|
|
|
|
|
|
let min_positive_float = f64::MIN_POSITIVE.to_string();
|
|
|
|
|
|
|
|
assert_eq!(
|
|
|
|
lex(&min_positive_float).unwrap()[0].0,
|
|
|
|
Token::Float(f64::MIN_POSITIVE)
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn negative_float() {
|
|
|
|
assert_eq!(lex("-0.0").unwrap()[0].0, Token::Float(-0.0));
|
|
|
|
assert_eq!(lex("-42.0").unwrap()[0].0, Token::Float(-42.0));
|
|
|
|
|
|
|
|
let min_float = f64::MIN.to_string() + ".0";
|
|
|
|
|
|
|
|
assert_eq!(lex(&min_float).unwrap()[0].0, Token::Float(f64::MIN));
|
|
|
|
|
|
|
|
let max_negative_float = format!("-{}", f64::MIN_POSITIVE);
|
|
|
|
|
|
|
|
assert_eq!(
|
|
|
|
lex(&max_negative_float).unwrap()[0].0,
|
|
|
|
Token::Float(-f64::MIN_POSITIVE)
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn other_float() {
|
|
|
|
assert_eq!(lex("Infinity").unwrap()[0].0, Token::Float(f64::INFINITY));
|
|
|
|
assert_eq!(
|
|
|
|
lex("-Infinity").unwrap()[0].0,
|
|
|
|
Token::Float(f64::NEG_INFINITY)
|
|
|
|
);
|
|
|
|
|
|
|
|
if let Token::Float(float) = &lex("NaN").unwrap()[0].0 {
|
|
|
|
assert!(float.is_nan());
|
|
|
|
} else {
|
|
|
|
panic!("Expected a float.")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn positive_integer() {
|
|
|
|
for i in 0..10 {
|
|
|
|
let source = i.to_string();
|
|
|
|
let tokens = lex(&source).unwrap();
|
|
|
|
|
|
|
|
assert_eq!(tokens[0].0, Token::Integer(i))
|
|
|
|
}
|
|
|
|
|
|
|
|
assert_eq!(lex("42").unwrap()[0].0, Token::Integer(42));
|
|
|
|
|
|
|
|
let maximum_integer = i64::MAX.to_string();
|
|
|
|
|
|
|
|
assert_eq!(
|
|
|
|
lex(&maximum_integer).unwrap()[0].0,
|
|
|
|
Token::Integer(i64::MAX)
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn negative_integer() {
|
|
|
|
for i in -9..1 {
|
|
|
|
let source = i.to_string();
|
|
|
|
let tokens = lex(&source).unwrap();
|
|
|
|
|
|
|
|
assert_eq!(tokens[0].0, Token::Integer(i))
|
|
|
|
}
|
|
|
|
|
|
|
|
assert_eq!(lex("-42").unwrap()[0].0, Token::Integer(-42));
|
|
|
|
|
|
|
|
let minimum_integer = i64::MIN.to_string();
|
|
|
|
|
|
|
|
assert_eq!(
|
|
|
|
lex(&minimum_integer).unwrap()[0].0,
|
|
|
|
Token::Integer(i64::MIN)
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn double_quoted_string() {
|
|
|
|
assert_eq!(lex("\"\"").unwrap()[0].0, Token::String(""));
|
|
|
|
assert_eq!(lex("\"42\"").unwrap()[0].0, Token::String("42"));
|
|
|
|
assert_eq!(lex("\"foobar\"").unwrap()[0].0, Token::String("foobar"));
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn single_quoted_string() {
|
|
|
|
assert_eq!(lex("''").unwrap()[0].0, Token::String(""));
|
|
|
|
assert_eq!(lex("'42'").unwrap()[0].0, Token::String("42"));
|
|
|
|
assert_eq!(lex("'foobar'").unwrap()[0].0, Token::String("foobar"));
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn grave_quoted_string() {
|
|
|
|
assert_eq!(lex("``").unwrap()[0].0, Token::String(""));
|
|
|
|
assert_eq!(lex("`42`").unwrap()[0].0, Token::String("42"));
|
|
|
|
assert_eq!(lex("`foobar`").unwrap()[0].0, Token::String("foobar"));
|
|
|
|
}
|
|
|
|
}
|