commit d04d10cb9a20d880e98cac9fed28419e054f96d9 Author: Daniel Bulant Date: Wed Nov 10 21:08:29 2021 +0100 progress on input diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/target diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..8332f5d --- /dev/null +++ b/Cargo.lock @@ -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", +] diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..576cd1f --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "rush" +version = "0.1.0" +authors = ["Daniel Bulant "] +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" \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..61af4d3 --- /dev/null +++ b/README.md @@ -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. diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..ab92897 --- /dev/null +++ b/src/main.rs @@ -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(); +} diff --git a/src/parser/mod.rs b/src/parser/mod.rs new file mode 100644 index 0000000..4c27926 --- /dev/null +++ b/src/parser/mod.rs @@ -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(()) +} diff --git a/src/parser/vars.rs b/src/parser/vars.rs new file mode 100644 index 0000000..b9bac1c --- /dev/null +++ b/src/parser/vars.rs @@ -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), + array(Vec) +} + +#[derive(Debug)] +pub struct Scope { + active: bool, + vars: HashMap +} + +#[derive(Debug)] +pub struct Context { + pub scopes: Vec, + pub parent_context: Option> +} + +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); + } +} \ No newline at end of file