/// <summary> /// Generates a slice literal of the specified type name and optional values. /// </summary> /// <param name="typeName">The fully qualified slice type name.</param> /// <param name="values">Optional slice values. Pass null to omit values.</param> /// <returns>The root node in this slice literal AST.</returns> public static Node Generate(string typeName, IEnumerable <Node> values) { if (string.IsNullOrWhiteSpace(typeName)) { throw new ArgumentException(nameof(typeName)); } if (values != null && !values.Any()) { throw new ArgumentException("pass null for no slice elements"); } var openBracket = new OpenDelimiter(BinaryDelimiterType.Bracket); var closeBracket = openBracket.AddClosingDelimiter(); var id = new Identifier(typeName); closeBracket.AddChild(id); var openBrace = new OpenDelimiter(BinaryDelimiterType.Brace); if (values != null) { var seq = DelimitedSequence.Generate(UnaryDelimiterType.Comma, values.ToList()); openBrace.AddChild(seq); } openBrace.AddClosingDelimiter(); id.AddChild(openBrace); return(openBracket); }
/// <summary> /// Generates a switch-case block with the specified clauses. /// </summary> /// <param name="condition">Optional condition expression.</param> /// <param name="cases">List of cases, must be at least one.</param> /// <param name="defaultAction">Optional default action.</param> /// <returns>The root node in this switch block AST.</returns> public static Node Generate(Node condition, IReadOnlyList <SwitchCase> cases, Node defaultAction) { if (cases == null || cases.Count == 0) { throw new ArgumentException(nameof(cases)); } Switch switchStmt; if (condition != null) { switchStmt = new Switch(condition); } else { switchStmt = new Switch(); } var openBrace = new OpenDelimiter(BinaryDelimiterType.Brace); foreach (var c in cases) { openBrace.AddChild(new Case(c.Predicate, c.Action)); openBrace.AddChild(new Terminal()); } if (defaultAction != null) { openBrace.AddChild(new Default(defaultAction)); } openBrace.AddClosingDelimiter(); switchStmt.AddChild(openBrace); return(switchStmt); }
/// <summary> /// Determines if this delimiter is part of a slice literal (e.g. []string{"foo"}). /// </summary> /// <param name="delimiter">The OpenDelimiter object to inspect.</param> /// <returns>True if this node is part of a slice literal.</returns> private bool IsStartOfSliceLiteral(OpenDelimiter delimiter) { if (delimiter.Type != BinaryDelimiterType.Brace) { return(false); } Node n = delimiter.Parent; int trips = 0; while (n != null) { if (n is Identifier) { ++trips; } else if (n is CloseDelimiter && ((CloseDelimiter)n).Type == BinaryDelimiterType.Bracket) { ++trips; } else if (n is OpenDelimiter && ((OpenDelimiter)n).Type == BinaryDelimiterType.Bracket) { ++trips; } if (trips == 3) { return(true); } n = n.Parent; } return(false); }
/// <summary> /// Generates a for block with the specified loop conditions and body. /// </summary> /// <param name="init">The optional init statement.</param> /// <param name="condition">The optional condition statement.</param> /// <param name="post">The optional post statement.</param> /// <param name="body">The body of the for loop.</param> /// <returns>The root node of the for block AST.</returns> public static Node Generate(Node init, Node condition, Node post, IReadOnlyList <Node> body) { if (init != null || condition != null || post != null) { throw new NotImplementedException("init, condition and post are NYI"); } if (body == null || body.Count == 0) { throw new ArgumentException(nameof(body)); } var forBlock = new For(); var openBrace = new OpenDelimiter(BinaryDelimiterType.Brace); foreach (var node in body) { openBrace.AddChild(node); } openBrace.AddClosingDelimiter(); forBlock.AddChild(openBrace); return(forBlock); }
/// <summary> /// Generates an if block with the specified condition and body. /// if condition { /// ...body... /// } /// </summary> /// <param name="condition">The conditional of the if statement.</param> /// <param name="body">The body to execute if the condition is true.</param> /// <returns>The root node of the if block AST.</returns> public static Node Generate(Node condition, IReadOnlyList <Node> body) { if (condition == null) { throw new ArgumentNullException(nameof(condition)); } if (body == null || body.Count == 0) { throw new ArgumentException(nameof(body)); } var ifBlock = new If(); ifBlock.AddChild(condition); var openBrace = new OpenDelimiter(BinaryDelimiterType.Brace); foreach (var node in body) { openBrace.AddChild(node); } openBrace.AddClosingDelimiter(); ifBlock.AddChild(openBrace); return(ifBlock); }
/// <summary> /// Generates a function signature from the specified parameters. /// </summary> /// <param name="receiver">Optional signature for a receiver.</param> /// <param name="name">The name of the function.</param> /// <param name="parameters">Optional list of parameters. Pass null if there are no parameters.</param> /// <param name="returns">Optional list of return values. Pass null if there are no return values.</param> /// <returns>The root node in the function signature AST.</returns> public static Node Generate(FuncParamSig receiver, string name, IReadOnlyList <FuncParamSig> parameters, IReadOnlyList <FuncReturnSig> returns) { if (string.IsNullOrWhiteSpace(name)) { throw new ArgumentException(nameof(name)); } var func = new Func(); if (receiver != null) { var recvOpenParen = new OpenDelimiter(BinaryDelimiterType.Paren); recvOpenParen.AddChild(receiver); recvOpenParen.AddClosingDelimiter(); func.AddChild(recvOpenParen); } func.AddChild(new Identifier(name)); // build parameters var paramsStart = new OpenDelimiter(BinaryDelimiterType.Paren); if (parameters != null) { paramsStart.AddChild(DelimitedSequence.Generate(UnaryDelimiterType.Comma, parameters)); } paramsStart.AddClosingDelimiter(); func.AddChild(paramsStart); if (returns != null) { // multiple return values or a single return value // that has a name must be enclosed in parentheses if (returns.Count == 1) { var ret = returns[0]; if (ret.IsNamed) { var returnOpenParen = new OpenDelimiter(BinaryDelimiterType.Paren); returnOpenParen.AddChild(ret); returnOpenParen.AddClosingDelimiter(); func.AddChild(returnOpenParen); } else { func.AddChild(ret); } } else { var returnOpenParen = new OpenDelimiter(BinaryDelimiterType.Paren); returnOpenParen.AddChild(DelimitedSequence.Generate(UnaryDelimiterType.Comma, returns)); returnOpenParen.AddClosingDelimiter(); func.AddChild(returnOpenParen); } } return(func); }
/// <summary> /// Determines if this delimiter starts an empty braces sequence (e.g. {}). /// </summary> /// <param name="delimiter">The OpenDelimiter object to inspect.</param> /// <returns>True if this is node is empty braces.</returns> private bool IsEmptyBraces(OpenDelimiter delimiter) { if (delimiter.Type != BinaryDelimiterType.Brace) { return(false); } return(delimiter.Children[0] is CloseDelimiter); }
/// <summary> /// Generates a function call for the specified values. /// Example: package.Func(one, &two, three) /// </summary> /// <param name="name">The fully qualified name of the function to call.</param> /// <param name="parameters">The optional list of parameters. Pass null if there are no parameters.</param> /// <returns>The root node for this function call AST.</returns> public static Node Generate(string name, IReadOnlyList <FuncCallParam> parameters) { if (string.IsNullOrWhiteSpace(name)) { throw new ArgumentException(nameof(name)); } if (parameters != null && parameters.Count == 0) { throw new ArgumentException("pass null if there are no arguments"); } var funcCall = new Identifier(name); var paramsStart = new OpenDelimiter(BinaryDelimiterType.Paren); funcCall.AddChild(paramsStart); if (parameters != null) { var seq = new Node[parameters.Count]; // build a comma separated list of params for (int i = 0; i < parameters.Count; ++i) { var param = parameters[i]; Node p = null; if (param.Pass == TypeModifier.ByReference) { p = UnaryOpSequence.Generate(UnaryOperatorType.Ampersand, param.Param); } else if (param.Pass == TypeModifier.ByValue) { p = param.Param; } seq[i] = p; } paramsStart.AddChild(DelimitedSequence.Generate(UnaryDelimiterType.Comma, seq)); } paramsStart.AddClosingDelimiter(); return(funcCall); }
/// <summary> /// Generates a type conversion sequence for the specified values. /// E.g. toTypeName(expression). /// </summary> /// <param name="expression">The expression to which the type conversion is applied.</param> /// <param name="toTypeName">The name of the type.</param> /// <returns>The root node in this type conversion AST.</returns> public static Node Generate(Node expression, string toTypeName) { if (expression == null) { throw new ArgumentNullException(nameof(expression)); } if (string.IsNullOrWhiteSpace(toTypeName)) { throw new ArgumentException(nameof(toTypeName)); } var typeName = new TypeName(toTypeName); var openParen = new OpenDelimiter(BinaryDelimiterType.Paren); openParen.AddChild(expression); openParen.AddClosingDelimiter(); typeName.AddChild(openParen); return(typeName); }
/// <summary> /// Generates a struct definition with the specified name and fields. /// If the struct is to contain no fields pass null for the fields parameter. /// </summary> /// <param name="name">The name of the struct to be created.</param> /// <param name="fields">Optional list of fields. Pass null if the struct has no fields.</param> /// <returns>Root node for this struct definition's AST.</returns> public static Node Generate(string name, IReadOnlyList <StructFieldDef> fields) { if (string.IsNullOrWhiteSpace(name)) { throw new ArgumentException(nameof(name)); } if (fields != null && fields.Count == 0) { throw new ArgumentException("pass null for no struct fields"); } var typeDef = new TypeDef(); typeDef.AddChild(new Identifier(name)); var structDef = new StructDef(); var defStart = new OpenDelimiter(BinaryDelimiterType.Brace); if (fields != null) { foreach (var field in fields) { var id = new Identifier(field.Name); id.AddChild(new TypeName(field.TypeName)); if (field.Tag != null) { id.AddChild(new Tag(field.Tag)); } defStart.AddChild(id); } } defStart.AddClosingDelimiter(); structDef.AddChild(defStart); typeDef.AddChild(structDef); return(typeDef); }
/// <summary> /// Determines if this delimiter is part of a control block. /// </summary> /// <param name="delimiter">The OpenDelimiter object to inspect.</param> /// <returns>True if this delimiter is part of a control block.</returns> private bool IsOpeningBraceInControlBlock(OpenDelimiter delimiter) { if (delimiter.Type != BinaryDelimiterType.Brace) { return(false); } Node n = delimiter.Parent; while (n != null) { if (n is If || n is For || n is Switch) { return(true); } n = n.Parent; } return(false); }
public void Visit(OpenDelimiter delimiter) { // TODO: verify sequence ends with matching close delimiter, or if // this is a slice that the first child is a closing square bracket. switch (delimiter.Type) { case BinaryDelimiterType.Brace: if (_prev is CloseDelimiter || IsOpeningBraceInControlBlock(delimiter) || _prev is TypeName) { _sb.Append(' '); } _sb.Append('{'); if (!IsEmptyBraces(delimiter) && !IsStartOfSliceLiteral(delimiter)) { _sb.AppendLine(); ++_indentLevel; } break; case BinaryDelimiterType.Bracket: _sb.Append('['); break; case BinaryDelimiterType.Paren: _sb.Append('('); if (_prev is Import) { _sb.AppendLine(); ++_indentLevel; } break; default: throw new InvalidOperationException($"unsupported open delimiter {delimiter.Type}"); } _prev = delimiter; }
/// <summary> /// Generates a struct literal of the specified type name and optional field values. /// </summary> /// <param name="structTypeName">The fully qualified struct type name.</param> /// <param name="values">Optional field values. Pass null to omit field initialization.</param> /// <returns>The root node in this struct literal AST.</returns> public static Node Generate(string structTypeName, IEnumerable <StructField> values) { if (string.IsNullOrWhiteSpace(structTypeName)) { throw new ArgumentException(nameof(structTypeName)); } if (values != null && !values.Any()) { throw new ArgumentException("pass null to omit struct field initializers"); } var id = new Identifier(structTypeName); var openBrace = new OpenDelimiter(BinaryDelimiterType.Brace); if (values != null) { var initList = new List <Node>(); foreach (var val in values) { var root = DelimitedSequence.Generate(UnaryDelimiterType.Colon, new[] { new Identifier(val.FieldName), val.Value }); initList.Add(root); } var seqRoot = DelimitedSequence.Generate(UnaryDelimiterType.Comma, initList, true); openBrace.AddChild(seqRoot); } openBrace.AddClosingDelimiter(); id.AddChild(openBrace); return(id); }
private static Node GetRegisterServer(string typeName) { var logParamName = "log"; var func = FunctionSignature.Generate(null, "RegisterServer", new[] { new FuncParamSig(logParamName, TypeModifier.ByReference, "log.Logger") }, null); var funcBody = new OpenDelimiter(BinaryDelimiterType.Brace); funcBody.AddChild(BinaryOpSequence.Generate(BinaryOperatorType.Assignment, new Identifier(LoggerVarName), new Identifier(logParamName))); funcBody.AddChild(new Terminal()); funcBody.AddChild(FunctionCall.Generate("rpc.Register", new[] { new FuncCallParam(StructLiteral.Generate(typeName, null), TypeModifier.ByReference) })); funcBody.AddClosingDelimiter(); func.AddChild(funcBody); return(func); }
/// <summary> /// Generates a test case function for the specified method. /// </summary> /// <param name="mg">The method for which a test case will be generated.</param> /// <param name="rpcTypeName">The type name use for RPC dispatch.</param> /// <returns>The root node in this test case function AST.</returns> public static Tuple <Node, string> Generate(MethodGo mg, string rpcTypeName, string replyTypeName, string replyConvFuncName, string loggerVarName) { if (mg == null) { throw new ArgumentNullException(nameof(mg)); } if (string.IsNullOrWhiteSpace(replyTypeName)) { throw new ArgumentException(nameof(replyTypeName)); } if (string.IsNullOrWhiteSpace(replyConvFuncName)) { throw new ArgumentException(nameof(replyConvFuncName)); } var errorVar = "err"; var jsonParamVar = "parameters"; var replyVar = "reply"; var operationId = mg.SerializedName.ToString(); if (!char.IsUpper(operationId[0])) { operationId = $"{char.ToUpperInvariant(operationId[0])}{operationId.Substring(1)}"; } var rpcTypeVar = char.ToLowerInvariant(rpcTypeName[0]).ToString(); var func = FunctionSignature.Generate(new FuncParamSig(rpcTypeVar, TypeModifier.ByReference, rpcTypeName), operationId, new[] { new FuncParamSig(jsonParamVar, TypeModifier.ByValue, "json.RawMessage"), new FuncParamSig(replyVar, TypeModifier.ByReference, replyTypeName) }, new[] { new FuncReturnSig(null, TypeModifier.ByValue, "error") }); var funcBody = new OpenDelimiter(BinaryDelimiterType.Brace); // generate a type used to unmarshal the parameters string unmarshalVar = null; var unmarshalType = GenerateTypeForParamsUnmarshal(mg); funcBody.AddChild(unmarshalType.Item1); funcBody.AddChild(new Terminal()); unmarshalVar = unmarshalType.Item2.ToLowerInvariant(); // do the unmarshaling and return on failure funcBody.AddChild(VariableDecl.Generate(unmarshalVar, unmarshalType.Item2)); funcBody.AddChild(new Terminal()); funcBody.AddChild(BinaryOpSequence.Generate(BinaryOperatorType.DeclareAndAssign, new Identifier(errorVar), FunctionCall.Generate("json.Unmarshal", new[] { new FuncCallParam(new Identifier(jsonParamVar), TypeModifier.ByValue), new FuncCallParam(new Identifier(unmarshalVar), TypeModifier.ByReference) }))); funcBody.AddChild(new Terminal()); funcBody.AddChild(ErrorCheck.Generate(errorVar, OnError.ReturnError)); funcBody.AddChild(new Terminal()); var authVar = "auth"; funcBody.AddChild(BinaryOpSequence.Generate(BinaryOperatorType.DeclareAndAssign, DelimitedSequence.Generate(UnaryDelimiterType.Comma, new[] { new Identifier(authVar), new Identifier(errorVar) }), FunctionCall.Generate($"{unmarshalVar}.{ReservedParamsFieldName}.Credentials.CreateBearerAuthorizer", null))); funcBody.AddChild(new Terminal()); funcBody.AddChild(ErrorCheck.Generate(errorVar, OnError.ReturnError)); funcBody.AddChild(new Terminal()); // create the client and call the target function var clientVar = "client"; // check if the client ctor has any parameters List <FuncCallParam> clientParams = null; if (!string.IsNullOrWhiteSpace(((MethodGroupGo)mg.MethodGroup).GlobalParameters)) { clientParams = new List <FuncCallParam>(); var globalParams = ((MethodGroupGo)mg.MethodGroup).GlobalParameters.Split(new[] { ',' }); // each param is the var name followed by the type name, e.g. "subscriptionID string" foreach (var globalParam in globalParams) { var paramName = globalParam.Substring(0, globalParam.IndexOf(' ')); clientParams.Add(new FuncCallParam(new Identifier($"{unmarshalVar}.{paramName.Capitalize()}"), TypeModifier.ByValue)); } } funcBody.AddChild(FunctionCall.Generate($"{loggerVarName}.Println", new[] { new FuncCallParam(new Literal <string>(operationId), TypeModifier.ByValue) })); funcBody.AddChild(new Terminal()); funcBody.AddChild(FunctionCall.Generate($"{loggerVarName}.Printf", new[] { new FuncCallParam(new Literal <string>("received %v\\n"), TypeModifier.ByValue), new FuncCallParam(new Identifier(unmarshalVar), TypeModifier.ByValue) })); funcBody.AddChild(new Terminal()); // the client creation function name prefix is hard-coded in the Go generator var clientCtorName = $"{mg.CodeModel.Namespace}.New"; if (!string.IsNullOrWhiteSpace(mg.MethodGroup.Name)) { clientCtorName = $"{clientCtorName}{((MethodGroupGo)mg.MethodGroup).ClientName}"; } funcBody.AddChild(BinaryOpSequence.Generate( BinaryOperatorType.DeclareAndAssign, new Identifier(clientVar), FunctionCall.Generate(clientCtorName, clientParams))); funcBody.AddChild(new Terminal()); funcBody.AddChild(BinaryOpSequence.Generate(BinaryOperatorType.Assignment, new Identifier($"{clientVar}.Authorizer"), new Identifier(authVar))); funcBody.AddChild(new Terminal()); var rChanVar = "rChan"; var eChanVar = "eChan"; var responseVar = "resp"; var rawResponseVar = "rawResp"; var isLro = mg.IsLongRunningOperation() && !mg.IsPageable; funcBody.AddChild(BinaryOpSequence.Generate(BinaryOperatorType.DeclareAndAssign, DelimitedSequence.Generate(UnaryDelimiterType.Comma, new[] { new Identifier(isLro ? rChanVar : responseVar), new Identifier(isLro ? eChanVar : errorVar) }), FunctionCall.Generate($"{clientVar}.{mg.Name}", GenerateApiCallParams(mg, unmarshalVar)))); funcBody.AddChild(new Terminal()); if (isLro) { funcBody.AddChild(BinaryOpSequence.Generate(BinaryOperatorType.Assignment, new Identifier(errorVar), new Identifier($"<-{eChanVar}"))); funcBody.AddChild(new Terminal()); } funcBody.AddChild(ErrorCheck.Generate(errorVar, OnError.ReturnError)); funcBody.AddChild(new Terminal()); if (isLro) { funcBody.AddChild(BinaryOpSequence.Generate(BinaryOperatorType.DeclareAndAssign, new Identifier(responseVar), new Identifier($"<-{rChanVar}"))); funcBody.AddChild(new Terminal()); } funcBody.AddChild(BinaryOpSequence.Generate(BinaryOperatorType.DeclareAndAssign, DelimitedSequence.Generate(UnaryDelimiterType.Comma, new[] { new Identifier(rawResponseVar), new Identifier(errorVar) }), FunctionCall.Generate(replyConvFuncName, new[] { new FuncCallParam(new Identifier(mg.HasReturnValue() ? $"{responseVar}.Response" : $"{responseVar}"), TypeModifier.ByReference), new FuncCallParam(mg.HasReturnValue() ? (Node) new Identifier(responseVar) : new Nil(), TypeModifier.ByValue) }))); funcBody.AddChild(new Terminal()); funcBody.AddChild(FunctionCall.Generate($"{loggerVarName}.Printf", new[] { new FuncCallParam(new Literal <string>("response %v\\n"), TypeModifier.ByValue), new FuncCallParam(new Identifier($"*{rawResponseVar}"), TypeModifier.ByValue) })); funcBody.AddChild(new Terminal()); funcBody.AddChild(BinaryOpSequence.Generate(BinaryOperatorType.Assignment, new Identifier($"*{replyVar}"), new Identifier($"*{rawResponseVar}"))); funcBody.AddChild(new Terminal()); funcBody.AddChild(ReturnStatement.Generate(new[] { errorVar })); funcBody.AddClosingDelimiter(); func.AddChild(funcBody); return(new Tuple <Node, string>(func, operationId)); }