///|
pub(all) enum RdfLexerMode {
NTriples
Turtle
N3
} derive(Eq)
///|
pub(all) struct RdfLexer {
mode : RdfLexerMode
lenient : Bool
}
///|
pub fn RdfLexer::RdfLexer(mode : RdfLexerMode, lenient : Bool) -> RdfLexer {
{ mode, lenient }
}
///|
fn RdfLexer::recognize_iri(
self : RdfLexer,
data : ArrayView[Byte],
options : LexerOptions,
location_pos : SourcePos,
) -> (Int, Result[LocatedTokenOrLineJump, TokenRecognizerError])? {
let string = []
let mut i = 1
// 循环直到找到 '>'
for ;; {
let end_opt = array_view_memchr(b'>', data[i:])
let end = match end_opt {
Some(e) => e
None =>
return Some(
(
data.length(),
Err(
TokenRecognizerError::IriTruncated(
data[i:],
Range(
location_pos.offset + i,
location_pos.offset + i + data.length(),
),
),
),
),
)
}
// 将内容追加到 string,同时处理 Unicode 转义
let mut j = i
while j < i + end {
let byte = data[j]
if byte == b'\\' && j + 1 < data.length() {
let next_byte = data[j + 1]
if next_byte == b'u' && j + 5 < i + end {
// \uXXXX - 4 位十六进制
let hex = str_from_utf8(data[j + 2:j + 6], Range::Range(j + 2, j + 6)).unwrap_or(
"0000",
)
// 检查是否全是有效的十六进制字符
if !is_valid_hex_string(hex) {
return Some(
(
i + end + 1,
Err(
TokenRecognizerError::InvalidErrMessage(
data[j:j + 6],
Range(location_pos.offset + j, location_pos.offset + j + 6),
"Invalid unicode escape: \\u followed by non-hex characters",
),
),
),
)
}
let code = hex.to_upper()
let codepoint = hex_to_uint(code)
let ch = UInt::to_byte(codepoint)
string.append([ch])
j += 6
} else if next_byte == b'U' && j + 9 < i + end {
// \UXXXXXXXX - 8 位十六进制
let hex = str_from_utf8(
data[j + 2:j + 10],
Range::Range(j + 2, j + 10),
).unwrap_or("00000000")
// 检查是否全是有效的十六进制字符
if !is_valid_hex_string(hex) {
return Some(
(
i + end + 1,
Err(
TokenRecognizerError::InvalidErrMessage(
data[j:j + 10],
Range(location_pos.offset + j, location_pos.offset + j + 10),
"Invalid unicode escape: \\U followed by non-hex characters",
),
),
),
)
}
let code = hex.to_upper()
let codepoint = hex_to_uint(code)
let ch = UInt::to_byte(codepoint)
string.append([ch])
j += 10
} else {
// 其他转义,保持原样
string.push(byte)
j += 1
}
} else {
string.push(byte)
j += 1
}
}
i += end + 1
return Some(
(
i,
self.parse_iri(
string,
Range(location_pos.offset, location_pos.offset + i),
options,
),
),
)
}
}
///|
fn RdfLexer::parse_iri(
self : RdfLexer,
iri_bytes : Array[Byte],
position : Range,
options : LexerOptions,
) -> Result[LocatedTokenOrLineJump, TokenRecognizerError] {
// 1. 将字节数组转换为字符串,处理可能的 UTF-8 错误
let iri_str = match string_from_utf8(iri_bytes, position) {
Ok(s) => s
Err(e) => return Err(e)
}
if iri_str.contains("\\") {
return Err(
TokenRecognizerError::InvalidErrMessage(
iri_bytes, position, "IRIs in N-Triples cannot contain backslashes",
),
)
}
// 2. 解析或解析并合并 Base IRI
let final_iri = if options.base_iri is Some(base_iri) {
// 如果有 base_iri,尝试解析相对 IRI
let base_iri = base_iri.as_ref()
if self.lenient {
// 宽松模式:直接合并,不检查有效性
Ok(base_iri.resolve_unchecked(iri_str))
} else {
// 严格模式:解析并检查有效性
base_iri
.resolve(iri_str)
.map_err(fn(e) {
TokenRecognizerError::IriParsingError(e.to_string(), position)
})
}
} else {
// 没有 base_iri,直接解析绝对 IRI
let iri = Iri::new(iri_str)
if self.lenient {
Ok(IriRef::parse_unchecked(iri))
} else {
// 严格模式
IriRef::parse(iri).map_err(fn(e) {
TokenRecognizerError::IriParsingError(e.to_string(), position)
})
}
}
// 3. 返回 Token
let final_iri = match final_iri {
Ok(iri) => iri
Err(e) => return Err(e)
}
// 使用传入的 position (已经是全局 Range)
Ok(
LocatedTokenOrLineJump::LocatedN3Token(
LocatedN3Token(N3Token::Iri(final_iri.base_iri), position),
),
)
}
///|
fn RdfLexer::recognize_pname_or_keyword(
self : RdfLexer,
data : ArrayView[Byte],
is_ending : Bool,
location_pos : SourcePos,
) -> (Int, Result[LocatedTokenOrLineJump, TokenRecognizerError])? {
let mut i = 0
// 解析前缀部分,直到遇到 ':'
for ;; {
match RdfLexer::recognize_unicode_char(data[i:], i) {
Some(Ok((c, consumed))) =>
if c == ':' {
i += consumed
} else if i == 0 {
// 第一个字符必须符合 base 规则
if !RdfLexer::is_possible_pn_chars_base(c) {
return Some(
(
consumed,
Err(
TokenRecognizerError::InvalidErrMessage(
data[0:consumed],
Range(location_pos.offset, location_pos.offset + consumed),
"'\{c}' is not allowed at the beginning of a prefix name",
),
),
),
)
}
i += consumed
// 后续字符可以是 pn_chars 或 '.'
} else if RdfLexer::is_possible_pn_chars(c) || c == '.' {
i += consumed
} else {
// 遇到无效字符,作为 PlainKeyword 返回
while array_view_has_prefix(data[:i], b".") {
i -= 1
}
let range = Range(location_pos.offset, location_pos.offset + i)
return Some(
(
i,
str_from_utf8(
data[:i],
Range(location_pos.offset, location_pos.offset + i),
).map(s => {
LocatedTokenOrLineJump::LocatedN3Token(
LocatedN3Token(N3Token::PlainKeyword(s), range),
)
}),
),
)
}
Some(Err(e)) => return Some((e.range().end - location_pos.offset, Err(e))) // 注意:子函数返回的 range 可能需要调整,这里假设 e.range 是局部的
None =>
// 没有更多字符可解析
if is_ending {
// 结束输入
while array_view_has_prefix(data[:i], b".") {
i -= 1
}
return Some(
if i == 0 {
(
1,
Err(
TokenRecognizerError::UnexpectedEndOfInput(
data[0:1],
Range(location_pos.offset, location_pos.offset + 1),
),
),
)
} else {
(
i,
str_from_utf8(
data[:i],
Range(location_pos.offset, location_pos.offset + i),
).map(s => {
LocatedTokenOrLineJump::LocatedN3Token(
LocatedN3Token(
N3Token::PlainKeyword(s),
Range(location_pos.offset, location_pos.offset + i),
),
)
}),
)
},
)
} else if i > 0 && data.length() > 0 {
// 继续等待更多输入,返回 None 让 lexer 获取更多数据
return None
} else {
return None
}
}
}
// 提取前缀 (如果代码能到达这里,说明遇到了 ':')
let pn_prefix = match
str_from_utf8(
data[:i - 1],
Range(location_pos.offset, location_pos.offset + i - 1),
) {
Ok(prefix) => prefix
Err(e) => return Some((i, Err(e)))
}
// 前缀不能以 '.' 结尾
if pn_prefix.has_suffix(".") {
return Some(
(
i,
Err(
TokenRecognizerError::InvalidErrMessage(
data[0:i],
Range(location_pos.offset, location_pos.offset + i),
"'\{pn_prefix}' is not a valid prefix: prefixes are not allowed to end with '.'",
),
),
),
)
}
// 解析本地部分
match
self.recognize_optional_pn_local(
data[i:],
is_ending,
location_pos.offset + i,
) {
Some((consumed, Ok((localx, might_be_invalid_iri)))) => {
let total_consumed = consumed + i
let range = Range(
location_pos.offset,
location_pos.offset + total_consumed,
)
Some(
(
total_consumed,
Ok(
LocatedTokenOrLineJump::LocatedN3Token(
LocatedN3Token::new(
N3Token::PrefixedName(
prefix=pn_prefix,
localx~,
might_be_invalid_iri~,
),
range,
),
),
),
),
)
}
Some((consumed, Err(e))) => Some((consumed + i, Err(e)))
None => None
}
}
///|
fn RdfLexer::recognize_variable(
self : RdfLexer,
data : ArrayView[Byte],
is_ending : Bool,
location_pos : SourcePos,
) -> (Int, Result[LocatedTokenOrLineJump, TokenRecognizerError])? {
if data.is_empty() || data[0] != b'?' {
return None
}
// 修复:对 Option 类型进行模式匹配
// 传递 ? 之后的起始位置
match
self.recognize_optional_pn_local(
data[1:],
is_ending,
location_pos.offset + 1,
) {
Some((consumed, name_result)) => {
let total_consumed = consumed + 1
let token_result = match name_result {
Ok((name, _)) if !name.is_empty() =>
Ok(
LocatedTokenOrLineJump::LocatedN3Token(
LocatedN3Token::new(
N3Token::Variable(name),
Range(location_pos.offset, location_pos.offset + total_consumed),
),
),
)
Ok((_name, _)) =>
Err(
TokenRecognizerError::InvalidErrMessage(
data[0:total_consumed],
Range(location_pos.offset, location_pos.offset + total_consumed),
"A variable name is not allowed to be empty",
),
)
Err(e) => Err(e)
}
Some((total_consumed, token_result))
}
None => None
}
}
///|
fn RdfLexer::recognize_optional_pn_local(
self : RdfLexer,
data : ArrayView[Byte],
is_ending : Bool,
start_offset : Int, // 接收起始全局偏移量
) -> (Int, Result[(String, Bool), TokenRecognizerError])? {
let mut i = 0
let mut buffer : StringBuilder? = None
let mut position_that_is_already_in_buffer = 0
let mut might_be_invalid_iri = false
let mut ends_with_unescaped_dot = 0
for ;; {
if RdfLexer::recognize_unicode_char(data[i:], i) is Some(r) {
match r {
Ok((c, consumed)) =>
if c == '%' {
i += 1
match (data.get(i), data.get(i + 1)) {
(Some(byte_a), Some(byte_b)) => {
let a = Byte::to_char(byte_a)
i += 1
let b = Byte::to_char(byte_b)
if !a.is_ascii_hexdigit() || !b.is_ascii_hexdigit() {
return Some(
(
i + 1,
Err(
TokenRecognizerError::InvalidErrMessage(
data[i - 2:i],
Range(start_offset + i - 2, start_offset + i),
"escapes in IRIs should be % followed by two hexadecimal characters, found '\{a}\{b}'",
),
),
),
)
}
i += 1
ends_with_unescaped_dot = 0
}
_ =>
// 数据不足,等待更多输入
return None
}
} else if c == '\'' {
i += 1
match data.get(i) {
Some(next_byte) => {
let a = Byte::to_char(next_byte)
if self.lenient ||
a == '_' ||
a == '~' ||
a == '.' ||
a == '-' ||
a == '!' ||
a == '$' ||
a == '&' ||
a == '\'' ||
a == '(' ||
a == ')' ||
a == '*' ||
a == '+' ||
a == ',' ||
a == ';' ||
a == '=' {
// allowed
} else if a == '/' ||
a == '?' ||
a == '#' ||
a == '@' ||
a == '%' {
might_be_invalid_iri = true
} else {
return Some(
(
i + 1,
Err(
TokenRecognizerError::InvalidErrMessage(
data[i:i + 1],
Range(start_offset + i, start_offset + i + 1),
"The character that are allowed to be escaped in IRIs are _~.-!$&'()*+,;=/?#@%, found '\{a}'",
),
),
),
)
}
if buffer is None {
buffer = Some(StringBuilder::new())
}
match buffer {
Some(b) => {
if i - position_that_is_already_in_buffer > 1 {
match
str_from_utf8(
data[position_that_is_already_in_buffer:i - 1],
Range(
start_offset + position_that_is_already_in_buffer,
start_offset + i - 1,
),
) {
Ok(data_str) => b.write_stringview(data_str)
Err(e) => return Some((i, Err(e)))
}
}
b.write_char(a)
i += 1
position_that_is_already_in_buffer = i
ends_with_unescaped_dot = 0
}
None => abort(" cannot happen")
}
}
None =>
// 数据不足,等待更多输入
return None
}
} else if i == 0 {
if !(RdfLexer::is_possible_pn_chars_u(c) ||
c == ':' ||
c.is_ascii_digit()) {
return Some((0, Ok(("".to_string(), false))))
}
if !self.lenient {
might_be_invalid_iri = RdfLexer::is_possible_pn_chars_base_but_not_valid_iri(
c,
) ||
c == ':'
}
i += consumed
} else if RdfLexer::is_possible_pn_chars(c) || c == ':' {
if !self.lenient {
might_be_invalid_iri = RdfLexer::is_possible_pn_chars_base_but_not_valid_iri(
c,
) ||
c == ':'
}
i += consumed
ends_with_unescaped_dot = 0
} else if c == '.' {
i += consumed
ends_with_unescaped_dot += 1
} else {
// 遇到非法字符,准备返回结果
let result = if buffer is Some(buffer_val) {
match
str_from_utf8(
data[position_that_is_already_in_buffer:i],
Range(
start_offset + position_that_is_already_in_buffer,
start_offset + i,
),
) {
Ok(data_str) => buffer_val.write_stringview(data_str)
Err(e) => return Some((i, Err(e)))
}
let mut res_str = buffer_val.to_string()
if ends_with_unescaped_dot > 0 {
let len = res_str.length()
if len >= ends_with_unescaped_dot {
res_str = res_str[:len - ends_with_unescaped_dot].to_owned()
}
}
i -= ends_with_unescaped_dot
res_str
} else {
let mut data_str = match
str_from_utf8(data[:i], Range(start_offset, start_offset + i)) {
Ok(d) => d
Err(e) => return Some((i, Err(e)))
}
data_str = data_str[:data_str.length() - ends_with_unescaped_dot].to_owned()
i -= ends_with_unescaped_dot
data_str
}
return Some((i, Ok((result, might_be_invalid_iri))))
}
Err(e) => return Some((e.range().end - start_offset, Err(e)))
}
} else if is_ending {
let result = if buffer is Some(buffer_val) {
if i - position_that_is_already_in_buffer > 0 {
match
str_from_utf8(
data[position_that_is_already_in_buffer:i],
Range(
start_offset + position_that_is_already_in_buffer,
start_offset + i,
),
) {
Ok(data_str) => buffer_val.write_stringview(data_str)
Err(e) => return Some((i, Err(e)))
}
}
let mut res_str = buffer_val.to_string()
while res_str.has_suffix(".") {
let len = res_str.length()
res_str = res_str[:len - 1].to_owned()
i -= 1
}
res_str
} else {
let mut data_str = match
str_from_utf8(data[:i], Range(start_offset, start_offset + i)) {
Ok(d) => d
Err(e) => return Some((i, Err(e)))
}
while data_str.has_suffix(".") {
data_str = data_str[:data_str.length() - 1].to_owned()
i -= 1
}
data_str
}
return Some((i, Ok((result, might_be_invalid_iri))))
} else {
return None
}
}
}
///|
fn RdfLexer::recognize_blank_node_label(
data : ArrayView[Byte],
is_ending : Bool,
location_pos : SourcePos,
) -> (Int, Result[LocatedTokenOrLineJump, TokenRecognizerError])? {
let mut i = 2
while true {
match RdfLexer::recognize_unicode_char(data[i:], i) {
Some(c) =>
match c {
Ok((c, consumed)) => {
if (
i == 2 &&
(RdfLexer::is_possible_pn_chars_u(c) || c.is_ascii_digit())
) ||
(i > 2 && RdfLexer::is_possible_pn_chars(c)) {
} else if i == 2 {
return Some(
(
i,
Err(
TokenRecognizerError::InvalidErrMessage(
data[0:i],
Range(location_pos.offset, location_pos.offset + i),
"A blank node ID cannot be empty",
),
),
),
)
} else if c == '.' {
if data[i - 1] == b'.' {
i -= 1
return Some(
(
i,
str_from_utf8(
data[2:i],
Range(location_pos.offset + 2, location_pos.offset + i),
).map(s => {
LocatedTokenOrLineJump::LocatedN3Token(
LocatedN3Token::new(
N3Token::BlankNodeLabel(s),
Range(
location_pos.offset + 2,
location_pos.offset + i,
),
),
)
}),
),
)
}
} else if data[i - 1] == b'.' {
i -= 1
return Some(
(
i,
str_from_utf8(
data[2:i],
Range(location_pos.offset + 2, location_pos.offset + i),
).map(s => {
LocatedTokenOrLineJump::LocatedN3Token(
LocatedN3Token::new(
N3Token::BlankNodeLabel(s),
Range(location_pos.offset + 2, location_pos.offset + i),
),
)
}),
),
)
} else {
return Some(
(
i,
str_from_utf8(
data[2:i],
Range(location_pos.offset + 2, location_pos.offset + i),
).map(s => {
LocatedTokenOrLineJump::LocatedN3Token(
LocatedN3Token::new(
N3Token::BlankNodeLabel(s),
Range(location_pos.offset + 2, location_pos.offset + i),
),
)
}),
),
)
}
i += consumed
}
Err(e) => return Some((e.range().end - location_pos.offset, Err(e)))
}
_ => break
}
}
if is_ending {
if data[i - 1] == b'.' {
i -= 1
}
if i > 2 {
return Some(
(
i,
str_from_utf8(
data[2:i],
Range(location_pos.offset + 2, location_pos.offset + i),
).map(s => {
LocatedTokenOrLineJump::LocatedN3Token(
LocatedN3Token::new(
N3Token::BlankNodeLabel(s),
Range(location_pos.offset + 2, location_pos.offset + i),
),
)
}),
),
)
} else {
return Some(
(
i,
Err(
TokenRecognizerError::InvalidErrMessage(
data[0:i],
Range(location_pos.offset, location_pos.offset + i),
"A blank node ID cannot be empty",
),
),
),
)
}
}
None
}
///|
fn RdfLexer::recognize_lang_tag(
self : RdfLexer,
data : ArrayView[Byte],
location_pos : SourcePos,
) -> (Int, Result[LocatedTokenOrLineJump, TokenRecognizerError])? {
let mut is_last_block_empty = true
let mut are_digits_allowed = false
let mut last_valid_index = 1
for _item in data[1:].iter2() {
let (i, c) = _item
if (are_digits_allowed && is_ascii_alphanumeric(Byte::to_char(c))) ||
(!are_digits_allowed && Byte::to_char(c).is_ascii_alphabetic()) {
is_last_block_empty = false
last_valid_index = i + 2
} else if i == 0 {
// ... 错误处理 ...
} else if is_last_block_empty {
// ... 处理连字符逻辑 ...
} else if c == b'-' {
is_last_block_empty = true
are_digits_allowed = true
} else {
// 遇到非字母数字字符(如空格、标点),说明语言标签结束
return Some(
(
i + 1,
self.parse_lang_tag(
data[1:i + 1].to_owned(),
None,
Range(location_pos.offset + 1, location_pos.offset + i + 1),
),
),
)
}
}
// 循环结束,说明到了输入末尾
if !is_last_block_empty {
// 返回已识别的部分
return Some(
(
last_valid_index,
self.parse_lang_tag(
data[1:last_valid_index].to_owned(),
None,
Range(location_pos.offset + 1, location_pos.offset + last_valid_index),
),
),
)
}
None
}
///|
fn RdfLexer::parse_lang_tag(
self : RdfLexer,
lang_tag : ArrayView[Byte],
direction : Bytes?,
position : Range,
) -> Result[LocatedTokenOrLineJump, TokenRecognizerError] {
let lang_str_result = str_from_utf8(lang_tag, position)
let parsed_lang = match lang_str_result {
Err(e) =>
Err(
TokenRecognizerError::InvalidErrMessage(
lang_tag[position.start:position.end],
position,
e.to_string(),
),
)
Ok(lang_str) =>
if self.lenient {
Ok(lang_str)
} else {
LanguageTag::parse(lang_str)
.map(fn(tag) { tag.to_string() })
.map_err(fn(e) {
TokenRecognizerError::InvalidErrMessage(
lang_tag[position.start:position.end],
position,
e.to_string(),
)
})
}
}
match parsed_lang {
Err(e) => Err(e)
Ok(lang_str) => {
let parsed_dir = match direction {
None => Ok(None)
Some(dir_bytes) =>
if dir_bytes == b"ltr" {
Ok(Some(BaseDirection::Ltr))
} else if dir_bytes == b"rtl" {
Ok(Some(BaseDirection::Rtl))
} else {
Err(
TokenRecognizerError::InvalidErrMessage(
dir_bytes.to_array(),
Range(position.end - dir_bytes.length(), position.end),
"The allowed base directions are --ltr and --rtl, found --" +
@utf8.decode_lossy(dir_bytes),
),
)
}
}
match parsed_dir {
Err(e) => Err(e)
Ok(dir_opt) =>
Ok(
LocatedTokenOrLineJump::LocatedN3Token(
LocatedN3Token::new(
N3Token::LangTag(language=lang_str, direction=dir_opt),
Range(position.start, position.end),
),
),
)
}
}
}
}
///|
fn RdfLexer::recognize_string(
self : RdfLexer,
data : ArrayView[Byte],
delimiter : Byte,
is_ending : Bool,
location_pos : SourcePos,
) -> (Int, Result[LocatedTokenOrLineJump, TokenRecognizerError])? {
let string = StringBuilder::new()
let mut i = 1
for ;; {
// 1. 查找下一个定界符或反斜杠
let end_opt = array_view_memchr2(delimiter, b'\\', data[i:])
let mut end = match end_opt {
Some(e) => e
None =>
if is_ending {
return Some(
(
data.length(),
Err(
TokenRecognizerError::UnexpectedEndOfInput(
data[i:],
Range(
location_pos.offset + i,
location_pos.offset + data.length(),
),
),
),
),
)
} else {
return None
}
}
// 2. 在非宽松模式下,检查这段普通字符中是否包含未转义的换行符
if !self.lenient {
if array_view_memchr2(b'\n', b'\r', data[i:i + end])
is Some(line_jump_end) {
end = line_jump_end
}
}
// 3. 将普通字符部分追加到字符串缓冲区
match
str_from_utf8(
data[i:i + end],
Range(location_pos.offset + i, location_pos.offset + i + end),
) {
Ok(s) => string.write_string(s)
Err(e) => return Some((i + end, Err(e)))
}
i += end
if i >= data.length() {
return None
}
match data[i] {
c if c == delimiter =>
// 遇到定界符,字符串结束
return Some(
(
i + 1,
Ok(
LocatedTokenOrLineJump::LocatedN3Token(
LocatedN3Token::new(
N3Token::String(string.to_string()),
Range(location_pos.offset, location_pos.offset + i + 1),
),
),
),
),
)
b'\\' => {
// 遇到反斜杠,处理转义序列
let escape_result = self.recognize_escape(
data[i:],
location_pos.offset + i,
true,
)
match escape_result {
Some((additional, c_result)) => {
i += additional + 1
if i > data.length() {
return None
}
match c_result {
Ok(c) => string.write_char(c)
Err(e) => {
let end_opt = array_view_memchr(delimiter, data[i:])
let final_end = match end_opt {
Some(e) => i + e + 1
None => data.length()
}
return Some((final_end, Err(e)))
}
}
}
None => return None
}
}
b'\n' | b'\r' => {
let end_opt = array_view_memchr(delimiter, data[i:])
let final_end = match end_opt {
Some(e) => i + e + 1
None => i + 1
}
return Some(
(
final_end,
Err(
TokenRecognizerError::InvalidErrMessage(
data[i:i + 1],
Range(location_pos.offset + i, location_pos.offset + i + 1),
"Line jumps are not allowed in string literals, use \\n",
),
),
),
)
}
_ =>
return Some(
(
i + 1,
Err(
TokenRecognizerError::InvalidErrMessage(
data[i:i + 1],
Range(location_pos.offset + i, location_pos.offset + i + 1),
"Unexpected character in string literal",
),
),
),
)
}
}
}
///|
fn RdfLexer::recognize_long_string(
self : RdfLexer,
data : ArrayView[Byte],
delimiter : Byte,
is_ending : Bool,
location_ctx : SourcePos,
) -> (Int, Result[LocatedTokenOrLineJump, TokenRecognizerError])? {
match
self.recognize_triple_quote_content(
data[3:],
delimiter,
location_ctx.offset + 3,
) {
Some((bytes_consumed, content, found_ending)) =>
if found_ending {
Some(
(
bytes_consumed + 3,
Ok(
LocatedTokenOrLineJump::LocatedN3Token(
LocatedN3Token::new(
N3Token::LongString(content),
Range(
location_ctx.offset,
location_ctx.offset + bytes_consumed + 3,
),
),
),
),
),
)
} else if is_ending {
Some(
(
bytes_consumed + 3,
Err(
TokenRecognizerError::UnexpectedEndOfInput(
data[3:],
Range(
location_ctx.offset + 3,
location_ctx.offset + data.length(),
),
),
),
),
)
} else {
None
}
None =>
if is_ending {
Some(
(
data.length(),
Err(
TokenRecognizerError::UnexpectedEndOfInput(
data[3:],
Range(
location_ctx.offset + 3,
location_ctx.offset + data.length(),
),
),
),
),
)
} else {
None
}
}
}
///|
fn RdfLexer::recognize_number(
data : ArrayView[Byte],
is_ending : Bool,
location_pos : SourcePos,
) -> (Int, Result[LocatedTokenOrLineJump, TokenRecognizerError])? {
let mut i = 0
match data.get(0) {
Some(c) => if c == b'+' || c == b'-' { i += 1 }
None => return None
}
let count_before = match RdfLexer::recognize_digits(data[i:], is_ending) {
Some(count) => count
None => return None
}
i += count_before
let c = if data.get(i) is Some(c) {
Some(c)
} else if is_ending {
None
} else {
return None
}
let count_after = if c == Some(b'.') {
i += 1
let count_after = match RdfLexer::recognize_digits(data[i:], is_ending) {
Some(count) => count
None => return None
}
i += count_after
Some(count_after)
} else {
None
}
let c = if data.get(i) is Some(c) {
Some(c)
} else if is_ending {
None
} else {
return None
}
if c is Some(b'e' | b'E') {
i += 1
let c = if data.get(i) is Some(c) {
Some(c)
} else if is_ending {
None
} else {
return None
}
if c is Some(b'+' | b'-') {
i += 1
}
let count_exp = match RdfLexer::recognize_digits(data[i:], is_ending) {
Some(count) => count
None => return None
}
i += count_exp
Some(
(
i,
if count_exp == 0 {
Err(
TokenRecognizerError::InvalidErrMessage(
data[0:i],
Range(location_pos.offset, location_pos.offset + i),
"A double exponent cannot be empty",
),
)
} else if count_before == 0 && count_after.unwrap_or(0) == 0 {
Err(
TokenRecognizerError::InvalidErrMessage(
data[0:i],
Range(location_pos.offset, location_pos.offset + i),
"A double should not be empty",
),
)
} else {
str_from_utf8(
data[:i],
Range(location_pos.offset, location_pos.offset + i),
).map(s => {
LocatedTokenOrLineJump::LocatedN3Token(
LocatedN3Token::new(
N3Token::Double(s),
Range(location_pos.offset, location_pos.offset + i),
),
)
})
},
),
)
} else if count_after is Some(count_after) {
if count_after == 0 {
i -= 1
Some(
(
i,
if count_before == 0 {
Err(
TokenRecognizerError::InvalidErrMessage(
data[0:i],
Range(location_pos.offset, location_pos.offset + i),
"An integer should not be empty",
),
)
} else {
str_from_utf8(
data[:i],
Range(location_pos.offset, location_pos.offset + i),
).map(s => {
LocatedTokenOrLineJump::LocatedN3Token(
LocatedN3Token::new(
N3Token::Integer(s),
Range(location_pos.offset, location_pos.offset + i),
),
)
})
},
),
)
} else {
Some(
(
i,
str_from_utf8(
data[:i],
Range(location_pos.offset, location_pos.offset + i),
).map(s => {
LocatedTokenOrLineJump::LocatedN3Token(
LocatedN3Token::new(
N3Token::Decimal(s),
Range(location_pos.offset, location_pos.offset + i),
),
)
}),
),
)
}
} else {
Some(
(
i,
if count_before == 0 {
Err(
TokenRecognizerError::InvalidErrMessage(
data[0:i],
Range(location_pos.offset, location_pos.offset + i),
"An integer should not be empty",
),
)
} else {
str_from_utf8(
data[:i],
Range(location_pos.offset, location_pos.offset + i),
).map(s => {
LocatedTokenOrLineJump::LocatedN3Token(
LocatedN3Token::new(
N3Token::Integer(s),
Range(location_pos.offset, location_pos.offset + i),
),
)
})
},
),
)
}
}
///|
fn RdfLexer::recognize_digits(data : ArrayView[Byte], is_ending : Bool) -> Int? {
for _item in data.iter2() {
let (i, c) = _item
if !Byte::to_char(c).is_ascii_digit() {
return Some(i)
}
}
if is_ending {
Some(data.length())
} else {
None
}
}
///|
///|
fn RdfLexer::recognize_escape(
self : RdfLexer,
data : ArrayView[Byte],
position : Int, // 全局起始位置
with_echar : Bool,
) -> (Int, Result[Char, TokenRecognizerError])? {
match data.get(1) {
Some(b'u') =>
match RdfLexer::recognize_hex_char(data[2:], 4, 'u', position) {
Ok(Some(c)) => Some((5, Ok(c)))
Ok(None) => None
Err(e) =>
if self.lenient {
match RdfLexer::recognize_utf16_surrogate_pair(data[2:], position) {
Ok(Some(c)) => Some((11, Ok(c)))
Ok(None) => None
Err(e) => Some((5, Err(e)))
}
} else {
Some((5, Err(e)))
}
}
Some(b'U') =>
match RdfLexer::recognize_hex_char(data[2:], 8, 'U', position) {
Ok(Some(c)) => Some((9, Ok(c)))
Ok(None) => None
Err(e) => Some((9, Err(e)))
}
Some(b't') if with_echar => Some((1, Ok('\t')))
Some(b'b') if with_echar => Some((1, Ok('\u{0008}')))
Some(b'n') if with_echar => Some((1, Ok('\n')))
Some(b'r') if with_echar => Some((1, Ok('\r')))
Some(b'f') if with_echar => Some((1, Ok('\u{000C}')))
Some(b'"') if with_echar => Some((1, Ok('"')))
Some(b'\'') if with_echar => Some((1, Ok('\'')))
Some(b'\\') if with_echar => Some((1, Ok('\\')))
Some(c) =>
Some(
(
1,
Err(
TokenRecognizerError::InvalidErrMessage(
data[1:2],
Range(position + 1, position + 2),
"Unexpected escape character \{c}",
),
),
),
)
None => None
}
}
///|
fn RdfLexer::recognize_hex_char(
data : ArrayView[Byte],
len : Int,
escape_char : Char,
position : Int,
) -> Result[Char?, TokenRecognizerError] {
if data.length() < len {
return Ok(None)
}
let mut codepoint = 0
for i in 0.. Byte::to_char(c - b'0')
b'a'..=b'f' => Byte::to_char(c - b'a' + 10)
b'A'..=b'F' => Byte::to_char(c - b'A' + 10)
_ => {
let val = @utf8.decode_lossy(array_to_view(data[:len]))
return Err(
TokenRecognizerError::InvalidErrMessage(
data[position + i + 2:position + i + 3],
Range(position + i + 2, position + i + 3),
"The escape sequence '\{escape_char}\{val}' is not a valid hexadecimal string",
),
)
}
},
)
}
let c = Int::unsafe_to_char(codepoint)
Ok(Some(c))
}
///|
fn RdfLexer::recognize_utf16_surrogate_pair(
data : ArrayView[Byte],
position : Int,
) -> Result[Char?, TokenRecognizerError] {
if data.length() < 12 {
return Ok(None)
}
let val_high_bytes = data[2:6]
let val_high = match
str_from_utf8(val_high_bytes, Range(position + 2, position + 6)) {
Ok(s) => s
Err(e) => return Err(e)
}
let surrogate_high = match from_str_radix(val_high, 16) {
Ok(v) => v
Err(_) =>
return Err(
TokenRecognizerError::InvalidErrMessage(
val_high_bytes,
Range(position + 2, position + 6),
"The escape sequence '\\u{val_high}' is not a valid hexadecimal string",
),
)
}
if !(surrogate_high >= 0xD800 && surrogate_high <= 0xDBFF) {
return Err(
TokenRecognizerError::InvalidErrMessage(
val_high_bytes,
Range(position + 2, position + 6),
"The escape sequence '\\u{val_high}' is not a UTF-16 high surrogate",
),
)
}
if data[6] != b'\\' || data[7] != b'u' {
return Err(
TokenRecognizerError::InvalidErrMessage(
data[6:8],
Range(position + 6, position + 8),
"UTF-16 surrogate escape sequence must be followed by another surrogate escape sequence '\\u'",
),
)
}
let val_low_bytes = data[8:12]
let val_low = match
str_from_utf8(val_low_bytes, Range(position + 8, position + 12)) {
Ok(s) => s
Err(e) => return Err(e)
}
let surrogate_low = match from_str_radix(val_low, 16) {
Ok(v) => v
Err(_) =>
return Err(
TokenRecognizerError::InvalidErrMessage(
val_low_bytes,
Range(position + 8, position + 12),
"The escape sequence '\\u\{val_low}' is not a valid hexadecimal string",
),
)
}
if !(surrogate_low >= 0xDC00 && surrogate_low <= 0xDFFF) {
return Err(
TokenRecognizerError::InvalidErrMessage(
val_low_bytes,
Range(position + 8, position + 12),
"The escape sequence '\\u\{val_low}' is not a UTF-16 low surrogate",
),
)
}
let c = Int::to_char(
((UInt::reinterpret_as_int(surrogate_high) - 0xD800) << 10) +
(UInt::reinterpret_as_int(surrogate_low) - 0xDC00) +
0x10000,
)
match c {
Some(char) => Ok(Some(char))
None =>
Err(
TokenRecognizerError::InvalidErrMessage(
data[2:12],
Range(position + 2, position + 12),
"Escape sequences do not form a valid Unicode character",
),
)
}
}
///|
fn RdfLexer::recognize_unicode_char(
data : ArrayView[Byte],
position : Int,
) -> Result[(Char, Int), TokenRecognizerError]? {
let mut code_point : UInt = 0
let mut bytes_needed : Int = 0
let mut lower_boundary = 0x80
let mut upper_boundary = 0xBF
let byte = match data.get(0) {
Some(b) => b
None => return None
}
match byte {
0x00..=0x7F => return Some(Ok((Byte::to_char(byte), 1)))
0xC2..=0xDF => {
bytes_needed = 1
code_point = Byte::to_uint(byte) & 0x1F
}
0xE0..=0xEF => {
if byte == 0xE0 {
lower_boundary = 0xA0
}
if byte == 0xED {
upper_boundary = 0x9F
}
bytes_needed = 2
code_point = Byte::to_uint(byte) & 0xF
}
0xF0..=0xF4 => {
if byte == 0xF0 {
lower_boundary = 0x90
}
if byte == 0xF4 {
upper_boundary = 0x8F
}
bytes_needed = 3
code_point = Byte::to_uint(byte) & 0x7
}
_ =>
return Some(
Err(
TokenRecognizerError::InvalidErrMessage(
data[position:position + 1],
Range(position, position + 1),
"Invalid UTF-8 character encoding",
),
),
)
}
for i in 1..<=bytes_needed {
let next_byte = match data.get(i) {
Some(b) => b
None => return None
}
if next_byte < Int::to_byte(lower_boundary) ||
upper_boundary < Byte::to_int(byte) {
return Some(
Err(
TokenRecognizerError::InvalidErrMessage(
data[position:position + i],
Range(position, position + i),
"Invalid UTF-8 character encoding",
),
),
)
}
lower_boundary = 0x80
upper_boundary = 0xBF
code_point = (code_point << 6) | (Byte::to_uint(byte) & 0x3F)
}
match Int::to_char(UInt::reinterpret_as_int(code_point)) {
Some(c) => Some(Ok((c, bytes_needed + 1)))
None => {
let error_range = Range(position, position + bytes_needed)
let msg = "The codepoint \{code_point} is not a valid unicode character"
Some(
Err(
TokenRecognizerError::InvalidErrMessage(
data[position:position + bytes_needed],
error_range,
msg,
),
),
)
}
}
}
///|
fn RdfLexer::is_possible_pn_chars_base(c : Char) -> Bool {
c
is ('A'..='Z'
| 'a'..='z'
| '\u{00C0}'..='\u{00D6}'
| '\u{00D8}'..='\u{00F6}'
| '\u{00F8}'..='\u{02FF}'
| '\u{0370}'..='\u{037D}'
| '\u{037F}'..='\u{1FFF}'
| '\u{200C}'..='\u{200D}'
| '\u{2070}'..='\u{218F}'
| '\u{2C00}'..='\u{2FEF}'
| '\u{3001}'..='\u{D7FF}'
| '\u{F900}'..='\u{FDCF}'
| '\u{FDF0}'..='\u{FFFD}'
| '\u{10000}'..='\u{EFFFF}')
}
///|
pub fn RdfLexer::is_possible_pn_chars_u(c : Char) -> Bool {
RdfLexer::is_possible_pn_chars_base(c) || c == '_'
}
///|
pub fn RdfLexer::is_possible_pn_chars(c : Char) -> Bool {
RdfLexer::is_possible_pn_chars_u(c) ||
c
is ('-'
| '0'..='9'
| '\u{00B7}'
| '\u{0300}'..='\u{036F}'
| '\u{203F}'..='\u{2040}')
}
///|
fn RdfLexer::is_possible_pn_chars_base_but_not_valid_iri(c : Char) -> Bool {
c is ('\u{FFF0}'..='\u{FFFD}') ||
Char::to_int(c) % Char::to_int('\u{FFFE}') == 0 ||
Char::to_int(c) % Char::to_int('\u{FFFF}') == 0
}
///|
pub fn resolve_local_name(
prefix : String,
localx : String,
might_be_invalid_iri : Bool,
prefixes : Map[String, Iri],
) -> Result[NamedNodeRef, String] {
if prefixes.get(prefix) is Some(start) {
let iri = "\{start.into_string()}\{localx}"
let iri = Iri::Iri(iri).as_ref()
if might_be_invalid_iri || start.path() is Some("") {
if IriRef::parse(iri) is Err(e) {
return Err(
"The prefixed name \{prefix}:\{localx} builds IRI \{iri.into_string()} that is invalid:\{e.to_string()}",
)
}
}
Ok(NamedNodeRef::new_unchecked(iri.into_string()))
} else {
Err("The prefix \{prefix}: has not been declared")
}
}
///|
fn str_from_utf8(
data : ArrayView[Byte],
range : Range,
) -> Result[String, TokenRecognizerError] {
let view = array_to_view(data)
let s = try @utf8.decode(view.to_owned()) |> Ok() catch {
_e => {
let error_start = range.start
let error_end = Int::min(range.end, error_start + 4)
return Err(
TokenRecognizerError::InvalidErrMessage(
data[error_start:error_end],
Range(error_start, error_end),
"Invalid UTF-8 sequence",
),
)
}
}
s
}
///|
fn string_from_utf8(
data : Array[Byte],
range : Range,
) -> Result[String, TokenRecognizerError] {
str_from_utf8(data[:], range)
}
///|
/// 辅助函数:处理三引号长字符串的内容
/// 规则:
/// - 单个 delimiter:作为内容中的 delimiter 字符
/// - 两个连续 delimiter:作为内容中的两个 delimiter 字符
/// - 三个连续 delimiter:表示字符串结束
fn RdfLexer::recognize_triple_quote_content(
self : RdfLexer,
data : ArrayView[Byte],
delimiter : Byte,
start_offset : Int,
) -> (Int, String, Bool)? {
// 返回: (消耗的字节数, 解析的字符串, 是否遇到结束标记)
let string = StringBuilder::new()
let mut i = 0
let len = data.length()
for ;; {
if i >= len {
return None // 未找到结束标记,且没有更多数据
}
println("[DEBUG triple_quote] i=\{i}, len=\{len}")
// 查找下一个 delimiter 或反斜杠
let search_result = array_view_memchr2(delimiter, b'\\', data[i:])
match search_result {
None => {
// 没有找到 delimiter 或反斜杠,将剩余内容写入字符串
match
str_from_utf8(data[i:], Range(start_offset + i, start_offset + len)) {
Ok(s) => string.write_string(s)
Err(_) => return None
}
return None // 内容结束但未找到结束标记
}
Some(pos) => {
let offset = i + pos
let c = data[offset]
if c == b'\\' {
// 处理转义字符
match
self.recognize_escape(data[offset:], start_offset + offset, true) {
Some((additional, c_result)) => {
i = offset + additional + 1
match c_result {
Ok(ch) => string.write_char(ch)
Err(_) => {
// 无效转义,把 \ 和下一个字符都当作普通内容
if pos > 0 {
match
str_from_utf8(
data[i:offset],
Range(start_offset + i, start_offset + offset),
) {
Ok(s) => string.write_string(s)
Err(_) => return None
}
}
// 把 \ 当作普通字符
string.write_char('\\')
i = offset + 1
}
}
}
None => {
// 没有足够的字符来形成完整的转义,把 \ 当作普通字符
if pos > 0 {
match
str_from_utf8(
data[i:offset],
Range(start_offset + i, start_offset + offset),
) {
Ok(s) => string.write_string(s)
Err(_) => return None
}
}
string.write_char('\\')
i = offset + 1
}
}
} else {
// c == delimiter,找到了 delimiter
// 检查是否是三引号结束
if data.get(offset + 1) == Some(delimiter) &&
data.get(offset + 2) == Some(delimiter) {
// 三引号结束!
// 在结束之前,先将当前找到 delimiter 之前的内容写入
if pos > 0 {
match
str_from_utf8(
data[i:offset],
Range(start_offset + i, start_offset + offset),
) {
Ok(s) => string.write_string(s)
Err(_) => return None
}
}
return Some((offset + 3, string.to_string(), true))
}
// 单个或两个 delimiter,作为内容处理
// 先将 delimiter 之前的内容写入
if pos > 0 {
match
str_from_utf8(
data[i:offset],
Range(start_offset + i, start_offset + offset),
) {
Ok(s) => string.write_string(s)
Err(_) => return None
}
}
// 计算连续 delimiter 的数量
let mut quote_count = 1
while offset + quote_count < len &&
data[offset + quote_count] == delimiter {
quote_count += 1
}
// 将 delimiter 写入字符串(除了最后一个,因为可能还有更多)
// 如果是连续 2 个 delimiter,写入 2 个
// 如果是连续 3 个 delimiter(前面已经处理过三引号情况),写入 2 个
for _q in 0..