Permit Statements in SequenceExpressions
The previous shape of SequenceExpression only permitted expressions in the sequence. This is pretty common in most ILs, however, it usually leads to complicated manual spilling in the event that a statement is needed. This is often necessary when, for example, a compiler is deeply nested in some expression production, and then realizes the code expansion requires a statement (e.g., maybe a new local variable must be declared, etc). Instead of requiring complicated code-gen, this change permits SequenceExpression to contain an arbitrary mixture of expression/statement prelude nodes, terminating with a single, final Expression which yields the actual expression value. The runtime bears the burden of implementing this which, frankly, is pretty trivial.
This commit is contained in:
parent
748432299a
commit
fde88b7cf4
7 changed files with 46 additions and 23 deletions
|
@ -268,10 +268,12 @@ export interface ConditionalExpression extends Expression {
|
|||
export const conditionalExpressionKind = "ConditionalExpression";
|
||||
export type ConditionalExpressionKind = "ConditionalExpression";
|
||||
|
||||
// A sequence expression allows composition of multiple expressions into one. It evaluates to the last one.
|
||||
// A sequence expression allows evaluation of multiple "prelude" statements and/or expressions as though they were a
|
||||
// single expression. The overall sequence evaluates to the final "value" expression.
|
||||
export interface SequenceExpression extends Expression {
|
||||
kind: SequenceExpressionKind;
|
||||
expressions: Expression[];
|
||||
kind: SequenceExpressionKind;
|
||||
prelude: (Expression | statements.Statement)[];
|
||||
value: Expression;
|
||||
}
|
||||
export const sequenceExpressionKind = "SequenceExpression";
|
||||
export type SequenceExpressionKind = "SequenceExpression";
|
||||
|
|
|
@ -757,9 +757,14 @@ class ConditionalExpression(Expression):
|
|||
self.alternate = alternate # the expression to evaluate if `false`.
|
||||
|
||||
class SequenceExpression(Expression):
|
||||
"""A expression allows composition of multiple expressions into one. It evaluates to the last one's value."""
|
||||
def __init__(self, expressions, loc=None):
|
||||
assert isinstance(expressions, list) and all(isinstance(expr, Expression) for expr in expressions)
|
||||
"""A sequence expression allows evaluation of multiple prelude statements and/or expressions as though they were
|
||||
a single expression. The overall sequence evaluates to the final value expression."""
|
||||
def __init__(self, prelude, value, loc=None):
|
||||
assert(
|
||||
isinstance(prelude, list) and
|
||||
(all(isinstance(expr, Expression) or isinstance(expr, Statement) for expr in expressions)))
|
||||
assert isinstance(value, Expression)
|
||||
super(SequenceExpression, self).__init__("SequenceExpression", loc)
|
||||
self.expressions = expressions
|
||||
self.prelude = prelude
|
||||
self.value = value
|
||||
|
||||
|
|
|
@ -398,10 +398,12 @@ var _ Expression = (*ConditionalExpression)(nil)
|
|||
|
||||
const ConditionalExpressionKind NodeKind = "ConditionalExpression"
|
||||
|
||||
// SequenceExpression allows composition of multiple expressions into one. It evaluates to the last one.
|
||||
// SequenceExpression allows evaluation of multiple "prelude" statements and/or expressions as though they were a
|
||||
// single expression. The overall sequence evaluates to the final "value" expression.
|
||||
type SequenceExpression struct {
|
||||
ExpressionNode
|
||||
Expressions []Expression `json:"expressions"`
|
||||
Prelude []Node `json:"prelude"`
|
||||
Value Expression `json:"value"`
|
||||
}
|
||||
|
||||
var _ Node = (*SequenceExpression)(nil)
|
||||
|
|
|
@ -224,9 +224,10 @@ func Walk(v Visitor, node Node) {
|
|||
Walk(v, n.Consequent)
|
||||
Walk(v, n.Alternate)
|
||||
case *SequenceExpression:
|
||||
for _, expr := range n.Expressions {
|
||||
Walk(v, expr)
|
||||
for _, node := range n.Prelude {
|
||||
Walk(v, node)
|
||||
}
|
||||
Walk(v, n.Value)
|
||||
case *NullLiteral, *BoolLiteral, *NumberLiteral, *StringLiteral:
|
||||
// No children, nothing to do.
|
||||
|
||||
|
|
|
@ -711,7 +711,16 @@ func (a *astBinder) checkConditionalExpression(node *ast.ConditionalExpression)
|
|||
}
|
||||
|
||||
func (a *astBinder) checkSequenceExpression(node *ast.SequenceExpression) {
|
||||
// Ensure that all prelude nodes are statements or expressions.
|
||||
for _, prelnode := range node.Prelude {
|
||||
switch prelnode.(type) {
|
||||
case ast.Expression, ast.Statement:
|
||||
// good
|
||||
default:
|
||||
a.b.Diag().Errorf(errors.ErrorSequencePreludeExprStmt.At(prelnode))
|
||||
}
|
||||
}
|
||||
|
||||
// The type of a sequence expression is just the type of the last expression in the sequence.
|
||||
// TODO: check that there's at least one!
|
||||
a.b.ctx.RegisterType(node, a.b.ctx.RequireType(node.Expressions[len(node.Expressions)-1]))
|
||||
a.b.ctx.RegisterType(node, a.b.ctx.RequireType(node.Value))
|
||||
}
|
||||
|
|
|
@ -41,4 +41,5 @@ var (
|
|||
ErrorInvalidCast = newError(529, "Illegal cast from '%v' to '%v'; this can never succeed")
|
||||
ErrorModuleAliasTargetNotFound = newError(530, "Module alias target '%v' was not found (from '%v')")
|
||||
ErrorDerivedClassHasNoCtor = newError(531, "Class '%v' has no constructor, but its base class '%v' does")
|
||||
ErrorSequencePreludeExprStmt = newError(532, "Sequence preludes must consist of expressions and/or statements")
|
||||
)
|
||||
|
|
|
@ -2057,16 +2057,19 @@ func (e *evaluator) evalConditionalExpression(node *ast.ConditionalExpression) (
|
|||
}
|
||||
|
||||
func (e *evaluator) evalSequenceExpression(node *ast.SequenceExpression) (*rt.Object, *rt.Unwind) {
|
||||
// Simply walk through the sequence and return the last object.
|
||||
var obj *rt.Object
|
||||
contract.Assert(len(node.Expressions) > 0)
|
||||
for _, expr := range node.Expressions {
|
||||
var uw *rt.Unwind
|
||||
if obj, uw = e.evalExpression(expr); uw != nil {
|
||||
// If the rt.Unwind was non-nil, stop visiting the expressions and propagate it now.
|
||||
return nil, uw
|
||||
// Evaluate the sequence's prelude and then, afterwards, evaluate to its value. If an unwind happens anywhere
|
||||
// during the prelude, we will abruptly terminate the sequence and return it.
|
||||
for _, prelnode := range node.Prelude {
|
||||
switch n := prelnode.(type) {
|
||||
case ast.Expression:
|
||||
if _, uw := e.evalExpression(n); uw != nil {
|
||||
return nil, uw
|
||||
}
|
||||
case ast.Statement:
|
||||
if uw := e.evalStatement(n); uw != nil {
|
||||
return nil, uw
|
||||
}
|
||||
}
|
||||
}
|
||||
// Return the last expression's object.
|
||||
return obj, nil
|
||||
return e.evalExpression(node.Value)
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue