`
RednaxelaFX
  • 浏览: 3052156 次
  • 性别: Icon_minigender_1
  • 来自: 海外
社区版块
存档分类
最新评论

跟语法搏斗……

阅读更多
以前一直听别人说写语法是件极其烧脑细胞的事,但是以前没自己写过没什么实感。这回可是好好体会了一次。

因为自顶而下的递归下降parser比较符合人的思维顺序,而相对来说LR系的shift-reduce冲突可能很难找到根源,所以这次在写语法的时候用ANTLR来帮忙了。具体来说是靠ANTLRWorks的编辑器/解释器/调试器来帮忙写出一个能正确解析我想使用的语法构造的LL(*)语法,然后再从这个语法出发来写比较正式的语法,大概还是会回归LALR(1)吧。我还是很想用Irony的。

问题是在不熟悉ANTLR的情况下,即便是递归下降也一样RP多多。我已经试了数不清多少次瞪着解释器里出现的那NoViableAltException发呆了。而且真的出现NoViableAltException,或者别的RecognitionException时,在ANTLRWorks能看到的信息还是觉得太少了。
可能是我现在还不太熟悉ANTLRWorks里的调试器的用法,总觉得真的要出错的时候,就在关键的那一步,一按step就出来一堆decision然后就error了,脑子完全没跟上啊。

结果还是靠把lexer和parser生成出来之后扔到Eclipse里慢慢调试才明白发生了什么事。不过想来也是,假如这时面对的是LR系的parser的话恐怕更郁闷了。
在调试的时候,基本上就是从自己的parser类跳到CommonTokenStream.LA(int)里,继续跳入CommonTokenStream.LT(int),CommonTokenStream.fillBuffer(),这里会跳入自己的lexer里的nextToken()。我碰到的好几个问题都是在lexer里出现的,刚开始跟是在CommonTokenStream.fillBuffer()里跟,等那个while循环慢慢把token吃进来然后在tokens这个ArrayList里看每个token的type看看对不对。实际上直接在自己的lexer里设断点也应该能行吧。

嘛,我是太不熟悉这玩意了。出现左递归还好办,但很多错误在ANTLRWorks的grammar check里都不会说出来;它也不知道你是不是故意那样写的嘛。我一开始肯定是昏了,居然没想起一个lexeme只能对应于一个token type。然后满怀希望要让"="既属于EQ又属于AssignmentOperator。傻了嗯。

ANTLR会为在语法规则里出现的所有字面量生成匿名的token type,看上去就像T10、T32之类的。为了让生成的代码好读一点,也应该用赋予了名字的词法规则来指定那些字面量,像是左括号"(",右括号")"之类。当希望某个lexeme本身就以一个独立的token type出现而不与其它lexeme混在一起(例如说"="与"+="要有不同的token type)时,这个词法规则就不能有fragment修饰符。

昨晚开始从头琢磨了一个语法出来。现在还相当的初步和粗糙,大概还有不少bug吧,只是我暂时还没看到。能接受这样的输入:
var globalVar : int = 0;

// a line of comment

function globalFunction() {
    function nestedFunction() {
        /* make a local variable */
        var localVar = ( globalVar = 1 );
    }
    nestedFunction(); // call the nested function
}

globalFunction(); // call the global function


生成这样的语法树:


慢慢再拿这东西继续实验吧。我觉得这个语法文件的命就是要被弄得很乱,呵呵。

话说我对C-like的语言彻底改观了。以前没有自己从头写过语法没有体会,这纯C的语法也是一点都不简单啊。像是for循环的初始化和更新的部分,里面到底应该怎么写才对我现在都还不清楚;下面的语法文件里对forStatement写的很明显是猜的+凑的。Java Language Specification 3rdD 2.0里的定义就很不一样,但这中间的差异可能一直会影响到表达式和语句最基本的规则的写法。到底怎么样比较好,我还是自己慢慢实验慢慢体会吧~

Dolphin.g (2008-03-26 rev 3):
grammar Dolphin;

// Parser

program	:	sourceElements EOF
	;

sourceElements
	:	( sourceElement )+
	;

sourceElement
	:	statement
	|	functionDeclaration
	;

functionDeclaration
	:	FUNCTION Identifier LPAREN formalParameters RPAREN block
	;

formalParameters
	:	formalParameterList
	|	/* empty */
	;

formalParameterList
	:	parameter ( COMMA parameter)*
	;

parameter
	:	Identifier ( COLON TypeSpecifier )?
	;

block
	:	LBRACE sourceElements RBRACE
	;

statement
	:	block
	|	variableStatement
	|	expressionStatement
	|	selectionStatement
	|	iterationStatement
	|	breakStatement
	|	continueStatement
	|	returnStatement
	|	emptyStatement
	;

variableStatement
	:	VAR variableDeclarationList SEMICOLON
	;

variableDeclarationList
	:	variableDeclaration ( COMMA variableDeclaration )*
	;

variableDeclaration
	:	Identifier ( COLON TypeSpecifier )? ( initialiser )?
	;

initialiser
	:	AssignmentOperator expression
	;

selectionStatement
	:	ifStatement
//	|	switchStatement
	;

ifStatement
	:	IF LPAREN expression RPAREN statement
	;

//switchStatement
//	:	
//	;

iterationStatement
	:	doWhileStatement
	|	whileStatement
	|	forStatement
	;

doWhileStatement
	:	DO statement WHILE LPAREN expression RPAREN SEMICOLON
	;

whileStatement
	:	WHILE LPAREN expression RPAREN statement
	;

forStatement
	:	FOR LPAREN ( VAR variableDeclaration | statementExpressionList )? SEMICOLON expression? SEMICOLON statementExpressionList? RPAREN statement
	;

breakStatement
	:	BREAK SEMICOLON
	;

continueStatement
	:	CONTINUE SEMICOLON
	;

returnStatement
	:	RETURN expression? SEMICOLON
	;

emptyStatement
	:	/* empty */ SEMICOLON
	;

expressionStatement
	:	expression SEMICOLON
	;

statementExpressionList
	:	statementExpression ( COMMA statementExpression )*
	;

statementExpression
	:	expression // TODO: this rule requires further refactoring
	;

expression
	:	assignmentExpression
	|	simpleExpression
	;

assignmentExpression
	:	Identifier ( AssignmentOperator | AssignmentShorthandOperator ) expression
	;

simpleExpression
	:	additiveExpression ( RelationalOperator additiveExpression )?
	;

additiveExpression
	:	multiplicativeExpression ( AddOperator multiplicativeExpression )*
	;

multiplicativeExpression
	:	primaryExpression ( MulOperator primaryExpression )*
	;

primaryExpression
	:	LPAREN expression RPAREN
	|	functionInvocationExpression
	|	Identifier // variable
	|	literal
	;

functionInvocationExpression
	:	Identifier LPAREN argumentList RPAREN
	;

argumentList
	:	expression ( COMMA expression )*
	|	/* empty */
	;

literal	:	integerLiteral
	|	FloatingPointLiteral
	|	CharacterLiteral
	|	StringLiteral
	|	BooleanLiteral
	|	NullLiteral
	;

integerLiteral
	:	HexLiteral
	|	OctalLiteral
	|	DecimalLiteral
	;

// Lexer

AssignmentShorthandOperator
	:	( '+=' | '-=' | '*=' | '/=' | '%=' )
	;

RelationalOperator
	:	( '<' | '>' | '==' | '<=' | '>=' | '!=' )
	;

AddOperator
	:	( '+' | '-' )
	;

MulOperator
	:	( '*' | '/' | '%' )
	;

AssignmentOperator
	:	'='
	;

COMMA	:	','
	;

COLON	:	':'
	;

SEMICOLON
	:	';'
	;

LPAREN	:	'('
	;

RPAREN	:	')'
	;

LBRACE	:	'{'
	;

RBRACE	:	'}'
	;

BREAK	:	'break'
	;

CONTINUE:	'continue'
	;

DO	:	'do'
	;

FOR	:	'for'
	;

FUNCTION:	'function'
	;

IF	:	'if'
	;

RETURN	:	'return'
	;

VAR	:	'var'
	;

WHILE	:	'while'
	;

HexLiteral
	:	'0' ('x'|'X') HexDigit+ IntegerTypeSuffix?
	;

DecimalLiteral
	:	('0' | '1'..'9' '0'..'9'*) IntegerTypeSuffix?
	;

OctalLiteral
	:	'0' ('0'..'7')+ IntegerTypeSuffix?
	;

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

fragment
IntegerTypeSuffix
	:	('l'|'L')
	;

FloatingPointLiteral
	:	('0'..'9')+ '.' ('0'..'9')* Exponent? FloatTypeSuffix?
	|	'.' ('0'..'9')+ Exponent? FloatTypeSuffix?
	|	('0'..'9')+ Exponent FloatTypeSuffix?
	;

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

fragment
FloatTypeSuffix
	:	( 'f' | 'F' | 'd' | 'D' )
	;

BooleanLiteral
	:	'true'
	|	'false'
	;

NullLiteral
	:	'null'
	;

CharacterLiteral
	:	'\'' ( EscapeSequence | ~( '\'' | '\\' ) ) '\''
	;

StringLiteral
	:	'"' ( EscapeSequence | ~( '\\' | '"' ) )* '"'
	;

fragment
EscapeSequence
	:	'\\' ( 'b' | 't' | 'n' | 'f' | 'r' | '\"' | '\'' | '\\' )
	|	UnicodeEscape
	|	OctalEscape
	;

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

fragment
UnicodeEscape
	:	'\\' 'u' HexDigit HexDigit HexDigit HexDigit
	;

TypeSpecifier
	:	'boolean'
	|	'char'
	|	'short'
	|	'int'
	|	'long'
	|	'double'
	|	'string'
	;

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

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

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

LineComment
	:	'//' ~('\n'|'\r')* '\r'? '\n' { $channel = HIDDEN; }
	;
分享到:
评论
3 楼 lwwin 2008-03-28  
偶不习惯OO那种繁琐的东西,偶大概能够看懂的也只有C了罢- -!
2 楼 RednaxelaFX 2008-03-28  
lwwin 写道
貌似是比较新的C实现?

没啦。基本上是想从简单的东西做起来找感觉。
这东西从C与ECMAScript 4糅合简化而来的。暂时定义了一些primitive type,以ECMAScript 4/ActionScript 3的形式给变量标注类型(可选标注),然后允许嵌套的函数定义这样。没有面向对象的支持。要把canconial name的问题都考虑上的话好麻烦 T T
总之这个慢慢来……
1 楼 lwwin 2008-03-28  
貌似是比较新的C实现?
支持随机定义貌似-v-+ 好长好长

最近电源坏了- -

相关推荐

    编译原理语法制导翻译器 课程设计

    3、1语法制导定义…………………………………… 2 3、2设计思想………………………………………… 3 3、3基本思路………………………………………… 4 四、设计内容……………………………………………… 4 五、设计...

    Velocity模板技术语法详细介绍

    1.变量………………………………………………………………………………1 2.循环………………………………………………………………………………2 3.条件语句……………………………………………………………………...

    记事本2(编程助手,小巧,绿色,多种语法高亮显示……)

    纯绿色 记事本,功能强大,小巧,支持多种编程语言,语法高亮显示,集成Word、UltraEdit等的许多功能……

    编译原理 语法分析 语法树生成

    本主题主要关注的是“语法分析”和“语法树生成”,特别是如何使用经典的工具yacc(Yet Another Compiler-Compiler)和lex来实现C++语言的语法分析器。 首先,我们来了解一下什么是语法分析。语法分析是编译器设计...

    JS语法字典 JS语法字典 JS语法字典

    JS语法字典JS语法字典JS语法字典JS语法字典JS语法字典JS语法字典JS语法字典JS语法字典JS语法字典JS语法字典JS语法字典JS语法字典JS语法字典JS语法字典JS语法字典JS语法字典JS语法字典JS语法字典

    日语二级语法汇总下载版

    它通常跟在形容词连体形或名词+の后面,表示因为某个原因导致了过度的状态,常常用来表达由于某种情绪的过度而产生了某种结果。比如,因为过于高兴而无法控制自己的情绪,可以这样表达:“喜びのあまり、声をあげた...

    编译原理语法树的实现

    语法树,也被称为抽象语法树(Abstract Syntax Tree, AST),是编译器或解释器在解析源代码时构建的一种数据结构,它以树的形式表示程序的语法结构。本文将深入探讨编译原理中语法树的实现,以及其在程序分析、翻译...

    小学语文语法知识大全.doc

    * 为下面的句子搭上恰当的关联词:只要……就……、如果……就……、因为……所以……、既然……就……、虽然……但是……、不但……而且……、即使……也…… 七、小学关联词专项练习题: * 一.为下面的句子搭上...

    java 实现词法分析器以及语法分析器

    在编程领域,编译原理是理解计算机语言处理过程的关键部分,它涉及到词法分析和语法分析两个重要步骤。本文将详细介绍使用Java实现词法分析器和语法分析器的相关知识点。 词法分析,也称为扫描或Tokenization,是...

    日语三级考试重点语法总结

    日语三级考试是针对日语学习者的中等级别考试,主要测试考生对于日语语法、词汇、读解和听力理解的能力。以下是对日语三级考试中的一些重点语法点的详细解释: 1. お+动词连用形+になる / お+动词连用形+くださ...

    c++语法分析器c++语法分析器c++语法分析器c++语法分析器c++语法分析器

    语法分析器语法分析器语法分析器语法分析器语法分析器语法分析器语法分析器语法分析器语法分析器

    PRD语法、自查、排版……产品需求文档应该酱紫!(附大厂模板).doc

    在撰写PRD时,遵循一定的格式、语法和自查方法至关重要,以确保信息准确无误地传递。 一、PRD的形式 1. 原型附带文字:对于移动端产品,通常会结合原型图进行描述,特别是交互逻辑和取值规则。如果文字较多,可以将...

    C语言常见的语法错误

    C语言常见的语法错误 C语言是一种古老而强大的编程语言,在编程过程中,程序员经常会遇到各种语法错误。这些错误可能会导致程序崩溃、无法编译或运行不正常。了解这些常见的语法错误,可以帮助初学者减少调试时间、...

    高级英语语法(薄冰)下.pdf

    高级英语语法是英语学习中一个深入且必要的领域,它帮助学习者能够掌握和运用复杂的英语句式和表达方式。薄冰所著的《高级英语语法》是该领域的经典教材之一,本书内容涵盖了英语语法的多个方面,其中包括语气、形容...

    英语语法大全(上).pdf

    ### 英语语法大全(上)知识点概览 #### 一、书籍概述 《英语语法大全(上)》是一本全面介绍英语语法基础知识的专业书籍,旨在帮助中高级水平的英语学习者深入理解并掌握英语的基本规则。该书分为前后两大部分,...

    oracle存储过程语法

    4. 用 select …… into ……给变量赋值。 5. 在代码中抛异常用 raise+异常名。 Oracle存储过程语法是Oracle数据库中的一种重要语法规则,用于创建和管理存储过程。存储过程可以帮助我们实现复杂的业务逻辑,并提高...

    antlr抽象语法树的构建

    ### ANTLR抽象语法树构建详解 #### 一、引言 在软件开发尤其是编译器设计领域,ANTLR 是一款非常流行的工具,用于构建解析器、词法分析器以及抽象语法树 (Abstract Syntax Tree, AST)。抽象语法树是表示源代码结构...

    日语一级词汇,语法,练习

    【日语一级词汇,语法,练习】是针对日本语能力测试N1级别学习者的重要复习资源,涵盖了词汇、文法和大量的练习题。N1级别是日语能力测试的最高等级,要求考生具备高度的日语读写听说能力。以下是对部分题目中涉及的...

    struts2校验器

    struts2校验器,字段,非字段校验器的语法规则……

Global site tag (gtag.js) - Google Analytics