Skip to content

Commit

Permalink
add: Name the language MLTS; mvoe pages over to lang-workshop
Browse files Browse the repository at this point in the history
  • Loading branch information
jacobjwalters authored Nov 28, 2024
1 parent 46bab06 commit 9a7cff0
Show file tree
Hide file tree
Showing 7 changed files with 97 additions and 82 deletions.
2 changes: 1 addition & 1 deletion pages/resources.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ permalink: "/resources/"
This page contains resources from previous TypeSig events, courses, and workshops, as well as more general reference material for various topics.

### Current Courses
[Lisp Workshop]({{ site.url }}/resources/lisp-workshop)
[Language Workshop]({{ site.url }}/resources/lang-workshop)

### Previous Courses

Expand Down
23 changes: 16 additions & 7 deletions pages/resources/lisp-workshop.md
Original file line number Diff line number Diff line change
@@ -1,16 +1,25 @@
---
layout: page
title: Lisp Workshop
permalink: "/resources/lisp-workshop"
title: Language Workshop
permalink: "/resources/lang-workshop"
---
Welcome to the lisp workshop! This is a series of worksheets that will walk you through implementing an interpreter for a programming language. The steps are designed to be completed in order; if you're stuck on one of the steps, ask a TypeSig committee member for help in the workshop, or post a message on our [Discord server][discord].
Welcome to the language workshop!
This is a series of worksheets that will walk you through implementing an interpreter for a programming language called MLTS (pronounced as "melts").
MLTS is a functional language, and you get to choose what features your implementation supports!
We'll walk you through implementing a parser, evaluator, REPL, and typechecker.

The steps are designed to be completed in order; if you're stuck on one of the steps, ask a TypeSig committee member for help in the workshop, or post a message on our [Discord server][discord].

You can choose which language to write the interpreter in; pick one that you're comfortable with already, or if you want a further challenge, pick one that you want to learn!

- [Step 1: The Beginning](lisp-workshop/step1)
- [Step 2: Parsing and Printing](lisp-workshop/step2)
- [Step 3: Eval](lisp-workshop/step3)
- [Step 4: Environments](lisp-workshop/step4)
The only prior knowledge we assume is that you know how to program.
If you're not too confident with your programming skills, this can be a great way to develop them!
In this case, we recommend that you use a language that you have the most experience in, and ask our workshop helpers if you're unsure about anything :)

- [Step 1: The Beginning](lang-workshop/step1)
- [Step 2: Parsing and Printing](lang-workshop/step2)
- [Step 3: Eval](lang-workshop/step3)
- [Step 4: Environments](lang-workshop/step4)
- More to come in the next few weeks!

[discord]: {{site.social.discord}}
8 changes: 4 additions & 4 deletions pages/resources/lisp-workshop/step1.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
---
layout: page
title: "Lisp Workshop - Step 1: The Beginning"
permalink: "/resources/lisp-workshop/step1"
title: "Step 1: The Beginning | Language Workshop"
permalink: "/resources/lang-workshop/step1"
---
Complexity: Short

Expand Down Expand Up @@ -34,9 +34,9 @@ These are some extra challenges you can attempt to build your understanding furt
- Print a prompt to the console to indicate when the user should input text:

```console
lisp> 42
MLTS> 42
42
lisp>
MLTS>
```

- If the user enters `quit` or `exit` as their input, stop the loop. You should also print some text when starting the interpreter so your users know this is a possibility!
Expand Down
43 changes: 24 additions & 19 deletions pages/resources/lisp-workshop/step2.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
---
layout: page
title: "Lisp Workshop - Step 2: Parsing and Printing"
permalink: "/resources/lisp-workshop/step2"
title: "Step 2: Parsing and Printing | Language Workshop"
permalink: "/resources/lang-workshop/step2"
---
Complexity: Long

Expand All @@ -16,9 +16,14 @@ If you're familiar with parsing, feel free to skip to the Tasks section.

## S-Expressions

To keep things simple, we've opted for a very simple grammar, known as S-Expressions (the S stands for Symbolic. They are often abbreviated to sexps). They are used in the Lisp family of programming languages.
An S-Expression consists of a pair of brackets, that contain a list of "atoms" separated by whitespace. These atoms may be literal values, like numbers or strings, or symbols, which are arbitrary strings of characters. You're also allowed to have another S-Expression in place of an atom, allowing you to build more complex programs.
In Lisp languages, the first atom is used as a function or operator name, and the following atoms are provided as arguments to the function or operator.
To keep things simple, we've opted for a very simple grammar, known as S-Expressions (the S stands for Symbolic. They are often abbreviated to sexps).
They are used in the Lisp family of programming languages.

An S-Expression consists of a pair of brackets, that contain a list of "atoms" separated by whitespace.
These atoms may be literal values, like numbers or strings, or symbols, which are arbitrary strings of characters.
You're also allowed to have another S-Expression in place of an atom, allowing you to build more complex programs.

In Lisp-like syntax, the first atom is used as a function or operator name, and the following atoms are provided as arguments to the function or operator.
Here's an example of some code written in this style:

```scheme
Expand Down Expand Up @@ -274,26 +279,26 @@ If you've spent some time trying to come up with a design for your parser, but a
Here's some test cases that you can use to check if your implementation is along the right lines:

```console
lisp>
lisp> 1
MLTS>
MLTS> 1
1
lisp> hello
MLTS> hello
hello
lisp> ( )
MLTS> ( )
()
lisp> (+ 1 2)
MLTS> (+ 1 2)
(+ 1 2)
lisp> (+ 1 (* 2 3))
MLTS> (+ 1 (* 2 3))
(+ 1 (* 2 3))
lisp> (())
MLTS> (())
(())
lisp> ( ( ) )
MLTS> ( ( ) )
(())
lisp> ( foo bar )
MLTS> ( foo bar )
(foo bar)
lisp> (* 3
MLTS> (* 3
parse: expected closing bracket
lisp> * 3 4)
MLTS> * 3 4)
parse: unexpected closing bracket
```

Expand All @@ -303,16 +308,16 @@ These are some extra challenges you can attempt to build your understanding furt

- Allow floating point numbers as well as integers.

- Add support for comments. Traditionally Lisp languages use `;` to start comments, but there's nothing stopping you from picking your own comment syntax.
- Add support for comments.

- Treat square brackets (`[`/`]`) the same as normal brackets/parentheses, so the user can switch between them for clarity.

- Add the REPL commands `:lex` and `:parse`, which takes an expression and prints the output of the lexer and parser respectively when run on that expression. For example:

```console
lisp> :lex (+ 1 (* 2 foo))
MLTS> :lex (+ 1 (* 2 foo))
["+", "1", "(", "*", "2", "foo", ")"]
lisp> :parse (+ 1 (* 2 foo))
MLTS> :parse (+ 1 (* 2 foo))
SExpr [LSym "+", LInt 1, SExpr [LSym "*", LInt 2, LSym "foo"]]
```

Expand Down
32 changes: 16 additions & 16 deletions pages/resources/lisp-workshop/step3.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
---
layout: page
title: "Lisp Workshop - Step 3: Eval"
permalink: "/resources/lisp-workshop/step3"
title: "Step 3: Eval | Language Workshop"
permalink: "/resources/lang-workshop/step3"
---

Complexity: Medium
Expand Down Expand Up @@ -39,7 +39,7 @@ data Value = VInt Integer
Not every expression can be transformed into a value. Some programs are syntactically correct, but semantically meaningless! As an analogy, consider the English sentence "The sky walks a hamburger". This sentence is syntactically valid according to the rules of the English language, being a noun phrase followed by a verb and another noun phrase, but (in pretty much any context) it's meaningless!

The same principle applies in programming languages.
In Lisp languages, an S-Expression must start with a operator for it to be considered a redex.
In Lisp-like syntax, an S-Expression must start with a operator for it to be considered a redex.
Any S-Expression that doesn't is considered to be semantically invalid. This form of notation is commonly called [prefix (or Polish) notation](https://en.wikipedia.org/wiki/Polish_notation).
Additionally, if the type and number of the arguments don't match what the operator expects, the S-Expression isn't valid either.

Expand Down Expand Up @@ -149,7 +149,7 @@ The primary advantage of applicative order over normal order is that it's easier

Normal order is less frequently seen in commonly used languages. The main examples are Haskell, and, of all things, R.
Normal order gets its name from the lambda calculus, where normal order evaluation is guaranteed to result in the normal form of an expression, if one exists. The same is not guaranteed of applicative order.
To illustrate this, let's take the following two lambda calculus expressions, written in our Lisp syntax (you don't have to understand this fully yet!):
To illustrate this, let's take the following two lambda calculus expressions, written in our Lisp-like syntax (you don't have to understand this fully yet!):

```scheme
(lambda x (lambda y x))
Expand Down Expand Up @@ -197,31 +197,31 @@ Update your REPL function, by running `eval` on the parsed input, and passing th
Here's some test cases that you can use to check if your implementation is along the right lines:

```console
lisp> 1
MLTS> 1
1
lisp> (+ 1 2)
MLTS> (+ 1 2)
3
lisp> (+ 1 (* 2 3))
MLTS> (+ 1 (* 2 3))
6
lisp> (* 1 (+ 2 3))
MLTS> (* 1 (+ 2 3))
5
lisp> (- 1 2)
MLTS> (- 1 2)
-1
lisp> 1 + 2
MLTS> 1 + 2
1
+
2
lisp> (+ 1)
MLTS> (+ 1)
eval: `+` expects exactly two integers
lisp> (* 1 2 3)
MLTS> (* 1 2 3)
eval: `*` expects exactly two integers
lisp> (+ -1 1)
MLTS> (+ -1 1)
0
lisp> (unknown 1)
MLTS> (unknown 1)
eval: `unknown` is an unknown symbol
lisp> ()
MLTS> ()
eval: can't eval empty S-Expression
lisp> (1 + 2)
MLTS> (1 + 2)
eval: `1` doesn't evaluate to a function or primitive
```

Expand Down
54 changes: 27 additions & 27 deletions pages/resources/lisp-workshop/step4.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
---
layout: page
title: "Lisp Workshop - Step 4: Enviroments"
permalink: "/resources/lisp-workshop/step4"
title: "Step 4: Enviroments | Language Workshop"
permalink: "/resources/lang-workshop/step4"
---

Complexity: Short
Expand Down Expand Up @@ -207,40 +207,40 @@ Finally, you should update your REPL to keep track of its environment, to allow
Here's some test cases that you can use to check if your implementation is along the right lines (assuming you imlpement shadowing):

```console
lisp> (define one 1)
lisp> one
MLTS> (define one 1)
MLTS> one
1
lisp> (+ 1 one)
MLTS> (+ 1 one)
2
lisp> (define two 2)
lisp> (+ one two)
MLTS> (define two 2)
MLTS> (+ one two)
3

lisp> (define number 42)
lisp> (define secret (* number 17))
lisp> (define number 12)
lisp> number
MLTS> (define number 42)
MLTS> (define secret (* number 17))
MLTS> (define number 12)
MLTS> number
12
lisp> secret
MLTS> secret
714

lisp> (define foo 1)
lisp> (define foo foo)
lisp> foo
MLTS> (define foo 1)
MLTS> (define foo foo)
MLTS> foo
1

lisp> (define plus +)
lisp> (plus 1 2)
MLTS> (define plus +)
MLTS> (plus 1 2)
3
lisp> (define * +)
lisp> (* 1 2)
MLTS> (define * +)
MLTS> (* 1 2)
3

lisp> (define define 1)
lisp> define
MLTS> (define define 1)
MLTS> define
1
lisp> (define foo 2)
lisp> foo
MLTS> (define foo 2)
MLTS> foo
2
```

Expand All @@ -250,20 +250,20 @@ These are some extra challenges you can attempt to build your understanding furt

- Add a REPL command `:env` that prints the contents of the current environment.

- Add a basic import system: define another top-level declaration called `import`, which takes a filename; loads that file; evaluates all of the top level declarations stored in it; and extends the current environment with these declarations. For example, if `foo.lisp` contains the following:
- Add a basic import system: define another top-level declaration called `import`, which takes a filename; loads that file; evaluates all of the top level declarations stored in it; and extends the current environment with these declarations. For example, if `foo.mlts` contains the following:

```scheme
(define my-favourite-number 12)
```

and `main.lisp` contains this program:
and `main.mlts` contains this program:

```scheme
(import "foo.lisp")
(import "foo.mlts")
my-favourite-number
```

then evaluating `main.lisp` should print `12`, assuming that `foo.lisp` is in the same directory as `main.lisp`.
then evaluating `main.mlts` should print `12`, assuming that `foo.mlts` is in the same directory as `main.mlts`.

- Allow top-level definitions to reference each other in any order. As an example, when running the following file:

Expand Down
17 changes: 9 additions & 8 deletions pages/resources/lisp-workshop/step5.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
---
layout: page
title: "Lisp Workshop - Step 5: Lambda"
permalink: "/resources/lisp-workshop/step5"
title: "Step 5: Lambda | Language Workshop"
permalink: "/resources/lang-workshop/step5"
---
# THIS PAGE IS A WORK IN PROGRESS

Expand Down Expand Up @@ -188,16 +188,17 @@ These are some extra challenges you can attempt to build your understanding furt
For example:

```console
lisp> ((lambda (x y) x) 1 2)
MLTS> ((lambda (x y) x) 1 2)
1
lisp> ((lambda (x y z) (+ x (+ y z))) 1 2 3)
MLTS> ((lambda (x y z) (+ x (+ y z))) 1 2 3)
6
```

- Add support for `let` expressions. `let` is convenient syntactic sugar for temporarily binding an expression to a name. In Lisp, `let` expressions look as follows:
- Add support for `let` expressions. `let` is convenient syntactic sugar for temporarily binding an expression to a name. In Lisp-like syntax, `let` expressions look as follows:

```scheme
(let ((x 1))
(+ x 2))
(+ x 2))
```

This expression should return `3.
Expand All @@ -208,7 +209,7 @@ These are some extra challenges you can attempt to build your understanding furt
```scheme
(let ((x 1)
(y 2))
(+ x y))
(+ x y))
```

In a Haskell-like syntax, you might write that as:
Expand All @@ -225,7 +226,7 @@ These are some extra challenges you can attempt to build your understanding furt
(let ((s1 e1)
...
(sn en))
body)
body)
```

To evaluate a `let` expression, you extend the current environment with `s1 -> eval(e1), ..., sn -> eval(en)`, and evaluate `body` in this new environment.
Expand Down

0 comments on commit 9a7cff0

Please sign in to comment.