public Tuple<AST.Env, List<Tuple<AST.Env, AST.Decln>>> GetDeclns(AST.Env env) { // Get storage class, and base type. Tuple<AST.Env, AST.Decln.SCS, AST.ExprType> r_specs = decln_specs.GetSCSType(env); env = r_specs.Item1; AST.Decln.SCS scs = r_specs.Item2; AST.ExprType base_type = r_specs.Item3; List<Tuple<AST.Env, AST.Decln>> declns = new List<Tuple<AST.Env, AST.Decln>>(); // For each init declarators, we'll generate a declaration. foreach (InitDeclr init_declr in init_declrs) { // Get the final type, name, and initializer. Tuple<AST.Env, AST.ExprType, Option<AST.Initr>, String> r_declr = init_declr.GetInitDeclr(env, base_type); env = r_declr.Item1; AST.ExprType type = r_declr.Item2; Option<AST.Initr> initr = r_declr.Item3; String name = r_declr.Item4; // Insert the new symbol into the environment. AST.Env.EntryKind kind; switch (scs) { case AST.Decln.SCS.AUTO: if (env.IsGlobal()) { kind = AST.Env.EntryKind.GLOBAL; } else { kind = AST.Env.EntryKind.STACK; } break; case AST.Decln.SCS.EXTERN: kind = AST.Env.EntryKind.GLOBAL; break; case AST.Decln.SCS.STATIC: kind = AST.Env.EntryKind.GLOBAL; break; case AST.Decln.SCS.TYPEDEF: kind = AST.Env.EntryKind.TYPEDEF; break; default: throw new InvalidOperationException(); } env = env.PushEntry(kind, name, type); // Generate the declaration. declns.Add(Tuple.Create(env, new AST.Decln(name, scs, type, initr))); } return new Tuple<AST.Env, List<Tuple<AST.Env, AST.Decln>>>(env, declns); }
public Tuple<AST.Env, AST.FuncDef> GetFuncDef(AST.Env env) { // Get storage class specifier and base type from declaration specifiers. Tuple<AST.Env, AST.Decln.SCS, AST.ExprType> r_specs = this.specs.GetSCSType(env); env = r_specs.Item1; AST.Decln.SCS scs = r_specs.Item2; AST.ExprType base_type = r_specs.Item3; // Get function name and function type from declarator. Tuple<String, AST.ExprType> r_declr = this.declr.GetNameAndType(env, base_type); String name = r_declr.Item1; AST.ExprType type = r_declr.Item2; AST.TFunction func_type; if (type.kind == AST.ExprType.Kind.FUNCTION) { func_type = (AST.TFunction)type; } else { throw new InvalidOperationException($"{name} is not a function."); } switch (scs) { case AST.Decln.SCS.AUTO: case AST.Decln.SCS.EXTERN: case AST.Decln.SCS.STATIC: env = env.PushEntry(AST.Env.EntryKind.GLOBAL, name, type); break; case AST.Decln.SCS.TYPEDEF: default: throw new InvalidOperationException("Invalid storage class specifier for function definition."); } env = env.SetCurrentFunction(func_type); Tuple<AST.Env, AST.Stmt> r_stmt = this.stmt.GetStmt(env); env = r_stmt.Item1; AST.Stmt stmt = r_stmt.Item2; env = env.SetCurrentFunction(new AST.TEmptyFunction()); return Tuple.Create(env, new AST.FuncDef(name, scs, func_type, stmt)); }
public Tuple<AST.Env, AST.ExprType> GetExprTypeEnv(Boolean is_struct, AST.Env env, Boolean is_const, Boolean is_volatile) { if (name == "") { // If no name is supplied: must be complete. // struct { ... } or union { ... } if (declns == null) { throw new ArgumentNullException("Error: parser should ensure declns != null"); } Tuple<AST.Env, List<Tuple<String, AST.ExprType>>> r_attribs = GetAttribs(env); env = r_attribs.Item1; if (is_struct) { return new Tuple<AST.Env, AST.ExprType>(env, AST.TStructOrUnion.CreateStruct("<anonymous>", r_attribs.Item2, is_const, is_volatile)); } else { return new Tuple<AST.Env, AST.ExprType>(env, AST.TStructOrUnion.CreateUnion("<anonymous>", r_attribs.Item2, is_const, is_volatile)); } } else { // If a name is supplied, split into 2 cases. String typename = is_struct ? $"struct {name}" : $"union {name}"; if (declns == null) { // Case 1: If no attribute list supplied, then we are either // 1) mentioning an already-existed struct/union // or 2) creating an incomplete struct/union Option<AST.Env.Entry> entry_opt = env.Find(typename); if (entry_opt.IsNone) { // If the struct/union is not in the current environment, // then add an incomplete struct/union into the environment AST.ExprType type = is_struct ? AST.TStructOrUnion.CreateIncompleteStruct(name, is_const, is_volatile) : AST.TStructOrUnion.CreateIncompleteUnion(name, is_const, is_volatile); env = env.PushEntry(AST.Env.EntryKind.TYPEDEF, typename, type); return Tuple.Create(env, type); } if (entry_opt.Value.kind != AST.Env.EntryKind.TYPEDEF) { throw new InvalidProgramException(typename + " is not a type? This should be my fault."); } // If the struct/union is found, return it. return Tuple.Create(env, entry_opt.Value.type); } else { // Case 2: If an attribute list is supplied. // 1) Make sure there is no complete struct/union in the current environment. Option<AST.Env.Entry> entry_opt = env.Find(typename); if (entry_opt.IsSome && entry_opt.Value.type.kind == AST.ExprType.Kind.STRUCT_OR_UNION && ((AST.TStructOrUnion)entry_opt.Value.type).IsComplete) { throw new InvalidOperationException($"Redefining {typename}"); } // 2) Add an incomplete struct/union into the environment. AST.TStructOrUnion type = is_struct ? AST.TStructOrUnion.CreateIncompleteStruct(name, is_const, is_volatile) : AST.TStructOrUnion.CreateIncompleteUnion(name, is_const, is_volatile); env = env.PushEntry(AST.Env.EntryKind.TYPEDEF, typename, type); // 3) Iterate over the attributes. Tuple<AST.Env, List<Tuple<String, AST.ExprType>>> r_attribs = GetAttribs(env); env = r_attribs.Item1; // 4) Make the type complete. This would also change the entry inside env. if (is_struct) { type.DefineStruct(r_attribs.Item2); } else { type.DefineUnion(r_attribs.Item2); } return new Tuple<AST.Env, AST.ExprType>(env, type); } } }
public override Tuple<AST.Env, AST.ExprType> GetExprTypeEnv(AST.Env env, Boolean is_const, Boolean is_volatile) { if (enums == null) { // if there is no content in this enum type, we must find it's definition in the environment Option<AST.Env.Entry> entry_opt = env.Find($"enum {name}"); if (entry_opt.IsNone || entry_opt.Value.kind != AST.Env.EntryKind.TYPEDEF) { throw new InvalidOperationException($"Type 'enum {name}' has not been defined."); } } else { // so there are something in this enum type, we need to put this type into the environment Int32 idx = 0; foreach (Enumerator elem in enums) { Tuple<AST.Env, String, Int32> r_enum = elem.GetEnumerator(env, idx); env = r_enum.Item1; String name = r_enum.Item2; idx = r_enum.Item3; env = env.PushEnum(name, new AST.TLong(), idx); idx++; } env = env.PushEntry(AST.Env.EntryKind.TYPEDEF, "enum " + name, new AST.TLong()); } return new Tuple<AST.Env, AST.ExprType>(env, new AST.TLong(is_const, is_volatile)); }
public override AST.Expr GetExpr(AST.Env env) { // Step 1: get arguments passed into the function. // Note that currently the arguments are not casted based on the prototype. var args = this.args.Select(_ => _.GetExpr(env)).ToList(); // A special case: // If we cannot find the function prototype in the environment, make one up. // This function returns int. // Update the environment to add this function type. if ((this.func is Variable) && env.Find((this.func as Variable).name).IsNone) { // TODO: get this env used. env = env.PushEntry( loc: AST.Env.EntryKind.TYPEDEF, name: (this.func as Variable).name, type: AST.TFunction.Create( ret_type: new AST.TLong(is_const: true), args: args.ConvertAll(_ => Tuple.Create("", _.type)), is_varargs: false ) ); } // Step 2: get function expression. AST.Expr func = this.func.GetExpr(env); // Step 3: get the function type. AST.TFunction func_type; switch (func.type.kind) { case AST.ExprType.Kind.FUNCTION: func_type = func.type as AST.TFunction; break; case AST.ExprType.Kind.POINTER: var ref_t = (func.type as AST.TPointer).ref_t; if (!(ref_t is AST.TFunction)) { throw new InvalidOperationException("Expected a function pointer."); } func_type = ref_t as AST.TFunction; break; default: throw new InvalidOperationException("Expected a function in function call."); } Int32 num_args_prototype = func_type.args.Count; Int32 num_args_actual = args.Count; // If this function doesn't take varargs, make sure the number of arguments match that in the prototype. if (!func_type.is_varargs && num_args_actual != num_args_prototype) { throw new InvalidOperationException("Number of arguments mismatch."); } // Anyway, you can't call a function with fewer arguments than the prototype. if (num_args_actual < num_args_prototype) { throw new InvalidOperationException("Too few arguments."); } // Make implicit cast. args = Enumerable.Zip( args.GetRange(0, num_args_prototype), func_type.args, (arg, entry) => AST.TypeCast.MakeCast(arg, entry.type) ).Concat(args.GetRange(num_args_prototype, num_args_actual - num_args_prototype)).ToList(); return new AST.FuncCall(func, func_type, args); }