Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
131 changes: 81 additions & 50 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ git clone [email protected]:mkantor/please-lang-prototype.git
cd please-lang-prototype
npm install
npm run build
echo '{@runtime, context => :context.program.start_time}' | ./please --output-format=json
echo '@runtime { context => :context.program.start_time }' | ./please --output-format=json
```

There are more example programs in [`./examples`](./examples).
Expand Down Expand Up @@ -45,7 +45,7 @@ data representation implied by the fact that a value is an atom (e.g. the atom
`2` may be an integer in memory).

Bare words not containing any
[reserved character sequences](./src/language/parsing/atom.ts#L33-L55) are
[reserved character sequences](./src/language/parsing/atom.ts#L34-L57) are
atoms:

```
Expand Down Expand Up @@ -178,13 +178,18 @@ expressions_. Most of the interesting stuff that Please does involves evaluating
keyword expressions.

Under the hood, keyword expressions are modeled as objects. For example, `:foo`
desugars to `{ @lookup, key: foo }`. All such expressions have a key `0`
referring to a value that is an `@`-prefixed atom (the keyword). Keywords
include `@apply`, `@check`, `@function`, `@if`, `@index`, `@lookup`, `@panic`,
and `@runtime`.
desugars to `{ 0: "@lookup", 1: { key: foo } }`. All such expressions have a
property named `0` referring to a value that is an `@`-prefixed atom (the
keyword). Most keyword expressions also require a property named `1` to pass an
argument to the expression. Keywords include `@apply`, `@check`, `@function`,
`@if`, `@index`, `@lookup`, `@panic`, and `@runtime`.

Currently only `@function`, `@lookup`, `@index`, and `@apply` have syntax
sugars.
In addition to the specific syntax sugars shown above, any keyword expression
can be written using a generalized sugar:

```
@keyword { … } // desugars to `{ 0: "@keyword", 1: { … } }`
```

### Semantics

Expand All @@ -210,7 +215,7 @@ function from other programming languages, except there can be any number of
`@runtime` expressions in a given program. Here's an example:

```
{@runtime, context => :context.program.start_time}
@runtime { context => :context.program.start_time }
```

Unsurprisingly, this program outputs the current time when run.
Expand Down Expand Up @@ -249,7 +254,7 @@ Take this example `plz` program:
{
language: Please
message: :atom.prepend("Welcome to ")(:language)
now: {@runtime, context => :context.program.start_time}
now: @runtime { context => :context.program.start_time }
}
```

Expand All @@ -259,40 +264,58 @@ It desugars to the following `plo` program:
{
language: Please
message: {
0: @apply
function: {
0: @apply
0: "@apply"
1: {
function: {
0: @index
object: {
0: @lookup
key: atom
0: "@apply"
1: {
function: {
0: "@index"
1: {
object: {
0: "@lookup"
1: {
key: atom
}
}
query: {
0: prepend
}
}
}
argument: "Welcome to "
}
query: {
0: prepend
}
argument: {
0: "@lookup"
1: {
key: language
}
}
argument: "Welcome to "
}
argument: {
0: @lookup
key: language
}
}
now: {
0: @runtime
0: "@runtime"
1: {
0: @function
parameter: context
body: {
0: @index
object: {
0: @lookup
key: context
}
query: {
0: program
1: start_time
0: {
0: "@function"
1: {
parameter: context
body: {
0: "@index"
1: {
object: {
0: "@lookup"
1: {
key: context
}
}
query: {
0: program
1: start_time
}
}
}
}
}
}
Expand All @@ -307,19 +330,27 @@ Which in turn compiles to the following `plt` program:
language: Please
message: "Welcome to Please"
now: {
0: @runtime
function: {
0: @function
parameter: context
body: {
0: @index
object: {
0: @lookup
key: context
}
query: {
0: program
1: start_time
0: "@runtime"
1: {
function: {
0: "@function"
1: {
parameter: context
body: {
0: "@index"
1: {
object: {
0: "@lookup"
1: {
key: context
}
}
query: {
0: program
1: start_time
}
}
}
}
}
}
Expand All @@ -333,7 +364,7 @@ Which produces the following runtime output:
{
language: Please
message: "Welcome to Please"
now: "2025-02-14T18:45:14.168Z"
now: "2025-05-13T22:47:50.802Z"
}
```

Expand Down
20 changes: 9 additions & 11 deletions examples/fibonacci.plz
Original file line number Diff line number Diff line change
@@ -1,20 +1,18 @@
{
fibonacci: n => {
@if
condition: :n < 2
then: :n
else: :fibonacci(:n - 1) + :fibonacci(:n - 2)
}
fibonacci: n =>
@if {
condition: :n < 2
then: :n
else: :fibonacci(:n - 1) + :fibonacci(:n - 2)
}

input: {
@runtime
context => :context.arguments.lookup(input)
input: @runtime { context =>
:context.arguments.lookup(input)
}

output: :input match {
none: _ => "missing input argument"
some: input => {
@if
some: input => @if {
condition: :natural_number.is(:input)
then: :fibonacci(:input)
else: "input must be a natural number"
Expand Down
4 changes: 2 additions & 2 deletions examples/kitchen-sink.plz
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,6 @@
add_one: :integer.add(1)
three: :add_one(:two)
function: x => { value: :x }
conditional_value: :function({ @if, :sky_is_blue, :two, :three })
side_effect: { @runtime, context => :context.log("this goes to stderr") }
conditional_value: :function(@if { :sky_is_blue, :two, :three })
side_effect: @runtime { context => :context.log("this goes to stderr") }
}
16 changes: 7 additions & 9 deletions examples/lookup-environment-variable.plz
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,12 @@
* Given CLI arguments like `--variable=FOO`, looks up the environment
* variable named `FOO`.
*/
{
@runtime
context =>
:context.arguments.lookup(variable) match {
@runtime { context =>
:context.arguments.lookup(variable) match {
none: {}
some: :context.environment.lookup >> :match({
none: {}
some: :context.environment.lookup >> :match({
none: {}
some: :identity
})
}
some: :identity
})
}
}
Loading