[fine] Starting to parse (ugh)

This commit is contained in:
John Doty 2024-01-01 08:07:29 -08:00
parent 7fccab8f59
commit ece5576fb2
3 changed files with 534 additions and 185 deletions

View file

@ -1 +1,2 @@
pub mod parser;
pub mod tokens; pub mod tokens;

360
oden-script/src/parser.rs Normal file
View file

@ -0,0 +1,360 @@
use crate::tokens::{Token, TokenKind, Tokens};
use std::fmt;
#[derive(PartialEq, Eq)]
pub struct SyntaxError {
pub line: usize,
pub column: usize,
pub message: String,
}
impl SyntaxError {
pub fn new(line: usize, column: usize, message: String) -> Self {
SyntaxError {
line,
column,
message,
}
}
}
impl fmt::Debug for SyntaxError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}:{}: {}", self.line, self.column, self.message)
}
}
impl fmt::Display for SyntaxError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}:{}: {}", self.line, self.column, self.message)
}
}
pub enum Literal {
Float64(f64),
}
pub enum UnaryOp {
Negate,
}
pub enum BinaryOp {
Add,
Subtract,
Mutiply,
Divide,
And,
Or,
}
pub enum Expr {
Literal(Literal),
Unary(UnaryOp, ExprRef),
Binary(BinaryOp, ExprRef, ExprRef),
}
pub struct ExprRef(Option<usize>);
impl ExprRef {
pub fn error() -> Self {
ExprRef(None)
}
}
pub struct SyntaxTree {
pub errors: Vec<SyntaxError>,
expressions: Vec<Expr>,
}
impl SyntaxTree {
pub fn new() -> Self {
SyntaxTree {
errors: Vec::new(),
expressions: Vec::new(),
}
}
pub fn add_error(&mut self, error: SyntaxError) {
self.errors.push(error);
}
pub fn add_expr(&mut self, expr: Expr) -> ExprRef {
let index = self.expressions.len();
self.expressions.push(expr);
ExprRef(Some(index))
}
pub fn dump_expr(&self, expr: &ExprRef) -> String {
match expr.0 {
Some(idx) => {
let expr = &self.expressions[idx];
match expr {
Expr::Literal(lit) => match lit {
Literal::Float64(f) => f.to_string(),
},
Expr::Unary(op, e) => {
let op = match op {
UnaryOp::Negate => "-",
};
format!("({op} {})", self.dump_expr(e))
}
Expr::Binary(op, l, r) => {
let op = match op {
BinaryOp::Add => "+",
BinaryOp::Subtract => "-",
BinaryOp::Mutiply => "*",
BinaryOp::Divide => "/",
BinaryOp::And => "and",
BinaryOp::Or => "or",
};
format!("({op} {} {})", self.dump_expr(l), self.dump_expr(r))
}
}
}
None => "<|EOF|>".to_string(),
}
}
}
// BINDING POWERS. When parsing expressions we only accept expressions that
// meet a minimum binding power. (This is like "precedence" but I just super
// don't like that terminology.)
const ASSIGNMENT_POWER: u8 = 0; // =
const OR_POWER: u8 = 1; // or
const AND_POWER: u8 = 2; // and
const EQUALITY_POWER: u8 = 3; // == !=
const COMPARISON_POWER: u8 = 4; // < > <= >=
const TERM_POWER: u8 = 5; // + -
const FACTOR_POWER: u8 = 6; // * /
const UNARY_POWER: u8 = 7; // ! -
// const CALL_POWER: u8 = 8; // . ()
// const PRIMARY_POWER: u8 = 9;
fn token_power<'a>(token: &Option<Token<'a>>) -> Option<u8> {
let token = match token {
Some(t) => t,
None => return None,
};
match token.kind() {
TokenKind::Equal => Some(ASSIGNMENT_POWER),
TokenKind::Or => Some(OR_POWER),
TokenKind::And => Some(AND_POWER),
TokenKind::EqualEqual | TokenKind::BangEqual => Some(EQUALITY_POWER),
TokenKind::Less | TokenKind::Greater | TokenKind::GreaterEqual | TokenKind::LessEqual => {
Some(COMPARISON_POWER)
}
TokenKind::Plus | TokenKind::Minus => Some(TERM_POWER),
TokenKind::Star | TokenKind::Slash => Some(FACTOR_POWER),
_ => None,
}
}
pub struct Parser<'a> {
tokens: Tokens<'a>,
tree: SyntaxTree,
current: Option<Token<'a>>,
previous: Option<Token<'a>>,
panic_mode: bool,
}
impl<'a> Parser<'a> {
pub fn new(source: &'a str) -> Self {
let mut parser = Parser {
tokens: Tokens::new(source),
tree: SyntaxTree::new(),
current: None,
previous: None,
panic_mode: false,
};
parser.advance();
parser
}
pub fn parse(mut self) -> (SyntaxTree, ExprRef) {
let expr = self.expression();
self.consume(None, "expected end of expression");
(self.tree, expr)
}
fn expression(&mut self) -> ExprRef {
self.expression_with_power(0)
}
fn expression_with_power(&mut self, minimum_power: u8) -> ExprRef {
self.advance();
let mut expr = self.prefix_expression();
loop {
let power = match token_power(&self.current) {
Some(p) => p,
None => break, // EOF, end of expression?
};
if power < minimum_power {
break;
}
self.advance();
expr = self.infix_expression(power, expr);
}
expr
}
fn prefix_expression(&mut self) -> ExprRef {
let token = self.previous.as_ref();
match token {
Some(token) => match token.kind() {
TokenKind::LeftParen => self.grouping(),
TokenKind::Number => self.number(),
TokenKind::Minus => self.unary(),
_ => {
self.error("expected an expression");
ExprRef::error()
}
},
None => {
self.error("expected an expression");
ExprRef::error()
}
}
}
fn infix_expression(&mut self, power: u8, left: ExprRef) -> ExprRef {
let kind = self.previous.as_ref().unwrap().kind();
match kind {
TokenKind::Plus | TokenKind::Minus | TokenKind::Star | TokenKind::Slash => {
self.binary(power, left)
}
_ => panic!("Unknown infix operator, dispatch error?"),
}
}
fn number(&mut self) -> ExprRef {
let token = self.previous.as_ref().unwrap();
// What kind is it? For now let's just ... make it good.
match token.as_str().parse::<f64>() {
Ok(v) => self.tree.add_expr(Expr::Literal(Literal::Float64(v))),
Err(e) => {
self.error(format!("invalid f64: {e}"));
ExprRef::error()
}
}
}
fn grouping(&mut self) -> ExprRef {
let result = self.number();
self.consume(
Some(TokenKind::RightParen),
"expected ')' after an expression",
);
result
}
fn unary(&mut self) -> ExprRef {
let kind = self.previous.as_ref().unwrap().kind();
let expr = self.expression_with_power(UNARY_POWER);
let op = match kind {
TokenKind::Minus => UnaryOp::Negate,
_ => panic!("unsuitable unary: {:?}: no op", kind),
};
self.tree.add_expr(Expr::Unary(op, expr))
}
fn binary(&mut self, power: u8, left: ExprRef) -> ExprRef {
let right = self.expression_with_power(power + 1);
let op = match self.previous.as_ref().unwrap().kind() {
TokenKind::Plus => BinaryOp::Add,
TokenKind::Minus => BinaryOp::Subtract,
TokenKind::Star => BinaryOp::Mutiply,
TokenKind::Slash => BinaryOp::Divide,
TokenKind::And => BinaryOp::And,
TokenKind::Or => BinaryOp::Or,
_ => panic!("unsuitable binary: {:?}: no op", self.previous),
};
self.tree.add_expr(Expr::Binary(op, left, right))
}
fn advance(&mut self) {
self.previous = self.current.take();
loop {
self.current = self.tokens.next();
match &self.current {
Some(token) if token.kind() == TokenKind::Error => {
self.error_at_current(token.clone())
}
_ => break,
}
}
}
fn consume(&mut self, kind: Option<TokenKind>, error: &str) {
match (&self.current, kind) {
(Some(token), Some(kind)) if token.kind() == kind => self.advance(),
(None, None) => (),
_ => {
self.error_at_current(error);
}
}
}
fn error<T>(&mut self, message: T)
where
T: Into<String>,
{
self.error_at(self.previous.clone(), message)
}
fn error_at_current<T>(&mut self, message: T)
where
T: Into<String>,
{
self.error_at(self.current.clone(), message)
}
fn error_at<T>(&mut self, token: Option<Token<'a>>, message: T)
where
T: Into<String>,
{
if self.panic_mode {
return;
}
self.panic_mode = true;
let message: String = message.into();
let (line, column) = self.tokens.token_position(&token);
let mut final_message = "Error ".to_string();
match token {
None => final_message.push_str("at end"),
Some(t) => {
if t.kind() != TokenKind::Error {
final_message.push_str("at '");
final_message.push_str(t.as_str());
final_message.push_str("'");
}
}
}
final_message.push_str(": ");
final_message.push_str(&message);
self.tree
.add_error(SyntaxError::new(line, column, final_message));
}
}
#[cfg(test)]
mod tests {
use super::*;
use pretty_assertions::assert_eq;
#[test]
pub fn number_expressions() {
// How am I going to test this?
let (tree, expr) = Parser::new("23.5").parse();
assert_eq!(Vec::<SyntaxError>::new(), tree.errors);
assert_eq!("23.5", tree.dump_expr(&expr));
}
}

View file

@ -1,5 +1,5 @@
#[derive(Debug, PartialEq, Eq)] #[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum TokenKind<'a> { pub enum TokenKind {
LeftBrace, LeftBrace,
RightBrace, RightBrace,
LeftBracket, LeftBracket,
@ -23,9 +23,9 @@ pub enum TokenKind<'a> {
Less, Less,
LessEqual, LessEqual,
Identifier(&'a str), // TODO Identifier,
String(&'a str), String,
Number(&'a str), Number,
And, And,
Async, Async,
@ -47,80 +47,54 @@ pub enum TokenKind<'a> {
While, While,
Yield, Yield,
Error(String), Error,
} }
#[derive(Debug, PartialEq, Eq)] #[derive(Debug, PartialEq, Eq, Clone)]
pub struct Token<'a> { pub struct Token<'a> {
kind: TokenKind<'a>, kind: TokenKind,
start: usize, start: usize,
value: Result<&'a str, String>,
} }
impl<'a> Token<'a> { impl<'a> Token<'a> {
pub fn new(start: usize, kind: TokenKind<'a>) -> Self { pub fn new(kind: TokenKind, start: usize, value: &'a str) -> Self {
Token { kind, start } Token {
kind,
start,
value: Ok(value),
}
}
pub fn error(start: usize, message: String) -> Self {
Token {
kind: TokenKind::Error,
start,
value: Err(message),
}
}
pub fn kind(&self) -> TokenKind {
self.kind
} }
pub fn as_str<'b>(&'b self) -> &'a str pub fn as_str<'b>(&'b self) -> &'a str
where where
'b: 'a, 'b: 'a,
{ {
use TokenKind::*; match &self.value {
match &self.kind { Ok(v) => v,
LeftBrace => "{", Err(e) => &e,
RightBrace => "}",
LeftBracket => "[",
RightBracket => "]",
LeftParen => "(",
RightParen => ")",
Comma => ",",
Dot => ".",
Minus => "-",
Plus => "+",
Semicolon => ";",
Slash => "/",
Star => "*",
Bang => "+",
BangEqual => "!=",
Equal => "=",
EqualEqual => "==",
Greater => ">",
GreaterEqual => ">=",
Less => "<",
LessEqual => "<=",
Identifier(v) => v,
String(v) => v,
Number(v) => v,
And => "and",
Async => "async",
Await => "await",
Class => "class",
Else => "else",
False => "false",
For => "for",
From => "from",
Fun => "fun",
If => "if",
Let => "let",
Or => "or",
Print => "print",
Return => "return",
Select => "select",
This => "this",
True => "true",
While => "while",
Yield => "yield",
Error(e) => e,
} }
} }
} }
impl<'a> Into<String> for Token<'a> {
fn into(self) -> String {
self.as_str().to_string()
}
}
pub struct Tokens<'a> { pub struct Tokens<'a> {
source: &'a str, source: &'a str,
chars: std::str::CharIndices<'a>, chars: std::str::CharIndices<'a>,
@ -140,8 +114,17 @@ impl<'a> Tokens<'a> {
result result
} }
pub fn token_position(&self, token: &Token) -> (usize, usize) { /// Return the position of the given token as a (line, column) pair. By
let line_end_index = match self.newlines.binary_search(&token.start) { /// convention, lines are 1-based and columns are 0-based. Also, in
/// keeping with the iterator-nature of the tokenizer, `None` here
/// indicates end-of-file, and will return the position of the end of the
/// file.
pub fn token_position(&self, token: &Option<Token>) -> (usize, usize) {
let start = match token {
Some(t) => t.start,
None => self.source.len(),
};
let line_end_index = match self.newlines.binary_search(&start) {
Ok(index) => index, Ok(index) => index,
Err(index) => index, Err(index) => index,
}; };
@ -151,15 +134,16 @@ impl<'a> Tokens<'a> {
self.newlines[line_end_index - 1] + 1 self.newlines[line_end_index - 1] + 1
}; };
let line_number = line_end_index + 1; let line_number = line_end_index + 1;
let column_offset = token.start - line_start_pos; let column_offset = start - line_start_pos;
(line_number, column_offset) (line_number, column_offset)
} }
fn token(&self, start: usize, kind: TokenKind<'a>) -> Token<'a> { fn token(&self, start: usize, kind: TokenKind) -> Token<'a> {
Token::new(start, kind) let value = &self.source[start..self.pos()];
Token::new(kind, start, value)
} }
fn number(&mut self, start: usize) -> TokenKind<'a> { fn number(&mut self, start: usize) -> Token<'a> {
// First, the main part. // First, the main part.
loop { loop {
if !self.matches_digit() { if !self.matches_digit() {
@ -198,9 +182,10 @@ impl<'a> Tokens<'a> {
if !saw_digit { if !saw_digit {
// This is just a broken number. // This is just a broken number.
let slice = &self.source[start..self.pos()]; let slice = &self.source[start..self.pos()];
return TokenKind::Error(format!( return Token::error(
"Invalid floating-point literal: {slice}" start,
)); format!("Invalid floating-point literal: {slice}"),
);
} }
} }
} else { } else {
@ -209,13 +194,13 @@ impl<'a> Tokens<'a> {
} }
} }
TokenKind::Number(&self.source[start..self.pos()]) self.token(start, TokenKind::Number)
} }
fn string(&mut self, start: usize, delimiter: char) -> TokenKind<'a> { fn string(&mut self, start: usize, delimiter: char) -> Token<'a> {
while !self.matches(delimiter) { while !self.matches(delimiter) {
if self.eof() { if self.eof() {
return TokenKind::Error("Unterminated string constant".to_string()); return Token::error(start, "Unterminated string constant".to_string());
} }
if self.matches('\\') { if self.matches('\\') {
self.advance(); self.advance();
@ -224,20 +209,12 @@ impl<'a> Tokens<'a> {
} }
} }
TokenKind::String(&self.source[start..self.pos()]) self.token(start, TokenKind::String)
} }
fn identifier(&mut self, start: usize) -> TokenKind<'a> { fn identifier_token_kind(ident: &str) -> TokenKind {
loop { match ident.chars().nth(0).unwrap() {
// TODO: Use unicode identifier classes instead 'a' => {
if !self.matches_next(|c| c.is_ascii_alphanumeric() || c == '_') {
break;
}
}
let ident = &self.source[start..self.pos()];
match ident.chars().nth(0) {
Some('a') => {
if ident == "and" { if ident == "and" {
return TokenKind::And; return TokenKind::And;
} }
@ -248,17 +225,17 @@ impl<'a> Tokens<'a> {
return TokenKind::Await; return TokenKind::Await;
} }
} }
Some('c') => { 'c' => {
if ident == "class" { if ident == "class" {
return TokenKind::Class; return TokenKind::Class;
} }
} }
Some('e') => { 'e' => {
if ident == "else" { if ident == "else" {
return TokenKind::Else; return TokenKind::Else;
} }
} }
Some('f') => { 'f' => {
if ident == "false" { if ident == "false" {
return TokenKind::False; return TokenKind::False;
} }
@ -272,37 +249,37 @@ impl<'a> Tokens<'a> {
return TokenKind::Fun; return TokenKind::Fun;
} }
} }
Some('i') => { 'i' => {
if ident == "if" { if ident == "if" {
return TokenKind::If; return TokenKind::If;
} }
} }
Some('l') => { 'l' => {
if ident == "let" { if ident == "let" {
return TokenKind::Let; return TokenKind::Let;
} }
} }
Some('o') => { 'o' => {
if ident == "or" { if ident == "or" {
return TokenKind::Or; return TokenKind::Or;
} }
} }
Some('p') => { 'p' => {
if ident == "print" { if ident == "print" {
return TokenKind::Print; return TokenKind::Print;
} }
} }
Some('r') => { 'r' => {
if ident == "return" { if ident == "return" {
return TokenKind::Return; return TokenKind::Return;
} }
} }
Some('s') => { 's' => {
if ident == "select" { if ident == "select" {
return TokenKind::Select; return TokenKind::Select;
} }
} }
Some('t') => { 't' => {
if ident == "this" { if ident == "this" {
return TokenKind::This; return TokenKind::This;
} }
@ -310,12 +287,12 @@ impl<'a> Tokens<'a> {
return TokenKind::True; return TokenKind::True;
} }
} }
Some('w') => { 'w' => {
if ident == "while" { if ident == "while" {
return TokenKind::While; return TokenKind::While;
} }
} }
Some('y') => { 'y' => {
if ident == "yield" { if ident == "yield" {
return TokenKind::Yield; return TokenKind::Yield;
} }
@ -323,7 +300,20 @@ impl<'a> Tokens<'a> {
_ => (), _ => (),
} }
TokenKind::Identifier(ident) TokenKind::Identifier
}
fn identifier(&mut self, start: usize) -> Token<'a> {
loop {
// TODO: Use unicode identifier classes instead
if !self.matches_next(|c| c.is_ascii_alphanumeric() || c == '_') {
break;
}
}
let ident = &self.source[start..self.pos()];
let kind = Self::identifier_token_kind(ident);
Token::new(kind, start, ident)
} }
fn matches(&mut self, ch: char) -> bool { fn matches(&mut self, ch: char) -> bool {
@ -342,14 +332,9 @@ impl<'a> Tokens<'a> {
{ {
if let Some((_, next_ch)) = self.next_char { if let Some((_, next_ch)) = self.next_char {
if f(next_ch) { if f(next_ch) {
eprintln!("MATCHES NEXT: {next_ch}");
self.advance(); self.advance();
return true; return true;
} else {
eprintln!("NOT MATCHES NEXT: {next_ch}");
} }
} else {
eprintln!("E O F");
} }
false false
} }
@ -361,7 +346,6 @@ impl<'a> Tokens<'a> {
fn advance(&mut self) -> Option<(usize, char)> { fn advance(&mut self) -> Option<(usize, char)> {
let result = self.next_char; let result = self.next_char;
self.next_char = self.chars.next(); self.next_char = self.chars.next();
eprintln!("NEXT: {:?}", self.next_char);
result result
} }
@ -399,57 +383,57 @@ impl<'a> std::iter::Iterator for Tokens<'a> {
}; };
let token = match c { let token = match c {
'{' => TokenKind::LeftBrace, '{' => self.token(pos, TokenKind::LeftBrace),
'}' => TokenKind::RightBrace, '}' => self.token(pos, TokenKind::RightBrace),
'[' => TokenKind::LeftBracket, '[' => self.token(pos, TokenKind::LeftBracket),
']' => TokenKind::RightBracket, ']' => self.token(pos, TokenKind::RightBracket),
'(' => TokenKind::LeftParen, '(' => self.token(pos, TokenKind::LeftParen),
')' => TokenKind::RightParen, ')' => self.token(pos, TokenKind::RightParen),
',' => TokenKind::Comma, ',' => self.token(pos, TokenKind::Comma),
'.' => TokenKind::Dot, '.' => self.token(pos, TokenKind::Dot),
'-' => { '-' => {
if self.matches_next(|c| c.is_ascii_digit()) { if self.matches_next(|c| c.is_ascii_digit()) {
self.number(pos) self.number(pos)
} else { } else {
TokenKind::Minus self.token(pos, TokenKind::Minus)
} }
} }
'+' => { '+' => {
if self.matches_next(|c| c.is_ascii_digit()) { if self.matches_next(|c| c.is_ascii_digit()) {
self.number(pos) self.number(pos)
} else { } else {
TokenKind::Plus self.token(pos, TokenKind::Plus)
} }
} }
';' => TokenKind::Semicolon, ';' => self.token(pos, TokenKind::Semicolon),
'/' => TokenKind::Slash, '/' => self.token(pos, TokenKind::Slash),
'*' => TokenKind::Star, '*' => self.token(pos, TokenKind::Star),
'!' => { '!' => {
if self.matches('=') { if self.matches('=') {
TokenKind::BangEqual self.token(pos, TokenKind::BangEqual)
} else { } else {
TokenKind::Bang self.token(pos, TokenKind::Bang)
} }
} }
'=' => { '=' => {
if self.matches('=') { if self.matches('=') {
TokenKind::EqualEqual self.token(pos, TokenKind::EqualEqual)
} else { } else {
TokenKind::Equal self.token(pos, TokenKind::Equal)
} }
} }
'>' => { '>' => {
if self.matches('=') { if self.matches('=') {
TokenKind::GreaterEqual self.token(pos, TokenKind::GreaterEqual)
} else { } else {
TokenKind::Greater self.token(pos, TokenKind::Greater)
} }
} }
'<' => { '<' => {
if self.matches('=') { if self.matches('=') {
TokenKind::LessEqual self.token(pos, TokenKind::LessEqual)
} else { } else {
TokenKind::Less self.token(pos, TokenKind::Less)
} }
} }
'\'' => self.string(pos, '\''), '\'' => self.string(pos, '\''),
@ -460,11 +444,10 @@ impl<'a> std::iter::Iterator for Tokens<'a> {
} else if c.is_ascii_alphabetic() || c == '_' { } else if c.is_ascii_alphabetic() || c == '_' {
self.identifier(pos) self.identifier(pos)
} else { } else {
TokenKind::Error(format!("Unexpected character '{c}'")) Token::error(pos, format!("Unexpected character '{c}'"))
} }
} }
}; };
let token = self.token(pos, token);
Some(token) Some(token)
} }
} }
@ -480,7 +463,12 @@ mod tests {
fn $name() { fn $name() {
use TokenKind::*; use TokenKind::*;
let tokens: Vec<_> = Tokens::new($input).collect(); let tokens: Vec<_> = Tokens::new($input).collect();
let expected = vec![$($s),*];
let expected: Vec<Token> = (vec![$($s),*])
.into_iter()
.map(|t| Token::new(t.1, t.0, t.2))
.collect();
assert_eq!(expected, tokens); assert_eq!(expected, tokens);
} }
} }
@ -489,81 +477,81 @@ mod tests {
test_tokens!( test_tokens!(
numbers, numbers,
"1 1.0 1.2e7 2.3e+7 3.3E-06 7_6 8.0e_8", "1 1.0 1.2e7 2.3e+7 3.3E-06 7_6 8.0e_8",
Token::new(0, Number("1")), (0, Number, "1"),
Token::new(2, Number("1.0")), (2, Number, "1.0"),
Token::new(6, Number("1.2e7")), (6, Number, "1.2e7"),
Token::new(12, Number("2.3e+7")), (12, Number, "2.3e+7"),
Token::new(19, Number("3.3E-06")), (19, Number, "3.3E-06"),
Token::new(27, Number("7_6")), (27, Number, "7_6"),
Token::new(31, Number("8.0e_8")) (31, Number, "8.0e_8")
); );
test_tokens!( test_tokens!(
identifiers, identifiers,
"asdf x _123 a_23 x3a and or yield async await class else false for from", "asdf x _123 a_23 x3a and or yield async await class else false for from",
Token::new(0, Identifier("asdf")), (0, Identifier, "asdf"),
Token::new(5, Identifier("x")), (5, Identifier, "x"),
Token::new(7, Identifier("_123")), (7, Identifier, "_123"),
Token::new(12, Identifier("a_23")), (12, Identifier, "a_23"),
Token::new(17, Identifier("x3a")), (17, Identifier, "x3a"),
Token::new(21, And), (21, And, "and"),
Token::new(25, Or), (25, Or, "or"),
Token::new(28, Yield), (28, Yield, "yield"),
Token::new(34, Async), (34, Async, "async"),
Token::new(40, Await), (40, Await, "await"),
Token::new(46, Class), (46, Class, "class"),
Token::new(52, Else), (52, Else, "else"),
Token::new(57, False), (57, False, "false"),
Token::new(63, For), (63, For, "for"),
Token::new(67, From) (67, From, "from")
); );
test_tokens!( test_tokens!(
more_keywords, more_keywords,
"fun if let print return select this true while truewhile", "fun if let print return select this true while truewhile",
Token::new(0, Fun), (0, Fun, "fun"),
Token::new(4, If), (4, If, "if"),
Token::new(7, Let), (7, Let, "let"),
Token::new(11, Print), (11, Print, "print"),
Token::new(17, Return), (17, Return, "return"),
Token::new(24, Select), (24, Select, "select"),
Token::new(31, This), (31, This, "this"),
Token::new(36, True), (36, True, "true"),
Token::new(41, While), (41, While, "while"),
Token::new(47, Identifier("truewhile")) (47, Identifier, "truewhile")
); );
test_tokens!( test_tokens!(
strings, strings,
r#"'this is a string that\'s great!\r\n' "foo's" 'bar"s' "#, r#"'this is a string that\'s great!\r\n' "foo's" 'bar"s' "#,
Token::new(0, String(r#"'this is a string that\'s great!\r\n'"#)), (0, String, r#"'this is a string that\'s great!\r\n'"#),
Token::new(38, String(r#""foo's""#)), (38, String, r#""foo's""#),
Token::new(46, String("'bar\"s'")) (46, String, "'bar\"s'")
); );
test_tokens!( test_tokens!(
symbols, symbols,
"{ } ( ) [ ] . ! != < <= > >= = == , - + * / ;", "{ } ( ) [ ] . ! != < <= > >= = == , - + * / ;",
Token::new(0, LeftBrace), (0, LeftBrace, "{"),
Token::new(2, RightBrace), (2, RightBrace, "}"),
Token::new(4, LeftParen), (4, LeftParen, "("),
Token::new(6, RightParen), (6, RightParen, ")"),
Token::new(8, LeftBracket), (8, LeftBracket, "["),
Token::new(10, RightBracket), (10, RightBracket, "]"),
Token::new(12, Dot), (12, Dot, "."),
Token::new(14, Bang), (14, Bang, "!"),
Token::new(16, BangEqual), (16, BangEqual, "!="),
Token::new(19, Less), (19, Less, "<"),
Token::new(21, LessEqual), (21, LessEqual, "<="),
Token::new(24, Greater), (24, Greater, ">"),
Token::new(26, GreaterEqual), (26, GreaterEqual, ">="),
Token::new(29, Equal), (29, Equal, "="),
Token::new(31, EqualEqual), (31, EqualEqual, "=="),
Token::new(34, Comma), (34, Comma, ","),
Token::new(36, Minus), (36, Minus, "-"),
Token::new(38, Plus), (38, Plus, "+"),
Token::new(40, Star), (40, Star, "*"),
Token::new(42, Slash), (42, Slash, "/"),
Token::new(44, Semicolon) (44, Semicolon, ";")
); );
} }