Click here to Skip to main content
15,889,281 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
See more:
I'm creating an Antlr grammar for "The Elderscrolls 3" aka. Morrowind's scripting engine.(This is my first grammar btw, so it's probably not that good.)

Basically, it's not letting me define legal if blocks. (In my TES3 scripts.)

C#
// No viable alternative input at 'endif'. Line: 9
Begin test

short DoOnce

if ( DoOnce == 0 )
	MessageBox "Done"
	set DoOnce to -1
	return
endif

End


Here is my Antlr grammar. (Any general tips would be welcome. I basically looked at other grammars, and tried to figure out how to make it work with TES3's scripting language. So, this could have lot's of issues.)

C#
grammar TES3;

options 
{
	language=CSharp2;
}

tokens
{
	BEGIN = 'Begin';
	END = 'End';
	SET = 'set';
	TO = 'to';
	FIX = '->';
	QUOTE = '"';
}

@members 
{

// Script Name
public string ScriptName = String.Empty;

// Variables
public Dictionary<string,> Variables = new Dictionary<string,>(StringComparer.CurrentCultureIgnoreCase);

// Exception List
ArrayList exceptions = new ArrayList();

// Parsing: Entry Point
public void Begin(ListBox lb)
{
	lb.Items.Clear();
	program();
	PrintMessages(lb);
}

// Print Error Messages
void PrintMessages(ListBox lb)
{
	if (HasError)
	{
		// Overwrite Cryptic Errors
		string error = ErrorMessage;
		
		// End-of-File
		if (error.Contains("<eof>"))
			error = error.Replace("<eof>", "End-of-File");
			
		
			
		lb.ReportError(error, ErrorLevel.Error, ErrorPosition);
	}
}

// Report Error
public override void ReportError(RecognitionException e)
{
    exceptions.Add(e);
}

// Has Error
public bool HasError
{
	get { return exceptions.Count > 0; }
}

// ErrorMessage: Get's the error message.
public string ErrorMessage
{
	get 
    { 
    	if (exceptions.Count != 0)
        	return this.GetErrorMessage(exceptions[0] as RecognitionException, this.TokenNames);
        else	
		    return "No Errors Detected!";
    }
}

// ErrorPosition: Get's the line where the error occurred.
public int ErrorPosition
{
	get 
    {
	    if (exceptions.Count != 0)
        {
    	    RecognitionException EX = (RecognitionException)exceptions[0];
            return EX.Line;
        }
        else
        	return 0;
	}
}

// Quick Report
public void QuickReport(string s, int line)
{
	RecognitionException re = new RecognitionException(s);
    re.Line = line;
	ReportError(re);
}

// Format Report
public void FormatReport(string s, int line, params object[] param)
{
	QuickReport(String.Format(s, param), line);
}

}

@lexer::namespace {
    Parser
}

@lexer::members
{

}

@parser::namespace {
    Parser
}

@header 
{
using System.Text;
using System.Windows.Forms;
using AvalonEditor;
}

//===========================================================
// TES3 Grammar
//===========================================================

// Variables
variables : tes3short | tes3long | tes3float;

// Short
tes3short: 'short' id=IDENTIFIER
{
	if ($id != null)
		if (!Variables.ContainsKey($id.Text))
			Variables.Add($id.Text, "short");
		else
			FormatReport("A variable of type '{0}' already exist with the name '{1}'!", $id.Line, Variables[$id.Text], $id.Text);
};

// Long
tes3long: 'long' id=IDENTIFIER
{
	if ($id != null)
		if (!Variables.ContainsKey($id.Text))
			Variables.Add($id.Text, "long");
		else
			FormatReport("A variable of type '{0}' already exist with the name '{1}'!", $id.Line, Variables[$id.Text], $id.Text);
};

// Float
tes3float: 'float' id=IDENTIFIER
{
	if ($id != null)
		if (!Variables.ContainsKey($id.Text))
			Variables.Add($id.Text, "float");
		else
			FormatReport("A variable of type '{0}' already exist with the name '{1}'!", $id.Line, Variables[$id.Text], $id.Text);
};

// Begin
begin : BEGIN id=IDENTIFIER
{
	if ($id != null)
	{
		if ($id.Text.StartsWith("_"))
			QuickReport("Script names cannot begin with an underscore!", $id.Line);
			
		ScriptName = $id.Text;
	}
};

// End
end : END id=IDENTIFIER?
{
	if ($id != null)
		if ($id.Text != ScriptName)
			FormatReport("Script name '{0}' at 'End' doesn't match script name '{1}' at 'Begin'!", $id.Line, $id.Text, ScriptName);
};

// Expressions
expression : compare;

// Compare
compare : 
IDENTIFIER |
IDENTIFIER OPERATOR IDENTIFIER |
IDENTIFIER OPERATOR VALUE |
VALUE OPERATOR IDENTIFIER |
VALUE OPERATOR VALUE
IDENTIFIER FIX IDENTIFIER |
IDENTIFIER FIX IDENTIFIER OPERATOR VALUE |
IDENTIFIER FIX IDENTIFIER OPERATOR IDENTIFIER;

// Statements
statement: 
	if_statement |
	while_statement |
	return_statement |
	set_statement |
	functions;

// If statement
if_statement : 
	'if' '(' expression ')'
		statement* 
	('elseif') => elseif_statement*
		statement* 
	('else') => else_statement?
		statement* 
	('endif') => endif_statement;

// Else IF Statement
elseif_statement : 'elseif' '(' expression ')';

// Else statement
else_statement : 'else';

// EndIf statement
endif_statement : 'endif';

// While statement
while_statement : 
	'while' '(' expression ')'
		(statement)*
	('endwhile') => endwhile_statement;

// EndWhile Statement
endwhile_statement: 'endwhile';

// Return statement
return_statement: 'return';

// Operation
operation: set_operation;

// Set operation
set_operation:
	VALUE |
	'(' VALUE ')' |
	'(' IDENTIFIER OPERATOR VALUE ')' |
	'(' IDENTIFIER FIX IDENTIFIER ')';

// Set statement
set_statement : SET IDENTIFIER TO operation;

//===========
// Functions
//===========

// Functions
functions: t3_message;

// MessageBox
t3_message : 'MessageBox' '"' (IDENTIFIER|VALUE)* '"';


//====================
// Program: Main Loop
//====================
program: 
begin
(variables)*
(statement)* 
end
EOF;

//====================
// Lexer Rules
//====================

fragment PLUS : '+';
fragment MINUS : '-';
fragment DIV : '/';
fragment MUL : '*';

fragment GT : '>';
fragment EQ : '==';
fragment LT : '<';
fragment LTE : '<=';
fragment GTE : '>=';
fragment NE : '!=';

fragment DIGIT : ('0'..'9');
fragment LOWER : ('a'..'z');
fragment UPPER : ('A'..'Z');

fragment INTEGER : DIGIT*;
fragment FLOAT : DIGIT* '.' DIGIT*;

IDENTIFIER : ('_' | LOWER | UPPER)('_' | LOWER | UPPER | DIGIT)*;
    
VALUE: (MINUS?)(INTEGER|FLOAT);
OPERATOR: (PLUS|MINUS|DIV|MUL|GT|EQ|LT|LTE|GTE|NE);

// New Line
NEWLINE
    : '\r'? '\n' {$channel=Antlr.Runtime.TokenChannels.Hidden;}
    ;

// White Space
SPACE
    : (' ')+ {$channel=Antlr.Runtime.TokenChannels.Hidden;}
    ;

// Tab
TAB
	: ('\t')+ {$channel=Antlr.Runtime.TokenChannels.Hidden;}
	;
	
// Comment
COMMENT
	: ';' ( ~('\n'|'\r') )*( '\n'|'\r'('\n')? )? {$channel=Antlr.Runtime.TokenChannels.Hidden;}
	;
</eof></eof>
Posted
Updated 18-Aug-11 10:40am
v4

1 solution

Looks to me like your definition of an if_statement requires that the if include one elseif and one else before the endif.

Assuming * means 0 or more and ? means 0 or one, you'll want to define it something like:

if_statement :
    'if' '(' expression ')'
          statement*
     elseif_statement*
     else_statement?
     'endif'

elseif_statement :
     'elseif' '(' expression ')'
          statement*
  
else_statement :
     'else' '(' expression ')'
          statement*
 
Share this answer
 
Comments
[no name] 18-Aug-11 16:51pm    
Thanks, that seemed to do the trick. :)

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900