mirror of
https://github.com/danbulant/rush
synced 2026-05-19 04:18:35 +00:00
progress on input
This commit is contained in:
commit
d04d10cb9a
7 changed files with 342 additions and 0 deletions
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
/target
|
||||
74
Cargo.lock
generated
Normal file
74
Cargo.lock
generated
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "arrayvec"
|
||||
version = "0.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "1.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.107"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fbe5e23404da5b4f555ef85ebed98fb4083e55a00c317800bc2a50ede9f3d219"
|
||||
|
||||
[[package]]
|
||||
name = "numtoa"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b8f8bdf33df195859076e54ab11ee78a1b208382d3a26ec40d142ffc1ecc49ef"
|
||||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.2.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8383f39639269cde97d255a32bdb68c047337295414940c68bdd30c2e13203ff"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "redox_termios"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8440d8acb4fd3d277125b4bd01a6f38aee8d814b3b5fc09b3f2b825d37d3fe8f"
|
||||
dependencies = [
|
||||
"redox_syscall",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rush"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"termion",
|
||||
"utf8-chars",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "termion"
|
||||
version = "1.5.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "077185e2eac69c3f8379a4298e1e07cd36beb962290d4a51199acf0fdc10607e"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"numtoa",
|
||||
"redox_syscall",
|
||||
"redox_termios",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "utf8-chars"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c1348d8face79d019be7cbc0198e36bf93e160ddbfaa7bb54c9592627b9ec841"
|
||||
dependencies = [
|
||||
"arrayvec",
|
||||
]
|
||||
11
Cargo.toml
Normal file
11
Cargo.toml
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
[package]
|
||||
name = "rush"
|
||||
version = "0.1.0"
|
||||
authors = ["Daniel Bulant <danbulant@gmail.com>"]
|
||||
edition = "2018"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
utf8-chars = "1.0.0"
|
||||
termion = "1.5.6"
|
||||
127
README.md
Normal file
127
README.md
Normal file
|
|
@ -0,0 +1,127 @@
|
|||
# Rush
|
||||
|
||||
Rust shell. Inspired by Ion.
|
||||
|
||||
In case you're reading this: rush is in the works and not a priority. Features may be missing even if defined below.
|
||||
|
||||
## Syntax
|
||||
|
||||
`;` is 'alias' for new line.
|
||||
|
||||
Syntax and type errors crash the program.
|
||||
|
||||
Scopes are for each block. Functions won't have access to variables it wouldn't have access if it wasn't a function:
|
||||
|
||||
```sh
|
||||
fn testing
|
||||
echo $t
|
||||
end
|
||||
|
||||
if true
|
||||
testing # Error! t is not defined
|
||||
end
|
||||
```
|
||||
|
||||
### Variables
|
||||
|
||||
String variables using `$`, arrays using `@`.
|
||||
|
||||
Assigned using `let`.
|
||||
Left side is evaluated to a string as well.
|
||||
Variable names must be valid ascii characters, or part of namespace (`namespace::var` syntax).
|
||||
|
||||
```sh
|
||||
let a = d
|
||||
let $b = c
|
||||
echo $d # c
|
||||
```
|
||||
|
||||
Arrays are assigned using `[ var ]`. You can join arrays and strings by simply passing them there, like `[ $var @var ]`.
|
||||
|
||||
All assignments are done via the `let` keyword.
|
||||
Instead of `=`, other operations are supported:
|
||||
|
||||
* `*=` - multiply
|
||||
* `+=` - addition
|
||||
* `-=` - substraction
|
||||
* `/=` - division
|
||||
* `//=` - int division
|
||||
* `%=` - modulo
|
||||
* `::=` - append
|
||||
* `@@=` - prepend
|
||||
|
||||
#### Special variables
|
||||
|
||||
`env::` namespace contains the environment (and doesn't error out if the variable doesn't exist, instead, empty string is returned)
|
||||
`color::` (alias `c::`) has a number of colors
|
||||
|
||||
#### Types
|
||||
|
||||
Based on the value set in the set `let` (the one with just `=`), a type is infered (unless specificaly set using `let x:type = ...`). This type is then used for the operations after.
|
||||
|
||||
Supported types:
|
||||
|
||||
* `i32` (alias int)
|
||||
* `i64`
|
||||
* `i128`
|
||||
* `u32`
|
||||
* `u64`
|
||||
* `u128`
|
||||
* `f32`
|
||||
* `f64`
|
||||
* `str`
|
||||
* `hmap[T]` (where T is one of the other types, except array)
|
||||
* `[T]` (where T is one of the other types, except hmap)
|
||||
|
||||
HashMap is basically array, but with string keys (instead of numbers) in random order.
|
||||
|
||||
### Return
|
||||
|
||||
Sets the exit code (and possibly exits function/script early). If no return is set, the return code is set to the return code of the last expression.
|
||||
|
||||
### Math
|
||||
|
||||
Using `$(())` syntax. Math priority applies. Brackets are supported. Variables used without their prepender. Indexing is possible.
|
||||
|
||||
### String and Array methods
|
||||
|
||||
* $trim(str) - Trims the string (both sides)
|
||||
* $trimLeft(str) - Trims the string (start/left)
|
||||
* $trimright(str) - Trims the string (end/right)
|
||||
* $escape(str) - Escapes string
|
||||
* $unescape(str) - Unescapes string
|
||||
* @split(str by) - Splits `str` by `by` into array
|
||||
* @join(str by=" ") - Joins `str` together `by`
|
||||
|
||||
Slices: `[x..y]` gets a substring (or subarray) of the variable. When `x` ommited, defaults to `0` (the start). When `y` ommited, defaults to end.
|
||||
|
||||
### Control
|
||||
|
||||
Bracketless. Scopes are ended by the keyword `end`.
|
||||
|
||||
`if` - Runs it's scope if the command returns `0`. Useful in pair with `test` builtin.
|
||||
`for $val of @arr` - Runs for each value of the array (or hashmap)
|
||||
`for $val of ...` - Runs for each number in the range.
|
||||
`while` - Runs in loop as long as the command returns `0`
|
||||
|
||||
### Functions
|
||||
|
||||
Defined by `fn name arg -- desc`. `arg` can be ommited, or repeated. `desc` will be printed when the `arg` is missing (or when `describe` command is used).
|
||||
|
||||
#### Special
|
||||
|
||||
From config (defined by `~/.rushrc`), special functions can be defined.
|
||||
|
||||
* `PROMPT` will be run to render the prompt
|
||||
* `HIGHLIGHT` will run (for each key - make it fast) to highlight the text.
|
||||
|
||||
#### Builtins
|
||||
|
||||
* `let` for assigning variables
|
||||
* `export` for exporting variables to env
|
||||
* `test` tests for evaluation (`=` for equality, `>`, `<`, `<=`, `>=` for number comparisons)
|
||||
* `exists` for existance of a given string, or if given a flag (`-F`unctions, `-v`ariables, `-e`nv, `-f`ile, `-d`irectory, `-r`eadable file, `-w`ritable file, e`-x`ecutable file), existence of the selected object
|
||||
* `true` returns `0`
|
||||
* `false` returns `1`
|
||||
|
||||
Some GNU standard utils may be overwritten by rush builtins, but must be made compatible.
|
||||
73
src/main.rs
Normal file
73
src/main.rs
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
mod parser;
|
||||
|
||||
use std::io::{self, Read, Write};
|
||||
use std::cmp;
|
||||
use std::convert::TryInto;
|
||||
|
||||
use termion::raw::IntoRawMode;
|
||||
use termion::input::TermRead;
|
||||
use termion::cursor::{self, DetectCursorPos};
|
||||
use termion::event::*;
|
||||
use termion::input::{MouseTerminal};
|
||||
|
||||
fn main() {
|
||||
let stdin = io::stdin();
|
||||
let mut stdout = io::stdout().into_raw_mode().unwrap();
|
||||
let ctx = parser::vars::Context {
|
||||
scopes: Vec::new(),
|
||||
parent_context: None
|
||||
};
|
||||
let mut output = String::new();
|
||||
let mut idx = 0;
|
||||
for c in stdin.keys() {
|
||||
let c = c.unwrap();
|
||||
match c {
|
||||
Key::Char('\n') => {
|
||||
break;
|
||||
}
|
||||
Key::Backspace => {
|
||||
if output.len() > 0 && idx > 0 {
|
||||
output.remove(idx - 1);
|
||||
idx -= 1;
|
||||
print!("{}{} ", cursor::Left((output.len() - idx + 2).try_into().unwrap()), &output[idx..]);
|
||||
}
|
||||
}
|
||||
Key::Delete => {
|
||||
if idx < output.len() {
|
||||
output.remove(idx);
|
||||
print!("{} {}", &output[idx..], cursor::Left((output.len() - idx + 1).try_into().unwrap()));
|
||||
}
|
||||
}
|
||||
Key::End => {
|
||||
let start = idx;
|
||||
idx = cmp::max(output.len(), 1) - 1;
|
||||
print!("{}", cursor::Right((idx - start).try_into().unwrap()));
|
||||
}
|
||||
Key::Home => {
|
||||
let start = idx;
|
||||
idx = 0;
|
||||
print!("{}", cursor::Left(start.try_into().unwrap()));
|
||||
}
|
||||
Key::Left => {
|
||||
if idx > 0 {
|
||||
idx -= 1;
|
||||
}
|
||||
print!("{}", cursor::Left(1));
|
||||
}
|
||||
Key::Right => {
|
||||
if idx < output.len() - 1 {
|
||||
idx += 1;
|
||||
}
|
||||
print!("{}", cursor::Right(1));
|
||||
}
|
||||
Key::Char(char) => {
|
||||
output.insert(idx, char);
|
||||
idx += 1;
|
||||
print!("{}", char);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
stdout.flush().unwrap();
|
||||
}
|
||||
parser::read_parse(&mut output.as_bytes(), ctx).unwrap();
|
||||
}
|
||||
12
src/parser/mod.rs
Normal file
12
src/parser/mod.rs
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
pub mod vars;
|
||||
|
||||
use std::io;
|
||||
//use std::io::prelude::*;
|
||||
use utf8_chars::BufReadCharsExt;
|
||||
|
||||
pub fn read_parse(reader: &mut dyn std::io::BufRead, ctx: vars::Context) -> io::Result<()> {
|
||||
for c in reader.chars().map(|x| x.unwrap()) {
|
||||
println!("char {}\r", c);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
44
src/parser/vars.rs
Normal file
44
src/parser/vars.rs
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
use std::collections::HashMap;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Variable {
|
||||
String(String),
|
||||
I32(i32),
|
||||
I64(i64),
|
||||
I128(i128),
|
||||
U32(u32),
|
||||
U64(u64),
|
||||
U128(u128),
|
||||
F32(f32),
|
||||
F64(f64),
|
||||
hmap(HashMap<String, Variable>),
|
||||
array(Vec<Variable>)
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Scope {
|
||||
active: bool,
|
||||
vars: HashMap<String, Variable>
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Context {
|
||||
pub scopes: Vec<Scope>,
|
||||
pub parent_context: Option<Box<Context>>
|
||||
}
|
||||
|
||||
impl Context {
|
||||
pub fn new() -> Context {
|
||||
Context {
|
||||
scopes: Vec::new(),
|
||||
parent_context: None
|
||||
}
|
||||
}
|
||||
pub fn add_scope(self: &mut Context, active: bool) {
|
||||
let scope = Scope {
|
||||
active: active,
|
||||
vars: HashMap::new()
|
||||
};
|
||||
self.scopes.push(scope);
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue