4
$\begingroup$

I have thought about how I could specify an expression separate from a statement.

A fragment of code that resolves to a value.

But void expressions are still expressions but do not really resolve to a value.

A fragment of code that performs an action.

Includes statements too.

An assignment or function call or literal value or variable.

An ostensibly exhaustive list is a cop-out.

How could I best define an expression separately from a statement?

Note: The top-voted answer here permits such questions.

$\endgroup$
3
  • 3
    $\begingroup$ "An ostensibly exhaustive list is a cop-out." ─ Why? This is what many real language specifications do. $\endgroup$
    – kaya3
    Commented May 22, 2023 at 22:34
  • $\begingroup$ @kaya3 It just says a bunch of things which are expressions. It does not really say why they are expressions or what an expression really is or means. $\endgroup$
    – CPlus
    Commented May 22, 2023 at 23:05
  • 2
    $\begingroup$ What difference do you intend to make between “expression” and “statement”? This is very dependent on the language. Some languages make no difference at all (e.g. Lisp). In some languages, statements are expressions that return void. In some languages, expressions are one kind of statement, and there are others, but even in such languages, the impact of a statement being an expression differs: for example, in standard C, an expression cannot contain loops except buried under function calls, but in GCC-extended C, an expression can contain a statement. What's your language like? $\endgroup$ Commented May 23, 2023 at 21:19

5 Answers 5

2
$\begingroup$

"Statements" and "expressions" are kinds of nodes in some languages' grammars, including English.
Those concepts may exist in the formally specified grammar, as in C-like languages, or not, as in languages in the ML, Haskell and Lisp families.

A statement is a node that can exist in a list, and an expression is one that can exist in a tree.
Literally speaking, if you see an EBNF syntax looking like

Function ::= "fn" Identifier Parameters? "{" Stuff* "}"

then Stuff is statements, as they can be listed.
Meanwhile,

Term ::= Addition | Number
Addition ::= Term "+" Term

then Term is an expression, as it is a tree.

This all is true even in languages that have nothing called "statement" in their formal grammar.
Take Haskell: it has a kind of expression called do-notation representing abstract trees of nested Monadic operations flattened to lists.

This tree code:

main =
    getLine >>= \str ->
        putStrLn str

can be flattened to this list:

main = do
    str <- getLine
    putStrLn str

where do-notation's grammar is "do" ((Identifier "<-" | "let" Identifier "=")? Expression)+.
And in this context, even though the grammar has no concept of "statement," this is exactly the documented and used term.

Lisp is another expression-oriented family of languages where most of the syntax revolve around Expr ::= "(" Identifier Expr* ")", and one of its functions, called do, just evaluates an arbitrary list of expressions as arguments.

(do
    (println (+ 40 2))
    (println (* 21 2)))

Statements!
This doesn't even need to be a builtin construct. You can define do as simply as a variadic function that ignores all its arguments.

TL;DR: Those are purely syntactic constructs with no bearings on semantics.

$\endgroup$
4
$\begingroup$

Just list them

The C# language specification and current draft ECMAScript specification both opt to not define what a statement is, and instead just give a list of what statements exist.

The Swift Programming Language defines it in paragraph form, but again by listing different types of statements rather than giving a singular definition:

In Swift, there are three kinds of statements: simple statements, compiler control statements, and control flow statements. Simple statements are the most common and consist of either an expression or a declaration. Compiler control statements allow the program to change aspects of the compiler’s behavior and include a conditional compilation block and a line control statement.

Control flow statements are used to control the flow of execution in a program. There are several types of control flow statements in Swift, including loop statements, branch statements, and control transfer statements. Loop statements allow a block of code to be executed repeatedly, branch statements allow a certain block of code to be executed only when certain conditions are met, and control transfer statements provide a way to alter the order in which code is executed. In addition, Swift provides a do statement to introduce scope, and catch and handle errors, and a defer statement for running cleanup actions just before the current scope exits.

$\endgroup$
4
$\begingroup$

Describe them by how they can and cannot be used. For instance, in many languages, an expression can appear on the right side of an assignment statement, but a statement cannot. Thus, the following may be legal (because x == 1 ? 3 : 2 is a conditional expression):

x = x == 1 ? 3 : 2;

Whereas the following would be illegal in a language like C++ (because if indicates the start of a conditional statement):

x = if (x == 1) { 3 } else { 2 };
$\endgroup$
4
$\begingroup$

The meaning of the words "expression" and "statement" depend on context.

In a language's grammar, these words just refer to syntactic forms. It is not strictly correct to say that a syntactically valid expression like 1 + x produces a value when executed, because this expression cannot even necessarily be executed. For example, x could be the wrong type, or there might be no such variable named x, and then this expression could not be compiled and would never be able to produce a value. Or it could be something semantically valid like System.out.println("Hello, world!") in Java, which doesn't produce a value (because it invokes a void method), but is nonetheless an expression according to Java's grammar. So the best we can say is that expressions are grammatical forms which typically correspond with computations that produce values, and statements are grammatical forms which typically correspond with instructions which change the program state.

Or in abstract syntax trees, expressions and statements are kinds of nodes. Expression nodes typically would produce values when evaluated, and statement nodes typically would change the program state when executed. But even in the AST, we generally don't insist that an expression must e.g. pass type-checking in order for it to be an expression, so expressions need not actually be evaluable; likewise for statements.

On the other hand, in an intermediate representation used after name resolution, type-checking and other checks have occurred, we might only construct IR nodes which are semantically valid. In this case, a statement or expression in the IR would always represent something evaluable. However, depending on implementation, the IR might not distinguish statements from expressions at all; for example, a statement if(a) x = b; else x = c; and an expression a ? (x = b) : (x = c) might be the same in some IR.

Note that these are descriptions, not definitions, so it's OK to use words like "typically" as long as it gets the idea across.

$\endgroup$
1
$\begingroup$

"Expression" usually refers to a fragment of code that can be found as the condition of a conditional language construct, to what produces a value that can be saved into a variable per assignment or passed into a parameter (in-place evaluation of the expression), it's basically a syntax unit which produces a value result by performing the actual computation, calculation, logic operations or comparison (typically using operators on operands).

void (or nil or similar) may act as a (pseudo-)value as well, just an empty one (depends on whether or not a language defines it as a proper/permitted result/value of expressions if it's handled in a compatible way similar to other expression results, otherwise with an incompatible design choice treating void special. Depends on if there's a constraint on void in the area of type checking, but if void could be the result of an expression, that might qualify as just another value. With "expression" being a certain category of language constructs, it's up to the language designer to decide whether void might be included in this category or not.

A "statement" on the other hand is considered to be broader. Expressions could be seen as statements too, if just found as standalone. But then there's statements that are not expressions. Declarations, various keywords, control flow constructs. These are not allowed to appear on the value-side of an assignment, or go as argument into a function parameter, etc., as these are not evaluated/resolved/computed-to some result, and instead perform actions.

Operators, performing an action, are not expressions themselves. Operators + operands make an expression. Value literals, function calls or variable names (as these resolve to a value) can be found in places which would also allow for an in-place larger expression which has to compute a value/result first.

New contributor
skreutzer is a new contributor to this site. Take care in asking for clarification, commenting, and answering. Check out our Code of Conduct.
$\endgroup$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .