3 // Copyright (c) 2018 Guillaume Gomez
5 // Permission is hereby granted, free of charge, to any person obtaining a copy
6 // of this software and associated documentation files (the "Software"), to deal
7 // in the Software without restriction, including without limitation the rights
8 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 // copies of the Software, and to permit persons to whom the Software is
10 // furnished to do so, subject to the following conditions:
12 // The above copyright notice and this permission notice shall be included in all
13 // copies or substantial portions of the Software.
15 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
24 use std
::iter
::Peekable
;
25 use std
::str::CharIndices
;
27 pub trait MyTryFrom
<T
>: Sized
{
29 fn try_from(value
: T
) -> Result
<Self, Self::Error
>;
32 #[derive(Debug, PartialEq, Eq, Clone, Copy)]
33 pub enum ReservedChar
{
65 fn is_useless(&self) -> bool
{
66 *self == ReservedChar
::Space
||
67 *self == ReservedChar
::Tab
||
68 *self == ReservedChar
::Backline
72 impl fmt
::Display
for ReservedChar
{
73 fn fmt(&self, f
: &mut fmt
::Formatter
) -> fmt
::Result
{
76 ReservedChar
::Comma
=> '
,'
,
77 ReservedChar
::OpenParenthese
=> '
('
,
78 ReservedChar
::CloseParenthese
=> '
)'
,
79 ReservedChar
::OpenCurlyBrace
=> '
{'
,
80 ReservedChar
::CloseCurlyBrace
=> '
}'
,
81 ReservedChar
::OpenBracket
=> '
['
,
82 ReservedChar
::CloseBracket
=> '
]'
,
83 ReservedChar
::Colon
=> '
:'
,
84 ReservedChar
::SemiColon
=> '
;'
,
85 ReservedChar
::Dot
=> '
.'
,
86 ReservedChar
::Quote
=> '
\''
,
87 ReservedChar
::DoubleQuote
=> '
"',
88 ReservedChar::ExclamationMark => '!',
89 ReservedChar::QuestionMark => '?',
90 ReservedChar::Slash => '/',
91 ReservedChar::Modulo => '%',
92 ReservedChar::Star => '*',
93 ReservedChar::Minus => '-',
94 ReservedChar::Plus => '+',
95 ReservedChar::EqualSign => '=',
96 ReservedChar::Backslash => '\\',
97 ReservedChar::Space => ' ',
98 ReservedChar::Tab => '\t',
99 ReservedChar::Backline => '\n',
100 ReservedChar::LessThan => '<',
101 ReservedChar::SuperiorThan => '>',
102 ReservedChar::Pipe => '|',
103 ReservedChar::Ampersand => '&',
108 impl MyTryFrom<char> for ReservedChar {
109 type Error = &'static str;
111 fn try_from(value: char) -> Result<ReservedChar, Self::Error> {
113 ',' => Ok(ReservedChar::Comma),
114 '(' => Ok(ReservedChar::OpenParenthese),
115 ')' => Ok(ReservedChar::CloseParenthese),
116 '{' => Ok(ReservedChar::OpenCurlyBrace),
117 '}' => Ok(ReservedChar::CloseCurlyBrace),
118 '[' => Ok(ReservedChar::OpenBracket),
119 ']' => Ok(ReservedChar::CloseBracket),
120 ':' => Ok(ReservedChar::Colon),
121 ';' => Ok(ReservedChar::SemiColon),
122 '.' => Ok(ReservedChar::Dot),
123 '\'' => Ok(ReservedChar::Quote),
124 '"'
=> Ok(ReservedChar
::DoubleQuote
),
125 '
!'
=> Ok(ReservedChar
::ExclamationMark
),
126 '?'
=> Ok(ReservedChar
::QuestionMark
),
127 '
/'
=> Ok(ReservedChar
::Slash
),
128 '
%'
=> Ok(ReservedChar
::Modulo
),
129 '
*'
=> Ok(ReservedChar
::Star
),
130 '
-'
=> Ok(ReservedChar
::Minus
),
131 '
+'
=> Ok(ReservedChar
::Plus
),
132 '
='
=> Ok(ReservedChar
::EqualSign
),
133 '
\\'
=> Ok(ReservedChar
::Backslash
),
134 ' '
=> Ok(ReservedChar
::Space
),
135 '
\t'
=> Ok(ReservedChar
::Tab
),
137 '
\r'
=> Ok(ReservedChar
::Backline
),
138 '
<'
=> Ok(ReservedChar
::LessThan
),
139 '
>'
=> Ok(ReservedChar
::SuperiorThan
),
140 '
|'
=> Ok(ReservedChar
::Pipe
),
141 '
&'
=> Ok(ReservedChar
::Ampersand
),
142 _
=> Err("Unknown reserved char"),
147 #[derive(Debug, PartialEq, Eq, Clone, Copy)]
181 fn get_required
<'a
>(next
: &Token
<'a
>) -> Option
<char> {
183 Token
::Keyword(_
) | Token
::Other(_
) => Some(' '
),
188 impl fmt
::Display
for Keyword
{
189 fn fmt(&self, f
: &mut fmt
::Formatter
) -> fmt
::Result
{
192 Keyword
::Break
=> "break",
193 Keyword
::Case
=> "case",
194 Keyword
::Catch
=> "catch",
195 Keyword
::Const
=> "const",
196 Keyword
::Continue
=> "continue",
197 Keyword
::Default
=> "default",
199 Keyword
::Else
=> "else",
200 Keyword
::False
=> "false",
201 Keyword
::Finally
=> "finally",
202 Keyword
::Function
=> "function",
203 Keyword
::For
=> "for",
206 Keyword
::InstanceOf
=> "instanceof",
207 Keyword
::New
=> "new",
208 Keyword
::Null
=> "null",
209 Keyword
::Private
=> "private",
210 Keyword
::Protected
=> "protected",
211 Keyword
::Public
=> "public",
212 Keyword
::Return
=> "return",
213 Keyword
::Switch
=> "switch",
214 Keyword
::This
=> "this",
215 Keyword
::Throw
=> "throw",
216 Keyword
::True
=> "true",
217 Keyword
::Try
=> "try",
218 Keyword
::Typeof
=> "typeof",
219 Keyword
::Static
=> "static",
220 Keyword
::Var
=> "var",
221 Keyword
::While
=> "while",
226 impl<'a
> MyTryFrom
<&'a
str> for Keyword
{
227 type Error
= &'
static str;
229 fn try_from(value
: &str) -> Result
<Keyword
, Self::Error
> {
231 "break" => Ok(Keyword
::Break
),
232 "case" => Ok(Keyword
::Case
),
233 "catch" => Ok(Keyword
::Catch
),
234 "const" => Ok(Keyword
::Const
),
235 "continue" => Ok(Keyword
::Continue
),
236 "default" => Ok(Keyword
::Default
),
237 "do" => Ok(Keyword
::Do
),
238 "else" => Ok(Keyword
::Else
),
239 "false" => Ok(Keyword
::False
),
240 "finally" => Ok(Keyword
::Finally
),
241 "function" => Ok(Keyword
::Function
),
242 "for" => Ok(Keyword
::For
),
243 "if" => Ok(Keyword
::If
),
244 "in" => Ok(Keyword
::In
),
245 "instanceof" => Ok(Keyword
::InstanceOf
),
246 "new" => Ok(Keyword
::New
),
247 "null" => Ok(Keyword
::Null
),
248 "private" => Ok(Keyword
::Private
),
249 "protected" => Ok(Keyword
::Protected
),
250 "public" => Ok(Keyword
::Public
),
251 "return" => Ok(Keyword
::Return
),
252 "switch" => Ok(Keyword
::Switch
),
253 "this" => Ok(Keyword
::This
),
254 "throw" => Ok(Keyword
::Throw
),
255 "true" => Ok(Keyword
::True
),
256 "try" => Ok(Keyword
::Try
),
257 "typeof" => Ok(Keyword
::Typeof
),
258 "static" => Ok(Keyword
::Static
),
259 "var" => Ok(Keyword
::Var
),
260 "while" => Ok(Keyword
::While
),
261 _
=> Err("Unkown keyword"),
266 #[derive(Debug, PartialEq, Eq, Clone, Copy)]
280 impl fmt
::Display
for Condition
{
281 fn fmt(&self, f
: &mut fmt
::Formatter
) -> fmt
::Result
{
284 Condition
::And
=> "&&",
285 Condition
::Or
=> "||",
286 Condition
::DifferentThan
=> "!=",
287 Condition
::SuperDifferentThan
=> "!==",
288 Condition
::EqualTo
=> "==",
289 Condition
::SuperEqualTo
=> "===",
290 Condition
::SuperiorThan
=> ">",
291 Condition
::SuperiorOrEqualTo
=> ">=",
292 Condition
::InferiorThan
=> "<",
293 Condition
::InferiorOrEqualTo
=> "<=",
298 #[derive(Debug, PartialEq, Eq, Clone, Copy)]
313 impl fmt
::Display
for Operation
{
314 fn fmt(&self, f
: &mut fmt
::Formatter
) -> fmt
::Result
{
317 Operation
::Addition
=> "+",
318 Operation
::AdditionEqual
=> "+=",
319 Operation
::Subtract
=> "-",
320 Operation
::SubtractEqual
=> "-=",
321 Operation
::Multiply
=> "*",
322 Operation
::MultiplyEqual
=> "*=",
323 Operation
::Divide
=> "/",
324 Operation
::DivideEqual
=> "/=",
325 Operation
::Modulo
=> "%",
326 Operation
::ModuloEqual
=> "%=",
327 Operation
::Equal
=> "=",
332 impl MyTryFrom
<ReservedChar
> for Operation
{
333 type Error
= &'
static str;
335 fn try_from(value
: ReservedChar
) -> Result
<Operation
, Self::Error
> {
337 ReservedChar
::Plus
=> Operation
::Addition
,
338 ReservedChar
::Minus
=> Operation
::Subtract
,
339 ReservedChar
::Slash
=> Operation
::Divide
,
340 ReservedChar
::Star
=> Operation
::Multiply
,
341 ReservedChar
::Modulo
=> Operation
::Modulo
,
342 _
=> return Err("Unkown operation"),
347 #[derive(Debug, PartialEq)]
358 is_interactive
: bool
,
360 Condition(Condition
),
361 Operation(Operation
),
364 impl<'a
> fmt
::Display
for Token
<'a
> {
365 fn fmt(&self, f
: &mut fmt
::Formatter
) -> fmt
::Result
{
367 Token
::Keyword(x
) => write
!(f
, "{}", x
),
368 Token
::Char(x
) => write
!(f
, "{}", x
),
371 Token
::Other(x
) => write
!(f
, "{}", x
),
372 Token
::License(x
) => write
!(f
, "/*!{}*/", x
),
373 Token
::Regex { regex, is_global, is_interactive }
=> {
374 let x
= write
!(f
, "/{}/", regex
);
383 Token
::Condition(x
) => write
!(f
, "{}", x
),
384 Token
::Operation(x
) => write
!(f
, "{}", x
),
390 fn is_comment(&self) -> bool
{
392 Token
::Comment(_
) => true,
397 fn get_char(&self) -> Option
<ReservedChar
> {
399 Token
::Char(c
) => Some(c
),
405 fn is_condition(&self) -> bool
{
407 Token
::Condition(_
) => true,
412 fn is_other(&self) -> bool
{
414 Token
::Other(_
) => true,
419 fn is_white_character(&self) -> bool
{
421 Token
::Char(c
) => c
.is_useless(),
427 fn get_line_comment
<'a
>(source
: &'a
str, iterator
: &mut Peekable
<CharIndices
>,
428 start_pos
: &mut usize) -> Option
<Token
<'a
>> {
430 while let Some((pos
, c
)) = iterator
.next() {
431 if let Ok(c
) = ReservedChar
::try_from(c
) {
432 if c
== ReservedChar
::Backline
{
433 let ret
= Some(Token
::Comment(&source
[*start_pos
..pos
]));
442 fn get_regex
<'a
>(source
: &'a
str, iterator
: &mut Peekable
<CharIndices
>,
443 start_pos
: &mut usize) -> Option
<Token
<'a
>> {
445 while let Some((pos
, c
)) = iterator
.next() {
447 // we skip next character
451 if let Ok(c
) = ReservedChar
::try_from(c
) {
452 if c
== ReservedChar
::Slash
{
453 let mut is_global
= false;
454 let mut is_interactive
= false;
457 match iterator
.peek() {
458 Some((_
, 'i'
)) => is_interactive
= true,
459 Some((_
, 'g'
)) => is_global
= true,
465 let ret
= Some(Token
::Regex
{
466 regex
: &source
[*start_pos
..pos
],
470 *start_pos
= pos
+ add
;
478 fn get_comment
<'a
>(source
: &'a
str, iterator
: &mut Peekable
<CharIndices
>,
479 start_pos
: &mut usize) -> Option
<Token
<'a
>> {
480 let mut prev
= ReservedChar
::Quote
;
482 let builder
= if let Some((_
, c
)) = iterator
.next() {
487 if let Ok(c
) = ReservedChar
::try_from(c
) {
496 while let Some((pos
, c
)) = iterator
.next() {
497 if let Ok(c
) = ReservedChar
::try_from(c
) {
498 if c
== ReservedChar
::Slash
&& prev
== ReservedChar
::Star
{
499 let ret
= Some(builder(&source
[*start_pos
..pos
- 1]));
505 prev
= ReservedChar
::Space
;
511 fn get_string
<'a
>(source
: &'a
str, iterator
: &mut Peekable
<CharIndices
>, start_pos
: &mut usize,
512 start
: ReservedChar
) -> Option
<Token
<'a
>> {
513 while let Some((pos
, c
)) = iterator
.next() {
515 // we skip next character
519 if let Ok(c
) = ReservedChar
::try_from(c
) {
521 let ret
= Some(Token
::String(&source
[*start_pos
..pos
+ 1]));
530 fn first_useful
<'a
>(v
: &'a
[Token
<'a
>]) -> Option
<&'a Token
<'a
>> {
531 for x
in v
.iter().rev() {
532 if x
.is_white_character() {
540 pub fn tokenize
<'a
>(source
: &'a
str) -> Tokens
<'a
> {
541 let mut v
= Vec
::with_capacity(1000);
543 let mut iterator
= source
.char_indices().peekable();
546 let (mut pos
, c
) = match iterator
.next() {
550 if let Ok(c
) = ReservedChar
::try_from(c
) {
552 if let Ok(w
) = Keyword
::try_from(&source
[start
..pos
]) {
553 v
.push(Token
::Keyword(w
));
555 v
.push(Token
::Other(&source
[start
..pos
]));
558 if c
== ReservedChar
::Quote
|| c
== ReservedChar
::DoubleQuote
{
559 if let Some(s
) = get_string(source
, &mut iterator
, &mut pos
, c
) {
562 } else if c
== ReservedChar
::Slash
&&
563 *v
.last().unwrap_or(&Token
::Other("")) == Token
::Operation(Operation
::Divide
) {
565 if let Some(s
) = get_line_comment(source
, &mut iterator
, &mut pos
) {
568 } else if c
== ReservedChar
::Slash
&&
569 iterator
.peek().is_some() &&
570 iterator
.peek().unwrap().1 != '
/'
&&
571 iterator
.peek().unwrap().1 != '
*'
&&
572 !first_useful(&v
).unwrap_or(&Token
::String("")).is_other() {
573 if let Some(r
) = get_regex(source
, &mut iterator
, &mut pos
) {
576 } else if c
== ReservedChar
::Star
&&
577 *v
.last().unwrap_or(&Token
::Other("")) == Token
::Operation(Operation
::Divide
) {
579 if let Some(s
) = get_comment(source
, &mut iterator
, &mut pos
) {
582 } else if c
== ReservedChar
::Pipe
&&
583 *v
.last().unwrap_or(&Token
::Other("")) == Token
::Char(ReservedChar
::Pipe
) {
585 v
.push(Token
::Condition(Condition
::Or
));
586 } else if c
== ReservedChar
::Ampersand
&&
587 *v
.last().unwrap_or(&Token
::Other("")) == Token
::Char(ReservedChar
::Ampersand
) {
589 v
.push(Token
::Condition(Condition
::And
));
590 } else if c
== ReservedChar
::EqualSign
&&
591 *v
.last().unwrap_or(&Token
::Other("")) == Token
::Char(ReservedChar
::EqualSign
) {
593 v
.push(Token
::Condition(Condition
::EqualTo
));
594 } else if c
== ReservedChar
::EqualSign
&&
595 *v
.last().unwrap_or(&Token
::Other("")) == Token
::Condition(Condition
::EqualTo
) {
597 v
.push(Token
::Condition(Condition
::SuperEqualTo
));
598 } else if c
== ReservedChar
::EqualSign
&&
599 *v
.last().unwrap_or(&Token
::Other("")) == Token
::Char(ReservedChar
::ExclamationMark
) {
601 v
.push(Token
::Condition(Condition
::DifferentThan
));
602 } else if c
== ReservedChar
::EqualSign
&&
603 *v
.last().unwrap_or(&Token
::Other("")) == Token
::Condition(Condition
::DifferentThan
) {
605 v
.push(Token
::Condition(Condition
::SuperDifferentThan
));
606 } else if c
== ReservedChar
::EqualSign
&&
607 *v
.last().unwrap_or(&Token
::Other("")) == Token
::Operation(Operation
::Divide
) {
609 v
.push(Token
::Operation(Operation
::DivideEqual
));
610 } else if c
== ReservedChar
::EqualSign
&&
611 *v
.last().unwrap_or(&Token
::Other("")) == Token
::Operation(Operation
::Multiply
) {
613 v
.push(Token
::Operation(Operation
::MultiplyEqual
));
614 } else if c
== ReservedChar
::EqualSign
&&
615 *v
.last().unwrap_or(&Token
::Other("")) == Token
::Operation(Operation
::Addition
) {
617 v
.push(Token
::Operation(Operation
::AdditionEqual
));
618 } else if c
== ReservedChar
::EqualSign
&&
619 *v
.last().unwrap_or(&Token
::Other("")) == Token
::Operation(Operation
::Subtract
) {
621 v
.push(Token
::Operation(Operation
::SubtractEqual
));
622 } else if c
== ReservedChar
::EqualSign
&&
623 *v
.last().unwrap_or(&Token
::Other("")) == Token
::Operation(Operation
::Modulo
) {
625 v
.push(Token
::Operation(Operation
::ModuloEqual
));
626 } else if let Ok(o
) = Operation
::try_from(c
) {
627 v
.push(Token
::Operation(o
));
629 v
.push(Token
::Char(c
));
637 pub struct Tokens
<'a
>(pub Vec
<Token
<'a
>>);
639 impl<'a
> fmt
::Display
for Tokens
<'a
> {
640 fn fmt(&self, f
: &mut fmt
::Formatter
) -> fmt
::Result
{
641 let tokens
= &self.0;
642 for i
in 0..tokens
.len() {
643 write
!(f
, "{}", tokens
[i
])?
;
644 if let Some(c
) = match tokens
[i
] {
646 Token
::Other(_
) if i
+ 1 < tokens
.len() => get_required(&tokens
[i
+ 1]),
656 pub fn clean_tokens
<'a
>(tokens
: &mut Tokens
<'a
>) {
657 tokens
.0.retain(|c
| {
659 if let Some(x
) = c
.get_char() {