Ejemplo n.º 1
0
        /// <summary>
        ///
        /// </summary>
        /// <param name="original"></param>
        /// <completionlist cref=""/>
        /// <example></example>
        /// <include file='' path='[@name=""]'/>
        /// <permission cref=""></permission>
        /// <remarks></remarks>
        /// <see cref=""/>
        /// <seealso cref=""/>
        /// <typeparam></typeparam>
        public Doc()
        {
            def = GenLanguageDef.Empty.With(
                CommentStart: null,
                CommentEnd: null,
                CommentLine: null,
                NestedComments: false,
                IdentStart: letter,
                IdentLetter: alphaNum,
                OpStart: oneOf(@"!%&*+.<=>?@/\^|-~"),
                OpLetter: oneOf(@"!%&*+.<=>?@/\^|-~"),
                ReservedOpNames: List <string>(),
                ReservedNames: List <string>(
                    "summary", "remarks", "param", "example", "permission",
                    "include", "completionlist", "see", "seealso"),
                CaseSensitive: true
                );

            lexer = makeTokenParser(def);
        }
        public ProcessSystemConfigParser(string nodeName, Types typeDefs, IEnumerable <FuncSpec> strategyFuncs)
        {
            strategyFuncs     = strategyFuncs ?? new FuncSpec[0];
            this.nodeName     = nodeName;
            this.types        = typeDefs;
            this.clusterType  = types.Register(BuildClusterType());
            this.processType  = types.Register(BuildProcessType());
            this.routerType   = types.Register(BuildRouterType());
            this.strategyType = types.Register(BuildStrategySpec(types, strategyFuncs));

            var opChars = ":!%&*+.<=>\\^|-~";

            // Process config definition
            Definition = GenLanguageDef.Empty.With(
                CommentStart: "/*",
                CommentEnd: "*/",
                CommentLine: "//",
                NestedComments: true,
                OpStart: oneOf(opChars),
                OpLetter: oneOf(opChars),
                IdentStart: letter,
                IdentLetter: either(alphaNum, oneOf("-_")),
                ReservedNames: List("if", "then", "else").AddRange(types.AllInOrder.Map(t => t.Name)),
                ReservedOpNames: List("-", "+", "/", "*", "==", "!=", ">", "<", "<=", ">=", "||", "&&", "|", "&", "%", "!", "~", "^")
                );

            // Token parser
            // This builds the standard token parser from the definition above
            TokenParser   = makeTokenParser(Definition);
            identifier    = TokenParser.Identifier;
            stringLiteral = TokenParser.StringLiteral;
            integer       = TokenParser.Integer;
            floating      = TokenParser.Float;
            natural       = TokenParser.Natural;
            whiteSpace    = TokenParser.WhiteSpace;
            symbol        = TokenParser.Symbol;
            reserved      = TokenParser.Reserved;
            reservedOp    = TokenParser.ReservedOp;

            // Binary operator parser
            Func <string, Assoc, Operator <ValueToken> > binary =
                (name, assoc) =>
                Operator.Infix(assoc,
                               from x in reservedOp(name)
                               select ValueToken.BinaryOp(name));

            // Prefix operator parser
            Func <string, Operator <ValueToken> > prefix =
                (name) =>
                Operator.Prefix(
                    from x in reservedOp(name)
                    select ValueToken.PrefixOp(name));

            // Postfix operator parser
            Func <string, Operator <ValueToken> > postfix =
                (name) =>
                Operator.Postfix(
                    from x in reservedOp(name)
                    select ValueToken.PostfixOp(name));

            // Operator table
            Operator <ValueToken>[][] table =
            {
                new [] { prefix("-"), prefix("+"), prefix("!") },
                new [] { binary("*",  Assoc.Left), binary("/", Assoc.Left), binary("%", Assoc.Left)},
                new [] { binary("+",  Assoc.Left), binary("-", Assoc.Left)},
                new [] { binary("<",  Assoc.Left), binary(">", Assoc.Left), binary(">=", Assoc.Left), binary("<=", Assoc.Left)},
                new [] { binary("==", Assoc.Left), binary("!=", Assoc.Left)},
                new [] { binary("&",  Assoc.Left) },
                new [] { binary("^",  Assoc.Left) },
                new [] { binary("|",  Assoc.Left) },
                new [] { binary("&&", Assoc.Left) },
                new [] { binary("||", Assoc.Left) },
            };


            Func <Option <string>, TypeDef, Parser <ValueToken> > valueInst = null;
            Parser <TypeDef> typeName = null;

            // ProcessId parser
            processId =
                token(
                    from xs in many1(choice(lower, digit, oneOf("@/[,-_]{}: ")))
                    let r                     = (new string(xs.ToArray())).Trim()
                                      let pid = ProcessId.TryParse(r)
                                                from res in pid.Match(
                        Right: x => result(x),
                        Left: ex => failure <ProcessId>($"{ex.Message} '({r})'"))
                                                select res);

            // ProcessName parser
            processName =
                token(
                    from o in symbol("\"")
                    from xs in many1(choice(lower, digit, oneOf("@/[,-_]{.}: ")))
                    from c in symbol("\"")
                    let r                 = (new string(xs.ToArray())).Trim()
                                    let n = ProcessName.TryParse(r)
                                            from res in n.Match(
                        Right: x => result(x),
                        Left: ex => failure <ProcessName>(ex.Message))
                                            select res);

            // Attribute parser
            Func <string, Parser <ValueToken>, Parser <NamedValueToken> > attr =
                (name, p) =>
                from x in reserved(name)
                from _ in symbol("=")
                from v in p
                select new NamedValueToken(name, v, None);

            // Type name parser
            Parser <Type> type =
                from x in letter
                from xs in many1(choice(letter, ch('.'), ch('_')))
                select Type.GetType(new string(x.Cons(xs).ToArray()));

            var directive    = types.Directive.ValueParser(this).Map(x => (Directive)x);
            var msgDirective = types.MessageDirective.ValueParser(this).Map(x => (MessageDirective)x);

            Parser <State <Exception, Option <Directive> > > exceptionDirective =
                from b in symbol("|")
                from t in token(type)
                from a in symbol("->")
                from d in directive
                select Strategy.With(d, t);

            Parser <State <Exception, Option <Directive> > > otherwiseDirective =
                from b in symbol("|")
                from t in symbol("_")
                from a in symbol("->")
                from d in directive
                select Strategy.Otherwise(d);

            Parser <State <Directive, Option <MessageDirective> > > matchMessageDirective =
                from b in symbol("|")
                from d in token(directive)
                from a in symbol("->")
                from m in token(msgDirective)
                select Strategy.When(m, d);

            Parser <State <Directive, Option <MessageDirective> > > otherwiseMsgDirective =
                from b in symbol("|")
                from t in symbol("_")
                from a in symbol("->")
                from d in token(msgDirective)
                select Strategy.Otherwise(d);

            // Strategy exception -> directive parser
            match =
                from _ in attempt(reserved("match"))
                from direx in many(attempt(exceptionDirective))
                from other in optional(otherwiseDirective)
                let dirs = direx.Append(other.AsEnumerable()).ToArray()
                           from ok in dirs.Length > 0
                    ? result(dirs)
                    : failure <State <Exception, Option <Directive> >[]>("'match' must be followed by at least one clause")
                           select new ValueToken(types.Get("strategy"), Strategy.Match(dirs));

            Parser <State <StrategyContext, Unit> > redirectMatch =
                from direx in many(attempt(matchMessageDirective))
                from other in optional(otherwiseMsgDirective)
                let dirs = direx.Append(other.AsEnumerable()).ToArray()
                           from ok in dirs.Length > 0
                    ? result(dirs)
                    : failure <State <Directive, Option <MessageDirective> >[]>("'redirect when' must be followed by at least one clause")
                           select Strategy.Redirect(dirs);

            // Strategy directive -> message-directive matching parser
            redirect =
                from n in attempt(reserved("redirect"))
                from t in either(attempt(symbol(":")), reserved("when"))
                from r in t == ":"
                   ? from d in token(msgDirective)
                select Strategy.Redirect(d)
                   : redirectMatch
                select new ValueToken(types.Get("strategy"), r);

            // Type name parser
            typeName = choice(types.AllInOrder.Map(t => reserved(t.Name).Map(_ => t)).ToArray());

            // cluster.<alias>.<property> parser -- TODO: generalise
            Parser <ValueToken> clusterVar =
                attempt(
                    from _ in reserved("cluster")
                    from tup in either(
                        attempt(
                            from d1 in symbol(".")
                            from alias in identifier
                            from d2 in symbol(".")
                            from id in identifier
                            select Tuple(alias, id)),
                        attempt(
                            from d in symbol(".")
                            from id in identifier
                            select Tuple("", id)))
                    from sub in optional(from d2 in symbol(".")
                                         from id2 in identifier
                                         select id2).Map(x => x.IfNone("value"))
                    from state in getState <ParserState>()                              // TODO: This can be generalised into an object walking system
                    from v in state.Clusters.Find(tup.Item1).Match(                     //       where an object (in this case the cluster), is in 'scope'
                        Some: cluster =>                                                //       and recursive walking of the dot operator will find the
                        cluster.Settings.Find(tup.Item2).Match(                         //       value.
                            Some: local => result(local),
                            None: () => failure <ValueToken>($"unknown identifier 'cluster.{tup.Item2}'")),
                        None: () => failure <ValueToken>($"cluster.{tup.Item2} used when a cluster with a node-name attribute set to '{nodeName}' hasn't been defined.  Or the cluster called '{nodeName}' exists and is aliased, and you're not providing the variable in the form: cluster.<alias>.<property-name>"))
                    select v
                    );

            // Variable of unknown type parser
            Parser <ValueToken> variable =
                either(
                    clusterVar,
                    attempt(
                        from id in identifier
                        from state in getState <ParserState>()
                        from v in state.Local(id).Match(
                            Some: v => result(v),
                            None: () => failure <ValueToken>($"unknown identifier '{id}' "))
                        select v
                        )
                    );

            // Variable of known type parser
            Func <TypeDef, Parser <ValueToken> > variableOfType =
                expect =>
                from v in variable
                from r in v.Type == expect
                        ? result(v)
                        : expect.Convert(v).Match(
                    Some: x => result(x),
                    None: () => failure <ValueToken>($"type mismatch {v.Type} found, expected {expect}")
                    )
                select r;

            Parser <ValueToken> ternary =
                token(
                    from __ in attempt(reserved("if"))
                    from eb in exprUnknownType
                    from ex in eb.Type == types.Bool
                        ? result(eb)
                        : failure <ValueToken>("ternary expressions must evaluate to a boolean value")
                    from th in reserved("then")
                    from te in exprUnknownType
                    from el in reserved("else")
                    from ee in exprUnknownType
                    select((bool)ex.Value) ? te : ee
                    );

            valueUntyped = choice(
                variable,
                choice(types.AllInOrder.Map(typ => attempt(typ.ValueParser(this).Map(val => new ValueToken(typ, typ.Ctor(None, val))))).ToArray())
                );

            // Expression term parser
            Parser <ValueToken> termUnknownType =
                choice(
                    parens(lazyp(() => exprUnknownType)),
                    ternary,
                    valueUntyped);

            // Expression parser
            exprUnknownType =
                buildExpressionParser(table, termUnknownType);

            // Variable declaration parser
            valueDef =
                from typ in either(
                    attempt(reserved("let")).Map(x => Option <TypeDef> .None),
                    typeName.Map(Option <TypeDef> .Some)
                    )
                from arr in optional(symbol("[]"))
                from _ in arr.IsSome && typ.IsNone
                    ? failure <Unit>("when declaring an array you must specify the type, you can't use 'let'")
                    : result <Unit>(unit)
                from id in identifier.label("identifier")
                from alias in optional(
                    from a_ in reserved("as")
                    from nm in identifier
                    select nm)
                from __ in symbol(":")
                from v in arr.IsSome
                    ? either(
                    attempt(valueInst(alias, TypeDef.Map(() => typ.IfNone(TypeDef.Unknown)))),
                    valueInst(alias, TypeDef.Array(() => typ.IfNone(TypeDef.Unknown))))
                    : typ.Map(t => expr(alias, t))
                .IfNone(() => exprUnknownType)
                from nv in result(new NamedValueToken(id, v, alias))
                from state in getState <ParserState>()
                from res in state.LocalExists(id)
                    ? failure <ParserState>($"A value with the name '{id}' already declared")
                    : result(state.AddLocal(id, v))
                from ___ in setState(res)
                select nv;

            // Value or variable parser
            valueInst = (alias, typ) => either(variableOfType(typ), typ.ValueParser(this).Map(value => new ValueToken(typ, typ.Ctor(alias, value))));

            // Expression term parser
            term =
                (alias, expected) =>
                choice(
                    parens(lazyp(() => expr(alias, expected))),
                    valueInst(alias, expected),
                    from val in exprUnknownType
                    from res in val.Type == expected
                            ? result(val)
                            : failure <ValueToken>($"expression must evaluate to {expected}, it actually evaluates to {val.Type}")
                    select res
                    );

            // Expression parser
            expr =
                (alias, expected) =>
                buildExpressionParser(table, term(alias, expected));

            // Parses a named argument: name = value
            namedArgument =
                (settingName, spec) =>
                attempt(token(attr(spec.Name, expr(None, spec.Type()))));

            // Parses a single non-named argument
            argument =
                (settingName, spec) =>
                attempt(token(expr(None, spec.Type()).Map(x => new NamedValueToken(spec.Name, x, None))));

            // Parses many arguments, wrapped in ( )
            argumentMany =
                (settingName, spec) =>
                from a in commaSep1(choice(spec.Map(arg => namedArgument(settingName, arg))))
                from r in a.Count == spec.Length
                        ? result(a)
                        : failure <Seq <NamedValueToken> >("Invalid arguments for " + settingName)
                select r;

            // Parses the arguments for a setting
            arguments =
                (settingName, spec) =>
                spec.Length == 0
                        ? failure <Seq <NamedValueToken> >("Invalid arguments spec, has zero arguments")
                        : spec.Length == 1
                            ? from a in argument(settingName, spec.Head())
                select SeqOne(a)
                            : argumentMany(settingName, spec);

            // Declare the global type
            var globalType = new TypeDef("global", (_, x) => x, typeof(Lst <NamedValueToken>), nodeName, 0);

            // Global namespace
            parser = from ws in whiteSpace
                     from __ in setState(ParserState.Empty)
                     from ss in globalType.ValueParser(this)
                     select(Lst <NamedValueToken>) ss;
        }