diff options
| author | Tolmachev Igor <me@igorek.dev> | 2026-05-10 12:47:18 +0300 |
|---|---|---|
| committer | Tolmachev Igor <me@igorek.dev> | 2026-05-10 12:47:37 +0300 |
| commit | 1801afdbd0058cc9cc040b977de0d5652d65aab9 (patch) | |
| tree | fafafb12e7d877f506e022ecb282e66cecb72bdc /compiler/src | |
| parent | 160b64427d79290a59ac48c9babca064232d8dfd (diff) | |
| download | crisp-1801afdbd0058cc9cc040b977de0d5652d65aab9.tar.gz crisp-1801afdbd0058cc9cc040b977de0d5652d65aab9.zip | |
Remove Quote from parser
Dropped to make the language simpler.
Diffstat (limited to 'compiler/src')
| -rw-r--r-- | compiler/src/ast/parser.rs | 9 | ||||
| -rw-r--r-- | compiler/src/ast/tests.rs | 136 | ||||
| -rw-r--r-- | compiler/src/lex/lexer.rs | 6 | ||||
| -rw-r--r-- | compiler/src/lex/tests.rs | 63 | ||||
| -rw-r--r-- | compiler/src/lex/token.rs | 1 |
5 files changed, 2 insertions, 213 deletions
diff --git a/compiler/src/ast/parser.rs b/compiler/src/ast/parser.rs index 33b36be..bb4e0ce 100644 --- a/compiler/src/ast/parser.rs +++ b/compiler/src/ast/parser.rs | |||
| @@ -119,15 +119,6 @@ where | |||
| 119 | self.consume(); | 119 | self.consume(); |
| 120 | return Err(Spanned::new(Error::UnexpectedRightPar, span)); | 120 | return Err(Spanned::new(Error::UnexpectedRightPar, span)); |
| 121 | } | 121 | } |
| 122 | Token::Quote => { | ||
| 123 | self.consume(); | ||
| 124 | let quote = Spanned::new(Expr::Atom(Atom::Symbol("quote".into())), span); | ||
| 125 | let expr = self.parse_expr()?; | ||
| 126 | Spanned::new( | ||
| 127 | Expr::List(vec![quote, expr]), | ||
| 128 | Span::new(span.start, self.last_token_span.end), | ||
| 129 | ) | ||
| 130 | } | ||
| 131 | Token::Number(number) => { | 122 | Token::Number(number) => { |
| 132 | self.consume(); | 123 | self.consume(); |
| 133 | let atom = parse_number(number).map_err(|e| Spanned::new(e, span))?; | 124 | let atom = parse_number(number).map_err(|e| Spanned::new(e, span))?; |
diff --git a/compiler/src/ast/tests.rs b/compiler/src/ast/tests.rs index 8905427..505b075 100644 --- a/compiler/src/ast/tests.rs +++ b/compiler/src/ast/tests.rs | |||
| @@ -1,4 +1,4 @@ | |||
| 1 | use std::{fmt::Debug, iter::repeat_n, rc::Rc}; | 1 | use std::{fmt::Debug, iter::repeat_n}; |
| 2 | 2 | ||
| 3 | use crate::{ | 3 | use crate::{ |
| 4 | ast::{Atom, Error, Expr, Parser, parser::MAX_DEPTH, tests::E::*}, | 4 | ast::{Atom, Error, Expr, Parser, parser::MAX_DEPTH, tests::E::*}, |
| @@ -263,53 +263,6 @@ fn test_lists() { | |||
| 263 | } | 263 | } |
| 264 | } | 264 | } |
| 265 | 265 | ||
| 266 | fn quote(expr: E) -> E { | ||
| 267 | List(vec![Sym("quote"), expr]) | ||
| 268 | } | ||
| 269 | |||
| 270 | #[test] | ||
| 271 | fn test_quote() { | ||
| 272 | let cases = vec![ | ||
| 273 | // 'x -> (quote x) | ||
| 274 | (vec![Quote, Symbol("x")], vec![quote(Sym("x"))]), | ||
| 275 | // '42 -> (quote 42) | ||
| 276 | (vec![Quote, Number("42")], vec![quote(Int(42))]), | ||
| 277 | // '() -> (quote nil) | ||
| 278 | (vec![Quote, LeftPar, RightPar], vec![quote(Nil)]), | ||
| 279 | // ''x -> (quote (quote x)) | ||
| 280 | ( | ||
| 281 | vec![Quote, Quote, Symbol("x")], | ||
| 282 | vec![quote(quote(Sym("x")))], | ||
| 283 | ), | ||
| 284 | // '''x -> (quote (quote (quote x))) | ||
| 285 | ( | ||
| 286 | vec![Quote, Quote, Quote, Symbol("x")], | ||
| 287 | vec![quote(quote(quote(Sym("x"))))], | ||
| 288 | ), | ||
| 289 | // '(1 2) -> (quote (1 2)) | ||
| 290 | ( | ||
| 291 | vec![Quote, LeftPar, Number("1"), Number("2"), RightPar], | ||
| 292 | vec![quote(List(vec![Int(1), Int(2)]))], | ||
| 293 | ), | ||
| 294 | // (list 'a 'b) -> (list (quote a) (quote b)) | ||
| 295 | ( | ||
| 296 | vec![ | ||
| 297 | LeftPar, | ||
| 298 | Symbol("list"), | ||
| 299 | Quote, | ||
| 300 | Symbol("a"), | ||
| 301 | Quote, | ||
| 302 | Symbol("b"), | ||
| 303 | RightPar, | ||
| 304 | ], | ||
| 305 | vec![List(vec![Sym("list"), quote(Sym("a")), quote(Sym("b"))])], | ||
| 306 | ), | ||
| 307 | ]; | ||
| 308 | for (tokens, expected) in cases { | ||
| 309 | assert_eq!(parse(tokens.clone()), expected, "input: {tokens:?}"); | ||
| 310 | } | ||
| 311 | } | ||
| 312 | |||
| 313 | #[test] | 266 | #[test] |
| 314 | fn test_top_level() { | 267 | fn test_top_level() { |
| 315 | let cases = vec![ | 268 | let cases = vec![ |
| @@ -344,8 +297,6 @@ fn test_unexpected_right_par() { | |||
| 344 | vec![RightPar], | 297 | vec![RightPar], |
| 345 | vec![Number("1"), RightPar], | 298 | vec![Number("1"), RightPar], |
| 346 | vec![LeftPar, Symbol("a"), RightPar, RightPar], | 299 | vec![LeftPar, Symbol("a"), RightPar, RightPar], |
| 347 | vec![Quote, RightPar], | ||
| 348 | vec![LeftPar, Quote, Quote, RightPar], | ||
| 349 | ]; | 300 | ]; |
| 350 | for tokens in cases { | 301 | for tokens in cases { |
| 351 | assert_eq!( | 302 | assert_eq!( |
| @@ -373,24 +324,6 @@ fn test_unclosed_left_par() { | |||
| 373 | } | 324 | } |
| 374 | 325 | ||
| 375 | #[test] | 326 | #[test] |
| 376 | fn test_unexpected_eof_after_quote() { | ||
| 377 | let cases = vec![ | ||
| 378 | vec![Quote], | ||
| 379 | vec![Quote, Quote], | ||
| 380 | vec![Symbol("a"), Quote], | ||
| 381 | vec![LeftPar, Quote], | ||
| 382 | vec![Quote, LeftPar, Quote], | ||
| 383 | ]; | ||
| 384 | for tokens in cases { | ||
| 385 | assert_eq!( | ||
| 386 | parse_err(tokens.clone()), | ||
| 387 | Error::UnexpectedEof, | ||
| 388 | "input: {tokens:?}" | ||
| 389 | ); | ||
| 390 | } | ||
| 391 | } | ||
| 392 | |||
| 393 | #[test] | ||
| 394 | fn test_invalid_integer() { | 327 | fn test_invalid_integer() { |
| 395 | let cases = vec![ | 328 | let cases = vec![ |
| 396 | "99999999999999999999", | 329 | "99999999999999999999", |
| @@ -553,63 +486,6 @@ fn test_span_empty_list_covers_parens() { | |||
| 553 | } | 486 | } |
| 554 | 487 | ||
| 555 | #[test] | 488 | #[test] |
| 556 | fn test_span_quote_atom() { | ||
| 557 | // 'x -> outer = [0..2]; Sym(quote) = [0..1]; x = [1..2] | ||
| 558 | let q = sp((1, 0, 0), (1, 1, 1)); | ||
| 559 | let x = sp((1, 1, 1), (1, 2, 2)); | ||
| 560 | let tokens = vec![tsp(Quote, q), tsp(Symbol("x"), x)]; | ||
| 561 | let prog = parse_sp(tokens.clone()); | ||
| 562 | |||
| 563 | assert_eq!(prog[0].span, sp((1, 0, 0), (1, 2, 2)), "input: {tokens:?}"); | ||
| 564 | |||
| 565 | if let Expr::List(items) = &prog[0].inner { | ||
| 566 | assert_eq!( | ||
| 567 | items[0].inner, | ||
| 568 | Expr::Atom(Atom::Symbol(Rc::from("quote"))), | ||
| 569 | "input: {tokens:?}", | ||
| 570 | ); | ||
| 571 | assert_eq!(items[0].span, q, "input: {tokens:?}"); | ||
| 572 | assert_eq!(items[1].span, x, "input: {tokens:?}"); | ||
| 573 | } else { | ||
| 574 | panic!("expected list, input: {tokens:?}"); | ||
| 575 | } | ||
| 576 | } | ||
| 577 | |||
| 578 | #[test] | ||
| 579 | fn test_span_quote_of_list() { | ||
| 580 | // '(quote x) | ||
| 581 | // 0 1 2..7 8 9 10 | ||
| 582 | let q = sp((1, 0, 0), (1, 1, 1)); | ||
| 583 | let lp = sp((1, 1, 1), (1, 2, 2)); | ||
| 584 | let q_sym = sp((1, 2, 2), (1, 7, 7)); | ||
| 585 | let x_sym = sp((1, 8, 8), (1, 9, 9)); | ||
| 586 | let rp = sp((1, 9, 9), (1, 10, 10)); | ||
| 587 | |||
| 588 | let tokens = vec![ | ||
| 589 | tsp(Quote, q), | ||
| 590 | tsp(LeftPar, lp), | ||
| 591 | tsp(Symbol("quote"), q_sym), | ||
| 592 | tsp(Symbol("x"), x_sym), | ||
| 593 | tsp(RightPar, rp), | ||
| 594 | ]; | ||
| 595 | let prog = parse_sp(tokens.clone()); | ||
| 596 | |||
| 597 | let outer = &prog[0]; // (quote (quote x)) | ||
| 598 | assert_eq!(outer.span, sp((1, 0, 0), (1, 10, 10)), "input: {tokens:?}"); | ||
| 599 | |||
| 600 | if let Expr::List(items) = &outer.inner { | ||
| 601 | assert_eq!(items[0].span, q, "input: {tokens:?}"); | ||
| 602 | assert_eq!( | ||
| 603 | items[1].span, | ||
| 604 | sp((1, 1, 1), (1, 10, 10)), | ||
| 605 | "input: {tokens:?}" | ||
| 606 | ); | ||
| 607 | } else { | ||
| 608 | panic!("expected list, input: {tokens:?}"); | ||
| 609 | } | ||
| 610 | } | ||
| 611 | |||
| 612 | #[test] | ||
| 613 | fn test_error_span_unexpected_right_par() { | 489 | fn test_error_span_unexpected_right_par() { |
| 614 | let s = sp((1, 5, 5), (1, 6, 6)); | 490 | let s = sp((1, 5, 5), (1, 6, 6)); |
| 615 | let tokens = vec![tsp(RightPar, s)]; | 491 | let tokens = vec![tsp(RightPar, s)]; |
| @@ -664,16 +540,6 @@ fn test_error_span_unclosed_left_par_nested() { | |||
| 664 | } | 540 | } |
| 665 | 541 | ||
| 666 | #[test] | 542 | #[test] |
| 667 | fn test_error_span_unexpected_eof_after_quote() { | ||
| 668 | let q = sp((1, 0, 0), (1, 1, 1)); | ||
| 669 | let tokens = vec![tsp(Quote, q)]; | ||
| 670 | let err = parse_sp_err(tokens.clone()); | ||
| 671 | |||
| 672 | assert_eq!(err.inner, Error::UnexpectedEof, "input: {tokens:?}"); | ||
| 673 | assert_eq!(err.span, q, "input: {tokens:?}"); | ||
| 674 | } | ||
| 675 | |||
| 676 | #[test] | ||
| 677 | fn test_error_span_unclosed_string() { | 543 | fn test_error_span_unclosed_string() { |
| 678 | let s = sp((1, 0, 0), (1, 7, 7)); | 544 | let s = sp((1, 0, 0), (1, 7, 7)); |
| 679 | let tokens = vec![tsp(UnclosedString("oops"), s)]; | 545 | let tokens = vec![tsp(UnclosedString("oops"), s)]; |
diff --git a/compiler/src/lex/lexer.rs b/compiler/src/lex/lexer.rs index 801d382..6efbca0 100644 --- a/compiler/src/lex/lexer.rs +++ b/compiler/src/lex/lexer.rs | |||
| @@ -4,7 +4,7 @@ use crate::{ | |||
| 4 | }; | 4 | }; |
| 5 | 5 | ||
| 6 | fn is_terminator(ch: char) -> bool { | 6 | fn is_terminator(ch: char) -> bool { |
| 7 | ch.is_whitespace() || matches!(ch, '(' | ')' | '\'' | '"' | ';') | 7 | ch.is_whitespace() || matches!(ch, '(' | ')' | '"' | ';') |
| 8 | } | 8 | } |
| 9 | 9 | ||
| 10 | pub struct Lexer<'a> { | 10 | pub struct Lexer<'a> { |
| @@ -128,10 +128,6 @@ impl<'a> Iterator for Lexer<'a> { | |||
| 128 | self.consume(); | 128 | self.consume(); |
| 129 | Token::RightPar | 129 | Token::RightPar |
| 130 | } | 130 | } |
| 131 | '\'' => { | ||
| 132 | self.consume(); | ||
| 133 | Token::Quote | ||
| 134 | } | ||
| 135 | 131 | ||
| 136 | // Number | 132 | // Number |
| 137 | ch if ch.is_ascii_digit() | 133 | ch if ch.is_ascii_digit() |
diff --git a/compiler/src/lex/tests.rs b/compiler/src/lex/tests.rs index 2d872a2..d0ed658 100644 --- a/compiler/src/lex/tests.rs +++ b/compiler/src/lex/tests.rs | |||
| @@ -38,24 +38,6 @@ fn test_parens() { | |||
| 38 | } | 38 | } |
| 39 | 39 | ||
| 40 | #[test] | 40 | #[test] |
| 41 | fn test_quote() { | ||
| 42 | let cases = vec![ | ||
| 43 | ("'", vec![Quote]), | ||
| 44 | ("'a", vec![Quote, Symbol("a")]), | ||
| 45 | ("''a", vec![Quote, Quote, Symbol("a")]), | ||
| 46 | ("'()", vec![Quote, LeftPar, RightPar]), | ||
| 47 | ( | ||
| 48 | "'(1 2)", | ||
| 49 | vec![Quote, LeftPar, Number("1"), Number("2"), RightPar], | ||
| 50 | ), | ||
| 51 | ("(' )", vec![LeftPar, Quote, RightPar]), | ||
| 52 | ]; | ||
| 53 | for (code, tokens) in cases { | ||
| 54 | assert_eq!(tokenize(code), tokens); | ||
| 55 | } | ||
| 56 | } | ||
| 57 | |||
| 58 | #[test] | ||
| 59 | fn test_numbers() { | 41 | fn test_numbers() { |
| 60 | let cases = vec![ | 42 | let cases = vec![ |
| 61 | ("0", vec![Number("0")]), | 43 | ("0", vec![Number("0")]), |
| @@ -83,7 +65,6 @@ fn test_strings() { | |||
| 83 | (r#""hello""#, vec![String("hello")]), | 65 | (r#""hello""#, vec![String("hello")]), |
| 84 | (r#""hello world""#, vec![String("hello world")]), | 66 | (r#""hello world""#, vec![String("hello world")]), |
| 85 | (r#""(not a list)""#, vec![String("(not a list)")]), | 67 | (r#""(not a list)""#, vec![String("(not a list)")]), |
| 86 | (r#""'not a quote""#, vec![String("'not a quote")]), | ||
| 87 | (r#""; not a comment""#, vec![String("; not a comment")]), | 68 | (r#""; not a comment""#, vec![String("; not a comment")]), |
| 88 | (r#"" spaces ""#, vec![String(" spaces ")]), | 69 | (r#"" spaces ""#, vec![String(" spaces ")]), |
| 89 | ]; | 70 | ]; |
| @@ -96,7 +77,6 @@ fn test_strings() { | |||
| 96 | fn test_string_escapes() { | 77 | fn test_string_escapes() { |
| 97 | let cases = vec![ | 78 | let cases = vec![ |
| 98 | (r#""line\nbreak""#, vec![String(r"line\nbreak")]), | 79 | (r#""line\nbreak""#, vec![String(r"line\nbreak")]), |
| 99 | (r#""with \"quotes\"""#, vec![String(r#"with \"quotes\""#)]), | ||
| 100 | (r#""\\""#, vec![String(r"\\")]), | 80 | (r#""\\""#, vec![String(r"\\")]), |
| 101 | ("\"single\\\nline\"", vec![String("single\\\nline")]), | 81 | ("\"single\\\nline\"", vec![String("single\\\nline")]), |
| 102 | ]; | 82 | ]; |
| @@ -169,7 +149,6 @@ fn test_no_separators() { | |||
| 169 | ("(foo)", vec![LeftPar, Symbol("foo"), RightPar]), | 149 | ("(foo)", vec![LeftPar, Symbol("foo"), RightPar]), |
| 170 | ("(1)", vec![LeftPar, Number("1"), RightPar]), | 150 | ("(1)", vec![LeftPar, Number("1"), RightPar]), |
| 171 | ("(a)b", vec![LeftPar, Symbol("a"), RightPar, Symbol("b")]), | 151 | ("(a)b", vec![LeftPar, Symbol("a"), RightPar, Symbol("b")]), |
| 172 | ("'(a)", vec![Quote, LeftPar, Symbol("a"), RightPar]), | ||
| 173 | (r#"("s")"#, vec![LeftPar, String("s"), RightPar]), | 152 | (r#"("s")"#, vec![LeftPar, String("s"), RightPar]), |
| 174 | ]; | 153 | ]; |
| 175 | for (code, tokens) in cases { | 154 | for (code, tokens) in cases { |
| @@ -212,23 +191,6 @@ fn test_expressions() { | |||
| 212 | vec![LeftPar, Symbol("+"), Number("1"), Number("2"), RightPar], | 191 | vec![LeftPar, Symbol("+"), Number("1"), Number("2"), RightPar], |
| 213 | ), | 192 | ), |
| 214 | ( | 193 | ( |
| 215 | "(if (= x 0) 'zero 'nonzero)", | ||
| 216 | vec![ | ||
| 217 | LeftPar, | ||
| 218 | Symbol("if"), | ||
| 219 | LeftPar, | ||
| 220 | Symbol("="), | ||
| 221 | Symbol("x"), | ||
| 222 | Number("0"), | ||
| 223 | RightPar, | ||
| 224 | Quote, | ||
| 225 | Symbol("zero"), | ||
| 226 | Quote, | ||
| 227 | Symbol("nonzero"), | ||
| 228 | RightPar, | ||
| 229 | ], | ||
| 230 | ), | ||
| 231 | ( | ||
| 232 | r#"(print "hello, world")"#, | 194 | r#"(print "hello, world")"#, |
| 233 | vec![LeftPar, Symbol("print"), String("hello, world"), RightPar], | 195 | vec![LeftPar, Symbol("print"), String("hello, world"), RightPar], |
| 234 | ), | 196 | ), |
| @@ -248,17 +210,6 @@ fn test_expressions() { | |||
| 248 | RightPar, | 210 | RightPar, |
| 249 | ], | 211 | ], |
| 250 | ), | 212 | ), |
| 251 | ( | ||
| 252 | "'(1 2 3)", | ||
| 253 | vec![ | ||
| 254 | Quote, | ||
| 255 | LeftPar, | ||
| 256 | Number("1"), | ||
| 257 | Number("2"), | ||
| 258 | Number("3"), | ||
| 259 | RightPar, | ||
| 260 | ], | ||
| 261 | ), | ||
| 262 | ]; | 213 | ]; |
| 263 | for (code, tokens) in cases { | 214 | for (code, tokens) in cases { |
| 264 | assert_eq!(tokenize(code), tokens); | 215 | assert_eq!(tokenize(code), tokens); |
| @@ -367,17 +318,3 @@ fn test_span_after_comment() { | |||
| 367 | let s = spans("; cm\nfoo"); | 318 | let s = spans("; cm\nfoo"); |
| 368 | assert_eq!(s, vec![(Pos::new(2, 0, 5), Pos::new(2, 3, 8))]); | 319 | assert_eq!(s, vec![(Pos::new(2, 0, 5), Pos::new(2, 3, 8))]); |
| 369 | } | 320 | } |
| 370 | |||
| 371 | #[test] | ||
| 372 | fn test_span_after_quote() { | ||
| 373 | // 'hello | ||
| 374 | // 0123456 | ||
| 375 | let s = spans("'hello"); | ||
| 376 | assert_eq!( | ||
| 377 | s, | ||
| 378 | vec![ | ||
| 379 | (Pos::new(1, 0, 0), Pos::new(1, 1, 1)), | ||
| 380 | (Pos::new(1, 1, 1), Pos::new(1, 6, 6)) | ||
| 381 | ] | ||
| 382 | ); | ||
| 383 | } | ||
diff --git a/compiler/src/lex/token.rs b/compiler/src/lex/token.rs index 2d07885..910c8ff 100644 --- a/compiler/src/lex/token.rs +++ b/compiler/src/lex/token.rs | |||
| @@ -2,7 +2,6 @@ | |||
| 2 | pub enum Token<'a> { | 2 | pub enum Token<'a> { |
| 3 | LeftPar, | 3 | LeftPar, |
| 4 | RightPar, | 4 | RightPar, |
| 5 | Quote, | ||
| 6 | Number(&'a str), | 5 | Number(&'a str), |
| 7 | String(&'a str), | 6 | String(&'a str), |
| 8 | UnclosedString(&'a str), | 7 | UnclosedString(&'a str), |
