Commit Graph

8 Commits

Author SHA1 Message Date
Brent Yorgey
08730162bd
Don't set it variable when an exception was raised (#1922)
Fixes #1899.  It turned out the problem was not specifically with infinite loop detection but simply any time an exception was thrown and bubbled up to the top level.  After logging the exception the CESK machine returned `VUnit` and there was no way for the UI to tell the difference between a computation that ended successfully with value `VUnit` and one with an uncaught exception.  This PR adds a new special value `VExc` to denote the result of a computation that threw an exception.  The UI can then check for this and not set the `it` variable in that case.
2024-06-10 19:14:45 +00:00
Brent Yorgey
01c45ab968
Type synonyms (#1865)
Adds type synonym declarations to the language, like so:
```
tydef Maybe a = Unit + a end
def lookdown : Cmd (Maybe Text) = scan down end

> lookdown
it2 : Maybe Text = inl ()
```

`tydef` behaves very similarly to `def` except that it defines a new (parameterized) type instead of a new term.

Note that higher-kinded types are not yet supported; for example we cannot say `tydef Foo f = Unit + f Int` (in fact this would be a syntax error since type variables cannot be at the head of an application).  However there is nothing stopping us from adding that in the future; I just wanted to keep things simple for now.

If you know of any scenario code that would be a good candidate for using type synonyms, let me know --- it would be nice to update a few scenarios as a better stress test of type synonyms before merging this.

Closes #153.
2024-06-01 21:09:50 +00:00
Brent Yorgey
4bd409dd69
Parse comments (#1838)
Towards #1467.  This is incomplete but I thought it would be useful to split up the work into multiple PRs to avoid having a really massive one at the end.

This PR accomplishes a few things:
- Creates a new data type `Comment` to record the text of a comment, along with a bit of metadata giving its source location, whether it is a line or block comment, and whether it was on a line by itself or at the end of a line with some other non-comment tokens (this information will be used to decide which AST node to associate the comment with).
- Adds a field to store comments in every `Syntax'` node.  Note this is currently unused, since we haven't yet implemented the logic to insert comments into appropriate AST nodes (that will come in a later PR).
    - Note it's a bit annoying that the number of fields of each `Syntax'` node is growing.  I did look into consolidating and generalizing `Syntax'` to just have a single field for arbitrary annotations, but the changes required seemed annoying enough that I didn't want to bother.
2024-05-12 00:32:21 +00:00
Brent Yorgey
a8087b725a
Recommend cabal (#1822)
Changes to documentation etc. to recommend the use of `cabal` rather than `stack`.  Closes #1820.

- [x] Update `README.md`
- [x] Update `feedback.yaml`
- [x] Update scripts in `scripts/`
- [x] Update `CONTRIBUTING.md`
- [x] Maybe HLS defaults to using Stack now? Should we create a `hie.yaml.cabal` file with `cradle: cabal:`?
    - See https://discourse.haskell.org/t/whats-the-current-status-of-hls-cradle-discovery/8254/5
    - See https://github.com/Avi-D-coder/implicit-hie
2024-05-06 00:09:33 +00:00
Brent Yorgey
bf73f2acd9
Change appear command to take an optional attribute (#1807)
Also, fix a bug---it didn't work before if an appearance string of length 5 was given.

Closes #1230.

I don't know how to write an automated test for this, but you can see it working by *e.g.*:

- Start a creative game
- Execute e.g. `appear "DNESW" (inr "rock")`
- Observe that the base now looks like a rock-colored `N`
- Now do some `turn` commands (including `turn down`) and observe the appearance changing to e.g. `S` when facing south
2024-04-28 20:32:49 +00:00
Brent Yorgey
a4c8057a28
Records (#1148)
Add record types to the language: record values are written like `[x = 3, y = "hi"]` and have types like `[x : int, y : text]`.  Empty and singleton records are allowed.  You can project a field out of a record using standard dot notation, like `r.x`.  If things named e.g. `x` and `y` are in scope, you can also write e.g. `[x, y]` as a shorthand for `[x=x, y=y]`.

Closes #1093 .

#153 would make this even nicer to use.

One reason this is significant is that record projection is our first language construct whose type cannot be inferred, because if we see something like `r.x` all we know about the type of `r` is that it is a record type with at least one field `x`, but we don't know how many other fields it might have.  Without some complex stuff like row polymorphism we can't deal with that, so we just punt and throw an error saying that we can't infer the type of a projection.  To make this usable we have to do a better job checking types, a la #99 . For example `def f : [x:int] -> int = \r. r.x end` would not have type checked before, since when checking the lambda we immediately switched into inference mode, and then encountered the record projection and threw up our hands.  Now we work harder to push the given function type down into the lambda so that we are still in checking mode when we get to `r.x` which makes it work.  But it is probably easy to write examples of other things where this doesn't work.  Eventually we will want to fully implement #99 ; in the meantime one can always add a type annotation (#1164) on the record to get around this problem.

Note, I was planning to add a `open e1 in e2` syntax, which would take a record expression `e1` and "open" it locally in `e2`, so all the fields would be in scope within `e2`.  For example, if we had  `r = [x = 3, y = 7]` then instead of writing `r.x + r.y` you could write `open r in x + y`.  This would be especially useful for imports, as in `open import foo.sw in ...`.  However, it turns out to be problematic: the only way to figure out the free variables in `open e1 in e2` is if you know the *type* of `e1`, so you know which names it binds in `e2`.  (In all other cases, bound names can be determined statically from the *syntax*.)  However, in our current codebase there is one place where we get the free variables of an untyped term: we decide at parse time whether definitions are recursive (and fill in a boolean to that effect) by checking whether the name of the thing being defined occurs free in its body.  One idea might be to either fill in this boolean later, after typechecking, or simply compute it on the fly when it is needed; currently this is slightly problematic because we need the info about whether a definition is recursive when doing capability checking, which is currently independent of typechecking.

I was also planning to add `export` keyword which creates a record with all names currently in scope --- this could be useful for creating modules.  However, I realized that very often you don't really want *all* in-scope names, so it's not that useful to have `export`.  Instead I added record punning so if you have several variables `x`, `y`, `z` in scope that you want to package into a record, you can just write `[x, y, z]` instead of `[x=x, y=y, z=z]`.  Though it could still be rather annoying if you wanted to make a module with tons of useful functions and had to list them all in a record at the end...

Originally I started adding records because I thought it would be a helpful way to organize modules and imports.  However, that would require having records that contain fields with polymorphic types.  I am not yet sure how that would play out.  It would essentially allow encoding arbitrary higher-rank types, so it sounds kind of scary.  In any case, I'm still glad I implemented records and I learned a lot, even if they can't be used for my original motivation.

I can't think of a way to make a scenario that requires the use of records.  Eventually once we have proper #94 we could make a scenario where you have to communicate with another robot and send it a value of some required type.  That would be a cool way to test the use of other language features like lambdas, too.
2023-03-25 11:58:34 +00:00
Brent Yorgey
da4e0cec6c remove --flag=swarm:ci from feedback.yaml
Fixes #528.  I was being silly. No user will pass `--flag=swarm:ci`
when building with `stack`; I was doing so locally to get `-Werror` but
there's no particular reason I need to do that.
2022-07-04 21:33:04 -05:00
Brent Yorgey
b1e2c59b85 create feedback.yaml file
For use with https://github.com/NorfairKing/feedback .
2022-03-19 11:08:35 -05:00