나는 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); }
;
스몰 토크 런타임/인터프리터에는 많은 차이가 있습니까? GNU 스몰 토크 3.2.4 버전은'(1 - 2) printNl'을 허용하지 않기 때문에 묻습니다. 그것은 *'(1 - - 2) printNl'을 받아들입니다 (끝에'.'가 있거나 없슴). –
[문법]에 관한 한 가지 더 (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'' 토큰으로 토큰 화합니다. 니가 원하는게 아니지, 그렇지? 또는 나는 무엇인가 놓치고 있냐? –
답변 해 주셔서 감사합니다. 변경해야 할 AST가 있기 때문에 근본적으로 문법을 변경하는 것은 이상적인 솔루션이 아닙니다. 지금 당장은 많은 작업이 필요합니다. 단항 마이너스를 추가하는 간단한 방법을 찾고 있는데 (1 - -2는 아이디어 일 뿐이므로 -1 + 2 또는 2 - (- 3) 작업을하는 것이 매우 행복 할 것입니다. –