2012-05-10 4 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 단항 마이너스

:

은 여기 내 지금까지 문법이다. 문제는 마이너스가 유효한 바이너리 메시지라는 것입니다. 상황을 악화 시키려면 두 개의 마이너스 기호도 유효한 이진 메시지입니다. 내가 필요한 것은 바이너리 메시지를 보낼 객체가없는 경우 단항 마이너스입니다 (예를 들어, -3 + 4는 단항 마이너스 여야합니다. -3의 frot에는 아무것도 없기 때문입니다). 또한, (-3)은 2 진 빼기도해야합니다. 1 - -2가 매개 변수 -2가있는 이진 메시지 '-'이면 좋겠지 만, 나는 그걸없이 살 수 있습니다. 어떻게해야합니까?

단항 빼기를 주석 처리 해제하면 1-2와 같은 구문 분석시 오류 MismatchedSetException (0! = null)이 발생합니다.

2

는 스몰 토크에서 같은 chainging 메시지를 구현하는 가장 좋은 방법이 있을까요 체인 메시지? 내가이 의미하는 것은이 같은 것입니다 : 모든 메시지는이 경우 obj에, 같은 객체를 전송되는

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

. 메시지 우선 순위는 유지되어야합니다 (단항> 바이너리> 키워드).

3. 뒤로를

이 문법의 대부분은 k=2로 해석 될 수 있지만, 입력은이 같은 경우 :

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

파서 single_keyword_message_selector로 확대 개체를 일치 시키려고 토큰에 UnwantedTokenExcaption 제기 message. k=2을 제거하고 backtrack=true (내가했던 것처럼)을 설정하면 모든 것이 제대로 작동합니다. 역 추적을 제거하고 원하는 동작을 얻으려면 어떻게해야합니까?

또한 대부분의 문법은 k=1을 사용하여 구문 분석 할 수 있으므로이 규칙을 필요로하는 규칙에 대해서만 k=2으로 설정하려고 시도했지만 무시됩니다. 저는 다음과 같이했습니다 :

그러나 전역 옵션에서 k를 설정하기 전까지는 작동하지 않습니다. 내가 여기서 무엇을 놓치고 있니?


업데이트 : 나는 그것에 따라 코드를 많이 가지고 있기 때문에

처음부터 문법을 쓸 수있는 이상적인 솔루션이 아닙니다. 또한 누락 된 스몰 토크의 일부 기능은 설계 상 누락되었습니다. 이것은 다른 스몰 토크 구현을위한 것이 아니며 스몰 토크는 영감을주었습니다.

-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

안녕 스몰 토크 문법 작가, 첫째

, 제대로 분석하는 스몰 토크의 문법을 얻을 수 있습니다 (1 - -2) '.'와 옵션을 지원하기 위해 마지막 문장 등에서는 공백을 중요하게 취급해야합니다. 숨겨진 채널에 두지 마십시오.

지금까지 문법은 규칙을 충분히 작은 조각으로 나누지 않았습니다. 이것은 K = 2와 역 추적으로 보아온 것처럼 문제가 될 것입니다.

이 나는 ​​레드 라인 스몰 토크 프로젝트 http://redline.st & https://github.com/redline-smalltalk/redline-smalltalk

RGS, 제임스에 의해 정의 된대로 ANTLR에서 작업 스몰 토크의 문법을 확인하시기 바랍니다.

+0

스몰 토크 런타임/인터프리터에는 많은 차이가 있습니까? GNU 스몰 토크 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) 작업을하는 것이 매우 행복 할 것입니다. –