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 Errors { get; private set; } public override void DisplayRecognitionError(String[] tokenNames, RecognitionException e) { base.DisplayRecognitionError(tokenNames, e); if(Errors == null) { Errors = new List(); } 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 value] @init { List expressions = new List(); } : first=logicalExpression {expressions.Add($first.value);} ( ',' follow=logicalExpression {expressions.Add($follow.value);})* { $value = expressions; } ; arguments returns [List value] @init { $value = new List(); } : '(' ( 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;} ;