using System; using System.Collections.Immutable; using System.Linq; namespace AST { using static SemanticAnalysis; /// /// Modify a Type into a function, array, or pointer /// public abstract class TypeModifier : ISyntaxTreeNode { public int Line { get; private set; } public int Column { get; private set; } public void Copy(ILineInfo info) { Line = info.Line; Column = info.Column; } [SemantMethod] public abstract ISemantReturn DecorateType(ABT.Env env, ABT.ExprType baseType); } public sealed class FunctionModifier : TypeModifier { private FunctionModifier(Option paramTypeList) { this.ParamTypeList = paramTypeList; } public static FunctionModifier Create(Option paramTypeList) => new FunctionModifier(paramTypeList); public Option ParamTypeList { get; } [SemantMethod] public override ISemantReturn DecorateType(ABT.Env env, ABT.ExprType returnType) { if (this.ParamTypeList.IsNone) { return SemantReturn.Create(env, ABT.FunctionType.Create(returnType)); } var paramTypeList = this.ParamTypeList.Value; var namesAndTypes = Semant(paramTypeList.GetNamesAndTypes, ref env); var hasVarArgs = paramTypeList.HasVarArgs; return SemantReturn.Create(env, ABT.FunctionType.Create(returnType, namesAndTypes, hasVarArgs)); } } /// /// parameter-Type-list /// : parameter-list [ ',' '...' ]? /// /// parameter-list /// : parameter-declaration [ ',' parameter-declaration ]* /// public sealed class ParamTypeList : ISyntaxTreeNode { public int Line { get; private set; } public int Column { get; private set; } public void Copy(ILineInfo info) { Line = info.Line; Column = info.Column; } private ParamTypeList(ImmutableList paramDeclns, Boolean hasVarArgs) { this.ParamDeclns = paramDeclns; this.HasVarArgs = hasVarArgs; } public static ParamTypeList Create(ImmutableList paramDeclns, Boolean hasVarArgs) => new ParamTypeList(paramDeclns, hasVarArgs); public static ParamTypeList Create() => Create(ImmutableList.Empty, true); public ImmutableList ParamDeclns { get; } public Boolean HasVarArgs { get; } [SemantMethod] public ISemantReturn, ABT.ExprType>>> GetNamesAndTypes(ABT.Env env) { var namesAndTypes = this.ParamDeclns.ConvertAll( paramDecln => Tuple.Create( paramDecln.ParamDeclr.Name, Semant(paramDecln.GetParamType, ref env) ) ); return SemantReturn.Create(env, namesAndTypes); } } public sealed class ArrayModifier : TypeModifier { private ArrayModifier(Option numElements) { this.NumElems = numElements; } public static ArrayModifier Create(Option numElements) => new ArrayModifier(numElements); [SemantMethod] public override ISemantReturn DecorateType(ABT.Env env, ABT.ExprType elemType) { if (this.NumElems.IsNone) { return SemantReturn.Create(env, new ABT.IncompleteArrayType(elemType)); } // Get number of elements. // Be careful: the environment might change. var numElems = SemantExpr(this.NumElems.Value, ref env); // Try to cast number of elements to a integer. // TODO: allow float??? numElems = ABT.TypeCast.MakeCast(numElems, new ABT.LongType(true, false)); if (!numElems.IsConstExpr) { throw new InvalidOperationException("Number of elements of an array must be constant.").Attach(this); } return SemantReturn.Create(env, new ABT.ArrayType(elemType, ((ABT.ConstLong) numElems).Value)); } public Option NumElems { get; } } public sealed class PointerModifier : TypeModifier { private PointerModifier(ImmutableList typeQuals) { this.TypeQuals = typeQuals; } public static PointerModifier Create(ImmutableList typeQuals) => new PointerModifier(typeQuals); [SemantMethod] public override ISemantReturn DecorateType(ABT.Env env, ABT.ExprType targetType) { var isConst = this.TypeQuals.Contains(TypeQual.CONST); var isVolatile = this.TypeQuals.Contains(TypeQual.VOLATILE); return SemantReturn.Create(env, new ABT.PointerType(targetType, isConst, isVolatile)); } public ImmutableList TypeQuals { get; } } /// /// Has a list of modifiers. Has an optional name. /// public class ParamDeclr : AbstractDeclr { protected ParamDeclr(Option name, ImmutableList typeModifiers) : base(typeModifiers) { this.Name = name; } public static ParamDeclr Create(Declr declr) => new ParamDeclr(Option.Some(declr.Name), declr.TypeModifiers); public static ParamDeclr Create(AbstractDeclr abstractDeclr) => new ParamDeclr(Option.None, abstractDeclr.TypeModifiers); public new static ParamDeclr Empty { get; } = new ParamDeclr(Option.None, ImmutableList.Empty); public Option Name { get; } } /// /// struct-declarator /// : [declarator]? ':' constant-expression /// | declarator /// public sealed class StructDeclr : ParamDeclr { private StructDeclr(Option name, ImmutableList typeModifiers, Option numBits) : base(name, typeModifiers) { this.NumBits = numBits; } public static StructDeclr Create(Option declr, Expr numBits) { return declr.IsNone ? new StructDeclr(Option.None, ImmutableList.Empty, Option.Some(numBits)) : new StructDeclr(Option.Some(declr.Value.Name), declr.Value.TypeModifiers, Option.None); } public new static StructDeclr Create(Declr declr) => new StructDeclr(Option.Some(declr.Name), declr.TypeModifiers, Option.None); public Option NumBits { get; } } /// /// abstract-declarator /// : [pointer]? direct-abstract-declarator /// | pointer /// /// direct-abstract-declarator /// : [ /// '(' abstract-declarator ')' /// | '[' [constant-expression]? ']' // array modifier /// | '(' [parameter-type_list]? ')' // function modifier /// ] [ /// '[' [constant-expression]? ']' // array modifier /// | '(' [parameter-Type-list]? ')' // function modifier /// ]* /// /// An abstract declarator is a list of (pointer, function, or array) Type modifiers /// public class AbstractDeclr { protected AbstractDeclr(ImmutableList typeModifiers) { this.TypeModifiers = typeModifiers; } public static AbstractDeclr Create(ImmutableList typeModifiers) where Modifier : TypeModifier => new AbstractDeclr(typeModifiers.ToImmutableList()); public static AbstractDeclr Empty { get; } = Create(ImmutableList.Empty); public static AbstractDeclr Add(AbstractDeclr abstractDeclr, TypeModifier typeModifier) => Create(abstractDeclr.TypeModifiers.Add(typeModifier)); public static AbstractDeclr Add(ImmutableList pointerModifiers, AbstractDeclr abstractDeclr) => Create(abstractDeclr.TypeModifiers.AddRange(pointerModifiers)); [SemantMethod] public ISemantReturn DecorateType(ABT.Env env, ABT.ExprType baseType) { var type = this.TypeModifiers .Reverse() // The first Type modifier is nearest to the symbol name, which indicates the outmost Type. .Aggregate( // Wrap up the Type based on the Type modifiers. baseType, (currentType, typeModifier) => Semant(typeModifier.DecorateType, currentType, ref env) ); return SemantReturn.Create(env, type); } public ImmutableList TypeModifiers { get; } } /// /// Has a name and a list of modifiers. /// public sealed class Declr : AbstractDeclr { private Declr(String name, ImmutableList typeModifiers) : base(typeModifiers) { this.Name = name; } public static Declr Create(String name, ImmutableList typeModifiers) => new Declr(name, typeModifiers); public static Declr Create(String name) => new Declr(name, ImmutableList.Empty); public static Declr Create(Option> pointerModifiers, Declr declr) => Add(pointerModifiers.IsSome ? pointerModifiers.Value : ImmutableList.Empty, declr); public static Declr Add(Declr declr, TypeModifier typeModifier) => Create(declr.Name, declr.TypeModifiers.Add(typeModifier)); public static Declr Add(ImmutableList pointerModifiers, Declr declr) => Create(declr.Name, declr.TypeModifiers.AddRange(pointerModifiers)); public String Name { get; } } /// /// Init-declarator /// : declarator [ '=' initializer ]? /// public sealed class InitDeclr : ISyntaxTreeNode { public int Line { get; private set; } public int Column { get; private set; } public void Copy(ILineInfo info) { Line = info.Line; Column = info.Column; } private InitDeclr(Declr declr, Option initr) { this.Declr = declr; this.Initr = initr; } public Declr Declr { get; } public Option Initr { get; } public static InitDeclr Create(Declr declr, Option initr) => new InitDeclr(declr, initr); [SemantMethod] public String GetName() => this.Declr.Name; [SemantMethod] public ISemantReturn>> GetDecoratedTypeAndInitr(ABT.Env env, ABT.ExprType baseType) { // Get the Type based on the declarator. Note that this might be an incomplete array. var type = Semant(this.Declr.DecorateType, baseType, ref env); Option initrOption; if (this.Initr.IsNone) { initrOption = Option.None; } else { // If an initializer is present: var initr = Semant(this.Initr.Value.GetInitr, ref env); // Check that the initializer conforms to the Type. initr = initr.ConformType(type); // If the object is an incomplete array, we must determine the length based on the initializer. if (type.Kind == ABT.ExprTypeKind.INCOMPLETE_ARRAY) { var incomplete = (ABT.IncompleteArrayType)type; // Now we need to determine the length. // Find the last element in the Init list. var lastOffset = -1; initr.Iterate(type, (offset, _) => { lastOffset = offset; }); /* if (lastOffset == -1) { throw new InvalidOperationException("Cannot determine the length of the array based on an empty initializer list.").Attach(this); } */ var elemType = incomplete.ElemType; int numElems = 0; if (lastOffset != -1) { var initList = initr as ABT.InitList; if (initList != null) { numElems = initList.initrs.Count; } else { numElems = 1 + lastOffset / incomplete.ElemType.SizeOf; } } type = incomplete.SetDeclaratorElems(numElems); } initrOption = Option.Some(initr); } // Now everything is created. Check that the Type is complete. if (!type.IsComplete) { throw new InvalidOperationException("Cannot create an object with an incomplete Type.").Attach(this); } return SemantReturn.Create(env, Tuple.Create(type, initrOption)); } } /// /// initializer /// : assignment-expression /// | '{' initializer-list '}' /// | '{' initializer-list ',' '}' /// /// initializer-list /// : initializer [ ',' initializer ]* /// public abstract class Initr : ISyntaxTreeNode { public int Line { get; private set; } public int Column { get; private set; } public void Copy(ILineInfo info) { Line = info.Line; Column = info.Column; } [SemantMethod] public abstract ISemantReturn GetInitr(ABT.Env env); } public sealed class InitExpr : Initr { private InitExpr(Expr expr) { this.Expr = expr; } public static InitExpr Create(Expr expr) => new InitExpr(expr); public Expr Expr { get; } [SemantMethod] public override ISemantReturn GetInitr(ABT.Env env) { var expr = SemantExpr(this.Expr, ref env); return SemantReturn.Create(env, new ABT.InitExpr(expr)); } } public sealed class InitList : Initr { private InitList(ImmutableList initrs) { this.Initrs = initrs; } public static InitList Create(ImmutableList initrs) => new InitList(initrs); public static InitList Create(Option> initrs) { if (initrs.IsNone) return Create(ImmutableList.Empty); return Create(initrs.Value); } public ImmutableList Initrs { get; } [SemantMethod] public override ISemantReturn GetInitr(ABT.Env env) { var initrs = this.Initrs.ConvertAll( initr => Semant(initr.GetInitr, ref env) ); return SemantReturn.Create(env, new ABT.InitList(initrs.ToList())); } } }