304 lines
9.0 KiB
Plaintext
304 lines
9.0 KiB
Plaintext
|
grammar NCalc;
|
||
|
|
||
|
options
|
||
|
{
|
||
|
output=AST;
|
||
|
ASTLabelType=CommonTree;
|
||
|
language=CSharp;
|
||
|
}
|
||
|
|
||
|
@header {
|
||
|
using System.Text;
|
||
|
using System.Globalization;
|
||
|
using System.Collections.Generic;
|
||
|
using NCalc.Domain;
|
||
|
}
|
||
|
|
||
|
@members {
|
||
|
private const char BS = '\\';
|
||
|
private static NumberFormatInfo numberFormatInfo = new NumberFormatInfo();
|
||
|
|
||
|
private string extractString(string text) {
|
||
|
|
||
|
StringBuilder sb = new StringBuilder(text);
|
||
|
int startIndex = 1; // Skip initial quote
|
||
|
int slashIndex = -1;
|
||
|
|
||
|
while ((slashIndex = sb.ToString().IndexOf(BS, startIndex)) != -1)
|
||
|
{
|
||
|
char escapeType = sb[slashIndex + 1];
|
||
|
switch (escapeType)
|
||
|
{
|
||
|
case 'u':
|
||
|
string hcode = String.Concat(sb[slashIndex+4], sb[slashIndex+5]);
|
||
|
string lcode = String.Concat(sb[slashIndex+2], sb[slashIndex+3]);
|
||
|
char unicodeChar = Encoding.Unicode.GetChars(new byte[] { System.Convert.ToByte(hcode, 16), System.Convert.ToByte(lcode, 16)} )[0];
|
||
|
sb.Remove(slashIndex, 6).Insert(slashIndex, unicodeChar);
|
||
|
break;
|
||
|
case 'n': sb.Remove(slashIndex, 2).Insert(slashIndex, '\n'); break;
|
||
|
case 'r': sb.Remove(slashIndex, 2).Insert(slashIndex, '\r'); break;
|
||
|
case 't': sb.Remove(slashIndex, 2).Insert(slashIndex, '\t'); break;
|
||
|
case '\'': sb.Remove(slashIndex, 2).Insert(slashIndex, '\''); break;
|
||
|
case '\\': sb.Remove(slashIndex, 2).Insert(slashIndex, '\\'); break;
|
||
|
default: throw new RecognitionException("Unvalid escape sequence: \\" + escapeType);
|
||
|
}
|
||
|
|
||
|
startIndex = slashIndex + 1;
|
||
|
|
||
|
}
|
||
|
|
||
|
sb.Remove(0, 1);
|
||
|
sb.Remove(sb.Length - 1, 1);
|
||
|
|
||
|
return sb.ToString();
|
||
|
}
|
||
|
|
||
|
public List<string> Errors { get; private set; }
|
||
|
|
||
|
public override void DisplayRecognitionError(String[] tokenNames, RecognitionException e) {
|
||
|
|
||
|
base.DisplayRecognitionError(tokenNames, e);
|
||
|
|
||
|
if(Errors == null)
|
||
|
{
|
||
|
Errors = new List<string>();
|
||
|
}
|
||
|
|
||
|
String hdr = GetErrorHeader(e);
|
||
|
String msg = GetErrorMessage(e, tokenNames);
|
||
|
Errors.Add(msg + " at " + hdr);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
@init {
|
||
|
numberFormatInfo.NumberDecimalSeparator = ".";
|
||
|
}
|
||
|
|
||
|
ncalcExpression returns [LogicalExpression value]
|
||
|
: logicalExpression EOF! {$value = $logicalExpression.value; }
|
||
|
;
|
||
|
|
||
|
logicalExpression returns [LogicalExpression value]
|
||
|
: left=conditionalExpression { $value = $left.value; } ( '?' middle=conditionalExpression ':' right=conditionalExpression { $value = new TernaryExpression($left.value, $middle.value, $right.value); })?
|
||
|
;
|
||
|
|
||
|
conditionalExpression returns [LogicalExpression value]
|
||
|
@init {
|
||
|
BinaryExpressionType type = BinaryExpressionType.Unknown;
|
||
|
}
|
||
|
: left=booleanAndExpression { $value = $left.value; } (
|
||
|
('||' | 'or') { type = BinaryExpressionType.Or; }
|
||
|
right=conditionalExpression { $value = new BinaryExpression(type, $value, $right.value); }
|
||
|
)*
|
||
|
;
|
||
|
|
||
|
booleanAndExpression returns [LogicalExpression value]
|
||
|
@init {
|
||
|
BinaryExpressionType type = BinaryExpressionType.Unknown;
|
||
|
}
|
||
|
: left=bitwiseOrExpression { $value = $left.value; } (
|
||
|
('&&' | 'and') { type = BinaryExpressionType.And; }
|
||
|
right=bitwiseOrExpression { $value = new BinaryExpression(type, $value, $right.value); }
|
||
|
)*
|
||
|
;
|
||
|
|
||
|
bitwiseOrExpression returns [LogicalExpression value]
|
||
|
@init {
|
||
|
BinaryExpressionType type = BinaryExpressionType.Unknown;
|
||
|
}
|
||
|
: left=bitwiseXOrExpression { $value = $left.value; } (
|
||
|
'|' { type = BinaryExpressionType.BitwiseOr; }
|
||
|
right=bitwiseOrExpression { $value = new BinaryExpression(type, $value, $right.value); }
|
||
|
)*
|
||
|
;
|
||
|
|
||
|
bitwiseXOrExpression returns [LogicalExpression value]
|
||
|
@init {
|
||
|
BinaryExpressionType type = BinaryExpressionType.Unknown;
|
||
|
}
|
||
|
: left=bitwiseAndExpression { $value = $left.value; } (
|
||
|
'^' { type = BinaryExpressionType.BitwiseXOr; }
|
||
|
right=bitwiseAndExpression { $value = new BinaryExpression(type, $value, $right.value); }
|
||
|
)*
|
||
|
;
|
||
|
|
||
|
bitwiseAndExpression returns [LogicalExpression value]
|
||
|
@init {
|
||
|
BinaryExpressionType type = BinaryExpressionType.Unknown;
|
||
|
}
|
||
|
: left=equalityExpression { $value = $left.value; } (
|
||
|
'&' { type = BinaryExpressionType.BitwiseAnd; }
|
||
|
right=equalityExpression { $value = new BinaryExpression(type, $value, $right.value); }
|
||
|
)*
|
||
|
;
|
||
|
|
||
|
equalityExpression returns [LogicalExpression value]
|
||
|
@init {
|
||
|
BinaryExpressionType type = BinaryExpressionType.Unknown;
|
||
|
}
|
||
|
: left=relationalExpression { $value = $left.value; } (
|
||
|
( ('==' | '=' ) { type = BinaryExpressionType.Equal; }
|
||
|
| ('!=' | '<>' ) { type = BinaryExpressionType.NotEqual; } )
|
||
|
right=relationalExpression { $value = new BinaryExpression(type, $value, $right.value); }
|
||
|
)*
|
||
|
;
|
||
|
|
||
|
relationalExpression returns [LogicalExpression value]
|
||
|
@init {
|
||
|
BinaryExpressionType type = BinaryExpressionType.Unknown;
|
||
|
}
|
||
|
: left=shiftExpression { $value = $left.value; } (
|
||
|
( '<' { type = BinaryExpressionType.Lesser; }
|
||
|
| '<=' { type = BinaryExpressionType.LesserOrEqual; }
|
||
|
| '>' { type = BinaryExpressionType.Greater; }
|
||
|
| '>=' { type = BinaryExpressionType.GreaterOrEqual; } )
|
||
|
right=shiftExpression { $value = new BinaryExpression(type, $value, $right.value); }
|
||
|
)*
|
||
|
;
|
||
|
|
||
|
shiftExpression returns [LogicalExpression value]
|
||
|
@init {
|
||
|
BinaryExpressionType type = BinaryExpressionType.Unknown;
|
||
|
}
|
||
|
: left=additiveExpression { $value = $left.value; } (
|
||
|
( '<<' { type = BinaryExpressionType.LeftShift; }
|
||
|
| '>>' { type = BinaryExpressionType.RightShift; } )
|
||
|
right=additiveExpression { $value = new BinaryExpression(type, $value, $right.value); }
|
||
|
)*
|
||
|
;
|
||
|
|
||
|
additiveExpression returns [LogicalExpression value]
|
||
|
@init {
|
||
|
BinaryExpressionType type = BinaryExpressionType.Unknown;
|
||
|
}
|
||
|
: left=multiplicativeExpression { $value = $left.value; } (
|
||
|
( '+' { type = BinaryExpressionType.Plus; }
|
||
|
| '-' { type = BinaryExpressionType.Minus; } )
|
||
|
right=multiplicativeExpression { $value = new BinaryExpression(type, $value, $right.value); }
|
||
|
)*
|
||
|
;
|
||
|
|
||
|
multiplicativeExpression returns [LogicalExpression value]
|
||
|
@init {
|
||
|
BinaryExpressionType type = BinaryExpressionType.Unknown;
|
||
|
}
|
||
|
: left=unaryExpression { $value = $left.value); } (
|
||
|
( '*' { type = BinaryExpressionType.Times; }
|
||
|
| '/' { type = BinaryExpressionType.Div; }
|
||
|
| '%' { type = BinaryExpressionType.Modulo; } )
|
||
|
right=unaryExpression { $value = new BinaryExpression(type, $value, $right.value); }
|
||
|
)*
|
||
|
;
|
||
|
|
||
|
|
||
|
unaryExpression returns [LogicalExpression value]
|
||
|
: primaryExpression { $value = $primaryExpression.value; }
|
||
|
| ('!' | 'not') primaryExpression { $value = new UnaryExpression(UnaryExpressionType.Not, $primaryExpression.value); }
|
||
|
| ('~') primaryExpression { $value = new UnaryExpression(UnaryExpressionType.BitwiseNot, $primaryExpression.value); }
|
||
|
| '-' primaryExpression { $value = new UnaryExpression(UnaryExpressionType.Negate, $primaryExpression.value); }
|
||
|
;
|
||
|
|
||
|
primaryExpression returns [LogicalExpression value]
|
||
|
: '(' logicalExpression ')' { $value = $logicalExpression.value; }
|
||
|
| expr=value { $value = $expr.value; }
|
||
|
| identifier {$value = (LogicalExpression) $identifier.value; } (arguments {$value = new Function($identifier.value, ($arguments.value).ToArray()); })?
|
||
|
;
|
||
|
|
||
|
value returns [ValueExpression value]
|
||
|
: INTEGER { try { $value = new ValueExpression(int.Parse($INTEGER.text)); } catch(System.OverflowException) { $value = new ValueExpression(long.Parse($INTEGER.text)); } }
|
||
|
| FLOAT { $value = new ValueExpression(double.Parse($FLOAT.text, NumberStyles.Float, numberFormatInfo)); }
|
||
|
| STRING { $value = new ValueExpression(extractString($STRING.text)); }
|
||
|
| DATETIME { $value = new ValueExpression(DateTime.Parse($DATETIME.text.Substring(1, $DATETIME.text.Length-2))); }
|
||
|
| TRUE { $value = new ValueExpression(true); }
|
||
|
| FALSE { $value = new ValueExpression(false); }
|
||
|
;
|
||
|
|
||
|
identifier returns[Identifier value]
|
||
|
: ID { $value = new Identifier($ID.text); }
|
||
|
| NAME { $value = new Identifier($NAME.text.Substring(1, $NAME.text.Length-2)); }
|
||
|
;
|
||
|
|
||
|
expressionList returns [List<LogicalExpression> value]
|
||
|
@init {
|
||
|
List<LogicalExpression> expressions = new List<LogicalExpression>();
|
||
|
}
|
||
|
: first=logicalExpression {expressions.Add($first.value);} ( ',' follow=logicalExpression {expressions.Add($follow.value);})*
|
||
|
{ $value = expressions; }
|
||
|
;
|
||
|
|
||
|
arguments returns [List<LogicalExpression> value]
|
||
|
@init {
|
||
|
$value = new List<LogicalExpression>();
|
||
|
}
|
||
|
: '(' ( expressionList {$value = $expressionList.value;} )? ')'
|
||
|
;
|
||
|
|
||
|
TRUE
|
||
|
: 'true'
|
||
|
;
|
||
|
|
||
|
FALSE
|
||
|
: 'false'
|
||
|
;
|
||
|
|
||
|
ID
|
||
|
: LETTER (LETTER | DIGIT)*
|
||
|
;
|
||
|
|
||
|
INTEGER
|
||
|
: DIGIT+
|
||
|
;
|
||
|
|
||
|
FLOAT
|
||
|
: DIGIT* '.' DIGIT+ E?
|
||
|
| DIGIT+ E
|
||
|
;
|
||
|
|
||
|
STRING
|
||
|
: '\'' ( EscapeSequence | (options {greedy=false;} : ~('\u0000'..'\u001f' | '\\' | '\'' ) ) )* '\''
|
||
|
;
|
||
|
|
||
|
DATETIME
|
||
|
: '#' (options {greedy=false;} : ~('#')*) '#'
|
||
|
;
|
||
|
|
||
|
NAME : '[' (options {greedy=false;} : ~(']')*) ']'
|
||
|
;
|
||
|
|
||
|
E : ('E'|'e') ('+'|'-')? DIGIT+
|
||
|
;
|
||
|
|
||
|
fragment LETTER
|
||
|
: 'a'..'z'
|
||
|
| 'A'..'Z'
|
||
|
| '_'
|
||
|
;
|
||
|
|
||
|
fragment DIGIT
|
||
|
: '0'..'9'
|
||
|
;
|
||
|
|
||
|
fragment EscapeSequence
|
||
|
: '\\'
|
||
|
(
|
||
|
'n'
|
||
|
| 'r'
|
||
|
| 't'
|
||
|
| '\''
|
||
|
| '\\'
|
||
|
| UnicodeEscape
|
||
|
)
|
||
|
;
|
||
|
|
||
|
fragment HexDigit
|
||
|
: ('0'..'9'|'a'..'f'|'A'..'F') ;
|
||
|
|
||
|
|
||
|
fragment UnicodeEscape
|
||
|
: 'u' HexDigit HexDigit HexDigit HexDigit
|
||
|
;
|
||
|
|
||
|
/* Ignore white spaces */
|
||
|
WS : (' '|'\r'|'\t'|'\u000C'|'\n') {$channel=HIDDEN;}
|
||
|
;
|