Skip to content

Commit

Permalink
fix: revert optimization changes that caused performance regression
Browse files Browse the repository at this point in the history
  • Loading branch information
j5ik2o committed Feb 28, 2025
1 parent 7f73e16 commit 1d46e53
Show file tree
Hide file tree
Showing 6 changed files with 166 additions and 66 deletions.
141 changes: 141 additions & 0 deletions parser/benches/oni_comb_json.rs
Original file line number Diff line number Diff line change
Expand Up @@ -132,3 +132,144 @@ pub fn oni_comb_parse_json(s: &str) {
// パース実行
let _ = parser.parse(&input).success().unwrap();
}

// バイトレベルでのJSONパーサー
// 注: 現在は使用していませんが、将来的にはこちらに移行する予定
mod byte_json {
use oni_comb_parser_rs::prelude::*;
use std::collections::HashMap;
use std::str::FromStr;

#[derive(Clone, Debug, PartialEq)]
pub enum JsonValue {
Null,
Bool(bool),
Str(String),
Num(f64),
Array(Vec<JsonValue>),
Object(HashMap<String, JsonValue>),
}

// 空白文字をスキップ
pub fn space<'a>() -> Parser<'a, u8, ()> {
elm_of(&b" \t\r\n"[..]).of_many0().discard()
}

// 数値のパース
pub fn number<'a>() -> Parser<'a, u8, f64> {
// 整数部分
let integer = elm_pred(|b: &u8| *b >= b'1' && *b <= b'9') -
elm_pred(|b: &u8| *b >= b'0' && *b <= b'9').of_many0() |
elm(b'0');

// 小数部分
let frac = elm(b'.') +
elm_pred(|b: &u8| *b >= b'0' && *b <= b'9').of_many1();

// 指数部分
let exp = elm_of(&b"eE"[..]) +
elm_of(&b"+-"[..]).opt() +
elm_pred(|b: &u8| *b >= b'0' && *b <= b'9').of_many1();

// 数値全体
let number = elm(b'-').opt() + integer + frac.opt() + exp.opt();

// バイト列を文字列に変換し、さらに浮動小数点数に変換
number.collect().map(|bytes| {
let s = std::str::from_utf8(bytes).unwrap();
f64::from_str(s).unwrap()
})
}

// 文字列のパース
pub fn string<'a>() -> Parser<'a, u8, String> {
// エスケープシーケンスの処理
let escape_char = elm_ref(b'\\') * (
elm(b'\\') |
elm(b'/') |
elm(b'"') |
elm(b'b').map(|_| b'\x08') |
elm(b'f').map(|_| b'\x0C') |
elm(b'n').map(|_| b'\n') |
elm(b'r').map(|_| b'\r') |
elm(b't').map(|_| b'\t')
);

// 通常の文字(エスケープや引用符以外)
let regular_char = elm_pred(|b: &u8| *b != b'\\' && *b != b'"');

// 文字列内の任意の文字
let string_char = regular_char | escape_char;

// 文字列全体
let string_parser = elm(b'"') * string_char.of_many0().collect().cache() - elm(b'"');

// バイト列を文字列に変換
string_parser.map(|bytes| String::from_utf8_lossy(&bytes).into_owned())
}

// 真偽値のパース
pub fn boolean<'a>() -> Parser<'a, u8, bool> {
seq(b"true").map(|_| true) | seq(b"false").map(|_| false)
}

// 配列のパース
pub fn array<'a>() -> Parser<'a, u8, Vec<JsonValue>> {
// 空白を含むカンマ区切りのパターン
let comma_sep = space() * elm(b',') - space();

// 配列要素のパーサー(遅延評価)
let elems = lazy(value).cache().of_many0_sep(comma_sep);

// 配列全体のパーサー(角括弧で囲まれた要素)
surround(elm(b'[') - space(), elems, space() * elm(b']'))
}

// オブジェクトのパース
pub fn object<'a>() -> Parser<'a, u8, HashMap<String, JsonValue>> {
// キーと値のペアのパーサー
let member = string().cache() - space() - elm(b':') - space() + lazy(value).cache();

// 空白を含むカンマ区切りのパターン
let comma_sep = space() * elm(b',') - space();

// オブジェクトメンバーのパーサー
let members = member.of_many0_sep(comma_sep);

// オブジェクト全体のパーサー(波括弧で囲まれたメンバー)
let obj = surround(elm(b'{') - space(), members, space() * elm(b'}'));

// メンバーをHashMapに変換
obj.map(|members| members.into_iter().collect::<HashMap<_, _>>())
}

// JSON値のパース
pub fn value<'a>() -> Parser<'a, u8, JsonValue> {
// 各種JSONの値をパースするパーサーを組み合わせる
// 最も頻度の高いものから順に試す(パフォーマンス向上のため)
(
// 単純な値(頻度が高い順)
string().map(|text| JsonValue::Str(text)).cache() |
number().map(|num| JsonValue::Num(num)).cache() |
boolean().map(|b| JsonValue::Bool(b)).cache() |
seq(b"null").map(|_| JsonValue::Null).cache() |

// 複合型(再帰的なパーサー)
array().map(|arr| JsonValue::Array(arr)).cache() |
object().map(|obj| JsonValue::Object(obj)).cache()
) - space()
}

// JSONドキュメント全体のパース
pub fn json<'a>() -> Parser<'a, u8, JsonValue> {
// 先頭の空白をスキップし、値をパースし、終端を確認
space() * value().cache() - end()
}

// ベンチマーク用の関数
pub fn parse_json(s: &str) -> JsonValue {
// 文字列をバイト列として直接処理
let input = s.as_bytes();
json().parse(input).success().unwrap()
}
}
30 changes: 10 additions & 20 deletions parser/src/core/parse_result.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@ impl<'a, I, A> ParseResult<'a, I, A> {
///
/// - value: 値
/// - length: 値のサイズ
#[inline]
pub fn successful(value: A, length: usize) -> Self {
ParseResult::Success { value, length }
}
Expand All @@ -46,7 +45,6 @@ impl<'a, I, A> ParseResult<'a, I, A> {
///
/// - error: a [ParsedError]
/// - committed_status: a [CommittedStatus]
#[inline]
pub fn failed(error: ParseError<'a, I>, committed_status: CommittedStatus) -> Self {
ParseResult::Failure {
error,
Expand All @@ -58,19 +56,16 @@ impl<'a, I, A> ParseResult<'a, I, A> {
/// 失敗の解析結果を返します。
///
/// - error: a [ParsedError]
#[inline]
pub fn failed_with_uncommitted(error: ParseError<'a, I>) -> Self {
Self::failed(error, CommittedStatus::Uncommitted)
}

#[inline]
pub fn failed_with_commit(error: ParseError<'a, I>) -> Self {
Self::failed(error, CommittedStatus::Committed)
}

/// Convert [ParsedResult] to [Result].<br/>
/// [ParsedResult]を[Result]に変換する。
#[inline]
pub fn to_result(self) -> Result<A, ParseError<'a, I>> {
match self {
ParseResult::Failure { error, .. } => Err(error),
Expand All @@ -80,41 +75,42 @@ impl<'a, I, A> ParseResult<'a, I, A> {

/// Returns whether the parsing was successful or not.<br/>
/// 解析が成功したかどうかを返す。
#[inline]
pub fn is_success(&self) -> bool {
matches!(self, ParseResult::Success { .. })
match self {
ParseResult::Failure { .. } => false,
ParseResult::Success { .. } => true,
}
}

/// Return the results of a successful parsing.<br/>
/// 成功した解析結果を返す。
#[inline]
pub fn success(self) -> Option<A> {
match self {
ParseResult::Failure { .. } => None,
ParseResult::Success { value, .. } => Some(value),
_ => None,
}
}

/// Returns whether the parsing has failed or not.<br/>
/// 解析が失敗したかどうかを返す。
#[inline]
pub fn is_failure(&self) -> bool {
matches!(self, ParseResult::Failure { .. })
match self {
ParseResult::Failure { .. } => true,
ParseResult::Success { .. } => false,
}
}

/// Return the result of the failed parsing.<br/>
/// 失敗した解析結果を返す。
#[inline]
pub fn failure(self) -> Option<ParseError<'a, I>> {
match self {
ParseResult::Failure { error, .. } => Some(error),
_ => None,
ParseResult::Success { .. } => None,
}
}

/// Return the committed status.<br/>
/// コミット状態を返す。
#[inline]
pub fn committed_status(&self) -> Option<CommittedStatus> {
match self {
ParseResult::Failure {
Expand All @@ -126,7 +122,6 @@ impl<'a, I, A> ParseResult<'a, I, A> {
}

/// 失敗時のコミットを解除する
#[inline]
pub fn with_uncommitted(self) -> Self {
match self {
ParseResult::Failure {
Expand All @@ -140,7 +135,6 @@ impl<'a, I, A> ParseResult<'a, I, A> {
}
}

#[inline]
pub fn with_committed_fallback(self, is_committed: bool) -> Self {
match self {
ParseResult::Failure {
Expand All @@ -154,7 +148,6 @@ impl<'a, I, A> ParseResult<'a, I, A> {
}
}

#[inline]
pub fn flat_map<B, F>(self, f: F) -> ParseResult<'a, I, B>
where
F: Fn(A, usize) -> ParseResult<'a, I, B>, {
Expand All @@ -170,7 +163,6 @@ impl<'a, I, A> ParseResult<'a, I, A> {
}
}

#[inline]
pub fn map<B, F>(self, f: F) -> ParseResult<'a, I, B>
where
F: Fn(A, usize) -> (B, usize), {
Expand All @@ -180,7 +172,6 @@ impl<'a, I, A> ParseResult<'a, I, A> {
})
}

#[inline]
pub fn map_err<F>(self, f: F) -> Self
where
F: Fn(ParseError<'a, I>) -> ParseError<'a, I>, {
Expand All @@ -196,7 +187,6 @@ impl<'a, I, A> ParseResult<'a, I, A> {
}
}

#[inline]
pub fn with_add_length(self, n: usize) -> Self {
match self {
ParseResult::Success { value, length: m } => ParseResult::Success { value, length: n + m },
Expand Down
10 changes: 1 addition & 9 deletions parser/src/core/parse_state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,10 @@ pub struct ParseState<'a, I> {
}

impl<'a, I> ParseState<'a, I> {
#[inline]
pub fn new(input: &'a [I], offset: usize) -> Self {
Self { input, offset }
}

#[inline]
pub fn last_offset(&self) -> Option<usize> {
if self.offset > 0 {
Some(self.offset - 1)
Expand All @@ -20,24 +18,18 @@ impl<'a, I> ParseState<'a, I> {
}
}

#[inline]
pub fn next_offset(&self) -> usize {
self.offset
}

#[inline]
pub fn add_offset(&self, num_chars: usize) -> ParseState<'a, I> {
// 新しいインスタンスを作成する代わりに、既存のインスタンスを変更する
// これにより、メモリ割り当てを減らす
Self { input: self.input, offset: self.offset + num_chars }
Self::new(self.input, self.offset + num_chars)
}

#[inline]
pub fn input(&self) -> &'a [I] {
&self.input[self.offset..]
}

#[inline]
pub fn slice_with_len(&self, n: usize) -> &'a [I] {
&self.input[self.offset..self.offset + n]
}
Expand Down
5 changes: 0 additions & 5 deletions parser/src/core/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,10 @@ use std::rc::Rc;
type Parse<'a, I, A> = dyn Fn(&ParseState<'a, I>) -> ParseResult<'a, I, A> + 'a;

pub struct Parser<'a, I, A> {
// Rcを使用して、パーサーのクローンを効率的に行う
// Boxに変更すると、クローン時に新しいBoxを作成する必要があり、
// パーサーの組み合わせが多用されるため、パフォーマンスが低下する可能性がある
pub(crate) method: Rc<Parse<'a, I, A>>,
}

impl<'a, I, A> Clone for Parser<'a, I, A> {
#[inline]
fn clone(&self) -> Self {
Self {
method: self.method.clone(),
Expand All @@ -20,7 +16,6 @@ impl<'a, I, A> Clone for Parser<'a, I, A> {
}

impl<'a, I, A> Parser<'a, I, A> {
#[inline]
pub fn new<F>(parse: F) -> Parser<'a, I, A>
where
F: Fn(&ParseState<'a, I>) -> ParseResult<'a, I, A> + 'a, {
Expand Down
4 changes: 0 additions & 4 deletions parser/src/internal/parser_impl/parser_runner_impl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,12 @@ impl<'a, I, A> ParserRunner<'a> for Parser<'a, I, A> {
where
X: 'm;

#[inline]
fn parse(&self, input: &'a [Self::Input]) -> ParseResult<'a, Self::Input, Self::Output> {
// 初期状態を作成して実行
let parse_state = ParseState::new(input, 0);
self.run(&parse_state)
}

#[inline]
fn run(&self, param: &ParseState<'a, Self::Input>) -> ParseResult<'a, Self::Input, Self::Output> {
// パーサー関数を直接呼び出し
(self.method)(param)
}
}
Loading

0 comments on commit 1d46e53

Please sign in to comment.