use eure_document::value::Tuple;
use eure_document::{
document::{EureDocument, source_constructor::SourceConstructor},
identifier::Identifier,
path::{ArrayIndexKind, PathSegment},
source::Comment,
text::{Language, SyntaxHint, Text, TextParseError},
value::{ObjectKey, PartialObjectKey, PrimitiveValue},
};
use eure_tree::tree::{InputSpan, RecursiveView};
use eure_tree::{prelude::*, tree::TerminalHandle};
use num_bigint::BigInt;
use regex::Regex;
use std::sync::LazyLock;
use crate::document::{CodeBlockError, DocumentConstructionError, InlineCodeError, OriginMap};
#[derive(Debug, Clone, Default)]
struct TerminalTokens {
terminals: Vec<TerminalToken>,
}
#[derive(Debug, Clone)] enum TerminalToken {
Input(InputSpan),
Dynamic(DynamicTokenId),
}
impl TerminalTokens {
pub fn new() -> Self {
Self {
terminals: Vec::new(),
}
}
pub fn push_terminal(&mut self, token: TerminalData) {
let new_token = match (self.terminals.last_mut(), token) {
(Some(TerminalToken::Input(span)), TerminalData::Input(input_span))
if span.end == input_span.start =>
{
span.end = input_span.end;
return;
}
(_, TerminalData::Dynamic(id)) => TerminalToken::Dynamic(id),
(_, TerminalData::Input(input_span)) => TerminalToken::Input(input_span),
};
self.terminals.push(new_token);
}
pub fn into_string(
self,
input: &str,
cst: &impl CstFacade,
) -> Result<String, DocumentConstructionError> {
let mut string = String::new();
for token in self.terminals {
match token {
TerminalToken::Input(span) => {
string.push_str(&input[span.start as usize..span.end as usize])
}
TerminalToken::Dynamic(id) => {
let str = cst
.dynamic_token(id)
.ok_or(DocumentConstructionError::DynamicTokenNotFound(id))?;
string.push_str(str);
}
}
}
Ok(string)
}
pub fn from_lit_str_1_list<F: CstFacade>(
list: &LitStr1ListHandle,
tree: &F,
) -> Result<Self, DocumentConstructionError> {
let mut tokens = Self::new();
if let Some(view) = list.get_view(tree)? {
let groups = view.get_all(tree)?;
for group in groups {
match group.get_view(tree)? {
LitStr1ListGroupView::NoSQuote(h) => {
let view = h.get_view(tree)?;
tokens.push_terminal(view.no_s_quote.get_data(tree)?);
}
LitStr1ListGroupView::SQuote(h) => {
let view = h.get_view(tree)?;
tokens.push_terminal(view.s_quote.get_data(tree)?);
}
}
}
}
Ok(tokens)
}
pub fn from_lit_str_2_list<F: CstFacade>(
list: &LitStr2ListHandle,
tree: &F,
) -> Result<Self, DocumentConstructionError> {
let mut tokens = Self::new();
if let Some(view) = list.get_view(tree)? {
let groups = view.get_all(tree)?;
for group in groups {
match group.get_view(tree)? {
LitStr2ListGroupView::NoSQuote(h) => {
let view = h.get_view(tree)?;
tokens.push_terminal(view.no_s_quote.get_data(tree)?);
}
LitStr2ListGroupView::SQuote(h) => {
let view = h.get_view(tree)?;
tokens.push_terminal(view.s_quote.get_data(tree)?);
}
}
}
}
Ok(tokens)
}
pub fn from_lit_str_3_list<F: CstFacade>(
list: &LitStr3ListHandle,
tree: &F,
) -> Result<Self, DocumentConstructionError> {
let mut tokens = Self::new();
if let Some(view) = list.get_view(tree)? {
let groups = view.get_all(tree)?;
for group in groups {
match group.get_view(tree)? {
LitStr3ListGroupView::NoSQuote(h) => {
let view = h.get_view(tree)?;
tokens.push_terminal(view.no_s_quote.get_data(tree)?);
}
LitStr3ListGroupView::SQuote(h) => {
let view = h.get_view(tree)?;
tokens.push_terminal(view.s_quote.get_data(tree)?);
}
}
}
}
Ok(tokens)
}
}
static INLINE_CODE_1_REGEX: LazyLock<Regex> =
LazyLock::new(|| Regex::new(r"^([a-zA-Z0-9_-]*)`([^`\r\n]*)`$").unwrap());
static DELIM_CODE_START_1_REGEX: LazyLock<Regex> =
LazyLock::new(|| Regex::new(r"^([a-zA-Z0-9_-]*)<`$").unwrap());
static DELIM_CODE_START_2_REGEX: LazyLock<Regex> =
LazyLock::new(|| Regex::new(r"^([a-zA-Z0-9_-]*)<<`$").unwrap());
static DELIM_CODE_START_3_REGEX: LazyLock<Regex> =
LazyLock::new(|| Regex::new(r"^([a-zA-Z0-9_-]*)<<<`$").unwrap());
static CODE_BLOCK_START_REGEX: LazyLock<Regex> =
LazyLock::new(|| Regex::new(r"^`+([a-zA-Z0-9_-]*)[ \t]*(?:\r\n|\r|\n)$").unwrap());
static INT_DOT_INT_PATTERN: LazyLock<Regex> =
LazyLock::new(|| Regex::new(r"^(\d[\d_]*)\.(\d[\d_]*)$").unwrap());
#[derive(Debug, Clone, Copy)]
enum CodeOrigin {
DelimCode1(DelimCode1Handle),
DelimCode2(DelimCode2Handle),
DelimCode3(DelimCode3Handle),
CodeBlock(CodeBlockHandle),
}
struct CodeStart {
language: Language,
syntax_hint: SyntaxHint,
terminals: TerminalTokens,
origin: Option<CodeOrigin>,
}
impl CodeStart {
fn new(language: Language, syntax_hint: SyntaxHint) -> Self {
Self {
language,
syntax_hint,
terminals: TerminalTokens::new(),
origin: None,
}
}
fn with_origin(language: Language, syntax_hint: SyntaxHint, origin: CodeOrigin) -> Self {
Self {
language,
syntax_hint,
terminals: TerminalTokens::new(),
origin: Some(origin),
}
}
}
pub struct CstInterpreter<'a> {
input: &'a str,
document: SourceConstructor,
code_start: Option<CodeStart>,
collecting_partial_keys: Vec<Vec<PartialObjectKey>>,
origins: OriginMap,
pending_code_origin: Option<CodeOrigin>,
pending_newlines: usize,
}
impl<'a> CstInterpreter<'a> {
pub fn new(input: &'a str) -> Self {
Self {
input,
document: SourceConstructor::new(),
code_start: None,
collecting_partial_keys: vec![],
origins: OriginMap::new(),
pending_code_origin: None,
pending_newlines: 0,
}
}
fn parse_inline_code_1(token: &str) -> Result<(Language, String), InlineCodeError> {
let caps = INLINE_CODE_1_REGEX
.captures(token)
.ok_or(InlineCodeError::InvalidInlineCode1Pattern)?;
let lang = caps.get(1).unwrap().as_str();
let content = caps.get(2).unwrap().as_str();
let language = if lang.is_empty() {
Language::Implicit
} else {
Language::new(lang.to_string())
};
Ok((language, content.to_string()))
}
fn parse_delim_code_start_1(token: &str) -> Result<Language, InlineCodeError> {
let caps = DELIM_CODE_START_1_REGEX
.captures(token)
.ok_or(InlineCodeError::InvalidDelimCodeStartPattern)?;
let lang = caps.get(1).unwrap().as_str();
let language = if lang.is_empty() {
Language::Implicit
} else {
Language::new(lang.to_string())
};
Ok(language)
}
fn parse_delim_code_start_2(token: &str) -> Result<Language, InlineCodeError> {
let caps = DELIM_CODE_START_2_REGEX
.captures(token)
.ok_or(InlineCodeError::InvalidDelimCodeStartPattern)?;
let lang = caps.get(1).unwrap().as_str();
let language = if lang.is_empty() {
Language::Implicit
} else {
Language::new(lang.to_string())
};
Ok(language)
}
fn parse_delim_code_start_3(token: &str) -> Result<Language, InlineCodeError> {
let caps = DELIM_CODE_START_3_REGEX
.captures(token)
.ok_or(InlineCodeError::InvalidDelimCodeStartPattern)?;
let lang = caps.get(1).unwrap().as_str();
let language = if lang.is_empty() {
Language::Implicit
} else {
Language::new(lang.to_string())
};
Ok(language)
}
fn parse_code_block_start(token: &str) -> Result<Language, CodeBlockError> {
let caps = CODE_BLOCK_START_REGEX
.captures(token)
.ok_or(CodeBlockError::InvalidCodeBlockStartPattern)?;
let lang = caps.get(1).unwrap().as_str();
let language = if lang.is_empty() {
Language::Implicit
} else {
Language::new(lang.to_string())
};
Ok(language)
}
pub fn into_document(self) -> EureDocument {
self.document.finish().document
}
pub fn into_document_and_origin_map(self) -> (EureDocument, OriginMap) {
(self.document.finish().document, self.origins)
}
pub fn into_source_document(self) -> eure_document::source::SourceDocument {
self.document.finish()
}
fn record_definition(
&mut self,
node_id: eure_document::document::NodeId,
cst_node_id: CstNodeId,
) {
self.origins.record_definition(node_id, cst_node_id);
}
fn record_value(&mut self, node_id: eure_document::document::NodeId, cst_node_id: CstNodeId) {
self.origins.record_value(node_id, cst_node_id);
}
fn record_key_origin(
&mut self,
map_node_id: eure_document::document::NodeId,
key: ObjectKey,
cst_node_id: CstNodeId,
) {
self.origins.record_key(map_node_id, key, cst_node_id);
}
fn get_terminal_str<T: TerminalHandle>(
&'a self,
tree: &'a impl CstFacade,
handle: T,
) -> Result<&'a str, DocumentConstructionError> {
match tree.get_terminal_str(self.input, handle)? {
Ok(str) => Ok(str),
Err(id) => Err(DocumentConstructionError::DynamicTokenNotFound(id)),
}
}
fn flush_blank_lines(&mut self) {
for _ in 1..self.pending_newlines {
self.document.blank_line();
}
self.pending_newlines = 0;
}
fn parse_str_terminal(
&self,
str_handle: StrHandle,
tree: &impl CstFacade,
) -> Result<String, DocumentConstructionError> {
let str_view = str_handle.get_view(tree)?;
let str_with_quotes = self.get_terminal_str(tree, str_view.str)?;
let str_content = str_with_quotes
.strip_prefix('"')
.and_then(|s| s.strip_suffix('"'))
.ok_or_else(|| DocumentConstructionError::InvalidStringKey {
node_id: str_handle.node_id(),
error: TextParseError::InvalidEndOfStringAfterEscape,
})?;
let text = Text::parse_quoted_string(str_content).map_err(|error| {
DocumentConstructionError::InvalidStringKey {
node_id: str_handle.node_id(),
error,
}
})?;
Ok(text.content)
}
fn get_key_ident_str(
&'a self,
tree: &'a impl CstFacade,
ident_handle: KeyIdentHandle,
) -> Result<&'a str, DocumentConstructionError> {
let ident_view = ident_handle.get_view(tree)?;
let ident_str = match ident_view {
KeyIdentView::Ident(ident_handle) => {
self.get_terminal_str(tree, ident_handle.get_view(tree)?.ident)?
}
KeyIdentView::True(true_handle) => {
self.get_terminal_str(tree, true_handle.get_view(tree)?.r#true)?
}
KeyIdentView::False(false_handle) => {
self.get_terminal_str(tree, false_handle.get_view(tree)?.r#false)?
}
KeyIdentView::Null(null_handle) => {
self.get_terminal_str(tree, null_handle.get_view(tree)?.r#null)?
}
};
Ok(ident_str)
}
fn handle_float_as_integer_keys<F: CstFacade>(
&mut self,
float_handle: FloatHandle,
tree: &F,
) -> Result<(), DocumentConstructionError> {
let float_view = float_handle.get_view(tree)?;
let str = self.get_terminal_str(tree, float_view.float)?;
let float_span = tree.span(float_handle.node_id()).ok_or_else(|| {
DocumentConstructionError::InvalidFloatKey {
node_id: float_handle.node_id(),
value: str.to_string(),
}
})?;
let captures = INT_DOT_INT_PATTERN.captures(str).ok_or_else(|| {
DocumentConstructionError::InvalidFloatKey {
node_id: float_handle.node_id(),
value: str.to_string(),
}
})?;
let first_int_str = &captures[1];
let first_big_int: BigInt = first_int_str
.replace('_', "")
.parse()
.map_err(|_| DocumentConstructionError::InvalidBigInt(first_int_str.to_string()))?;
let first_key = ObjectKey::Number(first_big_int);
let second_int_str = &captures[2];
let second_big_int: BigInt = second_int_str
.replace('_', "")
.parse()
.map_err(|_| DocumentConstructionError::InvalidBigInt(second_int_str.to_string()))?;
let second_key = ObjectKey::Number(second_big_int);
let first_span = InputSpan::new(
float_span.start,
float_span.start + first_int_str.len() as u32,
);
let second_span = InputSpan::new(
float_span.start + first_int_str.len() as u32 + 1, float_span.end,
);
let first_container_id = self.document.current_node_id();
self.origins
.record_key_span(first_container_id, first_key.clone(), first_span);
self.origins
.record_key_span_by_cst(float_handle.node_id(), first_key.clone(), first_span);
let float_node_id = float_handle.node_id(); self.document
.navigate(PathSegment::Value(first_key))
.map_err(|e| DocumentConstructionError::DocumentInsert {
error: e,
node_id: float_node_id, parent_node_id: Some(first_container_id),
})?;
let first_child_id = self.document.current_node_id();
self.record_definition(first_child_id, float_handle.node_id());
let second_container_id = self.document.current_node_id();
self.origins
.record_key_span(second_container_id, second_key.clone(), second_span);
self.origins.record_key_span_by_cst(
float_handle.node_id(),
second_key.clone(),
second_span,
);
let float_node_id = float_handle.node_id(); self.document
.navigate(PathSegment::Value(second_key.clone()))
.map_err(|e| DocumentConstructionError::DocumentInsert {
error: e,
node_id: float_node_id, parent_node_id: Some(second_container_id),
})?;
let second_child_id = self.document.current_node_id();
self.record_definition(second_child_id, float_handle.node_id());
Ok(())
}
}
impl<F: CstFacade> CstVisitor<F> for CstInterpreter<'_> {
type Error = DocumentConstructionError;
fn visit_eure(
&mut self,
handle: EureHandle,
view: EureView,
tree: &F,
) -> Result<(), Self::Error> {
let root_id = self.document.current_node_id();
if root_id == self.document.document().get_root_id() {
self.record_value(root_id, handle.node_id());
}
self.visit_eure_super(handle, view, tree)?;
let has_value_binding = view.eure_opt.get_view(tree)?.is_some();
if self.document.current_node().content.is_hole() && !has_value_binding {
self.document.bind_empty_map().map_err(|e| {
DocumentConstructionError::DocumentInsert {
error: e,
node_id: handle.node_id(),
parent_node_id: None,
}
})?;
}
Ok(())
}
fn visit_object(
&mut self,
handle: ObjectHandle,
view: ObjectView,
tree: &F,
) -> Result<(), Self::Error> {
let container_id = self.document.current_node_id();
self.record_value(container_id, handle.node_id());
self.document.suspend_path_tracking();
let result = (|| {
let has_value_binding = if let Some(object_opt_view) = view.object_opt.get_view(tree)? {
self.visit_value_binding_handle(object_opt_view.value_binding, tree)?;
true
} else {
false
};
if let Some(object_list_view) = view.object_list.get_view(tree)? {
for item in object_list_view.get_all(tree)? {
let scope = self.document.begin_scope();
self.visit_keys_handle(item.keys, tree)?;
let node_id = self.document.current_node_id();
self.document.require_hole().map_err(|e| {
DocumentConstructionError::DocumentInsert {
error: e,
node_id: handle.node_id(),
parent_node_id: None,
}
})?;
self.record_value(node_id, item.keys.node_id());
self.visit_value_handle(item.value, tree)?;
self.document.end_scope(scope).map_err(|e| {
DocumentConstructionError::DocumentInsert {
error: e,
node_id: handle.node_id(),
parent_node_id: None,
}
})?;
}
}
if self.document.current_node().content.is_hole() && !has_value_binding {
self.document.bind_empty_map().map_err(|e| {
DocumentConstructionError::DocumentInsert {
error: e,
node_id: handle.node_id(),
parent_node_id: None,
}
})?;
}
self.document.set_last_bound_node(container_id);
Ok(())
})();
self.document.resume_path_tracking();
result
}
fn visit_array(
&mut self,
handle: ArrayHandle,
view: ArrayView,
tree: &F,
) -> Result<(), Self::Error> {
let container_id = self.document.current_node_id();
self.record_value(container_id, handle.node_id());
self.document.suspend_path_tracking();
let result = (|| {
if let Some(elements_handle) = view.array_opt.get_view(tree)? {
let mut current = Some(elements_handle);
let mut index = 0usize;
while let Some(elem_handle) = current {
let elem_view = elem_handle.get_view(tree)?;
let scope = self.document.begin_scope();
let node_id = self
.document
.navigate(PathSegment::ArrayIndex(ArrayIndexKind::Specific(index)))
.map_err(|e| DocumentConstructionError::DocumentInsert {
error: e,
node_id: handle.node_id(),
parent_node_id: None,
})?;
self.record_value(node_id, handle.node_id());
self.visit_value_handle(elem_view.value, tree)?;
self.document.end_scope(scope).map_err(|e| {
DocumentConstructionError::DocumentInsert {
error: e,
node_id: handle.node_id(),
parent_node_id: None,
}
})?;
current =
if let Some(tail_handle) = elem_view.array_elements_opt.get_view(tree)? {
let tail_view = tail_handle.get_view(tree)?;
tail_view.array_elements_tail_opt.get_view(tree)?
} else {
None
};
index += 1;
}
} else {
self.document.bind_empty_array().map_err(|e| {
DocumentConstructionError::DocumentInsert {
error: e,
node_id: handle.node_id(),
parent_node_id: None,
}
})?;
}
self.document.set_last_bound_node(container_id);
Ok(())
})();
self.document.resume_path_tracking();
result
}
fn visit_tuple(
&mut self,
handle: TupleHandle,
view: TupleView,
tree: &F,
) -> Result<(), Self::Error> {
let container_id = self.document.current_node_id();
self.record_value(container_id, handle.node_id());
self.document.suspend_path_tracking();
let result = (|| {
if let Some(elements_handle) = view.tuple_opt.get_view(tree)? {
let mut current = Some(elements_handle);
let mut index = 0u8;
while let Some(elem_handle) = current {
let elem_view = elem_handle.get_view(tree)?;
let scope = self.document.begin_scope();
let node_id = self
.document
.navigate(PathSegment::TupleIndex(index))
.map_err(|e| DocumentConstructionError::DocumentInsert {
error: e,
node_id: handle.node_id(),
parent_node_id: None,
})?;
self.record_value(node_id, handle.node_id());
self.visit_value_handle(elem_view.value, tree)?;
self.document.end_scope(scope).map_err(|e| {
DocumentConstructionError::DocumentInsert {
error: e,
node_id: handle.node_id(),
parent_node_id: None,
}
})?;
current =
if let Some(tail_handle) = elem_view.tuple_elements_opt.get_view(tree)? {
let tail_view = tail_handle.get_view(tree)?;
tail_view.tuple_elements_tail_opt.get_view(tree)?
} else {
None
};
index = index.saturating_add(1);
}
} else {
self.document.bind_empty_tuple().map_err(|e| {
DocumentConstructionError::DocumentInsert {
error: e,
node_id: handle.node_id(),
parent_node_id: None,
}
})?;
}
self.document.set_last_bound_node(container_id);
Ok(())
})();
self.document.resume_path_tracking();
result
}
fn visit_key(&mut self, handle: KeyHandle, view: KeyView, tree: &F) -> Result<(), Self::Error> {
if let KeyView::Float(float_handle) = view {
return self.handle_float_as_integer_keys(float_handle, tree);
}
let container_id = self.document.current_node_id();
if let KeyView::Hole(hole_handle) = view {
let hole_view = hole_handle.get_view(tree)?;
let token_str = self.get_terminal_str(tree, hole_view.hole)?;
let label = if token_str == "!" {
None
} else {
Some(token_str[1..].parse::<Identifier>()?)
};
let key_cst_node = hole_handle.node_id();
self.document
.navigate_partial_map_entry(PartialObjectKey::Hole(label))
.map_err(|e| DocumentConstructionError::DocumentInsert {
error: e,
node_id: handle.node_id(),
parent_node_id: Some(container_id),
})?;
let child_id = self.document.current_node_id();
self.record_definition(child_id, key_cst_node);
return Ok(());
}
let (segment, key_origin_info) = match view {
KeyView::KeyIdent(ident_handle) => {
let ident_str = self.get_key_ident_str(tree, ident_handle)?;
let identifier: Identifier = ident_str.parse()?;
let object_key = ObjectKey::String(ident_str.to_string());
(
PathSegment::Ident(identifier),
Some((object_key, ident_handle.node_id())),
)
}
KeyView::ExtensionNameSpace(ext_handle) => {
let ext_view = ext_handle.get_view(tree)?;
let ident_str = self.get_key_ident_str(tree, ext_view.key_ident)?;
let identifier: Identifier = ident_str.parse()?;
(PathSegment::Extension(identifier), None)
}
KeyView::String(string_handle) => {
let content = self.parse_string(string_handle, tree)?;
let object_key = ObjectKey::String(content);
(
PathSegment::Value(object_key.clone()),
Some((object_key, string_handle.node_id())),
)
}
KeyView::Integer(int_handle) => {
let int_view = int_handle.get_view(tree)?;
let str = self.get_terminal_str(tree, int_view.integer)?;
let big_int: BigInt = str
.parse()
.map_err(|_| DocumentConstructionError::InvalidBigInt(str.to_string()))?;
let object_key = ObjectKey::Number(big_int);
(
PathSegment::Value(object_key.clone()),
Some((object_key, int_handle.node_id())),
)
}
KeyView::KeyTuple(tuple_handle) => {
self.collecting_partial_keys.push(vec![]);
self.visit_key_tuple_handle(tuple_handle, tree)?;
let keys = self.collecting_partial_keys.pop().expect(
"collecting_partial_keys stack should not be empty after visiting KeyTuple",
);
let object_keys: Result<Vec<_>, _> =
keys.iter().cloned().map(ObjectKey::try_from).collect();
if object_keys.is_err() {
let partial_key = PartialObjectKey::Tuple(Tuple(keys));
self.document
.navigate_partial_map_entry(partial_key)
.map_err(|e| DocumentConstructionError::DocumentInsert {
error: e,
node_id: handle.node_id(),
parent_node_id: Some(container_id),
})?;
let child_id = self.document.current_node_id();
self.record_definition(child_id, tuple_handle.node_id());
return Ok(());
}
let object_key = ObjectKey::Tuple(Tuple(
object_keys.expect("tuple key without holes must convert"),
));
(
PathSegment::Value(object_key.clone()),
Some((object_key, tuple_handle.node_id())),
)
}
KeyView::TupleIndex(tuple_index_handle) => {
let tuple_index_view = tuple_index_handle.get_view(tree)?;
let int_view = tuple_index_view.integer.get_view(tree)?;
let str = self.get_terminal_str(tree, int_view.integer)?;
let length: u8 =
str.parse()
.map_err(|_| DocumentConstructionError::InvalidTupleIndex {
node_id: tuple_index_handle.node_id(),
value: str.to_string(),
})?;
(PathSegment::TupleIndex(length), None)
}
KeyView::Float(_) => unreachable!("handled above"),
KeyView::Hole(_) => unreachable!("handled above"),
};
let key_cst_node = key_origin_info.as_ref().map(|(_, id)| *id);
if let Some((object_key, key_cst_node_id)) = key_origin_info {
self.record_key_origin(container_id, object_key, key_cst_node_id);
}
self.document
.navigate(segment)
.map_err(|e| DocumentConstructionError::DocumentInsert {
error: e,
node_id: handle.node_id(),
parent_node_id: None,
})?;
if let Some(cst_node_id) = key_cst_node {
let child_id = self.document.current_node_id();
self.record_definition(child_id, cst_node_id);
}
Ok(())
}
fn visit_array_marker(
&mut self,
handle: ArrayMarkerHandle,
view: ArrayMarkerView,
tree: &F,
) -> Result<(), Self::Error> {
let kind = if let Some(group_handle) = view.array_marker_opt.get_view(tree)? {
match group_handle.get_view(tree)? {
ArrayMarkerOptGroupView::Integer(int_handle) => {
let int_view = int_handle.get_view(tree)?;
let str = self.get_terminal_str(tree, int_view.integer)?;
let index: usize = str
.parse()
.map_err(|_| DocumentConstructionError::InvalidInteger(str.to_string()))?;
ArrayIndexKind::Specific(index)
}
ArrayMarkerOptGroupView::Caret(_) => ArrayIndexKind::Current,
}
} else {
ArrayIndexKind::Push
};
self.document
.navigate(PathSegment::ArrayIndex(kind))
.map_err(|e| DocumentConstructionError::DocumentInsert {
error: e,
node_id: handle.node_id(),
parent_node_id: None,
})?;
Ok(())
}
fn visit_key_value(
&mut self,
_handle: KeyValueHandle,
view: KeyValueView,
tree: &F,
) -> Result<(), Self::Error> {
let partial_key = match view {
KeyValueView::Integer(int_handle) => {
let int_view = int_handle.get_view(tree)?;
let str = self.get_terminal_str(tree, int_view.integer)?;
let big_int: BigInt = str
.parse()
.map_err(|_| DocumentConstructionError::InvalidBigInt(str.to_string()))?;
PartialObjectKey::Number(big_int)
}
KeyValueView::Boolean(bool_handle) => {
let bool_view = bool_handle.get_view(tree)?;
match bool_view {
BooleanView::True(_) => PartialObjectKey::String("true".to_string()),
BooleanView::False(_) => PartialObjectKey::String("false".to_string()),
}
}
KeyValueView::Str(str_handle) => {
let result = self.parse_str_terminal(str_handle, tree)?;
PartialObjectKey::String(result)
}
KeyValueView::KeyTuple(tuple_handle) => {
self.collecting_partial_keys.push(vec![]);
self.visit_key_tuple_handle(tuple_handle, tree)?;
let keys = self.collecting_partial_keys.pop().expect(
"collecting_partial_keys stack should not be empty after visiting KeyTuple",
);
PartialObjectKey::Tuple(Tuple(keys))
}
KeyValueView::Hole(hole_handle) => {
let hole_view = hole_handle.get_view(tree)?;
let token_str = self.get_terminal_str(tree, hole_view.hole)?;
let label = if token_str == "!" {
None
} else {
Some(token_str[1..].parse::<Identifier>()?)
};
PartialObjectKey::Hole(label)
}
};
self.collecting_partial_keys
.last_mut()
.expect("collecting_partial_keys stack should not be empty when visiting KeyValue")
.push(partial_key);
Ok(())
}
fn visit_binding(
&mut self,
handle: BindingHandle,
view: BindingView,
tree: &F,
) -> Result<(), Self::Error> {
self.document.begin_binding();
let scope = self.document.begin_scope();
self.visit_keys_handle(view.keys, tree)?;
let node_id = self.document.current_node_id();
self.document
.require_hole()
.map_err(|e| DocumentConstructionError::DocumentInsert {
error: e,
node_id: handle.node_id(),
parent_node_id: None,
})?;
self.record_value(node_id, handle.node_id());
let binding_rhs_view = view.binding_rhs.get_view(tree)?;
let is_block = matches!(binding_rhs_view, BindingRhsView::SectionBinding(_));
if is_block {
self.document.begin_eure_block();
}
self.visit_binding_rhs_handle(view.binding_rhs, tree)?;
if is_block {
self.document.end_eure_block().map_err(|e| {
DocumentConstructionError::DocumentInsert {
error: e,
node_id: handle.node_id(),
parent_node_id: None,
}
})?;
}
self.document
.end_scope(scope)
.map_err(|e| DocumentConstructionError::DocumentInsert {
error: e,
node_id: handle.node_id(),
parent_node_id: None,
})?;
if is_block {
self.document.end_binding_block().map_err(|e| {
DocumentConstructionError::DocumentInsert {
error: e,
node_id: handle.node_id(),
parent_node_id: None,
}
})?;
} else {
self.document.end_binding_value().map_err(|e| {
DocumentConstructionError::DocumentInsert {
error: e,
node_id: handle.node_id(),
parent_node_id: None,
}
})?;
}
Ok(())
}
fn visit_section(
&mut self,
handle: SectionHandle,
view: SectionView,
tree: &F,
) -> Result<(), Self::Error> {
let SectionView {
at: _,
keys,
section_body,
} = view;
self.document.begin_section();
let scope = self.document.begin_scope();
self.visit_keys_handle(keys, tree)?;
let node_id = self.document.current_node_id();
self.document
.require_hole()
.map_err(|e| DocumentConstructionError::DocumentInsert {
error: e,
node_id: handle.node_id(),
parent_node_id: None,
})?;
self.record_value(node_id, handle.node_id());
let section_body_view = section_body.get_view(tree)?;
let is_block = matches!(section_body_view, SectionBodyView::BlockBody(_));
if is_block {
self.document.begin_eure_block();
} else {
self.document.begin_section_items();
}
self.visit_section_body_handle(section_body, tree)?;
if is_block {
self.document.end_eure_block().map_err(|e| {
DocumentConstructionError::DocumentInsert {
error: e,
node_id: handle.node_id(),
parent_node_id: None,
}
})?;
}
self.document
.end_scope(scope)
.map_err(|e| DocumentConstructionError::DocumentInsert {
error: e,
node_id: handle.node_id(),
parent_node_id: None,
})?;
if is_block {
self.document.end_section_block().map_err(|e| {
DocumentConstructionError::DocumentInsert {
error: e,
node_id: handle.node_id(),
parent_node_id: None,
}
})?;
} else {
self.document.end_section_items().map_err(|e| {
DocumentConstructionError::DocumentInsert {
error: e,
node_id: handle.node_id(),
parent_node_id: None,
}
})?;
}
Ok(())
}
fn visit_section_body(
&mut self,
handle: SectionBodyHandle,
view: SectionBodyView,
tree: &F,
) -> Result<(), Self::Error> {
match view {
SectionBodyView::SectionBodyOpt(section_body_opt) => {
if let Some(flat_body_handle) = section_body_opt.get_view(tree)? {
self.visit_flat_body_handle(flat_body_handle, tree)?;
} else {
self.document.bind_empty_map().map_err(|e| {
DocumentConstructionError::DocumentInsert {
error: e,
node_id: handle.node_id(),
parent_node_id: None,
}
})?;
}
}
SectionBodyView::BlockBody(_) => {
self.visit_section_body_super(handle, view, tree)?;
}
}
Ok(())
}
fn visit_flat_body(
&mut self,
handle: FlatBodyHandle,
view: FlatBodyView,
tree: &F,
) -> Result<(), Self::Error> {
let section_head_view = view.section_head.get_view(tree)?;
let has_bindings = view.flat_body_list.get_view(tree)?.is_some();
let has_root_binding = match §ion_head_view {
SectionHeadView::RootBinding(_) => true,
SectionHeadView::NewlineHead(newline_head_handle) => {
let newline_head_view = newline_head_handle.get_view(tree)?;
newline_head_view.newline_head_opt.get_view(tree)?.is_some()
}
};
let is_empty = !has_root_binding && !has_bindings;
if is_empty {
self.document.bind_empty_map().map_err(|e| {
DocumentConstructionError::DocumentInsert {
error: e,
node_id: handle.node_id(),
parent_node_id: None,
}
})?;
} else {
self.visit_flat_body_super(handle, view, tree)?;
}
Ok(())
}
fn visit_root_text_binding(
&mut self,
handle: RootTextBindingHandle,
view: RootTextBindingView,
tree: &F,
) -> Result<(), Self::Error> {
let text_view = view.root_text_binding_opt_0.get_view(tree)?;
let text = if let Some(text_handle) = text_view {
let text_view = text_handle.get_view(tree)?;
let text_str = self.get_terminal_str(tree, text_view.text)?;
Text::parse_text_binding(text_str).map_err(|error| {
DocumentConstructionError::InvalidStringKey {
node_id: text_handle.node_id(),
error,
}
})?
} else {
Text::new(String::new(), Language::Plaintext)
};
let node_id = self.document.current_node_id();
self.document
.bind_primitive(PrimitiveValue::Text(text))
.map_err(|e| DocumentConstructionError::DocumentInsert {
error: e,
node_id: handle.node_id(),
parent_node_id: None,
})?;
self.record_value(node_id, handle.node_id());
Ok(())
}
fn visit_null(
&mut self,
handle: NullHandle,
_view: NullView,
_tree: &F,
) -> Result<(), Self::Error> {
let node_id = self.document.current_node_id();
self.document
.bind_primitive(PrimitiveValue::Null)
.map_err(|e| DocumentConstructionError::DocumentInsert {
error: e,
node_id: handle.node_id(),
parent_node_id: None,
})?;
self.record_value(node_id, handle.node_id());
Ok(())
}
fn visit_true(
&mut self,
handle: TrueHandle,
_view: TrueView,
_tree: &F,
) -> Result<(), Self::Error> {
let node_id = self.document.current_node_id();
self.document
.bind_primitive(PrimitiveValue::Bool(true))
.map_err(|e| DocumentConstructionError::DocumentInsert {
error: e,
node_id: handle.node_id(),
parent_node_id: None,
})?;
self.record_value(node_id, handle.node_id());
Ok(())
}
fn visit_false(
&mut self,
handle: FalseHandle,
_view: FalseView,
_tree: &F,
) -> Result<(), Self::Error> {
let node_id = self.document.current_node_id();
self.document
.bind_primitive(PrimitiveValue::Bool(false))
.map_err(|e| DocumentConstructionError::DocumentInsert {
error: e,
node_id: handle.node_id(),
parent_node_id: None,
})?;
self.record_value(node_id, handle.node_id());
Ok(())
}
fn visit_integer(
&mut self,
handle: IntegerHandle,
view: IntegerView,
tree: &F,
) -> Result<(), Self::Error> {
let str = self.get_terminal_str(tree, view.integer)?;
let clean_str = str.replace('_', "");
let big_int: BigInt = clean_str
.parse()
.map_err(|_| DocumentConstructionError::InvalidBigInt(str.to_string()))?;
let node_id = self.document.current_node_id();
self.document
.bind_primitive(PrimitiveValue::Integer(big_int))
.map_err(|e| DocumentConstructionError::DocumentInsert {
error: e,
node_id: handle.node_id(),
parent_node_id: None,
})?;
self.record_value(node_id, handle.node_id());
Ok(())
}
fn visit_inf(&mut self, handle: InfHandle, view: InfView, tree: &F) -> Result<(), Self::Error> {
let str = self.get_terminal_str(tree, view.inf)?;
let float = if str.starts_with('-') {
f64::NEG_INFINITY
} else {
f64::INFINITY
};
let node_id = self.document.current_node_id();
self.document
.bind_primitive(PrimitiveValue::F64(float))
.map_err(|e| DocumentConstructionError::DocumentInsert {
error: e,
node_id: handle.node_id(),
parent_node_id: None,
})?;
self.record_value(node_id, handle.node_id());
Ok(())
}
fn visit_na_n(
&mut self,
handle: NaNHandle,
_view: NaNView,
_tree: &F,
) -> Result<(), Self::Error> {
let node_id = self.document.current_node_id();
self.document
.bind_primitive(PrimitiveValue::F64(f64::NAN))
.map_err(|e| DocumentConstructionError::DocumentInsert {
error: e,
node_id: handle.node_id(),
parent_node_id: None,
})?;
self.record_value(node_id, handle.node_id());
Ok(())
}
fn visit_float(
&mut self,
handle: FloatHandle,
view: FloatView,
tree: &F,
) -> Result<(), Self::Error> {
let str = self.get_terminal_str(tree, view.float)?;
let (num_str, is_f32) = if let Some(stripped) = str.strip_suffix("f32") {
(stripped, true)
} else if let Some(stripped) = str.strip_suffix("f64") {
(stripped, false)
} else {
(str, false)
};
let clean_str = num_str.replace('_', "");
let node_id = self.document.current_node_id();
let primitive = if is_f32 {
let float: f32 = clean_str
.parse()
.map_err(|_| DocumentConstructionError::InvalidFloat(str.to_string()))?;
PrimitiveValue::F32(float)
} else {
let float: f64 = clean_str
.parse()
.map_err(|_| DocumentConstructionError::InvalidFloat(str.to_string()))?;
PrimitiveValue::F64(float)
};
self.document.bind_primitive(primitive).map_err(|e| {
DocumentConstructionError::DocumentInsert {
error: e,
node_id: handle.node_id(),
parent_node_id: None,
}
})?;
self.record_value(node_id, handle.node_id());
Ok(())
}
fn visit_hole(
&mut self,
handle: HoleHandle,
view: HoleView,
tree: &F,
) -> Result<(), Self::Error> {
let token_str = self.get_terminal_str(tree, view.hole)?;
let label = if token_str.len() > 1 {
Some(token_str[1..].parse::<Identifier>()?)
} else {
None
};
let node_id = self.document.current_node_id();
self.document
.bind_hole(label)
.map_err(|e| DocumentConstructionError::DocumentInsert {
error: e,
node_id: handle.node_id(),
parent_node_id: None,
})?;
self.record_value(node_id, handle.node_id());
Ok(())
}
fn visit_inline_code_1(
&mut self,
handle: InlineCode1Handle,
view: InlineCode1View,
tree: &F,
) -> Result<(), Self::Error> {
let token_str = self.get_terminal_str(tree, view.inline_code_1)?;
let (language, content) = Self::parse_inline_code_1(token_str).map_err(|error| {
DocumentConstructionError::InvalidInlineCode {
node_id: view.inline_code_1.node_id(),
error,
}
})?;
let text = Text::with_syntax_hint(content, language, SyntaxHint::Inline1);
let node_id = self.document.current_node_id();
self.document
.bind_primitive(PrimitiveValue::Text(text))
.map_err(|e| DocumentConstructionError::DocumentInsert {
error: e,
node_id: handle.node_id(),
parent_node_id: None,
})?;
self.record_value(node_id, handle.node_id());
Ok(())
}
fn visit_delim_code_1(
&mut self,
handle: DelimCode1Handle,
view: DelimCode1View,
tree: &F,
) -> Result<(), Self::Error> {
self.pending_code_origin = Some(CodeOrigin::DelimCode1(handle));
self.visit_delim_code_1_super(handle, view, tree)?;
self.pending_code_origin = None;
Ok(())
}
fn visit_delim_code_start_1(
&mut self,
_handle: DelimCodeStart1Handle,
view: DelimCodeStart1View,
tree: &F,
) -> Result<(), Self::Error> {
let token_str = self.get_terminal_str(tree, view.delim_code_start_1)?;
let language = Self::parse_delim_code_start_1(token_str).map_err(|error| {
DocumentConstructionError::InvalidInlineCode {
node_id: view.delim_code_start_1.node_id(),
error,
}
})?;
self.code_start = if let Some(origin) = self.pending_code_origin {
Some(CodeStart::with_origin(language, SyntaxHint::Delim1, origin))
} else {
Some(CodeStart::new(language, SyntaxHint::Delim1))
};
Ok(())
}
fn visit_delim_code_end_1(
&mut self,
handle: DelimCodeEnd1Handle,
_view: DelimCodeEnd1View,
tree: &F,
) -> Result<(), Self::Error> {
if let Some(code_start) = self.code_start.take() {
let content = code_start.terminals.into_string(self.input, tree)?;
let text = Text::with_syntax_hint(content, code_start.language, code_start.syntax_hint);
let node_id = self.document.current_node_id();
self.document
.bind_primitive(PrimitiveValue::Text(text))
.map_err(|e| DocumentConstructionError::DocumentInsert {
error: e,
node_id: handle.node_id(),
parent_node_id: None,
})?;
if let Some(CodeOrigin::DelimCode1(delim_handle)) = code_start.origin {
self.record_value(node_id, delim_handle.node_id());
}
}
Ok(())
}
fn visit_delim_code_2(
&mut self,
handle: DelimCode2Handle,
view: DelimCode2View,
tree: &F,
) -> Result<(), Self::Error> {
self.pending_code_origin = Some(CodeOrigin::DelimCode2(handle));
self.visit_delim_code_2_super(handle, view, tree)?;
self.pending_code_origin = None;
Ok(())
}
fn visit_delim_code_start_2(
&mut self,
_handle: DelimCodeStart2Handle,
view: DelimCodeStart2View,
tree: &F,
) -> Result<(), Self::Error> {
let token_str = self.get_terminal_str(tree, view.delim_code_start_2)?;
let language = Self::parse_delim_code_start_2(token_str).map_err(|error| {
DocumentConstructionError::InvalidInlineCode {
node_id: view.delim_code_start_2.node_id(),
error,
}
})?;
self.code_start = if let Some(origin) = self.pending_code_origin {
Some(CodeStart::with_origin(language, SyntaxHint::Delim2, origin))
} else {
Some(CodeStart::new(language, SyntaxHint::Delim2))
};
Ok(())
}
fn visit_delim_code_end_2(
&mut self,
handle: DelimCodeEnd2Handle,
_view: DelimCodeEnd2View,
tree: &F,
) -> Result<(), Self::Error> {
if let Some(code_start) = self.code_start.take() {
let content = code_start.terminals.into_string(self.input, tree)?;
let text = Text::with_syntax_hint(content, code_start.language, code_start.syntax_hint);
let node_id = self.document.current_node_id();
self.document
.bind_primitive(PrimitiveValue::Text(text))
.map_err(|e| DocumentConstructionError::DocumentInsert {
error: e,
node_id: handle.node_id(),
parent_node_id: None,
})?;
if let Some(CodeOrigin::DelimCode2(delim_handle)) = code_start.origin {
self.record_value(node_id, delim_handle.node_id());
}
}
Ok(())
}
fn visit_delim_code_3(
&mut self,
handle: DelimCode3Handle,
view: DelimCode3View,
tree: &F,
) -> Result<(), Self::Error> {
self.pending_code_origin = Some(CodeOrigin::DelimCode3(handle));
self.visit_delim_code_3_super(handle, view, tree)?;
self.pending_code_origin = None;
Ok(())
}
fn visit_delim_code_start_3(
&mut self,
_handle: DelimCodeStart3Handle,
view: DelimCodeStart3View,
tree: &F,
) -> Result<(), Self::Error> {
let token_str = self.get_terminal_str(tree, view.delim_code_start_3)?;
let language = Self::parse_delim_code_start_3(token_str).map_err(|error| {
DocumentConstructionError::InvalidInlineCode {
node_id: view.delim_code_start_3.node_id(),
error,
}
})?;
self.code_start = if let Some(origin) = self.pending_code_origin {
Some(CodeStart::with_origin(language, SyntaxHint::Delim3, origin))
} else {
Some(CodeStart::new(language, SyntaxHint::Delim3))
};
Ok(())
}
fn visit_delim_code_end_3(
&mut self,
handle: DelimCodeEnd3Handle,
_view: DelimCodeEnd3View,
tree: &F,
) -> Result<(), Self::Error> {
if let Some(code_start) = self.code_start.take() {
let content = code_start.terminals.into_string(self.input, tree)?;
let text = Text::with_syntax_hint(content, code_start.language, code_start.syntax_hint);
let node_id = self.document.current_node_id();
self.document
.bind_primitive(PrimitiveValue::Text(text))
.map_err(|e| DocumentConstructionError::DocumentInsert {
error: e,
node_id: handle.node_id(),
parent_node_id: None,
})?;
if let Some(CodeOrigin::DelimCode3(delim_handle)) = code_start.origin {
self.record_value(node_id, delim_handle.node_id());
}
}
Ok(())
}
fn visit_code_block(
&mut self,
handle: CodeBlockHandle,
view: CodeBlockView,
tree: &F,
) -> Result<(), Self::Error> {
self.pending_code_origin = Some(CodeOrigin::CodeBlock(handle));
self.visit_code_block_super(handle, view, tree)?;
self.pending_code_origin = None;
Ok(())
}
fn visit_code_block_start_3(
&mut self,
_handle: CodeBlockStart3Handle,
view: CodeBlockStart3View,
tree: &F,
) -> Result<(), Self::Error> {
let token_str = self.get_terminal_str(tree, view.code_block_start_3)?;
let language = Self::parse_code_block_start(token_str).map_err(|error| {
DocumentConstructionError::InvalidCodeBlock {
node_id: view.code_block_start_3.node_id(),
error,
}
})?;
self.code_start = if let Some(origin) = self.pending_code_origin {
Some(CodeStart::with_origin(language, SyntaxHint::Block3, origin))
} else {
Some(CodeStart::new(language, SyntaxHint::Block3))
};
Ok(())
}
fn visit_code_block_end_3(
&mut self,
handle: CodeBlockEnd3Handle,
_view: CodeBlockEnd3View,
tree: &F,
) -> Result<(), Self::Error> {
if let Some(code_start) = self.code_start.take() {
let content = code_start.terminals.into_string(self.input, tree)?;
let text =
Text::parse_indented_block(code_start.language, content, code_start.syntax_hint)
.map_err(|e| DocumentConstructionError::InvalidCodeBlock {
node_id: handle.node_id(),
error: CodeBlockError::from(e),
})?;
let node_id = self.document.current_node_id();
self.document
.bind_primitive(PrimitiveValue::Text(text))
.map_err(|e| DocumentConstructionError::DocumentInsert {
error: e,
node_id: handle.node_id(),
parent_node_id: None,
})?;
if let Some(CodeOrigin::CodeBlock(block_handle)) = code_start.origin {
self.record_value(node_id, block_handle.node_id());
}
}
Ok(())
}
fn visit_code_block_start_4(
&mut self,
_handle: CodeBlockStart4Handle,
view: CodeBlockStart4View,
tree: &F,
) -> Result<(), Self::Error> {
let token_str = self.get_terminal_str(tree, view.code_block_start_4)?;
let language = Self::parse_code_block_start(token_str).map_err(|error| {
DocumentConstructionError::InvalidCodeBlock {
node_id: view.code_block_start_4.node_id(),
error,
}
})?;
self.code_start = if let Some(origin) = self.pending_code_origin {
Some(CodeStart::with_origin(language, SyntaxHint::Block4, origin))
} else {
Some(CodeStart::new(language, SyntaxHint::Block4))
};
Ok(())
}
fn visit_code_block_end_4(
&mut self,
handle: CodeBlockEnd4Handle,
_view: CodeBlockEnd4View,
tree: &F,
) -> Result<(), Self::Error> {
if let Some(code_start) = self.code_start.take() {
let content = code_start.terminals.into_string(self.input, tree)?;
let text =
Text::parse_indented_block(code_start.language, content, code_start.syntax_hint)
.map_err(|e| DocumentConstructionError::InvalidCodeBlock {
node_id: handle.node_id(),
error: CodeBlockError::from(e),
})?;
let node_id = self.document.current_node_id();
self.document
.bind_primitive(PrimitiveValue::Text(text))
.map_err(|e| DocumentConstructionError::DocumentInsert {
error: e,
node_id: handle.node_id(),
parent_node_id: None,
})?;
if let Some(CodeOrigin::CodeBlock(block_handle)) = code_start.origin {
self.record_value(node_id, block_handle.node_id());
}
}
Ok(())
}
fn visit_code_block_start_5(
&mut self,
_handle: CodeBlockStart5Handle,
view: CodeBlockStart5View,
tree: &F,
) -> Result<(), Self::Error> {
let token_str = self.get_terminal_str(tree, view.code_block_start_5)?;
let language = Self::parse_code_block_start(token_str).map_err(|error| {
DocumentConstructionError::InvalidCodeBlock {
node_id: view.code_block_start_5.node_id(),
error,
}
})?;
self.code_start = if let Some(origin) = self.pending_code_origin {
Some(CodeStart::with_origin(language, SyntaxHint::Block5, origin))
} else {
Some(CodeStart::new(language, SyntaxHint::Block5))
};
Ok(())
}
fn visit_code_block_end_5(
&mut self,
handle: CodeBlockEnd5Handle,
_view: CodeBlockEnd5View,
tree: &F,
) -> Result<(), Self::Error> {
if let Some(code_start) = self.code_start.take() {
let content = code_start.terminals.into_string(self.input, tree)?;
let text =
Text::parse_indented_block(code_start.language, content, code_start.syntax_hint)
.map_err(|e| DocumentConstructionError::InvalidCodeBlock {
node_id: handle.node_id(),
error: CodeBlockError::from(e),
})?;
let node_id = self.document.current_node_id();
self.document
.bind_primitive(PrimitiveValue::Text(text))
.map_err(|e| DocumentConstructionError::DocumentInsert {
error: e,
node_id: handle.node_id(),
parent_node_id: None,
})?;
if let Some(CodeOrigin::CodeBlock(block_handle)) = code_start.origin {
self.record_value(node_id, block_handle.node_id());
}
}
Ok(())
}
fn visit_code_block_start_6(
&mut self,
_handle: CodeBlockStart6Handle,
view: CodeBlockStart6View,
tree: &F,
) -> Result<(), Self::Error> {
let token_str = self.get_terminal_str(tree, view.code_block_start_6)?;
let language = Self::parse_code_block_start(token_str).map_err(|error| {
DocumentConstructionError::InvalidCodeBlock {
node_id: view.code_block_start_6.node_id(),
error,
}
})?;
self.code_start = if let Some(origin) = self.pending_code_origin {
Some(CodeStart::with_origin(language, SyntaxHint::Block6, origin))
} else {
Some(CodeStart::new(language, SyntaxHint::Block6))
};
Ok(())
}
fn visit_code_block_end_6(
&mut self,
handle: CodeBlockEnd6Handle,
_view: CodeBlockEnd6View,
tree: &F,
) -> Result<(), Self::Error> {
if let Some(code_start) = self.code_start.take() {
let content = code_start.terminals.into_string(self.input, tree)?;
let text =
Text::parse_indented_block(code_start.language, content, code_start.syntax_hint)
.map_err(|e| DocumentConstructionError::InvalidCodeBlock {
node_id: handle.node_id(),
error: CodeBlockError::from(e),
})?;
let node_id = self.document.current_node_id();
self.document
.bind_primitive(PrimitiveValue::Text(text))
.map_err(|e| DocumentConstructionError::DocumentInsert {
error: e,
node_id: handle.node_id(),
parent_node_id: None,
})?;
if let Some(CodeOrigin::CodeBlock(block_handle)) = code_start.origin {
self.record_value(node_id, block_handle.node_id());
}
}
Ok(())
}
fn visit_text_binding(
&mut self,
handle: TextBindingHandle,
view: TextBindingView,
tree: &F,
) -> Result<(), Self::Error> {
let text_view = view.text_binding_opt_0.get_view(tree)?;
let text = if let Some(text_handle) = text_view {
let text_view = text_handle.get_view(tree)?;
let text_str = self.get_terminal_str(tree, text_view.text)?;
Text::parse_text_binding(text_str).map_err(|error| {
DocumentConstructionError::InvalidStringKey {
node_id: text_handle.node_id(),
error,
}
})?
} else {
Text::new(String::new(), Language::Plaintext)
};
let node_id = self.document.current_node_id();
self.document
.bind_primitive(PrimitiveValue::Text(text))
.map_err(|e| DocumentConstructionError::DocumentInsert {
error: e,
node_id: handle.node_id(),
parent_node_id: None,
})?;
self.record_value(node_id, handle.node_id());
Ok(())
}
fn visit_strings(
&mut self,
handle: StringsHandle,
view: StringsView,
tree: &F,
) -> Result<(), Self::Error> {
let (first_str, first_hint) = self.parse_string_with_hint(view.string, tree)?;
let (result, syntax_hint) = if let Some(list_view) = view.strings_list.get_view(tree)? {
let mut parts = vec![first_str];
for item in list_view.get_all(tree)? {
let (part, _) = self.parse_string_with_hint(item.string, tree)?;
parts.push(part);
}
(parts.join(""), first_hint)
} else {
(first_str, first_hint)
};
let text = Text::with_syntax_hint(result, Language::Plaintext, syntax_hint);
let node_id = self.document.current_node_id();
self.document
.bind_primitive(PrimitiveValue::Text(text))
.map_err(|e| DocumentConstructionError::DocumentInsert {
error: e,
node_id: handle.node_id(),
parent_node_id: None,
})?;
self.record_value(node_id, handle.node_id());
Ok(())
}
fn visit_new_line_terminal(
&mut self,
terminal: NewLine,
data: TerminalData,
tree: &F,
) -> Result<(), Self::Error> {
self.pending_newlines += 1;
self.visit_new_line_terminal_super(terminal, data, tree)
}
fn visit_line_comment_terminal(
&mut self,
terminal: LineComment,
data: TerminalData,
tree: &F,
) -> Result<(), Self::Error> {
self.flush_blank_lines();
let text = self.get_terminal_str(tree, terminal)?;
let text = text.strip_prefix("//").unwrap_or(text);
let text = text
.strip_prefix(' ')
.unwrap_or(text)
.trim_end_matches(['\r', '\n'])
.to_string();
self.document.comment(Comment::Line(text));
self.visit_line_comment_terminal_super(terminal, data, tree)
}
fn visit_block_comment_terminal(
&mut self,
terminal: BlockComment,
data: TerminalData,
tree: &F,
) -> Result<(), Self::Error> {
self.flush_blank_lines();
let text = self.get_terminal_str(tree, terminal)?;
let text = text
.strip_prefix("/*")
.and_then(|s| s.strip_suffix("*/"))
.unwrap_or(text)
.to_string();
self.document.comment(Comment::Block(text));
self.visit_block_comment_terminal_super(terminal, data, tree)
}
fn visit_terminal(
&mut self,
_id: CstNodeId,
kind: TerminalKind,
data: TerminalData,
_tree: &F,
) -> Result<(), Self::Error> {
if !matches!(
kind,
TerminalKind::NewLine
| TerminalKind::Whitespace
| TerminalKind::LineComment
| TerminalKind::BlockComment
) {
self.flush_blank_lines();
}
if let Some(code_start) = &mut self.code_start {
code_start.terminals.push_terminal(data);
}
Ok(())
}
fn then_construct_error(
&mut self,
_node_data: Option<CstNode>,
_parent: CstNodeId,
_kind: NodeKind,
error: CstConstructError,
_tree: &F,
) -> Result<(), Self::Error> {
Err(DocumentConstructionError::CstError(error))
}
}
impl<'a> CstInterpreter<'a> {
fn parse_string_with_hint<F: CstFacade>(
&self,
handle: StringHandle,
tree: &F,
) -> Result<(String, SyntaxHint), DocumentConstructionError> {
match handle.get_view(tree)? {
StringView::Str(h) => self
.parse_str_terminal(h, tree)
.map(|s| (s, SyntaxHint::Str)),
StringView::LitStr(h) => self.parse_lit_str(h, tree).map(|s| (s, SyntaxHint::LitStr)),
StringView::LitStr1(h) => self
.parse_lit_str_1(h, tree)
.map(|s| (s, SyntaxHint::LitStr1)),
StringView::LitStr2(h) => self
.parse_lit_str_2(h, tree)
.map(|s| (s, SyntaxHint::LitStr2)),
StringView::LitStr3(h) => self
.parse_lit_str_3(h, tree)
.map(|s| (s, SyntaxHint::LitStr3)),
}
}
fn parse_string<F: CstFacade>(
&self,
handle: StringHandle,
tree: &F,
) -> Result<String, DocumentConstructionError> {
self.parse_string_with_hint(handle, tree).map(|(s, _)| s)
}
fn parse_lit_str<F: CstFacade>(
&self,
handle: LitStrHandle,
tree: &F,
) -> Result<String, DocumentConstructionError> {
let view = handle.get_view(tree)?;
let token = self.get_terminal_str(tree, view.lit_str)?;
let content = token
.strip_prefix('\'')
.and_then(|s| s.strip_suffix('\''))
.ok_or_else(|| DocumentConstructionError::InvalidLiteralStr {
node_id: handle.node_id(),
})?;
Ok(content.to_string())
}
fn parse_lit_str_1<F: CstFacade>(
&self,
handle: LitStr1Handle,
tree: &F,
) -> Result<String, DocumentConstructionError> {
let view = handle.get_view(tree)?;
let terminals = TerminalTokens::from_lit_str_1_list(&view.lit_str_1_list, tree)?;
terminals.into_string(self.input, tree)
}
fn parse_lit_str_2<F: CstFacade>(
&self,
handle: LitStr2Handle,
tree: &F,
) -> Result<String, DocumentConstructionError> {
let view = handle.get_view(tree)?;
let terminals = TerminalTokens::from_lit_str_2_list(&view.lit_str_2_list, tree)?;
terminals.into_string(self.input, tree)
}
fn parse_lit_str_3<F: CstFacade>(
&self,
handle: LitStr3Handle,
tree: &F,
) -> Result<String, DocumentConstructionError> {
let view = handle.get_view(tree)?;
let terminals = TerminalTokens::from_lit_str_3_list(&view.lit_str_3_list, tree)?;
terminals.into_string(self.input, tree)
}
}
#[cfg(test)]
mod tests {
use super::*;
use eure_tree::tree::{ConcreteSyntaxTree, CstNodeData, InputSpan, TerminalData};
fn create_dummy_cst() -> ConcreteSyntaxTree<TerminalKind, NonTerminalKind> {
let root_data = CstNodeData::new_non_terminal(
NonTerminalKind::Root,
NonTerminalData::Input(InputSpan::EMPTY),
);
ConcreteSyntaxTree::new(root_data)
}
mod parse_inline_code_1_tests {
use super::*;
#[test]
fn test_simple_code_without_language() {
let result = CstInterpreter::parse_inline_code_1("`hello`");
assert!(result.is_ok());
let (language, content) = result.unwrap();
assert_eq!(language, Language::Implicit);
assert_eq!(content, "hello");
}
#[test]
fn test_code_with_language() {
let result = CstInterpreter::parse_inline_code_1("rust`fn main() {}`");
assert!(result.is_ok());
let (language, content) = result.unwrap();
assert_eq!(language, Language::Other("rust".into()));
assert_eq!(content, "fn main() {}");
}
#[test]
fn test_empty_code() {
let result = CstInterpreter::parse_inline_code_1("``");
assert!(result.is_ok());
let (language, content) = result.unwrap();
assert_eq!(language, Language::Implicit);
assert_eq!(content, "");
}
#[test]
fn test_code_with_special_chars() {
let result = CstInterpreter::parse_inline_code_1("`hello world!@#$%`");
assert!(result.is_ok());
let (language, content) = result.unwrap();
assert_eq!(language, Language::Implicit);
assert_eq!(content, "hello world!@#$%");
}
#[test]
fn test_language_with_hyphen_and_underscore() {
let result = CstInterpreter::parse_inline_code_1("foo-bar_123`content`");
assert!(result.is_ok());
let (language, content) = result.unwrap();
assert_eq!(language, Language::Other("foo-bar_123".into()));
assert_eq!(content, "content");
}
#[test]
fn test_no_backticks() {
let result = CstInterpreter::parse_inline_code_1("no backticks");
assert!(result.is_err());
assert!(matches!(
result.unwrap_err(),
InlineCodeError::InvalidInlineCode1Pattern
));
}
#[test]
fn test_single_backtick() {
let result = CstInterpreter::parse_inline_code_1("`");
assert!(result.is_err());
assert!(matches!(
result.unwrap_err(),
InlineCodeError::InvalidInlineCode1Pattern
));
}
}
mod parse_delim_code_start_1_tests {
use super::*;
#[test]
fn test_no_language() {
let result = CstInterpreter::parse_delim_code_start_1("<`");
assert!(result.is_ok());
assert_eq!(result.unwrap(), Language::Implicit);
}
#[test]
fn test_with_language() {
let result = CstInterpreter::parse_delim_code_start_1("rust<`");
assert!(result.is_ok());
assert_eq!(result.unwrap(), Language::Other("rust".into()));
}
#[test]
fn test_with_complex_language() {
let result = CstInterpreter::parse_delim_code_start_1("foo-bar_123<`");
assert!(result.is_ok());
assert_eq!(result.unwrap(), Language::Other("foo-bar_123".into()));
}
#[test]
fn test_no_delim() {
let result = CstInterpreter::parse_delim_code_start_1("rust");
assert!(result.is_err());
assert!(matches!(
result.unwrap_err(),
InlineCodeError::InvalidDelimCodeStartPattern
));
}
}
mod parse_delim_code_start_2_tests {
use super::*;
#[test]
fn test_no_language() {
let result = CstInterpreter::parse_delim_code_start_2("<<`");
assert!(result.is_ok());
assert_eq!(result.unwrap(), Language::Implicit);
}
#[test]
fn test_with_language() {
let result = CstInterpreter::parse_delim_code_start_2("rust<<`");
assert!(result.is_ok());
assert_eq!(result.unwrap(), Language::Other("rust".into()));
}
}
mod parse_delim_code_start_3_tests {
use super::*;
#[test]
fn test_no_language() {
let result = CstInterpreter::parse_delim_code_start_3("<<<`");
assert!(result.is_ok());
assert_eq!(result.unwrap(), Language::Implicit);
}
#[test]
fn test_with_language() {
let result = CstInterpreter::parse_delim_code_start_3("rust<<<`");
assert!(result.is_ok());
assert_eq!(result.unwrap(), Language::Other("rust".into()));
}
}
mod parse_code_block_start_tests {
use crate::document::CodeBlockError;
use super::*;
#[test]
fn test_no_language_3_backticks() {
let result = CstInterpreter::parse_code_block_start("```\n");
assert!(result.is_ok());
assert_eq!(result.unwrap(), Language::Implicit);
}
#[test]
fn test_with_language_3_backticks() {
let result = CstInterpreter::parse_code_block_start("```rust\n");
assert!(result.is_ok());
assert_eq!(result.unwrap(), Language::Other("rust".into()));
}
#[test]
fn test_with_language_4_backticks() {
let result = CstInterpreter::parse_code_block_start("````python\n");
assert!(result.is_ok());
assert_eq!(result.unwrap(), Language::Other("python".into()));
}
#[test]
fn test_with_language_5_backticks() {
let result = CstInterpreter::parse_code_block_start("`````javascript\n");
assert!(result.is_ok());
assert_eq!(result.unwrap(), Language::Other("javascript".into()));
}
#[test]
fn test_with_language_6_backticks() {
let result = CstInterpreter::parse_code_block_start("``````typescript\n");
assert!(result.is_ok());
assert_eq!(result.unwrap(), Language::Other("typescript".into()));
}
#[test]
fn test_language_with_trailing_whitespace() {
let result = CstInterpreter::parse_code_block_start("```rust \n");
assert!(result.is_ok());
assert_eq!(result.unwrap(), Language::Other("rust".into()));
}
#[test]
fn test_language_with_leading_whitespace_is_invalid() {
let result = CstInterpreter::parse_code_block_start("``` rust\n");
assert!(result.is_err());
assert!(matches!(
result.unwrap_err(),
CodeBlockError::InvalidCodeBlockStartPattern
));
}
#[test]
fn test_language_with_carriage_return() {
let result = CstInterpreter::parse_code_block_start("```rust\r\n");
assert!(result.is_ok());
assert_eq!(result.unwrap(), Language::Other("rust".into()));
}
#[test]
fn test_language_with_only_carriage_return() {
let result = CstInterpreter::parse_code_block_start("```rust\r");
assert!(result.is_ok());
assert_eq!(result.unwrap(), Language::Other("rust".into()));
}
#[test]
fn test_empty_language_with_spaces() {
let result = CstInterpreter::parse_code_block_start("``` \n");
assert!(result.is_ok());
assert_eq!(result.unwrap(), Language::Implicit);
}
#[test]
fn test_no_newline() {
let result = CstInterpreter::parse_code_block_start("```rust");
assert!(result.is_err());
assert!(matches!(
result.unwrap_err(),
CodeBlockError::InvalidCodeBlockStartPattern
));
}
#[test]
fn test_complex_language_tag() {
let result = CstInterpreter::parse_code_block_start("```foo-bar_123\n");
assert!(result.is_ok());
assert_eq!(result.unwrap(), Language::Other("foo-bar_123".into()));
}
}
#[test]
fn test_push_input() {
let mut tokens = TerminalTokens::new();
let span = InputSpan::new(0, 5);
tokens.push_terminal(TerminalData::Input(span));
assert_eq!(tokens.terminals.len(), 1);
match tokens.terminals[0] {
TerminalToken::Input(s) => assert_eq!(s, span),
_ => panic!("Expected Input token"),
}
}
#[test]
fn test_merge_adjacent_inputs() {
let mut tokens = TerminalTokens::new();
let span1 = InputSpan::new(0, 5);
let span2 = InputSpan::new(5, 10);
tokens.push_terminal(TerminalData::Input(span1));
tokens.push_terminal(TerminalData::Input(span2));
assert_eq!(tokens.terminals.len(), 1);
match tokens.terminals[0] {
TerminalToken::Input(s) => {
assert_eq!(s.start, 0);
assert_eq!(s.end, 10);
}
_ => panic!("Expected Input token"),
}
}
#[test]
fn test_dont_merge_non_adjacent() {
let mut tokens = TerminalTokens::new();
let span1 = InputSpan::new(0, 5);
let span2 = InputSpan::new(6, 10);
tokens.push_terminal(TerminalData::Input(span1));
tokens.push_terminal(TerminalData::Input(span2));
assert_eq!(tokens.terminals.len(), 2);
match tokens.terminals[0] {
TerminalToken::Input(s) => assert_eq!(s, span1),
_ => panic!("Expected Input token at 0"),
}
match tokens.terminals[1] {
TerminalToken::Input(s) => assert_eq!(s, span2),
_ => panic!("Expected Input token at 1"),
}
}
#[test]
fn test_dont_merge_dynamic() {
let mut tokens = TerminalTokens::new();
let span1 = InputSpan::new(0, 5);
let id = DynamicTokenId(1);
let span2 = InputSpan::new(5, 10);
tokens.push_terminal(TerminalData::Input(span1));
tokens.push_terminal(TerminalData::Dynamic(id));
tokens.push_terminal(TerminalData::Input(span2));
assert_eq!(tokens.terminals.len(), 3);
match tokens.terminals[0] {
TerminalToken::Input(s) => assert_eq!(s, span1),
_ => panic!("Expected Input token at 0"),
}
match tokens.terminals[1] {
TerminalToken::Dynamic(d) => assert_eq!(d, id),
_ => panic!("Expected Dynamic token at 1"),
}
match tokens.terminals[2] {
TerminalToken::Input(s) => assert_eq!(s, span2),
_ => panic!("Expected Input token at 2"),
}
}
#[test]
fn test_into_string() {
let mut cst = create_dummy_cst();
let id = cst.insert_dynamic_terminal("world");
let mut tokens = TerminalTokens::new();
tokens.push_terminal(TerminalData::Input(InputSpan::new(0, 6)));
tokens.push_terminal(TerminalData::Dynamic(id));
tokens.push_terminal(TerminalData::Input(InputSpan::new(6, 7)));
let input = "Hello !";
let result = tokens.into_string(input, &cst).expect("Should succeed");
assert_eq!(result, "Hello world!");
}
#[test]
fn test_into_string_missing_dynamic() {
let cst = create_dummy_cst(); let id = DynamicTokenId(999);
let mut tokens = TerminalTokens::new();
tokens.push_terminal(TerminalData::Dynamic(id));
let result = tokens.into_string("", &cst);
assert!(matches!(
result,
Err(DocumentConstructionError::DynamicTokenNotFound(i)) if i == id
));
}
}