public void EmptyParsing() { ExprAnalyser a = new ExprAnalyser(); JSTokenizer p = new JSTokenizer(); { p.Reset(""); Assert.That(p.IsEndOfInput); Expr e = a.Analyse(p); Assert.That(e is SyntaxErrorExpr); } { p.Reset(" \r\n \n \r \n \t "); Assert.That(p.IsEndOfInput); Expr e = a.Analyse(p); Assert.That(e is SyntaxErrorExpr); } }
public void BadNumbers() { ExprAnalyser a = new ExprAnalyser(); JSTokenizer p = new JSTokenizer(); { p.Reset( "45DD" ); Assert.That( p.IsErrorOrEndOfInput, Is.True ); Assert.That( p.ErrorCode, Is.EqualTo( JSTokenizerError.ErrorNumberIdentifierStartsImmediately ) ); } { p.Reset( "45.member" ); Assert.That( p.IsErrorOrEndOfInput, Is.True ); Assert.That( p.ErrorCode, Is.EqualTo( JSTokenizerError.ErrorNumberIdentifierStartsImmediately ) ); } { p.Reset( ".45.member" ); Assert.That( p.IsErrorOrEndOfInput, Is.True ); Assert.That( p.ErrorCode, Is.EqualTo( JSTokenizerError.ErrorNumberIdentifierStartsImmediately ) ); } { p.Reset( "45.01member" ); Assert.That( p.IsErrorOrEndOfInput, Is.True ); Assert.That( p.ErrorCode, Is.EqualTo( JSTokenizerError.ErrorNumberIdentifierStartsImmediately ) ); } { p.Reset( ".45.member" ); Assert.That( p.IsErrorOrEndOfInput, Is.True ); Assert.That( p.ErrorCode, Is.EqualTo( JSTokenizerError.ErrorNumberIdentifierStartsImmediately ) ); } { p.Reset( ".45.01member" ); Assert.That( p.IsErrorOrEndOfInput, Is.True ); Assert.That( p.ErrorCode, Is.EqualTo( JSTokenizerError.ErrorNumberIdentifierStartsImmediately ) ); } { p.Reset( "45.01e23member" ); Assert.That( p.IsErrorOrEndOfInput, Is.True ); Assert.That( p.ErrorCode, Is.EqualTo( JSTokenizerError.ErrorNumberIdentifierStartsImmediately ) ); } }
public void BadNumbers() { ExprAnalyser a = new ExprAnalyser(); JSTokenizer p = new JSTokenizer(); { p.Reset("45DD"); Assert.That(p.IsErrorOrEndOfInput, Is.True); Assert.That(p.ErrorCode, Is.EqualTo(JSTokenizerError.ErrorNumberIdentifierStartsImmediately)); } { p.Reset("45.member"); Assert.That(p.IsErrorOrEndOfInput, Is.True); Assert.That(p.ErrorCode, Is.EqualTo(JSTokenizerError.ErrorNumberIdentifierStartsImmediately)); } { p.Reset(".45.member"); Assert.That(p.IsErrorOrEndOfInput, Is.True); Assert.That(p.ErrorCode, Is.EqualTo(JSTokenizerError.ErrorNumberIdentifierStartsImmediately)); } { p.Reset("45.01member"); Assert.That(p.IsErrorOrEndOfInput, Is.True); Assert.That(p.ErrorCode, Is.EqualTo(JSTokenizerError.ErrorNumberIdentifierStartsImmediately)); } { p.Reset(".45.member"); Assert.That(p.IsErrorOrEndOfInput, Is.True); Assert.That(p.ErrorCode, Is.EqualTo(JSTokenizerError.ErrorNumberIdentifierStartsImmediately)); } { p.Reset(".45.01member"); Assert.That(p.IsErrorOrEndOfInput, Is.True); Assert.That(p.ErrorCode, Is.EqualTo(JSTokenizerError.ErrorNumberIdentifierStartsImmediately)); } { p.Reset("45.01e23member"); Assert.That(p.IsErrorOrEndOfInput, Is.True); Assert.That(p.ErrorCode, Is.EqualTo(JSTokenizerError.ErrorNumberIdentifierStartsImmediately)); } }
public void ArraySupport() { ExprAnalyser a = new ExprAnalyser(); JSTokenizer p = new JSTokenizer(); { p.Reset( "a[9]" ); Assert.That( p.IsErrorOrEndOfInput, Is.False ); Expr e = a.Analyse( p ); Assert.That( e is AccessorIndexerExpr ); AccessorIndexerExpr ac = e as AccessorIndexerExpr; IsConstant( ac.Index, 9 ); } { p.Reset( "array['Hello World!']" ); Assert.That( p.IsErrorOrEndOfInput, Is.False ); Expr e = a.Analyse( p ); Assert.That( e is AccessorIndexerExpr ); AccessorIndexerExpr ac = e as AccessorIndexerExpr; IsConstant( ac.Index, "Hello World!" ); } }
public void ArraySupport() { ExprAnalyser a = new ExprAnalyser(); JSTokenizer p = new JSTokenizer(); { p.Reset("a[9]"); Assert.That(p.IsErrorOrEndOfInput, Is.False); Expr e = a.Analyse(p); Assert.That(e is AccessorIndexerExpr); AccessorIndexerExpr ac = e as AccessorIndexerExpr; IsConstant(ac.Index, 9); } { p.Reset("array['Hello World!']"); Assert.That(p.IsErrorOrEndOfInput, Is.False); Expr e = a.Analyse(p); Assert.That(e is AccessorIndexerExpr); AccessorIndexerExpr ac = e as AccessorIndexerExpr; IsConstant(ac.Index, "Hello World!"); } }
public void RoundtripParsing() { JSTokenizer p = new JSTokenizer(); Assert.That(JSTokenizer.Explain(JSTokenizerToken.Integer), Is.EqualTo("42")); string s = " function ( x , z ) ++ -- { if ( x != z || x && z % x - x >>> z >> z << x | z & x ^ z -- = x ++ ) return x + ( z * 42 ) / 42 ; } void == typeof += new -= delete >>= instanceof >>>= x % z %= x === z !== x ! z ~ = x |= z &= x <<= z ^= x /= z *= x %="; p.Reset(s); string recompose = ""; while (!p.IsEndOfInput) { recompose += " " + JSTokenizer.Explain(p.CurrentToken); p.Forward(); } s = s.Replace("if", "identifier") .Replace("function", "identifier") .Replace("x", "identifier") .Replace("z", "identifier") .Replace("return", "identifier"); Assert.That(recompose, Is.EqualTo(s)); }
/// <summary> /// Analyses the tokens to produce an AST of Expr. /// When <paramref name="allowGlobalUse"/> is true and <see cref="Config.GlobalScope"/> is true, the top-level declarations /// go into the global scope. /// </summary> /// <param name="p">Tokeinzer to analyse.</param> /// <param name="allowGlobalUse">False to scope declarations to this analysis.</param> /// <returns>The AST (that may be a <see cref="SyntaxErrorExpr"/> or contains such errors).</returns> public Expr Analyse( JSTokenizer p, bool allowGlobalUse = true ) { _parser = p; if( !(allowGlobalUse && _scope.GlobalScope) ) _scope.OpenScope(); return HandleBlock( Expression( 0 ) ); }
public void SimpleExpression() { ExprAnalyser a = new ExprAnalyser(); JSTokenizer p = new JSTokenizer(); { p.Reset( "value" ); Assert.That( p.IsErrorOrEndOfInput, Is.False ); Expr e = a.Analyse( p ); Assert.That( e is AccessorMemberExpr ); AccessorMemberExpr ac = e as AccessorMemberExpr; Assert.That( ac.IsUnbound == true ); } { p.Reset( "!" ); Expr e = a.Analyse( p ); Assert.That( e is UnaryExpr ); UnaryExpr u = e as UnaryExpr; Assert.That( u.TokenType == JSTokenizerToken.Not ); Assert.That( u.Expression is SyntaxErrorExpr ); Assert.That( SyntaxErrorCollector.Collect( e, null ).Count == 1 ); } { p.Reset( "!value" ); Expr e = a.Analyse( p ); Assert.That( e is UnaryExpr ); UnaryExpr u = e as UnaryExpr; Assert.That( u.TokenType == JSTokenizerToken.Not ); Assert.That( u.Expression is AccessorExpr ); Assert.That( SyntaxErrorCollector.Collect( e, Util.ActionVoid ).Count == 0 ); } { p.Reset( " 0.12e43 && ~b " ); Expr e = a.Analyse( p ); Assert.That( e is BinaryExpr ); BinaryExpr and = e as BinaryExpr; Assert.That( and.BinaryOperatorToken == JSTokenizerToken.And ); IsConstant( and.Left, 0.12e43 ); Assert.That( and.Right is UnaryExpr ); UnaryExpr u = and.Right as UnaryExpr; Assert.That( u.TokenType == JSTokenizerToken.BitwiseNot ); Assert.That( u.Expression is AccessorExpr ); Assert.That( SyntaxErrorCollector.Collect( e, Util.ActionVoid ).Count == 0 ); } { p.Reset( @"!a||~""x""" ); Expr e = a.Analyse( p ); Assert.That( e is BinaryExpr ); BinaryExpr or = e as BinaryExpr; Assert.That( or.BinaryOperatorToken == JSTokenizerToken.Or ); Assert.That( or.Left is UnaryExpr ); Assert.That( or.Right is UnaryExpr ); UnaryExpr u = or.Right as UnaryExpr; Assert.That( u.TokenType == JSTokenizerToken.BitwiseNot ); IsConstant( u.Expression, "x" ); Assert.That( SyntaxErrorCollector.Collect( e, Util.ActionVoid ).Count == 0 ); } { p.Reset( "(3)" ); Expr e = a.Analyse( p ); IsConstant( e, 3 ); } { p.Reset( "(3+typeof 'x')" ); Expr e = a.Analyse( p ); Assert.That( e is BinaryExpr ); BinaryExpr b = e as BinaryExpr; IsConstant( b.Left, 3 ); Assert.That( b.Right is UnaryExpr ); UnaryExpr u = b.Right as UnaryExpr; Assert.That( u.TokenType == JSTokenizerToken.TypeOf ); IsConstant( u.Expression, "x" ); Assert.That( SyntaxErrorCollector.Collect( e, Util.ActionVoid ).Count == 0 ); } { p.Reset( "1 ? 2 : 3" ); Expr e = a.Analyse( p ); Assert.That( e is IfExpr ); IfExpr i = e as IfExpr; Assert.That( i.IsTernaryOperator == true ); IsConstant( i.Condition, 1 ); IsConstant( i.WhenTrue, 2 ); IsConstant( i.WhenFalse, 3 ); } }
public void RoundtripParsing() { JSTokenizer p = new JSTokenizer(); Assert.That( JSTokenizer.Explain( JSTokenizerToken.Integer ), Is.EqualTo( "42" ) ); string s = " function ( x , z ) ++ -- { if ( x != z || x && z % x - x >>> z >> z << x | z & x ^ z -- = x ++ ) return x + ( z * 42 ) / 42 ; } void == typeof += new -= delete >>= instanceof >>>= x % z %= x === z !== x ! z ~ = x |= z &= x <<= z ^= x /= z *= x %="; p.Reset( s ); string recompose = ""; while( !p.IsEndOfInput ) { recompose += " " + JSTokenizer.Explain( p.CurrentToken ); p.Forward(); } s = s.Replace( "if", "identifier" ) .Replace( "function", "identifier" ) .Replace( "x", "identifier" ) .Replace( "z", "identifier" ) .Replace( "return", "identifier" ); Assert.That( recompose, Is.EqualTo( s ) ); }
public void EmptyParsing() { ExprAnalyser a = new ExprAnalyser(); JSTokenizer p = new JSTokenizer(); { p.Reset( "" ); Assert.That( p.IsEndOfInput ); Expr e = a.Analyse( p ); Assert.That( e is SyntaxErrorExpr ); } { p.Reset( " \r\n \n \r \n \t " ); Assert.That( p.IsEndOfInput ); Expr e = a.Analyse( p ); Assert.That( e is SyntaxErrorExpr ); } }
public string Format(string javascript) { this._builder = new StringBuilder(javascript.Length); this._indents = new Indents(); this._parenCount = 0; this._bracketCount = 0; this._lineFlags = JSLineFlags.None; this._nextLineFlags = JSLineFlags.None; JSTokenizer jSTokenizer = new JSTokenizer(javascript); bool flag = false; bool flag2 = true; while (jSTokenizer.GetToken()) { JSToken token = jSTokenizer.Token; if (this._builder.Length > 0) { flag2 = flag; if (flag) { this.NewLine(); flag = false; } } switch (token.Type) { case JSTokenTypes.OpenBrace: { if (!flag2) { if (this.OpenBraceOnNewLine && this._builder.Length > 0) { this.NewLine(); } else if (token.PreviousType != JSTokenTypes.CRLF && token.PreviousType != JSTokenTypes.OpenParen && token.PreviousType != JSTokenTypes.OpenBracket) { this._builder.Append(' '); } } this._builder.Append(token.Value); JSToken jSToken = jSTokenizer.PeekToken(); if (jSToken.Type != JSTokenTypes.CloseBrace) { JSIndentFlags jSIndentFlags = JSIndentFlags.None; if (JSFormatter.HasFlags((int)this._lineFlags, 2)) { jSIndentFlags |= JSIndentFlags.DoBlock; } else if (JSFormatter.HasFlags((int)this._lineFlags, 8)) { jSIndentFlags |= JSIndentFlags.CaseBlock; } this._indents.Indent(jSIndentFlags); flag = true; continue; } jSTokenizer.GetToken(); this._builder.Append(jSTokenizer.Token.Value); jSToken = jSTokenizer.PeekToken(); if (jSToken.Type != JSTokenTypes.SemiColon && jSToken.Type != JSTokenTypes.Comma) { while (JSFormatter.HasFlags((int)this._indents.Current, 1)) { this._indents.Unindent(); } flag = true; continue; } if (jSToken.Type == JSTokenTypes.Comma) { jSTokenizer.GetToken(); this._builder.Append(jSTokenizer.Token.Value); continue; } continue; } case JSTokenTypes.CloseBrace: { if (JSFormatter.HasFlags((int)this._indents.Current, 4)) { this._indents.Unindent(); if (flag2) { Indents.StripTrailingIndent(this._builder); } } while (JSFormatter.HasFlags((int)this._indents.Current, 1)) { this._indents.Unindent(); } this._indents.Unindent(); if (flag2) { Indents.StripTrailingIndent(this._builder); } else { this.NewLine(); } this._builder.Append(token.Value); JSToken jSToken = jSTokenizer.PeekToken(); if (jSToken.Value != "catch" && jSToken.Value != "finally" && jSToken.Value != ":") { while (JSFormatter.HasFlags((int)this._indents.Current, 1)) { this._indents.Unindent(); } } if (JSFormatter.HasFlags((int)this._indents.LastIndent, 2)) { this._lineFlags |= JSLineFlags.EndDoBlock; } if (jSToken.Type != JSTokenTypes.CRLF && jSToken.Type != JSTokenTypes.SemiColon && jSToken.Type != JSTokenTypes.CloseParen && jSToken.Type != JSTokenTypes.CloseBracket && jSToken.Type != JSTokenTypes.Comma && jSToken.Type != JSTokenTypes.OpenParen && jSToken.Type != JSTokenTypes.Colon && !JSFormatter.HasFlags((int)this._lineFlags, 4)) { flag = true; continue; } continue; } case JSTokenTypes.OpenParen: if (!flag2 && token.PreviousType != JSTokenTypes.CRLF && token.PreviousType != JSTokenTypes.OpenParen && token.PreviousType != JSTokenTypes.UnaryPrefix && token.PreviousType != JSTokenTypes.CloseBracket && token.PreviousType != JSTokenTypes.CloseParen && token.PreviousType != JSTokenTypes.CloseBrace && (token.PreviousType != JSTokenTypes.Symbol || (JSFormatter.HasFlags((int)this._lineFlags, 1) && this._parenCount == 0))) { this._builder.Append(' '); } this._builder.Append(token.Value); this._parenCount++; continue; case JSTokenTypes.CloseParen: { this._builder.Append(token.Value); this._parenCount--; if (this._parenCount < 0) { this._parenCount = 0; } if (this._parenCount != 0 || !JSFormatter.HasFlags((int)this._lineFlags, 1)) { continue; } JSToken jSToken = jSTokenizer.PeekToken(); if (jSToken.Type != JSTokenTypes.OpenBrace) { this._indents.Indent(JSIndentFlags.NoBraces); flag = true; continue; } continue; } case JSTokenTypes.OpenBracket: { if (!flag2 && token.PreviousType != JSTokenTypes.CRLF && token.PreviousType != JSTokenTypes.Symbol && token.PreviousType != JSTokenTypes.OpenParen && token.PreviousType != JSTokenTypes.CloseParen && token.PreviousType != JSTokenTypes.CloseBracket) { this._builder.Append(' '); } JSToken jSToken = jSTokenizer.PeekToken(); if (JSFormatter.HasFlags((int)this._lineFlags, 16) && jSToken.Type != JSTokenTypes.CloseBracket && jSToken.Type == JSTokenTypes.OpenBrace && this._parenCount == 0) { if (this.OpenBraceOnNewLine) { this.NewLine(); } this._indents.Indent(JSIndentFlags.BracketBlock); flag = true; } this._builder.Append(token.Value); this._bracketCount++; continue; } case JSTokenTypes.CloseBracket: this._bracketCount = Math.Max(this._bracketCount - 1, 0); if (!JSFormatter.HasFlags((int)this._indents.Current, 8)) { this._builder.Append(token.Value); continue; } this._indents.Unindent(); if (flag2) { Indents.StripTrailingIndent(this._builder); this._builder.Append(token.Value); continue; } this.NewLine(); this._builder.Append(token.Value); continue; case JSTokenTypes.Symbol: { bool flag3 = Array.IndexOf <string>(JSFormatter._blockKeywords, token.Value) > -1; if (token.Value == "else" && jSTokenizer.PeekToken().Value != "if") { flag3 = true; } if (JSFormatter.HasFlags((int)this._indents.Current, 4) && (token.Value == "case" || token.Value == "default")) { Indents.StripTrailingIndent(this._builder); this._indents.Unindent(); } if (this._parenCount != 0 || !flag3) { if (!flag2 && token.PreviousType != JSTokenTypes.CRLF && token.PreviousType != JSTokenTypes.OpenParen && token.PreviousType != JSTokenTypes.OpenBracket && token.PreviousType != JSTokenTypes.UnaryPrefix && token.PreviousType != JSTokenTypes.Dot) { this._builder.Append(' '); } if (token.Value == "case" || token.Value == "default") { this._lineFlags |= JSLineFlags.CaseKeyword; } this._builder.Append(token.Value); continue; } if (!flag2) { this._builder.Append(' '); } this._builder.Append(token.Value); if (JSFormatter.HasFlags((int)this._lineFlags, 4) && !(token.Value != "while")) { continue; } if (token.Value == "do") { this._lineFlags |= JSLineFlags.DoKeyword; } JSToken jSToken = jSTokenizer.PeekToken(); if (jSToken.Type == JSTokenTypes.OpenBrace || jSToken.Type == JSTokenTypes.OpenParen) { this._lineFlags |= JSLineFlags.BlockKeyword; continue; } JSIndentFlags jSIndentFlags2 = JSIndentFlags.NoBraces; if (JSFormatter.HasFlags((int)this._lineFlags, 2)) { jSIndentFlags2 |= JSIndentFlags.DoBlock; } this._indents.Indent(jSIndentFlags2); flag = true; continue; } case JSTokenTypes.String: case JSTokenTypes.Number: case JSTokenTypes.RegEx: if (!flag2 && token.PreviousType != JSTokenTypes.CRLF && token.PreviousType != JSTokenTypes.OpenParen && token.PreviousType != JSTokenTypes.OpenBracket && token.PreviousType != JSTokenTypes.UnaryPrefix) { this._builder.Append(' '); } this._builder.Append(token.Value); continue; case JSTokenTypes.SemiColon: this._builder.Append(token.Value); if (this._parenCount == 0) { while (JSFormatter.HasFlags((int)this._indents.Current, 1)) { this._indents.Unindent(); } if (JSFormatter.HasFlags((int)this._indents.LastIndent, 2)) { this._nextLineFlags |= JSLineFlags.EndDoBlock; } JSToken jSToken = jSTokenizer.PeekToken(); if (jSToken.Type == JSTokenTypes.FullLineComment || jSToken.Type == JSTokenTypes.InlineComment) { bool flag4; if (jSToken.Type == JSTokenTypes.FullLineComment) { flag4 = this.NewLineBeforeLineComment; } else { flag4 = this.NewLineBeforeInlineComment; } jSTokenizer.GetToken(); if (flag4) { this.NewLine(); } else { this._builder.Append(' '); } this._builder.Append(jSTokenizer.Token.Value); } flag = true; continue; } continue; case JSTokenTypes.Comma: this._builder.Append(token.Value); if (token.PreviousType == JSTokenTypes.CloseBrace || (JSFormatter.HasFlags((int)this._lineFlags, 16) && this._parenCount == 0 && this._bracketCount == 0 && this._indents.Count > 0)) { flag = true; continue; } continue; case JSTokenTypes.Colon: if (JSFormatter.HasFlags((int)this._lineFlags, 8)) { this._builder.Append(token.Value); this._indents.Indent(JSIndentFlags.CaseBlock); flag = true; continue; } if (!flag2 && (JSFormatter.HasFlags((int)this._lineFlags, 32) || token.PreviousType == JSTokenTypes.CloseBrace)) { this._builder.Append(' '); } this._builder.Append(token.Value); if (!JSFormatter.HasFlags((int)this._lineFlags, 32)) { this._lineFlags |= JSLineFlags.JsonColon; continue; } continue; case JSTokenTypes.QuestionMark: this._lineFlags |= JSLineFlags.QuestionMark; if (!flag2) { this._builder.Append(' '); } this._builder.Append(token.Value); continue; case JSTokenTypes.BinaryOperator: case JSTokenTypes.UnaryPrefix: if (!flag2 && token.PreviousType != JSTokenTypes.OpenParen && token.PreviousType != JSTokenTypes.OpenBracket && token.PreviousType != JSTokenTypes.UnaryPrefix) { this._builder.Append(' '); } this._builder.Append(token.Value); continue; case JSTokenTypes.FullLineComment: if (!flag2) { if (this.NewLineBeforeLineComment) { this.NewLine(); } else { this._builder.Append(' '); } } this._builder.Append(token.Value); flag = true; continue; case JSTokenTypes.InlineComment: if (!flag2) { if (this.NewLineBeforeInlineComment) { this.NewLine(); } else { this._builder.Append(' '); } } this._builder.Append(token.Value); if (this.NewLineAfterInlineComment) { flag = true; continue; } continue; case JSTokenTypes.CRLF: if (!flag2) { flag = true; continue; } continue; } this._builder.Append(token.Value); } this._builder.AppendLine(); return(this._builder.ToString()); }
public void SimpleExpression() { ExprAnalyser a = new ExprAnalyser(); JSTokenizer p = new JSTokenizer(); { p.Reset("value"); Assert.That(p.IsErrorOrEndOfInput, Is.False); Expr e = a.Analyse(p); Assert.That(e is AccessorMemberExpr); AccessorMemberExpr ac = e as AccessorMemberExpr; Assert.That(ac.IsUnbound == true); } { p.Reset("!"); Expr e = a.Analyse(p); Assert.That(e is UnaryExpr); UnaryExpr u = e as UnaryExpr; Assert.That(u.TokenType == JSTokenizerToken.Not); Assert.That(u.Expression is SyntaxErrorExpr); Assert.That(SyntaxErrorCollector.Collect(e, null).Count == 1); } { p.Reset("!value"); Expr e = a.Analyse(p); Assert.That(e is UnaryExpr); UnaryExpr u = e as UnaryExpr; Assert.That(u.TokenType == JSTokenizerToken.Not); Assert.That(u.Expression is AccessorExpr); Assert.That(SyntaxErrorCollector.Collect(e, Util.ActionVoid).Count == 0); } { p.Reset(" 0.12e43 && ~b "); Expr e = a.Analyse(p); Assert.That(e is BinaryExpr); BinaryExpr and = e as BinaryExpr; Assert.That(and.BinaryOperatorToken == JSTokenizerToken.And); IsConstant(and.Left, 0.12e43); Assert.That(and.Right is UnaryExpr); UnaryExpr u = and.Right as UnaryExpr; Assert.That(u.TokenType == JSTokenizerToken.BitwiseNot); Assert.That(u.Expression is AccessorExpr); Assert.That(SyntaxErrorCollector.Collect(e, Util.ActionVoid).Count == 0); } { p.Reset(@"!a||~""x"""); Expr e = a.Analyse(p); Assert.That(e is BinaryExpr); BinaryExpr or = e as BinaryExpr; Assert.That(or.BinaryOperatorToken == JSTokenizerToken.Or); Assert.That(or.Left is UnaryExpr); Assert.That(or.Right is UnaryExpr); UnaryExpr u = or.Right as UnaryExpr; Assert.That(u.TokenType == JSTokenizerToken.BitwiseNot); IsConstant(u.Expression, "x"); Assert.That(SyntaxErrorCollector.Collect(e, Util.ActionVoid).Count == 0); } { p.Reset("(3)"); Expr e = a.Analyse(p); IsConstant(e, 3); } { p.Reset("(3+typeof 'x')"); Expr e = a.Analyse(p); Assert.That(e is BinaryExpr); BinaryExpr b = e as BinaryExpr; IsConstant(b.Left, 3); Assert.That(b.Right is UnaryExpr); UnaryExpr u = b.Right as UnaryExpr; Assert.That(u.TokenType == JSTokenizerToken.TypeOf); IsConstant(u.Expression, "x"); Assert.That(SyntaxErrorCollector.Collect(e, Util.ActionVoid).Count == 0); } { p.Reset("1 ? 2 : 3"); Expr e = a.Analyse(p); Assert.That(e is IfExpr); IfExpr i = e as IfExpr; Assert.That(i.IsTernaryOperator == true); IsConstant(i.Condition, 1); IsConstant(i.WhenTrue, 2); IsConstant(i.WhenFalse, 3); } }