mirror of
https://github.com/MontFerret/ferret.git
synced 2024-12-12 11:15:14 +02:00
Added possibility to use FOR loop in ternary expression
This commit is contained in:
parent
ef29241aa6
commit
8b2e210317
24
docs/examples/input.fql
Normal file
24
docs/examples/input.fql
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
LET g = DOCUMENT("https://www.google.com/", true)
|
||||||
|
LET inputBox = ELEMENT(g, 'input[name="q"]')
|
||||||
|
|
||||||
|
INPUT(inputBox, "ferrer")
|
||||||
|
|
||||||
|
LET searchBtn = ELEMENT(g, 'input[name="btnK"]')
|
||||||
|
|
||||||
|
CLICK(searchBtn)
|
||||||
|
|
||||||
|
WAIT_NAVIGATION(g)
|
||||||
|
|
||||||
|
LET result = ELEMENTS(g, '.g')
|
||||||
|
|
||||||
|
LOG(result ? 'element' : 'no element')
|
||||||
|
|
||||||
|
RETURN result ? (
|
||||||
|
FOR result IN ELEMENTS(g, '.g')
|
||||||
|
LOG('iterate')
|
||||||
|
RETURN {
|
||||||
|
title: ELEMENT(result, 'h3 > a'),
|
||||||
|
description: ELEMENT(result, '.st'),
|
||||||
|
url: ELEMENT(result, 'cite')
|
||||||
|
}
|
||||||
|
) : 'no results'
|
@ -90,3 +90,13 @@ func (c *FqlCompiler) Compile(query string) (program *runtime.Program, err error
|
|||||||
|
|
||||||
return program, err
|
return program, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *FqlCompiler) CompileP(query string) *runtime.Program {
|
||||||
|
program, err := c.Compile(query)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return program
|
||||||
|
}
|
||||||
|
@ -1602,6 +1602,92 @@ func TestInOperator(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestForTernaryExpression(t *testing.T) {
|
||||||
|
Convey("RETURN foo ? TRUE : (FOR i IN 1..5 RETURN i*2)", t, func() {
|
||||||
|
c := compiler.New()
|
||||||
|
|
||||||
|
out1, err := c.CompileP(`
|
||||||
|
LET foo = FALSE
|
||||||
|
RETURN foo ? TRUE : (FOR i IN 1..5 RETURN i*2)
|
||||||
|
`).Run(context.Background())
|
||||||
|
|
||||||
|
So(err, ShouldBeNil)
|
||||||
|
So(string(out1), ShouldEqual, `[2,4,6,8,10]`)
|
||||||
|
|
||||||
|
out2, err := c.CompileP(`
|
||||||
|
LET foo = TRUE
|
||||||
|
RETURN foo ? TRUE : (FOR i IN 1..5 RETURN i*2)
|
||||||
|
`).Run(context.Background())
|
||||||
|
|
||||||
|
So(err, ShouldBeNil)
|
||||||
|
So(string(out2), ShouldEqual, `true`)
|
||||||
|
})
|
||||||
|
|
||||||
|
Convey("RETURN foo ? (FOR i IN 1..5 RETURN i) : (FOR i IN 1..5 RETURN i*2)", t, func() {
|
||||||
|
c := compiler.New()
|
||||||
|
|
||||||
|
out1, err := c.CompileP(`
|
||||||
|
LET foo = FALSE
|
||||||
|
RETURN foo ? (FOR i IN 1..5 RETURN i) : (FOR i IN 1..5 RETURN i*2)
|
||||||
|
`).Run(context.Background())
|
||||||
|
|
||||||
|
So(err, ShouldBeNil)
|
||||||
|
So(string(out1), ShouldEqual, `[2,4,6,8,10]`)
|
||||||
|
|
||||||
|
out2, err := c.CompileP(`
|
||||||
|
LET foo = TRUE
|
||||||
|
RETURN foo ? (FOR i IN 1..5 RETURN i) : (FOR i IN 1..5 RETURN i*2)
|
||||||
|
`).Run(context.Background())
|
||||||
|
|
||||||
|
So(err, ShouldBeNil)
|
||||||
|
So(string(out2), ShouldEqual, `[1,2,3,4,5]`)
|
||||||
|
})
|
||||||
|
|
||||||
|
Convey("LET res = foo ? TRUE : (FOR i IN 1..5 RETURN i*2)", t, func() {
|
||||||
|
c := compiler.New()
|
||||||
|
|
||||||
|
out1, err := c.CompileP(`
|
||||||
|
LET foo = FALSE
|
||||||
|
LET res = foo ? TRUE : (FOR i IN 1..5 RETURN i*2)
|
||||||
|
RETURN res
|
||||||
|
`).Run(context.Background())
|
||||||
|
|
||||||
|
So(err, ShouldBeNil)
|
||||||
|
So(string(out1), ShouldEqual, `[2,4,6,8,10]`)
|
||||||
|
|
||||||
|
out2, err := c.CompileP(`
|
||||||
|
LET foo = TRUE
|
||||||
|
LET res = foo ? TRUE : (FOR i IN 1..5 RETURN i*2)
|
||||||
|
RETURN res
|
||||||
|
`).Run(context.Background())
|
||||||
|
|
||||||
|
So(err, ShouldBeNil)
|
||||||
|
So(string(out2), ShouldEqual, `true`)
|
||||||
|
})
|
||||||
|
|
||||||
|
Convey("LET res = foo ? (FOR i IN 1..5 RETURN i) : (FOR i IN 1..5 RETURN i*2)", t, func() {
|
||||||
|
c := compiler.New()
|
||||||
|
|
||||||
|
out1, err := c.CompileP(`
|
||||||
|
LET foo = FALSE
|
||||||
|
LET res = foo ? (FOR i IN 1..5 RETURN i) : (FOR i IN 1..5 RETURN i*2)
|
||||||
|
RETURN res
|
||||||
|
`).Run(context.Background())
|
||||||
|
|
||||||
|
So(err, ShouldBeNil)
|
||||||
|
So(string(out1), ShouldEqual, `[2,4,6,8,10]`)
|
||||||
|
|
||||||
|
out2, err := c.CompileP(`
|
||||||
|
LET foo = TRUE
|
||||||
|
LET res = foo ? (FOR i IN 1..5 RETURN i) : (FOR i IN 1..5 RETURN i*2)
|
||||||
|
RETURN res
|
||||||
|
`).Run(context.Background())
|
||||||
|
|
||||||
|
So(err, ShouldBeNil)
|
||||||
|
So(string(out2), ShouldEqual, `[1,2,3,4,5]`)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
//func TestHtml(t *testing.T) {
|
//func TestHtml(t *testing.T) {
|
||||||
// Convey("Should load a document", t, func() {
|
// Convey("Should load a document", t, func() {
|
||||||
// c := compiler.New()
|
// c := compiler.New()
|
||||||
|
@ -117,7 +117,13 @@ func (v *visitor) doVisitReturnExpression(ctx *fql.ReturnExpressionContext, scop
|
|||||||
}
|
}
|
||||||
|
|
||||||
exp = out
|
exp = out
|
||||||
} else {
|
|
||||||
|
return expressions.NewReturnExpression(v.getSourceMap(ctx), exp)
|
||||||
|
}
|
||||||
|
|
||||||
|
forIn := ctx.ForExpression()
|
||||||
|
|
||||||
|
if forIn != nil {
|
||||||
out, err := v.doVisitForExpression(ctx.ForExpression().(*fql.ForExpressionContext), scope.Fork())
|
out, err := v.doVisitForExpression(ctx.ForExpression().(*fql.ForExpressionContext), scope.Fork())
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -125,9 +131,23 @@ func (v *visitor) doVisitReturnExpression(ctx *fql.ReturnExpressionContext, scop
|
|||||||
}
|
}
|
||||||
|
|
||||||
exp = out
|
exp = out
|
||||||
|
|
||||||
|
return expressions.NewReturnExpression(v.getSourceMap(ctx), exp)
|
||||||
}
|
}
|
||||||
|
|
||||||
return expressions.NewReturnExpression(v.getSourceMap(ctx), exp)
|
forInTernary := ctx.ForTernaryExpression()
|
||||||
|
|
||||||
|
if forInTernary != nil {
|
||||||
|
out, err := v.doVisitForTernaryExpression(forInTernary.(*fql.ForTernaryExpressionContext), scope)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return expressions.NewReturnExpression(v.getSourceMap(ctx), out)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, ErrNotImplemented
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *visitor) doVisitForExpression(ctx *fql.ForExpressionContext, scope *scope) (core.Expression, error) {
|
func (v *visitor) doVisitForExpression(ctx *fql.ForExpressionContext, scope *scope) (core.Expression, error) {
|
||||||
@ -609,7 +629,9 @@ func (v *visitor) doVisitVariableDeclaration(ctx *fql.VariableDeclarationContext
|
|||||||
|
|
||||||
if exp != nil {
|
if exp != nil {
|
||||||
init, err = v.doVisitExpression(ctx.Expression().(*fql.ExpressionContext), scope)
|
init, err = v.doVisitExpression(ctx.Expression().(*fql.ExpressionContext), scope)
|
||||||
} else {
|
}
|
||||||
|
|
||||||
|
if init == nil && err == nil {
|
||||||
forIn := ctx.ForExpression()
|
forIn := ctx.ForExpression()
|
||||||
|
|
||||||
if forIn != nil {
|
if forIn != nil {
|
||||||
@ -617,6 +639,14 @@ func (v *visitor) doVisitVariableDeclaration(ctx *fql.VariableDeclarationContext
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if init == nil && err == nil {
|
||||||
|
forTer := ctx.ForTernaryExpression()
|
||||||
|
|
||||||
|
if forTer != nil {
|
||||||
|
init, err = v.doVisitForTernaryExpression(forTer.(*fql.ForTernaryExpressionContext), scope)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -661,16 +691,22 @@ func (v *visitor) doVisitChildren(node antlr.RuleNode, scope *scope) ([]core.Exp
|
|||||||
return make([]core.Expression, 0, 0), nil
|
return make([]core.Expression, 0, 0), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
result := make([]core.Expression, len(children))
|
result := make([]core.Expression, 0, len(children))
|
||||||
|
|
||||||
|
for _, child := range children {
|
||||||
|
_, ok := child.(antlr.TerminalNode)
|
||||||
|
|
||||||
|
if ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
for idx, child := range children {
|
|
||||||
out, err := v.visit(child, scope)
|
out, err := v.visit(child, scope)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
result[idx] = out
|
result = append(result, out)
|
||||||
}
|
}
|
||||||
|
|
||||||
return result, nil
|
return result, nil
|
||||||
@ -794,24 +830,10 @@ func (v *visitor) doVisitExpression(ctx *fql.ExpressionContext, scope *scope) (c
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
var test core.Expression
|
return v.createTernaryOperator(
|
||||||
var consequent core.Expression
|
|
||||||
var alternate core.Expression
|
|
||||||
|
|
||||||
if len(exps) == 3 {
|
|
||||||
test = exps[0]
|
|
||||||
consequent = exps[1]
|
|
||||||
alternate = exps[2]
|
|
||||||
} else {
|
|
||||||
test = exps[0]
|
|
||||||
alternate = exps[1]
|
|
||||||
}
|
|
||||||
|
|
||||||
return expressions.NewConditionExpression(
|
|
||||||
v.getSourceMap(ctx),
|
v.getSourceMap(ctx),
|
||||||
test,
|
exps,
|
||||||
consequent,
|
scope,
|
||||||
alternate,
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -860,14 +882,6 @@ func (v *visitor) doVisitExpression(ctx *fql.ExpressionContext, scope *scope) (c
|
|||||||
return v.doVisitRangeOperator(rangeOp.(*fql.RangeOperatorContext), scope)
|
return v.doVisitRangeOperator(rangeOp.(*fql.RangeOperatorContext), scope)
|
||||||
}
|
}
|
||||||
|
|
||||||
seq := ctx.ExpressionSequence()
|
|
||||||
|
|
||||||
if seq != nil {
|
|
||||||
// seq := seq.(*fql.ExpressionSequenceContext)
|
|
||||||
|
|
||||||
return nil, core.Error(ErrNotImplemented, "expression sequence")
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Complete it
|
// TODO: Complete it
|
||||||
return nil, ErrNotImplemented
|
return nil, ErrNotImplemented
|
||||||
}
|
}
|
||||||
@ -981,6 +995,42 @@ func (v *visitor) visit(node antlr.Tree, scope *scope) (core.Expression, error)
|
|||||||
return out, err
|
return out, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (v *visitor) doVisitForTernaryExpression(ctx *fql.ForTernaryExpressionContext, scope *scope) (*expressions.ConditionExpression, error) {
|
||||||
|
exps, err := v.doVisitChildren(ctx, scope)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return v.createTernaryOperator(
|
||||||
|
v.getSourceMap(ctx),
|
||||||
|
exps,
|
||||||
|
scope,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *visitor) createTernaryOperator(src core.SourceMap, exps []core.Expression, scope *scope) (*expressions.ConditionExpression, error) {
|
||||||
|
var test core.Expression
|
||||||
|
var consequent core.Expression
|
||||||
|
var alternate core.Expression
|
||||||
|
|
||||||
|
if len(exps) == 3 {
|
||||||
|
test = exps[0]
|
||||||
|
consequent = exps[1]
|
||||||
|
alternate = exps[2]
|
||||||
|
} else {
|
||||||
|
test = exps[0]
|
||||||
|
alternate = exps[1]
|
||||||
|
}
|
||||||
|
|
||||||
|
return expressions.NewConditionExpression(
|
||||||
|
src,
|
||||||
|
test,
|
||||||
|
consequent,
|
||||||
|
alternate,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
func (v *visitor) unexpectedToken(node antlr.Tree) error {
|
func (v *visitor) unexpectedToken(node antlr.Tree) error {
|
||||||
name := "undefined"
|
name := "undefined"
|
||||||
ctx, ok := node.(antlr.RuleContext)
|
ctx, ok := node.(antlr.RuleContext)
|
||||||
|
@ -23,6 +23,7 @@ bodyExpression
|
|||||||
returnExpression
|
returnExpression
|
||||||
: Return (Distinct)? expression
|
: Return (Distinct)? expression
|
||||||
| Return (Distinct)? OpenParen forExpression CloseParen
|
| Return (Distinct)? OpenParen forExpression CloseParen
|
||||||
|
| Return forTernaryExpression
|
||||||
;
|
;
|
||||||
|
|
||||||
forExpression
|
forExpression
|
||||||
@ -124,6 +125,7 @@ forExpressionReturn
|
|||||||
variableDeclaration
|
variableDeclaration
|
||||||
: Let Identifier Assign expression
|
: Let Identifier Assign expression
|
||||||
| Let Identifier Assign OpenParen forExpression CloseParen
|
| Let Identifier Assign OpenParen forExpression CloseParen
|
||||||
|
| Let Identifier Assign forTernaryExpression
|
||||||
;
|
;
|
||||||
|
|
||||||
variable
|
variable
|
||||||
@ -225,6 +227,12 @@ expression
|
|||||||
| noneLiteral
|
| noneLiteral
|
||||||
;
|
;
|
||||||
|
|
||||||
|
forTernaryExpression
|
||||||
|
: expression QuestionMark expression? Colon OpenParen forExpression CloseParen
|
||||||
|
| expression QuestionMark OpenParen forExpression CloseParen Colon expression
|
||||||
|
| expression QuestionMark OpenParen forExpression CloseParen Colon OpenParen forExpression CloseParen
|
||||||
|
;
|
||||||
|
|
||||||
equalityOperator
|
equalityOperator
|
||||||
: Gt
|
: Gt
|
||||||
| Lt
|
| Lt
|
||||||
@ -249,7 +257,7 @@ mathOperator
|
|||||||
|
|
||||||
unaryOperator
|
unaryOperator
|
||||||
: Not
|
: Not
|
||||||
| Plus
|
| Plus
|
||||||
| Minus
|
| Minus
|
||||||
| Like
|
| Like
|
||||||
;
|
;
|
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
@ -288,6 +288,12 @@ func (s *BaseFqlParserListener) EnterExpression(ctx *ExpressionContext) {}
|
|||||||
// ExitExpression is called when production expression is exited.
|
// ExitExpression is called when production expression is exited.
|
||||||
func (s *BaseFqlParserListener) ExitExpression(ctx *ExpressionContext) {}
|
func (s *BaseFqlParserListener) ExitExpression(ctx *ExpressionContext) {}
|
||||||
|
|
||||||
|
// EnterForTernaryExpression is called when production forTernaryExpression is entered.
|
||||||
|
func (s *BaseFqlParserListener) EnterForTernaryExpression(ctx *ForTernaryExpressionContext) {}
|
||||||
|
|
||||||
|
// ExitForTernaryExpression is called when production forTernaryExpression is exited.
|
||||||
|
func (s *BaseFqlParserListener) ExitForTernaryExpression(ctx *ForTernaryExpressionContext) {}
|
||||||
|
|
||||||
// EnterEqualityOperator is called when production equalityOperator is entered.
|
// EnterEqualityOperator is called when production equalityOperator is entered.
|
||||||
func (s *BaseFqlParserListener) EnterEqualityOperator(ctx *EqualityOperatorContext) {}
|
func (s *BaseFqlParserListener) EnterEqualityOperator(ctx *EqualityOperatorContext) {}
|
||||||
|
|
||||||
|
@ -183,6 +183,10 @@ func (v *BaseFqlParserVisitor) VisitExpression(ctx *ExpressionContext) interface
|
|||||||
return v.VisitChildren(ctx)
|
return v.VisitChildren(ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (v *BaseFqlParserVisitor) VisitForTernaryExpression(ctx *ForTernaryExpressionContext) interface{} {
|
||||||
|
return v.VisitChildren(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
func (v *BaseFqlParserVisitor) VisitEqualityOperator(ctx *EqualityOperatorContext) interface{} {
|
func (v *BaseFqlParserVisitor) VisitEqualityOperator(ctx *EqualityOperatorContext) interface{} {
|
||||||
return v.VisitChildren(ctx)
|
return v.VisitChildren(ctx)
|
||||||
}
|
}
|
||||||
|
@ -139,6 +139,9 @@ type FqlParserListener interface {
|
|||||||
// EnterExpression is called when entering the expression production.
|
// EnterExpression is called when entering the expression production.
|
||||||
EnterExpression(c *ExpressionContext)
|
EnterExpression(c *ExpressionContext)
|
||||||
|
|
||||||
|
// EnterForTernaryExpression is called when entering the forTernaryExpression production.
|
||||||
|
EnterForTernaryExpression(c *ForTernaryExpressionContext)
|
||||||
|
|
||||||
// EnterEqualityOperator is called when entering the equalityOperator production.
|
// EnterEqualityOperator is called when entering the equalityOperator production.
|
||||||
EnterEqualityOperator(c *EqualityOperatorContext)
|
EnterEqualityOperator(c *EqualityOperatorContext)
|
||||||
|
|
||||||
@ -283,6 +286,9 @@ type FqlParserListener interface {
|
|||||||
// ExitExpression is called when exiting the expression production.
|
// ExitExpression is called when exiting the expression production.
|
||||||
ExitExpression(c *ExpressionContext)
|
ExitExpression(c *ExpressionContext)
|
||||||
|
|
||||||
|
// ExitForTernaryExpression is called when exiting the forTernaryExpression production.
|
||||||
|
ExitForTernaryExpression(c *ForTernaryExpressionContext)
|
||||||
|
|
||||||
// ExitEqualityOperator is called when exiting the equalityOperator production.
|
// ExitEqualityOperator is called when exiting the equalityOperator production.
|
||||||
ExitEqualityOperator(c *EqualityOperatorContext)
|
ExitEqualityOperator(c *EqualityOperatorContext)
|
||||||
|
|
||||||
|
@ -139,6 +139,9 @@ type FqlParserVisitor interface {
|
|||||||
// Visit a parse tree produced by FqlParser#expression.
|
// Visit a parse tree produced by FqlParser#expression.
|
||||||
VisitExpression(ctx *ExpressionContext) interface{}
|
VisitExpression(ctx *ExpressionContext) interface{}
|
||||||
|
|
||||||
|
// Visit a parse tree produced by FqlParser#forTernaryExpression.
|
||||||
|
VisitForTernaryExpression(ctx *ForTernaryExpressionContext) interface{}
|
||||||
|
|
||||||
// Visit a parse tree produced by FqlParser#equalityOperator.
|
// Visit a parse tree produced by FqlParser#equalityOperator.
|
||||||
VisitEqualityOperator(ctx *EqualityOperatorContext) interface{}
|
VisitEqualityOperator(ctx *EqualityOperatorContext) interface{}
|
||||||
|
|
||||||
|
@ -40,3 +40,13 @@ func (p *Program) Run(ctx context.Context, setters ...Option) ([]byte, error) {
|
|||||||
|
|
||||||
return out.MarshalJSON()
|
return out.MarshalJSON()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *Program) RunP(ctx context.Context, setters ...Option) []byte {
|
||||||
|
out, err := p.Run(ctx, setters...)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
@ -67,6 +67,14 @@ func GetIn(from core.Value, byPath []core.Value) (core.Value, error) {
|
|||||||
result = el.GetChildNodes()
|
result = el.GetChildNodes()
|
||||||
case "length":
|
case "length":
|
||||||
result = el.Length()
|
result = el.Length()
|
||||||
|
case "url":
|
||||||
|
if result.Type() == core.HtmlDocumentType {
|
||||||
|
doc, ok := result.(HtmlDocument)
|
||||||
|
|
||||||
|
if ok {
|
||||||
|
result = doc.Url()
|
||||||
|
}
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
result = None
|
result = None
|
||||||
}
|
}
|
||||||
|
@ -2,30 +2,38 @@ package values
|
|||||||
|
|
||||||
import "github.com/MontFerret/ferret/pkg/runtime/core"
|
import "github.com/MontFerret/ferret/pkg/runtime/core"
|
||||||
|
|
||||||
type HtmlNode interface {
|
type (
|
||||||
core.Value
|
HtmlNode interface {
|
||||||
|
core.Value
|
||||||
|
|
||||||
NodeType() Int
|
NodeType() Int
|
||||||
|
|
||||||
NodeName() String
|
NodeName() String
|
||||||
|
|
||||||
Length() Int
|
Length() Int
|
||||||
|
|
||||||
InnerText() String
|
InnerText() String
|
||||||
|
|
||||||
InnerHtml() String
|
InnerHtml() String
|
||||||
|
|
||||||
Value() core.Value
|
Value() core.Value
|
||||||
|
|
||||||
GetAttributes() core.Value
|
GetAttributes() core.Value
|
||||||
|
|
||||||
GetAttribute(name String) core.Value
|
GetAttribute(name String) core.Value
|
||||||
|
|
||||||
GetChildNodes() core.Value
|
GetChildNodes() core.Value
|
||||||
|
|
||||||
GetChildNode(idx Int) core.Value
|
GetChildNode(idx Int) core.Value
|
||||||
|
|
||||||
QuerySelector(selector String) core.Value
|
QuerySelector(selector String) core.Value
|
||||||
|
|
||||||
QuerySelectorAll(selector String) core.Value
|
QuerySelectorAll(selector String) core.Value
|
||||||
}
|
}
|
||||||
|
|
||||||
|
HtmlDocument interface {
|
||||||
|
HtmlNode
|
||||||
|
|
||||||
|
Url() core.Value
|
||||||
|
}
|
||||||
|
)
|
||||||
|
@ -91,3 +91,32 @@ func Navigate(_ context.Context, args ...core.Value) (core.Value, error) {
|
|||||||
|
|
||||||
return values.None, doc.Navigate(args[1].(values.String))
|
return values.None, doc.Navigate(args[1].(values.String))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func Input(_ context.Context, args ...core.Value) (core.Value, error) {
|
||||||
|
err := core.ValidateArgs(args, 2, 3)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return values.None, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// TYPE(el, "foobar")
|
||||||
|
if len(args) == 2 {
|
||||||
|
arg1 := args[0]
|
||||||
|
|
||||||
|
err := core.ValidateType(arg1, core.HtmlElementType)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return values.False, err
|
||||||
|
}
|
||||||
|
|
||||||
|
el, ok := arg1.(*dynamic.HtmlElement)
|
||||||
|
|
||||||
|
if !ok {
|
||||||
|
return values.False, core.Error(core.ErrInvalidType, "expected dynamic element")
|
||||||
|
}
|
||||||
|
|
||||||
|
return values.None, el.Input(args[1], values.NewInt(100))
|
||||||
|
}
|
||||||
|
|
||||||
|
return values.None, nil
|
||||||
|
}
|
||||||
|
@ -15,7 +15,6 @@ import (
|
|||||||
"github.com/mafredri/cdp/protocol/page"
|
"github.com/mafredri/cdp/protocol/page"
|
||||||
"github.com/mafredri/cdp/rpcc"
|
"github.com/mafredri/cdp/rpcc"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"strings"
|
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
@ -25,7 +24,7 @@ type HtmlDocument struct {
|
|||||||
conn *rpcc.Conn
|
conn *rpcc.Conn
|
||||||
client *cdp.Client
|
client *cdp.Client
|
||||||
events *events.EventBroker
|
events *events.EventBroker
|
||||||
url string
|
url values.String
|
||||||
element *HtmlElement
|
element *HtmlElement
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -132,7 +131,7 @@ func NewHtmlDocument(
|
|||||||
doc.url = ""
|
doc.url = ""
|
||||||
|
|
||||||
if root.BaseURL != nil {
|
if root.BaseURL != nil {
|
||||||
doc.url = *root.BaseURL
|
doc.url = values.NewString(*root.BaseURL)
|
||||||
}
|
}
|
||||||
|
|
||||||
broker.AddEventListener("load", func(_ interface{}) {
|
broker.AddEventListener("load", func(_ interface{}) {
|
||||||
@ -154,7 +153,7 @@ func NewHtmlDocument(
|
|||||||
doc.url = ""
|
doc.url = ""
|
||||||
|
|
||||||
if updated.BaseURL != nil {
|
if updated.BaseURL != nil {
|
||||||
doc.url = *updated.BaseURL
|
doc.url = values.NewString(*updated.BaseURL)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -176,7 +175,7 @@ func (doc *HtmlDocument) String() string {
|
|||||||
doc.Lock()
|
doc.Lock()
|
||||||
defer doc.Unlock()
|
defer doc.Unlock()
|
||||||
|
|
||||||
return doc.url
|
return doc.url.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (doc *HtmlDocument) Unwrap() interface{} {
|
func (doc *HtmlDocument) Unwrap() interface{} {
|
||||||
@ -213,7 +212,7 @@ func (doc *HtmlDocument) Compare(other core.Value) int {
|
|||||||
case core.HtmlDocumentType:
|
case core.HtmlDocumentType:
|
||||||
other := other.(*HtmlDocument)
|
other := other.(*HtmlDocument)
|
||||||
|
|
||||||
return strings.Compare(doc.url, other.url)
|
return doc.url.Compare(other.url)
|
||||||
default:
|
default:
|
||||||
if other.Type() > core.HtmlDocumentType {
|
if other.Type() > core.HtmlDocumentType {
|
||||||
return -1
|
return -1
|
||||||
@ -320,6 +319,10 @@ func (doc *HtmlDocument) QuerySelectorAll(selector values.String) core.Value {
|
|||||||
return doc.element.QuerySelectorAll(selector)
|
return doc.element.QuerySelectorAll(selector)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (doc *HtmlDocument) Url() core.Value {
|
||||||
|
return doc.url
|
||||||
|
}
|
||||||
|
|
||||||
func (doc *HtmlDocument) ClickBySelector(selector values.String) (values.Boolean, error) {
|
func (doc *HtmlDocument) ClickBySelector(selector values.String) (values.Boolean, error) {
|
||||||
res, err := eval.Eval(
|
res, err := eval.Eval(
|
||||||
doc.client,
|
doc.client,
|
||||||
|
@ -164,11 +164,17 @@ func (el *HtmlElement) MarshalJSON() ([]byte, error) {
|
|||||||
el.Lock()
|
el.Lock()
|
||||||
defer el.Unlock()
|
defer el.Unlock()
|
||||||
|
|
||||||
return json.Marshal(el.innerHtml)
|
val, err := el.innerText.Value()
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return json.Marshal(val.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
func (el *HtmlElement) String() string {
|
func (el *HtmlElement) String() string {
|
||||||
return el.value.String()
|
return el.InnerHtml().String()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (el *HtmlElement) Compare(other core.Value) int {
|
func (el *HtmlElement) Compare(other core.Value) int {
|
||||||
@ -372,6 +378,13 @@ func (el *HtmlElement) Click() (values.Boolean, error) {
|
|||||||
return events.DispatchEvent(ctx, el.client, el.id, "click")
|
return events.DispatchEvent(ctx, el.client, el.id, "click")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (el *HtmlElement) Input(value core.Value, timeout values.Int) error {
|
||||||
|
ctx, cancel := contextWithTimeout()
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
return el.client.DOM.SetAttributeValue(ctx, dom.NewSetAttributeValueArgs(el.id, "value", value.String()))
|
||||||
|
}
|
||||||
|
|
||||||
func (el *HtmlElement) IsConnected() values.Boolean {
|
func (el *HtmlElement) IsConnected() values.Boolean {
|
||||||
el.Lock()
|
el.Lock()
|
||||||
defer el.Unlock()
|
defer el.Unlock()
|
||||||
|
@ -2,12 +2,13 @@ package static
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/MontFerret/ferret/pkg/runtime/core"
|
"github.com/MontFerret/ferret/pkg/runtime/core"
|
||||||
|
"github.com/MontFerret/ferret/pkg/runtime/values"
|
||||||
"github.com/PuerkitoBio/goquery"
|
"github.com/PuerkitoBio/goquery"
|
||||||
)
|
)
|
||||||
|
|
||||||
type HtmlDocument struct {
|
type HtmlDocument struct {
|
||||||
*HtmlElement
|
*HtmlElement
|
||||||
url string
|
url values.String
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewHtmlDocument(
|
func NewHtmlDocument(
|
||||||
@ -28,7 +29,7 @@ func NewHtmlDocument(
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return &HtmlDocument{el, url}, nil
|
return &HtmlDocument{el, values.NewString(url)}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (el *HtmlDocument) Type() core.Type {
|
func (el *HtmlDocument) Type() core.Type {
|
||||||
@ -38,8 +39,9 @@ func (el *HtmlDocument) Type() core.Type {
|
|||||||
func (el *HtmlDocument) Compare(other core.Value) int {
|
func (el *HtmlDocument) Compare(other core.Value) int {
|
||||||
switch other.Type() {
|
switch other.Type() {
|
||||||
case core.HtmlDocumentType:
|
case core.HtmlDocumentType:
|
||||||
// TODO: complete the comparison
|
otherDoc := other.(values.HtmlDocument)
|
||||||
return -1
|
|
||||||
|
return el.url.Compare(otherDoc.Url())
|
||||||
default:
|
default:
|
||||||
if other.Type() > core.HtmlDocumentType {
|
if other.Type() > core.HtmlDocumentType {
|
||||||
return -1
|
return -1
|
||||||
@ -48,3 +50,7 @@ func (el *HtmlDocument) Compare(other core.Value) int {
|
|||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (el *HtmlDocument) Url() core.Value {
|
||||||
|
return el.url
|
||||||
|
}
|
||||||
|
@ -24,13 +24,7 @@ func NewHtmlElement(node *goquery.Selection) (*HtmlElement, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (el *HtmlElement) MarshalJSON() ([]byte, error) {
|
func (el *HtmlElement) MarshalJSON() ([]byte, error) {
|
||||||
html, err := el.selection.Html()
|
return json.Marshal(el.InnerText().String())
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return json.Marshal(html)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (el *HtmlElement) Type() core.Type {
|
func (el *HtmlElement) Type() core.Type {
|
||||||
@ -38,7 +32,7 @@ func (el *HtmlElement) Type() core.Type {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (el *HtmlElement) String() string {
|
func (el *HtmlElement) String() string {
|
||||||
return el.selection.Text()
|
return el.InnerHtml().String()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (el *HtmlElement) Compare(other core.Value) int {
|
func (el *HtmlElement) Compare(other core.Value) int {
|
||||||
|
@ -12,5 +12,6 @@ func NewLib() map[string]core.Function {
|
|||||||
"WAIT_NAVIGATION": WaitNavigation,
|
"WAIT_NAVIGATION": WaitNavigation,
|
||||||
"CLICK": Click,
|
"CLICK": Click,
|
||||||
"NAVIGATE": Navigate,
|
"NAVIGATE": Navigate,
|
||||||
|
"INPUT": Input,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user