// Parse member dots, array indexers, function calls ast.ExprNode ParseExpressionMember(ParseContext ctx) { var lhs = ParseExpressionTerminal(ctx); while (true) { // Member dot '.' if (t.SkipOptional(Token.period)) { t.Require(Token.identifier); lhs = new ast.ExprNodeIdentifier(t.GetBookmark(), t.identifier, lhs); t.Next(); continue; } // Array indexer '[]' if (t.SkipOptional(Token.openSquare)) { var temp = new ast.ExprNodeIndexer(t.GetBookmark(), lhs, ParseCompositeExpressionNode(0)); t.SkipRequired(Token.closeSquare); lhs = temp; continue; } // Function call '()' if ((ctx & ParseContext.NoFunctionCalls) == 0 && t.SkipOptional(Token.openRound)) { var temp = new ast.ExprNodeCall(t.GetBookmark(), lhs); if (t.token != Token.closeRound) { while (true) { temp.Arguments.Add(ParseSingleExpressionNode(0)); if (t.SkipOptional(Token.comma)) { continue; } else { break; } } } t.SkipRequired(Token.closeRound); lhs = temp; continue; } break; } return(lhs); }
public bool DoesMatch(ast.ExprNodeIdentifier target, string member) { if (m_specNonMember != null) { return(false); } // Check the member name matches if (m_specMember != null) { if (!m_specMember.DoesMatch(member)) { return(false); } } // Check the target name matches if (m_specTarget != null) { // Skip over `prototype` if present if (target.Name == "prototype" || target.Name == "__proto__") { if (target.Lhs == null) { return(false); } if (target.Lhs.GetType() != typeof(ast.ExprNodeIdentifier)) { return(false); } target = (ast.ExprNodeIdentifier)target.Lhs; } // The target must be the left most part if (target.Lhs != null) { return(false); } if (!m_specTarget.DoesMatch(target.Name)) { return(false); } } // Match! return(true); }
public void ProcessAccessibilitySpecs(ast.ExprNodeIdentifier target, string identifier, Bookmark bmk) { // Check accessibility specs for (int i = m_AccessibilitySpecs.Count - 1; i >= 0; i--) { var spec = m_AccessibilitySpecs[i]; if (spec.IsWildcard() && spec.DoesMatch(target, identifier)) { var symbol=Members.DefineSymbol(identifier, bmk); if (symbol.Accessibility == Accessibility.Default) symbol.Accessibility = spec.GetAccessibility(); return; } } // Pass to outer scope if (OuterScope!=null) OuterScope.ProcessAccessibilitySpecs(target, identifier, bmk); }
public bool OnEnterNode(MiniME.ast.Node n) { // Define name of function in outer scope, before descending if (n.GetType() == typeof(ast.ExprNodeFunction)) { var fn = (ast.ExprNodeFunction)n; // Define a symbol for the new function if (!String.IsNullOrEmpty(fn.Name)) { DefineLocalSymbol(fn.Name, fn.Bookmark); currentScope.ProcessAccessibilitySpecs(fn.Name, fn.Bookmark); } } // Descending into an inner scope if (n.Scope != null) { System.Diagnostics.Debug.Assert(n.Scope.OuterScope == currentScope); currentScope = n.Scope; } // Descending into an inner pseudo scope if (n.PseudoScope != null) { System.Diagnostics.Debug.Assert(n.PseudoScope.OuterScope == currentPseudoScope); currentPseudoScope = n.PseudoScope; } // Define catch clause exception variables in the inner scope if (n.GetType() == typeof(ast.CatchClause)) { var cc = (ast.CatchClause)n; DefineLocalSymbol(cc.ExceptionVariable, n.Bookmark); return(true); } // Define variables in the current scope if (n.GetType() == typeof(ast.StatementVariableDeclaration)) { var vardecl = (ast.StatementVariableDeclaration)n; foreach (var v in vardecl.Variables) { DefineLocalSymbol(v.Name, v.Bookmark); currentScope.ProcessAccessibilitySpecs(v.Name, v.Bookmark); if (v.InitialValue != null && v.InitialValue.RootNode.GetType() == typeof(ast.ExprNodeObjectLiteral)) { // Get the object literal var literal = (ast.ExprNodeObjectLiteral)v.InitialValue.RootNode; // Create a fake/temp identifier node while we process accessibility specs var target = new ast.ExprNodeIdentifier(null, v.Name); // Process all keys that are identifiers foreach (var x in literal.Values) { var identifierKey = x.Key as ast.ExprNodeIdentifier; if (identifierKey != null && identifierKey.Lhs == null) { currentScope.ProcessAccessibilitySpecs(target, identifierKey.Name, identifierKey.Bookmark); } } } } return(true); } // Define parameters in the current scope if (n.GetType() == typeof(ast.Parameter)) { var p = (ast.Parameter)n; DefineLocalSymbol(p.Name, p.Bookmark); currentScope.ProcessAccessibilitySpecs(p.Name, p.Bookmark); return(true); } // Automatic declaration of private member? // We're looking for an assignment to a matching private spec if (n.GetType() == typeof(ast.StatementExpression)) { var exprstmt = (ast.StatementExpression)n; if (exprstmt.Expression.RootNode.GetType() == typeof(ast.ExprNodeAssignment)) { var assignOp = (ast.ExprNodeAssignment)exprstmt.Expression.RootNode; if (assignOp.Op == Token.assign) { // Lhs must be an identifier member // eg: target.member=<expr> if (assignOp.Lhs.GetType() == typeof(ast.ExprNodeIdentifier)) { var identifier = (ast.ExprNodeIdentifier)assignOp.Lhs; if (identifier.Lhs != null) { // For member specs, the identifier must have a lhs if (identifier.Lhs.GetType() != typeof(ast.ExprNodeIdentifier)) { return(false); } currentScope.ProcessAccessibilitySpecs((ast.ExprNodeIdentifier)identifier.Lhs, identifier.Name, identifier.Bookmark); } } // Assignment of an object literal // eg: target={member:value,member:value}; if (assignOp.Lhs.GetType() == typeof(ast.ExprNodeIdentifier) && assignOp.Rhs.GetType() == typeof(ast.ExprNodeObjectLiteral)) { var target = (ast.ExprNodeIdentifier)assignOp.Lhs; var literal = (ast.ExprNodeObjectLiteral)assignOp.Rhs; if (target.Lhs == null) { foreach (var x in literal.Values) { var identifierKey = x.Key as ast.ExprNodeIdentifier; if (identifierKey != null && identifierKey.Lhs == null) { currentScope.ProcessAccessibilitySpecs(target, identifierKey.Name, identifierKey.Bookmark); } } } } /* * if (assignOp.Rhs.GetType() == typeof(ast.ExprNodeObjectLiteral)) * { * var literal=ast.ExprNode * } */ } } } // Normally accessibility specs are already processed in the scope builder, but this // is a special case for declaring external symbols. We need to pick up the explicit // symbol declaration and add it to the pseudo scope to avoid incorrect warnings. // Use case is declaring `x` as an external global var by: // // // public:x // if (n.GetType() == typeof(ast.StatementAccessibility)) { var p = (ast.StatementAccessibility)n; foreach (var s in p.Specs) { if (!s.IsWildcard() && !s.IsMemberSpec()) { currentPseudoScope.Symbols.DefineSymbol(s.GetExplicitName(), n.Bookmark); } } } return(true); }
// Parse an expression terminal ast.ExprNode ParseExpressionTerminal(ParseContext ctx) { switch (t.token) { case Token.literal: { var temp = new ast.ExprNodeLiteral(t.GetBookmark(), t.literal); t.Next(); return temp; } case Token.openRound: { var bmk = t.GetBookmark(); t.Next(); var temp = ParseCompositeExpressionNode(0); t.SkipRequired(Token.closeRound); return new ast.ExprNodeParens(bmk, temp); } case Token.identifier: { var temp = new ast.ExprNodeIdentifier(t.GetBookmark(), t.identifier); t.Next(); return temp; } case Token.openSquare: { // Array literal t.Next(); var temp = new ast.ExprNodeArrayLiteral(t.GetBookmark()); while (true) { if (t.token == Token.closeSquare) break; // Empty expression if (t.token == Token.comma) { t.Next(); temp.Values.Add(null); } else { // Non-empty expression temp.Values.Add(ParseSingleExpressionNode(0)); // End of list? if (!t.SkipOptional(Token.comma)) break; } // Trailing blank element? if (t.token == Token.closeSquare) { t.Compiler.RecordWarning(t.GetBookmark(), "trailing comma in array literal"); temp.Values.Add(null); } } t.SkipRequired(Token.closeSquare); return temp; } case Token.openBrace: { // Create the literal var temp = new ast.ExprNodeObjectLiteral(t.GetBookmark()); // Object literal t.Next(); while (true) { if (t.token == Token.closeBrace) break; // Key // - can be an identifier, or string/number literal object key; if (t.token == Token.identifier) { key = new ast.ExprNodeIdentifier(t.GetBookmark(), t.identifier); } else { t.Require(Token.literal); key = t.literal; } t.Next(); // Key/value delimiter t.SkipRequired(Token.colon); // Value temp.Values.Add(new ast.KeyExpressionPair(key, ParseSingleExpressionNode(0))); // Another key/value pair if (!t.SkipOptional(Token.comma)) break; if (t.token == Token.closeBrace) { t.Compiler.RecordWarning(t.GetBookmark(), "trailing comma in object literal"); } } t.SkipRequired(Token.closeBrace); return temp; } case Token.divide: case Token.divideAssign: { // Regular expressions return new ast.ExprNodeRegEx(t.GetBookmark(), t.ParseRegEx()); } case Token.kw_function: { t.Next(); return ParseFunction(); } case Token.kw_new: { var bmk = t.GetBookmark(); t.Next(); // Parse the type var newType = ParseExpressionMember(ctx | ParseContext.NoFunctionCalls); // Create the new operator var newOp = new ast.ExprNodeNew(bmk, newType); // Parse parameters if (t.SkipOptional(Token.openRound)) { if (t.token != Token.closeRound) { while (true) { newOp.Arguments.Add(ParseSingleExpressionNode(0)); if (t.SkipOptional(Token.comma)) continue; else break; } } t.SkipRequired(Token.closeRound); } return newOp; } case Token.kw_delete: { var bmk = t.GetBookmark(); t.Next(); return new ast.ExprNodeUnary(bmk, ParseExpressionMember(ctx), Token.kw_delete); } } throw new CompileError(string.Format("Invalid expression, didn't expect {0}", t.DescribeCurrentToken()), t); }
// Parse member dots, array indexers, function calls ast.ExprNode ParseExpressionMember(ParseContext ctx) { var lhs = ParseExpressionTerminal(ctx); while (true) { // Member dot '.' if (t.SkipOptional(Token.period)) { t.Require(Token.identifier); lhs = new ast.ExprNodeIdentifier(t.GetBookmark(), t.identifier, lhs); t.Next(); continue; } // Array indexer '[]' if (t.SkipOptional(Token.openSquare)) { var temp = new ast.ExprNodeIndexer(t.GetBookmark(), lhs, ParseCompositeExpressionNode(0)); t.SkipRequired(Token.closeSquare); lhs = temp; continue; } // Function call '()' if ((ctx & ParseContext.NoFunctionCalls)==0 && t.SkipOptional(Token.openRound)) { var temp = new ast.ExprNodeCall(t.GetBookmark(), lhs); if (t.token != Token.closeRound) { while (true) { temp.Arguments.Add(ParseSingleExpressionNode(0)); if (t.SkipOptional(Token.comma)) continue; else break; } } t.SkipRequired(Token.closeRound); lhs = temp; continue; } break; } return lhs; }
public bool OnEnterNode(MiniME.ast.Node n) { // Define name of function in outer scope, before descending if (n.GetType() == typeof(ast.ExprNodeFunction)) { var fn = (ast.ExprNodeFunction)n; // Define a symbol for the new function if (!String.IsNullOrEmpty(fn.Name)) { DefineLocalSymbol(fn.Name, fn.Bookmark); currentScope.ProcessAccessibilitySpecs(fn.Name, fn.Bookmark); } } // Descending into an inner scope if (n.Scope != null) { System.Diagnostics.Debug.Assert(n.Scope.OuterScope == currentScope); currentScope = n.Scope; } // Descending into an inner pseudo scope if (n.PseudoScope != null) { System.Diagnostics.Debug.Assert(n.PseudoScope.OuterScope == currentPseudoScope); currentPseudoScope = n.PseudoScope; } // Define catch clause exception variables in the inner scope if (n.GetType() == typeof(ast.CatchClause)) { var cc = (ast.CatchClause)n; DefineLocalSymbol(cc.ExceptionVariable, n.Bookmark); return true; } // Define variables in the current scope if (n.GetType() == typeof(ast.StatementVariableDeclaration)) { var vardecl = (ast.StatementVariableDeclaration)n; foreach (var v in vardecl.Variables) { DefineLocalSymbol(v.Name, v.Bookmark); currentScope.ProcessAccessibilitySpecs(v.Name, v.Bookmark); if (v.InitialValue!=null && v.InitialValue.RootNode.GetType()==typeof(ast.ExprNodeObjectLiteral)) { // Get the object literal var literal=(ast.ExprNodeObjectLiteral)v.InitialValue.RootNode; // Create a fake/temp identifier node while we process accessibility specs var target = new ast.ExprNodeIdentifier(null, v.Name); // Process all keys that are identifiers foreach (var x in literal.Values) { var identifierKey=x.Key as ast.ExprNodeIdentifier; if (identifierKey!=null && identifierKey.Lhs==null) { currentScope.ProcessAccessibilitySpecs(target, identifierKey.Name, identifierKey.Bookmark); } } } } return true; } // Define parameters in the current scope if (n.GetType() == typeof(ast.Parameter)) { var p = (ast.Parameter)n; DefineLocalSymbol(p.Name, p.Bookmark); currentScope.ProcessAccessibilitySpecs(p.Name, p.Bookmark); return true; } // Automatic declaration of private member? // We're looking for an assignment to a matching private spec if (n.GetType() == typeof(ast.StatementExpression)) { var exprstmt = (ast.StatementExpression)n; if (exprstmt.Expression.RootNode.GetType()==typeof(ast.ExprNodeAssignment)) { var assignOp = (ast.ExprNodeAssignment)exprstmt.Expression.RootNode; if (assignOp.Op == Token.assign) { // Lhs must be an identifier member // eg: target.member=<expr> if (assignOp.Lhs.GetType()==typeof(ast.ExprNodeIdentifier)) { var identifier=(ast.ExprNodeIdentifier)assignOp.Lhs; if (identifier.Lhs!=null) { // For member specs, the identifier must have a lhs if (identifier.Lhs.GetType() != typeof(ast.ExprNodeIdentifier)) return false; currentScope.ProcessAccessibilitySpecs((ast.ExprNodeIdentifier)identifier.Lhs, identifier.Name, identifier.Bookmark); } } // Assignment of an object literal // eg: target={member:value,member:value}; if (assignOp.Lhs.GetType() == typeof(ast.ExprNodeIdentifier) && assignOp.Rhs.GetType() == typeof(ast.ExprNodeObjectLiteral)) { var target = (ast.ExprNodeIdentifier)assignOp.Lhs; var literal=(ast.ExprNodeObjectLiteral)assignOp.Rhs; if (target.Lhs == null) { foreach (var x in literal.Values) { var identifierKey=x.Key as ast.ExprNodeIdentifier; if (identifierKey!=null && identifierKey.Lhs==null) { currentScope.ProcessAccessibilitySpecs(target, identifierKey.Name, identifierKey.Bookmark); } } } } /* if (assignOp.Rhs.GetType() == typeof(ast.ExprNodeObjectLiteral)) { var literal=ast.ExprNode } */ } } } // Normally accessibility specs are already processed in the scope builder, but this // is a special case for declaring external symbols. We need to pick up the explicit // symbol declaration and add it to the pseudo scope to avoid incorrect warnings. // Use case is declaring `x` as an external global var by: // // // public:x // if (n.GetType() == typeof(ast.StatementAccessibility)) { var p = (ast.StatementAccessibility)n; foreach (var s in p.Specs) { if (!s.IsWildcard() && !s.IsMemberSpec()) currentPseudoScope.Symbols.DefineSymbol(s.GetExplicitName(), n.Bookmark); } } return true; }
// Parse an expression terminal ast.ExprNode ParseExpressionTerminal(ParseContext ctx) { switch (t.token) { case Token.literal: { var temp = new ast.ExprNodeLiteral(t.GetBookmark(), t.literal); t.Next(); return(temp); } case Token.openRound: { var bmk = t.GetBookmark(); t.Next(); var temp = ParseCompositeExpressionNode(0); t.SkipRequired(Token.closeRound); return(new ast.ExprNodeParens(bmk, temp)); } case Token.identifier: { var temp = new ast.ExprNodeIdentifier(t.GetBookmark(), t.identifier); t.Next(); return(temp); } case Token.openSquare: { // Array literal t.Next(); var temp = new ast.ExprNodeArrayLiteral(t.GetBookmark()); while (true) { if (t.token == Token.closeSquare) { break; } // Empty expression if (t.token == Token.comma) { t.Next(); temp.Values.Add(null); } else { // Non-empty expression temp.Values.Add(ParseSingleExpressionNode(0)); // End of list? if (!t.SkipOptional(Token.comma)) { break; } } // Trailing blank element? if (t.token == Token.closeSquare) { t.Compiler.RecordWarning(t.GetBookmark(), "trailing comma in array literal"); temp.Values.Add(null); } } t.SkipRequired(Token.closeSquare); return(temp); } case Token.openBrace: { // Create the literal var temp = new ast.ExprNodeObjectLiteral(t.GetBookmark()); // Object literal t.Next(); while (true) { if (t.token == Token.closeBrace) { break; } // Key // - can be an identifier, or string/number literal object key; if (t.token == Token.identifier) { key = new ast.ExprNodeIdentifier(t.GetBookmark(), t.identifier); } else { t.Require(Token.literal); key = t.literal; } t.Next(); // Key/value delimiter t.SkipRequired(Token.colon); // Value temp.Values.Add(new ast.KeyExpressionPair(key, ParseSingleExpressionNode(0))); // Another key/value pair if (!t.SkipOptional(Token.comma)) { break; } if (t.token == Token.closeBrace) { t.Compiler.RecordWarning(t.GetBookmark(), "trailing comma in object literal"); } } t.SkipRequired(Token.closeBrace); return(temp); } case Token.divide: case Token.divideAssign: { // Regular expressions return(new ast.ExprNodeRegEx(t.GetBookmark(), t.ParseRegEx())); } case Token.kw_function: { t.Next(); return(ParseFunction()); } case Token.kw_new: { var bmk = t.GetBookmark(); t.Next(); // Parse the type var newType = ParseExpressionMember(ctx | ParseContext.NoFunctionCalls); // Create the new operator var newOp = new ast.ExprNodeNew(bmk, newType); // Parse parameters if (t.SkipOptional(Token.openRound)) { if (t.token != Token.closeRound) { while (true) { newOp.Arguments.Add(ParseSingleExpressionNode(0)); if (t.SkipOptional(Token.comma)) { continue; } else { break; } } } t.SkipRequired(Token.closeRound); } return(newOp); } case Token.kw_delete: { var bmk = t.GetBookmark(); t.Next(); return(new ast.ExprNodeUnary(bmk, ParseExpressionMember(ctx), Token.kw_delete)); } } throw new CompileError(string.Format("Invalid expression, didn't expect {0}", t.DescribeCurrentToken()), t); }