2

私のアプリケーションでは、SQLiteパーサーを構築しようとしています。私のアプリケーションは Objective-C を使用しているので、ParseKitは良い選択肢のようです。SQLite の構文図を読み、それらに基づいて文法を作成しました。ただし、この文法を使用して何かを解析しようとすると、パーサーは無限再帰に入ります。

必要なステートメントは、SELECT、INSERT、UPDATE、および DELETE だけです (SELECT が必要なのは、他の人が参照しているためです)。私の @start は、セミコロンで区切られた複数のステートメントを処理するように設計されています。

@start = statement (';' statement)*;

statement = select_stmt | insert_stmt | update_stmt | delete_stmt ;

ステートメントは次のとおりです。

select_stmt = select_core ( compound_operator select_core )* order_expr? limit_expr? ;
select_core = 'select' ( 'distinct' | 'all' )? result_column ( ',' result_column )* from_expr where_expr group_expr ;
result_column = '*' | table_name '.' '*' | expr ( 'as'? column_alias )? ;

insert_stmt = 'insert' or_on_failure? 'into' table_name insert_expr ;
insert_expr = insert_expr_cols? (insert_expr_values | select_stmt) ;
insert_expr_cols = '(' column_name ( ',' column_name )* ')' ;
insert_expr_values = 'values' '(' expr ( ',' expr )* ')' ;
insert_expr_defaults = 'default' 'values' ;

update_stmt = 'update' or_on_failure? qualified_table_name update_expr where_expr? limited_expr? ;
update_expr = 'set' update_expr_col ( ',' update_expr_col )* ;
update_expr_col = column_name '=' expr ;

delete_stmt = 'delete' 'from' qualified_table_name where_expr? limited_expr? ;

そして、それらをサポートする表現:

order_expr = 'order' 'by' ordering_term (',' ordering_term)* ;
limit_expr = 'limit' expr ( ( 'offset' | ',' ) expr )? ;
from_expr = 'from' join_source ;
where_expr = 'where' expr ;
group_expr = 'group' 'by' expr ( ',' expr )? ( 'having' expr )? ;

join_source = single_source ( join_operator single_source join_constraint? )* ;
single_source = table_name 'as' table_alias indexed_by? | '(' select_stmt ')' ( 'as'? table_alias )? | '(' join_source ')' ;

or_on_failure = 'or' on_failure ;
on_failure = 'rollback' | 'abort' | 'replace' | 'fail' | 'ignore' ;
limited_expr = order_expr limit_expr ;

名前、別名:

database_name = name ;
table_name = (database_name '.')? name ;
column_name = (table_name '.')? name ;

table_alias = name ;
column_alias = name ;

index_name = name ;

type_name = name+ ( '(' number (',' number)? ')' )? ;
function_name = name ;
collation_name = name ;

qualified_table_name = table_name indexed_by? ;

その他の演算子など:

indexed_by = 'indexed' 'by' index_name | 'not' 'indexed' ;

unary_operator = symbol ;
binary_operator = symbol ;
compound_operator = 'union' 'all'? | 'intersect' | 'except' ;
join_operator = ',' | 'natural'? ( 'left' 'outer'? | 'inner' | 'cross' ) 'join' ;

join_constraint = 'on' expr | 'using' '(' column_name ( ',' column_name )* ')' ;

基本タイプ:

literal = number
    | string
    | 'null'
    | 'current_time'
    | 'current_data'
    | 'current_timestamp' ;
number = Number ;
string = Word
    | QuotedString ;
name = Word
    | QuotedString ;
symbol = Symbol;

そしてEXPR:

expr = literal
    | column_name
    | unary_operator expr
    | expr binary_operator expr
    | function_name '(' ( '*' | 'distinct'? expr ( ',' expr )* )? ')'
    | '(' expr ')'
    | 'cast' '(' expr 'as' type_name ')'
    | expr 'collate' collation_name
    | expr 'not'? ( 'like' | 'glob' | 'regexp' | 'match' ) expr ( 'escape' expr )?
    | expr ( 'isnull' | 'notnull' | 'not' 'null' )
    | expr 'is' 'not'? expr
    | expr 'not'? 'between' expr 'and' expr
    | expr 'not'? 'in' ( table_name | '(' ( select_stmt | expr ( ',' expr )* )? ')' )
    | ( 'not'? 'exists' )? '(' select_stmt ')'
    | 'case' expr? ( 'when' expr 'then' expr )+ ( 'else' expr )? 'end' ;

コードをステップ実行すると、基本的なパスは @start -> statement -> select_stmt -> select_core -> result_column -> expr -> expr -> expr... でした。

PKParser の matchAndAssemble: と PKParser/Subclass の allMatchesFor: の間で約 8 ~ 9,000 回呼び出した後、通常は EXC_BAD_ACCESS エラーが原因で何かが停止します (その後、LLDB も何もできなくなります)。

PS: 「ああ、本当にこれを使用する必要があります」という回答を投稿する場合は、A) 私は Objective-C が好きです。使わないように言わないでください。それは私の選択です。私の反応はおそらく暴言になるでしょう。B) SQLite のソースを掘り下げて、パーサーを使用しようとしました。私はどこにも行きませんでした。私がそれを使用する必要があると思われる場合は、パーサーのソースを他の依存関係のない単一のファイルとして投稿してください。

4

1 に答える 1

3

ParseKitの開発者はこちら。

まず、 ParseKit grammars のデバッグとParseKit grammars での無限再帰との戦いに関する以前の回答を参照してください。


一番最初の行に問題があるのではないかと思います (しかし、私は SQL の専門家ではないので、よくわかりません)。そうではありませんか:

@start = (statement ';')+;

アンダースコアを使用すると、Objective-C のコールバックが非常にぎこちなくなり、醜くなるため、アンダースコアの代わりにキャメル ケースを使用することを強くお勧めします。そのため、ParseKit 文法ではキャメル ケースが慣習となっています。


しかし、私は主な問題を見ています。あなたの文法には、ParseKit 文法では許可されていない左再帰が含まれています。特にあなたのexpr製品では(他の場所にもあるかどうかを詳しく調べていません)。

ParseKit は再帰には問題ありませんが、左再帰には問題ありません。これに関する最良の説明は、 Steven Metsker の「Building Parsers with Java」にあります。またはWeb を検索します

しかし、基本的に左再帰は、生成物が (式の左側で) 自分自身をすぐに参照する場合です。

e = e '-' Number;

また

e = Number | e '-' Number;

代わりに、次のように左再帰を削除するように文法を設計する必要があります。

e = Number ('-' Number)*;
于 2013-01-30T06:16:55.020 に答える