2012-05-10 10 views
1

antlrを使って単純な小トーク文法を書いています。 Smalltalkの簡略化されたバージョンですが、基本的なアイデアは同じです(たとえばメッセージの受け渡しなど)。私は番号の単項マイナス(ルールnumberについてコメント部分)に問題があるantlr - 単項マイナスとメッセージ連鎖を使った簡略化されたsmalltalk文法

grammar GAL; 

options { 
    //k=2; 
    backtrack=true; 
} 

ID : ('a'..'z'|'A'..'Z'|'_') ('a'..'z'|'A'..'Z'|'0'..'9'|'_')* 
    ; 

INT : '0'..'9'+ 
    ; 

FLOAT 
    : ('0'..'9')+ '.' ('0'..'9')* EXPONENT? 
    | '.' ('0'..'9')+ EXPONENT? 
    | ('0'..'9')+ EXPONENT 
    ; 

COMMENT 
    : '"' (options {greedy=false;} : .)* '"' {$channel=HIDDEN;} 
    ; 

WS : (' ' 
     | '\t' 
     ) {$channel=HIDDEN;} 
    ; 

NEW_LINE 
    : ('\r'?'\n') 
    ; 

STRING 
    : '\'' (ESC_SEQ | ~('\\'|'\''))* '\'' 
    ; 

fragment 
EXPONENT : ('e'|'E') ('+'|'-')? ('0'..'9')+ ; 

fragment 
HEX_DIGIT : ('0'..'9'|'a'..'f'|'A'..'F') ; 

fragment 
ESC_SEQ 
    : '\\' ('b'|'t'|'n'|'f'|'r'|'\"'|'\''|'\\') 
    | UNICODE_ESC 
    | OCTAL_ESC 
    ; 

fragment 
OCTAL_ESC 
    : '\\' ('0'..'3') ('0'..'7') ('0'..'7') 
    | '\\' ('0'..'7') ('0'..'7') 
    | '\\' ('0'..'7') 
    ; 

fragment 
UNICODE_ESC 
    : '\\' 'u' HEX_DIGIT HEX_DIGIT HEX_DIGIT HEX_DIGIT 
    ; 

BINARY_MESSAGE_CHAR 
    : ('~' | '!' | '@' | '%' | '&' | '*' | '-' | '+' | '=' | '|' | '\\' | '<' | '>' | ',' | '?' | '/') 
     ('~' | '!' | '@' | '%' | '&' | '*' | '-' | '+' | '=' | '|' | '\\' | '<' | '>' | ',' | '?' | '/')? 
    ; 

// parser 

program 
    : NEW_LINE* (statement (NEW_LINE+ | EOF))* 
    ; 

statement 

    : message_sending 
    | return_statement 
    | assignment 
    | temp_variables 
    ; 

return_statement 
    : '^' statement 
    ; 

assignment 
    : identifier ':=' statement 
    ; 

temp_variables 
    : '|' identifier+ '|' 
    ; 

object 
    : raw_object 
    ; 

raw_object 
    : number 
    | string 
    | identifier 
    | literal 
    | block 
    | '(' message_sending ')' 
    ; 

message_sending 
    : keyword_message_sending 
    ; 

keyword_message_sending 
    : binary_message_sending keyword_message? 
    ; 

binary_message_sending 
    : unary_message_sending binary_message* 
    ; 

unary_message_sending 
    : object (unary_message)* 
    ; 

unary_message 
    : unary_message_selector 
    ; 

binary_message 
    : binary_message_selector unary_message_sending 
    ; 

keyword_message 
    : (NEW_LINE? single_keyword_message_selector NEW_LINE? binary_message_sending)+ 
    ; 

block 
    : 
     '[' (block_signiture 

    )? NEW_LINE* 
     block_body 

     NEW_LINE* ']' 
    ; 

block_body 
    : (statement 

    )? 
     (NEW_LINE+ statement 

    )* 
    ; 


block_signiture 
    : 
     (':' identifier 

    )+ '|' 
    ; 

unary_message_selector 
    : identifier 
    ; 

binary_message_selector 
    : BINARY_MESSAGE_CHAR 
    ; 

single_keyword_message_selector 
    : identifier ':' 
    ; 

keyword_message_selector 
    : single_keyword_message_selector+ 
    ; 

symbol 
    : '#' (string | identifier | binary_message_selector | keyword_message_selector) 
    ; 

literal 
    : symbol block? // if there is block then this is method 
    ; 

number 
    : /*'-'?*/ 
    (INT | FLOAT) 
    ; 

string 
    : STRING 
    ; 

identifier 
    : ID 
    ; 

1.単項マイナス

はここに私のこれまでの文法です。問題は、マイナスが有効なバイナリメッセージであることです。状況を悪化させるために、2つのマイナス記号も有効なバイナリメッセージです。私が必要とするのは、バイナリメッセージを送るオブジェクトがない場合の単項マイナスです(たとえば-3のfrotに何もないので、-3 + 4は単項マイナスでなければなりません)。また、(-3)は2進マイナスでなければなりません。 1 - -2がパラメータ-2でバイナリメッセージ ' - 'になるのはすばらしいことですが、私はそれなしで暮らすことができます。これどうやってするの?

単項マイナスをコメント解除すると、1-2などの解析時にエラーMismatchedSetException(0!= null)が発生します。スモールトークのようchaingingメッセージを実施するための最良の方法だろう何

を連鎖

2.メッセージ?すべてのメッセージが、この場合objには、同じオブジェクトに送信される

obj message1 + 3; 
    message2; 
    + 3; 
    keyword: 2+3 

:私はこれによって意味することは、このようなものです。メッセージの優先順位は保持する必要があります(単項>バイナリ>キーワード)。

3.バックトラック

この文法のほとんどはk=2で解析することができますが、入力はこのようなものであるとき:

1 + 2 
Obj message: 
    1 + 2 
    message2: 'string' 

パーサはsingle_keyword_message_selectorとしてOBJに一致させようとトークンにUnwantedTokenExcaptionを上げますmessagek=2を削除してbacktrack=trueに設定した場合、すべてが正常に動作します。バックトラックを取り除いて希望の動作を得るにはどうすればよいですか?

また、文法のほとんどはk=1を使用して解析することができるので、それを必要とする規則についてのみk=2を設定しようとしましたが、無視されます。私はこのようなものでした:

rule 
    options { k = 2; } 
    : // rule definition 
    ; 

を私はグローバルオプションでのkを設定するまで、それは動作しません。私はここで何が欠けていますか?


更新

私はそれに依存したコードがたくさんあるので、ゼロから文法を記述するのに理想的なソリューションではありません。また、欠落しているsmalltalkのいくつかの機能は、設計上欠けています。これは、別の小トークトーク実装ではありません、小トークは単なるインスピレーションでした。

-1+2または2+(-1)のようなケースでは、単項マイナスをうまく働くことができます。2 -- -1のようなケースはあまり重要ではありません。

また、メッセージの連鎖は、ポーズと同じように簡単に行う必要があります。それは私が生成しているASTを変更する考えが好きではないことを意味します。

バックトラックについて - 私はそれと一緒に暮らすことができます、ちょうどここで個人的な好奇心から尋ねました。

これはASTを生成する少し修正された文法です - 変更したくないものを理解するのに役立つかもしれません。 (temp_variablesはおそらく削除される予定ですが、私はその決定をしなかった)。

grammar GAL; 

options { 
    //k=2; 
    backtrack=true; 
    language=CSharp3; 
    output=AST; 
} 

tokens { 
    HASH  = '#'; 
    COLON = ':'; 
    DOT  = '.'; 
    CARET = '^'; 
    PIPE  = '|'; 
    LBRACKET = '['; 
    RBRACKET = ']'; 
    LPAREN = '('; 
    RPAREN = ')'; 
    ASSIGN = ':='; 
} 

// generated files options 
@namespace { GAL.Compiler } 
@lexer::namespace { GAL.Compiler} 

// this will disable CLSComplaint warning in ANTLR generated code 
@parser::header { 
// Do not bug me about [System.CLSCompliant(false)] 
#pragma warning disable 3021 
} 

@lexer::header { 
// Do not bug me about [System.CLSCompliant(false)] 
#pragma warning disable 3021 
} 

ID : ('a'..'z'|'A'..'Z'|'_') ('a'..'z'|'A'..'Z'|'0'..'9'|'_')* 
    ; 

INT : '0'..'9'+ 
    ; 

FLOAT 
    : ('0'..'9')+ '.' ('0'..'9')* EXPONENT? 
    | '.' ('0'..'9')+ EXPONENT? 
    | ('0'..'9')+ EXPONENT 
    ; 

COMMENT 
    : '"' (options {greedy=false;} : .)* '"' {$channel=Hidden;} 
    ; 

WS : (' ' 
     | '\t' 
     ) {$channel=Hidden;} 
    ; 

NEW_LINE 
    : ('\r'?'\n') 
    ; 

STRING 
    : '\'' (ESC_SEQ | ~('\\'|'\''))* '\'' 
    ; 

fragment 
EXPONENT : ('e'|'E') ('+'|'-')? ('0'..'9')+ ; 

fragment 
HEX_DIGIT : ('0'..'9'|'a'..'f'|'A'..'F') ; 

fragment 
ESC_SEQ 
    : '\\' ('b'|'t'|'n'|'f'|'r'|'\"'|'\''|'\\') 
    | UNICODE_ESC 
    | OCTAL_ESC 
    ; 

fragment 
OCTAL_ESC 
    : '\\' ('0'..'3') ('0'..'7') ('0'..'7') 
    | '\\' ('0'..'7') ('0'..'7') 
    | '\\' ('0'..'7') 
    ; 

fragment 
UNICODE_ESC 
    : '\\' 'u' HEX_DIGIT HEX_DIGIT HEX_DIGIT HEX_DIGIT 
    ; 

BINARY_MESSAGE_CHAR 
    : ('~' | '!' | '@' | '%' | '&' | '*' | '-' | '+' | '=' | '|' | '\\' | '<' | '>' | ',' | '?' | '/') 
     ('~' | '!' | '@' | '%' | '&' | '*' | '-' | '+' | '=' | '|' | '\\' | '<' | '>' | ',' | '?' | '/')? 
    ; 

// parser 

public program returns [ AstProgram program ] 
    : { $program = new AstProgram(); } 
    NEW_LINE* 
    (statement (NEW_LINE+ | EOF) 
     { $program.AddStatement($statement.stmt); } 
    )* 
    ; 

statement returns [ AstNode stmt ] 
    : message_sending 
     { $stmt = $message_sending.messageSending; } 
    | return_statement 
     { $stmt = $return_statement.ret; } 
    | assignment 
     { $stmt = $assignment.assignment; } 
    | temp_variables 
     { $stmt = $temp_variables.tempVars; } 
    ; 

return_statement returns [ AstReturn ret ] 
    : CARET statement 
     { $ret = new AstReturn($CARET, $statement.stmt); } 
    ; 

assignment returns [ AstAssignment assignment ] 
    : dotted_expression ASSIGN statement 
     { $assignment = new AstAssignment($dotted_expression.dottedExpression, $ASSIGN, $statement.stmt); } 
    ; 

temp_variables returns [ AstTempVariables tempVars ] 
    : p1=PIPE 
     { $tempVars = new AstTempVariables($p1); } 
    (identifier 
     { $tempVars.AddVar($identifier.identifier); } 
    )+ 
    p2=PIPE 
     { $tempVars.EndToken = $p2; } 
    ; 

object returns [ AstNode obj ] 
    : number 
     { $obj = $number.number; } 
    | string 
     { $obj = $string.str; } 
    | dotted_expression 
     { $obj = $dotted_expression.dottedExpression; } 
    | literal 
     { $obj = $literal.literal; } 
    | block 
     { $obj = $block.block; } 
    | LPAREN message_sending RPAREN 
     { $obj = $message_sending.messageSending; } 
    ; 

message_sending returns [ AstKeywordMessageSending messageSending ] 
    : keyword_message_sending 
     { $messageSending = $keyword_message_sending.keywordMessageSending; } 
    ; 

keyword_message_sending returns [ AstKeywordMessageSending keywordMessageSending ] 
    : binary_message_sending 
     { $keywordMessageSending = new AstKeywordMessageSending($binary_message_sending.binaryMessageSending); } 
    (keyword_message 
     { $keywordMessageSending = $keywordMessageSending.NewMessage($keyword_message.keywordMessage); } 
    )? 
    ; 

binary_message_sending returns [ AstBinaryMessageSending binaryMessageSending ] 
    : unary_message_sending 
     { $binaryMessageSending = new AstBinaryMessageSending($unary_message_sending.unaryMessageSending); } 
    (binary_message 
     { $binaryMessageSending = $binaryMessageSending.NewMessage($binary_message.binaryMessage); } 
    )* 
    ; 

unary_message_sending returns [ AstUnaryMessageSending unaryMessageSending ] 
    : object 
     { $unaryMessageSending = new AstUnaryMessageSending($object.obj); } 
    (
     unary_message 
     { $unaryMessageSending = $unaryMessageSending.NewMessage($unary_message.unaryMessage); } 
    )* 
    ; 

unary_message returns [ AstUnaryMessage unaryMessage ] 
    : unary_message_selector 
     { $unaryMessage = new AstUnaryMessage($unary_message_selector.unarySelector); } 
    ; 

binary_message returns [ AstBinaryMessage binaryMessage ] 
    : binary_message_selector unary_message_sending 
     { $binaryMessage = new AstBinaryMessage($binary_message_selector.binarySelector, $unary_message_sending.unaryMessageSending); } 
    ; 

keyword_message returns [ AstKeywordMessage keywordMessage ] 
    : 
    { $keywordMessage = new AstKeywordMessage(); } 
    (
     NEW_LINE? 
     single_keyword_message_selector 
     NEW_LINE? 
     binary_message_sending 
     { $keywordMessage.AddMessagePart($single_keyword_message_selector.singleKwSelector, $binary_message_sending.binaryMessageSending); } 
    )+ 
    ; 

block returns [ AstBlock block ] 
    : LBRACKET 
     { $block = new AstBlock($LBRACKET); } 
    (
     block_signiture 
     { $block.Signiture = $block_signiture.blkSigniture; } 
    )? NEW_LINE* 
     block_body 
     { $block.Body = $block_body.blkBody; } 
     NEW_LINE* 
     RBRACKET 
     { $block.SetEndToken($RBRACKET); } 
    ; 

block_body returns [ IList<AstNode> blkBody ] 
    @init { $blkBody = new List<AstNode>(); } 
    : 
    (s1=statement 
     { $blkBody.Add($s1.stmt); } 
    )? 
    (NEW_LINE+ s2=statement 
     { $blkBody.Add($s2.stmt); } 
    )* 
    ; 


block_signiture returns [ AstBlockSigniture blkSigniture ] 
    @init { $blkSigniture = new AstBlockSigniture(); } 
    : 
    (COLON identifier 
     { $blkSigniture.AddIdentifier($COLON, $identifier.identifier); } 
    )+ PIPE 
     { $blkSigniture.SetEndToken($PIPE); } 
    ; 

unary_message_selector returns [ AstUnaryMessageSelector unarySelector ] 
    : identifier 
     { $unarySelector = new AstUnaryMessageSelector($identifier.identifier); } 
    ; 

binary_message_selector returns [ AstBinaryMessageSelector binarySelector ] 
    : BINARY_MESSAGE_CHAR 
     { $binarySelector = new AstBinaryMessageSelector($BINARY_MESSAGE_CHAR); } 
    ; 

single_keyword_message_selector returns [ AstIdentifier singleKwSelector ] 
    : identifier COLON 
     { $singleKwSelector = $identifier.identifier; } 
    ; 

keyword_message_selector returns [ AstKeywordMessageSelector keywordSelector ] 
    @init { $keywordSelector = new AstKeywordMessageSelector(); } 
    : 
    (single_keyword_message_selector 
     { $keywordSelector.AddIdentifier($single_keyword_message_selector.singleKwSelector); } 
    )+ 
    ; 

symbol returns [ AstSymbol symbol ] 
    : HASH 
    (string 
     { $symbol = new AstSymbol($HASH, $string.str); } 
    | identifier 
     { $symbol = new AstSymbol($HASH, $identifier.identifier); } 
    | binary_message_selector 
     { $symbol = new AstSymbol($HASH, $binary_message_selector.binarySelector); } 
    | keyword_message_selector 
     { $symbol = new AstSymbol($HASH, $keyword_message_selector.keywordSelector); } 
    ) 
    ; 

literal returns [ AstNode literal ] 
    : symbol 
     { $literal = $symbol.symbol; } 
    (block 
     { $literal = new AstMethod($symbol.symbol, $block.block); } 
    )? // if there is block then this is method 
    ; 

number returns [ AstNode number ] 
    : /*'-'?*/ 
    (INT 
     { $number = new AstInt($INT); } 
    | FLOAT 
     { $number = new AstInt($FLOAT); } 
    ) 
    ; 

string returns [ AstString str ] 
    : STRING 
     { $str = new AstString($STRING); } 
    ; 

dotted_expression returns [ AstDottedExpression dottedExpression ] 
    : i1=identifier 
     { $dottedExpression = new AstDottedExpression($i1.identifier); } 
    (DOT i2=identifier 
     { $dottedExpression.AddIdentifier($i2.identifier); } 
    )* 
    ; 

identifier returns [ AstIdentifier identifier ] 
    : ID 
     { $identifier = new AstIdentifier($ID); } 
    ; 

答えて

1

ハイSmalltalkの文法ライター、まず

、正しく解析するSmalltalkの文法を取得する(1 - -2) '' およびオプションをサポートするために最後の文などでは、空白を重要なものとして扱うべきです。隠しチャンネルに置かないでください。

これまでの文法は、ルールを十分に小さい断片に分解していません。これはあなたがK = 2とバックトラッキングで見たような問題になります。

私はレッドラインSmalltalkのプロジェクトhttp://redline.st & https://github.com/redline-smalltalk/redline-smalltalk

RGS、ジェームズによって定義されるようにあなたはANTLRで働いSmalltalkの文法をチェックアウトをお勧めします。

+0

Smalltalkのランタイム/インタープリタには大きな違いがありますか? GNU Smalltalkバージョン3.2.4は '(1 - -2)printNl'を受け入れないので尋ねます。それは '(1 - - 2)printNl'(最後に' .'を付けてもしなくても)を受け入れます*。 –

+0

に関するもう一つ[あなたの文法](https://github.com/redline-smalltalk/redline-smalltalk/blob/master/src/main/antlr3/st/redline/compiler/Smalltalk.g):あなたのために」 '' r''を 'number'パーサルールに入れ、' | r |のように入力します。 r:= 42。 R printNl.'は '「r'' *** ***' ID'トークンとしてトークン化されないが、しかし、リテラル '」として発生しますr''トークン。あなたが望むものではないでしょうか?または私は何かを逃していますか? –

+0

あなたの答えをありがとう。私はASTを変更する必要があるので、文法を根本的に変更するのは理想的な解決策ではありません。私は単項マイナスを加える簡単な方法を探しています(1 - 2は単なるアイデアでした。私は-1 + 2または2 - ( - 3)の作業をするのがとてもうれしいです。 –

関連する問題