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
? 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
" is interpreted as a
function call instead of the string
foo. Since we haven’t defined a function
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
? 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
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.,
"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.