use crate::*; macro_rules! def_peek_seek(($peek:ident, $seek:ident, $seek_start:ident, $seek_length:ident)=>{ /// Find a slice corrensponding to a syntax token. pub const fn $peek (source: &str) -> DslPerhaps<&str> { match $seek(source) { Ok(Some((start, length))) => Ok(Some(str_range(source, start, start + length))), Ok(None) => Ok(None), Err(e) => Err(e) } } /// Find a start and length corresponding to a syntax token. pub const fn $seek (source: &str) -> DslPerhaps<(usize, usize)> { match $seek_start(source) { Ok(Some(start)) => match $seek_length(str_from(source, start)) { Ok(Some(length)) => Ok(Some((start, length))), Ok(None) => Ok(None), Err(e) => Err(e), }, Ok(None) => Ok(None), Err(e) => Err(e) } } }); def_peek_seek!(exp_peek, exp_seek, exp_seek_start, exp_seek_length); pub const fn is_exp_start (c: char) -> bool { c == '(' } pub const fn is_exp_end (c: char) -> bool { c == ')' } pub const fn exp_seek_start (mut source: &str) -> DslPerhaps { for_each!((i, c) in char_indices(source) => if is_exp_start(c) { return Ok(Some(i)) } else if !is_whitespace(c) { return Err(Unexpected(c)) }); Ok(None) } pub const fn exp_seek_length (mut source: &str) -> DslPerhaps { let mut depth = 0; for_each!((i, c) in char_indices(source) => if is_exp_start(c) { depth += 1; } else if is_exp_end(c) { if depth == 0 { return Err(Unexpected(c)) } else if depth == 1 { return Ok(Some(i + 1)) } else { depth -= 1; } }); Err(Incomplete) } def_peek_seek!(sym_peek, sym_seek, sym_seek_start, sym_seek_length); pub const fn is_sym_start (c: char) -> bool { matches!(c, ':'|'@') } pub const fn is_sym_char (c: char) -> bool { matches!(c, 'a'..='z'|'A'..='Z'|'0'..='9'|'-') } pub const fn is_sym_end (c: char) -> bool { matches!(c, ' '|'\n'|'\r'|'\t'|')') } pub const fn sym_seek_start (mut source: &str) -> DslPerhaps { for_each!((i, c) in char_indices(source) => if is_sym_start(c) { return Ok(Some(i)) } else if !is_whitespace(c) { return Err(Unexpected(c)) }); Ok(None) } pub const fn sym_seek_length (mut source: &str) -> DslPerhaps { for_each!((i, c) in char_indices(source) => if is_sym_end(c) { return Ok(Some(i)) } else if !is_sym_char(c) { return Err(Unexpected(c)) }); Ok(Some(source.len())) } def_peek_seek!(key_peek, key_seek, key_seek_start, key_seek_length); pub const fn is_key_start (c: char) -> bool { matches!(c, '/'|('a'..='z')) } pub const fn is_key_char (c: char) -> bool { matches!(c, 'a'..='z'|'0'..='9'|'-'|'/') } pub const fn is_key_end (c: char) -> bool { matches!(c, ' '|'\n'|'\r'|'\t'|')') } pub const fn key_seek_start (mut source: &str) -> DslPerhaps { for_each!((i, c) in char_indices(source) => if is_key_start(c) { return Ok(Some(i)) } else if !is_whitespace(c) { return Err(Unexpected(c)) }); Ok(None) } pub const fn key_seek_length (mut source: &str) -> DslPerhaps { for_each!((i, c) in char_indices(source) => if is_key_end(c) { return Ok(Some(i)) } else if !is_key_char(c) { return Err(Unexpected(c)) }); Ok(Some(source.len())) } def_peek_seek!(text_peek, text_seek, text_seek_start, text_seek_length); pub const fn is_text_start (c: char) -> bool { matches!(c, '"') } pub const fn is_text_end (c: char) -> bool { matches!(c, '"') } pub const fn text_seek_start (mut source: &str) -> DslPerhaps { for_each!((i, c) in char_indices(source) => if is_text_start(c) { return Ok(Some(i)) } else if !is_whitespace(c) { return Err(Unexpected(c)) }); Ok(None) } pub const fn text_seek_length (mut source: &str) -> DslPerhaps { for_each!((i, c) in char_indices(source) => if is_text_end(c) { return Ok(Some(i)) }); Ok(None) } def_peek_seek!(num_peek, num_seek, num_seek_start, num_seek_length); pub const fn num_seek_start (mut source: &str) -> DslPerhaps { for_each!((i, c) in char_indices(source) => if is_digit(c) { return Ok(Some(i)); } else if !is_whitespace(c) { return Err(Unexpected(c)) }); Ok(None) } pub const fn num_seek_length (mut source: &str) -> DslPerhaps { for_each!((i, c) in char_indices(source) => if is_num_end(c) { return Ok(Some(i)) } else if !is_digit(c) { return Err(Unexpected(c)) }); Ok(None) } pub const fn is_digit (c: char) -> bool { matches!(c, '0'..='9') } pub const fn is_num_end (c: char) -> bool { matches!(c, ' '|'\n'|'\r'|'\t'|')') } pub const fn to_number (digits: &str) -> Result { let mut iter = char_indices(digits); let mut value = 0; while let Some(((_, c), next)) = iter.next() { match to_digit(c) { Ok(digit) => value = 10 * value + digit, Err(e) => return Err(e), } iter = next; } Ok(value) } pub const fn to_digit (c: char) -> Result { Ok(match c { '0' => 0, '1' => 1, '2' => 2, '3' => 3, '4' => 4, '5' => 5, '6' => 6, '7' => 7, '8' => 8, '9' => 9, _ => return Err(Unexpected(c)) }) } pub const fn peek (mut src: &str) -> DslPerhaps<&str> { Ok(Some(match () { _ if let Ok(Some(exp)) = exp_peek(src) => exp, _ if let Ok(Some(sym)) = sym_peek(src) => sym, _ if let Ok(Some(key)) = key_peek(src) => key, _ if let Ok(Some(num)) = num_peek(src) => num, _ if let Ok(Some(text)) = text_peek(src) => text, _ => { for_each!((i, c) in char_indices(src) => if !is_whitespace(c) { return Err(Unexpected(c)) }); return Ok(None) } })) } pub const fn seek (mut src: &str) -> DslPerhaps<(usize, usize)> { Ok(Some(match () { _ if let Ok(Some(exp)) = exp_seek(src) => exp, _ if let Ok(Some(sym)) = sym_seek(src) => sym, _ if let Ok(Some(key)) = key_seek(src) => key, _ if let Ok(Some(num)) = num_seek(src) => num, _ if let Ok(Some(text)) = text_seek(src) => text, _ => { for_each!((i, c) in char_indices(src) => if !is_whitespace(c) { return Err(Unexpected(c)) }); return Ok(None) } })) } pub const fn is_whitespace (c: char) -> bool { matches!(c, ' '|'\n'|'\r'|'\t') }