From 1d860d1811bf172a0f036f5d4e2d4e00ccaddfe3 Mon Sep 17 00:00:00 2001 From: Michael Sippel Date: Mon, 30 Oct 2023 15:30:42 +0100 Subject: [PATCH] wip cmd-type-dsl --- src/ast.rs | 115 ----------- src/cmd_type_dsl/mod.rs | 66 +++--- src/env.rs | 9 - src/expand.rs | 44 ---- src/main.rs | 8 +- src/parse.rs | 438 ---------------------------------------- typeDB/cut | 14 +- typeDB/seq | 2 +- 8 files changed, 50 insertions(+), 646 deletions(-) delete mode 100644 src/ast.rs delete mode 100644 src/env.rs delete mode 100644 src/expand.rs delete mode 100644 src/parse.rs diff --git a/src/ast.rs b/src/ast.rs deleted file mode 100644 index 87e7aae..0000000 --- a/src/ast.rs +++ /dev/null @@ -1,115 +0,0 @@ -use std::boxed::Box; - -//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>\\ - -#[derive(Debug, PartialEq)] -pub enum Command { - Simple { - assignments: Vec, - command_word: Word, - redirections: Vec - }, - Pipeline(Vec), - Sequence(Vec), - ShortCircuitConjunction(Vec), - ShortCircuitDisjunction(Vec), - Negation(Box), - While { - condition: Box, - loop_body: Box - }, - For { - varname: String, - sequence: Word, - loop_body: Box - }, - If { - condition: Box, - then_branch: Box, - else_branch: Box - }, - Case { - expr: Word, - cases: Vec<(Word, Command)> - }, - Function { - name: String, - body: Box - } -} - -//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>\\ - -#[derive(Debug, PartialEq)] -pub struct Assignment { - pub name: String, - pub value: Word -} - -#[derive(Debug, PartialEq)] -pub struct Word { - pub segments: Vec -} - -#[derive(Debug, PartialEq)] -pub enum WordSegment { - Tilde(String), - Literal(String), - Parameter(String, ParameterFormat), - Subshell(Command), - DoubleQuote(Word), -} - -#[derive(Debug, PartialEq)] -pub enum ParameterFormat { - Normal, - Length, - Default(Word), - Assign(Word), - Error(Word), - Alt(Word), - Sub(ParamSubSide, ParamSubMode, Word), -} - -#[derive(Debug, PartialEq)] -pub enum ParamSubMode { - Shortest, Longest -} - -#[derive(Debug, PartialEq)] -pub enum ParamSubSide { - Prefix, Suffix -} - -//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>\\ - -#[derive(Debug, PartialEq)] -pub struct Redirection { - redirection_type: RedirectionType, - fd: u64, - target: Word -} - -#[derive(Debug, PartialEq)] -pub enum RedirectionType { - File(FileRedirectionType), - Dup(DupRedirectionType), - Heredoc // '<<' -} - -#[derive(Debug, PartialEq)] -pub enum FileRedirectionType { - In, // '<' - InOut, // '<>' - Out, // '>' - OutReplace, // '>|' - OutAppend, // '>>' -} - -#[derive(Debug, PartialEq)] -pub enum DupRedirectionType { - In, // '<&' - Out // '>&' -} - -//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>\\ diff --git a/src/cmd_type_dsl/mod.rs b/src/cmd_type_dsl/mod.rs index 4d7ec13..1b55ab2 100644 --- a/src/cmd_type_dsl/mod.rs +++ b/src/cmd_type_dsl/mod.rs @@ -3,17 +3,12 @@ use std::{ boxed::Box }; -use crate::ast::Command; +use crate::sh::ast::Command; use laddertypes::*; - pub struct Substitution(HashMap< String, CommandTypeExpr >); -impl Substitution { - pub fn apply(&self, expr: &mut CommandTypeExpr) { - - } -} +#[derive(Clone)] pub enum CommandArgPattern { Literal(String), Variable(String), @@ -23,39 +18,58 @@ pub enum CommandArgPattern { Disjunction(Vec) } +#[derive(Clone)] pub struct CommandPattern { name: String, args: Vec, env: Vec<(String, CommandTypeExpr)>, } +#[derive(Clone)] +pub struct MatchCandidate { + at: usize, + expected: CommandPattern, + found: CommandTypeExpr, +} + +#[derive(Clone)] +pub struct UnificationError( Vec ); + +#[derive(Clone)] +pub enum CommandTypeExpr { + Type(TypeTerm), + Match(Box, Vec<(CommandArgPattern, CommandTypeExpr)>) +} + impl CommandArgPattern { pub fn match_cmd(&self, cmd: &Command) -> Result { Err(UnificationError(vec![])) } } -pub struct MatchCandidate { - at: usize, - expected: CommandPattern, - found: CommandTypeExpr, -} - -pub struct UnificationError( Vec ); - - - -pub enum CommandTypeExpr { - Parameter(String), - ParameterPack(String), - Char(char), - Match(Box, Vec<(CommandArgPattern, CommandTypeExpr)>) -} - impl CommandTypeExpr { - pub fn eval(self) -> CommandTypeExpr { + pub fn eval(self) -> Result { match self { - s=>s + CommandTypeExpr::Type(typ) => Ok(typ), + CommandTypeExpr::Match(pattern, cases) => { + + } + s=> Ok(s) + } + } + + pub fn apply_subst(&mut self, subst: &Substitution) { + match self { + CommandTypeExpr::Type(typ) => { + self = CommandTypeExpr::Type( + typ.apply_substitution(|v: String| subst.get(v)) + ); + } + CommandTypeExpr::Match( pattern, cases ) => { + + // todo + } + _ => {} } } } diff --git a/src/env.rs b/src/env.rs deleted file mode 100644 index 091e68d..0000000 --- a/src/env.rs +++ /dev/null @@ -1,9 +0,0 @@ -use std::collections::HashMap; - -//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>\\ - -struct Environment { - variables: HashMap -} - -//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>\\ diff --git a/src/expand.rs b/src/expand.rs deleted file mode 100644 index 412fc5f..0000000 --- a/src/expand.rs +++ /dev/null @@ -1,44 +0,0 @@ -use crate::ast::*; - -//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>\\ - -impl Word { - pub fn (&mut self, env: &Environment) { - for x in self.0.iter_mut() { - x.expand_tilde(); - match x { - Word::Tilde => Word::Literal( env.get_home() ), - other => other, - } - } - } - - pub fn expand(&self) -> Vec { - let mut fields = Vec::new(); - - for seg in self.segments.iter() { - // - } - - fields - } - - pub fn split_field(&mut self) { - - } -} - -impl WordSegment { - pub fn split_field(&self) -> Word { - - match self { - - } - } - - pub fn expand(&self) -> Word { - match - } -} - -//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>\\ diff --git a/src/main.rs b/src/main.rs index f81dca8..02e6b77 100644 --- a/src/main.rs +++ b/src/main.rs @@ -6,11 +6,7 @@ use { tiny_ansi::TinyAnsi }; -mod ast; -mod env; -mod parse; -//mod expand; - +mod sh; mod cmd_type_dsl; //<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>\\ @@ -42,7 +38,7 @@ fn main() { let stdin = std::io::stdin(); for line in std::io::BufReader::new(stdin).lines() { if let Ok(line) = line { - let cmd = parse::parse_cmd( &mut line.chars().peekable() ); + let cmd = sh::parse::parse_cmd( &mut line.chars().peekable() ); eprintln!("parsed cmd: {:?}", cmd); /* let mut lex = parse::WordLexer::from( line.chars() ); diff --git a/src/parse.rs b/src/parse.rs deleted file mode 100644 index b4db237..0000000 --- a/src/parse.rs +++ /dev/null @@ -1,438 +0,0 @@ -use { - crate::ast::*, - std::iter::{Peekable}, -}; - - -#[derive(Debug, PartialEq)] -pub enum LexError { - UnexpectedEnd(Vec>), - UnexpectedToken(char), - InvalidFileRedirectionType -} - - -///! iterates chars until it finds some char in `delim` -pub struct DelimIter<'a, It> -where It: Iterator { - chars: &'a mut Peekable, - delim: Vec<(Option, bool)> -} - -impl<'a, It> DelimIter<'a, It> -where It: Iterator { - fn new(chars: &'a mut Peekable, delim: Vec<(Option, bool)>) -> Self { - DelimIter { chars, delim } - } - - fn new_whitespace(chars: &'a mut Peekable) -> Self { - DelimIter::new(chars, vec![ - (None, true), - (Some(' '), true), - (Some('\t'), true), - (Some('\n'), true) - ]) - } - - fn new_shell_word(chars: &'a mut Peekable) -> Self { - DelimIter::new(chars, vec![ - (None, true), - (Some(' '), true), - (Some('\t'), true), - (Some('\n'), true), - (Some('|'), false), - (Some('&'), false), - (Some(';'), false), - (Some('\"'), false), - (Some('\''), false) - ]) - } - - fn new_shell_word_or_assignment(chars: &'a mut Peekable) -> Self { - DelimIter::new(chars, vec![ - (None, true), - (Some(' '), true), - (Some('\t'), true), - (Some('\n'), true), - (Some('='), false), - (Some('|'), false), - (Some('&'), false), - (Some(';'), false), - (Some('\"'), false), - (Some('\''), false) - ]) - } -} - -impl<'a, It> Iterator for DelimIter<'a, It> -where It: 'a + Iterator { - type Item = Result; - - fn next(&mut self) -> Option> { - for (delim, consume) in self.delim.iter() { - if self.chars.peek().cloned() == *delim { - if *consume { - self.chars.next(); - } - return None; - } - } - - match self.chars.next() { - Some(c) => Some(Ok(c)), - None => Some(Err(LexError::UnexpectedEnd(vec![]))) - } - } -} - - -pub struct WordLexer<'a, It> -where It: 'a + Iterator { - chars: &'a mut Peekable -} - -impl<'a, It> WordLexer<'a, It> -where It: Iterator { - fn collect_until(&mut self, close: Option) -> Result { - DelimIter::new(&mut self.chars, vec![(close, true)]) - .try_collect::() - } -} - -pub fn skip_whitespace(chars: &mut Peekable) -where It: Iterator -{ - while let Some(c) = chars.peek() { - if c.is_whitespace() { - chars.next(); - } else { - break; - } - } -} - -pub fn parse_quoted(chars: &mut Peekable) -> Result -where It: Iterator -{ - assert_eq!( chars.next(), Some('\'')); - let quoted = DelimIter::new(chars, vec![(Some('\''), true)]).try_collect::(); - match quoted { - Ok(s) => { - Ok(WordSegment::Literal(s)) - }, - Err(e) => Err(e) - } -} - -pub fn parse_doublequoted(chars: &mut Peekable) -> Result -where It: Iterator -{ - assert_eq!( chars.next(), Some('\"')); - let quoted = DelimIter::new(chars, vec![(Some('\"'), true)]).try_collect::(); - match quoted { - Ok(s) => { - let word = Word { - segments: // fixme: handle spaces correctly -> create QuoteLexer - WordLexer { chars: &mut s.chars().peekable() } - .scan((), |_, x| x.ok()) - .collect::>() - }; - - Ok(WordSegment::DoubleQuote(word)) - }, - Err(e) => Err(e) - } -} - -pub fn parse_word(chars: &mut Peekable) -> Result -where It: Iterator -{ - Ok(Word { - segments: WordLexer{ chars }.try_collect::>()? - }) -} - -impl std::str::FromStr for FileRedirectionType { - type Err = LexError; - - fn from_str(s: &str) -> Result { - match s { - "<" => Ok(FileRedirectionType::In), - "<>" => Ok(FileRedirectionType::InOut), - ">" => Ok(FileRedirectionType::Out), - ">|" => Ok(FileRedirectionType::OutReplace), - ">>" => Ok(FileRedirectionType::OutAppend), - _ => Err(LexError::InvalidFileRedirectionType) - } - } -} - -pub fn parse_redirection(chars: &mut Peekable) -> Result -where It: Iterator -{ - Err(LexError::InvalidFileRedirectionType) - // let name = DelimIterator::new(chars, vec!['<', '>']).collect::(); -} - -pub fn parse_simple_cmd(chars: &mut Peekable) -> Result, LexError> -where It: Iterator -{ - let mut assignments = Vec::new(); - let mut redirections = Vec::new(); - - if chars.peek() == None { - return Ok(None); - } - - loop { - skip_whitespace(chars); - let mut name = DelimIter::new_shell_word_or_assignment(chars).try_collect::()?; - - match chars.peek().clone() { - Some('=') => { - chars.next(); - let mut lex = WordLexer{ chars }; - match lex.next() { - Some(Ok(value)) => { - assignments.push(Assignment { name, value: Word{ segments: vec![ value ] } }); - }, - Some(Err(e)) => { - return Err(e); - }, - None => { - return Err(LexError::UnexpectedEnd(vec![])); - } - } - } - _ => { - let mut cmd_segments = WordLexer{ chars }.try_collect::>()?; - cmd_segments.insert(0, WordSegment::Literal(name)); - - return Ok(Some(Command::Simple { - assignments, - command_word: Word { segments: cmd_segments }, - redirections, - })); - } - } - } -} - -pub fn parse_cmd(chars: &mut Peekable) -> Result, LexError> -where It: Iterator -{ - skip_whitespace(chars); - match chars.peek() { - Some('!') => { - chars.next(); - if let Some(cmd) = parse_cmd(chars)? { - Ok(Some(Command::Negation(Box::new(cmd)))) - } else { - Err(LexError::UnexpectedEnd(vec![])) - } - } - _ => { - if let Some(head) = parse_simple_cmd(chars)? { - skip_whitespace(chars); - - match chars.peek() { - Some(';') => { - chars.next(); - - let tail = parse_cmd( chars ) ?; - match tail { - Some(Command::Sequence(mut s)) => { - s.insert(0, head); - Ok(Some(Command::Sequence(s))) - } - Some(tail) => { - Ok(Some(Command::Sequence(vec![ head, tail ]))) - } - None => { - Ok(Some(head)) - } - } - } - Some('|') => { - chars.next(); - match chars.peek() { - Some('|') => { - chars.next(); - - let tail = parse_cmd( chars ) ?; - match tail { - Some(Command::ShortCircuitDisjunction(mut s)) => { - s.insert(0, head); - Ok(Some(Command::ShortCircuitDisjunction(s))) - } - Some(tail) => { - Ok(Some(Command::ShortCircuitDisjunction(vec![ head, tail ]))) - } - None => { - Err(LexError::UnexpectedEnd(vec![Some('|')])) - } - } - } - _ => { - let tail = parse_cmd( chars ) ?; - match tail { - Some(Command::Pipeline(mut s)) => { - s.insert(0, head); - Ok(Some(Command::Pipeline(s))) - } - Some(tail) => { - Ok(Some(Command::Pipeline(vec![ head, tail ]))) - } - None => { - Err(LexError::UnexpectedEnd(vec![])) - } - } - } - } - } - Some('&') => { - chars.next(); - match chars.peek() { - Some('&') => { - chars.next(); - - let tail = parse_cmd( chars ) ?; - match tail { - Some(Command::ShortCircuitConjunction(mut s)) => { - s.insert(0, head); - Ok(Some(Command::ShortCircuitConjunction(s))) - } - Some(tail) => { - Ok(Some(Command::ShortCircuitConjunction(vec![ head, tail ]))) - } - None => { - Err(LexError::UnexpectedEnd(vec![Some('&'), Some('&')])) - } - } - } - Some(c) => { - Err(LexError::UnexpectedToken(*c)) - } - None => { - // todo: - // background job - Ok(Some(head)) - } - } - } - Some(c) => { - Err(LexError::UnexpectedToken(*c)) - } - None => { - Ok(Some(head)) - } - } - } else { - Ok(None) - } - } - } -} - -impl<'a, It> Iterator for WordLexer<'a, It> -where It: 'a + Iterator { - type Item = Result; - - fn next(&mut self) -> Option> { - skip_whitespace(self.chars); - match self.chars.peek().cloned() { - Some('|') => { None } - Some('&') => { None } - Some(';') => { None } - Some('~') => { - self.chars.next(); - let user = DelimIter::new_whitespace(self.chars).collect(); - match user { - Ok(user) => Some(Ok(WordSegment::Tilde(user))), - Err(e) => Some(Err(e)) - } - } - Some('"') => { Some(parse_doublequoted(self.chars)) }, - Some('\'') => { Some(parse_quoted(self.chars)) }, - Some('$') => { - self.chars.next(); - match self.chars.peek() { - Some('{') => { - self.chars.next(); - match DelimIter::new(&mut self.chars, vec![(Some('}'), true)]).try_collect::() { - Ok(s) => { - Some(Ok(WordSegment::Parameter(s, ParameterFormat::Normal))) - } - Err(e) => Some(Err(e)) - } - } - Some('(') => { - self.chars.next(); - let subcmd_str = DelimIter::new(&mut self.chars, vec![(Some(')'), true)]).try_collect::(); - match subcmd_str { - Ok(subcmd_str) => { - match parse_cmd(&mut subcmd_str.chars().peekable()) { - Ok(Some(subcmd)) => { - Some(Ok(WordSegment::Subshell(subcmd))) - } - Ok(None) => None, - Err(err) => Some(Err(err)) - } - } - Err(err) => Some(Err(err)) - } - } - _ => { - match DelimIter::new_whitespace(self.chars).collect() { - Ok(s) => { - Some(Ok(WordSegment::Parameter(s, ParameterFormat::Normal))) - } - Err(e) => Some(Err(e)) - } - } - } - } - Some(c) => { - let s : Result = DelimIter::new_shell_word(self.chars).collect(); - match s { - Ok(s) => Some(Ok(WordSegment::Literal(s))), - Err(e) => Some(Err(e)) - } - } - None => { - None - } - } - } -} - - -mod test { - use crate::parse::*; - - #[test] - fn test_delim_iter() { - let mut cs = "test 1234".chars().peekable(); - let mut lexer = DelimIter::new_shell_word(&mut cs); - assert_eq!(lexer.try_collect::(), Ok(String::from("test"))); - } - - #[test] - fn test_word_lexer() { - let mut cs = "test 1234|test".chars().peekable(); - - { - let mut lexer = WordLexer{ chars: &mut cs }; - assert_eq!(lexer.next(), Some(Ok(WordSegment::Literal(String::from("test"))))); - assert_eq!(lexer.next(), Some(Ok(WordSegment::Literal(String::from("1234"))))); - assert_eq!(lexer.next(), None); - } - assert_eq!(cs.next(), Some('|')); - { - let mut lexer = WordLexer{ chars: &mut cs }; - assert_eq!(lexer.next(), Some(Ok(WordSegment::Literal(String::from("test"))))); - assert_eq!(lexer.next(), None); - } - } -} - diff --git a/typeDB/cut b/typeDB/cut index e860067..e9443b2 100644 --- a/typeDB/cut +++ b/typeDB/cut @@ -2,17 +2,17 @@ match OPTION... { --help { <1 : Help~ ; } - --version { <1 : VersionInfo~ ; } + --version { <1 : VersionInfo~ ; } * { - match OPTION... { - -f--fields LIST:~CutFieldList~ { + match OPTION... { + -f--fields LIST:~CutFieldList~ { ROW-DELIM = match OPTION... { -z--zero-terminated { '\0' } * { '\n' } - }; + }; IN-COL-DELIM = match OPTION... { @@ -22,9 +22,9 @@ OUT-COL-DELIM = match OPTION... { - --output-delimiter STRING: { STRING } - * { IN-COL-DELIM } - }; + --output-delimiter STRING: { STRING } + * { IN-COL-DELIM } + }; FILE : Path ~>~~ ; + <1 : >~~ ; } } }