Most programming languages have a distinction between expressions and statements: expressions resolve to a value, while statements can change the state of a program. Logo is among them even though this difference is not marked by special syntax: an expression does not look different than a statement, so that both can only be disambiguated by context. Let’s look at a simple example (? marks Logo’s prompt):

? print "foo
foo
? "foo
You don’t say what to do with foo
? foo
I don’t know how to foo.

In the first two examples, the expression "foo is syntactically valid, as evidenced by the absence of syntax errors. In the first example, the expression "foo (which evaluates to the string foo, as printed by Logo) is an argument in the statement print "foo which is a valid Logo program. In the second input, however, the expression "foo is syntactically valid, but does not, standing on its own, constitute a valid Logo program, and Logo prints an error. In the third example, we see that foo without " is interpreted as a function call instead of the string foo. Since we haven’t defined a function named foo, we get an error.

? print print "foo
foo
print didn’t output to print

In this example, we can see that the inner print "foo is evaluated before Logo prints the error message. Although Logo could, in theory, reject the program at parse time since it knows that while print "foo prints a value, it does not return one, it starts executing it until it encounters a runtime error.

? ifelse "true [ print "foo ] [ print "bar ]
foo
? print ifelse "true [ "foo ] [ "bar ]
foo
? print ifelse "true [ print "foo ] [ "bar ]
foo
print didn’t output to print

Here, we can see that ifelse … on its own is ambigous: It can be used both as a statement and as an expression. This falls in line with the previous example: Logo parses a program and starts executing it until it hits an error. What’s different in this example is that one can think of situations where it would not be clear at parse time whether an ifelse returns a value or not, e. g. when the condition depends on user input. While we can indeed for some programs determine at parse time that they will produce an error at runtime, that is not possible for all programs. In some situations we apparently have to check at runtime whether something returns a value.

The first milestone of elm-logo follows a simpler model: the parser will only recognize statements at the top level (e. g. print "foo). User-defined functions and ifelse … will be handled like statements (meaning, they can’t return a value for now). This simplifies the parser at the expense of turning some runtime errors into parse errors: the first version won’t, e. g., recognize "foo as syntactically valid.

In the next post, we will have a look at how the VM is structured. We will explore what data structures are used to represent a Logo program and how the VM makes use of them to execute a program.