use logos::Span;
use serde_json::{Number, Value};
use crate::{
error::{ErrorInfo, SourcePos, SyntaxError},
lexer::{Lexer, Parameters, DoubleQuoteString, SingleQuoteString, Token},
parser::{
ast::{Call, CallTarget, Element, ParameterValue},
path, ParseState,
},
SyntaxResult,
};
#[derive(Eq, PartialEq)]
pub(crate) enum CallParseContext {
Block,
Raw,
Statement,
ScopeStatement,
}
#[derive(Eq, PartialEq)]
enum CallContext {
Call,
SubExpr,
}
enum StringType {
Double,
Single,
}
fn string_literal<'source>(
source: &'source str,
lexer: &mut Lexer<'source>,
state: &mut ParseState,
current: (Parameters, Span),
string_type: StringType,
) -> SyntaxResult<Value> {
let (_lex, span) = current;
let str_start = span.end;
let mut str_end = span.end;
while let Some(token) = lexer.next() {
match string_type {
StringType::Double => {
match token {
Token::DoubleQuoteString(lex, span) => match &lex {
DoubleQuoteString::End => {
let str_value = &source[str_start..str_end];
return Ok(Value::String(str_value.to_string()));
}
_ => {
*state.byte_mut() = span.end;
str_end = span.end;
}
},
_ => panic!("Expecting string literal token"),
}
}
StringType::Single => {
match token {
Token::SingleQuoteString(lex, span) => match &lex {
SingleQuoteString::End => {
let str_value = &source[str_start..str_end];
return Ok(Value::String(str_value.to_string()));
}
_ => {
*state.byte_mut() = span.end;
str_end = span.end;
}
},
_ => panic!("Expecting string literal token"),
}
}
}
}
panic!("Failed to parse string literal");
}
fn json_literal<'source>(
source: &'source str,
lexer: &mut Lexer<'source>,
state: &mut ParseState,
current: (Parameters, Span),
) -> SyntaxResult<Value> {
let (lex, span) = current;
let value = match lex {
Parameters::Null => Value::Null,
Parameters::True => Value::Bool(true),
Parameters::False => Value::Bool(false),
Parameters::Number => {
let num: Number = source[span].parse().unwrap();
Value::Number(num)
}
Parameters::DoubleQuoteString => {
string_literal(source, lexer, state, (lex, span), StringType::Double)?
}
Parameters::SingleQuoteString => {
string_literal(source, lexer, state, (lex, span), StringType::Single)?
}
_ => {
panic!("Expecting JSON literal token.");
}
};
Ok(value)
}
fn value<'source>(
source: &'source str,
lexer: &mut Lexer<'source>,
state: &mut ParseState,
current: (Parameters, Span),
) -> SyntaxResult<(ParameterValue<'source>, Option<Token>)> {
let (lex, span) = current;
match &lex {
Parameters::ExplicitThisKeyword
| Parameters::ExplicitThisDotSlash
| Parameters::Identifier
| Parameters::LocalIdentifier
| Parameters::ParentRef
| Parameters::ArrayAccess => {
let (mut path, token) =
path::parse(source, lexer, state, (lex, span))?;
if let Some(path) = path.take() {
return Ok((ParameterValue::Path(path), token));
}
}
Parameters::StartSubExpression => {
let (call, token) = sub_expr(source, lexer, state, span)?;
if !call.is_closed() {
panic!("Sub expression was not terminated");
}
return Ok((ParameterValue::SubExpr(call), token));
}
Parameters::DoubleQuoteString
| Parameters::SingleQuoteString
| Parameters::Number
| Parameters::True
| Parameters::False
| Parameters::Null => {
let value = json_literal(source, lexer, state, (lex, span))?;
return Ok((ParameterValue::Json(value), lexer.next()));
}
_ => panic!("Unexpected token while parsing value! {:?}", lex),
}
panic!("Expecting value!");
}
fn key_value<'source>(
source: &'source str,
lexer: &mut Lexer<'source>,
state: &mut ParseState,
call: &mut Call<'source>,
current: (Parameters, Span),
) -> SyntaxResult<Option<Token>> {
let (_lex, span) = current;
let key = &source[span.start..span.end - 1];
let mut next: Option<Token> = None;
if let Some(token) = lexer.next() {
match token {
Token::Parameters(lex, span) => {
let (value, token) = value(source, lexer, state, (lex, span))?;
call.add_hash(key, value);
next = token;
}
_ => panic!("Expecting parameter token for key/value pair!"),
}
}
while let Some(token) = next {
match token {
Token::Parameters(lex, span) => match &lex {
Parameters::WhiteSpace | Parameters::Newline => {
if lex == Parameters::Newline {
*state.line_mut() += 1;
}
}
Parameters::HashKey => {
return key_value(source, lexer, state, call, (lex, span));
}
Parameters::End => {
call.exit(span);
return Ok(None);
}
_ => {
panic!("Unexpected parameter token parsing hash parameters")
}
},
_ => panic!("Unexpected token whilst parsing hash parameters"),
}
next = lexer.next();
}
Ok(None)
}
fn arguments<'source>(
source: &'source str,
lexer: &mut Lexer<'source>,
state: &mut ParseState,
call: &mut Call<'source>,
next: Option<Token>,
context: CallContext,
) -> SyntaxResult<Option<Token>> {
if let Some(token) = next {
match token {
Token::Parameters(lex, span) => {
match &lex {
Parameters::WhiteSpace | Parameters::Newline => {
if lex == Parameters::Newline {
*state.line_mut() += 1;
}
let next = lexer.next();
return arguments(
source, lexer, state, call, next, context,
);
}
Parameters::Partial => {
panic!("Partial indicator (>) must be the first part of a call statement");
}
Parameters::ElseKeyword => {}
Parameters::ExplicitThisKeyword
| Parameters::ExplicitThisDotSlash
| Parameters::Identifier
| Parameters::LocalIdentifier
| Parameters::ParentRef
| Parameters::ArrayAccess => {
let (value, token) =
value(source, lexer, state, (lex, span))?;
call.add_argument(value);
return arguments(
source, lexer, state, call, token, context,
);
}
Parameters::HashKey => {
return key_value(
source,
lexer,
state,
call,
(lex, span),
);
}
Parameters::StartSubExpression => {
let (value, token) =
value(source, lexer, state, (lex, span))?;
call.add_argument(value);
return arguments(
source, lexer, state, call, token, context,
);
}
Parameters::DoubleQuoteString
| Parameters::SingleQuoteString
| Parameters::Number
| Parameters::True
| Parameters::False
| Parameters::Null => {
let (value, token) =
value(source, lexer, state, (lex, span))?;
call.add_argument(value);
return arguments(
source, lexer, state, call, token, context,
);
}
Parameters::PathDelimiter => {
panic!("Unexpected path delimiter");
}
Parameters::EndSubExpression => {
if context == CallContext::SubExpr {
call.exit(span);
return Ok(lexer.next());
} else {
panic!("Unexpected end of sub expression");
}
}
Parameters::Error => {
panic!("Unexpected token");
}
Parameters::End => {
call.exit(span);
return Ok(None);
}
}
}
_ => {
panic!("Expecting parameter token");
}
}
}
Ok(None)
}
fn target<'source>(
source: &'source str,
lexer: &mut Lexer<'source>,
state: &mut ParseState,
call: &mut Call<'source>,
mut next: Option<Token>,
context: CallContext,
) -> SyntaxResult<Option<Token>> {
while let Some(token) = next {
match token {
Token::Parameters(lex, span) => {
match &lex {
Parameters::WhiteSpace | Parameters::Newline => {
if lex == Parameters::Newline {
*state.line_mut() += 1;
}
}
Parameters::ElseKeyword => {
panic!("Got else keyword parsing call target");
}
Parameters::ExplicitThisKeyword
| Parameters::ExplicitThisDotSlash
| Parameters::Identifier
| Parameters::LocalIdentifier
| Parameters::ParentRef
| Parameters::ArrayAccess
| Parameters::PathDelimiter => {
let (mut path, token) =
path::parse(source, lexer, state, (lex, span))?;
if let Some(path) = path.take() {
call.set_target(CallTarget::Path(path));
}
return Ok(token);
}
Parameters::StartSubExpression => {
if context == CallContext::SubExpr {
panic!("Sub expressions must use a path or identifier for the target");
}
let (sub_call, token) =
sub_expr(source, lexer, state, span)?;
call.set_target(CallTarget::SubExpr(Box::new(
sub_call,
)));
return Ok(token);
}
Parameters::End => {
if !call.has_target() && !call.is_conditional() {
return Err(SyntaxError::EmptyStatement(
ErrorInfo::new(
source,
state.file_name(),
SourcePos::from((
state.line(),
state.byte(),
)),
)
.into(),
));
}
call.exit(span);
return Ok(None);
}
_ => {
panic!(
"Unexpected token parsing call target {:?}",
lex
);
}
}
}
_ => {
panic!("Expecting parameter token, got {:?}", token);
}
}
next = lexer.next();
}
Ok(None)
}
fn flags<'source>(
_source: &'source str,
lexer: &mut Lexer<'source>,
state: &mut ParseState,
call: &mut Call<'source>,
mut next: Option<Token>,
) -> SyntaxResult<Option<Token>> {
while let Some(token) = next {
match token {
Token::Parameters(lex, span) => match &lex {
Parameters::WhiteSpace | Parameters::Newline => {
if lex == Parameters::Newline {
*state.line_mut() += 1;
}
}
Parameters::Partial => {
call.set_partial(true);
return Ok(lexer.next());
}
Parameters::ElseKeyword => {
call.set_conditional(true);
return Ok(lexer.next());
}
_ => return Ok(Some(Token::Parameters(lex, span))),
},
_ => return Ok(Some(token)),
}
next = lexer.next();
}
Ok(None)
}
pub(crate) fn sub_expr<'source>(
source: &'source str,
lexer: &mut Lexer<'source>,
state: &mut ParseState,
open: Span,
) -> SyntaxResult<(Call<'source>, Option<Token>)> {
*state.byte_mut() = open.end;
let mut call = Call::new(source, open);
let next = lexer.next();
let next =
target(source, lexer, state, &mut call, next, CallContext::SubExpr)?;
let next =
arguments(source, lexer, state, &mut call, next, CallContext::SubExpr)?;
if !call.is_closed() {
panic!("Sub expression statement was not terminated");
}
Ok((call, next))
}
pub(crate) fn parse<'source>(
source: &'source str,
lexer: &mut Lexer<'source>,
state: &mut ParseState,
open: Span,
_parse_context: CallParseContext,
) -> SyntaxResult<Call<'source>> {
*state.byte_mut() = open.end;
let mut call = Call::new(source, open);
let next = lexer.next();
let next = flags(source, lexer, state, &mut call, next)?;
if call.is_partial() && call.is_conditional() {
panic!("Partials and conditionals may not be combined.");
}
let next =
target(source, lexer, state, &mut call, next, CallContext::Call)?;
let _next =
arguments(source, lexer, state, &mut call, next, CallContext::Call)?;
if !call.is_closed() {
panic!("Call statement was not terminated");
}
Ok(call)
}