/// <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; }
static ScriptParser() { // All characters that can be operators var opChars = ":!%&*+.<=>\\^|-~()[]."; // Defines the components of the scripting language var definition = GenLanguageDef.Empty.With( CommentStart: "/*", CommentEnd: "*/", CommentLine: "//", NestedComments: true, OpStart: oneOf(opChars), OpLetter: oneOf(opChars), IdentStart: letter, IdentLetter: either(alphaNum, oneOf("-_")), ReservedNames: List("log", "fail", "Sum", "true", "false", "unit"), ReservedOpNames: List(";", "&&", "&", "-", "+", "/", "*", "==", "!=", "=", "<-", "<<", ">>", ">", "<", "<=", ">=", "=>", "||", "|", ",") ); // Takes the definition and builds a set of core parsers that // can be used to build more complex parsers. lexer = makeTokenParser(definition); // Take some local copies of the lexer parsers for ease of use later charlit = lexer.CharLiteral; ident = either( lexer.Identifier, from t in token(letter) from _ in notFollowedBy(letter) select t.ToString()); reserved = lexer.Reserved; reservedOp = lexer.ReservedOp; symbol = lexer.Symbol; whiteSpace = lexer.WhiteSpace; // This incidentally will also strip comments integer = lexer.Integer; floating = lexer.Float; stringlit = lexer.StringLiteral; // Helper method that takes an operator string and returns a Func // that makes it easy to build ScriptExprs that represent binary // operators Func <ScriptExpr, ScriptExpr, ScriptExpr> BinaryOp(string op) => (ScriptExpr lhs, ScriptExpr rhs) => op == "-" ? Binary(lhs, rhs, "-") : op == "+" ? Binary(lhs, rhs, "+") : op == "/" ? Binary(lhs, rhs, "/") : op == "*" ? Binary(lhs, rhs, "*") : op == "==" ? BooleanBinary(lhs, rhs, "==") : op == "!=" ? BooleanBinary(lhs, rhs, "!=") : op == "=" ? Binary(lhs, rhs, "=") : op == "<-" ? Binary(lhs, rhs, "<-") : op == ">" ? BooleanBinary(lhs, rhs, ">") : op == "<" ? BooleanBinary(lhs, rhs, "<") : op == ">=" ? BooleanBinary(lhs, rhs, ">=") : op == "<=" ? BooleanBinary(lhs, rhs, "<=") : op == "&&" ? BooleanBinary(lhs, rhs, "&&") : op == "||" ? BooleanBinary(lhs, rhs, "||") : op == "%" ? Binary(lhs, rhs, "%") : op == "??" ? Binary(lhs, rhs, "??") : op == ";" ? Binary(lhs, rhs, ";") : op == "," ? Args(lhs, rhs) : throw new NotSupportedException(); // Helper method that takes an operator string and returns a Func // that makes it easy to build ScriptExprs that represent unary // prefix operators Func <ScriptExpr, ScriptExpr> PrefixOp(string op) => (ScriptExpr rhs) => op == "!" ? BooleanPrefix(rhs, "!") : op == "-" ? Prefix(rhs, "-") : op == "+" ? rhs : throw new NotSupportedException(); // Binary operator parser definition - this is used in the operators table // to define precendence and associativity. Operator <ScriptExpr> binary(string name, Assoc assoc) => Operator.Infix(assoc, from x in reservedOp(name) select BinaryOp(name)); // Unary prefix operator parser definition - this is used in the operators // table to define precendence and associativity. Operator <ScriptExpr> prefix(string name) => Operator.Prefix( from x in reservedOp(name) select PrefixOp(name)); // Operator precedence and associativity table Operator <ScriptExpr>[][] operators = { 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)}, 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) }, new [] { binary(">>=", Assoc.Right) }, new [] { binary("?", Assoc.Left) }, new [] { binary(":", Assoc.Right) }, new [] { binary("=", Assoc.Right), binary("<-", Assoc.Right)}, new [] { binary(".", Assoc.Right) }, new [] { binary(",", Assoc.Right) }, new [] { binary("=>", Assoc.Right) }, new [] { binary(";", Assoc.Right) }, }; Parser <ScriptExpr> expr = null; Parser <ScriptExpr> lazyExpr = lazyp(() => expr); // Constant or interpolated string parser var interpStr = from str in attempt(stringlit).label("string literal") from res2 in (str.Contains('{') && str.Contains('}')) ? from istr in result(Interpolate.Parser(str.ToPString())) from res in istr.IsFaulted ? failure <ScriptExpr>(istr.Reply.Error.Msg) : result(istr.Reply.Result) select res : result(ConstString(str)) select res2; // Constant parser var constant = choice( attempt(integer.Map(Const).label("integer")), attempt(charlit.Map(ConstChar).label("char literal")), attempt(interpStr), attempt(floating.Map(Const).label("float")), attempt(symbol("true").Map(_ => Const(true)).label("true")), symbol("false").Map(_ => Const(false)).label("false")); // Function invocation parser var invoke = from id in ident from ar in either( attempt(symbol("()")).Map(_ => Seq <ScriptExpr>()), from op in symbol("(") from args in lazyExpr from cl in symbol(")") select FlattenTuple(args)) select Invoke(id, ar); // log function parser var log = from _ in symbol("log") from o in symbol("(") from m in lazyExpr from c in symbol(")") select Log(m); // fail function parser var fail = from _ in symbol("fail") from o in symbol("(") from m in lazyExpr from c in symbol(")") select Fail(m); // Term parser - this is the main building block for the expression parser // all language terms must be definied here. var term = choice( attempt(log), attempt(fail), attempt(invoke), attempt(constant), attempt(ident).Map(Ident), token(lexer.Parens(lazyExpr)).Map(Parens)); // This takes the operator precedence and associativity table and the term // parser and builds a full expression parser that deals with all of the // complex rules over precedence and associativity. This is a wonderful // function that saves lives. expr = buildExpressionParser(operators.Reverse().ToArray(), term); // This is the final parser. Because the tokens and the symbols all strip // the space *after* their content has been parsed, the final parser must // strip the space *before* the first token. So, that's what this does, // removes the space, parses the expression, then it expects an end-of-file parser = from ws in whiteSpace from ex in expr from __ in eof select ex; }
public Reader(Query <Q> alg) { this.alg = alg; lexer = makeTokenParser(GenLanguageDef.Empty); Parser <Seq <char> > toend(Parser <char> a) => manyUntil(a, ch(';')); var floating = from x in asString(many1(digit)) from y in either(attempt(from _ in either(attempt(ch('.')), ch(',')) from y in asString(many1(digit)) select(y)), result("0")) let opt = parseDouble($"{x},{y}") from res in opt.Match( x => result(x), () => failure <double>("not a valid number")) select(res); searchName = from _ in str("find") from __ in spaces from ___ in str("name") from ____ in spaces from name in asString(toend(anyChar)) select(alg.Name(name)); searchAll = from _ in str("find") from __ in spaces from ___ in str("all") from ____ in ch(';') select(alg.All()); searchFuzzyName = from _ in str("find") from __ in spaces from ___ in str("fuzzy") from ____ in spaces from _____ in str("name") from ______ in spaces from name in asString(toend(anyChar)) select(alg.FuzzyName(name)); Func <string, Parser <string> > findEarningX = p => from _ in str("find") from __ in spaces from ___ in str("earning") from ____ in spaces from _____ in str(p) from ______ in spaces from _______ in str("than") select(""); searchEarningMore = from _ in findEarningX("more") from __ in spaces from salary in floating from ___ in ch(';') select(alg.EarningMore(salary)); searchEarningLess = from _ in findEarningX("less") from __ in spaces from salary in floating from ___ in ch(';') select(alg.EarningLess(salary)); searchEarning = from _ in str("find") from __ in spaces from ___ in str("earning") from ____ in spaces from salary in floating select(alg.Earning(salary)); newEntryWithId = from _ in str("add") from __ in spaces from ___ in str("earning") from ____ in spaces from salary in floating from _____ in spaces from ______ in str("id") from _______ in spaces from ido in asInteger(many1(digit)) from id in ido.Match( x => result(x), () => failure <int>("")) from ________ in spaces from _________ in str("name") from __________ in spaces from name in asString(toend(anyChar)) select(alg.NewEntryWithId(name, id, salary)); newEntry = from _ in str("add") from __ in spaces from ___ in str("earning") from ____ in spaces from salary in floating from _____ in spaces from ______ in str("name") from _______ in spaces from name in asString(toend(anyChar)) select(alg.NewEntry(name, salary)); removeEntry = from _ in str("remove") from __ in spaces from ___ in str("id") from ____ in spaces from id in lexer.Integer from _____ in spaces from ______ in str("name") from _______ in spaces from name in asString(toend(anyChar)) select(alg.RemoveEntry(name, id)); save = from _ in str("save") from __ in spaces from filename in asString(many1(alphaNum)) from ext in str(".txt") from ___ in ch(';') select(alg.Save(filename + ext)); load = from _ in str("load") from __ in spaces from filename in asString(many1(alphaNum)) from ext in str(".txt") from ___ in ch(';') select(alg.Load(filename + ext)); clear = from _ in str("clear") from __ in ch(';') select(alg.Clear()); }