Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion bql/grammar/grammar.go
Original file line number Diff line number Diff line change
Expand Up @@ -802,6 +802,11 @@ func BQL() *Grammar {
NewTokenType(lexer.ItemNode),
},
},
{
Elements: []Element{
NewTokenType(lexer.ItemBlankNode),
},
},
{
Elements: []Element{
NewTokenType(lexer.ItemPredicate),
Expand Down Expand Up @@ -957,7 +962,9 @@ func SemanticBQL() *Grammar {
setClauseHook(semanticBQL, []semantic.Symbol{"CONSTRUCT_FACTS"}, semantic.InitWorkingConstructClauseHook(), nil)
constructTriplesSymbols := []semantic.Symbol{"CONSTRUCT_TRIPLES", "MORE_CONSTRUCT_TRIPLES"}
setClauseHook(semanticBQL, constructTriplesSymbols, semantic.NextWorkingConstructClauseHook(), semantic.NextWorkingConstructClauseHook())

setElementHook(semanticBQL, []semantic.Symbol{"CONSTRUCT_TRIPLES"}, semantic.ConstructSubjectClauseHook(), nil)
setElementHook(semanticBQL, []semantic.Symbol{"CONSTRUCT_PREDICATE"}, semantic.ConstructPredicateClauseHook(), nil)
setElementHook(semanticBQL, []semantic.Symbol{"CONSTRUCT_OBJECT"}, semantic.ConstructObjectClauseHook(), nil)

return semanticBQL
}
2 changes: 1 addition & 1 deletion bql/semantic/convert.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ func ToNode(ce ConsumedElement) (*node.Node, error) {
return nil, fmt.Errorf("semantic.ToNode cannot convert symbol %v to a node", ce)
}
tkn := ce.Token()
if tkn.Type != lexer.ItemNode {
if tkn.Type != lexer.ItemNode && tkn.Type != lexer.ItemBlankNode {
return nil, fmt.Errorf("semantic.ToNode cannot convert token type %s to a node", tkn.Type)
}
return node.Parse(tkn.Text)
Expand Down
167 changes: 147 additions & 20 deletions bql/semantic/hooks.go
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,24 @@ func NextWorkingConstructClauseHook() ClauseHook {
return NextWorkingConstructClause()
}

// ConstructSubjectClauseHook returns the singleton for populating the subject in the
// working construct clause.
func ConstructSubjectClauseHook() ElementHook {
return constructSubjectClause()
}

// ConstructPredicateClauseHook returns the singleton for populating the predicate in the
// working construct clause.
func ConstructPredicateClauseHook() ElementHook {
return constructPredicateClause()
}

// ConstructObjectClauseHook returns the singleton for populating the object in the
// working construct clause.
func ConstructObjectClauseHook() ElementHook {
return constructObjectClause()
}

// TypeBindingClauseHook returns a ClauseHook that sets the binding type.
func TypeBindingClauseHook(t StatementType) ClauseHook {
var f ClauseHook
Expand Down Expand Up @@ -298,23 +316,23 @@ func whereSubjectClause() ElementHook {
}
if lastNopToken.Type == lexer.ItemAs {
if c.SAlias != "" {
return nil, fmt.Errorf("AS alias binding for subject has already being assined on %v", st)
return nil, fmt.Errorf("AS alias binding for subject has already being assigned on %v", st)
}
c.SAlias = tkn.Text
lastNopToken = nil
return f, nil
}
if lastNopToken.Type == lexer.ItemType {
if c.STypeAlias != "" {
return nil, fmt.Errorf("TYPE alias binding for subject has already being assined on %v", st)
return nil, fmt.Errorf("TYPE alias binding for subject has already being assigned on %v", st)
}
c.STypeAlias = tkn.Text
lastNopToken = nil
return f, nil
}
if c.SIDAlias == "" && lastNopToken.Type == lexer.ItemID {
if c.SIDAlias != "" {
return nil, fmt.Errorf("ID alias binding for subject has already being assined on %v", st)
return nil, fmt.Errorf("ID alias binding for subject has already being assigned on %v", st)
}
c.SIDAlias = tkn.Text
lastNopToken = nil
Expand All @@ -327,9 +345,8 @@ func whereSubjectClause() ElementHook {
return f
}

// processPredicate updates the working graph clause if there is an available
// predicate.
func processPredicate(c *GraphClause, ce ConsumedElement, lastNopToken *lexer.Token) (*predicate.Predicate, string, string, bool, error) {
// processPredicate parses a consumed element and returns a predicate and its attributes if possible.
func processPredicate(ce ConsumedElement) (*predicate.Predicate, string, string, bool, error) {
var (
nP *predicate.Predicate
pID string
Expand Down Expand Up @@ -357,9 +374,8 @@ func processPredicate(c *GraphClause, ce ConsumedElement, lastNopToken *lexer.To
return nil, pID, pAnchorBinding, temporal, nil
}

// processPredicateBound updates the working graph clause if there is an
// available predicate bound.
func processPredicateBound(c *GraphClause, ce ConsumedElement, lastNopToken *lexer.Token) (string, string, string, *time.Time, *time.Time, bool, error) {
// processPredicate parses a consumed element and returns a bound predicate and its attributes if possible.
func processPredicateBound(ce ConsumedElement) (string, string, string, *time.Time, *time.Time, bool, error) {
var (
pID string
pLowerBoundAlias string
Expand Down Expand Up @@ -428,7 +444,7 @@ func wherePredicateClause() ElementHook {
if c.P != nil {
return nil, fmt.Errorf("invalid predicate %s on graph clause since already set to %s", tkn.Text, c.P)
}
p, pID, pAnchorBinding, pTemporal, err := processPredicate(c, ce, lastNopToken)
p, pID, pAnchorBinding, pTemporal, err := processPredicate(ce)
if err != nil {
return nil, err
}
Expand All @@ -439,7 +455,7 @@ func wherePredicateClause() ElementHook {
if c.PLowerBound != nil || c.PUpperBound != nil || c.PLowerBoundAlias != "" || c.PUpperBoundAlias != "" {
return nil, fmt.Errorf("invalid predicate bound %s on graph clause since already set to %s", tkn.Text, c.P)
}
pID, pLowerBoundAlias, pUpperBoundAlias, pLowerBound, pUpperBound, pTemp, err := processPredicateBound(c, ce, lastNopToken)
pID, pLowerBoundAlias, pUpperBoundAlias, pLowerBound, pUpperBound, pTemp, err := processPredicateBound(ce)
if err != nil {
return nil, err
}
Expand All @@ -456,17 +472,17 @@ func wherePredicateClause() ElementHook {
switch lastNopToken.Type {
case lexer.ItemAs:
if c.PAlias != "" {
return nil, fmt.Errorf("AS alias binding for predicate has already being assined on %v", st)
return nil, fmt.Errorf("AS alias binding for predicate has already being assigned on %v", st)
}
c.PAlias = tkn.Text
case lexer.ItemID:
if c.PIDAlias != "" {
return nil, fmt.Errorf("ID alias binding for predicate has already being assined on %v", st)
return nil, fmt.Errorf("ID alias binding for predicate has already being assigned on %v", st)
}
c.PIDAlias = tkn.Text
case lexer.ItemAt:
if c.PAnchorAlias != "" {
return nil, fmt.Errorf("AT alias binding for predicate has already being assined on %v", st)
return nil, fmt.Errorf("AT alias binding for predicate has already being assigned on %v", st)
}
c.PAnchorAlias = tkn.Text
default:
Expand Down Expand Up @@ -515,7 +531,7 @@ func whereObjectClause() ElementHook {
pred *predicate.Predicate
err error
)
pred, c.OID, c.OAnchorBinding, c.OTemporal, err = processPredicate(c, ce, lastNopToken)
pred, c.OID, c.OAnchorBinding, c.OTemporal, err = processPredicate(ce)
if err != nil {
return nil, err
}
Expand All @@ -528,7 +544,7 @@ func whereObjectClause() ElementHook {
if c.OLowerBound != nil || c.OUpperBound != nil || c.OLowerBoundAlias != "" || c.OUpperBoundAlias != "" {
return nil, fmt.Errorf("invalid predicate bound %s on graph clause since already set to %s", tkn.Text, c.O)
}
oID, oLowerBoundAlias, oUpperBoundAlias, oLowerBound, oUpperBound, oTemp, err := processPredicateBound(c, ce, lastNopToken)
oID, oLowerBoundAlias, oUpperBoundAlias, oLowerBound, oUpperBound, oTemp, err := processPredicateBound(ce)
if err != nil {
return nil, err
}
Expand All @@ -548,22 +564,22 @@ func whereObjectClause() ElementHook {
switch lastNopToken.Type {
case lexer.ItemAs:
if c.OAlias != "" {
return nil, fmt.Errorf("AS alias binding for predicate has already being assined on %v", st)
return nil, fmt.Errorf("AS alias binding for predicate has already being assigned on %v", st)
}
c.OAlias = tkn.Text
case lexer.ItemType:
if c.OTypeAlias != "" {
return nil, fmt.Errorf("TYPE alias binding for predicate has already being assined on %v", st)
return nil, fmt.Errorf("TYPE alias binding for predicate has already being assigned on %v", st)
}
c.OTypeAlias = tkn.Text
case lexer.ItemID:
if c.OIDAlias != "" {
return nil, fmt.Errorf("ID alias binding for predicate has already being assined on %v", st)
return nil, fmt.Errorf("ID alias binding for predicate has already being assigned on %v", st)
}
c.OIDAlias = tkn.Text
case lexer.ItemAt:
if c.OAnchorAlias != "" {
return nil, fmt.Errorf("AT alias binding for predicate has already being assined on %v", st)
return nil, fmt.Errorf("AT alias binding for predicate has already being assigned on %v", st)
}
c.OAnchorAlias = tkn.Text
default:
Expand Down Expand Up @@ -895,3 +911,114 @@ func NextWorkingConstructClause() ClauseHook {
}
return f
}

// constructSubjectClause returns an element hook that updates the subject
// modifiers on the working construct clause.
func constructSubjectClause() ElementHook {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would you mind add tests for the next functions being added here?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

var f ElementHook
f = func(st *Statement, ce ConsumedElement) (ElementHook, error) {
if ce.IsSymbol() {
return f, nil
}
tkn := ce.Token()
c := st.WorkingConstructClause()
if c.S != nil {
return nil, fmt.Errorf("invalid subject %v in construct clause, subject already set to %v", tkn.Type, c.S)
}
if c.SBinding != "" {
return nil, fmt.Errorf("invalid subject %v in construct clause, subject already set to %v", tkn.Type, c.SBinding)
}
switch tkn.Type {
case lexer.ItemNode, lexer.ItemBlankNode:
n, err := ToNode(ce)
if err != nil {
return nil, err
}
c.S = n
case lexer.ItemBinding:
c.SBinding = tkn.Text
}
return f, nil
}
return f
}

// constructPredicateClause returns an element hook that updates the predicate
// modifiers on the working construct clause.
func constructPredicateClause() ElementHook {
var f ElementHook
f = func(st *Statement, ce ConsumedElement) (ElementHook, error) {
if ce.IsSymbol() {
return f, nil
}
tkn := ce.Token()
c := st.WorkingConstructClause()
if c.P != nil {
return nil, fmt.Errorf("invalid predicate %v in construct clause, predicate already set to %v", tkn.Type, c.P)
}
if c.PID != "" {
return nil, fmt.Errorf("invalid predicate %v in construct clause, predicate already set to %v", tkn.Type, c.PID)
}
if c.PBinding != "" {
return nil, fmt.Errorf("invalid predicate %v in construct clause, predicate already set to %v", tkn.Type, c.PBinding)
}
switch tkn.Type {
case lexer.ItemPredicate:
p, pID, pAnchorBinding, pTemporal, err := processPredicate(ce)
if err != nil {
return nil, err
}
c.P, c.PID, c.PAnchorBinding, c.PTemporal = p, pID, pAnchorBinding, pTemporal
case lexer.ItemBinding:
c.PBinding = tkn.Text
}
return f, nil
}
return f
}

// constructObjectClause returns an element hook that updates the object
// modifiers on the working graph clause.
func constructObjectClause() ElementHook {
var f ElementHook
f = func(st *Statement, ce ConsumedElement) (ElementHook, error) {
if ce.IsSymbol() {
return f, nil
}
tkn := ce.Token()
c := st.WorkingConstructClause()
if c.O != nil {
return nil, fmt.Errorf("invalid object %v in construct clause, object already set to %v", tkn.Text, c.O)
}
if c.OID != "" {
return nil, fmt.Errorf("invalid object %v in construct clause, objct already set to %v", tkn.Type, c.OID)
}
if c.OBinding != "" {
return nil, fmt.Errorf("invalid object %v in construct clause, object already set to %v", tkn.Type, c.OBinding)
}
switch tkn.Type {
case lexer.ItemNode, lexer.ItemBlankNode, lexer.ItemLiteral:
obj, err := triple.ParseObject(tkn.Text, literal.DefaultBuilder())
if err != nil {
return nil, err
}
c.O = obj
case lexer.ItemPredicate:
var (
pred *predicate.Predicate
err error
)
pred, c.OID, c.OAnchorBinding, c.OTemporal, err = processPredicate(ce)
if err != nil {
return nil, err
}
if pred != nil {
c.O = triple.NewPredicateObject(pred)
}
case lexer.ItemBinding:
c.OBinding = tkn.Text
}
return f, nil
}
return f
}
Loading