koatl 0.1.37__tar.gz → 0.1.38__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (125) hide show
  1. {koatl-0.1.37 → koatl-0.1.38}/Cargo.lock +1 -1
  2. {koatl-0.1.37 → koatl-0.1.38}/PKG-INFO +1 -1
  3. {koatl-0.1.37 → koatl-0.1.38}/koatl/Cargo.toml +1 -1
  4. {koatl-0.1.37 → koatl-0.1.38}/koatl/python/koatl/prelude/__init__.tl +1 -0
  5. {koatl-0.1.37 → koatl-0.1.38}/koatl/python/koatl/runtime/__init__.py +0 -3
  6. {koatl-0.1.37 → koatl-0.1.38/koatl}/python/koatl/std/alg/base.tl +2 -0
  7. {koatl-0.1.37 → koatl-0.1.38/koatl}/python/koatl/std/iter.tl +2 -1
  8. koatl-0.1.37/koatl/python/koatl/runtime/classes.py → koatl-0.1.38/koatl/python/koatl/std/trait.py +1 -1
  9. {koatl-0.1.37 → koatl-0.1.38}/koatl-core/parser/src/lexer.rs +176 -94
  10. {koatl-0.1.37 → koatl-0.1.38}/python/koatl/prelude/__init__.tl +1 -0
  11. {koatl-0.1.37 → koatl-0.1.38}/python/koatl/runtime/__init__.py +0 -3
  12. {koatl-0.1.37/koatl → koatl-0.1.38}/python/koatl/std/alg/base.tl +2 -0
  13. {koatl-0.1.37/koatl → koatl-0.1.38}/python/koatl/std/iter.tl +2 -1
  14. koatl-0.1.37/python/koatl/runtime/classes.py → koatl-0.1.38/python/koatl/std/trait.py +1 -1
  15. {koatl-0.1.37 → koatl-0.1.38}/Cargo.toml +0 -0
  16. {koatl-0.1.37 → koatl-0.1.38}/README.md +0 -0
  17. {koatl-0.1.37 → koatl-0.1.38}/koatl/.github/workflows/CI.yml +0 -0
  18. {koatl-0.1.37 → koatl-0.1.38}/koatl/.gitignore +0 -0
  19. {koatl-0.1.37 → koatl-0.1.38}/koatl/LICENSE +0 -0
  20. {koatl-0.1.37 → koatl-0.1.38}/koatl/README.md +0 -0
  21. {koatl-0.1.37 → koatl-0.1.38}/koatl/python/koatl/__init__.py +0 -0
  22. {koatl-0.1.37 → koatl-0.1.38}/koatl/python/koatl/__main__.py +0 -0
  23. {koatl-0.1.37 → koatl-0.1.38}/koatl/python/koatl/cli.py +0 -0
  24. {koatl-0.1.37 → koatl-0.1.38}/koatl/python/koatl/notebook/__init__.py +0 -0
  25. {koatl-0.1.37 → koatl-0.1.38}/koatl/python/koatl/notebook/magic.py +0 -0
  26. {koatl-0.1.37 → koatl-0.1.38}/koatl/python/koatl/runtime/helpers.py +0 -0
  27. {koatl-0.1.37 → koatl-0.1.38}/koatl/python/koatl/runtime/meta_finder.py +0 -0
  28. {koatl-0.1.37 → koatl-0.1.38}/koatl/python/koatl/runtime/record.py +0 -0
  29. {koatl-0.1.37 → koatl-0.1.38}/koatl/python/koatl/runtime/virtual.py +0 -0
  30. {koatl-0.1.37 → koatl-0.1.38}/koatl/python/koatl/std/alg/__init__.tl +0 -0
  31. {koatl-0.1.37 → koatl-0.1.38}/koatl/python/koatl/std/alg/async.tl +0 -0
  32. {koatl-0.1.37 → koatl-0.1.38}/koatl/python/koatl/std/alg/env.tl +0 -0
  33. {koatl-0.1.37 → koatl-0.1.38}/koatl/python/koatl/std/alg/memo.tl +0 -0
  34. {koatl-0.1.37 → koatl-0.1.38}/koatl/python/koatl/std/alg/result.tl +0 -0
  35. {koatl-0.1.37 → koatl-0.1.38}/koatl/python/koatl/std/data/__init__.tl +0 -0
  36. {koatl-0.1.37 → koatl-0.1.38}/koatl/python/koatl/std/data/list.tl +0 -0
  37. {koatl-0.1.37 → koatl-0.1.38}/koatl/python/koatl/std/io.tl +0 -0
  38. {koatl-0.1.37 → koatl-0.1.38}/koatl/python/koatl/std/lazy_module.tl +0 -0
  39. {koatl-0.1.37 → koatl-0.1.38}/koatl/python/koatl/std/re.tl +0 -0
  40. {koatl-0.1.37 → koatl-0.1.38}/koatl/requirements.txt +0 -0
  41. {koatl-0.1.37 → koatl-0.1.38}/koatl/src/emit_py.rs +0 -0
  42. {koatl-0.1.37 → koatl-0.1.38}/koatl/src/lib.rs +0 -0
  43. {koatl-0.1.37 → koatl-0.1.38}/koatl/tests/e2e/base/containers.tl +0 -0
  44. {koatl-0.1.37 → koatl-0.1.38}/koatl/tests/e2e/base/data.txt +0 -0
  45. {koatl-0.1.37 → koatl-0.1.38}/koatl/tests/e2e/base/decorators.tl +0 -0
  46. {koatl-0.1.37 → koatl-0.1.38}/koatl/tests/e2e/base/destructure-for-and-fn.tl +0 -0
  47. {koatl-0.1.37 → koatl-0.1.38}/koatl/tests/e2e/base/destructure.tl +0 -0
  48. {koatl-0.1.37 → koatl-0.1.38}/koatl/tests/e2e/base/escape_ident.tl +0 -0
  49. {koatl-0.1.37 → koatl-0.1.38}/koatl/tests/e2e/base/fstr.tl +0 -0
  50. {koatl-0.1.37 → koatl-0.1.38}/koatl/tests/e2e/base/functions.tl +0 -0
  51. {koatl-0.1.37 → koatl-0.1.38}/koatl/tests/e2e/base/generator.tl +0 -0
  52. {koatl-0.1.37 → koatl-0.1.38}/koatl/tests/e2e/base/if_expr.tl +0 -0
  53. {koatl-0.1.37 → koatl-0.1.38}/koatl/tests/e2e/base/loops.tl +0 -0
  54. {koatl-0.1.37 → koatl-0.1.38}/koatl/tests/e2e/base/match.tl +0 -0
  55. {koatl-0.1.37 → koatl-0.1.38}/koatl/tests/e2e/base/nary-list.tl +0 -0
  56. {koatl-0.1.37 → koatl-0.1.38}/koatl/tests/e2e/base/placeholder.tl +0 -0
  57. {koatl-0.1.37 → koatl-0.1.38}/koatl/tests/e2e/base/precedence.tl +0 -0
  58. {koatl-0.1.37 → koatl-0.1.38}/koatl/tests/e2e/base/record.tl +0 -0
  59. {koatl-0.1.37 → koatl-0.1.38}/koatl/tests/e2e/base/scopes.tl +0 -0
  60. {koatl-0.1.37 → koatl-0.1.38}/koatl/tests/e2e/base/semantic_whitespace.tl +0 -0
  61. {koatl-0.1.37 → koatl-0.1.38}/koatl/tests/e2e/base/short_circuit.tl +0 -0
  62. {koatl-0.1.37 → koatl-0.1.38}/koatl/tests/e2e/base/with.tl +0 -0
  63. {koatl-0.1.37 → koatl-0.1.38}/koatl/tests/e2e/prelude/async.tl +0 -0
  64. {koatl-0.1.37 → koatl-0.1.38}/koatl/tests/e2e/prelude/aug_assign.tl +0 -0
  65. {koatl-0.1.37/koatl/tests/e2e/base → koatl-0.1.38/koatl/tests/e2e/prelude}/coal.tl +0 -0
  66. {koatl-0.1.37 → koatl-0.1.38}/koatl/tests/e2e/prelude/env.tl +0 -0
  67. {koatl-0.1.37/koatl/tests/e2e/base → koatl-0.1.38/koatl/tests/e2e/prelude}/imports.tl +0 -0
  68. {koatl-0.1.37 → koatl-0.1.38}/koatl/tests/e2e/prelude/iterables.tl +0 -0
  69. {koatl-0.1.37 → koatl-0.1.38}/koatl/tests/e2e/prelude/list.tl +0 -0
  70. {koatl-0.1.37 → koatl-0.1.38}/koatl/tests/e2e/prelude/memo.tl +0 -0
  71. {koatl-0.1.37 → koatl-0.1.38}/koatl/tests/e2e/prelude/result.tl +0 -0
  72. {koatl-0.1.37 → koatl-0.1.38}/koatl/tests/e2e/prelude/slice.tl +0 -0
  73. {koatl-0.1.37 → koatl-0.1.38}/koatl/tests/e2e/prelude/try.tl +0 -0
  74. {koatl-0.1.37 → koatl-0.1.38}/koatl/tests/e2e/prelude/virtual.tl +0 -0
  75. {koatl-0.1.37 → koatl-0.1.38}/koatl/tests/e2e/util/__init__.py +0 -0
  76. {koatl-0.1.37 → koatl-0.1.38}/koatl/tests/e2e/util/module0.tl +0 -0
  77. {koatl-0.1.37 → koatl-0.1.38}/koatl/tests/e2e/util/module1.tl +0 -0
  78. {koatl-0.1.37 → koatl-0.1.38}/koatl/tests/e2e/util/module2.tl +0 -0
  79. {koatl-0.1.37 → koatl-0.1.38}/koatl/tests/parse/arith.tl +0 -0
  80. {koatl-0.1.37 → koatl-0.1.38}/koatl/tests/parse/assign.tl +0 -0
  81. {koatl-0.1.37 → koatl-0.1.38}/koatl/tests/parse/block-comments.tl +0 -0
  82. {koatl-0.1.37 → koatl-0.1.38}/koatl/tests/parse/deco.tl +0 -0
  83. {koatl-0.1.37 → koatl-0.1.38}/koatl/tests/parse/func.tl +0 -0
  84. {koatl-0.1.37 → koatl-0.1.38}/koatl/tests/parse/matches.tl +0 -0
  85. {koatl-0.1.37 → koatl-0.1.38}/koatl/tests/test_e2e.py +0 -0
  86. {koatl-0.1.37 → koatl-0.1.38}/koatl/tests/test_parse.py +0 -0
  87. {koatl-0.1.37 → koatl-0.1.38}/koatl-core/Cargo.toml +0 -0
  88. {koatl-0.1.37 → koatl-0.1.38}/koatl-core/parser/Cargo.toml +0 -0
  89. {koatl-0.1.37 → koatl-0.1.38}/koatl-core/parser/src/ast.rs +0 -0
  90. {koatl-0.1.37 → koatl-0.1.38}/koatl-core/parser/src/lib.rs +0 -0
  91. {koatl-0.1.37 → koatl-0.1.38}/koatl-core/parser/src/parser.rs +0 -0
  92. {koatl-0.1.37 → koatl-0.1.38}/koatl-core/parser/src/util.rs +0 -0
  93. {koatl-0.1.37 → koatl-0.1.38}/koatl-core/src/inference.rs +0 -0
  94. {koatl-0.1.37 → koatl-0.1.38}/koatl-core/src/lib.rs +0 -0
  95. {koatl-0.1.37 → koatl-0.1.38}/koatl-core/src/main.rs +0 -0
  96. {koatl-0.1.37 → koatl-0.1.38}/koatl-core/src/parse_timer.rs +0 -0
  97. {koatl-0.1.37 → koatl-0.1.38}/koatl-core/src/parser.rs +0 -0
  98. {koatl-0.1.37 → koatl-0.1.38}/koatl-core/src/py/ast.rs +0 -0
  99. {koatl-0.1.37 → koatl-0.1.38}/koatl-core/src/py/emit.rs +0 -0
  100. {koatl-0.1.37 → koatl-0.1.38}/koatl-core/src/py/mod.rs +0 -0
  101. {koatl-0.1.37 → koatl-0.1.38}/koatl-core/src/py/util.rs +0 -0
  102. {koatl-0.1.37 → koatl-0.1.38}/koatl-core/src/resolve_scopes.rs +0 -0
  103. {koatl-0.1.37 → koatl-0.1.38}/koatl-core/src/transform.rs +0 -0
  104. {koatl-0.1.37 → koatl-0.1.38}/koatl-core/src/types.rs +0 -0
  105. {koatl-0.1.37 → koatl-0.1.38}/koatl-core/src/util.rs +0 -0
  106. {koatl-0.1.37 → koatl-0.1.38}/pyproject.toml +0 -0
  107. {koatl-0.1.37 → koatl-0.1.38}/python/koatl/__init__.py +0 -0
  108. {koatl-0.1.37 → koatl-0.1.38}/python/koatl/__main__.py +0 -0
  109. {koatl-0.1.37 → koatl-0.1.38}/python/koatl/cli.py +0 -0
  110. {koatl-0.1.37 → koatl-0.1.38}/python/koatl/notebook/__init__.py +0 -0
  111. {koatl-0.1.37 → koatl-0.1.38}/python/koatl/notebook/magic.py +0 -0
  112. {koatl-0.1.37 → koatl-0.1.38}/python/koatl/runtime/helpers.py +0 -0
  113. {koatl-0.1.37 → koatl-0.1.38}/python/koatl/runtime/meta_finder.py +0 -0
  114. {koatl-0.1.37 → koatl-0.1.38}/python/koatl/runtime/record.py +0 -0
  115. {koatl-0.1.37 → koatl-0.1.38}/python/koatl/runtime/virtual.py +0 -0
  116. {koatl-0.1.37 → koatl-0.1.38}/python/koatl/std/alg/__init__.tl +0 -0
  117. {koatl-0.1.37 → koatl-0.1.38}/python/koatl/std/alg/async.tl +0 -0
  118. {koatl-0.1.37 → koatl-0.1.38}/python/koatl/std/alg/env.tl +0 -0
  119. {koatl-0.1.37 → koatl-0.1.38}/python/koatl/std/alg/memo.tl +0 -0
  120. {koatl-0.1.37 → koatl-0.1.38}/python/koatl/std/alg/result.tl +0 -0
  121. {koatl-0.1.37 → koatl-0.1.38}/python/koatl/std/data/__init__.tl +0 -0
  122. {koatl-0.1.37 → koatl-0.1.38}/python/koatl/std/data/list.tl +0 -0
  123. {koatl-0.1.37 → koatl-0.1.38}/python/koatl/std/io.tl +0 -0
  124. {koatl-0.1.37 → koatl-0.1.38}/python/koatl/std/lazy_module.tl +0 -0
  125. {koatl-0.1.37 → koatl-0.1.38}/python/koatl/std/re.tl +0 -0
@@ -99,7 +99,7 @@ checksum = "f4c7245a08504955605670dbf141fceab975f15ca21570696aebe9d2e71576bd"
99
99
 
100
100
  [[package]]
101
101
  name = "koatl"
102
- version = "0.1.37"
102
+ version = "0.1.38"
103
103
  dependencies = [
104
104
  "ariadne",
105
105
  "koatl-core",
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: koatl
3
- Version: 0.1.37
3
+ Version: 0.1.38
4
4
  Classifier: Programming Language :: Rust
5
5
  Classifier: Programming Language :: Python :: Implementation :: CPython
6
6
  Classifier: Programming Language :: Python :: Implementation :: PyPy
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "koatl"
3
- version = "0.1.37"
3
+ version = "0.1.38"
4
4
  edition = "2021"
5
5
 
6
6
  [lib]
@@ -43,6 +43,7 @@ Extension.method(str, "search")& (str, regex) => Pattern(regex).search(str)
43
43
 
44
44
  import koatl.std.lazy_module.LazyModule
45
45
 
46
+ export import koatl.std.trait.*
46
47
  export Result, Ok, Err, Env, Memo, Async, AsyncMemo
47
48
  export std = LazyModule("koatl.std")
48
49
 
@@ -19,7 +19,6 @@ meta_finder.install_hook()
19
19
  from .virtual import *
20
20
  from .record import *
21
21
  from .helpers import *
22
- from .classes import *
23
22
 
24
23
 
25
24
  def dummy(name):
@@ -52,7 +51,6 @@ __tl__ = SimpleNamespace(
52
51
  **{name: helpers.__dict__[name] for name in helpers.__all__},
53
52
  **{name: record.__dict__[name] for name in record.__all__},
54
53
  **{name: virtual.__dict__[name] for name in virtual.__all__},
55
- **{name: classes.__dict__[name] for name in classes.__all__},
56
54
  )
57
55
 
58
56
 
@@ -61,5 +59,4 @@ __all__ = [
61
59
  *helpers.__all__,
62
60
  *record.__all__,
63
61
  *virtual.__all__,
64
- *classes.__all__,
65
62
  ]
@@ -1,3 +1,5 @@
1
+ import koatl.std.trait.Trait
2
+
1
3
  export Monad = class(Trait):
2
4
  """
3
5
  Abstract base class for monads.
@@ -1,7 +1,8 @@
1
1
  import itertools
2
2
  import builtins
3
- import .alg.(Traversable, Ok, Err, Memo, Async, AsyncMemo, Result)
4
3
 
4
+ import koatl.std.trait.Trait
5
+ import .alg.(Traversable, Ok, Err, Memo, Async, AsyncMemo, Result)
5
6
 
6
7
  export Iterable = class(Traversable, Trait):
7
8
  iter = Trait.abstract& self => None
@@ -40,7 +40,7 @@ class MappingMeta(type):
40
40
  return [(k, v) for k, v in inspect.getmembers(self) if not k.startswith("_")]
41
41
 
42
42
 
43
- # A utility base class to inherit from to enable __getitem__ method lookup on types and thus destructuring.
43
+ # A utility base class to inherit from that implements Mapping on the type and thus destructuring.
44
44
  class Class(metaclass=MappingMeta):
45
45
  pass
46
46
 
@@ -541,6 +541,19 @@ where
541
541
  }
542
542
  }
543
543
 
544
+ fn parse_char(&mut self, c: char) -> TResult<'src, ()> {
545
+ let start = self.cursor();
546
+
547
+ if self.next() != Some(c) {
548
+ return Err(Rich::custom(
549
+ self.span_since(&start),
550
+ format!("expected '{c}'"),
551
+ ));
552
+ }
553
+
554
+ Ok(())
555
+ }
556
+
544
557
  fn parse_seq(&mut self, seq: &str) -> TResult<'src, ()> {
545
558
  let start = self.cursor();
546
559
 
@@ -579,78 +592,67 @@ where
579
592
  }
580
593
  }
581
594
 
582
- fn parse_regular_str(&mut self) -> TResult<'src, Spanned<Token<'src>>> {
583
- let start = self.cursor();
584
-
585
- self.parse_seq("\"")?;
586
-
587
- let mut s = String::new();
588
- loop {
589
- if self.try_parse(|x| x.parse_seq("\"")).is_ok() {
590
- return Ok(Token::Str(s).spanned(self.span_since(&start)));
591
- }
592
-
593
- if self.try_parse(|x| x.parse_newline()).is_ok() {
594
- return Err(Rich::custom(self.span_since(&start), "unterminated string"));
595
- }
596
-
597
- s.push(self.parse_escaped_char()?);
598
- }
599
- }
600
-
601
- fn parse_verbatim_str(&mut self) -> TResult<'src, Spanned<Token<'src>>> {
602
- let start = self.cursor();
603
-
604
- self.parse_seq("\"\"\"")?;
605
-
606
- let mut s = String::new();
607
- loop {
608
- if self.try_parse(|x| x.parse_seq("\"\"\"")).is_ok() {
609
- return Ok(Token::Str(s).spanned(self.span_since(&start)));
610
- }
611
-
612
- s.push(self.next().ok_or_else(|| {
613
- Rich::custom(self.span_since(&start), "unterminated verbatim string")
614
- })?);
615
- }
616
- }
617
-
618
- fn parse_fstr_inner(&mut self, ending: &str, verbatim: bool) -> TResult<'src, TokenList<'src>> {
595
+ fn parse_fstr_inner(
596
+ &mut self,
597
+ ending_char: char,
598
+ ending_char_count: u32,
599
+ verbatim: bool,
600
+ ) -> TResult<'src, TokenList<'src>> {
619
601
  let mut marker = self.cursor();
620
602
  let mut tokens = vec![];
621
603
  let mut current_str = String::new();
622
604
 
605
+ let mut seen_quotes = 0;
606
+ let mut ending_marker = self.input.save();
623
607
  loop {
624
- if self.look_ahead(|x| x.parse_seq(ending)).is_ok() {
625
- if tokens.len() == 0 {
626
- tokens.push(Token::FstrBegin(current_str).spanned(self.span_since(&marker)));
627
- } else {
628
- tokens.push(Token::FstrContinue(current_str).spanned(self.span_since(&marker)));
629
- }
630
-
631
- return Ok(TokenList(tokens));
608
+ if !verbatim && self.try_parse(|x| x.parse_newline()).is_ok() {
609
+ return Err(Rich::custom(
610
+ self.span_since(&marker),
611
+ "unterminated fstring",
612
+ ));
632
613
  }
633
614
 
634
- if !verbatim && self.try_parse(|x| x.parse_newline()).is_ok() {
615
+ let Some(peeked) = self.peek() else {
635
616
  return Err(Rich::custom(
636
617
  self.span_since(&marker),
637
618
  "unterminated fstring",
638
619
  ));
620
+ };
621
+
622
+ let mut save_marker = false;
623
+
624
+ if peeked == ending_char {
625
+ seen_quotes += 1;
626
+
627
+ if seen_quotes == ending_char_count {
628
+ if tokens.len() == 0 {
629
+ tokens
630
+ .push(Token::FstrBegin(current_str).spanned(self.span_since(&marker)));
631
+ } else {
632
+ tokens.push(
633
+ Token::FstrContinue(current_str).spanned(self.span_since(&marker)),
634
+ );
635
+ }
636
+
637
+ self.input.rewind(ending_marker);
638
+
639
+ return Ok(TokenList(tokens));
640
+ }
641
+ } else {
642
+ for _ in 0..seen_quotes {
643
+ current_str.push(ending_char);
644
+ }
645
+ seen_quotes = 0;
646
+ save_marker = true;
639
647
  }
640
648
 
641
649
  if self.try_parse(|x| x.parse_seq("{{")).is_ok() {
642
650
  current_str.push('{');
643
- continue;
644
- }
645
-
646
- if self.try_parse(|x| x.parse_seq("}}")).is_ok() {
651
+ } else if self.try_parse(|x| x.parse_seq("}}")).is_ok() {
647
652
  current_str.push('}');
648
- continue;
649
- } else if self.try_parse(|x| x.parse_seq("}")).is_ok() {
653
+ } else if self.try_parse(|x| x.parse_char('}')).is_ok() {
650
654
  return Err(Rich::custom(self.span_since(&marker), "unexpected '}'"));
651
- }
652
-
653
- if self.try_parse(|x| x.parse_seq("{")).is_ok() {
655
+ } else if self.try_parse(|x| x.parse_char('{')).is_ok() {
654
656
  if tokens.len() == 0 {
655
657
  tokens.push(Token::FstrBegin(current_str).spanned(self.span_since(&marker)));
656
658
  } else {
@@ -669,7 +671,7 @@ where
669
671
  let mut format_tokens = vec![];
670
672
  if self.try_parse(|x| x.parse_seq("!")).is_ok() {
671
673
  format_tokens.push(Token::Symbol("!").spanned(self.span_since(&marker)));
672
- format_tokens.extend(self.parse_fstr_inner("}", verbatim)?);
674
+ format_tokens.extend(self.parse_fstr_inner('}', 1, verbatim)?);
673
675
  }
674
676
 
675
677
  self.parse_seq("}")?;
@@ -690,72 +692,152 @@ where
690
692
  tokens.extend(format_tokens);
691
693
 
692
694
  marker = self.cursor();
695
+ } else {
696
+ let next_char = if verbatim {
697
+ self.next().ok_or_else(|| {
698
+ Rich::custom(self.span_since(&marker), "unterminated verbatim fstring")
699
+ })?
700
+ } else {
701
+ self.parse_escaped_char()?
702
+ };
693
703
 
694
- continue;
704
+ if save_marker {
705
+ current_str.push(next_char);
706
+ }
695
707
  }
696
708
 
697
- if verbatim {
698
- current_str.push(self.next().ok_or_else(|| {
699
- Rich::custom(self.span_since(&marker), "unterminated verbatim fstring")
700
- })?);
701
- } else {
702
- current_str.push(self.parse_escaped_char()?);
709
+ if save_marker {
710
+ ending_marker = self.input.save();
703
711
  }
704
712
  }
705
713
  }
706
714
 
707
- fn parse_fstr(&mut self, verbatim: bool) -> TResult<'src, TokenList<'src>> {
708
- if verbatim {
709
- self.parse_seq("f\"\"\"")?;
710
- let inner = self.parse_fstr_inner("\"\"\"", true)?;
711
- self.parse_seq("\"\"\"")?;
712
- return Ok(inner);
713
- } else {
714
- self.parse_seq("f\"")?;
715
- let inner = self.parse_fstr_inner("\"", false)?;
716
- self.parse_seq("\"")?;
717
- return Ok(inner);
715
+ fn parse_regular_str(&mut self, ch: char) -> TResult<'src, Spanned<Token<'src>>> {
716
+ let start = self.cursor();
717
+ let mut quote_count = 0;
718
+
719
+ let mut verbatim = self.try_parse(|x| x.parse_char('r')).is_ok();
720
+
721
+ while self.try_parse(|x| x.parse_char(ch)).is_ok() {
722
+ quote_count += 1;
723
+ }
724
+
725
+ if quote_count == 2 {
726
+ return Ok(Token::Str(String::new()).spanned(self.span_since(&start)));
727
+ }
728
+
729
+ verbatim |= quote_count >= 3;
730
+
731
+ let mut seen_quotes = 0;
732
+ let mut s = String::new();
733
+ loop {
734
+ if !verbatim && self.try_parse(|x| x.parse_newline()).is_ok() {
735
+ return Err(Rich::custom(self.span_since(&start), "unterminated string"));
736
+ }
737
+
738
+ let Some(peeked) = self.peek() else {
739
+ return Err(Rich::custom(self.span_since(&start), "unterminated string"));
740
+ };
741
+
742
+ let mut push_next = false;
743
+ if peeked == ch {
744
+ seen_quotes += 1;
745
+
746
+ if seen_quotes == quote_count {
747
+ self.next();
748
+ return Ok(Token::Str(s).spanned(self.span_since(&start)));
749
+ }
750
+ } else {
751
+ for _ in 0..seen_quotes {
752
+ s.push(ch);
753
+ }
754
+ seen_quotes = 0;
755
+ push_next = true;
756
+ }
757
+
758
+ let next_char = if verbatim {
759
+ self.next().unwrap()
760
+ } else {
761
+ self.parse_escaped_char()?
762
+ };
763
+
764
+ if push_next {
765
+ s.push(next_char);
766
+ }
718
767
  }
719
768
  }
720
769
 
721
- fn parse_str_start(&mut self) -> TResult<'src, ()> {
770
+ fn parse_fstr(&mut self, ch: char) -> TResult<'src, TokenList<'src>> {
722
771
  let start = self.cursor();
772
+ let mut verbatim = self.try_parse(|x| x.parse_seq("r")).is_ok();
723
773
 
724
- if self.try_parse(|x| x.parse_seq("f\"")).is_ok() {
725
- return Ok(());
774
+ self.parse_seq("f")?;
775
+ let mut quote_count = 0;
776
+
777
+ while self.try_parse(|x| x.parse_char(ch)).is_ok() {
778
+ quote_count += 1;
726
779
  }
727
780
 
728
- if self.try_parse(|x| x.parse_seq("\"")).is_ok() {
729
- return Ok(());
781
+ if quote_count == 2 {
782
+ return Ok(TokenList(vec![
783
+ Token::FstrBegin(String::new()).spanned(self.span_since(&start)),
784
+ ]));
730
785
  }
731
786
 
732
- Err(Rich::custom(
733
- self.span_since(&start),
734
- "expected string start",
735
- ))
736
- }
787
+ verbatim |= quote_count >= 3;
737
788
 
738
- fn parse_str(&mut self) -> TResult<'src, TokenList<'src>> {
739
- if self.look_ahead(|x| x.parse_seq("\"\"\"")).is_ok() {
740
- let token = self.parse_verbatim_str()?;
741
- return Ok(TokenList(vec![token]));
789
+ let inner = self.parse_fstr_inner(ch, quote_count, verbatim)?;
790
+
791
+ let start = self.cursor();
792
+
793
+ let mut close_quote_count = 0;
794
+ while self.try_parse(|x| x.parse_char(ch)).is_ok() {
795
+ close_quote_count += 1;
742
796
  }
743
797
 
744
- if self.look_ahead(|x| x.parse_seq("\"")).is_ok() {
745
- let token = self.parse_regular_str()?;
746
- return Ok(TokenList(vec![token]));
798
+ if quote_count != close_quote_count {
799
+ return Err(Rich::custom(
800
+ self.span_since(&start),
801
+ format!(
802
+ "expected {} closing quotes, found {}",
803
+ quote_count, close_quote_count
804
+ ),
805
+ ));
747
806
  }
748
807
 
749
- if self.look_ahead(|x| x.parse_seq("f\"\"\"")).is_ok() {
750
- let tokens = self.parse_fstr(true)?;
808
+ return Ok(inner);
809
+ }
810
+
811
+ fn parse_str_start(&mut self) -> TResult<'src, ()> {
812
+ let start = self.cursor();
813
+
814
+ let _ = self.try_parse(|x| x.parse_ident_or_token());
815
+ self.parse_char('"')?;
816
+
817
+ Ok(())
818
+ }
819
+
820
+ fn parse_str(&mut self) -> TResult<'src, TokenList<'src>> {
821
+ if self.look_ahead(|x| x.parse_seq("rf\"")).is_ok() {
822
+ let tokens = self.parse_fstr('"')?;
751
823
  return Ok(tokens);
752
824
  }
753
825
 
754
826
  if self.look_ahead(|x| x.parse_seq("f\"")).is_ok() {
755
- let tokens = self.parse_fstr(false)?;
827
+ let tokens = self.parse_fstr('"')?;
756
828
  return Ok(tokens);
757
829
  }
758
830
 
831
+ if self.look_ahead(|x| x.parse_seq("r\"")).is_ok() {
832
+ let token = self.parse_regular_str('"')?;
833
+ return Ok(TokenList(vec![token]));
834
+ }
835
+
836
+ if self.look_ahead(|x| x.parse_seq("\"")).is_ok() {
837
+ let token = self.parse_regular_str('"')?;
838
+ return Ok(TokenList(vec![token]));
839
+ }
840
+
759
841
  Err(Rich::custom(
760
842
  self.span_since(&self.cursor()),
761
843
  "expected string start",
@@ -43,6 +43,7 @@ Extension.method(str, "search")& (str, regex) => Pattern(regex).search(str)
43
43
 
44
44
  import koatl.std.lazy_module.LazyModule
45
45
 
46
+ export import koatl.std.trait.*
46
47
  export Result, Ok, Err, Env, Memo, Async, AsyncMemo
47
48
  export std = LazyModule("koatl.std")
48
49
 
@@ -19,7 +19,6 @@ meta_finder.install_hook()
19
19
  from .virtual import *
20
20
  from .record import *
21
21
  from .helpers import *
22
- from .classes import *
23
22
 
24
23
 
25
24
  def dummy(name):
@@ -52,7 +51,6 @@ __tl__ = SimpleNamespace(
52
51
  **{name: helpers.__dict__[name] for name in helpers.__all__},
53
52
  **{name: record.__dict__[name] for name in record.__all__},
54
53
  **{name: virtual.__dict__[name] for name in virtual.__all__},
55
- **{name: classes.__dict__[name] for name in classes.__all__},
56
54
  )
57
55
 
58
56
 
@@ -61,5 +59,4 @@ __all__ = [
61
59
  *helpers.__all__,
62
60
  *record.__all__,
63
61
  *virtual.__all__,
64
- *classes.__all__,
65
62
  ]
@@ -1,3 +1,5 @@
1
+ import koatl.std.trait.Trait
2
+
1
3
  export Monad = class(Trait):
2
4
  """
3
5
  Abstract base class for monads.
@@ -1,7 +1,8 @@
1
1
  import itertools
2
2
  import builtins
3
- import .alg.(Traversable, Ok, Err, Memo, Async, AsyncMemo, Result)
4
3
 
4
+ import koatl.std.trait.Trait
5
+ import .alg.(Traversable, Ok, Err, Memo, Async, AsyncMemo, Result)
5
6
 
6
7
  export Iterable = class(Traversable, Trait):
7
8
  iter = Trait.abstract& self => None
@@ -40,7 +40,7 @@ class MappingMeta(type):
40
40
  return [(k, v) for k, v in inspect.getmembers(self) if not k.startswith("_")]
41
41
 
42
42
 
43
- # A utility base class to inherit from to enable __getitem__ method lookup on types and thus destructuring.
43
+ # A utility base class to inherit from that implements Mapping on the type and thus destructuring.
44
44
  class Class(metaclass=MappingMeta):
45
45
  pass
46
46
 
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes