git @ Cat's Eye Technologies Pophery / master
Upgradify to Python 3. Chris Pressey 1 year, 4 days ago
5 changed file(s) with 113 addition(s) and 126 deletion(s). Raw diff Collapse all Expand all
0 #!/usr/bin/env pophery.py
1 # encoding: UTF-8
2 # data slots
3 (^0)this(0$)(^1)andthat(1$)(^2)P(2$)(^3)`!(3$)
4 # clipboard
5 (^%)(%$)
6 # accumulator
7 (^?)(?$)
8 # name of currently executing instruction slot
9 (^`!)!(`!$)
10 # begin program. put contents of slot `3` into accumulator
11 (^!)3(!$)SCAV
12 # select slot named by the accumulator and... put the
13 # contents of the accumulator in it. No, no! We need these
14 # to be two different names! Argh!
15 SV
16 # put contents of slot `3` into accumulator, again
17 3SCAV
18 2SC3SDSV
19 (^P)0(P$)SCAVO
20 (^Q)1(Q$)SCAVO
+0
-21
eg/branch.tranzy less more
0 #!/usr/bin/env pophery.py
1 # encoding: UTF-8
2 # data slots
3 (^0)this(0$)(^1)andthat(1$)(^2)P(2$)(^3)`!(3$)
4 # clipboard
5 (^%)(%$)
6 # accumulator
7 (^?)(?$)
8 # name of currently executing instruction slot
9 (^`!)!(`!$)
10 # begin program. put contents of slot `3` into accumulator
11 (^!)3(!$)SCAV
12 # select slot named by the accumulator and... put the
13 # contents of the accumulator in it. No, no! We need these
14 # to be two different names! Argh!
15 SV
16 # put contents of slot `3` into accumulator, again
17 3SCAV
18 2SC3SDSV
19 (^P)0(P$)SCAVO
20 (^Q)1(Q$)SCAVO
0 #!/usr/bin/env pophery.py
1 # -- encoding: UTF-8
2 (^?)Hello, ωorld
3 !
4 (?$)(^!)O(!$)OO
+0
-5
eg/hello.tranzy less more
0 #!/usr/bin/env pophery.py
1 # -- encoding: UTF-8
2 (^?)Hello, ωorld
3 !
4 (?$)(^!)O(!$)OO
0 #!/usr/bin/env python
0 #!/usr/bin/env python3
11 # -*- coding: utf-8 -*-
22
33 """
6363
6464 """
6565 def __init__(self, initial):
66 self.string = unicode(initial)
66 self.string = initial
6767
6868 def __str__(self):
6969 return self.__unicode__()
8484 return self.string.find(sub)
8585
8686 def set(self, string):
87 self.string = unicode(string)
87 self.string = string
8888
8989 def pos_left(self, locator, delta):
9090 """Return the 0-based position within this MutableString of the
9595 is changed.
9696
9797 >>> a = MutableString("Mom(*)entous")
98 >>> print a.pos_left("(*)", 0)
98 >>> print(a.pos_left("(*)", 0))
9999 3
100100
101101 """
114114 is changed.
115115
116116 >>> a = MutableString("Mom(*)entous")
117 >>> print a.pos_right("(*)", 0)
117 >>> print(a.pos_right("(*)", 0))
118118 6
119119
120120 """
131131
132132 >>> a = MutableString("Momentous")
133133 >>> a.insert_locator("(*)", 3)
134 >>> print str(a)
134 >>> print(str(a))
135135 Mom(*)entous
136136
137137 """
138 self.set(self[:pos] + unicode(locator) + self[pos:])
138 self.set(self[:pos] + locator + self[pos:])
139139
140140 def remove_locator(self, locator):
141141 """Remove the given locator from this string.
142142
143143 >>> a = MutableString("Mom(*)entous")
144144 >>> a.remove_locator("(*)")
145 >>> print str(a)
145 >>> print(str(a))
146146 Momentous
147147
148148 """
149 locator = unicode(locator)
150149 posl = self.pos_left(locator, 0)
151150 posr = self.pos_right(locator, 0)
152151 self.set(self[:posl] + self[posr:])
159158
160159 >>> a = MutableString("Mom(*)entous")
161160 >>> a.move_locator("(*)", +3)
162 >>> print str(a)
161 >>> print(str(a))
163162 Moment(*)ous
164163
165164 """
166 locator = unicode(locator)
167165 posl = self.pos_left(locator, 0)
168166 posr = self.pos_right(locator, 0)
169167 self.set(self[:posl] + self[posr:])
180178
181179 >>> a = MutableString("Mom(*)en(+)tous")
182180 >>> a.slide_locator("(*)", +1)
183 >>> print str(a)
181 >>> print(str(a))
184182 Mome(*)n(+)tous
185183 >>> a.slide_locator("(*)", -1)
186 >>> print str(a)
184 >>> print(str(a))
187185 Mom(*)en(+)tous
188186
189187 >>> b = MutableString("(-)Cassowary(+)")
190188 >>> b.slide_locator("(+)", +1)
191 >>> print str(b)
189 >>> print(str(b))
192190 (-)Cassowary(+)
193191 >>> b.slide_locator("(-)", -1)
194 >>> print str(b)
192 >>> print(str(b))
195193 (-)Cassowary(+)
196194
197195 >>> c = MutableString("Imb(+)r(%)oglio")
198196 >>> c.slide_locator("(+)", +1)
199 >>> print str(c)
197 >>> print(str(c))
200198 Imbr(+)(%)oglio
201199
202200 """
203 locator = unicode(locator)
204201 if delta == +1:
205202 matching = True
206203 target = self.pos_right(locator, 0)
230227 """Retrieve the substring between the two given locators.
231228
232229 >>> a = MutableString("This is (a)my string(b) you know.")
233 >>> print a.read("(a)", "(b)")
230 >>> print(a.read("(a)", "(b)"))
234231 my string
235232
236233 """
243240
244241 >>> a = MutableString("This is (a)my string(b) you know.")
245242 >>> a.update("(a)", "(b)", "crazy talk")
246 >>> print str(a)
243 >>> print(str(a))
247244 This is (a)crazy talk(b) you know.
248245
249246 """
250247 a = self.pos_right(left, 0)
251248 b = self.pos_left(right, 0)
252 self.set(self.string[:a] + unicode(string) + self.string[b:])
249 self.set(self.string[:a] + str(string) + self.string[b:])
253250
254251 def find_matching(self, pos):
255252 """Find the parenthesis which matches the parenthesis at the given
311308
312309 >>> a = SlottedString("This is (^a)my slot(a$) you know.")
313310 >>> a.update_slot('a', 'good stuff')
314 >>> print str(a)
311 >>> print(str(a))
315312 This is (^a)good stuff(a$) you know.
316313 >>> a.update_slot('z', 'bad stuff')
317314 Traceback (most recent call last):
319316 UndefinedLocatorError: (^z)
320317
321318 """
322 slot_name = unicode(slot_name)
323319 return self.read(u"(^%s)" % slot_name, u"(%s$)" % slot_name)
324320
325321 def read_slot_indirect(self, slot_name):
326322 """
327323 >>> p = SlottedString("...(^A)M(A$)...(^R)A(R$)...")
328 >>> print p.read_slot_indirect('R')
324 >>> print(p.read_slot_indirect('R'))
329325 M
330 >>> print p.read_slot_indirect('A')
326 >>> print(p.read_slot_indirect('A'))
331327 Traceback (most recent call last):
332328 ...
333329 UndefinedLocatorError: (^M)
334330
335331 """
336 slot_name = unicode(slot_name)
337332 slot_name = self.read_slot(slot_name)
338333 return self.read_slot(slot_name)
339334
342337
343338 >>> a = SlottedString("This is (^a)my slot(a$) you know.")
344339 >>> a.update_slot('a', 'good stuff')
345 >>> print str(a)
340 >>> print(str(a))
346341 This is (^a)good stuff(a$) you know.
347342 >>> a.update_slot('a', MutableString('mutable stuff'))
348 >>> print str(a)
343 >>> print(str(a))
349344 This is (^a)mutable stuff(a$) you know.
350345 >>> a.update_slot('z', 'bad stuff')
351346 Traceback (most recent call last):
353348 UndefinedLocatorError: (^z)
354349
355350 """
356 slot_name = unicode(slot_name)
357 string = unicode(string)
358351 return self.update(u"(^%s)" % slot_name, u"(%s$)" % slot_name, string)
359352
360353 def update_slot_indirect(self, slot_name, string):
361354 """
362355 >>> p = SlottedString("Dolphin(^A)M(A$)Dolphin(^R)A(R$)Dolphin")
363356 >>> p.update_slot_indirect('R', 'Porphyry')
364 >>> print str(p)
357 >>> print(str(p))
365358 Dolphin(^A)Porphyry(A$)Dolphin(^R)A(R$)Dolphin
366359
367360 """
372365 """
373366
374367 >>> a = SlottedString("(^G)?(G$) (^P)_(P$) (^`P)Q(`P$) (^`K)(^/)Madge(/$)(`K$)")
375 >>> print a.get_slot_name('M')
368 >>> print(a.get_slot_name('M'))
376369 M
377 >>> print a.get_slot_name('G')
370 >>> print(a.get_slot_name('G'))
378371 G
379 >>> print a.get_slot_name('P')
372 >>> print(a.get_slot_name('P'))
380373 Q
381 >>> print a.get_slot_name('K')
374 >>> print(a.get_slot_name('K'))
382375 Madge
383376
384377 """
385 slot_name = unicode(slot_name)
386378 name_slot = u"`%s" % slot_name
387379 try:
388380 slot_name = self.read_slot(name_slot)
394386 def strip_all_locators(self, content):
395387 """
396388 >>> p = Program('')
397 >>> print p.strip_all_locators('')
389 >>> print(p.strip_all_locators(''))
398390 None
399 >>> print p.strip_all_locators('X')
391 >>> print(p.strip_all_locators('X'))
400392 X
401 >>> print p.strip_all_locators('Well-tempered')
393 >>> print(p.strip_all_locators('Well-tempered'))
402394 Well-tempered
403 >>> print p.strip_all_locators('(^8)(^7)(7$)CAT(8$)')
395 >>> print(p.strip_all_locators('(^8)(^7)(7$)CAT(8$)'))
404396 CAT
405 >>> print p.strip_all_locators('(^8(beat))D')
397 >>> print(p.strip_all_locators('(^8(beat))D'))
406398 D
407 >>> print p.strip_all_locators('(^8)(^7)(7$)(8$)')
399 >>> print(p.strip_all_locators('(^8)(^7)(7$)(8$)'))
408400 None
409401
410402 """
429421
430422 >>> a = SlottedString("This is my (^a)slot(a$) (^b)y(b$)ou know.")
431423 >>> a.slide_slot('a', +1)
432 >>> print str(a)
424 >>> print(str(a))
433425 This is my s(^a)lot (a$)(^b)y(b$)ou know.
434426 >>> a.slide_slot('b', -1)
435 >>> print str(a)
427 >>> print(str(a))
436428 This is my s(^a)lot(^b) (a$)(b$)you know.
437429
438430 """
439 slot_name = unicode(slot_name)
440431 if delta > 0:
441432 self.slide_locator("(%s$)" % slot_name, delta)
442433 self.slide_locator("(^%s)" % slot_name, delta)
458449 done = False
459450 string = ''
460451 for line in file.readlines():
461 line = unicode(line, 'utf-8') # for now
462452 if line.endswith('\n'):
463453 line = line[:-1]
464454 if line.startswith('#'):
473463
474464 >>> p = Program("(^!)A(!$)B(^M)C(M$)D")
475465 >>> p.advance()
476 >>> print str(p)
466 >>> print(str(p))
477467 A(^!)B(!$)(^M)C(M$)D
478468 >>> p.advance()
479 >>> print str(p)
469 >>> print(str(p))
480470 AB(^!)(^M)C(!$)(M$)D
481471 >>> p.advance()
482 >>> print str(p)
472 >>> print(str(p))
483473 AB(^M)C(^!)(M$)D(!$)
484474 >>> p.advance()
485 >>> print str(p)
475 >>> print(str(p))
486476 AB(^M)C(M$)D(^!)(!$)
487477
488478 >>> p = Program("(^!)A(!$)(^Moo)(^Gar)(Gar$)B(Moo$)")
489479 >>> p.advance()
490 >>> print str(p)
480 >>> print(str(p))
491481 A(^!)(^Moo)(^Gar)(Gar$)B(!$)(Moo$)
492482 >>> p.advance()
493 >>> print str(p)
483 >>> print(str(p))
494484 A(^Moo)(^Gar)(Gar$)B(^!)(!$)(Moo$)
495485
496486 """
499489 def clean_instruction(self, instruction):
500490 """
501491 >>> p = Program('')
502 >>> print p.clean_instruction('')
492 >>> print(p.clean_instruction(''))
503493 None
504 >>> print p.clean_instruction('X')
494 >>> print(p.clean_instruction('X'))
505495 X
506 >>> print p.clean_instruction('Well-tempered')
496 >>> print(p.clean_instruction('Well-tempered'))
507497 W
508 >>> print p.clean_instruction('(^8)(^7)(7$)CAT(8$)')
498 >>> print(p.clean_instruction('(^8)(^7)(7$)CAT(8$)'))
509499 C
510 >>> print p.clean_instruction('(^8(beat))D')
500 >>> print(p.clean_instruction('(^8(beat))D'))
511501 D
512 >>> print p.clean_instruction('(^8)(^7)(7$)(8$)')
502 >>> print(p.clean_instruction('(^8)(^7)(7$)(8$)'))
513503 None
514504
515505 """
572562
573563 >>> p = Semantics("(^?)(?$)")
574564 >>> p.execute('0')
575 >>> print str(p)
565 >>> print(str(p))
576566 (^?)0(?$)
577567
578568 * X ("cut") erases (updates with the zero-length string) the selection.
579569
580570 >>> p = Semantics("(^/)hi(/$)")
581571 >>> p.execute('X')
582 >>> print str(p)
572 >>> print(str(p))
583573 (^/)(/$)
584574 >>> p = Semantics("(^`/)X(`/$)(^X)hi(X$)")
585575 >>> p.execute('X')
586 >>> print str(p)
576 >>> print(str(p))
587577 (^`/)X(`/$)(^X)(X$)
588578
589579 * C ("copy") updates the contents of the clipboard with the contents
591581
592582 >>> p = Semantics("(^/)hi(/$)(^%)lo(%$)")
593583 >>> p.execute('C')
594 >>> print str(p)
584 >>> print(str(p))
595585 (^/)hi(/$)(^%)hi(%$)
596586 >>> p = Semantics("(^/)hi(/$)(^J)lo(J$)(^`%)J(`%$)")
597587 >>> p.execute('C')
598 >>> print str(p)
588 >>> print(str(p))
599589 (^/)hi(/$)(^J)hi(J$)(^`%)J(`%$)
600590
601591 * V ("paste") updates the contents of the selection with the contents
603593
604594 >>> p = Semantics("(^/)hi(/$)(^%)lo(%$)")
605595 >>> p.execute('V')
606 >>> print str(p)
596 >>> print(str(p))
607597 (^/)lo(/$)(^%)lo(%$)
608598 >>> p = Semantics("(^C)lo(C$)(^J)hi(J$)(^`/)J(`/$)(^`%)C(`%$)")
609599 >>> p.execute('V')
610 >>> print str(p)
600 >>> print(str(p))
611601 (^C)lo(C$)(^J)lo(J$)(^`/)J(`/$)(^`%)C(`%$)
612602
613603 * S ("select") selects the contents of the slot indirect by the
615605
616606 >>> p = Semantics("(^/)foo(/$)(^?)A(?$)(^A)Some text.(A$)")
617607 >>> p.execute('S')
618 >>> print str(p)
608 >>> print(str(p))
619609 foo(^?)A(?$)(^A)(^/)Some text.(/$)(A$)
620610 >>> p = Semantics("(^`/)k(`/$)(^k)foo(k$)(^?)A(?$)(^A)Some text.(A$)")
621611 >>> p.execute('S')
622 >>> print str(p)
612 >>> print(str(p))
623613 (^`/)k(`/$)foo(^?)A(?$)(^A)(^k)Some text.(k$)(A$)
624614
625615 * A ("select all") selects the contents of the accumulator.
626616
627617 >>> p = Semantics("(^/)foo(/$)(^?)A(?$)(^A)Some text.(A$)")
628618 >>> p.execute('A')
629 >>> print str(p)
619 >>> print(str(p))
630620 foo(^?)(^/)A(/$)(?$)(^A)Some text.(A$)
631621 >>> p = Semantics("(^`/)r(`/$)(^r)foo(r$)(^?)A(?$)(^A)Some text.(A$)")
632622 >>> p.execute('A')
633 >>> print str(p)
623 >>> print(str(p))
634624 (^`/)r(`/$)foo(^?)(^r)A(r$)(?$)(^A)Some text.(A$)
635625
636626 * L ("left") slides the left locator of the selection leftward.
637627
638628 >>> p = Semantics("foo(^/)bar(/$)")
639629 >>> p.execute('L')
640 >>> print str(p)
630 >>> print(str(p))
641631 fo(^/)obar(/$)
642632 >>> p = Semantics("(^/)foobar(/$)")
643633 >>> p.execute('L')
644 >>> print str(p)
634 >>> print(str(p))
645635 (^/)foobar(/$)
646636 >>> p = Semantics("foo(^C)bar(C$)(^`/)C(`/$)")
647637 >>> p.execute('L')
648 >>> print str(p)
638 >>> print(str(p))
649639 fo(^C)obar(C$)(^`/)C(`/$)
650640 >>> p = Semantics("The last time I saw Charlie")
651641 >>> p.execute('L')
657647
658648 >>> p = Semantics("foo(^/)bar(/$)")
659649 >>> p.execute('R')
660 >>> print str(p)
650 >>> print(str(p))
661651 foob(^/)ar(/$)
662652 >>> p = Semantics("foo(^/)(/$)bar")
663653 >>> p.execute('R')
664 >>> print str(p)
654 >>> print(str(p))
665655 foo(^/)(/$)bar
666656 >>> p = Semantics("foo(^C)bar(C$)(^`/)C(`/$)")
667657 >>> p.execute('R')
668 >>> print str(p)
658 >>> print(str(p))
669659 foob(^C)ar(C$)(^`/)C(`/$)
670660 >>> p = Semantics("The last time I saw Charlie")
671661 >>> p.execute('R')
679669
680670 >>> p = Semantics("foo(^/)bar(/$)baz")
681671 >>> p.execute('E')
682 >>> print str(p)
672 >>> print(str(p))
683673 foobar(^/)(/$)baz
684674 >>> p = Semantics("foo(^a)b(^`/)a(`/$)r(a$)baz")
685675 >>> p.execute('E')
686 >>> print str(p)
676 >>> print(str(p))
687677 foob(^`/)a(`/$)r(^a)(a$)baz
688678 >>> p = Semantics("The last time I saw Charlie")
689679 >>> p.execute('E')
697687
698688 >>> p = Semantics("(^?)By hook or by crook, we will.(?$)(^%)ook(%$)")
699689 >>> p.execute('F')
700 >>> print str(p)
690 >>> print(str(p))
701691 (^?)By h(^/)ook(/$) or by crook, we will.(?$)(^%)ook(%$)
702692
703693 * D ("drag-and-drop") moves the selection to the accumulator.
704694
705695 >>> p = Semantics("(^/)hi(/$)(^?)lo(?$)")
706696 >>> p.execute('D')
707 >>> print str(p)
697 >>> print(str(p))
708698 hi(^?)(^/)hi(/$)(?$)
709699 >>> p = Semantics("(^C)lo(C$)(^J)hi(J$)(^`/)J(`/$)(^`?)C(`?$)")
710700 >>> p.execute('D')
711 >>> print str(p)
701 >>> print(str(p))
712702 (^C)(^J)hi(J$)(C$)hi(^`/)J(`/$)(^`?)C(`?$)
713703
714704 * I ("input") waits for a line to appear on standard input, then
715705 places it (sans newline) in the accumulator.
716706
717 >>> from StringIO import StringIO
707 >>> from io import StringIO
718708 >>> p = Semantics("(^?)(?$)")
719709 >>> p.input = StringIO(chr(10).join(["Line.", "Line!", "LINE!"]))
720710 >>> p.execute('I')
721 >>> print str(p)
711 >>> print(str(p))
722712 (^?)Line.(?$)
723713 >>> p.execute('I')
724 >>> print str(p)
714 >>> print(str(p))
725715 (^?)Line!(?$)
726716 >>> p.execute('I')
727 >>> print str(p)
717 >>> print(str(p))
728718 (^?)LINE!(?$)
729719 >>> p.execute('I')
730 >>> print str(p)
720 >>> print(str(p))
731721 (^?)(?$)
732722
733723 * O ("output") outputs the string in the accumulator to standard
736726 >>> p = Semantics("(^?)Hello, world!(?$)")
737727 >>> p.execute('O')
738728 Hello, world!
739 >>> print str(p)
729 >>> print(str(p))
740730 (^?)Hello, world!(?$)
741731
742732 Now we demonstrate some idioms.
746736
747737 >>> p = Semantics("(^0)data(0$)(^%)(%$)(^?)(?$)(^!)0(!$)SCAV")
748738 >>> p.run()
749 >>> print str(p)
739 >>> print(str(p))
750740 (^0)data(0$)(^%)data(%$)(^?)(^/)data(/$)(?$)0SCAV(^!)(!$)
751741
752742 New data, say the literal string 1, can be stored into slot 0 with:
753743
754744 >>> p = Semantics("(^0)data(0$)(^%)(%$)(^?)(?$)(^!)1(!$)AC0SV")
755745 >>> p.run()
756 >>> print str(p)
746 >>> print(str(p))
757747 (^0)(^/)1(/$)(0$)(^%)1(%$)(^?)0(?$)1AC0SV(^!)(!$)
758748
759749 To copy from any arbitrary slot (say 0) to another (say 1), we can say:
760750
761751 >>> p = Semantics("(^0)hi(0$)(^1)(1$)(^%)(%$)(^?)(?$)(^!)0(!$)SC1SV")
762752 >>> p.run()
763 >>> print str(p)
753 >>> print(str(p))
764754 (^0)hi(0$)(^1)(^/)hi(/$)(1$)(^%)hi(%$)(^?)1(?$)0SC1SV(^!)(!$)
765755
766756 Accessing a slot with a longer name, such as (^123)xyz(123$), can be
768758
769759 >>> p = Semantics("(^0)(0$)(^123)xyz(123$)(^%)(%$)(^?)(?$)(^!)1(!$)AC0SV2AC0SEV3AC0SEV0SCAVSD")
770760 >>> p.run()
771 >>> print str(p)
761 >>> print(str(p))
772762 (^0)123(0$)(^123)xyz(123$)(^%)123(%$)(^?)(^/)xyz(/$)(?$)1AC0SV2AC0SEV3AC0SEV0SCAVSD(^!)(!$)
773763
774764 To write data, say (^8)foo(8$), into a slot whose name is stored in
776766
777767 >>> p = Semantics("(^8)foo(8$)(^9)jim(9$)(^jim)(jim$)(^%)(%$)(^?)(?$)(^!)8(!$)SC9SDSV")
778768 >>> p.run()
779 >>> print str(p)
769 >>> print(str(p))
780770 (^8)foo(8$)(^9)jim(9$)(^jim)(^/)foo(/$)(jim$)(^%)foo(%$)(^?)jim(?$)8SC9SDSV(^!)(!$)
781771
782772 Finally, a complete, if simple, program:
851841 self.update_slot(self.get_slot_name('?'), new_selection)
852842 elif instruction == 'O':
853843 line = self.read_slot('?') + "\n"
854 try:
855 self.output.write(line.encode('UTF-8'))
856 except UnicodeEncodeError:
857 self.output.write(line.encode('ascii', 'xmlcharrefreplace'))
844 self.output.write(line)
858845 elif instruction == 'I':
859846 text = self.input.readline()
860847 if text.endswith('\n'):
895882 super(TracedProgram, self).__init__(initial)
896883
897884 def run(self):
898 print "[%s]" % str(self)
885 print("[%s]" % str(self))
899886 super(TracedProgram, self).run()
900887
901888 def step(self):
902889 result = super(TracedProgram, self).step()
903890 if result:
904 print "[%s]" % str(self)
891 print("[%s]" % str(self))
905892 return result
906893
907894
923910 (options, args) = optparser.parse_args(argv[1:])
924911 exit_code = None
925912 if options.show_license:
926 print sys.argv[0]
927 print __doc__
928 print LICENSE
913 print(sys.argv[0])
914 print(__doc__)
915 print(LICENSE)
929916 exit_code = 0
930917 if options.run_tests:
931918 import doctest
932919 (fails, something) = doctest.testmod(verbose=True)
933920 if fails == 0:
934 print "All tests passed."
921 print("All tests passed.")
935922 exit_code = 0
936923 else:
937924 exit_code = 1