use crate::language::{ ast::{ * },
data::value::Value,
tokenizer::{ TokenBuffer, TokenKind, TokenLiteral, TokenValue, StringFlag },
parser::{ base_utils::{ expect_token,
match_one_of,
Lookahead,
try_expect_token,
try_expect_one_of_tokens },
results::{ ParseResult, ParserError, ParserErrorKind } } };
fn math_operator(kind: TokenKind) -> Option<(AstMathOperator, u8)>
{
match kind
{
TokenKind::Plus => Some((AstMathOperator::Add, 1)),
TokenKind::Minus => Some((AstMathOperator::Subtract, 1)),
TokenKind::Asterisk => Some((AstMathOperator::Multiply, 2)),
TokenKind::Slash => Some((AstMathOperator::Divide, 2)),
TokenKind::Percent => Some((AstMathOperator::Modulo, 2)),
_ => None
}
}
fn parse_math_binary_expression(buffer: &mut TokenBuffer<'_, '_>,
min_precedence: u8) -> ParseResult<AstExpression>
{
let location =
{
let peek = Lookahead::new(buffer);
peek.buffer.next()?.map(|token| token.location)
};
let left = parse_math_primary(buffer)?.ok_or(ParserError
{
location,
kind: ParserErrorKind::ExpectedExpression
})?;
parse_math_binary_tail(buffer, left, min_precedence, false)
}
fn parse_math_primary(buffer: &mut TokenBuffer<'_, '_>) -> ParseResult<Option<AstExpression>>
{
let mut lookahead = Lookahead::new(buffer);
let Some(token) = lookahead.buffer.next()? else { return Ok(None); };
let expression = match token.kind
{
TokenKind::Literal =>
{
let mut string_flag = None;
let value = match token.value
{
TokenValue::Literal(TokenLiteral::Integer(value, _)) =>
{
Value::Integer(value)
},
TokenValue::Literal(TokenLiteral::Float(value, text)) =>
{
Value::Float(value, Some(text))
},
TokenValue::Literal(TokenLiteral::String(value, flag)) =>
{
string_flag = Some(match flag
{
StringFlag::Interpolated =>
AstStringFlag::Interpolated,
StringFlag::NonInterpolated =>
AstStringFlag::NonInterpolated,
});
Value::String(value)
},
_ => return Ok(None)
};
new_ast_literal(token.location, value, string_flag)
},
TokenKind::Identifier =>
{
let TokenValue::Identifier(name) = token.value else { return Ok(None); };
if try_expect_token(&mut *lookahead.buffer, TokenKind::Splat)?.is_some()
{
return Ok(None);
}
new_ast_variable(token.location, name)
},
TokenKind::ParenOpen =>
{
let expression = parse_math_binary_expression(&mut *lookahead.buffer, 0)?;
expect_token(&mut *lookahead.buffer, TokenKind::ParenClose)?;
expression
},
_ => return Ok(None)
};
lookahead.commit();
Ok(Some(expression))
}
fn parse_math_binary_tail(buffer: &mut TokenBuffer<'_, '_>,
mut left: AstExpression,
min_precedence: u8,
allow_glob_fallback: bool) -> ParseResult<AstExpression>
{
let mut matched_operator = false;
loop
{
let mut lookahead = Lookahead::new(buffer);
let Some(token) = lookahead.buffer.next()? else { break; };
if matches!(token.kind, TokenKind::LineBreak | TokenKind::StatementBreak | TokenKind::ParenClose)
{
break;
}
let Some((operator, precedence)) = math_operator(token.kind) else { break; };
if precedence < min_precedence
{
break;
}
let right = match parse_math_primary(&mut *lookahead.buffer)?
{
Some(right) => right,
None =>
{
if allow_glob_fallback
&& !matched_operator
&& matches!(operator, AstMathOperator::Multiply)
{
break;
}
return Err(ParserError
{
location: Some(token.location),
kind: ParserErrorKind::ExpectedExpression
});
}
};
let right = parse_math_binary_tail(&mut *lookahead.buffer, right, precedence + 1, false)?;
left = AstExpression
{
location: left.location.clone(),
kind: AstExpressionKind::MathExpression(operator, Box::new(left), Box::new(right)),
string_flag: None
};
lookahead.commit();
matched_operator = true;
}
Ok(left)
}
fn parse_math_expression(buffer: &mut TokenBuffer<'_, '_>) -> ParseResult<Option<AstExpression>>
{
let mut lookahead = Lookahead::new(buffer);
let starts_with_group =
{
let peek = Lookahead::new(&mut *lookahead.buffer);
matches!(peek.buffer.next()?, Some(token) if token.kind == TokenKind::ParenOpen)
};
let Some(left) = parse_math_primary(&mut *lookahead.buffer)? else { return Ok(None); };
let expression = parse_math_binary_tail(&mut *lookahead.buffer, left, 0, !starts_with_group)?;
if starts_with_group || matches!(&expression.kind, AstExpressionKind::MathExpression(_, _, _))
{
lookahead.commit();
return Ok(Some(expression));
}
Ok(None)
}
fn parse_variable_expression(buffer: &mut TokenBuffer<'_, '_>) -> ParseResult<Option<AstExpression>>
{
let identifier = expect_token(buffer, TokenKind::Identifier)?;
let identifier_value = match identifier.value
{
TokenValue::Identifier(identifier) => identifier,
_ => return Err(ParserError
{
location: Some(identifier.location.clone()),
kind: ParserErrorKind::ExpectedExpression,
})
};
let splat_operator = try_expect_token(buffer, TokenKind::Splat)?;
if let Some(_) = splat_operator
{
Ok(Some(new_ast_variable_splat(identifier.location.clone(), identifier_value)))
}
else
{
Ok(Some(new_ast_variable(identifier.location.clone(), identifier_value)))
}
}
fn parse_symbol_expression(buffer: &mut TokenBuffer<'_, '_>) -> ParseResult<Option<AstExpression>>
{
let symbol = expect_token(buffer, TokenKind::Symbol)?;
let symbol_value = match symbol.value
{
TokenValue::Symbol(symbol) => symbol,
_ => return Err(ParserError
{
location: Some(symbol.location.clone()),
kind: ParserErrorKind::ExpectedExpression,
})
};
let string_flag = if symbol_value.contains('$')
{
Some(AstStringFlag::Interpolated)
}
else
{
Some(AstStringFlag::NonInterpolated)
};
Ok(Some(new_ast_symbol(symbol.location.clone(), symbol_value, string_flag)))
}
fn parse_literal_expression(buffer: &mut TokenBuffer<'_, '_>) -> ParseResult<Option<AstExpression>>
{
let literal = expect_token(buffer, TokenKind::Literal)?;
let literal_value = match literal.value
{
TokenValue::Literal(literal) => literal,
_ => return Err(ParserError
{
location: Some(literal.location.clone()),
kind: ParserErrorKind::ExpectedExpression,
})
};
let mut string_flag = None;
let literal_value = match literal_value
{
TokenLiteral::Integer(value, _) => Value::Integer(value),
TokenLiteral::Float(value, text) => Value::Float(value, Some(text)),
TokenLiteral::Boolean(value) => Value::Boolean(value),
TokenLiteral::String(value, flag) =>
{
string_flag = match flag
{
StringFlag::Interpolated => Some(AstStringFlag::Interpolated),
StringFlag::NonInterpolated => Some(AstStringFlag::NonInterpolated),
};
Value::String(value)
}
};
Ok(Some(new_ast_literal(literal.location.clone(), literal_value, string_flag)))
}
fn parse_lonely_glob_expression(buffer: &mut TokenBuffer<'_, '_>) -> ParseResult<Option<AstExpression>>
{
let glob_operator = try_expect_token(buffer, TokenKind::Glob)?;
if let Some(glob_operator) = glob_operator
{
let location = glob_operator.location.clone();
return Ok(Some(new_ast_symbol(location, "*".to_string(), None)));
}
let star = expect_token(buffer, TokenKind::Asterisk)?;
Ok(Some(new_ast_symbol(star.location.clone(), "*".to_string(), None)))
}
fn parse_operator_to_symbol(buffer: &mut TokenBuffer<'_, '_>) -> ParseResult<Option<AstExpression>>
{
let allowed_operators = [ TokenKind::Let,
TokenKind::Export,
TokenKind::Alias,
TokenKind::Comma,
TokenKind::Sub,
TokenKind::If,
TokenKind::Else,
TokenKind::While,
TokenKind::Loop,
TokenKind::Match,
TokenKind::Return,
TokenKind::Function,
TokenKind::Struct,
TokenKind::Enum,
TokenKind::Import,
TokenKind::TypeDelimiter,
TokenKind::Assign,
TokenKind::Minus,
TokenKind::Plus,
TokenKind::Slash,
TokenKind::Percent,
TokenKind::Scope,
TokenKind::ErrorSource ];
if let Some(operator) = try_expect_one_of_tokens(buffer, &allowed_operators)?
{
let location = operator.location.clone();
return Ok(Some(new_ast_symbol(location, operator.token_value_text(), None)));
}
Ok(None)
}
pub fn parse_expression(buffer: &mut TokenBuffer<'_, '_>) -> ParseResult<Option<AstExpression>>
{
if let Some(expression) = parse_math_expression(buffer)?
{
return Ok(Some(expression));
}
match_one_of(buffer, &[parse_variable_expression,
parse_symbol_expression,
parse_literal_expression,
parse_lonely_glob_expression,
parse_operator_to_symbol])
}
pub fn parse_exec_expression(buffer: &mut TokenBuffer<'_, '_>) -> ParseResult<Option<AstExpression>>
{
match_one_of(buffer, &[parse_variable_expression,
parse_symbol_expression,
parse_literal_expression,
parse_operator_to_symbol])
}