#![allow(dead_code)]
#![allow(clippy::ptr_arg)]
use super::{reader::RantTokenReader, lexer::RantToken, message::*, Problem, Reporter};
use crate::{InternalString, RantProgramInfo, lang::*};
use fnv::FnvBuildHasher;
use line_col::LineColLookup;
use quickscope::ScopeMap;
use std::{collections::{HashMap, HashSet}, ops::Range, rc::Rc};
type ParseResult<T> = Result<T, ()>;
const MAIN_PROGRAM_SCOPE_NAME: &str = "main scope";
const KW_RETURN: &str = "return";
const KW_BREAK: &str = "break";
const KW_CONTINUE: &str = "continue";
const KW_WEIGHT: &str = "weight";
const KW_TRUE: &str = "true";
const KW_FALSE: &str = "false";
#[derive(Copy, Clone, PartialEq)]
enum SequenceParseMode {
TopLevel,
BlockElement,
FunctionArg,
FunctionBodyBlock,
DynamicKey,
AnonFunctionExpr,
VariableAssignment,
AccessorFallbackValue,
ParamDefaultValue,
CollectionInit,
SingleItem,
}
enum CollectionInitKind {
List,
Map
}
enum SequenceEndType {
ProgramEnd,
BlockAssocDelim,
BlockDelim,
BlockEnd,
FunctionArgEndNext,
FunctionArgEndBreak,
FunctionArgEndToCompose,
FunctionBodyEnd,
DynamicKeyEnd,
AnonFunctionExprToArgs,
AnonFunctionExprNoArgs,
AnonFunctionExprToCompose,
VariableAccessEnd,
VariableAssignDelim,
AccessorFallbackValueToEnd,
AccessorFallbackValueToDelim,
CollectionInitEnd,
CollectionInitDelim,
SingleItemEnd,
ParamDefaultValueSeparator,
ParamDefaultValueSignatureEnd,
}
struct VarStats {
def_span: Range<usize>,
writes: usize,
reads: usize,
has_fallible_read: bool,
is_const: bool,
role: VarRole,
}
impl VarStats {
#[inline]
fn add_write(&mut self) {
self.writes += 1;
}
#[inline]
fn add_read(&mut self, is_fallible_read: bool) {
if matches!(self.role, VarRole::FallibleOptionalArgument) && is_fallible_read {
self.has_fallible_read = true;
}
self.reads += 1;
}
}
#[derive(Copy, Clone, PartialEq)]
enum VarRole {
Normal,
Function,
Argument,
FallibleOptionalArgument,
ComposeValue,
}
#[inline]
fn super_range(a: &Range<usize>, b: &Range<usize>) -> Range<usize> {
a.start.min(b.start)..a.end.max(b.end)
}
#[derive(Debug)]
enum ParsedSequenceExtras {
WeightedBlockElement {
weight_expr: Rc<Sequence>
}
}
struct ParsedSequence {
sequence: Sequence,
end_type: SequenceEndType,
is_printing: bool,
extras: Option<ParsedSequenceExtras>,
}
pub struct RantParser<'source, 'report, R: Reporter> {
source: &'source str,
has_errors: bool,
reader: RantTokenReader<'source>,
lookup: LineColLookup<'source>,
reporter: &'report mut R,
debug_enabled: bool,
info: Rc<RantProgramInfo>,
var_stack: ScopeMap<Identifier, VarStats>,
capture_stack: Vec<(usize, HashSet<Identifier, FnvBuildHasher>)>,
}
impl<'source, 'report, R: Reporter> RantParser<'source, 'report, R> {
pub fn new(source: &'source str, reporter: &'report mut R, debug_enabled: bool, info: &Rc<RantProgramInfo>) -> Self {
Self {
source,
has_errors: false,
reader: RantTokenReader::new(source),
lookup: LineColLookup::new(source),
reporter,
debug_enabled,
info: Rc::clone(info),
var_stack: Default::default(),
capture_stack: Default::default(),
}
}
}
impl<'source, 'report, R: Reporter> RantParser<'source, 'report, R> {
pub fn parse(&mut self) -> Result<Rc<Sequence>, ()> {
let result = self.parse_sequence(SequenceParseMode::TopLevel);
match result {
Ok(..) if self.has_errors => Err(()),
Ok(ParsedSequence { sequence, .. }) => Ok(Rc::new(sequence)),
Err(()) => Err(())
}
}
fn report_error(&mut self, problem: Problem, span: &Range<usize>) {
let (line, col) = self.lookup.get(span.start);
self.has_errors = true;
self.reporter.report(CompilerMessage::new(problem, Severity::Error, Some(Position::new(line, col, span.clone()))));
}
fn report_warning(&mut self, problem: Problem, span: &Range<usize>) {
let (line, col) = self.lookup.get(span.start);
self.reporter.report(CompilerMessage::new(problem, Severity::Warning, Some(Position::new(line, col, span.clone()))));
}
#[inline]
fn unexpected_last_token_error(&mut self) {
self.report_error(Problem::UnexpectedToken(self.reader.last_token_string().to_string()), &self.reader.last_token_span())
}
#[inline]
fn parse_sequence(&mut self, mode: SequenceParseMode) -> ParseResult<ParsedSequence> {
self.var_stack.push_layer();
let parse_result = self.parse_sequence_inner(mode);
self.analyze_top_vars();
self.var_stack.pop_layer();
parse_result
}
#[inline(always)]
fn parse_sequence_inner(&mut self, mode: SequenceParseMode) -> ParseResult<ParsedSequence> {
let mut sequence = Sequence::empty(&self.info);
let mut next_print_flag = PrintFlag::None;
let mut last_print_flag_span: Option<Range<usize>> = None;
let mut is_seq_printing = false;
let mut pending_whitespace = None;
let debug = self.debug_enabled;
macro_rules! check_dangling_printflags {
() => {
match next_print_flag {
PrintFlag::None => {},
PrintFlag::Hint => {
if let Some(flag_span) = last_print_flag_span.take() {
self.report_error(Problem::InvalidHint, &flag_span);
}
},
PrintFlag::Sink => {
if let Some(flag_span) = last_print_flag_span.take() {
self.report_error(Problem::InvalidSink, &flag_span);
}
}
}
}
}
while let Some((token, span)) = self.reader.next() {
let _debug_inject_toggle = true;
macro_rules! no_debug {
($e:expr) => {{
let _debug_inject_toggle = false;
$e
}}
}
macro_rules! inject_debug_info {
() => {
if debug && _debug_inject_toggle {
let (line, col) = self.lookup.get(span.start);
sequence.push(Rc::new(Rst::DebugCursor(DebugInfo::Location { line, col })));
}
}
}
macro_rules! no_flags {
(on $b:block) => {{
let elem = $b;
if !matches!(next_print_flag, PrintFlag::None) {
if let Some(flag_span) = last_print_flag_span.take() {
self.report_error(match next_print_flag {
PrintFlag::Hint => Problem::InvalidHintOn(elem.display_name()),
PrintFlag::Sink => Problem::InvalidSinkOn(elem.display_name()),
PrintFlag::None => unreachable!()
}, &flag_span)
}
}
inject_debug_info!();
sequence.push(Rc::new(elem));
}};
($b:block) => {
if matches!(next_print_flag, PrintFlag::None) {
$b
} else if let Some(flag_span) = last_print_flag_span.take() {
self.report_error(match next_print_flag {
PrintFlag::Hint => Problem::InvalidHint,
PrintFlag::Sink => Problem::InvalidSink,
PrintFlag::None => unreachable!()
}, &flag_span)
}
};
}
macro_rules! emit {
($elem:expr) => {{
inject_debug_info!();
sequence.push(Rc::new($elem));
}}
}
macro_rules! emit_last_string {
() => {{
inject_debug_info!();
sequence.push(Rc::new(Rst::Fragment(InternalString::from(self.reader.last_token_string()))));
}}
}
macro_rules! unexpected_token_error {
() => {
self.report_error(Problem::UnexpectedToken(self.reader.last_token_string().to_string()), &span)
};
}
macro_rules! whitespace {
(allow) => {
if is_seq_printing {
if let Some(ws) = pending_whitespace.take() {
emit!(Rst::Whitespace(ws));
}
} else {
pending_whitespace = None;
}
};
(queue next) => {{
if let Some((RantToken::Whitespace, ..)) = self.reader.take_where(|tt| matches!(tt, Some((RantToken::Whitespace, ..)))) {
pending_whitespace = Some(self.reader.last_token_string());
}
}};
(queue $ws:expr) => {
pending_whitespace = Some($ws);
};
(ignore prev) => {{
#![allow(unused_assignments)]
pending_whitespace = None;
}};
(ignore next) => {
self.reader.skip_ws();
};
(ignore both) => {{
whitespace!(ignore prev);
whitespace!(ignore next);
}};
}
macro_rules! consume_fragments {
($s:ident) => {
while let Some((token, _)) = self.reader.take_where(|t| matches!(t, Some((RantToken::Escape(..), ..)) | Some((RantToken::Fragment, ..)))) {
match token {
RantToken::Escape(ch) => $s.push(ch),
_ => $s.push_str(&self.reader.last_token_string()),
}
}
}
}
match token {
RantToken::Hint => no_flags!({
whitespace!(allow);
is_seq_printing = true;
next_print_flag = PrintFlag::Hint;
last_print_flag_span = Some(span.clone());
continue
}),
RantToken::Sink => no_flags!({
whitespace!(ignore prev);
next_print_flag = PrintFlag::Sink;
last_print_flag_span = Some(span.clone());
continue
}),
RantToken::Keyword(kw) => {
let kwstr = kw.as_str();
match kwstr {
KW_TRUE => no_debug!(no_flags!(on {
whitespace!(ignore both);
is_seq_printing = true;
Rst::Boolean(true)
})),
KW_FALSE => no_debug!(no_flags!(on {
whitespace!(ignore both);
is_seq_printing = true;
Rst::Boolean(false)
})),
KW_RETURN | KW_CONTINUE | KW_BREAK | KW_WEIGHT => {
whitespace!(ignore both);
let ParsedSequence {
sequence: charm_sequence,
end_type: charm_end_type,
is_printing: is_charm_printing,
extras: mut charm_extras
} = self.parse_sequence(mode)?;
let charm_sequence_name = charm_sequence.name.clone();
let charm_sequence = (!charm_sequence.is_empty()).then(|| Rc::new(charm_sequence));
match kw.as_str() {
KW_RETURN => emit!(Rst::Return(charm_sequence)),
KW_CONTINUE => emit!(Rst::Continue(charm_sequence)),
KW_BREAK => emit!(Rst::Break(charm_sequence)),
KW_WEIGHT => {
if mode == SequenceParseMode::BlockElement {
charm_extras = Some(ParsedSequenceExtras::WeightedBlockElement {
weight_expr: charm_sequence.unwrap_or_else(|| Rc::new(Sequence::empty(&self.info)))
});
} else {
self.report_error(Problem::WeightNotAllowed, &span);
}
},
_ => unreachable!()
};
check_dangling_printflags!();
return Ok(ParsedSequence {
sequence: if let Some(charm_sequence_name) = charm_sequence_name {
sequence.with_name(charm_sequence_name)
} else {
sequence
},
end_type: charm_end_type,
is_printing: is_charm_printing || is_seq_printing,
extras: charm_extras,
})
},
other => self.report_error(Problem::InvalidKeyword(other.to_string()), &span),
}
},
RantToken::Defer => {
self.reader.skip_ws();
let block = self.parse_block(true, next_print_flag)?;
match next_print_flag {
PrintFlag::Hint => {
whitespace!(allow);
is_seq_printing = true;
},
PrintFlag::Sink => whitespace!(ignore both),
PrintFlag::None => {
if let Block { flag: PrintFlag::Hint, ..} = block {
whitespace!(allow);
is_seq_printing = true;
}
}
}
emit!(Rst::BlockValue(Rc::new(block)));
},
RantToken::LeftBrace => {
let block = self.parse_block(false, next_print_flag)?;
match next_print_flag {
PrintFlag::Hint => {
whitespace!(allow);
is_seq_printing = true;
},
PrintFlag::Sink => whitespace!(ignore both),
PrintFlag::None => {
if let Block { flag: PrintFlag::Hint, ..} = block {
whitespace!(allow);
is_seq_printing = true;
}
}
}
emit!(Rst::Block(Rc::new(block)));
},
RantToken::Compose => no_flags!({
whitespace!(ignore prev);
match mode {
SequenceParseMode::FunctionArg => {
return Ok(ParsedSequence {
sequence: sequence.with_name_str("argument"),
end_type: SequenceEndType::FunctionArgEndToCompose,
is_printing: is_seq_printing,
extras: None,
})
},
SequenceParseMode::AnonFunctionExpr => {
return Ok(ParsedSequence {
sequence: sequence.with_name_str("anonymous function expression"),
end_type: SequenceEndType::AnonFunctionExprToCompose,
is_printing: is_seq_printing,
extras: None,
})
},
_ => unexpected_token_error!()
}
}),
RantToken::ComposeValue => no_flags!({
if let Some(compval) = self.var_stack.get_mut(COMPOSE_VALUE_NAME) {
emit!(Rst::ComposeValue);
compval.add_read(false);
} else {
self.report_error(Problem::NothingToCompose, &span);
}
}),
RantToken::Pipe => no_flags!({
whitespace!(ignore prev);
match mode {
SequenceParseMode::BlockElement => {
return Ok(ParsedSequence {
sequence: sequence.with_name_str("block element"),
end_type: SequenceEndType::BlockDelim,
is_printing: is_seq_printing,
extras: None,
})
},
SequenceParseMode::DynamicKey => {
self.report_error(Problem::DynamicKeyBlockMultiElement, &span);
},
SequenceParseMode::FunctionBodyBlock => {
self.report_error(Problem::FunctionBodyBlockMultiElement, &span);
},
_ => unexpected_token_error!()
}
}),
RantToken::RightBrace => no_flags!({
whitespace!(ignore prev);
match mode {
SequenceParseMode::BlockElement => {
return Ok(ParsedSequence {
sequence: sequence.with_name_str("block element"),
end_type: SequenceEndType::BlockEnd,
is_printing: true,
extras: None,
})
},
SequenceParseMode::FunctionBodyBlock => {
return Ok(ParsedSequence {
sequence: sequence.with_name_str("function body"),
end_type: SequenceEndType::FunctionBodyEnd,
is_printing: true,
extras: None,
})
},
SequenceParseMode::DynamicKey => {
return Ok(ParsedSequence {
sequence: sequence.with_name_str("dynamic key"),
end_type: SequenceEndType::DynamicKeyEnd,
is_printing: true,
extras: None,
})
}
_ => unexpected_token_error!()
}
}),
RantToken::At => no_flags!(on {
match self.reader.next_solid() {
Some((RantToken::LeftParen, _)) => {
self.parse_collection_initializer(CollectionInitKind::Map, &span)?
},
_ => {
self.report_error(Problem::ExpectedToken("(".to_owned()), &self.reader.last_token_span());
Rst::EmptyVal
},
}
}),
RantToken::LeftParen => no_flags!(on {
self.parse_collection_initializer(CollectionInitKind::List, &span)?
}),
RantToken::RightParen => no_flags!({
match mode {
SequenceParseMode::CollectionInit => {
return Ok(ParsedSequence {
sequence,
end_type: SequenceEndType::CollectionInitEnd,
is_printing: true,
extras: None,
})
},
_ => unexpected_token_error!()
}
}),
RantToken::LeftBracket => {
let func_access = self.parse_func_access(next_print_flag)?;
match func_access {
Rst::FuncCall(FunctionCall { flag, ..}) => {
match flag {
PrintFlag::Hint => {
is_seq_printing = true;
whitespace!(allow);
},
_ => whitespace!(ignore both)
}
},
Rst::FuncDef(_) => {
whitespace!(ignore both);
},
_ => {}
}
emit!(func_access);
},
RantToken::RightBracket => no_flags!({
match mode {
SequenceParseMode::AnonFunctionExpr => return Ok(ParsedSequence {
sequence: sequence.with_name_str("anonymous function expression"),
end_type: SequenceEndType::AnonFunctionExprNoArgs,
is_printing: true,
extras: None,
}),
SequenceParseMode::FunctionArg => return Ok(ParsedSequence {
sequence: sequence.with_name_str("argument"),
end_type: SequenceEndType::FunctionArgEndBreak,
is_printing: true,
extras: None,
}),
SequenceParseMode::ParamDefaultValue => return Ok(ParsedSequence {
sequence: sequence.with_name_str("default value"),
end_type: SequenceEndType::ParamDefaultValueSignatureEnd,
is_printing: true,
extras: None,
}),
_ => unexpected_token_error!()
}
}),
RantToken::LeftAngle => no_flags!({
let accessors = self.parse_accessor()?;
for accessor in accessors {
match accessor {
Rst::VarGet(..) | Rst::VarDepth(..) => {
is_seq_printing = true;
whitespace!(allow);
},
Rst::VarSet(..) | Rst::VarDef(..) | Rst::ConstDef(..) => {
},
_ => unreachable!()
}
emit!(accessor);
}
}),
RantToken::RightAngle => no_flags!({
match mode {
SequenceParseMode::VariableAssignment => return Ok(ParsedSequence {
sequence: sequence.with_name_str("setter value"),
end_type: SequenceEndType::VariableAccessEnd,
is_printing: true,
extras: None,
}),
SequenceParseMode::AccessorFallbackValue => return Ok(ParsedSequence {
sequence: sequence.with_name_str("fallback value"),
end_type: SequenceEndType::AccessorFallbackValueToEnd,
is_printing: true,
extras: None,
}),
_ => unexpected_token_error!()
}
}),
RantToken::Bang | RantToken::Question | RantToken::Slash | RantToken::Plus | RantToken::Dollar | RantToken::Equals | RantToken::Percent
=> no_flags!(on {
whitespace!(allow);
is_seq_printing = true;
let frag = self.reader.last_token_string();
Rst::Fragment(frag)
}),
RantToken::Fragment => no_flags!(on {
whitespace!(allow);
is_seq_printing = true;
let mut frag = self.reader.last_token_string();
consume_fragments!(frag);
Rst::Fragment(frag)
}),
RantToken::Whitespace => no_flags!({
if is_seq_printing {
let ws = self.reader.last_token_string();
whitespace!(queue ws);
}
}),
RantToken::Escape(ch) => no_flags!(on {
whitespace!(allow);
is_seq_printing = true;
let mut frag = InternalString::new();
frag.push(ch);
consume_fragments!(frag);
Rst::Fragment(frag)
}),
RantToken::Integer(n) => no_flags!(on {
whitespace!(allow);
is_seq_printing = true;
Rst::Integer(n)
}),
RantToken::Float(n) => no_flags!(on {
whitespace!(allow);
is_seq_printing = true;
Rst::Float(n)
}),
RantToken::EmptyValue => no_flags!(on {
Rst::EmptyVal
}),
RantToken::StringLiteral(s) => no_flags!(on {
whitespace!(allow);
is_seq_printing = true;
Rst::Fragment(s)
}),
RantToken::Colon => no_flags!({
match mode {
SequenceParseMode::AnonFunctionExpr => return Ok(ParsedSequence {
sequence: sequence.with_name_str("anonymous function expression"),
end_type: SequenceEndType::AnonFunctionExprToArgs,
is_printing: true,
extras: None,
}),
_ => emit_last_string!(),
}
}),
RantToken::Semi => no_flags!({
match mode {
SequenceParseMode::FunctionArg => return Ok(ParsedSequence {
sequence: sequence.with_name_str("argument"),
end_type: SequenceEndType::FunctionArgEndNext,
is_printing: true,
extras: None,
}),
SequenceParseMode::CollectionInit => return Ok(ParsedSequence {
sequence: sequence.with_name_str("collection item"),
end_type: SequenceEndType::CollectionInitDelim,
is_printing: true,
extras: None,
}),
SequenceParseMode::VariableAssignment => return Ok(ParsedSequence {
sequence: sequence.with_name_str("variable assignment"),
end_type: SequenceEndType::VariableAssignDelim,
is_printing: true,
extras: None,
}),
SequenceParseMode::AccessorFallbackValue => return Ok(ParsedSequence {
sequence: sequence.with_name_str("fallback"),
end_type: SequenceEndType::AccessorFallbackValueToDelim,
is_printing: true,
extras: None,
}),
SequenceParseMode::ParamDefaultValue => return Ok(ParsedSequence {
sequence: sequence.with_name_str("default value"),
end_type: SequenceEndType::ParamDefaultValueSeparator,
is_printing: true,
extras: None,
}),
_ => emit_last_string!(),
}
}),
RantToken::UnterminatedStringLiteral => {
self.report_error(Problem::UnclosedStringLiteral, &span);
return Err(())
},
_ => unexpected_token_error!(),
}
if mode == SequenceParseMode::SingleItem {
return Ok(ParsedSequence {
sequence,
end_type: SequenceEndType::SingleItemEnd,
is_printing: is_seq_printing,
extras: None,
})
}
next_print_flag = PrintFlag::None;
}
check_dangling_printflags!();
Ok(ParsedSequence {
sequence: sequence.with_name_str(MAIN_PROGRAM_SCOPE_NAME),
end_type: SequenceEndType::ProgramEnd,
is_printing: is_seq_printing,
extras: None,
})
}
fn parse_collection_initializer(&mut self, kind: CollectionInitKind, start_span: &Range<usize>) -> ParseResult<Rst> {
match kind {
CollectionInitKind::List => {
self.reader.skip_ws();
if self.reader.eat_where(|token| matches!(token, Some((RantToken::RightParen, ..)))) {
return Ok(Rst::ListInit(Rc::new(vec![])))
}
let mut sequences = vec![];
loop {
self.reader.skip_ws();
let ParsedSequence { sequence, end_type: seq_end, .. } = self.parse_sequence(SequenceParseMode::CollectionInit)?;
match seq_end {
SequenceEndType::CollectionInitDelim => {
sequences.push(Rc::new(sequence));
},
SequenceEndType::CollectionInitEnd => {
sequences.push(Rc::new(sequence));
break
},
SequenceEndType::ProgramEnd => {
self.report_error(Problem::UnclosedList, &super_range(start_span, &self.reader.last_token_span()));
return Err(())
},
_ => unreachable!()
}
}
if let Some(seq) = sequences.last() {
if seq.is_empty() {
sequences.pop();
}
}
Ok(Rst::ListInit(Rc::new(sequences)))
},
CollectionInitKind::Map => {
let mut pairs = vec![];
loop {
let key_expr = match self.reader.next_solid() {
Some((RantToken::LeftBrace, _)) => {
MapKeyExpr::Dynamic(Rc::new(self.parse_dynamic_expr(false)?))
},
Some((RantToken::Fragment, span)) => {
let key = self.reader.last_token_string();
if !is_valid_ident(key.as_str()) {
self.report_error(Problem::InvalidIdentifier(key.to_string()), &span);
}
MapKeyExpr::Static(key)
},
Some((RantToken::StringLiteral(s), _)) => {
MapKeyExpr::Static(s)
},
Some((RantToken::RightParen, _)) => break,
Some(_) => {
self.unexpected_last_token_error();
MapKeyExpr::Static(self.reader.last_token_string())
},
None => {
self.report_error(Problem::UnclosedMap, &super_range(start_span, &self.reader.last_token_span()));
return Err(())
}
};
self.reader.skip_ws();
if !self.reader.eat_where(|tok| matches!(tok, Some((RantToken::Equals, ..)))) {
self.report_error(Problem::ExpectedToken("=".to_owned()), &self.reader.last_token_span());
return Err(())
}
self.reader.skip_ws();
let ParsedSequence {
sequence: value_expr,
end_type: value_expr_end,
..
} = self.parse_sequence(SequenceParseMode::CollectionInit)?;
match value_expr_end {
SequenceEndType::CollectionInitDelim => {
pairs.push((key_expr, Rc::new(value_expr)));
},
SequenceEndType::CollectionInitEnd => {
pairs.push((key_expr, Rc::new(value_expr)));
break
},
SequenceEndType::ProgramEnd => {
self.report_error(Problem::UnclosedMap, &super_range(start_span, &self.reader.last_token_span()));
return Err(())
},
_ => unreachable!()
}
}
Ok(Rst::MapInit(Rc::new(pairs)))
},
}
}
fn parse_func_params(&mut self, start_span: &Range<usize>) -> ParseResult<Vec<(Parameter, Range<usize>)>> {
let mut params = vec![];
let mut params_set = HashSet::new();
let mut last_varity = Varity::Required;
let mut is_sig_variadic = false;
match self.reader.next_solid() {
Some((RantToken::Colon, _)) => {
'read_params: loop {
match self.reader.next_solid() {
Some((RantToken::Fragment, span)) => {
let param_name = Identifier::new(self.reader.last_token_string());
if !is_valid_ident(param_name.as_str()) {
self.report_error(Problem::InvalidIdentifier(param_name.to_string()), &span)
}
if !params_set.insert(param_name.clone()) {
self.report_error(Problem::DuplicateParameter(param_name.to_string()), &span);
}
self.reader.skip_ws();
let (varity, full_param_span) = if let Some((varity_token, varity_span)) =
self.reader.take_where(|t| matches!(t,
Some((RantToken::Question, _)) |
Some((RantToken::Star, _)) |
Some((RantToken::Plus, _))))
{
(match varity_token {
RantToken::Question => Varity::Optional,
RantToken::Star => Varity::VariadicStar,
RantToken::Plus => Varity::VariadicPlus,
_ => unreachable!()
}, super_range(&span, &varity_span))
} else {
(Varity::Required, span)
};
let is_param_variadic = varity.is_variadic();
if is_sig_variadic && is_param_variadic {
self.report_error(Problem::MultipleVariadicParams, &full_param_span);
} else if !Varity::is_valid_order(last_varity, varity) {
self.report_error(Problem::InvalidParamOrder(last_varity.to_string(), varity.to_string()), &full_param_span);
}
last_varity = varity;
is_sig_variadic |= is_param_variadic;
if matches!(varity, Varity::Optional) {
let ParsedSequence {
sequence: default_value_seq,
end_type: default_value_end_type,
..
} = self.parse_sequence(SequenceParseMode::ParamDefaultValue)?;
let should_continue = match default_value_end_type {
SequenceEndType::ParamDefaultValueSeparator => true,
SequenceEndType::ParamDefaultValueSignatureEnd => false,
SequenceEndType::ProgramEnd => {
self.report_error(Problem::UnclosedFunctionSignature, &start_span);
return Err(())
}
_ => unreachable!(),
};
let opt_param = Parameter {
name: param_name,
varity: Varity::Optional,
default_value_expr: (!default_value_seq.is_empty()).then(|| Rc::new(default_value_seq))
};
params.push((opt_param, full_param_span.end .. self.reader.last_token_span().start));
if should_continue {
continue 'read_params
} else {
break 'read_params
}
}
let param = Parameter {
name: param_name,
varity,
default_value_expr: None,
};
params.push((param, full_param_span));
match self.reader.next_solid() {
Some((RantToken::Semi, ..)) => {
continue 'read_params
},
Some((RantToken::RightBracket, ..)) => {
break 'read_params
},
Some((_, span)) => {
self.report_error(Problem::UnexpectedToken(self.reader.last_token_string().to_string()), &span);
return Err(())
},
None => {
self.report_error(Problem::UnclosedFunctionSignature, &start_span);
return Err(())
},
}
},
Some((RantToken::RightBracket, span)) => {
self.report_error(Problem::MissingIdentifier, &span);
break 'read_params
},
Some((.., span)) => {
self.report_error(Problem::InvalidIdentifier(self.reader.last_token_string().to_string()), &span)
},
None => {
self.report_error(Problem::UnclosedFunctionSignature, &start_span);
return Err(())
}
}
}
},
Some((RantToken::RightBracket, _)) => {},
Some((.., span)) => {
self.report_error(Problem::UnexpectedToken(self.reader.last_token_string().to_string()), &span);
return Err(())
},
None => {
self.report_error(Problem::UnclosedFunctionSignature, &start_span);
return Err(())
}
}
Ok(params)
}
fn parse_func_access(&mut self, flag: PrintFlag) -> ParseResult<Rst> {
let start_span = self.reader.last_token_span();
self.reader.skip_ws();
if let Some((func_access_type_token, func_access_type_span))
= self.reader.take_where(|t| matches!(t, Some((RantToken::Dollar, ..)) | Some((RantToken::Percent, ..)) | Some((RantToken::Question, ..)))) {
match func_access_type_token {
tt @ RantToken::Dollar | tt @ RantToken::Percent => {
let is_const = matches!(tt, RantToken::Percent);
let (func_path, _func_path_span) = self.parse_access_path(false)?;
if is_const && !func_path.is_variable() {
self.report_warning(Problem::NestedFunctionDefMarkedConstant, &func_access_type_span);
}
self.reader.skip_ws();
let ((body, params, end_func_sig_span), captures) = self.capture_pass(|self_| {
let params = self_.parse_func_params(&start_span)?;
let end_func_sig_span = self_.reader.last_token_span();
let body = self_.parse_func_body(¶ms, false)?;
Ok((body, params, end_func_sig_span))
})?;
if func_path.is_variable() {
if let Some(id) = &func_path.var_name() {
let func_def_span = super_range(&start_span, &end_func_sig_span);
self.track_variable(id, &func_path.kind(), is_const, VarRole::Function, &func_def_span);
}
}
Ok(Rst::FuncDef(FunctionDef {
body: Rc::new(body.with_name_str(format!("[{}]", func_path).as_str())),
path: Rc::new(func_path),
params: Rc::new(params.into_iter().map(|(p, _)| p).collect()),
capture_vars: Rc::new(captures),
is_const,
}))
},
RantToken::Question => {
let params = self.parse_func_params(&start_span)?;
self.reader.skip_ws();
let (body, captures) = self.capture_pass(|self_| self_.parse_func_body(¶ms, true))?;
Ok(Rst::Closure(ClosureExpr {
capture_vars: Rc::new(captures),
body: Rc::new(body.with_name_str("closure")),
params: Rc::new(params.into_iter().map(|(p, _)| p).collect()),
}))
},
_ => unreachable!()
}
} else {
let mut calls: Vec<FunctionCall> = vec![];
let mut is_composed = false;
let mut is_finished = false;
let mut is_chain_temporal = false;
while !is_finished {
self.reader.skip_ws();
let mut func_args = vec![];
let mut temporal_index_labels: HashMap<InternalString, usize> = Default::default();
let mut cur_temporal_index: usize = 0;
let is_anonymous = self.reader.eat_where(|t| matches!(t, Some((RantToken::Bang, ..))));
let mut is_temporal = false;
let mut is_compval_used = false;
macro_rules! parse_args {
() => {{
#[allow(unused_assignments)] loop {
self.reader.skip_ws();
let mut spread_mode = ArgumentSpreadMode::NoSpread;
match self.reader.take_where(|t| matches!(t, Some((RantToken::Star, ..)) | Some((RantToken::Temporal, ..)) | Some((RantToken::TemporalLabeled(_), ..)))) {
Some((RantToken::Star, ..)) => {
self.reader.skip_ws();
spread_mode = ArgumentSpreadMode::Parametric;
},
Some((RantToken::Temporal, ..)) => {
is_temporal = true;
self.reader.skip_ws();
spread_mode = ArgumentSpreadMode::Temporal { label: cur_temporal_index };
cur_temporal_index += 1;
},
Some((RantToken::TemporalLabeled(label_str), ..)) => {
is_temporal = true;
self.reader.skip_ws();
let label_index = if let Some(label_index) = temporal_index_labels.get(&label_str) {
*label_index
} else {
let label_index = cur_temporal_index;
temporal_index_labels.insert(label_str.clone(), label_index);
cur_temporal_index += 1;
label_index
};
spread_mode = ArgumentSpreadMode::Temporal { label: label_index };
},
Some(_) => unreachable!(),
None => {},
}
let ParsedSequence {
sequence: arg_seq,
end_type: arg_end,
..
} = if is_composed {
self.var_stack.push_layer();
let compval_stats = VarStats {
writes: 1,
reads: 0,
def_span: Default::default(), is_const: true,
has_fallible_read: false,
role: VarRole::ComposeValue,
};
self.var_stack.define(Identifier::from(COMPOSE_VALUE_NAME), compval_stats);
let parsed_arg_expr = self.parse_sequence_inner(SequenceParseMode::FunctionArg)?;
is_compval_used |= self.var_stack.get(COMPOSE_VALUE_NAME).unwrap().reads > 0;
self.analyze_top_vars();
self.var_stack.pop_layer();
parsed_arg_expr
} else {
self.parse_sequence(SequenceParseMode::FunctionArg)?
};
let arg = ArgumentExpr {
expr: Rc::new(arg_seq),
spread_mode,
};
func_args.push(arg);
match arg_end {
SequenceEndType::FunctionArgEndNext => continue,
SequenceEndType::FunctionArgEndBreak => {
is_finished = true;
break
},
SequenceEndType::FunctionArgEndToCompose => {
is_composed = true;
break
},
SequenceEndType::ProgramEnd => {
self.report_error(Problem::UnclosedFunctionCall, &self.reader.last_token_span());
return Err(())
},
_ => unreachable!()
}
}
}}
}
macro_rules! fallback_compose {
() => {
if calls.len() > 0 && !is_compval_used {
let arg = ArgumentExpr {
expr: Rc::new(Sequence::one(Rst::ComposeValue, &self.info)),
spread_mode: ArgumentSpreadMode::NoSpread,
};
func_args.insert(0, arg);
}
}
}
self.reader.skip_ws();
if is_anonymous {
self.var_stack.push_layer();
let compval_stats = VarStats {
writes: 1,
reads: 0,
def_span: Default::default(), is_const: true,
has_fallible_read: false,
role: VarRole::ComposeValue,
};
self.var_stack.define(Identifier::from(COMPOSE_VALUE_NAME), compval_stats);
let ParsedSequence {
sequence: func_expr,
end_type: func_expr_end,
..
} = self.parse_sequence_inner(SequenceParseMode::AnonFunctionExpr)?;
is_compval_used |= self.var_stack.get(COMPOSE_VALUE_NAME).unwrap().reads > 0;
self.analyze_top_vars();
self.var_stack.pop_layer();
match func_expr_end {
SequenceEndType::AnonFunctionExprNoArgs => {
is_finished = true;
},
SequenceEndType::AnonFunctionExprToArgs => parse_args!(),
SequenceEndType::AnonFunctionExprToCompose => {
is_composed = true;
}
_ => unreachable!()
}
fallback_compose!();
let fcall = FunctionCall {
target: FunctionCallTarget::Expression(Rc::new(func_expr)),
arguments: Rc::new(func_args),
flag,
is_temporal,
};
calls.push(fcall);
} else {
let (func_path, func_path_span) = self.parse_access_path(false)?;
if let Some((token, _)) = self.reader.next_solid() {
match token {
RantToken::RightBracket => {
is_finished = true;
},
RantToken::Colon => parse_args!(),
RantToken::Compose => {
is_composed = true;
}
_ => {
self.unexpected_last_token_error();
return Err(())
}
}
fallback_compose!();
self.track_variable_access(&func_path, false, false, &func_path_span);
let fcall = FunctionCall {
target: FunctionCallTarget::Path(Rc::new(func_path)),
arguments: Rc::new(func_args),
flag,
is_temporal,
};
calls.push(fcall);
} else {
self.report_error(Problem::UnclosedFunctionCall, &self.reader.last_token_span());
return Err(())
}
}
is_chain_temporal |= is_temporal;
}
Ok(if is_composed {
Rst::ComposedCall(ComposedFunctionCall {
flag,
is_temporal: is_chain_temporal,
steps: Rc::new(calls),
})
} else {
Rst::FuncCall(calls.drain(..).next().unwrap())
})
}
}
#[inline]
fn parse_access_path_kind(&mut self) -> AccessPathKind {
if let Some((token, _span)) = self.reader.take_where(
|t| matches!(t, Some((RantToken::Slash, _)) | Some((RantToken::Caret, _))
)) {
match token {
RantToken::Slash => {
AccessPathKind::ExplicitGlobal
},
RantToken::Caret => {
let mut descope_count = 1;
loop {
if !self.reader.eat_where(|t| matches!(t, Some((RantToken::Caret, _)))) {
break AccessPathKind::Descope(descope_count)
}
descope_count += 1;
}
},
_ => unreachable!()
}
} else {
AccessPathKind::Local
}
}
#[inline]
fn parse_access_path(&mut self, allow_anonymous: bool) -> ParseResult<(AccessPath, Range<usize>)> {
self.reader.skip_ws();
let mut idparts = vec![];
let start_span = self.reader.last_token_span();
let mut access_kind = AccessPathKind::Local;
if allow_anonymous && self.reader.eat_where(|t| matches!(t, Some((RantToken::Bang, ..)))) {
self.reader.skip_ws();
let ParsedSequence {
sequence: anon_expr,
end_type: anon_end_type,
..
} = self.parse_sequence(SequenceParseMode::SingleItem)?;
match anon_end_type {
SequenceEndType::SingleItemEnd => {
idparts.push(AccessPathComponent::AnonymousValue(Rc::new(anon_expr)));
},
SequenceEndType::ProgramEnd => {
self.report_error(Problem::UnclosedVariableAccess, &self.reader.last_token_span());
return Err(())
},
_ => unreachable!(),
}
} else {
access_kind = self.parse_access_path_kind();
let first_part = self.reader.next_solid();
match first_part {
Some((RantToken::Fragment, span)) => {
let varname = Identifier::new(self.reader.last_token_string());
if is_valid_ident(varname.as_str()) {
idparts.push(AccessPathComponent::Name(varname));
} else {
self.report_error(Problem::InvalidIdentifier(varname.to_string()), &span);
}
},
Some((RantToken::LeftBrace, _)) => {
let dynamic_key_expr = self.parse_dynamic_expr(false)?;
idparts.push(AccessPathComponent::DynamicKey(Rc::new(dynamic_key_expr)));
},
Some((RantToken::Colon, span)) => {
self.reader.take_where(|t| matches!(t, Some((RantToken::Integer(_), ..))));
self.report_error(Problem::AccessPathStartsWithSlice, &super_range(&span, &self.reader.last_token_span()));
}
Some((RantToken::Integer(_), span)) => {
self.reader.skip_ws();
if self.reader.eat_where(|t| matches!(t, Some((RantToken::Colon, ..)))) {
self.report_error(Problem::AccessPathStartsWithSlice, &super_range(&span, &self.reader.last_token_span()));
} else {
self.report_error(Problem::AccessPathStartsWithIndex, &span);
}
},
Some((.., span)) => {
self.report_error(Problem::InvalidIdentifier(self.reader.last_token_string().to_string()), &span);
},
None => {
self.report_error(Problem::MissingIdentifier, &start_span);
return Err(())
}
}
}
loop {
self.reader.skip_ws();
if self.reader.eat_where(|t| matches!(t, Some((RantToken::Slash, ..)))) {
let component = self.reader.next_solid();
match component {
Some((RantToken::Fragment, span)) => {
let varname = Identifier::new(self.reader.last_token_string());
if is_valid_ident(varname.as_str()) {
idparts.push(AccessPathComponent::Name(varname));
} else {
self.report_error(Problem::InvalidIdentifier(varname.to_string()), &span);
}
},
Some((RantToken::Integer(i), _)) => {
self.reader.skip_ws();
if self.reader.eat_where(|t| matches!(t, Some((RantToken::Colon, ..)))) {
self.reader.skip_ws();
match self.reader.peek() {
Some((RantToken::Integer(j), ..)) => {
let j = *j;
self.reader.skip_one();
idparts.push(AccessPathComponent::Slice(SliceExpr::Between(SliceIndex::Static(i), SliceIndex::Static(j))));
},
Some((RantToken::LeftBrace, ..)) => {
let to_expr = Rc::new(self.parse_dynamic_expr(true)?);
idparts.push(AccessPathComponent::Slice(SliceExpr::Between(SliceIndex::Static(i), SliceIndex::Dynamic(to_expr))));
},
Some((RantToken::Slash, ..)) |
Some((RantToken::RightAngle, ..)) |
Some((RantToken::Equals, ..)) |
Some((RantToken::Question, ..)) |
Some((RantToken::Semi, ..)) => {
idparts.push(AccessPathComponent::Slice(SliceExpr::From(SliceIndex::Static(i))));
},
Some(_) => {
self.reader.next();
let token = self.reader.last_token_string().to_string();
self.report_error(Problem::InvalidSliceBound(token), &self.reader.last_token_span());
},
None => {
self.report_error(Problem::UnclosedVariableAccess, &super_range(&start_span, &self.reader.last_token_span()));
return Err(())
}
}
} else {
idparts.push(AccessPathComponent::Index(i));
}
},
Some((RantToken::Colon, _)) => {
self.reader.skip_ws();
match self.reader.peek() {
Some((RantToken::Integer(to), ..)) => {
let to = *to;
self.reader.skip_one();
idparts.push(AccessPathComponent::Slice(SliceExpr::To(SliceIndex::Static(to))));
},
Some((RantToken::LeftBrace, ..)) => {
let to_expr = Rc::new(self.parse_dynamic_expr(true)?);
idparts.push(AccessPathComponent::Slice(SliceExpr::To(SliceIndex::Dynamic(to_expr))));
},
Some((RantToken::Slash, ..)) |
Some((RantToken::RightAngle, ..)) |
Some((RantToken::Equals, ..)) |
Some((RantToken::Question, ..)) |
Some((RantToken::Semi, ..)) => {
idparts.push(AccessPathComponent::Slice(SliceExpr::Full));
},
Some(_) => {
self.reader.next();
let token = self.reader.last_token_string().to_string();
self.report_error(Problem::InvalidSliceBound(token), &self.reader.last_token_span());
},
None => {
self.report_error(Problem::UnclosedVariableAccess, &super_range(&start_span, &self.reader.last_token_span()));
return Err(())
}
}
},
Some((RantToken::LeftBrace, _)) => {
let expr = Rc::new(self.parse_dynamic_expr(false)?);
self.reader.skip_ws();
if self.reader.eat_where(|t| matches!(t, Some((RantToken::Colon, ..)))) {
self.reader.skip_ws();
match self.reader.peek() {
Some((RantToken::Integer(to), ..)) => {
let to = *to;
self.reader.skip_one();
idparts.push(AccessPathComponent::Slice(SliceExpr::Between(SliceIndex::Dynamic(expr), SliceIndex::Static(to))));
},
Some((RantToken::LeftBrace, ..)) => {
let to_expr = Rc::new(self.parse_dynamic_expr(true)?);
idparts.push(AccessPathComponent::Slice(SliceExpr::Between(SliceIndex::Dynamic(expr), SliceIndex::Dynamic(to_expr))));
},
Some((RantToken::Slash, ..)) |
Some((RantToken::RightAngle, ..)) |
Some((RantToken::Equals, ..)) |
Some((RantToken::Question, ..)) |
Some((RantToken::Semi, ..)) => {
idparts.push(AccessPathComponent::Slice(SliceExpr::From(SliceIndex::Dynamic(expr))));
},
Some(_) => {
self.reader.next();
let token = self.reader.last_token_string().to_string();
self.report_error(Problem::InvalidSliceBound(token), &self.reader.last_token_span());
},
None => {
self.report_error(Problem::UnclosedVariableAccess, &super_range(&start_span, &self.reader.last_token_span()));
return Err(())
}
}
} else {
idparts.push(AccessPathComponent::DynamicKey(expr));
}
},
Some((.., span)) => {
self.report_error(Problem::InvalidIdentifier(self.reader.last_token_string().to_string()), &span);
},
None => {
self.report_error(Problem::MissingIdentifier, &self.reader.last_token_span());
return Err(())
}
}
} else {
return Ok((AccessPath::new(idparts, access_kind), start_span.start .. self.reader.last_token_span().start))
}
}
}
fn parse_dynamic_expr(&mut self, expect_opening_brace: bool) -> ParseResult<Sequence> {
if expect_opening_brace && !self.reader.eat_where(|t| matches!(t, Some((RantToken::LeftBrace, _)))) {
self.report_error(Problem::ExpectedToken("{".to_owned()), &self.reader.last_token_span());
return Err(())
}
let start_span = self.reader.last_token_span();
let ParsedSequence { sequence, end_type, .. } = self.parse_sequence(SequenceParseMode::DynamicKey)?;
match end_type {
SequenceEndType::DynamicKeyEnd => {},
SequenceEndType::ProgramEnd => {
let err_span = start_span.start .. self.source.len();
self.report_error(Problem::UnclosedBlock, &err_span);
return Err(())
},
_ => unreachable!()
}
Ok(sequence)
}
fn parse_func_body(&mut self, params: &Vec<(Parameter, Range<usize>)>, allow_inline: bool) -> ParseResult<Sequence> {
self.reader.skip_ws();
let is_block_body = if allow_inline {
self.reader.eat_where(|t| matches!(t, Some((RantToken::LeftBrace, _))))
} else {
if !self.reader.eat_where(|t| matches!(t, Some((RantToken::LeftBrace, _)))) {
self.report_error(Problem::ExpectedToken("{".to_owned()), &self.reader.last_token_span());
return Err(())
}
true
};
let start_span = self.reader.last_token_span();
for (param, span) in params {
self.var_stack.define(param.name.clone(), VarStats {
reads: 0,
writes: 1,
def_span: span.clone(),
is_const: true,
has_fallible_read: false,
role: if param.is_optional() && param.default_value_expr.is_none() {
VarRole::FallibleOptionalArgument
} else {
VarRole::Argument
}
});
}
let ParsedSequence { sequence, end_type, .. } = self.parse_sequence_inner(if is_block_body {
SequenceParseMode::FunctionBodyBlock
} else {
SequenceParseMode::SingleItem
})?;
match end_type {
SequenceEndType::FunctionBodyEnd | SequenceEndType::SingleItemEnd => {},
SequenceEndType::ProgramEnd => {
let err_span = start_span.start .. self.source.len();
self.report_error(if is_block_body {
Problem::UnclosedFunctionBody
} else {
Problem::MissingFunctionBody
}, &err_span);
return Err(())
},
_ => unreachable!()
}
Ok(sequence)
}
fn capture_pass<T>(&mut self, parse_func: impl FnOnce(&mut Self) -> ParseResult<T>) -> ParseResult<(T, Vec<Identifier>)> {
let capture_height = self.var_stack.depth();
self.capture_stack.push((capture_height, Default::default()));
self.var_stack.push_layer();
let parse_out = parse_func(self)?;
self.analyze_top_vars();
self.var_stack.pop_layer();
let (_, mut capture_set) = self.capture_stack.pop().unwrap();
Ok((parse_out, capture_set.drain().collect()))
}
fn parse_block(&mut self, expect_opening_brace: bool, flag: PrintFlag) -> ParseResult<Block> {
if expect_opening_brace && !self.reader.eat_where(|t| matches!(t, Some((RantToken::LeftBrace, _)))) {
self.report_error(Problem::ExpectedToken("{".to_owned()), &self.reader.last_token_span());
return Err(())
}
let start_pos = self.reader.last_token_pos();
let mut auto_hint = false;
let mut is_weighted = false;
let mut elements = vec![];
loop {
let ParsedSequence {
sequence,
end_type,
is_printing,
extras
} = self.parse_sequence(SequenceParseMode::BlockElement)?;
auto_hint |= is_printing;
let element = BlockElement {
main: Rc::new(sequence),
weight: if let Some(ParsedSequenceExtras::WeightedBlockElement { weight_expr }) = extras {
is_weighted = true;
Some(match (weight_expr.len(), weight_expr.first().map(Rc::as_ref)) {
(1, Some(Rst::Integer(n))) => BlockWeight::Constant(*n as f64),
(1, Some(Rst::Float(n))) => BlockWeight::Constant(*n),
_ => BlockWeight::Dynamic(weight_expr)
})
} else {
None
},
};
match end_type {
SequenceEndType::BlockDelim => {
elements.push(element);
},
SequenceEndType::BlockEnd => {
elements.push(element);
break
},
SequenceEndType::ProgramEnd => {
let err_span = start_pos .. self.source.len();
self.report_error(Problem::UnclosedBlock, &err_span);
return Err(())
},
_ => unreachable!()
}
}
if auto_hint && flag != PrintFlag::Sink {
Ok(Block::new(PrintFlag::Hint, is_weighted, elements))
} else {
Ok(Block::new(flag, is_weighted, elements))
}
}
fn parse_ident(&mut self) -> ParseResult<Identifier> {
if let Some((token, span)) = self.reader.next_solid() {
match token {
RantToken::Fragment => {
let idstr = self.reader.last_token_string();
if !is_valid_ident(idstr.as_str()) {
self.report_error(Problem::InvalidIdentifier(idstr.to_string()), &span);
}
Ok(Identifier::new(idstr))
},
_ => {
self.unexpected_last_token_error();
Err(())
}
}
} else {
self.report_error(Problem::MissingIdentifier, &self.reader.last_token_span());
Err(())
}
}
#[inline]
fn track_variable(&mut self, id: &Identifier, access_kind: &AccessPathKind, is_const: bool, role: VarRole, def_span: &Range<usize>) {
let (prev_tracker, requested_depth, found_depth) = match access_kind {
AccessPathKind::Local => {
(self.var_stack.get(id), 0, self.var_stack.depth_of(id))
},
AccessPathKind::Descope(n) => {
let (v, d) = self.var_stack
.get_parent_depth(id, *n)
.map(|(v, d)| (Some(v), Some(d)))
.unwrap_or_default();
(v, *n, d)
},
AccessPathKind::ExplicitGlobal => {
let rd = self.var_stack.depth();
let (v, d) = self.var_stack
.get_parent_depth(id, rd)
.map(|(v, d)| (Some(v), Some(d)))
.unwrap_or_default();
(v, rd, d)
},
};
if let Some(prev_tracker) = prev_tracker {
if prev_tracker.is_const && found_depth == Some(requested_depth) {
self.report_error(Problem::ConstantRedefinition(id.to_string()), def_span);
}
}
let v = VarStats {
writes: 0,
reads: 0,
def_span: def_span.clone(),
has_fallible_read: false,
is_const,
role,
};
match access_kind {
AccessPathKind::Local => {
self.var_stack.define(id.clone(), v);
},
AccessPathKind::Descope(n) => {
self.var_stack.define_parent(id.clone(), v, *n);
},
AccessPathKind::ExplicitGlobal => {
self.var_stack.define_parent(id.clone(), v, self.var_stack.depth());
},
}
}
#[inline]
fn track_variable_access(&mut self, path: &AccessPath, is_write: bool, fallback_hint: bool, span: &Range<usize>) {
if let Some(id) = &path.var_name() {
let tracker = match path.kind() {
AccessPathKind::Local => {
self.var_stack.get_mut(id)
},
AccessPathKind::Descope(n) => {
self.var_stack.get_parent_mut(id, n)
},
AccessPathKind::ExplicitGlobal => {
self.var_stack.get_parent_mut(id, self.var_stack.depth())
}
};
if let Some(tracker) = tracker {
if is_write {
tracker.writes += 1;
if tracker.is_const {
self.report_error(Problem::ConstantReassignment(id.to_string()), span);
}
} else {
tracker.add_read(!fallback_hint);
if tracker.has_fallible_read && tracker.role == VarRole::FallibleOptionalArgument {
self.report_warning(Problem::FallibleOptionalArgAccess(id.to_string()), span);
}
}
}
}
if path.kind().is_local() {
if let Some((capture_frame_height, captures)) = self.capture_stack.last_mut() {
if let Some(id) = path.var_name() {
if self.var_stack.height_of(&id).unwrap_or_default() < *capture_frame_height {
captures.insert(id);
}
}
}
}
}
#[inline]
fn analyze_top_vars(&mut self) {
let mut unused_vars: Vec<(String, VarRole, Range<usize>)> = vec![];
for (id, tracker) in self.var_stack.iter_top() {
if tracker.reads == 0 {
unused_vars.push((id.to_string(), tracker.role, tracker.def_span.clone()));
}
}
unused_vars.sort_by(|(.., a_span), (.., b_span)| a_span.start.cmp(&b_span.start));
for (name, role, span) in unused_vars {
match role {
VarRole::Normal => self.report_warning(Problem::UnusedVariable(name), &span),
VarRole::Argument => self.report_warning(Problem::UnusedParameter(name), &span),
VarRole::Function => self.report_warning(Problem::UnusedFunction(name), &span),
_ => {},
}
}
}
#[inline(always)]
fn parse_accessor(&mut self) -> ParseResult<Vec<Rst>> {
let mut accessors = vec![];
macro_rules! add_accessor {
($rst:expr) => {{
let rst = $rst;
accessors.push(rst);
}}
}
'read: loop {
self.reader.skip_ws();
if !accessors.is_empty() && self.reader.eat_where(|t| matches!(t, Some((RantToken::RightAngle, ..)))) {
break
}
let (is_def, is_const_def) = if let Some((def_token, ..))
= self.reader.take_where(|t| matches!(t, Some((RantToken::Dollar, ..)) | Some((RantToken::Percent, ..)))) {
match def_token {
RantToken::Dollar => (true, false),
RantToken::Percent => (true, true),
_ => unreachable!()
}
} else {
(false, false)
};
let access_start_span = self.reader.last_token_span();
self.reader.skip_ws();
if is_def {
let access_kind = self.parse_access_path_kind();
self.reader.skip_ws();
let var_name = self.parse_ident()?;
let def_span = access_start_span.start .. self.reader.last_token_span().end;
if let Some((token, _token_span)) = self.reader.next_solid() {
match token {
RantToken::RightAngle => {
if is_const_def {
self.track_variable(&var_name, &access_kind, true, VarRole::Normal, &def_span);
add_accessor!(Rst::ConstDef(var_name, access_kind, None));
} else {
self.track_variable(&var_name, &access_kind, false, VarRole::Normal, &def_span);
add_accessor!(Rst::VarDef(var_name, access_kind, None));
}
break 'read
},
RantToken::Semi => {
if is_const_def {
self.track_variable(&var_name, &access_kind, true, VarRole::Normal, &def_span);
add_accessor!(Rst::ConstDef(var_name, access_kind, None));
} else {
self.track_variable(&var_name, &access_kind, false, VarRole::Normal, &def_span);
add_accessor!(Rst::VarDef(var_name, access_kind, None));
}
continue 'read;
},
RantToken::Equals => {
self.reader.skip_ws();
let ParsedSequence {
sequence: setter_expr,
end_type: setter_end_type,
..
} = self.parse_sequence(SequenceParseMode::VariableAssignment)?;
let def_span = access_start_span.start .. self.reader.last_token_span().start;
if is_const_def {
self.track_variable(&var_name, &access_kind, true, VarRole::Normal, &def_span);
add_accessor!(Rst::ConstDef(var_name, access_kind, Some(Rc::new(setter_expr))));
} else {
self.track_variable(&var_name, &access_kind, false, VarRole::Normal, &def_span);
add_accessor!(Rst::VarDef(var_name, access_kind, Some(Rc::new(setter_expr))));
}
match setter_end_type {
SequenceEndType::VariableAssignDelim => {
continue 'read
},
SequenceEndType::VariableAccessEnd => {
break 'read
},
SequenceEndType::ProgramEnd => {
self.report_error(Problem::UnclosedVariableAccess, &self.reader.last_token_span());
return Err(())
},
_ => unreachable!()
}
},
_ => {
self.unexpected_last_token_error();
return Err(())
}
}
} else {
self.report_error(Problem::UnclosedVariableAccess, &self.reader.last_token_span());
return Err(())
}
} else {
let mut is_depth_op = false;
let (var_path, var_path_span) = self.parse_access_path(true)?;
self.reader.skip_ws();
if let Some((_, depth_op_range)) = self.reader.take_where(|t| matches!(t, Some((RantToken::And, _)))) {
if var_path.is_variable() && var_path.var_name().is_some() {
is_depth_op = true;
} else if var_path.len() == 1 && matches!(var_path.first(), Some(AccessPathComponent::DynamicKey(..))) {
self.report_error(Problem::DynamicDepth, &depth_op_range);
} else {
self.report_error(Problem::InvalidDepthUsage, &depth_op_range);
}
}
if let Some((token, cur_token_span)) = self.reader.next_solid() {
match token {
RantToken::RightAngle => {
self.track_variable_access(&var_path, false, false, &var_path_span);
add_accessor!(if is_depth_op {
Rst::VarDepth(var_path.var_name().unwrap(), var_path.kind(), None)
} else {
Rst::VarGet(Rc::new(var_path), None)
});
break 'read;
},
RantToken::Semi => {
self.track_variable_access(&var_path, false, false, &var_path_span);
add_accessor!(if is_depth_op {
Rst::VarDepth(var_path.var_name().unwrap(), var_path.kind(), None)
} else {
Rst::VarGet(Rc::new(var_path), None)
});
continue 'read;
},
RantToken::Question => {
self.reader.skip_ws();
let ParsedSequence {
sequence: fallback_expr,
end_type: fallback_end_type,
..
} = self.parse_sequence(SequenceParseMode::AccessorFallbackValue)?;
self.track_variable_access(&var_path, false, true, &var_path_span);
add_accessor!(if is_depth_op {
Rst::VarDepth(var_path.var_name().unwrap(), var_path.kind(), Some(Rc::new(fallback_expr)))
} else {
Rst::VarGet(Rc::new(var_path), Some(Rc::new(fallback_expr)))
});
match fallback_end_type {
SequenceEndType::AccessorFallbackValueToDelim => continue 'read,
SequenceEndType::AccessorFallbackValueToEnd => break 'read,
SequenceEndType::ProgramEnd => {
self.report_error(Problem::UnclosedVariableAccess, &cur_token_span);
return Err(())
},
_ => unreachable!()
}
},
RantToken::Equals => {
self.reader.skip_ws();
let ParsedSequence {
sequence: setter_rhs_expr,
end_type: setter_rhs_end,
..
} = self.parse_sequence(SequenceParseMode::VariableAssignment)?;
let assign_end_span = self.reader.last_token_span();
let setter_span = super_range(&access_start_span, &assign_end_span);
if var_path.is_anonymous() && var_path.len() == 1 {
self.report_error(Problem::AnonValueAssignment, &setter_span);
}
self.track_variable_access(&var_path, true, false, &setter_span);
add_accessor!(Rst::VarSet(Rc::new(var_path), Rc::new(setter_rhs_expr)));
if is_depth_op {
self.report_error(Problem::DepthAssignment, &(cur_token_span.start .. assign_end_span.start));
}
match setter_rhs_end {
SequenceEndType::VariableAccessEnd => {
break 'read;
},
SequenceEndType::VariableAssignDelim => {
continue 'read;
},
SequenceEndType::ProgramEnd => {
self.report_error(Problem::UnclosedVariableAccess, &self.reader.last_token_span());
return Err(())
},
_ => unreachable!()
}
},
_ => {
self.unexpected_last_token_error();
return Err(())
}
}
} else {
self.report_error(Problem::UnclosedVariableAccess, &self.reader.last_token_span());
return Err(())
}
}
}
Ok(accessors)
}
}