package parser
import (
"github.com/marzeq/qk/attributes"
"github.com/marzeq/qk/tokeniser"
)
// BlockResult returns the expression yielded when the block reaches its end.
func BlockResult(block *BlockNode) (ExpressionNode, bool) {
if block == nil || !block.Expression || len(block.Body) == 0 {
return nil, false
}
expr, ok := block.Body[len(block.Body)-1].(ExpressionNode)
if !ok {
return nil, false
}
if nested, ok := expr.(*BlockNode); ok && !nested.Expression {
return nil, false
}
if conditional, ok := expr.(*IfNode); ok && !conditional.Expression {
return nil, false
}
if match, ok := expr.(*MatchNode); ok && !match.Expression {
return nil, false
}
return expr, true
}
// NodeFallsThrough reports whether execution can continue past a node.
func NodeFallsThrough(node Node) bool {
switch n := node.(type) {
case *ControlKeywordNode:
return n.Keyword != tokeniser.KeywordReturn &&
n.Keyword != tokeniser.KeywordBreak &&
n.Keyword != tokeniser.KeywordContinue
case *BlockNode:
for _, child := range n.Body {
if !NodeFallsThrough(child) {
return false
}
}
return true
case *IfNode:
if n.ElseBranch == nil || NodeFallsThrough(n.IfBranch.Node) {
return true
}
for _, branch := range n.ElseIfBranches {
if NodeFallsThrough(branch.Node) {
return true
}
}
return NodeFallsThrough(n.ElseBranch)
case *MatchNode:
for _, arm := range n.Arms {
if NodeFallsThrough(arm.Body) {
return true
}
}
return false
case *FunctionCallNode:
return n.Symbol == nil || n.Symbol.Attributes.Get(attributes.AttributeTypeNoReturn) == nil
default:
return true
}
}