Actually just three programming languages in a trenchcoat
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Adding a few unit tests

+84 -30
+9
trilogy-parser/src/syntax/handler.rs
··· 19 19 } 20 20 } 21 21 } 22 + 23 + #[cfg(test)] 24 + mod test { 25 + use super::*; 26 + 27 + test_parse!(handler_given: "given hello(1)" => Handler::parse => "(Handler::Given (GivenHandler (RuleHead _ [_]) ()))"); 28 + test_parse!(handler_when: "when 'NAN resume 5" => Handler::parse => "(Handler::When (WhenHandler _ _ _ _))"); 29 + test_parse!(handler_else: "else n resume 5" => Handler::parse => "(Handler::Else (ElseHandler _ _ _))"); 30 + }
+8
trilogy-parser/src/syntax/handler_body.rs
··· 20 20 } 21 21 } 22 22 } 23 + 24 + #[cfg(test)] 25 + mod test { 26 + use super::*; 27 + 28 + test_parse!(handlerbody_block: "{ let x = 5; resume x }" => HandlerBody::parse => "(HandlerBody::Block (Block _))"); 29 + test_parse!(handlerbody_expr: "let y = 5, resume y" => HandlerBody::parse => "(HandlerBody::Expression _)"); 30 + }
+10
trilogy-parser/src/syntax/handler_strategy.rs
··· 30 30 } 31 31 } 32 32 } 33 + 34 + #[cfg(test)] 35 + mod test { 36 + use super::*; 37 + 38 + test_parse!(handler_strategy_yield: "yield" => HandlerStrategy::parse => "(HandlerStrategy::Yield _)"); 39 + test_parse!(handler_strategy_cancel: "cancel" => HandlerStrategy::parse => "(HandlerStrategy::Cancel _)"); 40 + test_parse!(handler_strategy_invert: "invert" => HandlerStrategy::parse => "(HandlerStrategy::Invert _)"); 41 + test_parse!(handler_strategy_resume: "resume" => HandlerStrategy::parse => "(HandlerStrategy::Resume _)"); 42 + }
+15
trilogy-parser/src/syntax/identifier.rs
··· 45 45 } 46 46 } 47 47 } 48 + 49 + #[cfg(test)] 50 + mod test { 51 + use super::*; 52 + 53 + test_parse!(identifier_normal: "hello" => Identifier::parse => "(Identifier)"); 54 + test_parse!(identifier_underscored: "_hello" => Identifier::parse => "(Identifier)"); 55 + test_parse!(identifier_numbers: "hello123" => Identifier::parse => "(Identifier)"); 56 + test_parse_error!(identifier_keyword: "for" => Identifier::parse); 57 + 58 + test_parse!(identifiereq_normal: "hello=" => Identifier::parse_eq => "(Identifier)"); 59 + test_parse!(identifiereq_underscored: "_hello=" => Identifier::parse_eq => "(Identifier)"); 60 + test_parse!(identifiereq_numbers: "hello123=" => Identifier::parse_eq => "(Identifier)"); 61 + test_parse_error!(identifiereq_keyword: "for=" => Identifier::parse_eq); 62 + }
+13 -20
trilogy-parser/src/test.rs
··· 79 79 ($name:ident : $src:literal |$parser:ident| $parse:expr => $sexp:literal) => { 80 80 #[test] 81 81 fn $name() { 82 - use crate::{Parser, PrettyPrintSExpr}; 83 - use trilogy_scanner::TokenType::*; 82 + use crate::PrettyPrintSExpr as _; 84 83 let scanner = trilogy_scanner::Scanner::new($src); 85 - let mut $parser = Parser::new(scanner); 86 - $parser.expect(StartOfFile).unwrap(); 84 + let mut $parser = crate::Parser::new(scanner); 85 + $parser.expect(trilogy_scanner::TokenType::StartOfFile).unwrap(); 87 86 let parse = $parse; 88 87 let mut allocator = pretty::RcAllocator; 89 88 let sexpr = format!("{}", parse.pretty_print_sexpr(&mut allocator).pretty(100)); 90 89 let parsed = crate::test::normalize_sexpr(&sexpr); 91 90 let expected = crate::test::normalize_sexpr($sexp); 92 - $parser.expect(EndOfFile).unwrap(); 91 + $parser.expect(trilogy_scanner::TokenType::EndOfFile).unwrap(); 93 92 assert!($parser.errors.is_empty()); 94 93 assert_eq!(parsed.split_ascii_whitespace().collect::<crate::test::SExpr>(), expected.split_ascii_whitespace().collect::<crate::test::SExpr>()); 95 94 } ··· 120 119 ($name:ident : $src:literal |$parser:ident| $parse:expr => $error:literal) => { 121 120 #[test] 122 121 fn $name() { 123 - use crate::Parser; 124 - use trilogy_scanner::TokenType::*; 125 122 let scanner = trilogy_scanner::Scanner::new($src); 126 - let mut $parser = Parser::new(scanner); 127 - $parser.expect(StartOfFile).unwrap(); 123 + let mut $parser = crate::Parser::new(scanner); 124 + $parser.expect(trilogy_scanner::TokenType::StartOfFile).unwrap(); 128 125 let _ = $parse; 129 126 assert_eq!($parser.errors.first().expect("parse should have reported an error message").message(), $error); 130 127 } ··· 133 130 ($name:ident : $src:literal |$parser:ident| $parse:expr) => { 134 131 #[test] 135 132 fn $name() { 136 - use crate::Parser; 137 - use trilogy_scanner::TokenType::*; 138 133 let scanner = trilogy_scanner::Scanner::new($src); 139 - let mut $parser = Parser::new(scanner); 140 - $parser.expect(StartOfFile).unwrap(); 134 + let mut $parser = crate::Parser::new(scanner); 135 + $parser.expect(trilogy_scanner::TokenType::StartOfFile).unwrap(); 141 136 let result = $parse; 142 137 if result.is_ok() && $parser.errors.is_empty() { 143 - assert!($parser.expect(EndOfFile).is_err()); 138 + assert!($parser.expect(trilogy_scanner::TokenType::EndOfFile).is_err()); 144 139 } else { 145 140 assert!(!$parser.errors.is_empty()); 146 141 } ··· 158 153 ($name:ident : $src:literal |$parser:ident| $parse:expr => $sexp:literal) => { 159 154 #[test] 160 155 fn $name() { 161 - use crate::{Parser, PrettyPrintSExpr}; 156 + use crate::PrettyPrintSExpr as _; 162 157 let scanner = trilogy_scanner::Scanner::new($src); 163 - let mut $parser = Parser::new(scanner); 158 + let mut $parser = crate::Parser::new(scanner); 164 159 let parse = $parse; 165 160 let mut allocator = pretty::RcAllocator; 166 161 let sexpr = format!("{}", parse.pretty_print_sexpr(&mut allocator).pretty(100)); ··· 180 175 ($name:ident : $src:literal |$parser:ident| $parse:expr => $error:literal) => { 181 176 #[test] 182 177 fn $name() { 183 - use crate::Parser; 184 178 let scanner = trilogy_scanner::Scanner::new($src); 185 - let mut $parser = Parser::new(scanner); 179 + let mut $parser = crate::Parser::new(scanner); 186 180 $parse; 187 181 assert_eq!($parser.errors.first().expect("parse should have reported an error message").message(), $error); 188 182 } ··· 191 185 ($name:ident : $src:literal |$parser:ident| $parse:expr) => { 192 186 #[test] 193 187 fn $name() { 194 - use crate::Parser; 195 188 use trilogy_scanner::TokenType::*; 196 189 let scanner = trilogy_scanner::Scanner::new($src); 197 - let mut $parser = Parser::new(scanner); 190 + let mut $parser = crate::Parser::new(scanner); 198 191 $parse; 199 192 assert!(!$parser.errors.is_empty()); 200 193 }
+8 -5
trilogy-scanner/src/scanner.rs
··· 133 133 134 134 fn identifier_or_keyword(&mut self, starts_with: char) -> Token { 135 135 let identifier = self.identifier(starts_with.into()); 136 - if self.expect('=').is_some() { 137 - return self.make_token(IdentifierEq).with_value(identifier); 138 - } 139 - self.make_token(Identifier) 140 - .with_value(identifier) 136 + let token = if self.expect('=').is_some() { 137 + self.make_token(IdentifierEq).with_value(identifier) 138 + } else { 139 + self.make_token(Identifier).with_value(identifier) 140 + }; 141 + 142 + token 141 143 .resolve_keywords() 144 + .unwrap_or_else(|err| self.make_error(err)) 142 145 } 143 146 144 147 fn unicode_escape_sequence(&mut self) -> Option<char> {
+9
trilogy-scanner/src/test.rs
··· 140 140 }; 141 141 } 142 142 143 + #[test] 144 + fn identifier_eq_keyword_error() { 145 + let tokens = Scanner::new("for=") 146 + .into_iter() 147 + .map(|token| token.token_type) 148 + .collect::<Vec<_>>(); 149 + assert_eq!(tokens.as_slice(), [StartOfFile, Error, EndOfFile]); 150 + } 151 + 143 152 test_tokenize!(discard => "_" = [Discard]); 144 153 test_tokenize!(underscore_ident => "_abc" = [Identifier]); 145 154
+12 -5
trilogy-scanner/src/token.rs
··· 30 30 self 31 31 } 32 32 33 - pub(crate) fn resolve_keywords(mut self) -> Self { 34 - if self.token_type != TokenType::Identifier { 35 - return self; 33 + pub(crate) fn resolve_keywords(mut self) -> Result<Self, &'static str> { 34 + if self.token_type != TokenType::Identifier && self.token_type != TokenType::IdentifierEq { 35 + return Ok(self); 36 36 } 37 + let is_eq = self.token_type == TokenType::IdentifierEq; 38 + 37 39 self.token_type = match self.value.as_ref().unwrap().as_str().unwrap() { 38 40 "_" => TokenType::Discard, 39 41 "and" => TokenType::KwAnd, ··· 115 117 "while" => TokenType::KwWhile, 116 118 "with" => TokenType::KwWith, 117 119 "yield" => TokenType::KwYield, 118 - _ => TokenType::Identifier, 120 + _ => self.token_type, 119 121 }; 120 - self 122 + 123 + if is_eq && self.token_type != TokenType::IdentifierEq { 124 + return Err("cannot use keyword as an assignment function"); 125 + } 126 + 127 + Ok(self) 121 128 } 122 129 }