Skip to content

Commit

Permalink
Merge pull request #738 from nevalang/ternary
Browse files Browse the repository at this point in the history
feat(parser): ternary
  • Loading branch information
emil14 authored Oct 28, 2024
2 parents 3c9aab5 + 603ec14 commit 8b0722a
Show file tree
Hide file tree
Showing 172 changed files with 2,378 additions and 1,962 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ Hello, World!
If you open `my_awesome_project/src/main.neva` with your favorite IDE you'll see this

```neva
flow Main(start) (stop) {
def Main(start) (stop) {
Println
---
:start -> ('Hello, World!' -> println -> :stop)
Expand Down
6 changes: 3 additions & 3 deletions cmd/lsp/server/get_file_view.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,9 @@ func (s *Server) GetFileView(glspCtx *glsp.Context, req GetFileViewRequest) (Get

scope := src.Scope{
Location: src.Location{
ModRef: s.index.EntryModRef,
PkgName: pkgName,
FileName: fileName,
Module: s.index.EntryModRef,
Package: pkgName,
Filename: fileName,
},
Build: *s.index,
}
Expand Down
2 changes: 1 addition & 1 deletion docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,4 @@ This document provides an overview of Nevalang, its core concepts, and how to us
- [Components](./components.md)
- [Networks](./networks.md)
- [Style Guide](./style_guide.md)
- [FAQ](./faq.md)
- [Questions and Answers](./qa.md)
26 changes: 13 additions & 13 deletions docs/components.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ Native components have only a signature (interface) and no implementation in Nev

```neva
#extern(and)
flow And(a bool, b bool) (res bool)
def And(a bool, b bool) (res bool)
```

> Native components may enable future Go-interop, allowing Go functions to be called from Neva using #extern.
Expand All @@ -21,7 +21,7 @@ Overloaded native components use a modified extern directive: `#extern(t1 f1, t2

```neva
#extern(int int_add, float float_add, string string_add)
pub flow Add<T int | float | string>(acc T, el T) (res T)
pub def Add<T int | float | string>(acc T, el T) (res T)
```

Usage:
Expand All @@ -39,7 +39,7 @@ Normal components are implemented in Nevalang source code. As a Nevalang program
Minimal nevalang program is one normal component `Main` without any nodes and with network of a single connection:

```neva
flow Main(start) (stop) {
def Main(start) (stop) {
:start -> :stop
}
```
Expand All @@ -51,7 +51,7 @@ Components however typically perform data transformations or side-effects using
Normal component `Main` with a `println` node (instance of `Println`):

```neva
flow Main(start) (stop) {
def Main(start) (stop) {
Println
---
:start -> (42 -> println -> :stop)
Expand All @@ -61,7 +61,7 @@ flow Main(start) (stop) {
As you can see, we refer to the instance of `Println` as `println`. The compiler implicitly assigns a lowercase version of the component name to its instance. However, with multiple instances of the same component, this leads to name collisions. To avoid this, the compiler requires explicit naming for nodes in such cases. Example:

```neva
flow Main(start) (stop) {
def Main(start) (stop) {
p1 Println
p2 Println
---
Expand All @@ -78,7 +78,7 @@ Normal components actually have implicit `in` and `out` nodes. Even in our `Main
Consider an application that performs some business logic with logging:

```neva
flow App(data) (sig) {
def App(data) (sig) {
Logic, Log
---
:data -> logic -> log -> :sig
Expand All @@ -88,7 +88,7 @@ flow App(data) (sig) {
Now imagine we want to replace `Logger` with another component based on a condition. Let's say we want to use real logger in production and a mock in testing. Without dependency injection (DI), we'd have to extend the interface with a `flag bool` inport and check it.

```neva
flow App(data any, prod bool) (sig any) {
def App(data any, prod bool) (sig any) {
Cond, Logic, ProdLogger, MockLogger
---
:data -> businessLogic -> cond:data
Expand All @@ -112,7 +112,7 @@ type ILog(data) (sig)
Next, define the dependency (interface node):

```neva
flow App(data) (sig) {
def App(data) (sig) {
Logic, ILog
---
:data -> logic -> iLog -> :sig
Expand All @@ -125,7 +125,7 @@ Before dependency injection:

```neva
// cmd/app
flow Main(start) (stop)
def Main(start) (stop)
App
...
}
Expand All @@ -134,12 +134,12 @@ flow Main(start) (stop)
After dependency injection:

```neva
flow Main(start) (stop)
def Main(start) (stop)
App{ProdLogger}
...
}
flow Test(start) (stop)
def Test(start) (stop)
App{MockLogger}
...
}
Expand Down Expand Up @@ -175,13 +175,13 @@ Components contain interfaces, which may have type parameters. Type arguments mu
Component uses type-parameters for its IO:

```neva
flow Foo<T>(data T) (sig any)
def Foo<T>(data T) (sig any)
```

Components can pass type parameters from their interface to node expressions:

```neva
flow Bar<T>(data T) (sig any) {
def Bar<T>(data T) (sig any) {
Println<T>
---
:data -> println -> :sig
Expand Down
4 changes: 2 additions & 2 deletions docs/constants.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ It's possible to use constant as a network-sender by refering to it with `$` pre
```neva
const one int = 1
flow Inc(data int) (res int) {
def Inc(data int) (res int) {
Add
---
$one -> add:acc
Expand All @@ -40,7 +40,7 @@ flow Inc(data int) (res int) {
You can omit explicit constants; the compiler will create and refer to them implicitly.

```neva
flow Inc(data int) (res int) {
def Inc(data int) (res int) {
Add
---
1 -> add:acc
Expand Down
8 changes: 4 additions & 4 deletions docs/directives.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ Tells compiler a component lacks source code implementation and requires a runti

```neva
#extern(println)
pub flow Println<T>(data T) (sig T)
pub def Println<T>(data T) (sig T)
```

### Overloading
Expand All @@ -17,7 +17,7 @@ Native components can be overloaded using `#extern(t1 f1, t2 f2, ...)`. These co

```neva
#extern(int int_add, float float_add, string string_add)
pub flow Add<T int | float | string>(acc T, el T) (res T)
pub def Add<T int | float | string>(acc T, el T) (res T)
```

## `#bind`
Expand All @@ -27,7 +27,7 @@ Instructs compiler to insert a given message into a runtime function call for no
```neva
const greeting string = 'Hello, World!'
flow Main(start any) (stop any) {
def Main(start any) (stop any) {
#bind(greeting)
greeting New<string>
println Println<string>
Expand All @@ -47,5 +47,5 @@ Derives component inports from its type-argument structure fields, rather than d
```neva
#autoports
#extern(struct_builder)
pub flow Struct<T struct {}> () (msg T)
pub def Struct<T struct {}> () (msg T)
```
2 changes: 1 addition & 1 deletion docs/interfaces.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ Syntax `add[i]` is shorthand for `add:data[i]`. The compiler infers the port nam
Another example of a component that benefits from array-ports is `Switch`. It's used for routing - imagine we have message `data` and need to route it to different destinations based on its value. For example, if it's `a` we send it to the first destination, if `b` to the second, and `c` to the third. Otherwise, we send it to a default destination to handle unknown values. An adhoc solution with a fixed number of ports wouldn't scale. We need a generic component with dynamic port support. Here's the Switch signature:

```neva
pub flow Switch<T>(data T, [case] T) ([case] T, else T)
pub def Switch<T>(data T, [case] T) ([case] T, else T)
```

This allows code like this
Expand Down
26 changes: 13 additions & 13 deletions docs/networks.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ foo:bar[3] -> baz:bax3
Components can't receive from their own array-inports by slot. Consider this scenario:

```neva
flow Foo([data]) (sig) {
def Foo([data]) (sig) {
:data[0] -> ...
:data[1] -> ...
:data[3] -> ...
Expand All @@ -91,7 +91,7 @@ This defeats the purpose of array-ports, which are needed for unknown situations
However, components can send to and receive from other nodes' array-ports by index:

```neva
flow Foo(sig) (sig) {
def Foo(sig) (sig) {
Bar
---
:sig -> bar[0]
Expand Down Expand Up @@ -157,7 +157,7 @@ We'll create a component that increments a number using an addition component wi
```neva
const one int = 1
flow Inc(data int) (res int) {
def Inc(data int) (res int) {
Add
---
$one -> add:acc
Expand All @@ -178,15 +178,15 @@ Const-ref and msg-literal senders are syntax sugar. In the desugared program, al

```neva
#extern(new)
pub flow New<T>() (msg T)
pub def New<T>() (msg T)
```

It's one of the few components without inports or outports, which are only allowed in stdlib. User-created components must have at least 1 inport and outport. New instances require the `#bind` directive to associate a constant with the node, allowing the runtime to use it throughout the program's lifecycle.

```
const p float = 3.14
flow Main(start) (stop) {
def Main(start) (stop) {
#bind(p)
New
Println
Expand All @@ -202,7 +202,7 @@ Message literal senders are implemented similarly, with the compiler inserting a
Sometimes it's convenient to refer to message values directly in the network without creating a dedicated constant. This works the same as using constants, as both are syntax sugar for creating an emitter-node with a bound message.

```neva
flow Inc(data int) (res int) {
def Inc(data int) (res int) {
Add
---
1 -> add:acc
Expand Down Expand Up @@ -243,7 +243,7 @@ We've seen port-address receivers:
If a component has one inport and one outport

```neva
flow Foo(a) (b)
def Foo(a) (b)
```

A "chained" connection is allowed
Expand All @@ -262,7 +262,7 @@ foo:b -> ...
Here's an example using this feature:

```neva
flow Foo(data) (sig) {
def Foo(data) (sig) {
Println
---
:data -> println -> :sig
Expand All @@ -276,7 +276,7 @@ flow Foo(data) (sig) {
println -> :sig
```

Components don't need matching inport and outport names. Chained connections require one port per side. Both `flow Foo(bar) (bar)` and `flow Foo (bar) (baz)` are valid.
Components don't need matching inport and outport names. Chained connections require one port per side. Both `def Foo(bar) (bar)` and `def Foo (bar) (baz)` are valid.

Chained connections can nest infinitely:

Expand Down Expand Up @@ -318,7 +318,7 @@ To ensure `42` is printed once, synchronize it with `:start` using "defer". Here
This syntax sugar inserts a `Lock` node between `:start` and `42`. Here's the desugared version:

```neva
flow Main(start) (stop) {
def Main(start) (stop) {
Lock, Println
---
:start -> lock:sig
Expand Down Expand Up @@ -432,13 +432,13 @@ Fan-in and fan-out can be combined:
There's one more connection type to discuss: array-bypass for components with array-ports.

```neva
flow FanInWrap([data]) (res)
def FanInWrap([data]) (res)
```

Such components can't refer to their ports by index (e.g., `data[i]`). To operate on these ports, we use array-bypass.

```neva
flow FanInWrap([data]) (res) {
def FanInWrap([data]) (res) {
FanIn
---
:data => fanIn
Expand All @@ -450,7 +450,7 @@ The `=>` operator indicates an array-bypass connection, where both sender and re
Let's examine a specific example to understand how it works:

```neva
flow Main() () {
def Main() () {
wrap FanInWrap, Println
---
1 -> wrap[0]
Expand Down
16 changes: 8 additions & 8 deletions docs/program_structure.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ neva: 0.26.0
deps:
github.com/nevalang/x:
path: github.com/nevalang/x
version: 0.0.12
version: 0.0.13
```
The `deps` field is a map where each dependency has an alias. When adding dependencies via CLI (e.g., `neva get github.com/nevalang/x`), the package manager automatically inserts a key-value pair. Third-party dependencies must have a valid git-clone path and a fixed semver version. The package manager uses git to download the repo and looks for the corresponding git-tag. The alias typically defaults to the module's path, but custom aliases allow multiple versions of the same module:
Expand All @@ -55,7 +55,7 @@ neva: 0.26.0
deps:
github.com/nevalang/x@0-0-12:
path: github.com/nevalang/x
version: 0.0.12
version: 0.0.13
github.com/nevalang/x@0-0-11:
path: github.com/nevalang/x
version: 0.0.11
Expand All @@ -71,7 +71,7 @@ Module references uniquely identify modules in a build, used by the compiler to

```yaml
path: github.com/nevalang/x
version: 0.0.12
version: 0.0.13
```

### Entry Module
Expand Down Expand Up @@ -167,7 +167,7 @@ Here we see 3 packages: `main`, `foo` and `foo/bar`. Note that `foo/bar` is a se
The main package must contain a `Main` component with a specific signature and no exports.

```neva
flow Main(start) (stop) {
def Main(start) (stop) {
// ...
}
```
Expand All @@ -188,7 +188,7 @@ A `.neva` file contains imports and entities. Files organize packages for readab
pub type myFloat float
// pkg/bar.neva
flow Bar(mf myFloat) (sig) {
def Bar(mf myFloat) (sig) {
// ...
}
```
Expand Down Expand Up @@ -287,7 +287,7 @@ pub const p int = 3.14
// foo/bar/bar.neva
import { @/foo }
pub flow AddP(el float) (res float) {
pub def AddP(el float) (res float) {
Add<float>
---
$foo.p -> add:acc
Expand All @@ -301,7 +301,7 @@ import {
@/foo/bar
}
flow Main(start) (stop) {
def Main(start) (stop) {
bar.AddP, Println
---
:start -> ($foo.p -> addP:el -> println -> :stop)
Expand All @@ -316,7 +316,7 @@ Third-party imports are imports of packages located in third-party modules - mod
deps:
github.com/nevalang/x:
path: github.com/nevalang/x
version: 0.0.12
version: 0.0.13
```

Then when you `import { github.com/nevalang/x }` compiler will know exactly path and version of the module you are referring to.
Expand Down
Loading

0 comments on commit 8b0722a

Please sign in to comment.