Исходники макробиблиотеки
От: Oyster Украина https://github.com/devoyster
Дата: 05.04.06 08:06
Оценка:
ConcreteUnitTypeAttribute.n:

using System;
using Nemerle.Utility;

namespace Oyster.Units
{
    /// Automatically attached to all concrete unit type implemetations
    public class ConcreteUnitTypeAttribute : Attribute
    {
        [Accessor] _normalizationCoef : double;

        public this(normalizationCoef : double)
        {
            _normalizationCoef = normalizationCoef
        }
    }
}

UnitTypeBase.n:

using Nemerle.Utility;

namespace Oyster.Units
{
    /// Base class for all unit types
    abstract public class UnitTypeBase
    {
        /// Unit value as double
        [Accessor] _value : double;
        

        /// Constructs given unit type instance from any other unit type instance
        protected this(value : UnitTypeBase)
        {
            // Perform normalization-denormalization here
            this(value._value * value.NormalizationCoef / NormalizationCoef)
        }
        
        /// Constructs given unit type instance from double
        protected this(value : double)
        {
            _value = value
        }
        

        /// Unit type normalization coefficient
        abstract public NormalizationCoef : double { get; }
        
        /// ToString() override
        override public ToString() : string
        {
            _value.ToString()
        }
    }
}

CompileTimeUtil.n:

using System;
using System.Collections.Generic;
using Nemerle.Collections;
using Nemerle.Compiler;
using Nemerle.Compiler.Parsetree;
using Nemerle.Utility;

using NST = Nemerle.Compiler.NamespaceTree;

namespace Oyster.Units.Macros
{
    /// Used at compile time by Units macros - stores some intermediate information
    module CompileTimeUtil
    {
        /// Units NS name
        public UnitsNamespace = "Oyster.Units";

        /// Stores list of base units (such as mass or length) registered in the system
        public mutable BaseUnits : list[string];

        /// Stores list of all registered units including aliases
        public mutable AllUnits : list[string];

        /// Stores powers list for given base unit or alias
        public mutable PowersMap : Dictionary[string, list[int]] = Dictionary();
        

        /// Generates (or gets) given unit type with given or automatically generated name,
        /// given base units powers and given normalization coefficient
        public GetUnitType(env : GlobalEnv, typeName : string, baseUnitPowers : list[int], normalizationCoef : double) : TypeInfo
        {
            // Determine name of the type to be generated
            def typeName = match (typeName)
            {
                | null => Macros.NewSymbol("TempUnitType")
                | _ => Macros.UseSiteSymbol(typeName)
            };
            
            // Try to find this type by name among existing ones
            def unitType = env.LookupType([ typeName.Id ]);
            if (unitType.HasValue)
            {
                unitType.Value
            }
            else
            {
                // Generate new type
                def abstractTypeName = Macros.UseSiteSymbol(GetAbstractUnitType(baseUnitPowers).Name);
                def unitType = DefineInCustomNamespace(<[ decl:
                    [ConcreteUnitType($(normalizationCoef : double))]
                    public class $(typeName : name) : $(abstractTypeName : name)
                    {
                        public this(value : $(abstractTypeName : name))
                        {
                            base(value)
                        }
                        
                        public this(value : double)
                        {
                            base(value)
                        }
                        
                        
                        override public NormalizationCoef : double
                        {
                            get { $(normalizationCoef : double) }
                        }
                        
            
            static public @:>(value : $(typeName : name)) : double
            {
              value.Value
            }
            
            static public @:(value : double) : $(typeName : name)
            {
              $(typeName : name)(value)
            }


                        static public @*(unit : $(typeName : name), value : double) : $(typeName : name)
                        {
                            $(typeName : name)(unit.Value * value)
                        }

                        static public @/(unit : $(typeName : name), value : double) : $(typeName : name)
                        {
                            $(typeName : name)(unit.Value / value)
                        }

                        static public @+(unit : $(typeName : name), value : double) : $(typeName : name)
                        {
                            $(typeName : name)(unit.Value + value)
                        }

                        static public @-(unit : $(typeName : name), value : double) : $(typeName : name)
                        {
                            $(typeName : name)(unit.Value - value)
                        }


                        static public @*(value : double, unit : $(typeName : name)) : $(typeName : name)
                        {
                            $(typeName : name)(value * unit.Value)
                        }

                        static public @/(value : double, unit : $(typeName : name)) : $(typeName : name)
                        {
                            $(typeName : name)(value / unit.Value)
                        }

                        static public @+(value : double, unit : $(typeName : name)) : $(typeName : name)
                        {
                            $(typeName : name)(value + unit.Value)
                        }

                        static public @-(value : double, unit : $(typeName : name)) : $(typeName : name)
                        {
                            $(typeName : name)(value - unit.Value)
                        }


                        static public @++(value : $(typeName : name)) : $(typeName : name)
                        {
                            $(typeName : name)(value.Value + 1)
                        }

                        static public @--(value : $(typeName : name)) : $(typeName : name)
                        {
                            $(typeName : name)(value.Value - 1)
                        }

                        static public @+(value : $(typeName : name)) : $(typeName : name)
                        {
                            value
                        }

                        static public @-(value : $(typeName : name)) : $(typeName : name)
                        {
                            $(typeName : name)(0 - value.Value)
                        }
                    }
                ]>);

                unitType.Compile();
                unitType
            }
        }
        
        /// Generates (or gets) abstract unit type for given base unit powers
        GetAbstractUnitType(baseUnitPowers : list[int]) : TypeInfo
        {
            def env = GlobalEnv.Core;

            // Try to find given type
            def abstractTypeName = $"$UnitsNamespace.AbstractUnitType$(GenerateUnitTypeSuffix(baseUnitPowers))";
            def abstractType = env.LookupType([ abstractTypeName ]);
            if (abstractType.HasValue)
            {
                abstractType.Value
            }
            else
            {
                // Generate abstract type
                def abstractType = DefineInCustomNamespace(<[ decl:
                    abstract public class $(abstractTypeName : usesite) : UnitTypeBase
                    {
                        protected this(value : $(abstractTypeName : usesite))
                        {
                            base(value)
                        }
                        
                        protected this(value : double)
                        {
                            base(value)
                        }
                    }
                ]>);
                
                abstractType.Compile();
                abstractType
            }
        }


        /// Defines given class in custom global namespace
        public DefineInCustomNamespace(decl : ClassMember.TypeDeclaration) : TypeBuilder
        {
            def builder = GlobalEnv.Core.Define(decl);

            // Register newly created type manually - otherwise it will be unbound
            NST.ExactPath(NString.Split(builder.Name, '.')).Value = NST.TypeInfoCache.Cached(builder);

            builder
        }

        /// Increases power for given unit into list
        public ModifyBaseUnitPower(baseUnitPowers : list[int], unitName : string, f : int -> int) : list[int]
        {
            // Maybe prepare list full of zeroes
            def baseUnitPowers =
                if (baseUnitPowers.Length != 0) baseUnitPowers else List.Map(BaseUnits, fun(_) { 0 });

            List.Map2(BaseUnits, baseUnitPowers, fun(n, i) { if (n == unitName) f(i) else i })
        }

        /// Generates abstract unit type suffix basing on base unit powers
        public GenerateUnitTypeSuffix(baseUnitPowers : list[int]) : string
        {
            | [] => string.Empty
            | _ => "_" + baseUnitPowers.Head.ToString() + GenerateUnitTypeSuffix(baseUnitPowers.Tail)
        }

        /// Parses abstract unit type suffix
        public ParseUnitTypeSuffix(typeName : string) : list[int]
        {
            List.Map(NString.Split(typeName.Substring(typeName.IndexOf('_') + 1), '_'), int.Parse)
        }

        /// Returns PExpr type
        public GetExprType(ctx : Typer, expr : PExpr) : TypeInfo
        {
            ctx.TypeExpr(expr).ty.FixedValue.TypeInfo
        }

        /// Returns normalization coef if type is concrete unit type
        public GetUnitTypeCoef(t : TypeInfo) : option[double]
        {
            | _ is TypeBuilder =>
                // Get attribute using Nemerle approach
                mutable value = None();
                foreach (attr in t.GetModifiers().GetCustomAttributes())
                {
                    | <[ ConcreteUnitType($(coef : double)) ]> => value = Some(coef)
                };
                value
            | _ =>
                // "Usual" attribute gathering
                def attr = Attribute.GetCustomAttribute(t.SystemType, typeof(ConcreteUnitTypeAttribute));
                def attr = attr :> ConcreteUnitTypeAttribute;
                if (attr == null) None() else Some(attr.NormalizationCoef)
        }


        /// Used inside ariphmetic op macros
        public OpMacroBody(
            ctx : Typer,
            lexpr : PExpr,
            rexpr : PExpr,
            opName : string,
            opExpr : PExpr,
            someFun : TypeInfo * TypeInfo * double * double -> PExpr) : PExpr
        {
            def ltype = GetExprType(ctx, lexpr);
            def rtype = GetExprType(ctx, rexpr);
            match ((GetUnitTypeCoef(ltype), GetUnitTypeCoef(rtype)))
            {
                | (Some(lcoef), Some(rcoef)) =>
                    someFun(ltype, rtype, lcoef, rcoef)
                | _ =>
                    def macroPath = NST.ExactPath(["Oyster", "Units", "Macros", opName]);
                    def value = macroPath.Value;
                    macroPath.Value = NST.TypeInfoCache.No();
                    def resexpr = ctx.TypeExpr(opExpr);
                    macroPath.Value = value;
                    <[ $(resexpr : typed) ]>
            }
        }

        /// Used inside * and / macros
        public MulDivMacroBody(
            ctx : Typer,
            lexpr : PExpr,
            rexpr : PExpr,
            opName : string,
            opExpr : PExpr,
            powersFun : int * int -> int,
            coefFun : double * double -> double,
            coefOpExpr : PExpr) : PExpr
        {
            OpMacroBody(
                ctx,
                lexpr,
                rexpr,
                opName,
                opExpr,
                fun (ltype, rtype, lcoef, rcoef)
                {
                    def powers = List.Map2(
                        ParseUnitTypeSuffix(ltype.BaseType.Name),
                        ParseUnitTypeSuffix(rtype.BaseType.Name),
                        powersFun);
                    def concreteType = GetUnitType(ctx.Env, null, powers, 1.0);
                    <[ $(concreteType.FullName : usesite)(double.op_Multiply($coefOpExpr, $(coefFun(lcoef, rcoef) : double))) ]>
                })
        }

        /// Used inside + and - macros
        public AddSubMacroBody(
            ctx : Typer,
            lexpr : PExpr,
            rexpr : PExpr,
            opName : string,
            opExpr : PExpr,
            ctorFun : PExpr -> PExpr) : PExpr
        {
            OpMacroBody(
                ctx,
                lexpr,
                rexpr,
                opName,
                opExpr,
                fun (ltype, rtype, lcoef, rcoef)
                {
                    match (ltype.BaseType.Name)
                    {
                        | s when s == rtype.BaseType.Name =>
                            def coef = rcoef / lcoef;
                            def paramExpr = ctorFun(<[ double.op_Multiply($rexpr.Value, $(coef : double)) ]>);
                            <[ $(ltype.FullName : usesite)($paramExpr) ]>
                        | _ =>
                            Message.FatalError(
                                $"Expression { $lexpr [$opName] $rexpr } can't be compiled "
                                 "because units are having different dimensions.")
                    }
                })
        }
    }
}

UnitTypesMacro.n:

using System;
using System.Collections.Generic;
using Nemerle.Collections;
using Nemerle.Compiler;
using Nemerle.Macros;

using System.Console;

namespace Oyster.Units.Macros
{
    using CT = CompileTimeUtil;

    /// Declares unit types with convertion rules
    macro unittypes(namespaceName, body)
    syntax ("unittypes", namespaceName, body)
    {
        // Parse namespace name
        def namespaceName = match (namespaceName)
        {
            | <[ $(n : name) ]> => n.Id
            | _ => Message.FatalError($"Invalid unittypes syntax: expected namespace name part, got $namespaceName")
        };
        
        // Prepare normalization coefficients map
        def coefSubMap = Dictionary();
        
        // Parse units
        match (body)
        {
            | <[ { .. $unitDecls } ]> =>
                // Check if BaseUnits must be initialized
                def initBaseUnits = match (CT.BaseUnits)
                {
                    | null =>
                        CT.BaseUnits = [];
                        true
                    | _ => false
                };

                mutable units = [];
                
                // Process all phys types and add info into PhysTypeHelper
                mutable index = 0;
                mutable calcBaseUnitPowers = initBaseUnits;
                foreach (<[ $decl ]> in unitDecls)
                {
                    def unitCoef = match (decl)
                    {
                        | <[ $(n : name) ]> => (n.Id, 1.0)
                        | <[ $(n : name) : ($coefExpr) ]> =>
                            // Function parses simple coefficient exprs
                            def parseCoefExpr(expr)
                            {
                                | <[ $(d : double) ]> => d
                                | <[ $lhs * $(d : double) ]> => parseCoefExpr(lhs) * d
                                | <[ $lhs / $(d : double) ]> => parseCoefExpr(lhs) / d
                                | _ => Message.FatalError($"Invalid unittypes syntax: expected \"UnitName [: (coefficient expr)]\", got $decl")
                            };
                            (n.Id, parseCoefExpr(coefExpr))
                        | <[ $(n : name) = $aliasExpr ]> =>
                            // Aliases are started - remember it
                            when (calcBaseUnitPowers)
                            {
                                // Fill base units list
                                CT.BaseUnits = units.Reverse();

                                // Prepare powers lists
                                CT.BaseUnits.Iter(fun(unitName) { CT.PowersMap[unitName] = CT.ModifyBaseUnitPower([], unitName, _ + 1) });

                                calcBaseUnitPowers = false
                            };

                            // Prepare powers list
                            def oldN = n.Id == "SpecificGravity";
                            def parseAliasExpr(expr)
                            {
                                | <[ $(n : name) ^ $(x : int) ]> => List.Map(CT.PowersMap[n.Id], _ * x)
                                | <[ $lhs * $(n : name) ^ $(x : int) ]> =>
                                    List.Map2(parseAliasExpr(lhs), parseAliasExpr(<[ $(n : name) ^ $(x : int) ]>), _ + _)
                                | <[ $lhs / $(n : name) ^ $(x : int) ]> =>
                                    List.Map2(parseAliasExpr(lhs), parseAliasExpr(<[ $(n : name) ^ $(x : int) ]>), _ - _)
                                | <[ $(n : name) ]> => parseAliasExpr(<[ $(n : name) ^ 1 ]>)
                                | <[ $lhs * $(n : name) ]> => parseAliasExpr(<[ $lhs * $(n : name) ^ 1 ]>)
                                | <[ $lhs / $(n : name) ]> => parseAliasExpr(<[ $lhs / $(n : name) ^ 1 ]>)
                                | _ => Message.FatalError($"Invalid unittypes syntax: expected \"UnitName = alias\", got $decl")
                            };
                            CT.PowersMap[n.Id] = parseAliasExpr(aliasExpr);

                            (n.Id, 1.0)
                        | _ => Message.FatalError($"Invalid unittypes syntax: expected \"UnitName [: (coefficient expr)]\", got $decl")
                    };

                    units ::= unitCoef[0];

                    // Add new normalization coefficient into map
                    coefSubMap[unitCoef[0]] = unitCoef[1];

                    index++
                };

                units = units.Reverse();
                when (initBaseUnits)
                {
                    CT.AllUnits = units
                }

                // Generate corresponding concrete types
                def units = if (units.Length >= CT.BaseUnits.Length) CT.AllUnits else units;
                foreach (n in units)
                {
                    def powers = CT.PowersMap[n];

                    mutable coef = 0.0;
                    when (!coefSubMap.TryGetValue(n, out coef))
                    {
                        coef = 1.0;

                        // Calculate coefficient from base unit coefficients on the fly
                        List.Iter2(
                            powers,
                            CT.BaseUnits,
                            fun (power : int, name)
                            {
                                when (power != 0)
                                {
                                    def f = if (power > 0) _ * _ else _ / _;
                                    coef = f(coef, Math.Pow(coefSubMap[name], Math.Abs(power)));
                                }
                            })
                    }

                    _ = CT.GetUnitType(
                        GlobalEnv.Core,
                        $"$(CT.UnitsNamespace).$namespaceName.$n",
                        powers,
                        coef)
                }
            | _ => Message.FatalError($"Invalid unittypes syntax: expected semicolon-separated type names, got $body")
        };
        
        <[ () ]>
    }
}

OperatorMacros.n:

using Nemerle.Collections;
using Nemerle.Compiler;
using Nemerle.Compiler.Typedtree;
using Nemerle.Macros;

namespace Oyster.Units.Macros
{
    using CT = CompileTimeUtil;

    /// Multiplies two units. Inserts usual * if operands are of wrong type
    macro @*(lexpr, rexpr)
    {
        CT.MulDivMacroBody(
            ImplicitCTX(),
            lexpr,
            rexpr,
            "*",
            <[ $lexpr * $rexpr ]>,
            _ + _,
            _ * _,
            <[ double.op_Multiply($lexpr.Value, $rexpr.Value) ]>)
    }

    /// Divides two units. Inserts usual / if operands are of wrong type
    macro @/(lexpr, rexpr)
    {
        CT.MulDivMacroBody(
            ImplicitCTX(),
            lexpr,
            rexpr,
            "/",
            <[ $lexpr / $rexpr ]>,
            _ - _,
            _ / _,
            <[ double.op_Division($lexpr.Value, $rexpr.Value) ]>)
    }

    /// Sums two units. Inserts usual + if operands are of wrong type.
    /// Throws compiler error if unit dimensions are not equal
    macro @+(lexpr, rexpr)
    {
        CT.AddSubMacroBody(
            ImplicitCTX(),
            lexpr,
            rexpr,
            "+",
            <[ $lexpr + $rexpr ]>,
            fun(arg2Expr) { <[ double.op_Addition($lexpr.Value, $arg2Expr) ]> })
    }

    /// Subtracts two units. Inserts usual - if operands are of wrong type.
    /// Throws compiler error if unit dimensions are not equal
    macro @-(lexpr, rexpr)
    {
        CT.AddSubMacroBody(
            ImplicitCTX(),
            lexpr,
            rexpr,
            "-",
            <[ $lexpr - $rexpr ]>,
            fun(arg2Expr) { <[ double.op_Subtraction($lexpr.Value, $arg2Expr) ]> })
    }
}
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.