mirror of
https://github.com/Dummi26/mers.git
synced 2025-03-10 05:43:53 +01:00
full rewrite, kinda works
This commit is contained in:
parent
16258c7a0a
commit
b81dac682e
4
.gitignore
vendored
4
.gitignore
vendored
@ -1,5 +1,9 @@
|
||||
/mers/target
|
||||
/mers/Cargo.lock
|
||||
/mers_lib/target
|
||||
/mers_lib/Cargo.lock
|
||||
/mers_old/target
|
||||
/mers_old/Cargo.lock
|
||||
/mers_libs/*/target
|
||||
/mers_libs/*/Cargo.lock
|
||||
/build_scripts/nu_plugin_mers
|
||||
|
326
README.md
326
README.md
@ -1,302 +1,54 @@
|
||||
# mers 
|
||||
# mers
|
||||
|
||||
Mers is a simple and reliable programming language.
|
||||
Mers is getting a rewrite!
|
||||
|
||||
While mers can already be used right now,
|
||||
many things about it will still change -
|
||||
at least until 1.0.0 is released.
|
||||
For serious projects, this probably
|
||||
isn't the language you should be using.
|
||||
This means that this README isn't complete,
|
||||
many things will change,
|
||||
and the docs/ are for a completely different language.
|
||||
|
||||
## Why mers?
|
||||
## why mers?
|
||||
|
||||
### it doesn't crash
|
||||
### Simplicity
|
||||
|
||||
If your program starts, **it won't crash**.
|
||||
The only exceptions to this are
|
||||
the builtin `exit()` function, which can exit with a nonzero exit code
|
||||
and the builtin `assume` functions, similar to Rust's `unwrap()`.
|
||||
Mers aims to be very simple, as in, having very few "special" things.
|
||||
But this means that many things you may be familiar with simply don't exist in mers,
|
||||
because they aren't actually needed.
|
||||
This includes:
|
||||
|
||||
This is because **errors in mers are values**,
|
||||
and the compiler forces you to handle them.
|
||||
**Exceptions**, because errors in mers are values just like any other.
|
||||
A function to read a UTF-8 text file from disk could have a return type like `String/IOError/UTF8DecodeError`,
|
||||
which tells you exactly what errors can happen and also forces you to handle them (see later for mers' type-system).
|
||||
|
||||
### type system
|
||||
**Loops**, because, as it turns out, a function which takes an iterable and a function can do this just fine.
|
||||
Javascript has `forEach`, Rust `for_each`, C# `Each`, and Java has `forEach`.
|
||||
At first, it seemed stupid to have a function that does the same thing a `for` or `foreach` loop can already do,
|
||||
but if a function can do the job, why do we even need a special language construct to do this for us?
|
||||
To keep it simple, mers doesn't have any loops except for `loop`.
|
||||
`loop` simply repeats until the inner expression returns `(T)`, which causes loop to return `T`.
|
||||
|
||||
In mers, a type can consist of multiple types:
|
||||
**Breaks** aren't necessary, since this can be achieved using iterator magic or by returning `(T)` in a `loop`.
|
||||
It is also similar to `goto`, because it makes control flow less obvious, so it had to go.
|
||||
|
||||
```
|
||||
// bool
|
||||
half := true
|
||||
// float/int
|
||||
number := if half { 0.5 } else { 1 }
|
||||
The same control flow obfuscation issue exists for **returns**, so these also aren't a thing.
|
||||
A function simply returns the value created by its expression.
|
||||
|
||||
// some math functions that work with int and float
|
||||
fn absolute_value(num int/float) {
|
||||
if num >= 0 { num } else { 0 - num }
|
||||
}
|
||||
fn difference(a int/float, b int/float) {
|
||||
absolute_value(a - b)
|
||||
}
|
||||
**Functions** do exist, but they have one key difference: They take exactly one argument. Always.
|
||||
Why? Because we don't need anything else.
|
||||
A function with no arguments now takes an empty tuple `()`,
|
||||
a function with two arguments now takes a two-length tuple `(a, b)`,
|
||||
a function with either zero, one, or three arguments now takes a `()/(a)/(a, b, c)`,
|
||||
and a function with n args takes a list, or a list as part of a tuple, or an optional list via `()/<the list>`.
|
||||
|
||||
debug(difference(0.2, 2)) // float: 1.8
|
||||
// although difference can work with floats, since we only give it ints, it also returns an int.
|
||||
// this is known at compile time.
|
||||
debug(difference(20, 5)) // int: 15
|
||||
```
|
||||
### Types
|
||||
|
||||
With this, you can write code like you would in dynamically typed languages:
|
||||
Mers is built around a type-system where a value could be one of multiple types.
|
||||
This is basically what dynamic typing allows you to do:
|
||||
|
||||
```
|
||||
user_input := if true { "some text input" } else { 19 }
|
||||
```
|
||||
x := if condition { 12 } else { "something went wrong" }
|
||||
|
||||
But you get the compile-time checks you love from statically typed ones:
|
||||
This would be valid code.
|
||||
However, in mers, the compiler still tracks all the types in your program,
|
||||
and it will catch every possible crash before the program even runs:
|
||||
If we tried to use `x` as an Int, the compiler would complain since it might be a string.
|
||||
|
||||
```
|
||||
if user_input >= min_age {
|
||||
println("welcome!")
|
||||
}
|
||||
```
|
||||
|
||||
Because `user_input` could be a string, the code above won't compile.
|
||||
|
||||
### it's simple
|
||||
|
||||
Almost everything in mers is built around this type system.
|
||||
This eliminates many concepts you may be familiar with:
|
||||
|
||||
#### instead of null or exceptions, just add `[]` or the error(s) to the return type
|
||||
|
||||
```
|
||||
// returns `[]` instead of dividing by zero
|
||||
fn div_checked(a int, b int) -> int/[] {
|
||||
if b != 0 {
|
||||
a / b
|
||||
} else {
|
||||
[]
|
||||
}
|
||||
}
|
||||
|
||||
// returns the file content as a string.
|
||||
// if the file can't be read, returns IoError and an error message.
|
||||
// if the file's contents weren't Utf8, return NotUtf8 and the file's bytes.
|
||||
fn load_file_as_string(path string) -> string/IoError(string)/NotUtf8([int ...]) {}
|
||||
```
|
||||
|
||||
#### structs are nothing more than tuples
|
||||
|
||||
```
|
||||
// define our type named Person
|
||||
type Person [int, string]
|
||||
|
||||
// function to get (but not set) the person's birth year
|
||||
fn birth_year(self Person) self.0
|
||||
|
||||
// function to get or set the person's name
|
||||
fn name(self Person/&Person) self.1
|
||||
|
||||
person := [1999, "Ryan"]
|
||||
|
||||
// Get the values
|
||||
person.name() // Ryan
|
||||
person.birth_year() // 1999
|
||||
|
||||
// Change the name
|
||||
&person.name() = "James"
|
||||
// or: name(&person) = "James"
|
||||
person.name() // James
|
||||
|
||||
// This doesn't compile:
|
||||
// &person.birth_year() = 1998
|
||||
```
|
||||
|
||||
#### enums are already part of the type system
|
||||
|
||||
```
|
||||
type DirectoryEntry File(string)/Dir(string)/Link(DirectoryEntry)
|
||||
```
|
||||
|
||||
#### return and break
|
||||
|
||||
Most people agree that `goto` statements are usually a bad idea.
|
||||
But `return` and `break` have similar issues, although less severe:
|
||||
|
||||
**they create weird control flow:**
|
||||
|
||||
Maybe you want to see how long a function takes to execute, so
|
||||
you add a `println` to the end - just to see absolutely no output.
|
||||
Now you might think the function is stuck in an infinite loop somewhere,
|
||||
although it just returned before it ever got to your println.
|
||||
This can be annoying. If we remove `return` statements,
|
||||
it becomes way easier to tell when code will or won't be executed.
|
||||
|
||||
**return is exclusive to functions, break to loops**
|
||||
|
||||
With mers, whenever possible, concepts in the language should work on *statements*.
|
||||
|
||||
An example of this are type annotations:
|
||||
|
||||
```
|
||||
fn half(num int) -> int { num / 2 }
|
||||
```
|
||||
|
||||
The `-> int` indicates that this function returns an int.
|
||||
Actually, it indicates that the statement following it returns an int.
|
||||
This obviously means the function will also return an int,
|
||||
but since this syntax isn't specifically made for functions,
|
||||
we can use it anywhere we want:
|
||||
|
||||
```
|
||||
num := -> int { 4 + 5 }
|
||||
half(-> int { double * 2})
|
||||
```
|
||||
|
||||
**So how do we return values then?**
|
||||
Simple!
|
||||
|
||||
```
|
||||
// a function is just another statement
|
||||
fn return_int() 4 + 5
|
||||
|
||||
// a block returns whatever its last statement returns,
|
||||
fn return_string() {
|
||||
a := "start"
|
||||
b := "end"
|
||||
a + " " + b
|
||||
}
|
||||
|
||||
// this returns string/[], because there is no else
|
||||
if condition() {
|
||||
"some value"
|
||||
}
|
||||
|
||||
// this returns string/int
|
||||
if condition() {
|
||||
"some value"
|
||||
} else {
|
||||
12
|
||||
}
|
||||
```
|
||||
|
||||
Most returns should be relatively intuitive,
|
||||
although some special ones (like loops)
|
||||
use matching (see docs/intro)
|
||||
|
||||
### it has references
|
||||
|
||||
To explain why this is necessary,
|
||||
let's look at examples from other languages.
|
||||
|
||||
Here is some JavaScript:
|
||||
|
||||
```js
|
||||
function doSmth(list) {
|
||||
list[0] = 0
|
||||
}
|
||||
function modify(num) {
|
||||
num = 0
|
||||
}
|
||||
list = [1]
|
||||
num = 1
|
||||
doSmth(list)
|
||||
modify(num)
|
||||
console.log(list) // [ 0 ]
|
||||
console.log(num) // 1
|
||||
```
|
||||
|
||||
The same thing in Go:
|
||||
|
||||
```go
|
||||
package main
|
||||
import "fmt"
|
||||
|
||||
func doSmth(list []int) {
|
||||
list[0] = 0
|
||||
}
|
||||
func modify(num int) {
|
||||
num = 0
|
||||
}
|
||||
func main() {
|
||||
list := []int{1}
|
||||
num := 1
|
||||
doSmth(list)
|
||||
modify(num)
|
||||
fmt.Println(list) // [0]
|
||||
fmt.Println(num) // 1
|
||||
}
|
||||
```
|
||||
|
||||
In both cases, the function was able to modify the list's contents,
|
||||
but unable to change the number to a different value.
|
||||
This is not just inconsistent, it's also not always what you wanted to do:
|
||||
|
||||
- i passed the list by value, how could the function change the inner value?
|
||||
- the function is called `modify`, why can't it modify the value?
|
||||
+ In Go, we could use references to make this work - but that just makes the list example even more annoying, since we don't need a reference to change the inner value.
|
||||
|
||||
So, let's look at mers:
|
||||
|
||||
```
|
||||
fn doSmth(list [int ...]) {
|
||||
&list.get(0).assume1() = 0
|
||||
}
|
||||
fn modify(num &int) {
|
||||
num = 0
|
||||
}
|
||||
list := [1 ...]
|
||||
num := 1
|
||||
doSmth(list)
|
||||
modify(&num)
|
||||
debug(list) // [1 ...]
|
||||
debug(num) // 0
|
||||
```
|
||||
|
||||
The list is unchanged, but the number was changed by `modify`,
|
||||
because it was explicitly passed as a reference `&int`.
|
||||
**Unless you pass a reference, the value you passed will not be changed.**
|
||||
|
||||
### compile-time execution via macros
|
||||
|
||||
```
|
||||
// at compile-time, runs primes.mers to find the first few prime numbers
|
||||
primes := !(mers "primes.mers")
|
||||
|
||||
// at compile-time, runs the code to create a string
|
||||
// containing 256 '-'s
|
||||
long_string := !(mers {
|
||||
str := "-"
|
||||
loop {
|
||||
str = str + str
|
||||
if str.len() >= 256 {
|
||||
str
|
||||
}
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
## How mers?
|
||||
|
||||
There are prebuilt binaries in `build_scripts/`.
|
||||
|
||||
Mers is written in Rust. If you have `cargo`, you can also use the build script in `build_scripts/` to build it yourself.
|
||||
|
||||
Now, create a new text file (or choose one from the examples) and run it: `mers <file>`.
|
||||
|
||||
## Known Issues (only major ones)
|
||||
|
||||
### Multithreading
|
||||
|
||||
If a function is called from two threads, all local variables of that function are shared.
|
||||
This doesn't affect builtin functions, and since functions usually don't take long to execute,
|
||||
the chance of anyone encountering this is low, but it's something to be aware of.
|
||||
It's a simple fix in theory, but a lot of work to implement, which is why the bug isn't fixed yet.
|
||||
|
||||
## Docs
|
||||
|
||||
[intro](docs/intro.md)
|
||||
|
||||
[syntax cheat sheet](docs/syntax_cheat_sheet.md)
|
||||
|
||||
[builtins](docs/builtins.md)
|
||||
|
||||
[statements](docs/statements.md)
|
||||
(note: type-checks aren't implemented since the rewrite is just barely functional, but they will be there and fully working soon)
|
||||
|
302
README_old.md
Executable file
302
README_old.md
Executable file
@ -0,0 +1,302 @@
|
||||
# mers 
|
||||
|
||||
Mers is a simple and reliable programming language.
|
||||
|
||||
While mers can already be used right now,
|
||||
many things about it will still change -
|
||||
at least until 1.0.0 is released.
|
||||
For serious projects, this probably
|
||||
isn't the language you should be using.
|
||||
|
||||
## Why mers?
|
||||
|
||||
### it doesn't crash
|
||||
|
||||
If your program starts, **it won't crash**.
|
||||
The only exceptions to this are
|
||||
the builtin `exit()` function, which can exit with a nonzero exit code
|
||||
and the builtin `assume` functions, similar to Rust's `unwrap()`.
|
||||
|
||||
This is because **errors in mers are values**,
|
||||
and the compiler forces you to handle them.
|
||||
|
||||
### type system
|
||||
|
||||
In mers, a type can consist of multiple types:
|
||||
|
||||
```
|
||||
// bool
|
||||
half := true
|
||||
// float/int
|
||||
number := if half { 0.5 } else { 1 }
|
||||
|
||||
// some math functions that work with int and float
|
||||
fn absolute_value(num int/float) {
|
||||
if num >= 0 { num } else { 0 - num }
|
||||
}
|
||||
fn difference(a int/float, b int/float) {
|
||||
absolute_value(a - b)
|
||||
}
|
||||
|
||||
debug(difference(0.2, 2)) // float: 1.8
|
||||
// although difference can work with floats, since we only give it ints, it also returns an int.
|
||||
// this is known at compile time.
|
||||
debug(difference(20, 5)) // int: 15
|
||||
```
|
||||
|
||||
With this, you can write code like you would in dynamically typed languages:
|
||||
|
||||
```
|
||||
user_input := if true { "some text input" } else { 19 }
|
||||
```
|
||||
|
||||
But you get the compile-time checks you love from statically typed ones:
|
||||
|
||||
```
|
||||
if user_input >= min_age {
|
||||
println("welcome!")
|
||||
}
|
||||
```
|
||||
|
||||
Because `user_input` could be a string, the code above won't compile.
|
||||
|
||||
### it's simple
|
||||
|
||||
Almost everything in mers is built around this type system.
|
||||
This eliminates many concepts you may be familiar with:
|
||||
|
||||
#### instead of null or exceptions, just add `[]` or the error(s) to the return type
|
||||
|
||||
```
|
||||
// returns `[]` instead of dividing by zero
|
||||
fn div_checked(a int, b int) -> int/[] {
|
||||
if b != 0 {
|
||||
a / b
|
||||
} else {
|
||||
[]
|
||||
}
|
||||
}
|
||||
|
||||
// returns the file content as a string.
|
||||
// if the file can't be read, returns IoError and an error message.
|
||||
// if the file's contents weren't Utf8, return NotUtf8 and the file's bytes.
|
||||
fn load_file_as_string(path string) -> string/IoError(string)/NotUtf8([int ...]) {}
|
||||
```
|
||||
|
||||
#### structs are nothing more than tuples
|
||||
|
||||
```
|
||||
// define our type named Person
|
||||
type Person [int, string]
|
||||
|
||||
// function to get (but not set) the person's birth year
|
||||
fn birth_year(self Person) self.0
|
||||
|
||||
// function to get or set the person's name
|
||||
fn name(self Person/&Person) self.1
|
||||
|
||||
person := [1999, "Ryan"]
|
||||
|
||||
// Get the values
|
||||
person.name() // Ryan
|
||||
person.birth_year() // 1999
|
||||
|
||||
// Change the name
|
||||
&person.name() = "James"
|
||||
// or: name(&person) = "James"
|
||||
person.name() // James
|
||||
|
||||
// This doesn't compile:
|
||||
// &person.birth_year() = 1998
|
||||
```
|
||||
|
||||
#### enums are already part of the type system
|
||||
|
||||
```
|
||||
type DirectoryEntry File(string)/Dir(string)/Link(DirectoryEntry)
|
||||
```
|
||||
|
||||
#### return and break
|
||||
|
||||
Most people agree that `goto` statements are usually a bad idea.
|
||||
But `return` and `break` have similar issues, although less severe:
|
||||
|
||||
**they create weird control flow:**
|
||||
|
||||
Maybe you want to see how long a function takes to execute, so
|
||||
you add a `println` to the end - just to see absolutely no output.
|
||||
Now you might think the function is stuck in an infinite loop somewhere,
|
||||
although it just returned before it ever got to your println.
|
||||
This can be annoying. If we remove `return` statements,
|
||||
it becomes way easier to tell when code will or won't be executed.
|
||||
|
||||
**return is exclusive to functions, break to loops**
|
||||
|
||||
With mers, whenever possible, concepts in the language should work on *statements*.
|
||||
|
||||
An example of this are type annotations:
|
||||
|
||||
```
|
||||
fn half(num int) -> int { num / 2 }
|
||||
```
|
||||
|
||||
The `-> int` indicates that this function returns an int.
|
||||
Actually, it indicates that the statement following it returns an int.
|
||||
This obviously means the function will also return an int,
|
||||
but since this syntax isn't specifically made for functions,
|
||||
we can use it anywhere we want:
|
||||
|
||||
```
|
||||
num := -> int { 4 + 5 }
|
||||
half(-> int { double * 2})
|
||||
```
|
||||
|
||||
**So how do we return values then?**
|
||||
Simple!
|
||||
|
||||
```
|
||||
// a function is just another statement
|
||||
fn return_int() 4 + 5
|
||||
|
||||
// a block returns whatever its last statement returns,
|
||||
fn return_string() {
|
||||
a := "start"
|
||||
b := "end"
|
||||
a + " " + b
|
||||
}
|
||||
|
||||
// this returns string/[], because there is no else
|
||||
if condition() {
|
||||
"some value"
|
||||
}
|
||||
|
||||
// this returns string/int
|
||||
if condition() {
|
||||
"some value"
|
||||
} else {
|
||||
12
|
||||
}
|
||||
```
|
||||
|
||||
Most returns should be relatively intuitive,
|
||||
although some special ones (like loops)
|
||||
use matching (see docs/intro)
|
||||
|
||||
### it has references
|
||||
|
||||
To explain why this is necessary,
|
||||
let's look at examples from other languages.
|
||||
|
||||
Here is some JavaScript:
|
||||
|
||||
```js
|
||||
function doSmth(list) {
|
||||
list[0] = 0
|
||||
}
|
||||
function modify(num) {
|
||||
num = 0
|
||||
}
|
||||
list = [1]
|
||||
num = 1
|
||||
doSmth(list)
|
||||
modify(num)
|
||||
console.log(list) // [ 0 ]
|
||||
console.log(num) // 1
|
||||
```
|
||||
|
||||
The same thing in Go:
|
||||
|
||||
```go
|
||||
package main
|
||||
import "fmt"
|
||||
|
||||
func doSmth(list []int) {
|
||||
list[0] = 0
|
||||
}
|
||||
func modify(num int) {
|
||||
num = 0
|
||||
}
|
||||
func main() {
|
||||
list := []int{1}
|
||||
num := 1
|
||||
doSmth(list)
|
||||
modify(num)
|
||||
fmt.Println(list) // [0]
|
||||
fmt.Println(num) // 1
|
||||
}
|
||||
```
|
||||
|
||||
In both cases, the function was able to modify the list's contents,
|
||||
but unable to change the number to a different value.
|
||||
This is not just inconsistent, it's also not always what you wanted to do:
|
||||
|
||||
- i passed the list by value, how could the function change the inner value?
|
||||
- the function is called `modify`, why can't it modify the value?
|
||||
+ In Go, we could use references to make this work - but that just makes the list example even more annoying, since we don't need a reference to change the inner value.
|
||||
|
||||
So, let's look at mers:
|
||||
|
||||
```
|
||||
fn doSmth(list [int ...]) {
|
||||
&list.get(0).assume1() = 0
|
||||
}
|
||||
fn modify(num &int) {
|
||||
num = 0
|
||||
}
|
||||
list := [1 ...]
|
||||
num := 1
|
||||
doSmth(list)
|
||||
modify(&num)
|
||||
debug(list) // [1 ...]
|
||||
debug(num) // 0
|
||||
```
|
||||
|
||||
The list is unchanged, but the number was changed by `modify`,
|
||||
because it was explicitly passed as a reference `&int`.
|
||||
**Unless you pass a reference, the value you passed will not be changed.**
|
||||
|
||||
### compile-time execution via macros
|
||||
|
||||
```
|
||||
// at compile-time, runs primes.mers to find the first few prime numbers
|
||||
primes := !(mers "primes.mers")
|
||||
|
||||
// at compile-time, runs the code to create a string
|
||||
// containing 256 '-'s
|
||||
long_string := !(mers {
|
||||
str := "-"
|
||||
loop {
|
||||
str = str + str
|
||||
if str.len() >= 256 {
|
||||
str
|
||||
}
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
## How mers?
|
||||
|
||||
There are prebuilt binaries in `build_scripts/`.
|
||||
|
||||
Mers is written in Rust. If you have `cargo`, you can also use the build script in `build_scripts/` to build it yourself.
|
||||
|
||||
Now, create a new text file (or choose one from the examples) and run it: `mers <file>`.
|
||||
|
||||
## Known Issues (only major ones)
|
||||
|
||||
### Multithreading
|
||||
|
||||
If a function is called from two threads, all local variables of that function are shared.
|
||||
This doesn't affect builtin functions, and since functions usually don't take long to execute,
|
||||
the chance of anyone encountering this is low, but it's something to be aware of.
|
||||
It's a simple fix in theory, but a lot of work to implement, which is why the bug isn't fixed yet.
|
||||
|
||||
## Docs
|
||||
|
||||
[intro](docs/intro.md)
|
||||
|
||||
[syntax cheat sheet](docs/syntax_cheat_sheet.md)
|
||||
|
||||
[builtins](docs/builtins.md)
|
||||
|
||||
[statements](docs/statements.md)
|
Binary file not shown.
1550
mers/Cargo.lock
generated
1550
mers/Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@ -1,26 +1,10 @@
|
||||
[package]
|
||||
name = "mers"
|
||||
version = "0.2.3"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[lib]
|
||||
name = "mers_libs"
|
||||
path = "src/lib.rs"
|
||||
|
||||
[dependencies]
|
||||
edit = "0.1.4"
|
||||
notify = "5.1.0"
|
||||
regex = "1.7.2"
|
||||
static_assertions = "1.1.0"
|
||||
nu-plugin = { version = "0.79.0", optional = true }
|
||||
nu-protocol = { version = "0.79.0", features = ["plugin"], optional = true }
|
||||
colorize = "0.1.0"
|
||||
|
||||
[features]
|
||||
# default = ["nushell_plugin"]
|
||||
nushell_plugin = ["dep:nu-plugin", "dep:nu-protocol"]
|
||||
|
||||
[profile.nushellplugin]
|
||||
inherits = "release"
|
||||
mers_lib = { path = "../mers_lib" }
|
||||
clap = { version = "4.3.19", features = ["derive"] }
|
||||
|
288
mers/src/main.rs
288
mers/src/main.rs
@ -1,245 +1,53 @@
|
||||
use lang::global_info::ColorFormatMode;
|
||||
use lang::global_info::GlobalScriptInfo;
|
||||
use lang::global_info::LogKind;
|
||||
use lang::val_data::VDataEnum;
|
||||
use lang::val_type::VSingleType;
|
||||
use clap::{Parser, Subcommand, ValueEnum};
|
||||
use mers_lib::prelude_compile::*;
|
||||
use std::{fmt::Display, fs, path::PathBuf};
|
||||
|
||||
use crate::lang::fmtgs::FormatGs;
|
||||
|
||||
mod interactive_mode;
|
||||
mod lang;
|
||||
mod libs;
|
||||
#[cfg(feature = "nushell_plugin")]
|
||||
mod nushell_plugin;
|
||||
mod parsing;
|
||||
mod pathutil;
|
||||
mod tutor;
|
||||
|
||||
fn main() {
|
||||
#[cfg(not(feature = "nushell_plugin"))]
|
||||
normal_main();
|
||||
#[cfg(feature = "nushell_plugin")]
|
||||
nushell_plugin::main();
|
||||
#[derive(Parser)]
|
||||
struct Args {
|
||||
#[arg(long, value_enum, default_value_t = Configs::Std)]
|
||||
config: Configs,
|
||||
#[command(subcommand)]
|
||||
command: Command,
|
||||
}
|
||||
|
||||
fn normal_main() {
|
||||
let args: Vec<_> = std::env::args().skip(1).collect();
|
||||
let mut info = GlobalScriptInfo::default();
|
||||
let mut run = true;
|
||||
let mut args_to_skip = 2;
|
||||
let mut file = match args.len() {
|
||||
0 => {
|
||||
println!("no arguments, use -h for help");
|
||||
std::process::exit(100);
|
||||
}
|
||||
_ => {
|
||||
if args[0].trim_start().starts_with("-") {
|
||||
let mut execute = false;
|
||||
let mut print_version = false;
|
||||
let mut verbose = false;
|
||||
let mut verbose_args = String::new();
|
||||
let mut interactive = 0;
|
||||
let mut interactive_use_new_terminal = false;
|
||||
let mut teachme = false;
|
||||
let mut prev_char = None;
|
||||
let mut advanced = false;
|
||||
for ch in args[0][1..].chars() {
|
||||
if !advanced {
|
||||
if ch == '+' {
|
||||
advanced = true;
|
||||
continue;
|
||||
}
|
||||
match ch {
|
||||
'h' => {
|
||||
eprintln!("~~~~ mers help ~~~~");
|
||||
eprintln!();
|
||||
eprintln!(" ~~ cli ~~");
|
||||
eprintln!("Mers has the following cli options:");
|
||||
eprintln!("-h shows this Help message");
|
||||
eprintln!("-e - mers will treat the run argument as code to be Executed rather than a file path");
|
||||
eprintln!(" mers -e 'println(\"Hello, World!\")'");
|
||||
eprintln!(
|
||||
"-c - mers will Check the code for errors, but won't run it"
|
||||
);
|
||||
eprintln!("-f - mers will Format the code and print it. useful if you suspect the parser might be misinterpreting your code");
|
||||
eprintln!(
|
||||
"+c - use Colors in the output to better visualize things"
|
||||
);
|
||||
eprintln!("+C - don't use colors (opposite of +c, redundant since this is the default)");
|
||||
eprintln!("-v - mers will be more Verbose");
|
||||
eprintln!("+???+ - customize what mers is verbose about and how - bad syntax, barely useful, don't use it until it gets improved (TODO!)");
|
||||
eprintln!("-i - launches an Interactive session to play around with (opens your editor and runs code on each file save)");
|
||||
eprintln!("+t - spawns a new terminal for the editor (if you use a terminal editors, add +t)");
|
||||
eprintln!(" mers -i+t");
|
||||
eprintln!("-t - launches the Tutor, which will attempt to Teach you the basics of the language");
|
||||
eprintln!();
|
||||
eprintln!(" ~~ getting started ~~");
|
||||
eprintln!("mers doesn't need a specific structure for directories, just create a UTF-8 text file, write code, and run it:");
|
||||
eprintln!(" echo 'println(\"Hello, World!\")' > hello.mers");
|
||||
eprintln!(" mers hello.mers");
|
||||
return;
|
||||
}
|
||||
'e' => execute = true,
|
||||
'v' => verbose = true,
|
||||
'c' => run = false,
|
||||
'f' => {
|
||||
run = false;
|
||||
info.log.after_parse.stderr = true;
|
||||
}
|
||||
'V' => print_version = true,
|
||||
'i' => interactive += 1,
|
||||
't' => teachme = true,
|
||||
ch => {
|
||||
eprintln!("Ignoring -{ch}. (unknown char)");
|
||||
continue;
|
||||
}
|
||||
}
|
||||
prev_char = Some(ch);
|
||||
} else {
|
||||
advanced = false;
|
||||
if let Some(prev_char) = prev_char {
|
||||
match prev_char {
|
||||
'i' => match ch {
|
||||
't' => interactive_use_new_terminal = true,
|
||||
_ => eprintln!("Ignoring i+{ch}. (unknown adv char)"),
|
||||
},
|
||||
'v' => {
|
||||
if ch != '+' {
|
||||
advanced = true;
|
||||
verbose_args.push(ch);
|
||||
}
|
||||
}
|
||||
'f' => match ch {
|
||||
'c' => info.formatter.mode = ColorFormatMode::Colorize,
|
||||
'C' => info.formatter.mode = ColorFormatMode::Plain,
|
||||
_ => eprintln!("Ignoring f+{ch}. (unknown adv char)"),
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
} else {
|
||||
eprintln!(
|
||||
"Ignoring advanced args because there was no previous argument."
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
if print_version {
|
||||
println!(
|
||||
"mers {}",
|
||||
option_env!("CARGO_PKG_VERSION")
|
||||
.unwrap_or("[[ version unknown: no CARGO_PKG_VERSION ]]")
|
||||
);
|
||||
return;
|
||||
}
|
||||
if teachme {
|
||||
tutor::start(false);
|
||||
return;
|
||||
}
|
||||
if verbose {
|
||||
if verbose_args.is_empty() {
|
||||
fn f() -> LogKind {
|
||||
LogKind {
|
||||
stderr: true,
|
||||
log: true,
|
||||
}
|
||||
}
|
||||
info.log.vtype_fits_in = f();
|
||||
info.log.vsingletype_fits_in = f();
|
||||
} else {
|
||||
fn kind(val: Option<&str>) -> LogKind {
|
||||
match val {
|
||||
Some("stderr") => LogKind {
|
||||
stderr: true,
|
||||
..Default::default()
|
||||
},
|
||||
Some("log") => LogKind {
|
||||
log: true,
|
||||
..Default::default()
|
||||
},
|
||||
Some("log+stderr" | "stderr+log") => LogKind {
|
||||
stderr: true,
|
||||
log: true,
|
||||
..Default::default()
|
||||
},
|
||||
_ => LogKind {
|
||||
stderr: true,
|
||||
..Default::default()
|
||||
},
|
||||
}
|
||||
}
|
||||
for verbose_arg in verbose_args.split(',') {
|
||||
let (arg, val) = match verbose_arg.split_once('=') {
|
||||
Some((left, right)) => (left, Some(right)),
|
||||
None => (verbose_arg, None),
|
||||
};
|
||||
match arg {
|
||||
"vtype_fits_in" => info.log.vtype_fits_in = kind(val),
|
||||
"vsingletype_fits_in" => info.log.vsingletype_fits_in = kind(val),
|
||||
_ => eprintln!("Warn: -v+ unknown arg '{arg}'."),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if interactive > 0 {
|
||||
match interactive {
|
||||
_ => {
|
||||
// basic: open file and watch for fs changes
|
||||
interactive_mode::fs_watcher::playground(interactive_use_new_terminal)
|
||||
.unwrap()
|
||||
}
|
||||
};
|
||||
return;
|
||||
} else if execute {
|
||||
parsing::file::File::new(
|
||||
args.iter().skip(1).fold(String::new(), |mut s, v| {
|
||||
if !s.is_empty() {
|
||||
s.push(' ');
|
||||
}
|
||||
s.push_str(v);
|
||||
s
|
||||
}),
|
||||
std::path::PathBuf::new(),
|
||||
)
|
||||
} else {
|
||||
args_to_skip += 1;
|
||||
if let Some(file) = args.get(1) {
|
||||
parsing::file::File::new(
|
||||
std::fs::read_to_string(file).unwrap(),
|
||||
file.into(),
|
||||
)
|
||||
} else {
|
||||
println!("nothing to do - missing arguments?");
|
||||
std::process::exit(101);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
parsing::file::File::new(
|
||||
std::fs::read_to_string(&args[0]).unwrap(),
|
||||
args[0].as_str().into(),
|
||||
)
|
||||
}
|
||||
}
|
||||
};
|
||||
info.main_fn_args = vec![(
|
||||
"args".to_string(),
|
||||
VSingleType::List(VSingleType::String.into()).to(),
|
||||
)];
|
||||
match parsing::parse::parse_custom_info(&mut file, info) {
|
||||
Ok(script) => {
|
||||
if run {
|
||||
script.run(vec![VDataEnum::List(
|
||||
VSingleType::String.to(),
|
||||
std::env::args()
|
||||
.skip(args_to_skip)
|
||||
.map(|v| VDataEnum::String(v).to())
|
||||
.collect(),
|
||||
)
|
||||
.to()]);
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
println!("Couldn't compile:\n{}", e.with_file(&file));
|
||||
std::process::exit(99);
|
||||
#[derive(Subcommand)]
|
||||
enum Command {
|
||||
Run { file: PathBuf },
|
||||
Exec { source: String },
|
||||
}
|
||||
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ValueEnum)]
|
||||
enum Configs {
|
||||
None,
|
||||
Std,
|
||||
}
|
||||
impl Display for Configs {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Self::None => write!(f, "none"),
|
||||
Self::Std => write!(f, "std"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let args = Args::parse();
|
||||
let config = match args.config {
|
||||
Configs::None => Config::new(),
|
||||
Configs::Std => Config::new().bundle_std(),
|
||||
};
|
||||
let (mut info1, mut info2) = config.infos();
|
||||
match args.command {
|
||||
Command::Run { file } => {
|
||||
let str = fs::read_to_string(file).unwrap();
|
||||
let mut src = Source::new(str);
|
||||
let parsed = parse(&mut src).unwrap();
|
||||
let run = parsed.compile(&mut info1, Default::default()).unwrap();
|
||||
run.run(&mut info2);
|
||||
}
|
||||
Command::Exec { source } => {
|
||||
let mut src = Source::new(source);
|
||||
let parsed = parse(&mut src).unwrap();
|
||||
let run = parsed.compile(&mut info1, Default::default()).unwrap();
|
||||
run.run(&mut info2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
15
mers/test.mers
Executable file
15
mers/test.mers
Executable file
@ -0,0 +1,15 @@
|
||||
list := (
|
||||
1,
|
||||
2,
|
||||
3,
|
||||
4,
|
||||
5,
|
||||
6,
|
||||
7,
|
||||
8,
|
||||
9,
|
||||
);
|
||||
iter := (list item -> { item.println 12 }).map
|
||||
"---".println
|
||||
list := iter.as_list
|
||||
list.println
|
4
mers_lib/Cargo.toml
Executable file
4
mers_lib/Cargo.toml
Executable file
@ -0,0 +1,4 @@
|
||||
[package]
|
||||
name = "mers_lib"
|
||||
version = "0.3.0"
|
||||
edition = "2021"
|
53
mers_lib/doc.md
Executable file
53
mers_lib/doc.md
Executable file
@ -0,0 +1,53 @@
|
||||
# mers documentation
|
||||
|
||||
## parsing
|
||||
|
||||
syntax:
|
||||
|
||||
- `// <comment>`
|
||||
- `/* <comment> */`
|
||||
|
||||
operators:
|
||||
- `<target> := <source>` init
|
||||
- `<target> = <source>` assign (`<target>` must be a reference)
|
||||
- `+`
|
||||
- `-`
|
||||
- `*`
|
||||
- `/`
|
||||
- `%`
|
||||
- `&`
|
||||
- `|`
|
||||
- `&&`
|
||||
- `||`
|
||||
|
||||
keywords (must be between whitespace):
|
||||
|
||||
- `if <condition> <statement>`
|
||||
- `else <statement>` (after `if`)
|
||||
- `loop <statement>`
|
||||
- `switch { <arms> }`
|
||||
- `<arg> -> <statement>`
|
||||
- `def <name> <: for types, = for comptime> <_>` for compile-time stuff (types, macros, ...)
|
||||
|
||||
## details
|
||||
|
||||
### functions
|
||||
|
||||
A function takes an argument and returns some data:
|
||||
|
||||
func := input -> input + 2
|
||||
3.func.println // 5
|
||||
(list, 0).get // first element
|
||||
(val, match -> match.println, [] -> "doesn't match".println).match
|
||||
|
||||
### switch
|
||||
|
||||
switch <val> {
|
||||
<type> <func>
|
||||
}
|
||||
|
||||
switch something {
|
||||
int num -> {"int: " + num}.println
|
||||
float num -> {"float: " + num}.println
|
||||
}
|
||||
|
15
mers_lib/init_to.rs
Executable file
15
mers_lib/init_to.rs
Executable file
@ -0,0 +1,15 @@
|
||||
use crate::data::Data;
|
||||
|
||||
use super::MersStatement;
|
||||
|
||||
pub struct InitTo {
|
||||
pub target: Box<dyn MersStatement>,
|
||||
pub source: Box<dyn MersStatement>,
|
||||
}
|
||||
|
||||
impl MersStatement for InitTo {
|
||||
fn has_scope(&self) -> bool {
|
||||
false
|
||||
}
|
||||
fn run(&self, info: &mut super::Info) -> Data {}
|
||||
}
|
47
mers_lib/src/data/bool.rs
Executable file
47
mers_lib/src/data/bool.rs
Executable file
@ -0,0 +1,47 @@
|
||||
use std::{any::Any, fmt::Display};
|
||||
|
||||
use super::{MersData, MersType};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Bool(pub bool);
|
||||
|
||||
impl MersData for Bool {
|
||||
fn clone(&self) -> Box<dyn MersData> {
|
||||
Box::new(Clone::clone(self))
|
||||
}
|
||||
fn as_any(&self) -> &dyn Any {
|
||||
self
|
||||
}
|
||||
fn mut_any(&mut self) -> &mut dyn Any {
|
||||
self
|
||||
}
|
||||
fn to_any(self) -> Box<dyn Any> {
|
||||
Box::new(self)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct BoolT;
|
||||
impl MersType for BoolT {
|
||||
fn is_same_type_as(&self, other: &dyn MersType) -> bool {
|
||||
other.as_any().downcast_ref::<Self>().is_some()
|
||||
}
|
||||
fn is_included_in_single(&self, target: &dyn MersType) -> bool {
|
||||
self.is_same_type_as(target)
|
||||
}
|
||||
fn as_any(&self) -> &dyn Any {
|
||||
self
|
||||
}
|
||||
fn mut_any(&mut self) -> &mut dyn Any {
|
||||
self
|
||||
}
|
||||
fn to_any(self) -> Box<dyn Any> {
|
||||
Box::new(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Bool {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{}", self.0)
|
||||
}
|
||||
}
|
13
mers_lib/src/data/defs.rs
Executable file
13
mers_lib/src/data/defs.rs
Executable file
@ -0,0 +1,13 @@
|
||||
use super::Data;
|
||||
|
||||
pub fn assign(from: Data, target: &Data) {
|
||||
let mut target = target.get_mut();
|
||||
if let Some(r) = target
|
||||
.mut_any()
|
||||
.downcast_mut::<crate::data::reference::Reference>()
|
||||
{
|
||||
*r.0.get_mut() = from.get().clone();
|
||||
} else {
|
||||
todo!("assignment to non-reference")
|
||||
}
|
||||
}
|
47
mers_lib/src/data/float.rs
Executable file
47
mers_lib/src/data/float.rs
Executable file
@ -0,0 +1,47 @@
|
||||
use std::{any::Any, fmt::Display};
|
||||
|
||||
use super::{MersData, MersType, Type};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Float(pub f64);
|
||||
|
||||
impl MersData for Float {
|
||||
fn clone(&self) -> Box<dyn MersData> {
|
||||
Box::new(Clone::clone(self))
|
||||
}
|
||||
fn as_any(&self) -> &dyn Any {
|
||||
self
|
||||
}
|
||||
fn mut_any(&mut self) -> &mut dyn Any {
|
||||
self
|
||||
}
|
||||
fn to_any(self) -> Box<dyn Any> {
|
||||
Box::new(self)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct FloatT;
|
||||
impl MersType for FloatT {
|
||||
fn is_same_type_as(&self, other: &dyn MersType) -> bool {
|
||||
other.as_any().downcast_ref::<Self>().is_some()
|
||||
}
|
||||
fn is_included_in_single(&self, target: &dyn MersType) -> bool {
|
||||
self.is_same_type_as(target)
|
||||
}
|
||||
fn as_any(&self) -> &dyn Any {
|
||||
self
|
||||
}
|
||||
fn mut_any(&mut self) -> &mut dyn Any {
|
||||
self
|
||||
}
|
||||
fn to_any(self) -> Box<dyn Any> {
|
||||
Box::new(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Float {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{}", self.0)
|
||||
}
|
||||
}
|
79
mers_lib/src/data/function.rs
Executable file
79
mers_lib/src/data/function.rs
Executable file
@ -0,0 +1,79 @@
|
||||
use std::{
|
||||
any::Any,
|
||||
fmt::{Debug, Display},
|
||||
sync::Arc,
|
||||
};
|
||||
|
||||
use crate::program::{self, run::Info};
|
||||
|
||||
use super::{Data, MersData, MersType, Type};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Function {
|
||||
pub info: Info,
|
||||
pub out: Arc<dyn Fn(&Type) -> Option<Type>>,
|
||||
pub run: Arc<dyn Fn(Data, &mut crate::program::run::Info) -> Data>,
|
||||
}
|
||||
impl Function {
|
||||
pub fn with_info(&self, info: program::run::Info) -> Self {
|
||||
Self {
|
||||
info,
|
||||
out: Arc::clone(&self.out),
|
||||
run: Arc::clone(&self.run),
|
||||
}
|
||||
}
|
||||
pub fn run(&self, arg: Data) -> Data {
|
||||
(self.run)(arg, &mut self.info.clone())
|
||||
}
|
||||
}
|
||||
|
||||
impl MersData for Function {
|
||||
fn clone(&self) -> Box<dyn MersData> {
|
||||
Box::new(Clone::clone(self))
|
||||
}
|
||||
fn as_any(&self) -> &dyn Any {
|
||||
self
|
||||
}
|
||||
fn mut_any(&mut self) -> &mut dyn Any {
|
||||
self
|
||||
}
|
||||
fn to_any(self) -> Box<dyn Any> {
|
||||
Box::new(self)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct FunctionT(Arc<dyn Fn(&Type) -> Option<Type>>);
|
||||
impl MersType for FunctionT {
|
||||
fn is_same_type_as(&self, _other: &dyn MersType) -> bool {
|
||||
false
|
||||
}
|
||||
fn is_included_in_single(&self, _target: &dyn MersType) -> bool {
|
||||
false
|
||||
}
|
||||
fn as_any(&self) -> &dyn Any {
|
||||
self
|
||||
}
|
||||
fn mut_any(&mut self) -> &mut dyn Any {
|
||||
self
|
||||
}
|
||||
fn to_any(self) -> Box<dyn Any> {
|
||||
Box::new(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for Function {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "Function")
|
||||
}
|
||||
}
|
||||
impl Debug for FunctionT {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "FunctionT")
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Function {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "<function>")
|
||||
}
|
||||
}
|
47
mers_lib/src/data/int.rs
Executable file
47
mers_lib/src/data/int.rs
Executable file
@ -0,0 +1,47 @@
|
||||
use std::{any::Any, fmt::Display};
|
||||
|
||||
use super::{MersData, MersType, Type};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Int(pub isize);
|
||||
|
||||
impl MersData for Int {
|
||||
fn clone(&self) -> Box<dyn MersData> {
|
||||
Box::new(Clone::clone(self))
|
||||
}
|
||||
fn as_any(&self) -> &dyn Any {
|
||||
self
|
||||
}
|
||||
fn mut_any(&mut self) -> &mut dyn Any {
|
||||
self
|
||||
}
|
||||
fn to_any(self) -> Box<dyn Any> {
|
||||
Box::new(self)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct IntT;
|
||||
impl MersType for IntT {
|
||||
fn is_same_type_as(&self, other: &dyn MersType) -> bool {
|
||||
other.as_any().downcast_ref::<Self>().is_some()
|
||||
}
|
||||
fn is_included_in_single(&self, target: &dyn MersType) -> bool {
|
||||
self.is_same_type_as(target)
|
||||
}
|
||||
fn as_any(&self) -> &dyn Any {
|
||||
self
|
||||
}
|
||||
fn mut_any(&mut self) -> &mut dyn Any {
|
||||
self
|
||||
}
|
||||
fn to_any(self) -> Box<dyn Any> {
|
||||
Box::new(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Int {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{}", self.0)
|
||||
}
|
||||
}
|
155
mers_lib/src/data/mod.rs
Executable file
155
mers_lib/src/data/mod.rs
Executable file
@ -0,0 +1,155 @@
|
||||
use std::{
|
||||
any::Any,
|
||||
fmt::{Debug, Display},
|
||||
sync::{Arc, RwLock, RwLockReadGuard, RwLockWriteGuard},
|
||||
};
|
||||
|
||||
pub mod bool;
|
||||
pub mod float;
|
||||
pub mod function;
|
||||
pub mod int;
|
||||
pub mod reference;
|
||||
pub mod string;
|
||||
pub mod tuple;
|
||||
|
||||
pub mod defs;
|
||||
|
||||
pub trait MersData: Any + Debug + Display {
|
||||
fn matches(&self) -> Option<Data> {
|
||||
None
|
||||
}
|
||||
fn iterable(&self) -> Option<Box<dyn Iterator<Item = Data>>> {
|
||||
None
|
||||
}
|
||||
/// By default, uses `iterable` to get an iterator and `nth` to retrieve the nth element.
|
||||
/// Should have a custom implementation for better performance on most types
|
||||
fn get(&self, i: usize) -> Option<Data> {
|
||||
self.iterable()?.nth(i)
|
||||
}
|
||||
fn clone(&self) -> Box<dyn MersData>;
|
||||
fn as_any(&self) -> &dyn Any;
|
||||
fn mut_any(&mut self) -> &mut dyn Any;
|
||||
fn to_any(self) -> Box<dyn Any>;
|
||||
}
|
||||
|
||||
pub trait MersType: Any + Debug {
|
||||
/// If Some((_, false)) is returned, data of this type could match. If it matches, it matches with the type.
|
||||
/// If Some((_, true)) is returned, data of this type will always match with the type.
|
||||
fn matches(&self) -> Option<(Type, bool)> {
|
||||
None
|
||||
}
|
||||
/// If Some(T), calling `iterable` on the MersData this MersType belongs to
|
||||
/// Should return Some(I), where I is an Iterator which only returns items of type T.
|
||||
fn iterable(&self) -> Option<Type> {
|
||||
None
|
||||
}
|
||||
/// If Some(T), calling `get` on data of this type may return T, but it might also return None.
|
||||
/// By default, this returns the same thing as `iterable`, since this is also the default implementation for `MersData::get`.
|
||||
fn get(&self) -> Option<Type> {
|
||||
self.iterable()
|
||||
}
|
||||
/// If self and other are different types (`other.as_any().downcast_ref::<Self>().is_none()`),
|
||||
/// this *must* return false.
|
||||
fn is_same_type_as(&self, other: &dyn MersType) -> bool;
|
||||
/// This doesn't handle the case where target is Type (is_included_in handles it)
|
||||
fn is_included_in_single(&self, target: &dyn MersType) -> bool;
|
||||
fn is_included_in(&self, target: &dyn MersType) -> bool {
|
||||
if let Some(target) = target.as_any().downcast_ref::<Type>() {
|
||||
target
|
||||
.types
|
||||
.iter()
|
||||
.any(|t| self.is_included_in_single(t.as_ref()))
|
||||
} else {
|
||||
self.is_included_in_single(target)
|
||||
}
|
||||
}
|
||||
fn as_any(&self) -> &dyn Any;
|
||||
fn mut_any(&mut self) -> &mut dyn Any;
|
||||
fn to_any(self) -> Box<dyn Any>;
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Data {
|
||||
pub data: Arc<RwLock<Box<dyn MersData>>>,
|
||||
}
|
||||
impl Data {
|
||||
pub fn new<T: MersData>(data: T) -> Self {
|
||||
Self::new_boxed(Box::new(data))
|
||||
}
|
||||
pub fn new_boxed(data: Box<dyn MersData>) -> Self {
|
||||
Self {
|
||||
data: Arc::new(RwLock::new(data)),
|
||||
}
|
||||
}
|
||||
pub fn empty_tuple() -> Self {
|
||||
Self::new(tuple::Tuple(vec![]))
|
||||
}
|
||||
pub fn one_tuple(v: Self) -> Self {
|
||||
Self::new(tuple::Tuple(vec![v]))
|
||||
}
|
||||
pub fn get(&self) -> RwLockReadGuard<Box<dyn MersData>> {
|
||||
self.data.read().unwrap()
|
||||
}
|
||||
pub fn get_mut(&self) -> RwLockWriteGuard<Box<dyn MersData>> {
|
||||
self.data.write().unwrap()
|
||||
}
|
||||
}
|
||||
impl Clone for Data {
|
||||
fn clone(&self) -> Self {
|
||||
// todo!("clone for data - requires CoW");
|
||||
Self {
|
||||
data: Arc::clone(&self.data),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Type {
|
||||
// TODO: Maybe make sure this is always sorted by (recursive?!?) TypeId,
|
||||
// that way is_same_type_as can work more efficiently (cuz good code but also branch prediction)
|
||||
types: Vec<Arc<dyn MersType>>,
|
||||
}
|
||||
impl Type {
|
||||
pub fn new<T: MersType>(t: T) -> Self {
|
||||
Self {
|
||||
types: vec![Arc::new(t)],
|
||||
}
|
||||
}
|
||||
pub fn newm(types: Vec<Arc<dyn MersType>>) -> Self {
|
||||
Self { types }
|
||||
}
|
||||
pub fn empty_tuple() -> Self {
|
||||
Self::new(tuple::TupleT(vec![]))
|
||||
}
|
||||
pub fn add<T: MersType>(&mut self, new: Box<T>) {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
// PROBLEM:
|
||||
// [int, int]/[int, string]/[string, int]/[string, string]
|
||||
// is the same type as [int/string, int/string],
|
||||
// but [int, int]/[int, string]/[string int] isn't.
|
||||
// somehow, we need to merge these into the simplest form (same outer type, inner types differ)
|
||||
// before we can implement `Type`
|
||||
// idea: pick all the ones with the same first type: [int, int]/[int, string] and [string, int]/[string, string]
|
||||
// then repeat with the second type if possible (here not, but for longer tuples, probably?)
|
||||
// merge the last existing type in all the collections until we reach the first type again or the last types aren't equal anymore (how to check????)
|
||||
|
||||
impl MersType for Type {
|
||||
fn is_same_type_as(&self, other: &dyn MersType) -> bool {
|
||||
todo!()
|
||||
}
|
||||
fn is_included_in_single(&self, target: &dyn MersType) -> bool {
|
||||
todo!()
|
||||
}
|
||||
fn as_any(&self) -> &dyn Any {
|
||||
self
|
||||
}
|
||||
fn mut_any(&mut self) -> &mut dyn Any {
|
||||
self
|
||||
}
|
||||
fn to_any(self) -> Box<dyn Any> {
|
||||
Box::new(self)
|
||||
}
|
||||
}
|
51
mers_lib/src/data/reference.rs
Executable file
51
mers_lib/src/data/reference.rs
Executable file
@ -0,0 +1,51 @@
|
||||
use std::{any::Any, fmt::Display, sync::Mutex};
|
||||
|
||||
use super::{Data, MersData, MersType, Type};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Reference(pub Data);
|
||||
|
||||
impl MersData for Reference {
|
||||
fn clone(&self) -> Box<dyn MersData> {
|
||||
Box::new(Clone::clone(self))
|
||||
}
|
||||
fn as_any(&self) -> &dyn Any {
|
||||
self
|
||||
}
|
||||
fn mut_any(&mut self) -> &mut dyn Any {
|
||||
self
|
||||
}
|
||||
fn to_any(self) -> Box<dyn Any> {
|
||||
Box::new(self)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ReferenceT(pub Type);
|
||||
impl MersType for ReferenceT {
|
||||
fn is_same_type_as(&self, other: &dyn MersType) -> bool {
|
||||
if let Some(o) = other.as_any().downcast_ref::<Self>() {
|
||||
self.0.is_same_type_as(&o.0)
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
fn is_included_in_single(&self, target: &dyn MersType) -> bool {
|
||||
self.is_same_type_as(target)
|
||||
}
|
||||
fn as_any(&self) -> &dyn Any {
|
||||
self
|
||||
}
|
||||
fn mut_any(&mut self) -> &mut dyn Any {
|
||||
self
|
||||
}
|
||||
fn to_any(self) -> Box<dyn Any> {
|
||||
Box::new(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Reference {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "&{}", self.0.get())
|
||||
}
|
||||
}
|
47
mers_lib/src/data/string.rs
Executable file
47
mers_lib/src/data/string.rs
Executable file
@ -0,0 +1,47 @@
|
||||
use std::{any::Any, fmt::Display};
|
||||
|
||||
use super::{MersData, MersType, Type};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct String(pub std::string::String);
|
||||
|
||||
impl MersData for String {
|
||||
fn clone(&self) -> Box<dyn MersData> {
|
||||
Box::new(Clone::clone(self))
|
||||
}
|
||||
fn as_any(&self) -> &dyn Any {
|
||||
self
|
||||
}
|
||||
fn mut_any(&mut self) -> &mut dyn Any {
|
||||
self
|
||||
}
|
||||
fn to_any(self) -> Box<dyn Any> {
|
||||
Box::new(self)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct StringT;
|
||||
impl MersType for StringT {
|
||||
fn is_same_type_as(&self, other: &dyn MersType) -> bool {
|
||||
other.as_any().downcast_ref::<Self>().is_some()
|
||||
}
|
||||
fn is_included_in_single(&self, target: &dyn MersType) -> bool {
|
||||
self.is_same_type_as(target)
|
||||
}
|
||||
fn as_any(&self) -> &dyn Any {
|
||||
self
|
||||
}
|
||||
fn mut_any(&mut self) -> &mut dyn Any {
|
||||
self
|
||||
}
|
||||
fn to_any(self) -> Box<dyn Any> {
|
||||
Box::new(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for String {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{}", self.0)
|
||||
}
|
||||
}
|
92
mers_lib/src/data/tuple.rs
Executable file
92
mers_lib/src/data/tuple.rs
Executable file
@ -0,0 +1,92 @@
|
||||
use std::{any::Any, fmt::Display};
|
||||
|
||||
use super::{Data, MersData, MersType, Type};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Tuple(pub Vec<Data>);
|
||||
|
||||
impl Tuple {
|
||||
pub fn len(&self) -> usize {
|
||||
self.0.len()
|
||||
}
|
||||
pub fn get(&self, i: usize) -> Option<&Data> {
|
||||
self.0.get(i)
|
||||
}
|
||||
}
|
||||
|
||||
impl MersData for Tuple {
|
||||
fn matches(&self) -> Option<Data> {
|
||||
if let Some(d) = self.0.first() {
|
||||
if self.0.len() == 1 {
|
||||
Some(d.clone())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
fn iterable(&self) -> Option<Box<dyn Iterator<Item = Data>>> {
|
||||
Some(Box::new(self.0.clone().into_iter()))
|
||||
}
|
||||
fn clone(&self) -> Box<dyn MersData> {
|
||||
Box::new(Clone::clone(self))
|
||||
}
|
||||
fn as_any(&self) -> &dyn Any {
|
||||
self
|
||||
}
|
||||
fn mut_any(&mut self) -> &mut dyn Any {
|
||||
self
|
||||
}
|
||||
fn to_any(self) -> Box<dyn Any> {
|
||||
Box::new(self)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct TupleT(pub Vec<Type>);
|
||||
impl MersType for TupleT {
|
||||
fn matches(&self) -> Option<(Type, bool)> {
|
||||
if let Some(d) = self.0.first() {
|
||||
if self.0.len() == 1 {
|
||||
Some((d.clone(), true))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
fn iterable(&self) -> Option<Type> {
|
||||
Some(todo!("joine types"))
|
||||
}
|
||||
fn is_same_type_as(&self, other: &dyn MersType) -> bool {
|
||||
other.as_any().downcast_ref::<Self>().is_some()
|
||||
}
|
||||
fn is_included_in_single(&self, target: &dyn MersType) -> bool {
|
||||
self.is_same_type_as(target)
|
||||
}
|
||||
fn as_any(&self) -> &dyn Any {
|
||||
self
|
||||
}
|
||||
fn mut_any(&mut self) -> &mut dyn Any {
|
||||
self
|
||||
}
|
||||
fn to_any(self) -> Box<dyn Any> {
|
||||
Box::new(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Tuple {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "(")?;
|
||||
for (i, c) in self.0.iter().enumerate() {
|
||||
if i > 0 {
|
||||
write!(f, ", ")?;
|
||||
}
|
||||
write!(f, "{}", c.get())?;
|
||||
}
|
||||
write!(f, ")")?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
58
mers_lib/src/info/mod.rs
Executable file
58
mers_lib/src/info/mod.rs
Executable file
@ -0,0 +1,58 @@
|
||||
use std::fmt::Debug;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Info<L: Local> {
|
||||
pub scopes: Vec<L>,
|
||||
}
|
||||
|
||||
impl<L: Local> Info<L> {
|
||||
/// Returns self, but completely empty (even without globals).
|
||||
/// Only use this if you assume this Info will never be used.
|
||||
pub fn neverused() -> Self {
|
||||
Self { scopes: vec![] }
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Local: Default + Debug {
|
||||
type VariableIdentifier;
|
||||
type VariableData;
|
||||
// type TypesIdentifier;
|
||||
// type TypesType;
|
||||
fn init_var(&mut self, id: Self::VariableIdentifier, value: Self::VariableData);
|
||||
fn get_var(&self, id: &Self::VariableIdentifier) -> Option<&Self::VariableData>;
|
||||
fn get_var_mut(&mut self, id: &Self::VariableIdentifier) -> Option<&mut Self::VariableData>;
|
||||
// fn add_type(&mut self, id: Self::TypesIdentifier, new_type: Self::TypesType);
|
||||
// fn get_type(&self, id: Self::TypesIdentifier) -> Option<&Self::TypesType>;
|
||||
}
|
||||
|
||||
impl<L: Local> Info<L> {
|
||||
pub fn create_scope(&mut self) {
|
||||
self.scopes.push(L::default())
|
||||
}
|
||||
/// WARNING: can remove the last scope, which can cause some other functions to panic. Use ONLY after a create_scope()
|
||||
pub fn end_scope(&mut self) {
|
||||
self.scopes.pop();
|
||||
}
|
||||
}
|
||||
|
||||
impl<L: Local> Local for Info<L> {
|
||||
type VariableIdentifier = L::VariableIdentifier;
|
||||
type VariableData = L::VariableData;
|
||||
fn init_var(&mut self, id: Self::VariableIdentifier, value: Self::VariableData) {
|
||||
self.scopes.last_mut().unwrap().init_var(id, value)
|
||||
}
|
||||
fn get_var(&self, id: &Self::VariableIdentifier) -> Option<&Self::VariableData> {
|
||||
self.scopes.iter().find_map(|l| l.get_var(id))
|
||||
}
|
||||
fn get_var_mut(&mut self, id: &Self::VariableIdentifier) -> Option<&mut Self::VariableData> {
|
||||
self.scopes.iter_mut().find_map(|l| l.get_var_mut(id))
|
||||
}
|
||||
}
|
||||
|
||||
impl<L: Local> Default for Info<L> {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
scopes: vec![L::default()],
|
||||
}
|
||||
}
|
||||
}
|
12
mers_lib/src/lib.rs
Executable file
12
mers_lib/src/lib.rs
Executable file
@ -0,0 +1,12 @@
|
||||
pub mod data;
|
||||
pub mod info;
|
||||
pub mod parsing;
|
||||
pub mod program;
|
||||
|
||||
pub mod prelude_compile {
|
||||
pub use crate::parsing::parse;
|
||||
pub use crate::parsing::Source;
|
||||
pub use crate::program::configs::Config;
|
||||
pub use crate::program::parsed::MersStatement as ParsedMersStatement;
|
||||
pub use crate::program::run::MersStatement as RunMersStatement;
|
||||
}
|
0
mers_lib/src/parsing/errors.rs
Executable file
0
mers_lib/src/parsing/errors.rs
Executable file
156
mers_lib/src/parsing/mod.rs
Executable file
156
mers_lib/src/parsing/mod.rs
Executable file
@ -0,0 +1,156 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::{
|
||||
info::Info,
|
||||
program::{self, parsed::CompInfo},
|
||||
};
|
||||
|
||||
pub mod errors;
|
||||
pub mod statements;
|
||||
pub mod types;
|
||||
|
||||
pub fn parse(src: &mut Source) -> Result<Box<dyn program::parsed::MersStatement>, ()> {
|
||||
Ok(Box::new(statements::parse_block(src)?))
|
||||
}
|
||||
|
||||
pub struct Source {
|
||||
src: String,
|
||||
i: usize,
|
||||
sections: Vec<SectionMarker>,
|
||||
}
|
||||
impl Source {
|
||||
pub fn new(src: String) -> Self {
|
||||
Self {
|
||||
src,
|
||||
i: 0,
|
||||
sections: vec![],
|
||||
}
|
||||
}
|
||||
|
||||
pub fn skip_whitespace(&mut self) {
|
||||
if let Some(i) = self.src[self.i..].char_indices().find_map(|(i, ch)| {
|
||||
if !ch.is_whitespace() {
|
||||
Some(i)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}) {
|
||||
self.i += i;
|
||||
} else {
|
||||
self.i = self.src.len();
|
||||
}
|
||||
}
|
||||
|
||||
fn end_sections(&mut self) {
|
||||
let pos = self.get_pos();
|
||||
for section in self.sections.iter_mut() {
|
||||
section.checked_end(pos);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn peek_char(&self) -> Option<char> {
|
||||
self.src[self.i..].chars().next()
|
||||
}
|
||||
pub fn next_char(&mut self) -> Option<char> {
|
||||
self.end_sections();
|
||||
let ch = self.src[self.i..].chars().next()?;
|
||||
self.i += ch.len_utf8();
|
||||
Some(ch)
|
||||
}
|
||||
fn word_splitter(ch: char) -> bool {
|
||||
ch.is_whitespace() || ".,;)}".contains(ch)
|
||||
}
|
||||
pub fn peek_word(&self) -> &str {
|
||||
self.src[self.i..]
|
||||
.split(Self::word_splitter)
|
||||
.next()
|
||||
.unwrap_or("")
|
||||
}
|
||||
pub fn next_word(&mut self) -> &str {
|
||||
self.end_sections();
|
||||
let word = self.src[self.i..]
|
||||
.split(Self::word_splitter)
|
||||
.next()
|
||||
.unwrap_or("");
|
||||
self.i += word.len();
|
||||
word
|
||||
}
|
||||
pub fn peek_line(&self) -> &str {
|
||||
self.src[self.i..].lines().next().unwrap_or("")
|
||||
}
|
||||
pub fn next_line(&mut self) -> &str {
|
||||
self.end_sections();
|
||||
let line = self.src[self.i..].lines().next().unwrap_or("");
|
||||
self.i += line.len();
|
||||
line
|
||||
}
|
||||
|
||||
pub fn get_pos(&self) -> SourcePos {
|
||||
SourcePos(self.i)
|
||||
}
|
||||
pub fn set_pos(&mut self, new: SourcePos) {
|
||||
self.i = new.0
|
||||
}
|
||||
|
||||
/// Returns a SectionMarker which, when dropped, indicates the end of this section.
|
||||
/// Useful for debugging the parser.
|
||||
pub fn section_begin(&mut self, section: String) -> Arc<String> {
|
||||
#[cfg(debug_assertions)]
|
||||
println!("Section begin: {}", §ion);
|
||||
let arc = Arc::new(section);
|
||||
self.sections.push(SectionMarker {
|
||||
section: Arc::clone(&arc),
|
||||
start: self.get_pos(),
|
||||
end: None,
|
||||
});
|
||||
arc
|
||||
}
|
||||
pub fn sections(&self) -> &Vec<SectionMarker> {
|
||||
&self.sections
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Source {
|
||||
fn drop(&mut self) {
|
||||
self.end_sections()
|
||||
}
|
||||
}
|
||||
|
||||
/// Returned from Source::begin(), this indicates that parsing of
|
||||
/// a certain section has not yet finished.
|
||||
/// Once this is dropped, a section is considered done.
|
||||
pub struct SectionMarker {
|
||||
section: Arc<String>,
|
||||
start: SourcePos,
|
||||
end: Option<SourcePos>,
|
||||
}
|
||||
impl SectionMarker {
|
||||
pub fn section(&self) -> &str {
|
||||
&self.section
|
||||
}
|
||||
pub fn start(&self) -> &SourcePos {
|
||||
&self.start
|
||||
}
|
||||
pub fn end(&self) -> &Option<SourcePos> {
|
||||
&self.end
|
||||
}
|
||||
/// If this is the only remaining SectionMarker for this section,
|
||||
/// this method sets its `end` property.
|
||||
fn checked_end(&mut self, end: SourcePos) {
|
||||
if self.end.is_none() {
|
||||
if Arc::strong_count(&self.section) == 1 {
|
||||
self.end = Some(end);
|
||||
#[cfg(debug_assertions)]
|
||||
println!("Section end : {}", &self.section);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct SourcePos(usize);
|
||||
impl SourcePos {
|
||||
fn diff(&self, rhs: &Self) -> usize {
|
||||
rhs.0 - self.0
|
||||
}
|
||||
}
|
199
mers_lib/src/parsing/statements.rs
Executable file
199
mers_lib/src/parsing/statements.rs
Executable file
@ -0,0 +1,199 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use super::Source;
|
||||
use crate::{
|
||||
data::{self, Data},
|
||||
program::{self, parsed::MersStatement},
|
||||
};
|
||||
|
||||
pub fn parse(src: &mut Source) -> Result<Option<Box<dyn program::parsed::MersStatement>>, ()> {
|
||||
src.section_begin("statement".to_string());
|
||||
let mut first = if let Some(s) = parse_no_chain(src)? {
|
||||
s
|
||||
} else {
|
||||
return Ok(None);
|
||||
};
|
||||
src.skip_whitespace();
|
||||
match src.peek_word() {
|
||||
":=" => {
|
||||
src.next_word();
|
||||
first = Box::new(program::parsed::init_to::InitTo {
|
||||
target: first,
|
||||
source: parse(src)?.expect("todo"),
|
||||
});
|
||||
}
|
||||
"=" => {
|
||||
src.next_word();
|
||||
first = Box::new(program::parsed::assign_to::AssignTo {
|
||||
target: first,
|
||||
source: parse(src)?.expect("todo"),
|
||||
});
|
||||
}
|
||||
"->" => {
|
||||
src.next_word();
|
||||
first = Box::new(program::parsed::function::Function {
|
||||
arg: first,
|
||||
run: parse(src)?.expect("err: bad eof, fn needs some statement"),
|
||||
});
|
||||
}
|
||||
_ => loop {
|
||||
src.skip_whitespace();
|
||||
if let Some('.') = src.peek_char() {
|
||||
src.next_char();
|
||||
let chained = parse_no_chain(src)?.expect("err: EOF instead of chain");
|
||||
first = Box::new(program::parsed::chain::Chain { first, chained });
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
},
|
||||
}
|
||||
if matches!(src.peek_char(), Some(',' | ';')) {
|
||||
src.next_char();
|
||||
}
|
||||
Ok(Some(first))
|
||||
}
|
||||
/// Assumes the { has already been parsed
|
||||
pub fn parse_block(src: &mut Source) -> Result<program::parsed::block::Block, ()> {
|
||||
Ok(program::parsed::block::Block {
|
||||
statements: parse_multiple(src, '}')?,
|
||||
})
|
||||
}
|
||||
pub fn parse_tuple(src: &mut Source) -> Result<program::parsed::tuple::Tuple, ()> {
|
||||
Ok(program::parsed::tuple::Tuple {
|
||||
elems: parse_multiple(src, ')')?,
|
||||
})
|
||||
}
|
||||
pub fn parse_multiple(src: &mut Source, end: char) -> Result<Vec<Box<dyn MersStatement>>, ()> {
|
||||
src.section_begin("block".to_string());
|
||||
let mut statements = vec![];
|
||||
loop {
|
||||
src.skip_whitespace();
|
||||
if matches!(src.peek_char(), Some(e) if e == end) {
|
||||
src.next_char();
|
||||
break;
|
||||
} else if let Some(s) = parse(src)? {
|
||||
statements.push(s);
|
||||
} else {
|
||||
// EOF
|
||||
break;
|
||||
}
|
||||
}
|
||||
Ok(statements)
|
||||
}
|
||||
pub fn parse_no_chain(
|
||||
src: &mut Source,
|
||||
) -> Result<Option<Box<dyn program::parsed::MersStatement>>, ()> {
|
||||
src.section_begin("statement no chain".to_string());
|
||||
src.skip_whitespace();
|
||||
match src.peek_char() {
|
||||
Some('{') => {
|
||||
src.next_char();
|
||||
return Ok(Some(Box::new(parse_block(src)?)));
|
||||
}
|
||||
Some('(') => {
|
||||
src.next_char();
|
||||
return Ok(Some(Box::new(parse_tuple(src)?)));
|
||||
}
|
||||
Some('"') => {
|
||||
src.section_begin("string literal".to_string());
|
||||
src.next_char();
|
||||
let mut s = String::new();
|
||||
loop {
|
||||
if let Some(ch) = src.next_char() {
|
||||
if ch == '\\' {
|
||||
s.push(match src.next_char() {
|
||||
Some('\\') => '\\',
|
||||
Some('r') => '\r',
|
||||
Some('n') => '\n',
|
||||
Some('t') => '\t',
|
||||
Some(o) => todo!("err: unknown backslash escape '\\{o}'"),
|
||||
None => todo!("err: eof in backslash escape"),
|
||||
});
|
||||
} else if ch == '"' {
|
||||
break;
|
||||
} else {
|
||||
s.push(ch);
|
||||
}
|
||||
} else {
|
||||
todo!("err: eof in string")
|
||||
}
|
||||
}
|
||||
return Ok(Some(Box::new(program::parsed::value::Value(Data::new(
|
||||
crate::data::string::String(s),
|
||||
)))));
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
Ok(Some(match src.next_word() {
|
||||
"if" => {
|
||||
src.section_begin("if".to_string());
|
||||
Box::new(program::parsed::r#if::If {
|
||||
condition: parse(src)?.expect("err: EOF instead of condition"),
|
||||
on_true: parse(src)?.expect("err: EOF instead of on_true"),
|
||||
on_false: {
|
||||
src.skip_whitespace();
|
||||
if src.peek_word() == "else" {
|
||||
src.section_begin("else".to_string());
|
||||
src.next_word();
|
||||
Some(parse(src)?.expect("err: EOF instead of on_false after else"))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
},
|
||||
})
|
||||
}
|
||||
"loop" => {
|
||||
src.section_begin("loop".to_string());
|
||||
Box::new(program::parsed::r#loop::Loop {
|
||||
inner: parse(src)?.expect("err: EOF instead of inner statement after loop"),
|
||||
})
|
||||
}
|
||||
"switch" => {
|
||||
src.section_begin("loop".to_string());
|
||||
todo!()
|
||||
}
|
||||
"true" => Box::new(program::parsed::value::Value(Data::new(
|
||||
crate::data::bool::Bool(true),
|
||||
))),
|
||||
"false" => Box::new(program::parsed::value::Value(Data::new(
|
||||
crate::data::bool::Bool(false),
|
||||
))),
|
||||
"" => return Ok(None),
|
||||
o => {
|
||||
let o = o.to_string();
|
||||
src.section_begin("literals, variables, and other non-keyword things".to_string());
|
||||
if let Ok(n) = o.parse() {
|
||||
if src.peek_char() == Some('.') {
|
||||
let here = src.get_pos();
|
||||
src.next_char();
|
||||
if let Ok(num) = format!("{o}.{}", src.next_word()).parse() {
|
||||
Box::new(program::parsed::value::Value(Data::new(
|
||||
crate::data::float::Float(num),
|
||||
)))
|
||||
} else {
|
||||
src.set_pos(here);
|
||||
Box::new(program::parsed::value::Value(Data::new(
|
||||
crate::data::int::Int(n),
|
||||
)))
|
||||
}
|
||||
} else {
|
||||
Box::new(program::parsed::value::Value(Data::new(
|
||||
crate::data::int::Int(n),
|
||||
)))
|
||||
}
|
||||
} else {
|
||||
if let Some('&') = o.chars().next() {
|
||||
Box::new(program::parsed::variable::Variable {
|
||||
is_ref: true,
|
||||
var: o[1..].to_string(),
|
||||
})
|
||||
} else {
|
||||
Box::new(program::parsed::variable::Variable {
|
||||
is_ref: false,
|
||||
var: o.to_string(),
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}))
|
||||
}
|
51
mers_lib/src/parsing/types.rs
Executable file
51
mers_lib/src/parsing/types.rs
Executable file
@ -0,0 +1,51 @@
|
||||
use super::Source;
|
||||
|
||||
/// multiple types are represented as a `Vec<ParsedType>`.
|
||||
pub enum ParsedType {
|
||||
Tuple(Vec<Vec<Self>>),
|
||||
Type(String),
|
||||
}
|
||||
|
||||
fn parse_single_type(src: &mut Source) -> Result<ParsedType, ()> {
|
||||
src.section_begin("parse single type".to_string());
|
||||
src.skip_whitespace();
|
||||
Ok(match src.peek_char() {
|
||||
// Tuple
|
||||
Some('(') => {
|
||||
src.next_char();
|
||||
src.section_begin("parse tuple's inner types".to_string());
|
||||
let mut inner = vec![];
|
||||
loop {
|
||||
match src.peek_char() {
|
||||
Some(')') => {
|
||||
src.next_char();
|
||||
break;
|
||||
}
|
||||
Some(',') => {
|
||||
src.next_char();
|
||||
}
|
||||
_ => todo!("err: bad char in tuple inner type"),
|
||||
}
|
||||
inner.push(parse_type(src)?);
|
||||
}
|
||||
ParsedType::Tuple(inner)
|
||||
}
|
||||
Some(_) => ParsedType::Type(src.next_word().to_lowercase()),
|
||||
None => todo!(),
|
||||
})
|
||||
}
|
||||
|
||||
fn parse_type(src: &mut Source) -> Result<Vec<ParsedType>, ()> {
|
||||
src.section_begin("parse single type".to_string());
|
||||
let mut types = vec![];
|
||||
loop {
|
||||
types.push(parse_single_type(src)?);
|
||||
src.skip_whitespace();
|
||||
if let Some('/') = src.peek_char() {
|
||||
continue;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
Ok(types)
|
||||
}
|
198
mers_lib/src/program/configs/mod.rs
Executable file
198
mers_lib/src/program/configs/mod.rs
Executable file
@ -0,0 +1,198 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::{
|
||||
data::{self, Data, Type},
|
||||
info::Local,
|
||||
program,
|
||||
};
|
||||
|
||||
mod with_command_running;
|
||||
mod with_iters;
|
||||
mod with_list;
|
||||
|
||||
/// Usage: create an empty Config using Config::new(), use the methods to customize it, then get the Infos using Config::infos()
|
||||
/// bundle_* for bundles (combines multiple groups or even bundles)
|
||||
/// with_* for usage-oriented groups
|
||||
/// add_* to add custom things
|
||||
///
|
||||
/// For doc-comments:
|
||||
/// Description
|
||||
/// `bundle_std()`
|
||||
/// `type` - description
|
||||
/// `var: type` - description
|
||||
pub struct Config {
|
||||
globals: usize,
|
||||
info_parsed: super::parsed::Info,
|
||||
info_run: super::run::Info,
|
||||
}
|
||||
|
||||
impl Config {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
globals: 0,
|
||||
info_parsed: Default::default(),
|
||||
info_run: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
/// standard utilitis used in many programs
|
||||
/// `bundle_base()`
|
||||
/// `with_list()`
|
||||
/// `with_command_running()`
|
||||
pub fn bundle_std(self) -> Self {
|
||||
self.with_command_running().with_list().bundle_base()
|
||||
}
|
||||
/// base utilities used in most programs
|
||||
/// `with_prints()`
|
||||
/// `with_math()`
|
||||
/// `with_get()`
|
||||
/// `with_iters()`
|
||||
pub fn bundle_base(self) -> Self {
|
||||
self.with_iters().with_get().with_math().with_prints()
|
||||
}
|
||||
|
||||
/// `println: fn` prints to stdout and adds a newline to the end
|
||||
/// `print: fn` prints to stdout
|
||||
/// `eprintln: fn` prints to stderr and adds a newline to the end
|
||||
/// `eprint: fn` prints to stderr
|
||||
/// `debug: fn` debug-prints any value
|
||||
pub fn with_prints(self) -> Self {
|
||||
self.add_var(
|
||||
"debug".to_string(),
|
||||
Data::new(data::function::Function {
|
||||
info: program::run::Info::neverused(),
|
||||
out: Arc::new(|_a| todo!()),
|
||||
run: Arc::new(|a, _i| {
|
||||
eprintln!("{:#?}", a.get());
|
||||
Data::empty_tuple()
|
||||
}),
|
||||
}),
|
||||
)
|
||||
.add_var(
|
||||
"eprint".to_string(),
|
||||
Data::new(data::function::Function {
|
||||
info: program::run::Info::neverused(),
|
||||
out: Arc::new(|_a| todo!()),
|
||||
run: Arc::new(|a, _i| {
|
||||
eprint!("{}", a.get());
|
||||
Data::empty_tuple()
|
||||
}),
|
||||
}),
|
||||
)
|
||||
.add_var(
|
||||
"eprintln".to_string(),
|
||||
Data::new(data::function::Function {
|
||||
info: program::run::Info::neverused(),
|
||||
out: Arc::new(|_a| todo!()),
|
||||
run: Arc::new(|a, _i| {
|
||||
eprintln!("{}", a.get());
|
||||
Data::empty_tuple()
|
||||
}),
|
||||
}),
|
||||
)
|
||||
.add_var(
|
||||
"print".to_string(),
|
||||
Data::new(data::function::Function {
|
||||
info: program::run::Info::neverused(),
|
||||
out: Arc::new(|_a| todo!()),
|
||||
run: Arc::new(|a, _i| {
|
||||
print!("{}", a.get());
|
||||
Data::empty_tuple()
|
||||
}),
|
||||
}),
|
||||
)
|
||||
.add_var(
|
||||
"println".to_string(),
|
||||
Data::new(data::function::Function {
|
||||
info: program::run::Info::neverused(),
|
||||
out: Arc::new(|_a| todo!()),
|
||||
run: Arc::new(|a, _i| {
|
||||
println!("{}", a.get());
|
||||
Data::empty_tuple()
|
||||
}),
|
||||
}),
|
||||
)
|
||||
}
|
||||
/// `sum: fn` returns the sum of all the numbers in the tuple
|
||||
pub fn with_math(self) -> Self {
|
||||
self.add_var(
|
||||
"sum".to_string(),
|
||||
Data::new(data::function::Function {
|
||||
info: program::run::Info::neverused(),
|
||||
out: Arc::new(|_a| todo!()),
|
||||
run: Arc::new(|a, _i| {
|
||||
if let Some(tuple) = a.get().as_any().downcast_ref::<data::tuple::Tuple>() {
|
||||
let mut sumi = 0;
|
||||
let mut sumf = 0.0;
|
||||
let mut usef = false;
|
||||
for val in &tuple.0 {
|
||||
if let Some(i) = val.get().as_any().downcast_ref::<data::int::Int>() {
|
||||
sumi += i.0;
|
||||
} else if let Some(i) =
|
||||
val.get().as_any().downcast_ref::<data::float::Float>()
|
||||
{
|
||||
sumf += i.0;
|
||||
usef = true;
|
||||
}
|
||||
}
|
||||
if usef {
|
||||
Data::new(data::float::Float(sumi as f64 + sumf))
|
||||
} else {
|
||||
Data::new(data::int::Int(sumi))
|
||||
}
|
||||
} else {
|
||||
unreachable!("sum called on non-tuple")
|
||||
}
|
||||
}),
|
||||
}),
|
||||
)
|
||||
}
|
||||
/// `get: fn` is used to retrieve elements from collections
|
||||
pub fn with_get(self) -> Self {
|
||||
self.add_var(
|
||||
"get".to_string(),
|
||||
Data::new(data::function::Function {
|
||||
info: program::run::Info::neverused(),
|
||||
out: Arc::new(|_a| todo!()),
|
||||
run: Arc::new(|a, _i| {
|
||||
if let Some(tuple) = a.get().as_any().downcast_ref::<data::tuple::Tuple>() {
|
||||
if let (Some(v), Some(i)) = (tuple.get(0), tuple.get(1)) {
|
||||
if let Some(i) = i.get().as_any().downcast_ref::<data::int::Int>() {
|
||||
if let Ok(i) = i.0.try_into() {
|
||||
if let Some(v) = v.get().get(i) {
|
||||
Data::one_tuple(v)
|
||||
} else {
|
||||
Data::empty_tuple()
|
||||
}
|
||||
} else {
|
||||
Data::empty_tuple()
|
||||
}
|
||||
} else {
|
||||
unreachable!("get called with non-int index")
|
||||
}
|
||||
} else {
|
||||
unreachable!("get called on tuple with len < 2")
|
||||
}
|
||||
} else {
|
||||
unreachable!("get called on non-tuple, arg must be (_, index)")
|
||||
}
|
||||
}),
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn add_var(mut self, name: String, val: Data) -> Self {
|
||||
self.info_parsed.scopes[0].init_var(name, (0, self.globals));
|
||||
self.info_run.scopes[0].init_var(self.globals, val);
|
||||
self.globals += 1;
|
||||
self
|
||||
}
|
||||
pub fn add_type(mut self, name: String, t: Type) -> Self {
|
||||
// TODO! needed for type syntax in the parser, everything else probably(?) works already
|
||||
self
|
||||
}
|
||||
|
||||
pub fn infos(self) -> (super::parsed::Info, super::run::Info) {
|
||||
(self.info_parsed, self.info_run)
|
||||
}
|
||||
}
|
104
mers_lib/src/program/configs/with_command_running.rs
Executable file
104
mers_lib/src/program/configs/with_command_running.rs
Executable file
@ -0,0 +1,104 @@
|
||||
use std::{fmt::Display, process::Command, sync::Arc};
|
||||
|
||||
use crate::{
|
||||
data::{self, Data, MersData, MersType},
|
||||
program,
|
||||
};
|
||||
|
||||
use super::Config;
|
||||
|
||||
impl Config {
|
||||
/// adds utilities to run commands installed on the system and get their output.
|
||||
/// `run_command: fn` runs a command with arguments.
|
||||
/// Args: (cmd, args) where cmd is a string and args is an Iterable over strings
|
||||
/// `RunCommandError` holds the error if the command can't be executed
|
||||
pub fn with_command_running(self) -> Self {
|
||||
self.add_var(
|
||||
"run_command".to_string(),
|
||||
Data::new(data::function::Function {
|
||||
info: program::run::Info::neverused(),
|
||||
out: Arc::new(|_a| todo!()),
|
||||
run: Arc::new(|a, _i| {
|
||||
if let Some(cmd) = a.get().as_any().downcast_ref::<data::tuple::Tuple>() {
|
||||
if let (Some(cmd), Some(args)) = (cmd.get(0), cmd.get(1)) {
|
||||
if let (Some(cmd), Some(args)) = (
|
||||
cmd.get().as_any().downcast_ref::<data::string::String>(),
|
||||
args.get().iterable(),
|
||||
) {
|
||||
match Command::new(&cmd.0)
|
||||
.args(args.map(|v| v.get().to_string()))
|
||||
.output()
|
||||
{
|
||||
Ok(output) => {
|
||||
let status = if let Some(code) = output.status.code() {
|
||||
Data::new(data::int::Int(code as _))
|
||||
} else {
|
||||
Data::empty_tuple()
|
||||
};
|
||||
let stdout =
|
||||
String::from_utf8_lossy(&output.stdout).into_owned();
|
||||
let stderr =
|
||||
String::from_utf8_lossy(&output.stderr).into_owned();
|
||||
Data::new(data::tuple::Tuple(vec![
|
||||
status,
|
||||
Data::new(data::string::String(stdout)),
|
||||
Data::new(data::string::String(stderr)),
|
||||
]))
|
||||
}
|
||||
Err(e) => Data::new(RunCommandError(e.to_string())),
|
||||
}
|
||||
} else {
|
||||
unreachable!("run_command called with arguments other than (String, <Iterable>).")
|
||||
}
|
||||
} else {
|
||||
unreachable!("run_command called with too few arguments")
|
||||
}
|
||||
} else {
|
||||
unreachable!("run_command called with non-tuple argument")
|
||||
}
|
||||
}),
|
||||
}),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct RunCommandError(String);
|
||||
#[derive(Debug)]
|
||||
pub struct RunCommandErrorT;
|
||||
impl MersData for RunCommandError {
|
||||
fn clone(&self) -> Box<dyn MersData> {
|
||||
Box::new(Clone::clone(self))
|
||||
}
|
||||
fn as_any(&self) -> &dyn std::any::Any {
|
||||
self
|
||||
}
|
||||
fn mut_any(&mut self) -> &mut dyn std::any::Any {
|
||||
self
|
||||
}
|
||||
fn to_any(self) -> Box<dyn std::any::Any> {
|
||||
Box::new(self)
|
||||
}
|
||||
}
|
||||
impl MersType for RunCommandErrorT {
|
||||
fn is_same_type_as(&self, other: &dyn MersType) -> bool {
|
||||
other.as_any().downcast_ref::<Self>().is_some()
|
||||
}
|
||||
fn is_included_in_single(&self, target: &dyn MersType) -> bool {
|
||||
self.is_same_type_as(target)
|
||||
}
|
||||
fn as_any(&self) -> &dyn std::any::Any {
|
||||
self
|
||||
}
|
||||
fn mut_any(&mut self) -> &mut dyn std::any::Any {
|
||||
self
|
||||
}
|
||||
fn to_any(self) -> Box<dyn std::any::Any> {
|
||||
Box::new(self)
|
||||
}
|
||||
}
|
||||
impl Display for RunCommandError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "RunCommandError: {}", self.0)
|
||||
}
|
||||
}
|
175
mers_lib/src/program/configs/with_iters.rs
Executable file
175
mers_lib/src/program/configs/with_iters.rs
Executable file
@ -0,0 +1,175 @@
|
||||
use std::{fmt::Display, sync::Arc};
|
||||
|
||||
use crate::{
|
||||
data::{self, Data, MersData},
|
||||
program,
|
||||
};
|
||||
|
||||
use super::Config;
|
||||
|
||||
impl Config {
|
||||
/// Adds functions to deal with iterables
|
||||
/// `iter: fn` executes a function once for each element of the iterable
|
||||
pub fn with_iters(self) -> Self {
|
||||
self.add_var(
|
||||
"iter".to_string(),
|
||||
Data::new(data::function::Function {
|
||||
info: program::run::Info::neverused(),
|
||||
out: Arc::new(|_a| todo!()),
|
||||
run: Arc::new(|a, _i| {
|
||||
if let Some(tuple) = a.get().as_any().downcast_ref::<data::tuple::Tuple>() {
|
||||
if let (Some(v), Some(f)) = (tuple.get(0), tuple.get(1)) {
|
||||
if let (Some(iter), Some(f)) = (
|
||||
v.get().iterable(),
|
||||
f.get().as_any().downcast_ref::<data::function::Function>(),
|
||||
) {
|
||||
for v in iter {
|
||||
f.run(v);
|
||||
}
|
||||
Data::empty_tuple()
|
||||
} else {
|
||||
unreachable!(
|
||||
"iter called on tuple not containing iterable and function"
|
||||
)
|
||||
}
|
||||
} else {
|
||||
unreachable!("iter called on tuple with len < 2")
|
||||
}
|
||||
} else {
|
||||
unreachable!("iter called on non-tuple")
|
||||
}
|
||||
}),
|
||||
}),
|
||||
)
|
||||
.add_var(
|
||||
"map".to_string(),
|
||||
Data::new(data::function::Function {
|
||||
info: program::run::Info::neverused(),
|
||||
out: Arc::new(|_a| todo!()),
|
||||
run: Arc::new(|a, _i| {
|
||||
if let Some(tuple) = a.get().as_any().downcast_ref::<data::tuple::Tuple>() {
|
||||
if let (Some(v), Some(f)) = (tuple.get(0), tuple.get(1)) {
|
||||
if let Some(f) =
|
||||
f.get().as_any().downcast_ref::<data::function::Function>()
|
||||
{
|
||||
Data::new(Iter(Iters::Map(Clone::clone(f)), v.clone()))
|
||||
} else {
|
||||
unreachable!("iter called on tuple not containing function")
|
||||
}
|
||||
} else {
|
||||
unreachable!("iter called on tuple with len < 2")
|
||||
}
|
||||
} else {
|
||||
unreachable!("iter called on non-tuple")
|
||||
}
|
||||
}),
|
||||
}),
|
||||
)
|
||||
.add_var(
|
||||
"filter".to_string(),
|
||||
Data::new(data::function::Function {
|
||||
info: program::run::Info::neverused(),
|
||||
out: Arc::new(|_a| todo!()),
|
||||
run: Arc::new(|a, _i| {
|
||||
if let Some(tuple) = a.get().as_any().downcast_ref::<data::tuple::Tuple>() {
|
||||
if let (Some(v), Some(f)) = (tuple.get(0), tuple.get(1)) {
|
||||
if let Some(f) =
|
||||
f.get().as_any().downcast_ref::<data::function::Function>()
|
||||
{
|
||||
Data::new(Iter(Iters::Filter(Clone::clone(f)), v.clone()))
|
||||
} else {
|
||||
unreachable!("iter called on tuple not containing function")
|
||||
}
|
||||
} else {
|
||||
unreachable!("iter called on tuple with len < 2")
|
||||
}
|
||||
} else {
|
||||
unreachable!("iter called on non-tuple")
|
||||
}
|
||||
}),
|
||||
}),
|
||||
)
|
||||
.add_var(
|
||||
"filter_map".to_string(),
|
||||
Data::new(data::function::Function {
|
||||
info: program::run::Info::neverused(),
|
||||
out: Arc::new(|_a| todo!()),
|
||||
run: Arc::new(|a, _i| {
|
||||
if let Some(tuple) = a.get().as_any().downcast_ref::<data::tuple::Tuple>() {
|
||||
if let (Some(v), Some(f)) = (tuple.get(0), tuple.get(1)) {
|
||||
if let Some(f) =
|
||||
f.get().as_any().downcast_ref::<data::function::Function>()
|
||||
{
|
||||
Data::new(Iter(Iters::FilterMap(Clone::clone(f)), v.clone()))
|
||||
} else {
|
||||
unreachable!("iter called on tuple not containing function")
|
||||
}
|
||||
} else {
|
||||
unreachable!("iter called on tuple with len < 2")
|
||||
}
|
||||
} else {
|
||||
unreachable!("iter called on non-tuple")
|
||||
}
|
||||
}),
|
||||
}),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum Iters {
|
||||
Map(data::function::Function),
|
||||
Filter(data::function::Function),
|
||||
FilterMap(data::function::Function),
|
||||
}
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Iter(Iters, Data);
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct IterT(Iters);
|
||||
impl MersData for Iter {
|
||||
fn iterable(&self) -> Option<Box<dyn Iterator<Item = Data>>> {
|
||||
Some(match &self.0 {
|
||||
Iters::Map(f) => {
|
||||
let f = Clone::clone(f);
|
||||
Box::new(self.1.get().iterable()?.map(move |v| f.run(v)))
|
||||
}
|
||||
Iters::Filter(f) => {
|
||||
let f = Clone::clone(f);
|
||||
Box::new(self.1.get().iterable()?.filter(move |v| {
|
||||
f.run(v.clone())
|
||||
.get()
|
||||
.as_any()
|
||||
.downcast_ref::<data::bool::Bool>()
|
||||
.is_some_and(|b| b.0)
|
||||
}))
|
||||
}
|
||||
Iters::FilterMap(f) => {
|
||||
let f = Clone::clone(f);
|
||||
Box::new(
|
||||
self.1
|
||||
.get()
|
||||
.iterable()?
|
||||
.filter_map(move |v| f.run(v).get().matches()),
|
||||
)
|
||||
}
|
||||
_ => todo!(),
|
||||
})
|
||||
}
|
||||
fn clone(&self) -> Box<dyn MersData> {
|
||||
Box::new(Clone::clone(self))
|
||||
}
|
||||
fn as_any(&self) -> &dyn std::any::Any {
|
||||
self
|
||||
}
|
||||
fn mut_any(&mut self) -> &mut dyn std::any::Any {
|
||||
self
|
||||
}
|
||||
fn to_any(self) -> Box<dyn std::any::Any> {
|
||||
Box::new(self)
|
||||
}
|
||||
}
|
||||
impl Display for Iter {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "<Iter>")
|
||||
}
|
||||
}
|
90
mers_lib/src/program/configs/with_list.rs
Executable file
90
mers_lib/src/program/configs/with_list.rs
Executable file
@ -0,0 +1,90 @@
|
||||
use std::{fmt::Display, sync::Arc};
|
||||
|
||||
use crate::{
|
||||
data::{self, Data, MersData, MersType, Type},
|
||||
program,
|
||||
};
|
||||
|
||||
use super::Config;
|
||||
|
||||
impl Config {
|
||||
/// Adds a simple list type
|
||||
/// `List` can store a variable number of items
|
||||
/// `as_list: fn` turns a tuple into a list
|
||||
pub fn with_list(self) -> Self {
|
||||
// TODO: Type with generics
|
||||
self.add_type("List".to_string(), Type::new(ListT(Type::empty_tuple())))
|
||||
.add_var(
|
||||
"as_list".to_string(),
|
||||
Data::new(data::function::Function {
|
||||
info: program::run::Info::neverused(),
|
||||
out: Arc::new(|_a| todo!()),
|
||||
run: Arc::new(|a, _i| {
|
||||
if let Some(i) = a.get().iterable() {
|
||||
Data::new(List(i.collect()))
|
||||
} else {
|
||||
unreachable!("as_list called on non-iterable")
|
||||
}
|
||||
}),
|
||||
}),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct List(Vec<Data>);
|
||||
#[derive(Debug)]
|
||||
pub struct ListT(Type);
|
||||
impl MersData for List {
|
||||
fn iterable(&self) -> Option<Box<dyn Iterator<Item = Data>>> {
|
||||
Some(Box::new(self.0.clone().into_iter()))
|
||||
}
|
||||
fn clone(&self) -> Box<dyn MersData> {
|
||||
Box::new(Clone::clone(self))
|
||||
}
|
||||
fn as_any(&self) -> &dyn std::any::Any {
|
||||
self
|
||||
}
|
||||
fn mut_any(&mut self) -> &mut dyn std::any::Any {
|
||||
self
|
||||
}
|
||||
fn to_any(self) -> Box<dyn std::any::Any> {
|
||||
Box::new(self)
|
||||
}
|
||||
}
|
||||
impl MersType for ListT {
|
||||
fn iterable(&self) -> Option<Type> {
|
||||
Some(self.0.clone())
|
||||
}
|
||||
fn is_same_type_as(&self, other: &dyn MersType) -> bool {
|
||||
other
|
||||
.as_any()
|
||||
.downcast_ref::<Self>()
|
||||
.is_some_and(|v| self.0.is_same_type_as(&v.0))
|
||||
}
|
||||
fn is_included_in_single(&self, target: &dyn MersType) -> bool {
|
||||
self.is_same_type_as(target)
|
||||
}
|
||||
fn as_any(&self) -> &dyn std::any::Any {
|
||||
self
|
||||
}
|
||||
fn mut_any(&mut self) -> &mut dyn std::any::Any {
|
||||
self
|
||||
}
|
||||
fn to_any(self) -> Box<dyn std::any::Any> {
|
||||
Box::new(self)
|
||||
}
|
||||
}
|
||||
impl Display for List {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "[")?;
|
||||
for (i, c) in self.0.iter().enumerate() {
|
||||
if i > 0 {
|
||||
write!(f, ", ")?;
|
||||
}
|
||||
write!(f, "{}", c.get())?;
|
||||
}
|
||||
write!(f, "]")?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
3
mers_lib/src/program/mod.rs
Executable file
3
mers_lib/src/program/mod.rs
Executable file
@ -0,0 +1,3 @@
|
||||
pub mod configs;
|
||||
pub mod parsed;
|
||||
pub mod run;
|
25
mers_lib/src/program/parsed/assign_to.rs
Executable file
25
mers_lib/src/program/parsed/assign_to.rs
Executable file
@ -0,0 +1,25 @@
|
||||
use crate::{info::Local, program};
|
||||
|
||||
use super::{CompInfo, MersStatement};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct AssignTo {
|
||||
pub target: Box<dyn MersStatement>,
|
||||
pub source: Box<dyn MersStatement>,
|
||||
}
|
||||
|
||||
impl MersStatement for AssignTo {
|
||||
fn has_scope(&self) -> bool {
|
||||
false
|
||||
}
|
||||
fn compile_custom(
|
||||
&self,
|
||||
info: &mut crate::info::Info<super::Local>,
|
||||
mut comp: CompInfo,
|
||||
) -> Result<Box<dyn program::run::MersStatement>, String> {
|
||||
Ok(Box::new(program::run::assign_to::AssignTo {
|
||||
target: self.target.compile(info, comp)?,
|
||||
source: self.source.compile(info, comp)?,
|
||||
}))
|
||||
}
|
||||
}
|
26
mers_lib/src/program/parsed/block.rs
Executable file
26
mers_lib/src/program/parsed/block.rs
Executable file
@ -0,0 +1,26 @@
|
||||
use crate::{info, program};
|
||||
|
||||
use super::{CompInfo, MersStatement};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Block {
|
||||
pub statements: Vec<Box<dyn MersStatement>>,
|
||||
}
|
||||
impl MersStatement for Block {
|
||||
fn has_scope(&self) -> bool {
|
||||
true
|
||||
}
|
||||
fn compile_custom(
|
||||
&self,
|
||||
info: &mut info::Info<super::Local>,
|
||||
comp: CompInfo,
|
||||
) -> Result<Box<dyn program::run::MersStatement>, String> {
|
||||
Ok(Box::new(program::run::block::Block {
|
||||
statements: self
|
||||
.statements
|
||||
.iter()
|
||||
.map(|v| v.compile(info, comp))
|
||||
.collect::<Result<Vec<_>, _>>()?,
|
||||
}))
|
||||
}
|
||||
}
|
24
mers_lib/src/program/parsed/chain.rs
Executable file
24
mers_lib/src/program/parsed/chain.rs
Executable file
@ -0,0 +1,24 @@
|
||||
use crate::{info, program};
|
||||
|
||||
use super::{CompInfo, MersStatement};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Chain {
|
||||
pub first: Box<dyn MersStatement>,
|
||||
pub chained: Box<dyn MersStatement>,
|
||||
}
|
||||
impl MersStatement for Chain {
|
||||
fn has_scope(&self) -> bool {
|
||||
false
|
||||
}
|
||||
fn compile_custom(
|
||||
&self,
|
||||
info: &mut info::Info<super::Local>,
|
||||
comp: CompInfo,
|
||||
) -> Result<Box<dyn program::run::MersStatement>, String> {
|
||||
Ok(Box::new(program::run::chain::Chain {
|
||||
first: self.first.compile(info, comp)?,
|
||||
chained: self.chained.compile(info, comp)?,
|
||||
}))
|
||||
}
|
||||
}
|
42
mers_lib/src/program/parsed/function.rs
Executable file
42
mers_lib/src/program/parsed/function.rs
Executable file
@ -0,0 +1,42 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::{
|
||||
data::{self, Data},
|
||||
info::Local,
|
||||
program,
|
||||
};
|
||||
|
||||
use super::{CompInfo, MersStatement};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Function {
|
||||
pub arg: Box<dyn MersStatement>,
|
||||
pub run: Box<dyn MersStatement>,
|
||||
}
|
||||
|
||||
impl MersStatement for Function {
|
||||
fn has_scope(&self) -> bool {
|
||||
// TODO: what???
|
||||
false
|
||||
}
|
||||
fn compile_custom(
|
||||
&self,
|
||||
info: &mut crate::info::Info<super::Local>,
|
||||
mut comp: CompInfo,
|
||||
) -> Result<Box<dyn program::run::MersStatement>, String> {
|
||||
comp.is_init = true;
|
||||
let arg = self.arg.compile(info, comp)?;
|
||||
comp.is_init = false;
|
||||
let run = self.run.compile(info, comp)?;
|
||||
Ok(Box::new(program::run::function::Function {
|
||||
func_no_info: data::function::Function {
|
||||
info: program::run::Info::neverused(),
|
||||
out: Arc::new(|_i| todo!()),
|
||||
run: Arc::new(move |i, info| {
|
||||
data::defs::assign(i, &arg.run(info));
|
||||
run.run(info)
|
||||
}),
|
||||
},
|
||||
}))
|
||||
}
|
||||
}
|
31
mers_lib/src/program/parsed/if.rs
Executable file
31
mers_lib/src/program/parsed/if.rs
Executable file
@ -0,0 +1,31 @@
|
||||
use crate::program;
|
||||
|
||||
use super::{CompInfo, MersStatement};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct If {
|
||||
pub condition: Box<dyn MersStatement>,
|
||||
pub on_true: Box<dyn MersStatement>,
|
||||
pub on_false: Option<Box<dyn MersStatement>>,
|
||||
}
|
||||
|
||||
impl MersStatement for If {
|
||||
fn has_scope(&self) -> bool {
|
||||
true
|
||||
}
|
||||
fn compile_custom(
|
||||
&self,
|
||||
info: &mut crate::info::Info<super::Local>,
|
||||
comp: CompInfo,
|
||||
) -> Result<Box<dyn program::run::MersStatement>, String> {
|
||||
Ok(Box::new(program::run::r#if::If {
|
||||
condition: self.condition.compile(info, comp)?,
|
||||
on_true: self.condition.compile(info, comp)?,
|
||||
on_false: if let Some(v) = &self.on_false {
|
||||
Some(v.compile(info, comp)?)
|
||||
} else {
|
||||
None
|
||||
},
|
||||
}))
|
||||
}
|
||||
}
|
29
mers_lib/src/program/parsed/init_to.rs
Executable file
29
mers_lib/src/program/parsed/init_to.rs
Executable file
@ -0,0 +1,29 @@
|
||||
use crate::{info::Local, program};
|
||||
|
||||
use super::{CompInfo, MersStatement};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct InitTo {
|
||||
pub target: Box<dyn MersStatement>,
|
||||
pub source: Box<dyn MersStatement>,
|
||||
}
|
||||
|
||||
impl MersStatement for InitTo {
|
||||
fn has_scope(&self) -> bool {
|
||||
false
|
||||
}
|
||||
fn compile_custom(
|
||||
&self,
|
||||
info: &mut crate::info::Info<super::Local>,
|
||||
mut comp: CompInfo,
|
||||
) -> Result<Box<dyn crate::program::run::MersStatement>, String> {
|
||||
comp.is_init = true;
|
||||
let target = self.target.compile(info, comp)?;
|
||||
comp.is_init = false;
|
||||
let source = self.source.compile(info, comp)?;
|
||||
Ok(Box::new(program::run::assign_to::AssignTo {
|
||||
target,
|
||||
source,
|
||||
}))
|
||||
}
|
||||
}
|
23
mers_lib/src/program/parsed/loop.rs
Executable file
23
mers_lib/src/program/parsed/loop.rs
Executable file
@ -0,0 +1,23 @@
|
||||
use crate::program;
|
||||
|
||||
use super::{CompInfo, MersStatement};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Loop {
|
||||
pub inner: Box<dyn MersStatement>,
|
||||
}
|
||||
|
||||
impl MersStatement for Loop {
|
||||
fn has_scope(&self) -> bool {
|
||||
true
|
||||
}
|
||||
fn compile_custom(
|
||||
&self,
|
||||
info: &mut crate::info::Info<super::Local>,
|
||||
comp: CompInfo,
|
||||
) -> Result<Box<dyn program::run::MersStatement>, String> {
|
||||
Ok(Box::new(program::run::r#loop::Loop {
|
||||
inner: self.inner.compile(info, comp)?,
|
||||
}))
|
||||
}
|
||||
}
|
68
mers_lib/src/program/parsed/mod.rs
Executable file
68
mers_lib/src/program/parsed/mod.rs
Executable file
@ -0,0 +1,68 @@
|
||||
use std::{collections::HashMap, fmt::Debug};
|
||||
|
||||
use crate::info;
|
||||
|
||||
pub mod assign_to;
|
||||
pub mod block;
|
||||
pub mod chain;
|
||||
pub mod function;
|
||||
pub mod r#if;
|
||||
pub mod init_to;
|
||||
pub mod r#loop;
|
||||
pub mod switch;
|
||||
pub mod tuple;
|
||||
pub mod value;
|
||||
pub mod variable;
|
||||
|
||||
pub trait MersStatement: Debug {
|
||||
fn has_scope(&self) -> bool;
|
||||
fn compile_custom(
|
||||
&self,
|
||||
info: &mut Info,
|
||||
comp: CompInfo,
|
||||
) -> Result<Box<dyn super::run::MersStatement>, String>;
|
||||
fn compile(
|
||||
&self,
|
||||
info: &mut Info,
|
||||
comp: CompInfo,
|
||||
) -> Result<Box<dyn super::run::MersStatement>, String> {
|
||||
if self.has_scope() {
|
||||
info.create_scope();
|
||||
}
|
||||
let o = self.compile_custom(info, comp);
|
||||
if self.has_scope() {
|
||||
info.end_scope();
|
||||
}
|
||||
o
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct CompInfo {
|
||||
is_init: bool,
|
||||
}
|
||||
impl Default for CompInfo {
|
||||
fn default() -> Self {
|
||||
Self { is_init: false }
|
||||
}
|
||||
}
|
||||
|
||||
pub type Info = info::Info<Local>;
|
||||
|
||||
#[derive(Default, Clone, Debug)]
|
||||
pub struct Local {
|
||||
vars: HashMap<String, (usize, usize)>,
|
||||
}
|
||||
impl info::Local for Local {
|
||||
type VariableIdentifier = String;
|
||||
type VariableData = (usize, usize);
|
||||
fn init_var(&mut self, id: Self::VariableIdentifier, value: Self::VariableData) {
|
||||
self.vars.insert(id, value);
|
||||
}
|
||||
fn get_var(&self, id: &Self::VariableIdentifier) -> Option<&Self::VariableData> {
|
||||
self.vars.get(id)
|
||||
}
|
||||
fn get_var_mut(&mut self, id: &Self::VariableIdentifier) -> Option<&mut Self::VariableData> {
|
||||
self.vars.get_mut(id)
|
||||
}
|
||||
}
|
27
mers_lib/src/program/parsed/switch.rs
Executable file
27
mers_lib/src/program/parsed/switch.rs
Executable file
@ -0,0 +1,27 @@
|
||||
use crate::data::Type;
|
||||
|
||||
use super::{CompInfo, MersStatement};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Switch {
|
||||
source: Box<dyn MersStatement>,
|
||||
arms: Vec<SwitchArm>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct SwitchArm {
|
||||
requires_type: Type,
|
||||
}
|
||||
|
||||
impl MersStatement for Switch {
|
||||
fn has_scope(&self) -> bool {
|
||||
true
|
||||
}
|
||||
fn compile_custom(
|
||||
&self,
|
||||
info: &mut crate::info::Info<super::Local>,
|
||||
comp: CompInfo,
|
||||
) -> Result<Box<dyn crate::program::run::MersStatement>, String> {
|
||||
todo!()
|
||||
}
|
||||
}
|
26
mers_lib/src/program/parsed/tuple.rs
Executable file
26
mers_lib/src/program/parsed/tuple.rs
Executable file
@ -0,0 +1,26 @@
|
||||
use crate::{info, program};
|
||||
|
||||
use super::{CompInfo, MersStatement};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Tuple {
|
||||
pub elems: Vec<Box<dyn MersStatement>>,
|
||||
}
|
||||
impl MersStatement for Tuple {
|
||||
fn has_scope(&self) -> bool {
|
||||
false
|
||||
}
|
||||
fn compile_custom(
|
||||
&self,
|
||||
info: &mut info::Info<super::Local>,
|
||||
comp: CompInfo,
|
||||
) -> Result<Box<dyn program::run::MersStatement>, String> {
|
||||
Ok(Box::new(program::run::tuple::Tuple {
|
||||
elems: self
|
||||
.elems
|
||||
.iter()
|
||||
.map(|v| v.compile(info, comp))
|
||||
.collect::<Result<Vec<_>, _>>()?,
|
||||
}))
|
||||
}
|
||||
}
|
21
mers_lib/src/program/parsed/value.rs
Executable file
21
mers_lib/src/program/parsed/value.rs
Executable file
@ -0,0 +1,21 @@
|
||||
use crate::{data::Data, program};
|
||||
|
||||
use super::{CompInfo, MersStatement};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Value(pub Data);
|
||||
|
||||
impl MersStatement for Value {
|
||||
fn has_scope(&self) -> bool {
|
||||
false
|
||||
}
|
||||
fn compile_custom(
|
||||
&self,
|
||||
info: &mut crate::info::Info<super::Local>,
|
||||
comp: CompInfo,
|
||||
) -> Result<Box<dyn program::run::MersStatement>, String> {
|
||||
Ok(Box::new(program::run::value::Value {
|
||||
val: self.0.clone(),
|
||||
}))
|
||||
}
|
||||
}
|
38
mers_lib/src/program/parsed/variable.rs
Executable file
38
mers_lib/src/program/parsed/variable.rs
Executable file
@ -0,0 +1,38 @@
|
||||
use crate::{info::Local, program};
|
||||
|
||||
use super::{CompInfo, MersStatement};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Variable {
|
||||
pub is_ref: bool,
|
||||
pub var: String,
|
||||
}
|
||||
|
||||
impl MersStatement for Variable {
|
||||
fn has_scope(&self) -> bool {
|
||||
false
|
||||
}
|
||||
fn compile_custom(
|
||||
&self,
|
||||
info: &mut crate::info::Info<super::Local>,
|
||||
comp: CompInfo,
|
||||
) -> Result<Box<dyn program::run::MersStatement>, String> {
|
||||
if comp.is_init {
|
||||
info.init_var(
|
||||
self.var.clone(),
|
||||
(
|
||||
info.scopes.len() - 1,
|
||||
info.scopes.last().unwrap().vars.len(),
|
||||
),
|
||||
)
|
||||
}
|
||||
Ok(Box::new(program::run::variable::Variable {
|
||||
is_ref: comp.is_init || self.is_ref,
|
||||
var: if let Some(v) = info.get_var(&self.var) {
|
||||
*v
|
||||
} else {
|
||||
return Err(format!("No variable named '{}' found!", self.var));
|
||||
},
|
||||
}))
|
||||
}
|
||||
}
|
21
mers_lib/src/program/run/assign_to.rs
Executable file
21
mers_lib/src/program/run/assign_to.rs
Executable file
@ -0,0 +1,21 @@
|
||||
use crate::data;
|
||||
|
||||
use super::MersStatement;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct AssignTo {
|
||||
pub target: Box<dyn MersStatement>,
|
||||
pub source: Box<dyn MersStatement>,
|
||||
}
|
||||
|
||||
impl MersStatement for AssignTo {
|
||||
fn run_custom(&self, info: &mut super::Info) -> crate::data::Data {
|
||||
let source = self.source.run(info);
|
||||
let target = self.target.run(info);
|
||||
data::defs::assign(source, &target);
|
||||
target
|
||||
}
|
||||
fn has_scope(&self) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
20
mers_lib/src/program/run/block.rs
Executable file
20
mers_lib/src/program/run/block.rs
Executable file
@ -0,0 +1,20 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use super::MersStatement;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Block {
|
||||
pub statements: Vec<Box<dyn MersStatement>>,
|
||||
}
|
||||
impl MersStatement for Block {
|
||||
fn run_custom(&self, info: &mut super::Info) -> crate::data::Data {
|
||||
self.statements
|
||||
.iter()
|
||||
.map(|s| s.run(info))
|
||||
.last()
|
||||
.unwrap_or_else(|| crate::data::Data::new(crate::data::tuple::Tuple(vec![])))
|
||||
}
|
||||
fn has_scope(&self) -> bool {
|
||||
true
|
||||
}
|
||||
}
|
26
mers_lib/src/program/run/chain.rs
Executable file
26
mers_lib/src/program/run/chain.rs
Executable file
@ -0,0 +1,26 @@
|
||||
use std::{any::Any, sync::Arc};
|
||||
|
||||
use crate::data::{function::Function, Data};
|
||||
|
||||
use super::MersStatement;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Chain {
|
||||
pub first: Box<dyn MersStatement>,
|
||||
pub chained: Box<dyn MersStatement>,
|
||||
}
|
||||
impl MersStatement for Chain {
|
||||
fn run_custom(&self, info: &mut super::Info) -> Data {
|
||||
let f = self.first.run(info);
|
||||
let c = self.chained.run(info);
|
||||
let c = c.get();
|
||||
if let Some(func) = c.as_any().downcast_ref::<crate::data::function::Function>() {
|
||||
func.run(f)
|
||||
} else {
|
||||
todo!("err: not a function");
|
||||
}
|
||||
}
|
||||
fn has_scope(&self) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
17
mers_lib/src/program/run/function.rs
Executable file
17
mers_lib/src/program/run/function.rs
Executable file
@ -0,0 +1,17 @@
|
||||
use crate::data::{self, Data, MersData};
|
||||
|
||||
use super::MersStatement;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Function {
|
||||
pub func_no_info: data::function::Function,
|
||||
}
|
||||
|
||||
impl MersStatement for Function {
|
||||
fn has_scope(&self) -> bool {
|
||||
false
|
||||
}
|
||||
fn run_custom(&self, info: &mut super::Info) -> Data {
|
||||
Data::new(self.func_no_info.with_info(info.clone()))
|
||||
}
|
||||
}
|
18
mers_lib/src/program/run/if.rs
Executable file
18
mers_lib/src/program/run/if.rs
Executable file
@ -0,0 +1,18 @@
|
||||
use super::MersStatement;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct If {
|
||||
pub condition: Box<dyn MersStatement>,
|
||||
pub on_true: Box<dyn MersStatement>,
|
||||
pub on_false: Option<Box<dyn MersStatement>>,
|
||||
}
|
||||
|
||||
impl MersStatement for If {
|
||||
fn run_custom(&self, info: &mut super::Info) -> crate::data::Data {
|
||||
self.condition.run(info);
|
||||
todo!("what now?")
|
||||
}
|
||||
fn has_scope(&self) -> bool {
|
||||
true
|
||||
}
|
||||
}
|
21
mers_lib/src/program/run/loop.rs
Executable file
21
mers_lib/src/program/run/loop.rs
Executable file
@ -0,0 +1,21 @@
|
||||
use crate::data::MersData;
|
||||
|
||||
use super::MersStatement;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Loop {
|
||||
pub inner: Box<dyn MersStatement>,
|
||||
}
|
||||
|
||||
impl MersStatement for Loop {
|
||||
fn run_custom(&self, info: &mut super::Info) -> crate::data::Data {
|
||||
loop {
|
||||
if let Some(break_val) = self.inner.run(info).get().matches() {
|
||||
break break_val;
|
||||
}
|
||||
}
|
||||
}
|
||||
fn has_scope(&self) -> bool {
|
||||
true
|
||||
}
|
||||
}
|
63
mers_lib/src/program/run/mod.rs
Executable file
63
mers_lib/src/program/run/mod.rs
Executable file
@ -0,0 +1,63 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::{
|
||||
data::{self, Data, Type},
|
||||
info,
|
||||
};
|
||||
|
||||
pub mod assign_to;
|
||||
pub mod block;
|
||||
pub mod chain;
|
||||
pub mod function;
|
||||
pub mod r#if;
|
||||
pub mod r#loop;
|
||||
pub mod switch;
|
||||
pub mod tuple;
|
||||
pub mod value;
|
||||
pub mod variable;
|
||||
|
||||
pub trait MersStatement: std::fmt::Debug {
|
||||
fn run_custom(&self, info: &mut Info) -> Data;
|
||||
/// if true, local variables etc. will be contained inside their own scope.
|
||||
fn has_scope(&self) -> bool;
|
||||
// fn outputs(&self) -> Type;
|
||||
fn run(&self, info: &mut Info) -> Data {
|
||||
if self.has_scope() {
|
||||
info.create_scope();
|
||||
}
|
||||
let o = self.run_custom(info);
|
||||
if self.has_scope() {
|
||||
info.end_scope();
|
||||
}
|
||||
o
|
||||
}
|
||||
}
|
||||
|
||||
pub type Info = info::Info<Local>;
|
||||
|
||||
#[derive(Default, Clone, Debug)]
|
||||
pub struct Local {
|
||||
vars: Vec<Data>,
|
||||
}
|
||||
impl info::Local for Local {
|
||||
type VariableIdentifier = usize;
|
||||
type VariableData = Data;
|
||||
fn init_var(&mut self, id: Self::VariableIdentifier, value: Self::VariableData) {
|
||||
while self.vars.len() <= id {
|
||||
self.vars.push(Data::new(data::bool::Bool(false)));
|
||||
}
|
||||
self.vars[id] = value;
|
||||
}
|
||||
fn get_var(&self, id: &Self::VariableIdentifier) -> Option<&Self::VariableData> {
|
||||
match self.vars.get(*id) {
|
||||
Some(v) => Some(v),
|
||||
None => None,
|
||||
}
|
||||
}
|
||||
fn get_var_mut(&mut self, id: &Self::VariableIdentifier) -> Option<&mut Self::VariableData> {
|
||||
match self.vars.get_mut(*id) {
|
||||
Some(v) => Some(v),
|
||||
None => None,
|
||||
}
|
||||
}
|
||||
}
|
23
mers_lib/src/program/run/switch.rs
Executable file
23
mers_lib/src/program/run/switch.rs
Executable file
@ -0,0 +1,23 @@
|
||||
use crate::data::Type;
|
||||
|
||||
use super::MersStatement;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Switch {
|
||||
source: Box<dyn MersStatement>,
|
||||
arms: Vec<SwitchArm>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct SwitchArm {
|
||||
requires_type: Type,
|
||||
}
|
||||
|
||||
impl MersStatement for Switch {
|
||||
fn has_scope(&self) -> bool {
|
||||
true
|
||||
}
|
||||
fn run_custom(&self, info: &mut super::Info) -> crate::data::Data {
|
||||
todo!("switch")
|
||||
}
|
||||
}
|
18
mers_lib/src/program/run/tuple.rs
Executable file
18
mers_lib/src/program/run/tuple.rs
Executable file
@ -0,0 +1,18 @@
|
||||
use crate::data::{self, Data};
|
||||
|
||||
use super::MersStatement;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Tuple {
|
||||
pub elems: Vec<Box<dyn MersStatement>>,
|
||||
}
|
||||
impl MersStatement for Tuple {
|
||||
fn run_custom(&self, info: &mut super::Info) -> crate::data::Data {
|
||||
Data::new(data::tuple::Tuple(
|
||||
self.elems.iter().map(|s| s.run(info)).collect(),
|
||||
))
|
||||
}
|
||||
fn has_scope(&self) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
17
mers_lib/src/program/run/value.rs
Executable file
17
mers_lib/src/program/run/value.rs
Executable file
@ -0,0 +1,17 @@
|
||||
use crate::data::{Data, MersData};
|
||||
|
||||
use super::MersStatement;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Value {
|
||||
pub val: Data,
|
||||
}
|
||||
|
||||
impl MersStatement for Value {
|
||||
fn has_scope(&self) -> bool {
|
||||
false
|
||||
}
|
||||
fn run_custom(&self, info: &mut super::Info) -> Data {
|
||||
self.val.clone()
|
||||
}
|
||||
}
|
30
mers_lib/src/program/run/variable.rs
Executable file
30
mers_lib/src/program/run/variable.rs
Executable file
@ -0,0 +1,30 @@
|
||||
use crate::data::{self, Data};
|
||||
|
||||
use super::MersStatement;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Variable {
|
||||
pub is_ref: bool,
|
||||
pub var: (usize, usize),
|
||||
}
|
||||
|
||||
impl MersStatement for Variable {
|
||||
fn has_scope(&self) -> bool {
|
||||
false
|
||||
}
|
||||
fn run_custom(&self, info: &mut super::Info) -> Data {
|
||||
while info.scopes[self.var.0].vars.len() <= self.var.1 {
|
||||
info.scopes[self.var.0]
|
||||
.vars
|
||||
.push(Data::new(data::bool::Bool(false)));
|
||||
}
|
||||
if self.is_ref {
|
||||
Data::new(data::reference::Reference(
|
||||
info.scopes[self.var.0].vars[self.var.1].clone(),
|
||||
))
|
||||
} else {
|
||||
// Full-Clones!
|
||||
Data::new_boxed(info.scopes[self.var.0].vars[self.var.1].get().clone())
|
||||
}
|
||||
}
|
||||
}
|
26
mers_old/Cargo.toml
Executable file
26
mers_old/Cargo.toml
Executable file
@ -0,0 +1,26 @@
|
||||
[package]
|
||||
name = "mers"
|
||||
version = "0.2.3"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[lib]
|
||||
name = "mers_libs"
|
||||
path = "src/lib.rs"
|
||||
|
||||
[dependencies]
|
||||
edit = "0.1.4"
|
||||
notify = "5.1.0"
|
||||
regex = "1.7.2"
|
||||
static_assertions = "1.1.0"
|
||||
nu-plugin = { version = "0.79.0", optional = true }
|
||||
nu-protocol = { version = "0.79.0", features = ["plugin"], optional = true }
|
||||
colorize = "0.1.0"
|
||||
|
||||
[features]
|
||||
# default = ["nushell_plugin"]
|
||||
nushell_plugin = ["dep:nu-plugin", "dep:nu-protocol"]
|
||||
|
||||
[profile.nushellplugin]
|
||||
inherits = "release"
|
@ -1463,12 +1463,12 @@ impl BuiltinFunction {
|
||||
}
|
||||
}),
|
||||
Self::Insert => args[0].run(info).operate_on_data_mut(|v| {
|
||||
args[1].run(info).operate_on_data_immut(|i| {
|
||||
args[2].run(info).operate_on_data_immut(|i| {
|
||||
// TODO: find out why the fuck this helps
|
||||
if let (VDataEnum::Reference(v), VDataEnum::Int(i)) = (v, i) {
|
||||
v.operate_on_data_mut(|v| {
|
||||
if let VDataEnum::List(_, v) = v {
|
||||
v.insert(*i as _, args[2].run(info));
|
||||
v.insert(*i as _, args[1].run(info));
|
||||
}
|
||||
});
|
||||
VDataEnum::Tuple(vec![]).to()
|
245
mers_old/src/main.rs
Executable file
245
mers_old/src/main.rs
Executable file
@ -0,0 +1,245 @@
|
||||
use lang::global_info::ColorFormatMode;
|
||||
use lang::global_info::GlobalScriptInfo;
|
||||
use lang::global_info::LogKind;
|
||||
use lang::val_data::VDataEnum;
|
||||
use lang::val_type::VSingleType;
|
||||
|
||||
use crate::lang::fmtgs::FormatGs;
|
||||
|
||||
mod interactive_mode;
|
||||
mod lang;
|
||||
mod libs;
|
||||
#[cfg(feature = "nushell_plugin")]
|
||||
mod nushell_plugin;
|
||||
mod parsing;
|
||||
mod pathutil;
|
||||
mod tutor;
|
||||
|
||||
fn main() {
|
||||
#[cfg(not(feature = "nushell_plugin"))]
|
||||
normal_main();
|
||||
#[cfg(feature = "nushell_plugin")]
|
||||
nushell_plugin::main();
|
||||
}
|
||||
|
||||
fn normal_main() {
|
||||
let args: Vec<_> = std::env::args().skip(1).collect();
|
||||
let mut info = GlobalScriptInfo::default();
|
||||
let mut run = true;
|
||||
let mut args_to_skip = 2;
|
||||
let mut file = match args.len() {
|
||||
0 => {
|
||||
println!("no arguments, use -h for help");
|
||||
std::process::exit(100);
|
||||
}
|
||||
_ => {
|
||||
if args[0].trim_start().starts_with("-") {
|
||||
let mut execute = false;
|
||||
let mut print_version = false;
|
||||
let mut verbose = false;
|
||||
let mut verbose_args = String::new();
|
||||
let mut interactive = 0;
|
||||
let mut interactive_use_new_terminal = false;
|
||||
let mut teachme = false;
|
||||
let mut prev_char = None;
|
||||
let mut advanced = false;
|
||||
for ch in args[0][1..].chars() {
|
||||
if !advanced {
|
||||
if ch == '+' {
|
||||
advanced = true;
|
||||
continue;
|
||||
}
|
||||
match ch {
|
||||
'h' => {
|
||||
eprintln!("~~~~ mers help ~~~~");
|
||||
eprintln!();
|
||||
eprintln!(" ~~ cli ~~");
|
||||
eprintln!("Mers has the following cli options:");
|
||||
eprintln!("-h shows this Help message");
|
||||
eprintln!("-e - mers will treat the run argument as code to be Executed rather than a file path");
|
||||
eprintln!(" mers -e 'println(\"Hello, World!\")'");
|
||||
eprintln!(
|
||||
"-c - mers will Check the code for errors, but won't run it"
|
||||
);
|
||||
eprintln!("-f - mers will Format the code and print it. useful if you suspect the parser might be misinterpreting your code");
|
||||
eprintln!(
|
||||
"+c - use Colors in the output to better visualize things"
|
||||
);
|
||||
eprintln!("+C - don't use colors (opposite of +c, redundant since this is the default)");
|
||||
eprintln!("-v - mers will be more Verbose");
|
||||
eprintln!("+???+ - customize what mers is verbose about and how - bad syntax, barely useful, don't use it until it gets improved (TODO!)");
|
||||
eprintln!("-i - launches an Interactive session to play around with (opens your editor and runs code on each file save)");
|
||||
eprintln!("+t - spawns a new terminal for the editor (if you use a terminal editors, add +t)");
|
||||
eprintln!(" mers -i+t");
|
||||
eprintln!("-t - launches the Tutor, which will attempt to Teach you the basics of the language");
|
||||
eprintln!();
|
||||
eprintln!(" ~~ getting started ~~");
|
||||
eprintln!("mers doesn't need a specific structure for directories, just create a UTF-8 text file, write code, and run it:");
|
||||
eprintln!(" echo 'println(\"Hello, World!\")' > hello.mers");
|
||||
eprintln!(" mers hello.mers");
|
||||
return;
|
||||
}
|
||||
'e' => execute = true,
|
||||
'v' => verbose = true,
|
||||
'c' => run = false,
|
||||
'f' => {
|
||||
run = false;
|
||||
info.log.after_parse.stderr = true;
|
||||
}
|
||||
'V' => print_version = true,
|
||||
'i' => interactive += 1,
|
||||
't' => teachme = true,
|
||||
ch => {
|
||||
eprintln!("Ignoring -{ch}. (unknown char)");
|
||||
continue;
|
||||
}
|
||||
}
|
||||
prev_char = Some(ch);
|
||||
} else {
|
||||
advanced = false;
|
||||
if let Some(prev_char) = prev_char {
|
||||
match prev_char {
|
||||
'i' => match ch {
|
||||
't' => interactive_use_new_terminal = true,
|
||||
_ => eprintln!("Ignoring i+{ch}. (unknown adv char)"),
|
||||
},
|
||||
'v' => {
|
||||
if ch != '+' {
|
||||
advanced = true;
|
||||
verbose_args.push(ch);
|
||||
}
|
||||
}
|
||||
'f' => match ch {
|
||||
'c' => info.formatter.mode = ColorFormatMode::Colorize,
|
||||
'C' => info.formatter.mode = ColorFormatMode::Plain,
|
||||
_ => eprintln!("Ignoring f+{ch}. (unknown adv char)"),
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
} else {
|
||||
eprintln!(
|
||||
"Ignoring advanced args because there was no previous argument."
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
if print_version {
|
||||
println!(
|
||||
"mers {}",
|
||||
option_env!("CARGO_PKG_VERSION")
|
||||
.unwrap_or("[[ version unknown: no CARGO_PKG_VERSION ]]")
|
||||
);
|
||||
return;
|
||||
}
|
||||
if teachme {
|
||||
tutor::start(false);
|
||||
return;
|
||||
}
|
||||
if verbose {
|
||||
if verbose_args.is_empty() {
|
||||
fn f() -> LogKind {
|
||||
LogKind {
|
||||
stderr: true,
|
||||
log: true,
|
||||
}
|
||||
}
|
||||
info.log.vtype_fits_in = f();
|
||||
info.log.vsingletype_fits_in = f();
|
||||
} else {
|
||||
fn kind(val: Option<&str>) -> LogKind {
|
||||
match val {
|
||||
Some("stderr") => LogKind {
|
||||
stderr: true,
|
||||
..Default::default()
|
||||
},
|
||||
Some("log") => LogKind {
|
||||
log: true,
|
||||
..Default::default()
|
||||
},
|
||||
Some("log+stderr" | "stderr+log") => LogKind {
|
||||
stderr: true,
|
||||
log: true,
|
||||
..Default::default()
|
||||
},
|
||||
_ => LogKind {
|
||||
stderr: true,
|
||||
..Default::default()
|
||||
},
|
||||
}
|
||||
}
|
||||
for verbose_arg in verbose_args.split(',') {
|
||||
let (arg, val) = match verbose_arg.split_once('=') {
|
||||
Some((left, right)) => (left, Some(right)),
|
||||
None => (verbose_arg, None),
|
||||
};
|
||||
match arg {
|
||||
"vtype_fits_in" => info.log.vtype_fits_in = kind(val),
|
||||
"vsingletype_fits_in" => info.log.vsingletype_fits_in = kind(val),
|
||||
_ => eprintln!("Warn: -v+ unknown arg '{arg}'."),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if interactive > 0 {
|
||||
match interactive {
|
||||
_ => {
|
||||
// basic: open file and watch for fs changes
|
||||
interactive_mode::fs_watcher::playground(interactive_use_new_terminal)
|
||||
.unwrap()
|
||||
}
|
||||
};
|
||||
return;
|
||||
} else if execute {
|
||||
parsing::file::File::new(
|
||||
args.iter().skip(1).fold(String::new(), |mut s, v| {
|
||||
if !s.is_empty() {
|
||||
s.push(' ');
|
||||
}
|
||||
s.push_str(v);
|
||||
s
|
||||
}),
|
||||
std::path::PathBuf::new(),
|
||||
)
|
||||
} else {
|
||||
args_to_skip += 1;
|
||||
if let Some(file) = args.get(1) {
|
||||
parsing::file::File::new(
|
||||
std::fs::read_to_string(file).unwrap(),
|
||||
file.into(),
|
||||
)
|
||||
} else {
|
||||
println!("nothing to do - missing arguments?");
|
||||
std::process::exit(101);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
parsing::file::File::new(
|
||||
std::fs::read_to_string(&args[0]).unwrap(),
|
||||
args[0].as_str().into(),
|
||||
)
|
||||
}
|
||||
}
|
||||
};
|
||||
info.main_fn_args = vec![(
|
||||
"args".to_string(),
|
||||
VSingleType::List(VSingleType::String.into()).to(),
|
||||
)];
|
||||
match parsing::parse::parse_custom_info(&mut file, info) {
|
||||
Ok(script) => {
|
||||
if run {
|
||||
script.run(vec![VDataEnum::List(
|
||||
VSingleType::String.to(),
|
||||
std::env::args()
|
||||
.skip(args_to_skip)
|
||||
.map(|v| VDataEnum::String(v).to())
|
||||
.collect(),
|
||||
)
|
||||
.to()]);
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
println!("Couldn't compile:\n{}", e.with_file(&file));
|
||||
std::process::exit(99);
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user