public static call HasBlock(this call kall) { if (kall.block != null && kall.block.calls.Count > 0) { return(kall); } throw ObjException.BlockRequired(kall); }
public static call NoBlock(this call kall) { if (kall.block != null && kall.block.calls.Count > 0) { throw ObjException.BlockNotAccepted(kall); } return(kall); }
public static call DomBuilt(this call kall, IDocument dom) { if (dom == null) { throw ObjException.MissingDomAndCalled(kall); } return(kall); }
public async Task eval(call kall) { bool hadResult = resultSet; bool addedResult = await funcs[kall.name](kall); if (hadResult && addedResult) { throw ObjException.ExpressionDiscarded(kall, trace.FindLast(x => true)); } hadResult = addedResult; trace.Add(kall); }
public static call ElementSelected(this call kall, obj env) { var lastCall = env.trace.Last(); if (lastCall.name == "sel" || lastCall.name == "id") { return(kall); throw ObjException.MissingElementAndCalled(kall); } return(kall); }
public static call ArityMatches(this call kall, int expected) { if (kall.args == null && expected == 0) { return(kall); } if (kall.args?.Count != expected) { throw ObjException.ArityMismatch(kall, 1); } return(kall); }
public static obj TopLevel() { var topLevel = new obj(); BrowsingContext ctx = new BrowsingContext(Configuration.Default.WithDefaultLoader()); IDocument dom = null; IElement el = null; topLevel.funcs["true"] = async(kall) => { Ensure.ArityMatches(kall, 0).NoBlock(); topLevel.result = true; return(true); }; topLevel.funcs["false"] = async(kall) => { Ensure.ArityMatches(kall, 0).NoBlock(); topLevel.result = false; return(true); }; topLevel.funcs["out"] = async(kall) => { Ensure.ArityMatches(kall, 1); topLevel.Output[kall.args[0]] = topLevel.result; topLevel.result = null; return(false); }; topLevel.funcs["all"] = async(kall) => { Ensure.HasBlock(kall); foreach (call k in kall.block.calls) { await topLevel.eval(k); if (topLevel.resultSet && topLevel.result is false) { topLevel.result = false; return(false); } } // If we didn't find any results set to false, it then set result to true topLevel.result = true; return(false); }; topLevel.funcs["any"] = async(kall) => { Ensure.HasBlock(kall); foreach (call k in kall.block.calls) { await topLevel.eval(k); if (topLevel.resultSet && topLevel.result is true) { return(false); } } // If we didn't find any truthy values, then topLevel.result = false; return(false); }; topLevel.funcs["none"] = async(kall) => { Ensure.ArityMatches(kall, 0).HasBlock(); foreach (call k in kall.block.calls) { await topLevel.eval(k); if (topLevel.resultSet && topLevel.result is true) { topLevel.result = false; return(false); } } // If we didn't find any truthy values, then this was true topLevel.result = true; return(false); }; topLevel.funcs["has-text"] = async(kall) => { Ensure.ArityMatches(kall, 1).NoBlock().ElementSelected(topLevel); topLevel.result = el.TextContent.Contains(kall.args[0]); return(true); }; topLevel.funcs["sel"] = async(kall) => { Ensure.ArityMatches(kall, 1).NoBlock().DomBuilt(dom); el = dom.QuerySelector(kall.args[0]); return(false); }; topLevel.funcs["id"] = async(kall) => { Ensure.ArityMatches(kall, 1).DomBuilt(dom).NoBlock(); el = dom.GetElementById(kall.args[0]); return(false); }; topLevel.funcs["site"] = async(kall) => { Ensure.ArityMatches(kall, 1); dom = await ctx.OpenAsync(kall.args[0]); var outName = kall.block?.calls?.FirstOrDefault(x => x.name == "out")?.args?[0] ?? kall.args[0]; if (dom == null) { var failMsg = ObjException.UrlFailed(kall).Message; topLevel.Output[$"{outName}-error"] = failMsg; topLevel.Output[outName] = false; return(false); } if (kall.block != null) { try { await kall.block.eval(topLevel); } catch (ObjException) { // This sort of exception needs to pass through, since it indicates code is constructed incorrectly. throw; } catch (Exception ex) { topLevel.Output[$"{outName}-error"] = ex.Message; // In this case, we don't know if the site is up or down, // just that something went wrong topLevel.Output[outName] = null; return(false); } } return(false); }; return(topLevel); }