Compare commits
2 commits
Author | SHA1 | Date | |
---|---|---|---|
Erin | 0c6878b48a | ||
Erin | 850696a7ab |
151
Cargo.lock
generated
151
Cargo.lock
generated
|
@ -13,24 +13,43 @@ dependencies = [
|
|||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "allocator-api2"
|
||||
version = "0.2.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5"
|
||||
|
||||
[[package]]
|
||||
name = "beef"
|
||||
version = "0.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3a8241f3ebb85c056b509d4327ad0358fbbba6ffb340bf388f26350aeda225b1"
|
||||
|
||||
[[package]]
|
||||
name = "bumpalo"
|
||||
version = "3.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1"
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.0.83"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "chumsky"
|
||||
version = "1.0.0-alpha.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cc3172a80699de358070dd99f80ea8badc6cdf8ac2417cb5a96e6d81bf5fe06d"
|
||||
dependencies = [
|
||||
"hashbrown",
|
||||
"stacker",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fnv"
|
||||
version = "1.0.7"
|
||||
|
@ -38,31 +57,28 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.2.10"
|
||||
name = "hashbrown"
|
||||
version = "0.13.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427"
|
||||
checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"wasi",
|
||||
"ahash",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.14.0"
|
||||
name = "lasso"
|
||||
version = "0.7.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a"
|
||||
checksum = "4644821e1c3d7a560fe13d842d13f587c07348a1a05d3a797152d41c90c56df2"
|
||||
dependencies = [
|
||||
"ahash",
|
||||
"allocator-api2",
|
||||
"hashbrown",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.147"
|
||||
version = "0.2.148"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3"
|
||||
checksum = "9cdc71e17332e86d2e1d38c1f99edcb6288ee11b815fb1a4b049eaa2114d369b"
|
||||
|
||||
[[package]]
|
||||
name = "logos"
|
||||
|
@ -102,21 +118,24 @@ version = "1.18.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d"
|
||||
|
||||
[[package]]
|
||||
name = "ppv-lite86"
|
||||
version = "0.2.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.66"
|
||||
version = "1.0.67"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9"
|
||||
checksum = "3d433d9f1a3e8c1263d9456598b16fec66f4acc9a74dacffd35c7bb09b3a1328"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "psm"
|
||||
version = "0.1.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5787f7cda34e3033a72192c018bc5883100330f362ef279a8cbccfce8bb4e874"
|
||||
dependencies = [
|
||||
"cc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.33"
|
||||
|
@ -126,36 +145,6 @@ dependencies = [
|
|||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand"
|
||||
version = "0.8.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"rand_chacha",
|
||||
"rand_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_chacha"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
|
||||
dependencies = [
|
||||
"ppv-lite86",
|
||||
"rand_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_core"
|
||||
version = "0.6.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
|
||||
dependencies = [
|
||||
"getrandom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.6.29"
|
||||
|
@ -166,16 +155,30 @@ checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1"
|
|||
name = "rhea"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"hashbrown",
|
||||
"bumpalo",
|
||||
"chumsky",
|
||||
"lasso",
|
||||
"logos",
|
||||
"rand",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "stacker"
|
||||
version = "0.1.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c886bd4480155fd3ef527d45e9ac8dd7118a898a46530b7b94c3e21866259fce"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"psm",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.29"
|
||||
version = "2.0.33"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c324c494eba9d92503e6f1ef2e6df781e78f6a7705a0202d9801b198807d518a"
|
||||
checksum = "9caece70c63bfba29ec2fed841a09851b14a235c60010fa4de58089b6c025668"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
@ -184,9 +187,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.11"
|
||||
version = "1.0.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c"
|
||||
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
|
||||
|
||||
[[package]]
|
||||
name = "version_check"
|
||||
|
@ -195,7 +198,23 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.11.0+wasi-snapshot-preview1"
|
||||
name = "winapi"
|
||||
version = "0.3.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
|
||||
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
|
||||
dependencies = [
|
||||
"winapi-i686-pc-windows-gnu",
|
||||
"winapi-x86_64-pc-windows-gnu",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi-i686-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
||||
|
||||
[[package]]
|
||||
name = "winapi-x86_64-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||
|
|
11
Cargo.toml
11
Cargo.toml
|
@ -1,11 +1,10 @@
|
|||
[package]
|
||||
name = "rhea"
|
||||
name = "rhea"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
logos = "*"
|
||||
hashbrown = "*"
|
||||
rand = "*"
|
||||
bumpalo = { version = "3", features = ["collections"] }
|
||||
chumsky = "1.0.0-alpha"
|
||||
lasso = "0.7"
|
||||
logos = "0.13"
|
||||
|
|
10
assets/examples/array.rhea
Normal file
10
assets/examples/array.rhea
Normal file
|
@ -0,0 +1,10 @@
|
|||
var std = include "std";
|
||||
var print = std.print;
|
||||
|
||||
func main(){
|
||||
// Arrays do exist only tuples packed into a single var
|
||||
// zero indexed
|
||||
var arr = (123, 456, 789);
|
||||
// Print 456
|
||||
print(arr.1);
|
||||
}
|
|
@ -1 +1,2 @@
|
|||
// there must only be one main and it ***MUST*** exist
|
||||
func main(){}
|
|
@ -1,10 +1,18 @@
|
|||
func main() {
|
||||
var abc = 0;
|
||||
// an asm block may use variables outside of the asm scope
|
||||
asm {
|
||||
jmp r0, start
|
||||
start:
|
||||
jmp r0, start
|
||||
li r1, abc
|
||||
}
|
||||
// This is not really a requirement
|
||||
opcode {
|
||||
01
|
||||
}
|
||||
|
||||
// Exit
|
||||
var exit_status = 0;
|
||||
asm {
|
||||
li r255, exit_status
|
||||
tx
|
||||
}
|
||||
}
|
|
@ -1,3 +1,6 @@
|
|||
|
||||
// The only thing included here is the ability to pull in other functions
|
||||
// This is not a * include
|
||||
var std = include "std";
|
||||
|
||||
func main(){}
|
17
assets/examples/loops.rhea
Normal file
17
assets/examples/loops.rhea
Normal file
|
@ -0,0 +1,17 @@
|
|||
func main(){
|
||||
var i = 0;
|
||||
// The only loop is `loop` and it does not do any form of count up and break
|
||||
loop {
|
||||
match i {
|
||||
// break will break out of `one` loop
|
||||
10 -> break;
|
||||
}
|
||||
// Increment by one
|
||||
i = i + 1;
|
||||
}
|
||||
// This is functionally identical to a HCF
|
||||
loop {}
|
||||
}
|
||||
|
||||
|
||||
|
10
assets/examples/match.rhea
Normal file
10
assets/examples/match.rhea
Normal file
|
@ -0,0 +1,10 @@
|
|||
var std = import "std";
|
||||
var print = std.print;
|
||||
|
||||
func main() {
|
||||
match 2 {
|
||||
1 -> { print("One") }
|
||||
2 -> { print("Two") }
|
||||
// If there is not a matching case continue
|
||||
}
|
||||
}
|
6
assets/examples/rand.rhea
Normal file
6
assets/examples/rand.rhea
Normal file
|
@ -0,0 +1,6 @@
|
|||
var rand = include "rand";
|
||||
var rd64 = rand.random_u64;
|
||||
|
||||
func main(){
|
||||
var abc = rd64();
|
||||
}
|
10
assets/examples/string.rhea
Normal file
10
assets/examples/string.rhea
Normal file
|
@ -0,0 +1,10 @@
|
|||
// This string ***MUST*** never be edited
|
||||
const b_string = "XYZ";
|
||||
|
||||
func main() {
|
||||
var a_string = "ABC";
|
||||
// strings may be added together
|
||||
var fmt = a_string + " length " + a_string.len;
|
||||
// print may only take one argument
|
||||
print(fmt);
|
||||
}
|
|
@ -3,4 +3,7 @@
|
|||
const C = 299_792_458;
|
||||
|
||||
const pi = 3.141592653589793;
|
||||
const tau = 6.283185307179586;
|
||||
const π = pi;
|
||||
|
||||
const tau = 6.283185307179586;
|
||||
const 𝜏 = tau;
|
|
@ -1 +1,24 @@
|
|||
var constants = include "constants";
|
||||
var constants = include "constants";
|
||||
|
||||
func square_root(value){
|
||||
match value{
|
||||
0..1 -> return value;
|
||||
value -> {
|
||||
var y = x;
|
||||
var z = (y + (x/y)) / 2;
|
||||
// NOTE: not finalized syntax
|
||||
while abs(y - z) >= 0.00001{
|
||||
y = z
|
||||
z = (y + (x/y)) / 2
|
||||
}
|
||||
return z;
|
||||
}
|
||||
}
|
||||
}
|
||||
var sqrt = square_root;
|
||||
var √ = square_root;
|
||||
|
||||
func absolute_value(value){
|
||||
|
||||
}
|
||||
var abs = absolute_value;
|
5
assets/libraries/rand/rand.rhea
Normal file
5
assets/libraries/rand/rand.rhea
Normal file
|
@ -0,0 +1,5 @@
|
|||
func random_u64(){
|
||||
// TODO: randomness
|
||||
var value = 0;
|
||||
return value;
|
||||
}
|
20
assets/libraries/std/ecalls.rhea
Normal file
20
assets/libraries/std/ecalls.rhea
Normal file
|
@ -0,0 +1,20 @@
|
|||
|
||||
var (error,warn,info,debug,trace) = io.log.(error,warn,info,debug,trace);
|
||||
|
||||
|
||||
|
||||
// TODO: define and add in IDL shenanigans for a proper IPC Protocol
|
||||
func make_ipc_buffer(bounded: bool, length: u64) {
|
||||
match bounded{
|
||||
true -> match length {
|
||||
0 -> error("Bound array has length of zero")
|
||||
}
|
||||
}
|
||||
|
||||
asm {
|
||||
li r254, bounded
|
||||
li r253, length
|
||||
}
|
||||
// Return a pointer to a memory address with `length`
|
||||
return (123, 456);
|
||||
}
|
|
@ -1 +1,8 @@
|
|||
var log = include "log";
|
||||
var log = include "log";
|
||||
|
||||
|
||||
// print accepts all types in value
|
||||
func print(value) {
|
||||
// TODO: define an api for output
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,25 @@
|
|||
func error(){}
|
||||
func warn(){}
|
||||
func info(){}
|
||||
func debug(){}
|
||||
func trace(){}
|
||||
var io = include "io";
|
||||
var print = io.print;
|
||||
|
||||
// TODO: include time in the log
|
||||
|
||||
|
||||
func error(value){
|
||||
print("error " + value)
|
||||
}
|
||||
|
||||
func warn(value){
|
||||
print("warn " + value)
|
||||
}
|
||||
|
||||
func info(value){
|
||||
print("info " + value)
|
||||
}
|
||||
|
||||
func debug(value){
|
||||
print("debug " + value)
|
||||
}
|
||||
|
||||
func trace(value){
|
||||
print("trace " + value)
|
||||
}
|
|
@ -1,2 +1,6 @@
|
|||
var io = include "std.io";
|
||||
var math = include "math"
|
||||
var io = include "io";
|
||||
|
||||
var math = include "math";
|
||||
var ecalls = include "ecalls";
|
||||
|
||||
var print = io.print;
|
20
assets/libraries/std/terminal.rhea
Normal file
20
assets/libraries/std/terminal.rhea
Normal file
|
@ -0,0 +1,20 @@
|
|||
var ecalls = include "ecalls";
|
||||
var make_ipc_buffer = ecalls.make_ipc_buffer;
|
||||
|
||||
var terminal;
|
||||
terminal.init = terminal_init;
|
||||
terminal.write = terminal_write;
|
||||
|
||||
func terminal_init(){
|
||||
// setup a buffer with the TextIO protocol
|
||||
var buffer = make_ipc_buffer(false, 0);
|
||||
terminal.buffer = buffer;
|
||||
}
|
||||
|
||||
func terminal_write(value: String){
|
||||
// TODO: write value into buffer according to TextIO protocol
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
64
src/main.rs
64
src/main.rs
|
@ -1,61 +1,21 @@
|
|||
// Rhea
|
||||
|
||||
use bumpalo::Bump;
|
||||
use logos::Logos;
|
||||
use memory::{Variable, VariableHashmap, VariableType};
|
||||
use std::io::{stdin, Read};
|
||||
use utils::default;
|
||||
|
||||
mod memory;
|
||||
mod syntax;
|
||||
mod utils;
|
||||
|
||||
#[derive(Logos, Debug, PartialEq)]
|
||||
#[logos(skip r"[ \t\n\f]+")] // Ignore this regex pattern between tokens
|
||||
enum Token {
|
||||
#[token(".")]
|
||||
Period,
|
||||
|
||||
#[token("(")]
|
||||
LeftParen,
|
||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let mut buf = default();
|
||||
stdin().read_to_string(&mut buf)?;
|
||||
|
||||
#[token("{")]
|
||||
LeftBrace,
|
||||
let lexer = syntax::token::Token::lexer_with_extras(&buf, default());
|
||||
let arena = Bump::new();
|
||||
syntax::parser::parse_lexer(lexer, &arena);
|
||||
|
||||
#[token(")")]
|
||||
RightParen,
|
||||
|
||||
#[token("}")]
|
||||
RightBrace,
|
||||
|
||||
#[token("include")]
|
||||
Include,
|
||||
|
||||
#[token("=")]
|
||||
Equals,
|
||||
|
||||
#[token(";")]
|
||||
Semicolon,
|
||||
|
||||
#[token("\"")]
|
||||
Quote,
|
||||
|
||||
// Or regular expressions.
|
||||
#[regex("[a-zA-Z]+")]
|
||||
Text,
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let mut memmap = Vec::new();
|
||||
let mut varmap: VariableHashmap = hashbrown::HashMap::new();
|
||||
|
||||
let variable_name = "abcd".to_string();
|
||||
let variable_type = Some(memory::VariableType::Signed);
|
||||
let constant = false;
|
||||
let length = 8;
|
||||
let abc = Variable::new(&mut memmap, variable_type, constant, length);
|
||||
|
||||
varmap.insert(variable_name, abc);
|
||||
|
||||
let mut lex = Token::lexer(include_str!("../assets/examples/library.rhea"));
|
||||
for x in lex {
|
||||
println!("{:?}", x);
|
||||
}
|
||||
|
||||
//todo compile to hb
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -1,53 +0,0 @@
|
|||
use std::ops::Range;
|
||||
|
||||
// I do not like std
|
||||
use hashbrown::HashMap;
|
||||
|
||||
pub type MemoryMap = Vec<(u64, u64)>;
|
||||
|
||||
pub type VariableHashmap = HashMap<String, Variable>;
|
||||
|
||||
pub enum VariableType {
|
||||
Unsigned,
|
||||
Signed,
|
||||
String,
|
||||
}
|
||||
|
||||
pub struct Variable {
|
||||
vtype: VariableType,
|
||||
constant: bool,
|
||||
memory_addr: u64,
|
||||
length: u64,
|
||||
}
|
||||
impl Variable {
|
||||
pub fn new(
|
||||
memmap: &mut MemoryMap,
|
||||
variable_type: Option<VariableType>,
|
||||
constant: bool,
|
||||
length: u64,
|
||||
) -> Self {
|
||||
let addr = get_random_addr_and_validate(memmap, length);
|
||||
//todo type guessing
|
||||
let vtype = VariableType::String;
|
||||
Self {
|
||||
vtype,
|
||||
constant,
|
||||
memory_addr: addr,
|
||||
length,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
use rand::prelude::*;
|
||||
|
||||
pub fn get_random_addr_and_validate(memmap: &mut MemoryMap, length: u64) -> u64 {
|
||||
let raddr: u64 = rand::random();
|
||||
// TODO: validate fr later
|
||||
// for (x, _) in memmap.iter() {
|
||||
// if *x == raddr {
|
||||
// }
|
||||
// }
|
||||
|
||||
memmap.push((raddr, raddr + length));
|
||||
raddr
|
||||
}
|
83
src/syntax/ast.rs
Normal file
83
src/syntax/ast.rs
Normal file
|
@ -0,0 +1,83 @@
|
|||
use {super::token::IntLit, chumsky::span::SimpleSpan, lasso::Spur};
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub struct Spanned<T> {
|
||||
pub item: T,
|
||||
pub span: SimpleSpan,
|
||||
}
|
||||
|
||||
impl<T> Spanned<T> {
|
||||
#[inline]
|
||||
pub fn new(item: T, span: SimpleSpan) -> Self {
|
||||
Self { item, span }
|
||||
}
|
||||
}
|
||||
|
||||
pub type SpanExpr<'a> = Spanned<Expr<'a>>;
|
||||
pub type ExprRef<'a> = &'a SpanExpr<'a>;
|
||||
pub type ExprList<'a> = &'a [SpanExpr<'a>];
|
||||
pub type Ident = Spur;
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub enum Expr<'a> {
|
||||
Ident(Ident),
|
||||
Path(ExprList<'a>),
|
||||
Literal(Literal),
|
||||
Call(ExprRef<'a>, ExprList<'a>),
|
||||
Binary(Spanned<BinaryOperator>, ExprRef<'a>, ExprRef<'a>),
|
||||
Unary(Spanned<UnaryOperator>, ExprRef<'a>),
|
||||
BindLocal(Spanned<Pattern>, ExprRef<'a>, Option<ExprList<'a>>),
|
||||
BindIn(
|
||||
Spanned<Pattern>,
|
||||
ExprRef<'a>,
|
||||
ExprList<'a>,
|
||||
Option<ExprList<'a>>,
|
||||
),
|
||||
Set(ExprRef<'a>, ExprRef<'a>),
|
||||
Match(ExprRef<'a>, &'a [(Spanned<Pattern>, SpanExpr<'a>)]),
|
||||
Func(&'a [(Spanned<Pattern>, Spanned<Type>)], Spanned<Type>, ExprRef<'a>),
|
||||
Block(ExprList<'a>),
|
||||
Unit,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub enum Type {
|
||||
Ident(Ident),
|
||||
Unit,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub enum Pattern {
|
||||
Ident(Ident),
|
||||
Literal(Literal),
|
||||
None,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub enum Literal {
|
||||
String(Spur),
|
||||
Integer(IntLit),
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub enum BinaryOperator {
|
||||
Plus,
|
||||
Minus,
|
||||
Star,
|
||||
Slash,
|
||||
And,
|
||||
VLine,
|
||||
Lt,
|
||||
Gt,
|
||||
Equ,
|
||||
Nequ,
|
||||
LtEqu,
|
||||
GtEqu,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub enum UnaryOperator {
|
||||
Tilde,
|
||||
Minus,
|
||||
Star,
|
||||
}
|
3
src/syntax/mod.rs
Normal file
3
src/syntax/mod.rs
Normal file
|
@ -0,0 +1,3 @@
|
|||
pub mod ast;
|
||||
pub mod parser;
|
||||
pub mod token;
|
284
src/syntax/parser.rs
Normal file
284
src/syntax/parser.rs
Normal file
|
@ -0,0 +1,284 @@
|
|||
use super::ast::Type;
|
||||
|
||||
use {
|
||||
super::{
|
||||
ast::{BinaryOperator, Expr, Literal, Pattern, SpanExpr, Spanned, UnaryOperator},
|
||||
token::Token,
|
||||
},
|
||||
crate::utils::Pipe,
|
||||
bumpalo::Bump,
|
||||
chumsky::{
|
||||
extra::Full,
|
||||
input::{Stream, ValueInput},
|
||||
prelude::*,
|
||||
},
|
||||
logos::Lexer,
|
||||
};
|
||||
|
||||
/// Equivalently-named unit variant mapping
|
||||
macro_rules! equivmap {
|
||||
($src:ident, $target:ident, [$variant0:ident $(, $variant:ident)* $(,)?] $(,)?) => {
|
||||
just($src::$variant0).to($target::$variant0)
|
||||
$(.or(just($src::$variant).to($target::$variant)))*
|
||||
};
|
||||
}
|
||||
|
||||
fn expr<'a, I>() -> impl Parser<'a, I, SpanExpr<'a>, Extra<'a>> + Clone
|
||||
where
|
||||
I: Input<'a, Token = Token, Span = SimpleSpan> + ValueInput<'a>,
|
||||
{
|
||||
recursive(|expr| {
|
||||
let ident = select!(Token::Ident(id) => id);
|
||||
|
||||
let literal = select! {
|
||||
Token::Int(a) => Literal::Integer(a),
|
||||
Token::String(a) => Literal::String(a)
|
||||
};
|
||||
|
||||
let pattern = select! {
|
||||
Token::Ident(id) => Pattern::Ident(id),
|
||||
Token::Underscore => Pattern::None,
|
||||
}
|
||||
.or(literal.map(Pattern::Literal))
|
||||
.map_with_span(Spanned::new);
|
||||
|
||||
let type_ = just([Token::LeftParen, Token::RightParen])
|
||||
.to(Type::Unit)
|
||||
.or(ident.map(Type::Ident))
|
||||
.map_with_span(Spanned::new);
|
||||
|
||||
let block = expr
|
||||
.clone()
|
||||
.separated_by(just(Token::Semicolon))
|
||||
.allow_trailing()
|
||||
.pipe(arena_collect)
|
||||
.delimited_by(just(Token::LeftCurly), just(Token::RightCurly));
|
||||
|
||||
let func = just(Token::Func)
|
||||
.ignore_then(
|
||||
pattern
|
||||
.then_ignore(just(Token::Colon))
|
||||
.then(type_)
|
||||
.separated_by(just(Token::Comma))
|
||||
.allow_trailing()
|
||||
.pipe(arena_collect)
|
||||
.delimited_by(just(Token::LeftParen), just(Token::RightParen)),
|
||||
)
|
||||
.then_ignore(just(Token::Colon))
|
||||
.then(type_)
|
||||
.then(
|
||||
just(Token::Equ)
|
||||
.ignore_then(expr.clone())
|
||||
.or(block.clone().map(Expr::Block).map_with_span(Spanned::new)),
|
||||
)
|
||||
.map_with_state(|((params, ret), expr), _, state| {
|
||||
Expr::Func(params, ret, state.arena.alloc(expr))
|
||||
});
|
||||
|
||||
let atom = literal
|
||||
.map(Expr::Literal)
|
||||
.or(just([Token::LeftParen, Token::RightParen]).to(Expr::Unit))
|
||||
.or(ident.map(Expr::Ident))
|
||||
.or(func)
|
||||
.map_with_span(Spanned::new)
|
||||
.or(expr
|
||||
.clone()
|
||||
.delimited_by(just(Token::LeftParen), just(Token::RightParen)));
|
||||
|
||||
// <expr>(expr1, expr2, …)
|
||||
let call = atom.clone().foldl_with_state(
|
||||
expr.clone()
|
||||
.separated_by(just(Token::Comma))
|
||||
.allow_trailing()
|
||||
.pipe(arena_collect)
|
||||
.delimited_by(just(Token::LeftParen), just(Token::RightParen))
|
||||
.map_with_span(Spanned::new)
|
||||
.repeated(),
|
||||
|expr, paramlist, state: &mut State| {
|
||||
Spanned::new(
|
||||
Expr::Call(state.arena.alloc(expr), paramlist.item),
|
||||
merge_spans(expr.span, paramlist.span),
|
||||
)
|
||||
},
|
||||
);
|
||||
|
||||
let path = call
|
||||
.clone()
|
||||
.map_with_state(|item, _, state| bumpalo::vec![in state.arena; item])
|
||||
.foldl(
|
||||
just(Token::Dot).ignore_then(call).repeated(),
|
||||
|mut v, expr| {
|
||||
v.push(expr);
|
||||
v
|
||||
},
|
||||
)
|
||||
.map(|v| Expr::Path(v.into_bump_slice()))
|
||||
.map_with_span(Spanned::new);
|
||||
|
||||
/* let unary = equivmap!(Token, UnaryOperator, [Minus, Tilde])
|
||||
.map_with_span(Spanned::new)
|
||||
.repeated()
|
||||
.foldr_with_state(call, |op, expr, state| {
|
||||
Spanned::new(
|
||||
Expr::Unary(op, state.arena.alloc(expr)),
|
||||
merge_spans(op.span, expr.span),
|
||||
)
|
||||
});
|
||||
*/
|
||||
|
||||
let unary = path.foldl_with_state(
|
||||
just([Token::Dot, Token::Star])
|
||||
.to(UnaryOperator::Star)
|
||||
.or(just(Token::Tilde).to(UnaryOperator::Tilde))
|
||||
.map_with_span(Spanned::new)
|
||||
.repeated(),
|
||||
|expr, op, state| {
|
||||
Spanned::new(
|
||||
Expr::Unary(op, state.arena.alloc(expr)),
|
||||
merge_spans(expr.span, op.span),
|
||||
)
|
||||
},
|
||||
);
|
||||
|
||||
// <exprL> OP <exprR>
|
||||
let binary = unary.clone().foldl_with_state(
|
||||
equivmap!(
|
||||
Token,
|
||||
BinaryOperator,
|
||||
[Plus, Minus, Star, Slash, And, VLine, Lt, Gt, Equ, Nequ, LtEqu, GtEqu],
|
||||
)
|
||||
.map_with_span(Spanned::new)
|
||||
.then(unary)
|
||||
.repeated(),
|
||||
|l, (op, r), state: &mut State| {
|
||||
Spanned::new(
|
||||
Expr::Binary(op, state.arena.alloc(l), state.arena.alloc(r)),
|
||||
merge_spans(l.span, r.span),
|
||||
)
|
||||
},
|
||||
);
|
||||
|
||||
let bind = {
|
||||
let start = pattern.then_ignore(just(Token::Colon)).then(expr.clone()); // <pat> := <expr>
|
||||
let else_ = just(Token::Else).ignore_then(block.clone()).or_not(); // else {…}
|
||||
|
||||
// <pat> := <expr> [else {…}]
|
||||
let local = start.clone().then(else_.clone()).map_with_state(
|
||||
|((pat, expr), else_), _, state| {
|
||||
Expr::BindLocal(pat, &*state.arena.alloc(expr), else_)
|
||||
},
|
||||
);
|
||||
|
||||
// <pat> := <expr> {…} else {…}
|
||||
let in_ = start.then(block.clone()).then(else_).map_with_state(
|
||||
|(((pat, expr), block), else_), _, state| {
|
||||
Expr::BindIn(pat, &*state.arena.alloc(expr), block, else_)
|
||||
},
|
||||
);
|
||||
|
||||
in_.or(local)
|
||||
};
|
||||
|
||||
// <atom> ← <expr>
|
||||
let set = atom
|
||||
.clone()
|
||||
.then_ignore(just(Token::LArrow))
|
||||
.then(expr.clone())
|
||||
.map_with_state(|(place, expr), _, state| {
|
||||
Expr::Set(state.arena.alloc(place), state.arena.alloc(expr))
|
||||
});
|
||||
|
||||
// <expr>.match { <pat> → <expr>, … }
|
||||
let match_ = atom
|
||||
.clone()
|
||||
.then_ignore(just([Token::Dot, Token::Match]))
|
||||
.then(
|
||||
pattern
|
||||
.then_ignore(just(Token::RArrow))
|
||||
.then(expr)
|
||||
.separated_by(just(Token::Comma))
|
||||
.allow_trailing()
|
||||
.pipe(arena_collect)
|
||||
.delimited_by(just(Token::LeftCurly), just(Token::RightCurly)),
|
||||
)
|
||||
.map_with_state(|(expr, branches), _, state| {
|
||||
Expr::Match(state.arena.alloc(expr), branches)
|
||||
});
|
||||
|
||||
bind.or(set)
|
||||
.or(match_)
|
||||
.or(block.map(Expr::Block))
|
||||
.map_with_span(Spanned::new)
|
||||
.or(binary)
|
||||
.or(atom)
|
||||
})
|
||||
}
|
||||
|
||||
pub struct State<'a> {
|
||||
pub arena: &'a Bump,
|
||||
}
|
||||
|
||||
type Extra<'a> = Full<Rich<'a, Token>, State<'a>, ()>;
|
||||
type ParseResult = ();
|
||||
|
||||
pub fn parse_input<'a>(
|
||||
input: impl ValueInput<'a, Token = Token, Span = SimpleSpan>,
|
||||
arena: &'a Bump,
|
||||
) -> ParseResult {
|
||||
println!(
|
||||
"{:?}",
|
||||
expr()
|
||||
.separated_by(just(Token::Semicolon))
|
||||
.allow_trailing()
|
||||
.pipe(arena_collect)
|
||||
.parse_with_state(input, &mut State { arena })
|
||||
);
|
||||
}
|
||||
|
||||
pub fn parse_iter(
|
||||
input: impl Iterator<Item = (Token, SimpleSpan)>,
|
||||
eoi: impl Into<SimpleSpan>,
|
||||
arena: &Bump,
|
||||
) -> ParseResult {
|
||||
parse_input(Stream::from_iter(input).spanned(eoi.into()), arena)
|
||||
}
|
||||
|
||||
pub fn parse_lexer(input: Lexer<Token>, arena: &Bump) -> ParseResult {
|
||||
let end = input.span().end;
|
||||
parse_iter(
|
||||
input
|
||||
.spanned()
|
||||
.map(|(token, span)| (token.unwrap_or(Token::Invalid), span.into())),
|
||||
end..end + 1,
|
||||
arena,
|
||||
)
|
||||
}
|
||||
|
||||
fn arena_collect<'a, I, O: 'a>(
|
||||
parser: impl IterParser<'a, I, O, Extra<'a>> + Clone,
|
||||
) -> impl Parser<'a, I, &'a [O], Extra<'a>> + Clone
|
||||
where
|
||||
I: Input<'a, Span = SimpleSpan, Token = Token>,
|
||||
{
|
||||
empty()
|
||||
.map_with_state(|_, _, state: &mut State| bumpalo::vec![in state.arena])
|
||||
.foldl(parser, |mut v, o| {
|
||||
v.push(o);
|
||||
v
|
||||
})
|
||||
.map(bumpalo::collections::Vec::into_bump_slice)
|
||||
}
|
||||
|
||||
fn arena_box<'a, I, O: 'a>(
|
||||
parser: impl Parser<'a, I, O, Extra<'a>> + Clone,
|
||||
) -> impl Parser<'a, I, &'a O, Extra<'a>> + Clone
|
||||
where
|
||||
I: Input<'a, Span = SimpleSpan, Token = Token>,
|
||||
{
|
||||
parser.map_with_state(|item, _, state| &*state.arena.alloc(item))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn merge_spans(start: SimpleSpan, end: SimpleSpan) -> SimpleSpan {
|
||||
SimpleSpan::new(start.start, end.end)
|
||||
}
|
112
src/syntax/token.rs
Normal file
112
src/syntax/token.rs
Normal file
|
@ -0,0 +1,112 @@
|
|||
use lasso::Spur;
|
||||
use logos::Lexer;
|
||||
|
||||
use {lasso::Rodeo, logos::Logos};
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct Lextras {
|
||||
pub interner: Rodeo,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||
pub enum IntLit {
|
||||
Signed(i64),
|
||||
Unsigned(u64),
|
||||
}
|
||||
|
||||
#[derive(Logos, Copy, Clone, Debug, PartialEq, Eq)]
|
||||
#[logos(extras = Lextras)]
|
||||
#[logos(skip r"[ \t\n\f]+")]
|
||||
#[logos(skip r"-- .*")]
|
||||
#[rustfmt::skip]
|
||||
pub enum Token {
|
||||
#[token("(")] LeftParen,
|
||||
#[token(")")] RightParen,
|
||||
#[token("{")] LeftCurly,
|
||||
#[token("}")] RightCurly,
|
||||
#[token(".")] Dot,
|
||||
#[token(",")] Comma,
|
||||
#[token(":")] Colon,
|
||||
#[token(";")] Semicolon,
|
||||
#[token("_")] Underscore,
|
||||
|
||||
#[token("←")] //____
|
||||
#[token("<-")] LArrow,
|
||||
#[token("→")] //____
|
||||
#[token("->")] RArrow,
|
||||
|
||||
#[token(":>")] Pipe,
|
||||
|
||||
#[token("+")] Plus,
|
||||
#[token("-")] Minus,
|
||||
#[token("*")] Star,
|
||||
#[token("/")] Slash,
|
||||
#[token("&")] And,
|
||||
#[token("|")] VLine,
|
||||
#[token("~")] Tilde,
|
||||
|
||||
#[token("<")] Lt,
|
||||
#[token(">")] Gt,
|
||||
#[token("=")] Equ,
|
||||
#[token("≠") ] //__
|
||||
#[token("/=")] Nequ,
|
||||
#[token("≤") ] //___
|
||||
#[token("<=")] LtEqu,
|
||||
#[token("≥") ] //___,
|
||||
#[token(">=")] GtEqu,
|
||||
|
||||
#[token("match")] Match,
|
||||
#[token("else")] Else,
|
||||
#[token("loop")] Loop,
|
||||
#[token("const")] Const,
|
||||
#[token("var")] Var,
|
||||
#[token("func")] Func,
|
||||
// Modules aren't real here ondra just variables with imported functions
|
||||
#[token("module")] Module,
|
||||
|
||||
#[regex(
|
||||
r"\p{XID_Start}\p{XID_Continue}*",
|
||||
|l| l.extras.interner.get_or_intern(l.slice())
|
||||
)] Ident(Spur),
|
||||
|
||||
#[token("»", better_string)]
|
||||
#[regex(
|
||||
"\"[^\"]*\"",
|
||||
|l| {
|
||||
let slice = l.slice();
|
||||
l.extras.interner.get_or_intern(&slice[1..slice.len() - 1])
|
||||
}
|
||||
)] String(Spur),
|
||||
|
||||
#[regex(
|
||||
"-?[0-9]+",
|
||||
|l| {
|
||||
Some(if let Some(slice) = l.slice().strip_prefix('-') {
|
||||
IntLit::Signed(slice.parse::<i64>().ok()?)
|
||||
} else {
|
||||
IntLit::Unsigned(l.slice().parse::<u64>().ok()?)
|
||||
})
|
||||
}
|
||||
)] Int(IntLit),
|
||||
|
||||
Invalid,
|
||||
}
|
||||
|
||||
// For Evy, with love.
|
||||
fn better_string(lexer: &mut Lexer<Token>) -> Option<Spur> {
|
||||
let mut count = 1;
|
||||
for (ix, chr) in lexer.remainder().char_indices() {
|
||||
match chr {
|
||||
'«' => count -= 1,
|
||||
'»' => count += 1,
|
||||
_ => (),
|
||||
}
|
||||
|
||||
if count == 0 {
|
||||
let slice = &lexer.remainder()[..ix];
|
||||
lexer.bump(ix + '«'.len_utf8());
|
||||
return Some(lexer.extras.interner.get_or_intern(slice));
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
15
src/utils.rs
Normal file
15
src/utils.rs
Normal file
|
@ -0,0 +1,15 @@
|
|||
#[inline(always)]
|
||||
pub fn default<T: Default>() -> T {
|
||||
Default::default()
|
||||
}
|
||||
|
||||
pub trait Pipe {
|
||||
fn pipe<R>(self, mut f: impl FnMut(Self) -> R) -> R
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
f(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Pipe for T {}
|
Loading…
Reference in a new issue