1// ================================================================
2// This is for things that get us out of statement blocks: break, continue,
3// return.
4// ================================================================
5
6package cst
7
8import (
9	"errors"
10
11	"miller/src/dsl"
12	"miller/src/lib"
13	"miller/src/runtime"
14)
15
16// ----------------------------------------------------------------
17type BreakNode struct {
18}
19
20func (this *RootNode) BuildBreakNode(astNode *dsl.ASTNode) (*BreakNode, error) {
21	lib.InternalCodingErrorIf(astNode.Type != dsl.NodeTypeBreak)
22	lib.InternalCodingErrorIf(astNode.Children == nil)
23	lib.InternalCodingErrorIf(len(astNode.Children) != 0)
24
25	return &BreakNode{}, nil
26}
27
28func (this *BreakNode) Execute(state *runtime.State) (*BlockExitPayload, error) {
29	return &BlockExitPayload{
30		BLOCK_EXIT_BREAK,
31		nil,
32	}, nil
33}
34
35// ----------------------------------------------------------------
36type ContinueNode struct {
37}
38
39func (this *RootNode) BuildContinueNode(astNode *dsl.ASTNode) (*ContinueNode, error) {
40	lib.InternalCodingErrorIf(astNode.Type != dsl.NodeTypeContinue)
41	lib.InternalCodingErrorIf(astNode.Children == nil)
42	lib.InternalCodingErrorIf(len(astNode.Children) != 0)
43
44	return &ContinueNode{}, nil
45}
46
47func (this *ContinueNode) Execute(state *runtime.State) (*BlockExitPayload, error) {
48	return &BlockExitPayload{
49		BLOCK_EXIT_CONTINUE,
50		nil,
51	}, nil
52}
53
54// ----------------------------------------------------------------
55type ReturnNode struct {
56	returnValueExpression IEvaluable
57}
58
59func (this *RootNode) BuildReturnNode(astNode *dsl.ASTNode) (*ReturnNode, error) {
60	lib.InternalCodingErrorIf(astNode.Type != dsl.NodeTypeReturn)
61	lib.InternalCodingErrorIf(astNode.Children == nil)
62	if len(astNode.Children) == 0 {
63		return &ReturnNode{returnValueExpression: nil}, nil
64	} else if len(astNode.Children) == 1 {
65		returnValueExpression, err := this.BuildEvaluableNode(astNode.Children[0])
66		if err != nil {
67			return nil, err
68		}
69		return &ReturnNode{returnValueExpression: returnValueExpression}, nil
70	} else {
71		lib.InternalCodingErrorIf(true)
72	}
73	return nil, errors.New("Internal coding error: Statement should not be reached.")
74}
75
76func (this *ReturnNode) Execute(state *runtime.State) (*BlockExitPayload, error) {
77	if this.returnValueExpression == nil {
78		return &BlockExitPayload{
79			BLOCK_EXIT_RETURN_VOID,
80			nil,
81		}, nil
82	} else {
83		// The return value can be of type MT_ERROR but we do not use Go-level error return here.
84		returnValue := this.returnValueExpression.Evaluate(state)
85		return &BlockExitPayload{
86			BLOCK_EXIT_RETURN_VALUE,
87			returnValue.Copy(),
88		}, nil
89	}
90}
91