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
  • 2
    $\begingroup$ "An ostensibly exhaustive list is a cop-out." ─ Why? This is what many real language specifications do. $\endgroup$
    – kaya3
    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
    May 22, 2023 at 23:05
  • 1
    $\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$ May 23, 2023 at 21:19

4 Answers 4

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$
3
$\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$
3
$\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 even a well-formed 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. 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.

On the other hand, in an abstract syntax tree, 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.

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$

You must log in to answer this question.

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