public void EmitDecl(TranspilerStream stream) { // If this is the global block, don't allow statements. if (Parent == null && Stmts.Count > 0) { throw new CompileError(Stmts.First().Token, $"Statements not allowed in the global block"); } var typeDeclIds = new HashSet <string>(); foreach (var decl in TypeDecls) { typeDeclIds.Add(decl.Id.Text); decl.EmitForwardDecl(stream); } // Some metafunctions should be emitted within the type declaration // itself. Emit those now. if (ParentDecl is TypeDecl) { foreach (var stmt in Stmts) { if (stmt is ExprStmt expr) { if (expr.Expr is FnCall call) { Metafunctions.Emit(stream, call, forward: true); } } } } foreach (var decl in TypeDecls) { decl.EmitDecl(stream); } foreach (var decl in FnDecls) { if (typeDeclIds.Contains(decl.Id.Text) && !decl.IsGeneratedConstructor) { throw new CompileError(decl.Id, $"Type already declared in this scope with ID '{decl.Id.Text}'"); } decl.EmitForwardDecl(stream); } }
public override void Emit(TranspilerStream stream) { if (Id.Text.StartsWith('#')) { Metafunctions.Emit(stream, this, forward: false); } else { // Try to find a matching function: FnDecl?fn = null; if (ParentAccess != null) { fn = ParentAccess.Left.TypeDecl.Block.FindFn(this); } else { fn = Block.FindFn(this); } fn = fn ?? throw new CompileError(Token, "Cannot find matching function"); // Verify that the expressions passed into the function // match the pass-by-tyep of each argument. for (int i = 0; i < fn.Params.Count; i++) { var param = fn.Params[i]; var arg = Args[i]; if (!param.CompatibleWith(arg, out var errorMessage)) { throw new CompileError(arg.Token, errorMessage); } } stream.Write(Compiler.Prefix); stream.Write(Id.Text); // Functions also have suffixes relating to return type: stream.Write("__"); fn.ReturnType?.Emit(stream); // Write function arguments: stream.Write('('); var actualArgs = new Expr[Args.Count]; // Process as named args to get a new order of arguments to pass in. var namedArgsStarted = false; var index = 0; var namedArgs = new HashSet <string>(); foreach (var arg in Args) { if (arg.NamedArg.HasValue) { namedArgsStarted = true; if (!namedArgs.Add(arg.NamedArg.Value.Text)) { throw new CompileError("Named argument already specified"); } // Find the matching named parameter. var paramIndex = fn.Params.FindIndex( x => x.Id.Text == arg.NamedArg.Value.Text); if (paramIndex == -1) { throw new CompileError("Named argument not found"); } else { if (actualArgs[paramIndex] != null) { throw new CompileError("Argument already specified"); } actualArgs[paramIndex] = arg; } } else { if (namedArgsStarted) { throw new CompileError( "Positional arguments cannot follow named arguments"); } actualArgs[index] = arg; index += 1; } } // Check for missing arguments. if (actualArgs.Any(x => x == null)) { throw new CompileError("Missing one or more required arguments"); } // Process the generated array of positional args. var firstArg = true; foreach (var arg in actualArgs) { if (!firstArg) { stream.Write(','); } else { firstArg = false; } arg.Emit(stream); } stream.Write(")"); } }