趣味・茶碗拭き


Antlrworksで遊んでみる


WirthのCompiler Constructionを読んでOberon-0のパーサを作ることにしました。セマンティックは無しです。
grammarファイルはEBNFを正規表現で書き換えて、以下のようになりました。ちなみにスタートはmoduleからです。



grammar oberon0;


ident	:	ID;
integer	:	INT;
selector:	(('.' ident|'[' expression ']') )*;
factor	:	ident selector | integer |'('expression')' |'~' factor;
term	:	factor(('*'|'DIV'|'MOD'|'&') factor)*;
simpleexpression
	:	('+'|'-')? term (('+'|'-'|'OR') term)*;	
expression
	:	simpleexpression (('='|'#'|'<'|'<='|'>'|'>=') simpleexpression)?;
assignment
	:	ident selector ':=' expression;
actualparameters
	:	'('(expression(',' expression)*)?')';
procedurecall
	:	ident(actualparameters)?;
ifstatement
	:	'IF' expression 'THEN' statementsequence
		('ELSIF' expression 'THEN' statementsequence)*
		('ELSE' statementsequence)? 'END';
whilestatement
	:	'WHILE' expression 'DO' statementsequence 'END';
statement
	:	(assignment|procedurecall|ifstatement|whilestatement)?;
statementsequence
	:	statement(';' statement)*;
identlist
	:	ident(',' ident)*;
arraytype
	:	'ARRAY' expression 'OF' type;
fieldlist
	:	(identlist ':'type)?;
recordtype
	:	'RECORD' fieldlist (';' fieldlist)* 'END';
type	:	ident | arraytype | recordtype;
fpsection
	:	('VAR')? identlist ':' type;
formalparameters
	:	'(' (fpsection (';' fpsection)*)? ')';
procedureheading
	:	'PROCEDURE' ident(formalparameters)?;
procedurebody
	:	declarations ('BEGIN' statementsequence)? 'END';
proceduredeclaration
	:	procedureheading ';' procedurebody ident;
declarations
	:	('CONST' (ident '=' expression ';')*)?
		('TYPE' (ident '=' type ';')*)?
		('VAR' (identlist ':' type ';')*)?
		(proceduredeclaration ';')*;
module	:	'MODULE' ident ';' declarations
		('BEGIN' statementsequence)? 'END' ident '.';
		

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
    :   '//' ~('\n'|'\r')* '\r'? '\n' {$channel=HIDDEN;}
    |   '/*' ( options {greedy=false;} : . )* '*/' {$channel=HIDDEN;}
    ;

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

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

CHAR:  '\'' ( 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
    ;




入力するプログラムは、

MODULE Sample;

PROCEDURE Multiply;
 VAR x,y,z: INTEGER;
BEGIN Read(x);Read(y);z:=0;
  WHILE x > 0 DO
    IF x MOD 2 = 1 THEN z := z + y END;
    y :=2*y; x:=x DIV 2
  END;
  Write(x);Write(y);Write(z);WriteLn
END Multiply;

PROCEDURE Divide;
 VAR x,y,r,q,w:INTEGER;
BEGIN Read(x);Read(y);r :=x;q:=0;w:=y;
  WHILE w <=r DO w:=2*w END;
  WHILE w > y DO
    q:=2*q;w:=w DIV 2;
    IF w <= r THEN r:=r-w;q:=q+1 END
  END;
  Write(x);Write(y);Write(q);Write(r);WriteLn
END Divide;

PROCEDURE BinSearch;
 VAR i,j,k,n,x:INTEGER;
   a: ARRAY 32 OF INTEGER;
BEGIN Read(n); k := 0;
  WHILE k < n DO Read(a[k]);k:=k+1 END;
  Read(x);i:=0;j:=n;
  WHILE i < j DO
    k := (i+j) DIV 2;
    IF x < a[k] THEN j:=k ELSE i:=k+1 END
  END;
  Write(i);Write(j);Write(a[j]);WriteLn
END BinSearch;

END Sample.



特にバックトラックしたりする必要はなさそうです。パースツリーを載せてみます。