Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Lispy Syntax #58

Merged
merged 59 commits into from
Jun 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
59 commits
Select commit Hold shift + click to select a range
bfc412c
wip: remove old function stuff
Vandesm14 Jun 13, 2024
f1720f7
remove function parsing from parser
Vandesm14 Jun 13, 2024
d664446
add the is_function method back
Vandesm14 Jun 13, 2024
27ced2e
add helper methods for functions
Vandesm14 Jun 13, 2024
cb0b8f0
fix engine code
Vandesm14 Jun 13, 2024
3cea2ab
fix scope
Vandesm14 Jun 13, 2024
49528ca
clear warnings
Vandesm14 Jun 13, 2024
4cf12a3
fix debugger lib
Vandesm14 Jun 13, 2024
4873728
update parsing of functions
Vandesm14 Jun 13, 2024
6a0566f
fix displaying of fn symbol
Vandesm14 Jun 13, 2024
0ef67ed
make fn blue for debugger too
Vandesm14 Jun 13, 2024
9cb8170
fix display of fn symbols
Vandesm14 Jun 13, 2024
83149ba
update intrinsics to support functions as lists
Vandesm14 Jun 13, 2024
bc5465a
add function to function
Vandesm14 Jun 13, 2024
b1f2033
fix behavior of functions in list intrinsics
Vandesm14 Jun 13, 2024
70c8575
update docs
Vandesm14 Jun 13, 2024
5414b37
add tests
Vandesm14 Jun 25, 2024
45df078
add s-expression and underscore ExprKinds
Vandesm14 Jun 25, 2024
4571501
revise SExpr to have call and body
Vandesm14 Jun 25, 2024
ff78b57
test handling args in s-exprs
Vandesm14 Jun 25, 2024
ad54417
parse SExprs
Vandesm14 Jun 25, 2024
2752ea8
allow new exprs to be equal
Vandesm14 Jun 25, 2024
a219237
correctly parse s-exprs
Vandesm14 Jun 25, 2024
f8f35c8
parse underscores
Vandesm14 Jun 25, 2024
159bc97
run the call of an sexpr
Vandesm14 Jun 25, 2024
066f40d
update tests
Vandesm14 Jun 25, 2024
57e790f
eagerly evaluate SExprs
Vandesm14 Jun 25, 2024
8b1195b
flip some intrinsic methods
Vandesm14 Jun 25, 2024
872db37
records can be checked for eq
Vandesm14 Jun 25, 2024
6a6a8b0
add more tests
Vandesm14 Jun 25, 2024
b4cdb5f
square brackets are for lists
Vandesm14 Jun 25, 2024
a110a51
lex square brackets
Vandesm14 Jun 25, 2024
74b9e78
add run cmd to justfile
Vandesm14 Jun 25, 2024
3543631
display lists with square brackets
Vandesm14 Jun 25, 2024
d4ba152
fix some integration tests
Vandesm14 Jun 25, 2024
773a330
fix some engine tests
Vandesm14 Jun 25, 2024
789174d
flip all intrinsics
Vandesm14 Jun 25, 2024
3cf5842
fix expr debug
Vandesm14 Jun 25, 2024
df5dfd0
evaluate all args of a SExpr
Vandesm14 Jun 25, 2024
73389a5
fix ordering for insert
Vandesm14 Jun 25, 2024
5b861a4
fix list tests
Vandesm14 Jun 25, 2024
3ed1713
fix record tests
Vandesm14 Jun 25, 2024
8dde842
actually fix record tests (hashmaps are random)
Vandesm14 Jun 25, 2024
d747945
remove args for stack ops
Vandesm14 Jun 25, 2024
0de7fc1
fix debugger
Vandesm14 Jun 25, 2024
3564a09
remove ansi codes from printing
Vandesm14 Jun 25, 2024
d3c0a55
silently push and pop without making journal ops
Vandesm14 Jun 25, 2024
f41f96d
inherit debug info from parent expr
Vandesm14 Jun 25, 2024
9fe9bf9
allow intrinsics to work without reversing
Vandesm14 Jun 25, 2024
caaacd8
add a TODO
Vandesm14 Jun 25, 2024
813dcd6
fix ordering for some exprs
Vandesm14 Jun 25, 2024
6f9973e
remove voradic arity test
Vandesm14 Jun 25, 2024
4d6c18f
remove extra curly
Vandesm14 Jun 25, 2024
97ff15a
add mismatched paren error
Vandesm14 Jun 25, 2024
d9b1084
move lispy tests to engine.rs
Vandesm14 Jun 26, 2024
aa585a1
add docs hosting to justfile
Vandesm14 Jun 26, 2024
758d9fc
update all docs
Vandesm14 Jun 26, 2024
3d0f210
add doc on lispy syntax
Vandesm14 Jun 26, 2024
27dce44
use non-lispy syntax for engine tests
Vandesm14 Jun 29, 2024
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
1 change: 1 addition & 0 deletions docs/src/SUMMARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
- [Functions](introduction/functions.md)
- [Loops](introduction/loops.md)
- [Scopes](introduction/scopes.md)
- [Lispy Syntax](introduction/lispy-syntax.md)

# Reference

Expand Down
13 changes: 6 additions & 7 deletions docs/src/introduction/functions.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,20 @@
Though lists are a nice way of bundling code, they aren't perfect. For example, when creating your own named function, you will have to call it manually.

```clojure
'(1 +) 'add-one def
'[1 +] 'add-one def

1 add-one
;; Pushes the list to the stack, but doesn't call it
;; [1] -> [1 (1 +)]
;; [1] -> [1 [1 +]]

call
;; Calls the list
;; [1 (1 +)] -> [2]
;; [1 [1 +]] -> [2]
```

## The `fn` Expression

For this reason, Stack provides the `fn` expression to mark lists as functions. Internally, what you see as a "function" is actually just a list with the `fn` expression at the beginning.
For this reason, Stack provides parenthetical syntax to create functions. They function similar to lists, except they start with a `fn` or `fn!` identifier and use parenthesis instead of square brackets.

```clojure
'(fn 1 +) 'add-one def
Expand All @@ -32,11 +32,10 @@ Notice how we didn't need to call the list manually? That's because the `fn` exp

## Functions are Lists

Because functions are just marked lists, you can still use them as lists. This means that you can build and modify functions at runtime.
Though functions are different from lists, you can still use most of the list methods on them (more info [here](../reference/builtins.md)). This means that you can build and modify functions at runtime.

```clojure
'()
'fn push
'(fn)
1 push
'+ push
;; [] -> [(fn 1 +)]
Expand Down
202 changes: 202 additions & 0 deletions docs/src/introduction/lispy-syntax.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,202 @@
# Lispy Syntax

As you have seen, Stack uses postfix notation, meaning the data comes first, then the operations are applied to it. For example:

```clj
10 2 -
;; 8
```

## S-Expressions

Stack supports lisp-like (s-expressions) syntax out-of-the-box. For the example above, you could change it to:

```clj
(- 10 2)
;; 8
```

## Eager Evaluation

You can also add *most* operations within the s-expressions, which will be evaluated eagerly:

```clj
(- 10 (+ 1 1))
;; 8

(- 10 (fn (def 'a 2) a))
;; 8
```

**Important:** Functions and lists need to be lazy when used as the body of an `if` or `let`:

**`if`:**

```clj
;; As the argument: Shouldn't be lazy
(if (fn true) '("hey" print))
;; Prints "hey"

;; As the body: Should be lazy
(if true '(fn "hey" print))
;; Prints "hey"

;; Example with list as the body
(if true '["hey" print])
;; Prints "hey"

;; Example with a lazy-lazy list as the body (returns the list if true)
(if true ''["hey" print])
;; Pushes `["hey" print]` to the stack

;; Example with s-expression as the body
(if true '(print "hey"))
```

**`let`:**

```clj
;; BOTH arguments should be lazy (the symbol list and the body)

;; Lists
10 2 (let '[a b] '[a b -])
;; 8

;; Functions
10 2 (let '[a b] '(fn a b -))
;; 8

;; S-Expressions
10 2 (let '[a b] '(- a b))
;; 8
```

This is due to the eager evaluation, where both functions, lists, and s-expressions, if unlazied, will be called during eager evaluation. This can be useful for lambdas within the arguments of an s-expression

## The Underscore

To include items from the stack within an s-expression, you can use `_`. The underscore will pop the last item from the stack and use it as the argument in its place.

```clj
10 2
;; b a
(- _ _)
;; (- a b) -> (- 2 10) -> -8
```

In this example, you can see that the underscores don't order the arguments as they are visually. Instead, as the evaluator goes from left to right, they pop an item from the stack. The **first** underscore will pop the **last** item from the stack and the **second** underscore will pop the **second-to-last** item from the stack.

## Parity with Stack-based syntax

Stack's lispy syntax is one-to-one with the visual ordering of all intrinsics, except for a select few, which we will talk about in a bit.

For example, these expressions are all equivalent:

```clj
10 2 -
;; 8

(- 10 2)
;; 8

10 (- _ 2)
;; 8

2 (- 10 _)
;; 8
```

### Exceptions

For ergonomics, Stack modifies the syntax for list and record intrinsics.

**List of Exceptions:**
- `push`
- `insert`
- `let`
- `def`
- `set`

In this case, these `push` operations are equal:

```clj
0 '[] push
;; [0]

(push '[] 0)
;; [0]
```

```clj
"value" "key" {} insert
;; {key: "value"}

(insert {} "key" "value")
;; {key: "value"}
```

In these cases, the arguments for the s-expressions are reversed in comparison with the stack-based syntax. This is to aid in readability and intuition. The non-lisp syntax doesn't do this for aid in more efficient insertions or pushes:

```clj
3 2 1 '[] push push push
;; [1 2 3]

"bar" "foo" "value" "key" {} insert insert
;; {key: "value", foo: "bar"}
```

That said, pulling items from the stack works the same as with the other intrinsics:

```clj
{}
(insert _ "key" "value")
;; {key: "value"}

"key" {}
(insert _ _ "value")
;; {key: "value"}

"value" {}
(insert _ "key" _)
;; {key: "value"}

"value"
(insert {} "key" _)
;; {key: "value"}
```

Here are examples for the last few exceptions:

**Let:**

```clj
10 2 '[(- a b)] '[a b] let
;; 8

10 2 (let '[a b] '(- a b))
;; 8
```

**Def:**

```clj
0 'a def

(def 'a 0)

0 'a (def _ _)
```

**Set:**

```clj
;; define so we can set
0 'a def
;; end of boilerplate

1 'a set

(set 'a 1)

1 'a (set _ _)
```
44 changes: 22 additions & 22 deletions docs/src/introduction/lists.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,33 +4,33 @@ Lists are similar to arrays or vectors in other programming languages. A list ca

## Defining a List

Lists are defined using the `'()` syntax. The items inside of a list are separated by spaces.
Lists are defined using the `'[]` syntax. The items inside of a list are separated by spaces.

```clojure
'(1 2 3 4 5)
'[1 2 3 4 5]
```

## Eager Evaluation

When lists are pushed to the stack, the items inside of the list are evaluated in-order (due to the [purification](../introduction/stack#purification) step).

```clojure
(1 2 3)
[1 2 3]

;; Results in `1 2 3`
;; [] -> [(1 2 3)] -> [1 2 3]
;; [] -> [[1 2 3]] -> [1 2 3]

(2 2 +)
[2 2 +]

;; Results in `4`
;; [] -> [(2 2 +)] -> [4]
;; [] -> [[2 2 +]] -> [4]

2 'var def

(var)
[var]

;; Results in `2`
;; [] -> [(var)] -> [2]
;; [] -> [[var]] -> [2]
```

### Laziness
Expand All @@ -40,32 +40,32 @@ Symbols (variables) inside of lazy lists will not be evaluated.
```clojure
2 'var def

'(var)
'[var]

;; Results in `(var)`
;; [] -> [(var)]
;; Results in `[var]`
;; [] -> [[var]]
```

Instead, to add variables into a list, it will need to be created manually.

```clojure
2 'var def

var '() push
var '[] push

;; Results in `(2)`
;; [] -> [(2)]
;; Results in `[2]`
;; [] -> [[2]]
```

You can make specific items inside of a list lazy by prefixing `'` to the beginning of the item.

```clojure
(2 2 '+)
[2 2 '+]

;; Results in the items being pushed to the stack, but the `+` will not be not called
;; [] -> [2 2 +]

(2 2 + 5 '*)
[2 2 + 5 '*]

;; Results in the items being pushed to the stack, and only the `*` will not be called
;; [] -> [4 5 *]
Expand All @@ -78,7 +78,7 @@ Lazy lists can be called which will run each expression in the list in-order (le
Calling a list exhibits the same behavior as the [purification](../introduction/stack.md#purification) step.

```clojure
'(2 2 +) call
'[2 2 +] call

;; Results in `4` being pushed to the stack
;; [] -> [2 2 +] -> [4]
Expand All @@ -87,7 +87,7 @@ Calling a list exhibits the same behavior as the [purification](../introduction/
If a list contains callable items such as other lists, those will also be run.

```clojure
'((2 2 +)) call
'[[2 2 +]] call

;; Results in `4` being pushed to the stack
;; [] -> [2 2 +] -> [4]
Expand All @@ -96,16 +96,16 @@ If a list contains callable items such as other lists, those will also be run.
To change this behavior, any callable items should be made lazy when adding them to the list to ensure that they won't be called.

```clojure
'(2 2 +)
'[2 2 +]
;; (2 2 +)
lazy
;; '(2 2 +)
'() push
'[] push
;; ('(2 2 +))
call

;; Results in `(2 2 +)` being pushed to the stack
;; [] -> [(2 2 +)] -> [(2 2 +)]
;; Results in `[2 2 +]` being pushed to the stack
;; [] -> [[2 2 +]] -> [[2 2 +]]
```

<!-- **Note: Running `call` on a list doesn't provide the same behavior as the [purification](../introduction/stack#purification) step. It evaluates the items in the list, and doesn't keep the items inside the bounds of the list. To keep the items inside the bounds of the list, you can use the `call-list` operator.** -->
Expand Down
4 changes: 2 additions & 2 deletions docs/src/introduction/loops.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ Similar to functional programming languages such as Clojure, Stack utilizes recu
;; Function isn't lazy so it runs right away
(fn
;; Our if block
'(
'[
;; Push i to the stack
i

Expand All @@ -18,7 +18,7 @@ Similar to functional programming languages such as Clojure, Stack utilizes recu

;; Recur
recur
)
]

;; Check if i is less than 5
i 5 <
Expand Down
Loading
Loading