2 * Copyright (C) 2022 Nick Downing <nick@ndcode.org>
3 * SPDX-License-Identifier: GPL-2.0-only
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the Free
7 * Software Foundation; version 2.
9 * This program is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
14 * You should have received a copy of the GNU General Public License along with
15 * this program; if not, write to the Free Software Foundation, Inc., 51
16 * Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
29 random.seed(time.time())
30 last_random = random.random()
31 inter_statement_delay = 0.
33 FOR_NEXT_DELAY_HZ = 1080. # empty for/next loops per sec
34 FOR_NEXT_CLICK_HZ = 540. # clicks within a for/next loop
35 STATEMENT_CLICK_HZ = 1620. # clicks within a single statement
48 class IntLiteral: Text {
51 class FloatLiteral: Node {
56 float float_value = 0.;
58 class StrLiteral: Node;
59 class VariableName: Text;
61 /* grammar productions */
64 class Statement: Node;
65 class StatementEnd: Statement;
66 class StatementFor: Statement;
67 class StatementNext: Statement;
68 class StatementData: Statement;
69 class StatementInput: Statement;
70 class StatementDel: Statement;
71 class StatementDim: Statement;
72 class StatementRead: Statement;
73 class StatementGr: Statement;
74 class StatementText: Statement;
75 class StatementPrHash: Statement;
76 class StatementInHash: Statement;
77 class StatementCall: Statement;
78 class StatementPlot: Statement;
79 class StatementHLin: Statement;
80 class StatementVLin: Statement;
81 class StatementHGR2: Statement;
82 class StatementHGR: Statement;
83 class StatementHColorEqual: Statement;
84 class StatementHPlot: Statement;
85 class StatementDraw: Statement;
86 class StatementXDraw: Statement;
87 class StatementHTab: Statement;
88 class StatementHome: Statement;
89 class StatementRotEqual: Statement;
90 class StatementScaleEqual: Statement;
91 class StatementShLoad: Statement;
92 class StatementTrace: Statement;
93 class StatementNoTrace: Statement;
94 class StatementNormal: Statement;
95 class StatementInverse: Statement;
96 class StatementFlash: Statement;
97 class StatementColorEqual: Statement;
98 class StatementPop: Statement;
99 class StatementVTab: Statement;
100 class StatementHimemColon: Statement;
101 class StatementLomemColon: Statement;
102 class StatementOnErr: Statement;
103 class StatementResume: Statement;
104 class StatementRecall: Statement;
105 class StatementStore: Statement;
106 class StatementSpeedEqual: Statement;
107 class StatementLet: Statement;
108 class StatementGoto: Statement;
109 class StatementRun: Statement;
110 class StatementIf: Statement;
111 class StatementRestore: Statement;
112 class StatementAmpersand: Statement;
113 class StatementGosub: Statement;
114 class StatementReturn: Statement;
115 class StatementRem: Statement;
116 class StatementStop: Statement;
117 class StatementOnGoto: Statement;
118 class StatementOnGosub: Statement;
119 class StatementWait: Statement;
120 class StatementLoad: Statement;
121 class StatementSave: Statement;
122 class StatementDef: Statement;
123 class StatementPoke: Statement;
124 class StatementPrint: Statement {
127 class StatementCont: Statement;
128 class StatementList: Statement;
129 class StatementClear: Statement;
130 class StatementGet: Statement;
131 class StatementNew: Statement;
132 class DataText: Node;
136 class RValueStrLiteral: RValue;
137 class RValueFloatLiteral: RValue;
138 class RValueIntLiteral: RValue;
139 class RValueTabLParen: RValue;
140 class RValueSpcLParen: RValue;
141 class RValueNot: RValue;
142 class RValueSign: RValue {
145 class RValueAdd: RValue;
146 class RValueSubtract: RValue;
147 class RValueMultiply: RValue;
148 class RValueDivide: RValue;
149 class RValuePower: RValue;
150 class RValueAnd: RValue;
151 class RValueOr: RValue;
152 class RValueGT: RValue;
153 class RValueEqual: RValue;
154 class RValueLT: RValue;
155 class RValueGE: RValue;
156 class RValueLE: RValue;
157 class RValueNE: RValue;
158 class RValueSgn: RValue;
159 class RValueInt: RValue;
160 class RValueAbs: RValue;
161 class RValueUsr: RValue;
162 class RValueFre: RValue;
163 class RValueScrnLParen: RValue;
164 class RValuePdl: RValue;
165 class RValuePos: RValue;
166 class RValueSqr: RValue;
167 class RValueRnd: RValue;
168 class RValueLog: RValue;
169 class RValueExp: RValue;
170 class RValueCos: RValue;
171 class RValueSin: RValue;
172 class RValueTan: RValue;
173 class RValueAtn: RValue;
174 class RValuePeek: RValue;
175 class RValueLen: RValue;
176 class RValueStrDollar: RValue;
177 class RValueVal: RValue;
178 class RValueAsc: RValue;
179 class RValueChrDollar: RValue;
180 class RValueLeftDollar: RValue;
181 class RValueRightDollar: RValue;
182 class RValueMidDollar: RValue;
183 class LValue: RValue;
184 class LValueVariable: LValue;
185 class LValueArray: LValue;
189 # Copyright (C) 2018 Nick Downing <nick@ndcode.org>
190 # SPDX-License-Identifier: GPL-2.0-only
192 # This program is free software; you can redistribute it and/or modify it under
193 # the terms of the GNU General Public License as published by the Free Software
194 # Foundation; version 2.
196 # This program is distributed in the hope that it will be useful, but WITHOUT
197 # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
198 # FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
201 # You should have received a copy of the GNU General Public License along with
202 # this program; if not, write to the Free Software Foundation, Inc., 51
203 # Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
205 class EndException(Exception):
208 class ReturnException(Exception):
211 class NextException(Exception):
214 # variables are boxed for easy referencing and/or subscripting
216 def __init__(self, value):
233 self.program = program if program is not None else Program()
234 self.variables = variables if variables is not None else {}
235 self.arrays = arrays if arrays is not None else {}
237 # execute pointer (line index, statement index)
241 # read pointer (line index, statement index, item index)
246 # count peek(-16336) within a statement, convert to a tone
249 # program index for ONERR GOTO (not used for anything yet)
257 except ReturnException:
259 f'?RETURN WITHOUT GOSUB ERROR IN {self.line_number():d}'
261 except NextException:
263 f'?NEXT WITHOUT FOR ERROR IN {self.line_number():d}'
266 def line_number(self):
267 assert self.i < len(self.program.children)
268 return self.program.children[self.i].children[0].int_value
271 while self.i < len(self.program.children):
272 line = self.program.children[self.i]
273 if self.j < len(line.children):
274 statement = line.children[self.j]
276 statement.execute(self)
280 self.clicks * 2 / STATEMENT_CLICK_HZ
283 if inter_statement_delay != 0:
284 time.sleep(inter_statement_delay)
290 def find_line(self, target):
291 for i in range(len(self.program.children)):
292 if target == self.program.children[i].children[0].int_value:
295 f'?UNDEF\'D STATEMENT ERROR IN {self.line_number():d}'
298 def find_variable(self, name):
299 # variable is created and initialized to 0 if not existent
300 variable = self.variables.get(name)
302 null_value = '' if name[-1] == '$' else 0 if name[-1] == '%' else 0.
303 variable = Variable(null_value)
304 self.variables[name] = variable
307 def find_array(self, name, n_dim):
308 # array is created with 11^n_dim zeroed entries if non-existent
309 array = self.arrays.get(name)
311 null_value = '' if name[-1] == '$' else 0 if name[-1] == '%' else 0.
316 [create_array(i + 1) for j in range(11)]
318 array = create_array(0)
319 self.arrays[name] = array
322 def factory(tag, *args, **kwargs):
323 return tag_to_class[tag](*args, **kwargs)
326 def post_process(self, program):
327 for i in self.children:
328 i.post_process(program)
330 def post_process(self, program):
331 self.str_value = ''.join([i.text[0] for i in self.children])
333 def post_process(self, program):
334 Text.post_process(self, program)
335 self.int_value = int(self.str_value)
337 @method(FloatLiteral)
338 def post_process(self, program):
339 Node.post_process(self, program)
341 if len(self.children) >= 3:
342 exponent = self.children[2]
343 exponent_value = int(exponent.children[1].str_value)
344 if exponent.children[0].str_value == '-':
345 exponent_value = -exponent_value
347 int(self.children[0].str_value + self.children[1].str_value) *
348 10. ** (exponent_value - len(self.children[1].str_value))
350 self.float_value = value
354 def execute(self, context):
355 raise NotImplementedError()
356 @method(StatementEnd)
357 def execute(self, context):
359 @method(StatementFor)
360 def execute(self, context):
361 name = self.children[0].str_value
362 # note: if the loop index is an integer, we will print a type
363 # mismatch error, whereas applesoft would print a syntax error
366 f'?TYPE MISMATCH ERROR IN {context.line_number():d}'
368 variable = context.find_variable(name)
370 variable.value = self.children[1].get_float(context)
371 end = self.children[2].get_float(context)
373 self.children[3].get_float(context)
374 if len(self.children) >= 4 else
378 # heuristically detect delay loops
383 while i < len(context.program.children):
384 line = context.program.children[i]
385 if j < len(line.children):
386 statement = line.children[j]
389 isinstance(statement, StatementLet) and
390 isinstance(statement.children[1], RValuePeek) and
391 isinstance(statement.children[1].children[0], RValueSign) and
392 statement.children[1].children[0].sign == -1 and
393 isinstance(statement.children[1].children[0].children[0], RValueIntLiteral) and
394 statement.children[1].children[0].children[0].children[0].int_value == 16336
398 isinstance(statement, StatementNext) and (
399 len(statement.children) < 1 or
400 statement.children[0].str_value == name
403 counts = math.floor((end - variable.value) / step) + 1
407 clicks * counts * 2 / FOR_NEXT_CLICK_HZ
410 time.sleep(counts / FOR_NEXT_DELAY_HZ)
425 except NextException:
428 assert context.i < len(context.program.children)
429 line = context.program.children[context.i]
430 assert context.j > 1 and context.j <= len(line.children)
431 statement = line.children[context.j - 1]
432 assert isinstance(statement, StatementNext)
434 len(statement.children) >= 1 and
435 statement.children[0].str_value != name
438 f'?NEXT WITHOUT FOR ERROR IN {context.line_number():d}'
441 variable.value += step
442 if (variable.value > end if step >= 0 else variable.value < end):
447 @method(StatementNext)
448 def execute(self, context):
449 raise NextException()
450 @method(StatementData)
451 def execute(self, context):
453 @method(StatementInput)
454 def execute(self, context):
457 self.children[-2].text[0]
458 if len(self.children) >= 2 else
461 value = apple_io.input()
462 lvalue = self.children[-1]
463 name = lvalue.children[0].str_value
465 n, result = data_types.val(value)
466 if n == 0 or n < len(value):
467 apple_io._print('?REENTER\r')
471 value = data_types.cint(value)
472 lvalue.find_variable(context).value = value
474 @method(StatementDel)
475 def execute(self, context):
477 @method(StatementDim)
478 def execute(self, context):
479 for i in self.children:
480 dim = [j.get_int(context) + 1 for j in i.children[1:]]
482 # we don't check for an existing array until here, as it could be
483 # created with the default 11 elements while evaluating dimensions
484 name = i.children[0].str_value
485 if name in context.arrays:
487 f'?REDIM\'D ARRAY ERROR IN {context.line_number():d}'
490 null_value = '' if name[-1] == '$' else 0 if name[-1] == '%' else 0.
494 if j >= len(dim) else
495 [create_array(j + 1) for k in range(dim[j])]
497 context.arrays[name] = create_array(0)
498 @method(StatementRead)
499 def execute(self, context):
501 while context.k < len(context.program.children):
502 line = context.program.children[context.k]
503 while context.l < len(line.children):
504 statement = line.children[context.l]
505 if isinstance(statement, StatementData):
506 while context.m < len(statement.children):
507 item = statement.children[context.m]
510 lvalue = self.children[i]
512 name = lvalue.children[0].str_value
514 n, result = data_types.val(value)
517 f'?SYNTAX ERROR IN {context.line_number():d}'
521 value = data_types.cint(value)
522 lvalue.find_variable(context).value = value
523 if i >= len(self.children):
529 assert context.m == 0
531 f'?OUT OF DATA ERROR IN {context.line_number():d}'
534 def execute(self, context):
536 @method(StatementText)
537 def execute(self, context):
539 @method(StatementPrHash)
540 def execute(self, context):
541 value = self.children[0].get_int(context)
542 apple_io.pr_hash(value)
543 @method(StatementInHash)
544 def execute(self, context):
545 value = self.children[0].get_int(context)
546 apple_io.in_hash(value)
547 @method(StatementCall)
548 def execute(self, context):
549 value = self.children[0].get_int(context)
551 @method(StatementPlot)
552 def execute(self, context):
553 value0 = self.children[0].get_int(context)
554 value1 = self.children[1].get_int(context)
555 apple_io.plot(value0, value1)
556 @method(StatementHLin)
557 def execute(self, context):
558 value0 = self.children[0].get_int(context)
559 value1 = self.children[1].get_int(context)
560 value2 = self.children[2].get_int(context)
561 apple_io.hlin(value0, value1, value2)
562 @method(StatementVLin)
563 def execute(self, context):
564 value0 = self.children[0].get_int(context)
565 value1 = self.children[1].get_int(context)
566 value2 = self.children[2].get_int(context)
567 apple_io.vlin(value0, value1, value2)
568 @method(StatementHGR2)
569 def execute(self, context):
571 @method(StatementHGR)
572 def execute(self, context):
575 @method(StatementHColorEqual)
576 def execute(self, context):
578 @method(StatementHPlot)
579 def execute(self, context):
581 @method(StatementDraw)
582 def execute(self, context):
584 @method(StatementXDraw)
585 def execute(self, context):
587 @method(StatementHTab)
588 def execute(self, context):
589 value = self.children[0].get_int(context)
591 @method(StatementHome)
592 def execute(self, context):
594 @method(StatementRotEqual)
595 def execute(self, context):
597 @method(StatementScaleEqual)
598 def execute(self, context):
600 @method(StatementShLoad)
601 def execute(self, context):
603 @method(StatementTrace)
604 def execute(self, context):
606 @method(StatementNoTrace)
607 def execute(self, context):
609 @method(StatementNormal)
610 def execute(self, context):
612 @method(StatementInverse)
613 def execute(self, context):
615 @method(StatementFlash)
616 def execute(self, context):
618 @method(StatementColorEqual)
619 def execute(self, context):
620 value = self.children[0].get_int(context)
621 apple_io.color(value)
622 @method(StatementPop)
623 def execute(self, context):
625 @method(StatementVTab)
626 def execute(self, context):
627 value = self.children[0].get_int(context)
629 @method(StatementHimemColon)
630 def execute(self, context):
631 value = self.children[0].get_int(context)
632 apple_io.himem(value)
633 @method(StatementLomemColon)
634 def execute(self, context):
635 value = self.children[0].get_int(context)
636 apple_io.lomem(value)
637 @method(StatementOnErr)
638 def execute(self, context):
639 context.onerr = context.find_line(self.children[0].int_value)
640 @method(StatementResume)
641 def execute(self, context):
643 @method(StatementRecall)
644 def execute(self, context):
646 @method(StatementStore)
647 def execute(self, context):
649 @method(StatementSpeedEqual)
650 def execute(self, context):
651 value = self.children[0].get_int(context)
654 f'?ILLEGAL QUANTITY ERROR IN {context.line_number():d}'
656 apple_io.speed(value)
657 @method(StatementLet)
658 def execute(self, context):
659 value = self.children[1].get(context)
660 self.children[0].set(context, value)
661 @method(StatementGoto)
662 def execute(self, context):
663 context.i = context.find_line(self.children[0].int_value)
665 @method(StatementRun)
666 def execute(self, context):
669 def execute(self, context):
670 value = self.children[0].get(context)
671 if value == '' or value == 0.:
674 @method(StatementRestore)
675 def execute(self, context):
679 @method(StatementAmpersand)
680 def execute(self, context):
682 @method(StatementGosub)
683 def execute(self, context):
686 context.i = context.find_line(self.children[0].int_value)
690 except ReturnException:
692 except NextException:
694 f'?NEXT WITHOUT FOR ERROR IN {context.line_number():d}'
698 @method(StatementReturn)
699 def execute(self, context):
700 raise ReturnException()
701 @method(StatementRem)
702 def execute(self, context):
704 @method(StatementStop)
705 def execute(self, context):
707 @method(StatementOnGoto)
708 def execute(self, context):
709 value = self.children[0].get_int(context)
712 f'?ILLEGAL QUANTITY ERROR IN {context.line_number():d}'
714 if value >= 1 and value < len(self.children):
715 context.i = context.find_line(self.children[value].int_value)
717 @method(StatementOnGosub)
718 def execute(self, context):
719 value = self.children[0].get_int(context)
722 f'?ILLEGAL QUANTITY ERROR IN {context.line_number():d}'
724 if value >= 1 and value < len(self.children):
727 context.i = context.find_line(self.children[value].int_value)
731 except ReturnException:
733 except NextException:
735 f'?NEXT WITHOUT FOR ERROR IN {context.line_number():d}'
739 @method(StatementWait)
740 def execute(self, context):
742 @method(StatementLoad)
743 def execute(self, context):
745 @method(StatementSave)
746 def execute(self, context):
748 @method(StatementDef)
749 def execute(self, context):
751 @method(StatementPoke)
752 def execute(self, context):
753 value0 = self.children[0].get_int(context)
754 value1 = self.children[1].get_int(context)
755 apple_io.poke(value0, value1)
756 @method(StatementPrint)
757 def execute(self, context):
758 for i in self.children:
759 value = i.get(context)
760 if isinstance(value, float):
761 value = data_types.str_dollar(context, value)
762 apple_io._print(value)
763 if not self.semicolon:
764 apple_io._print('\r')
765 @method(StatementCont)
766 def execute(self, context):
768 @method(StatementList)
769 def execute(self, context):
771 @method(StatementClear)
772 def execute(self, context):
774 @method(StatementGet)
775 def execute(self, context):
776 value = apple_io.get()
777 # note: if the lvalue isn't a string then we will print a type
778 # mismatch error, whereas applesoft would print a syntax error
779 self.children[0].set(context, value)
780 @method(StatementNew)
781 def execute(self, context):
786 def get(self, context):
787 raise NotImplementedError()
788 @method(RValueStrLiteral)
789 def get(self, context):
790 return self.children[0].text[0]
791 @method(RValueFloatLiteral)
792 def get(self, context):
793 return self.children[0].float_value
794 @method(RValueIntLiteral)
795 def get(self, context):
796 return float(self.children[0].int_value)
797 @method(RValueTabLParen)
798 def get(self, context):
799 value = self.children[0].get_int(context)
800 # strangely, pos() is 0-based whereas tab() is 1-based
801 value -= 1 + apple_io.pos()
802 return ' ' * value if value >= 1 else ''
803 @method(RValueSpcLParen)
804 def get(self, context):
805 value = self.children[0].get_int(context)
808 f'?ILLEGAL QUANTITY ERROR IN {context.line_number():d}'
812 def get(self, context):
813 value = self.children[0].get_float(context)
814 return float(value == 0.)
816 def get(self, context):
817 value = self.children[0].get_float(context)
818 return self.sign * value
820 def get(self, context):
821 value0 = self.children[0].get(context)
823 self.children[1].get_float(context)
824 if isinstance(value0, float) else
825 self.children[1].get_str(context)
827 return value0 + value1
828 @method(RValueSubtract)
829 def get(self, context):
830 value0 = self.children[0].get_float(context)
831 value1 = self.children[1].get_float(context)
832 return value0 - value1
833 @method(RValueMultiply)
834 def get(self, context):
835 value0 = self.children[0].get_float(context)
836 value1 = self.children[1].get_float(context)
837 return value0 * value1
838 @method(RValueDivide)
839 def get(self, context):
840 value0 = self.children[0].get_float(context)
841 value1 = self.children[1].get_float(context)
842 return value0 / value1
844 def get(self, context):
845 value0 = self.children[0].get_float(context)
846 value1 = self.children[1].get_float(context)
847 return value0 ** value1
849 def get(self, context):
850 value0 = self.children[0].get_float(context)
851 value1 = self.children[1].get_float(context)
852 return float(value0 != 0. and value1 != 0.)
854 def get(self, context):
855 value0 = self.children[0].get_float(context)
856 value1 = self.children[1].get_float(context)
857 return float(value0 != 0. or value1 != 0.)
859 def get(self, context):
860 value0 = self.children[0].get(context)
862 self.children[1].get_float(context)
863 if isinstance(value0, float) else
864 self.children[1].get_str(context)
866 return float(value0 > value1)
868 def get(self, context):
869 value0 = self.children[0].get(context)
871 self.children[1].get_float(context)
872 if isinstance(value0, float) else
873 self.children[1].get_str(context)
875 return float(value0 == value1)
877 def get(self, context):
878 value0 = self.children[0].get(context)
880 self.children[1].get_float(context)
881 if isinstance(value0, float) else
882 self.children[1].get_str(context)
884 return float(value0 < value1)
886 def get(self, context):
887 value0 = self.children[0].get(context)
889 self.children[1].get_float(context)
890 if isinstance(value0, float) else
891 self.children[1].get_str(context)
893 return float(value0 >= value1)
895 def get(self, context):
896 value0 = self.children[0].get(context)
898 self.children[1].get_float(context)
899 if isinstance(value0, float) else
900 self.children[1].get_str(context)
902 return float(value0 <= value1)
904 def get(self, context):
905 value0 = self.children[0].get(context)
907 self.children[1].get_float(context)
908 if isinstance(value0, float) else
909 self.children[1].get_str(context)
911 return float(value0 != value1)
913 def get(self, context):
914 value = self.children[0].get_float(context)
915 return -1. if value < 0. else 1. if value > 0. else 0.
917 def get(self, context):
918 value = self.children[0].get_float(context)
919 return float(math.floor(value))
921 def get(self, context):
922 value = self.children[0].get_float(context)
925 def get(self, context):
926 value = self.children[0].get(context)
927 value = apple_io.usr(value)
928 if isinstance(value, int):
932 def get(self, context):
934 return -29188. # value I get from a freshly booted system
935 @method(RValueScrnLParen)
936 def get(self, context):
937 value0 = self.children[0].get_int(context)
938 value1 = self.children[1].get_int(context)
939 return float(apple_io.scrn(value0, value1))
941 def get(self, context):
942 value = self.children[0].get_int(context)
943 if value < 0 or value >= 4:
945 f'?ILLEGAL QUANTITY ERROR IN {context.line_number():d}'
947 return float(apple_io.pdl(value))
949 def get(self, context):
950 return float(apple_io.pos())
952 def get(self, context):
953 value = self.children[0].get_float(context)
956 f'?ILLEGAL QUANTITY ERROR IN {context.line_number():d}'
958 return math.sqrt(value)
960 def get(self, context):
961 value = self.children[0].get_float(context)
965 last_random = random.random()
968 def get(self, context):
969 value = self.children[0].get_float(context)
972 f'?ILLEGAL QUANTITY ERROR IN {context.line_number():d}'
974 return math.log(value)
976 def get(self, context):
977 value = self.children[0].get_float(context)
978 return math.exp(value)
980 def get(self, context):
981 value = self.children[0].get_float(context)
982 return math.cos(value)
984 def get(self, context):
985 value = self.children[0].get_float(context)
986 return math.sin(value)
988 def get(self, context):
989 value = self.children[0].get_float(context)
990 return math.tan(value)
992 def get(self, context):
993 value = self.children[0].get_float(context)
994 return math.atan(value)
996 def get(self, context):
997 value = self.children[0].get_int(context)
998 return float(apple_io.peek(value))
1000 def get(self, context):
1001 value = self.children[0].get_str(context)
1002 return float(len(value))
1003 @method(RValueStrDollar)
1004 def get(self, context):
1005 value = self.children[0].get_float(context)
1006 return data_types.str_dollar(context, value)
1008 def get(self, context):
1009 value = self.children[0].get_str(context)
1010 return data_types.val(value)[1] # ignore n chars converted
1012 def get(self, context):
1013 value = self.children[0].get_str(context)
1016 f'?ILLEGAL QUANTITY ERROR IN {context.line_number():d}'
1018 return float(ord(value[0]))
1019 @method(RValueChrDollar)
1020 def get(self, context):
1021 value = self.children[0].get_int(context)
1022 if value < 0 or value >= 0x110000: # never seen a unicode apple 2 before!
1024 f'?ILLEGAL QUANTITY ERROR IN {context.line_number():d}'
1027 @method(RValueLeftDollar)
1028 def get(self, context):
1029 value0 = self.children[0].get_str(context)
1030 value1 = self.children[1].get_int(context)
1033 f'?ILLEGAL QUANTITY ERROR IN {context.line_number():d}'
1035 return value0[:value1]
1036 @method(RValueRightDollar)
1037 def get(self, context):
1038 value0 = self.children[0].get_str(context)
1039 value1 = self.children[1].get_int(context)
1042 f'?ILLEGAL QUANTITY ERROR IN {context.line_number():d}'
1044 return '' if value1 == 0 else value0[-value1:]
1045 @method(RValueMidDollar)
1046 def get(self, context):
1047 value0 = self.children[0].get_str(context)
1048 value1 = self.children[1].get_int(context)
1051 f'?ILLEGAL QUANTITY ERROR IN {context.line_number():d}'
1054 if len(self.children) < 3:
1055 return value0[value1:]
1056 value2 = self.children[2].get_int(context)
1059 f'?ILLEGAL QUANTITY ERROR IN {context.line_number():d}'
1061 return value0[value1:value1 + value2]
1063 def get(self, context):
1064 value = self.find_variable(context).value
1065 name = self.children[0].str_value
1067 value = float(value)
1072 def get_str(self, context):
1073 value = self.get(context)
1074 if not isinstance(value, str):
1076 f'?TYPE MISMATCH ERROR IN {context.line_number():d}'
1082 def get_float(self, context):
1083 value = self.get(context)
1084 if not isinstance(value, float):
1086 f'?TYPE MISMATCH ERROR IN {context.line_number():d}'
1092 def get_int(self, context):
1093 return data_types.cint(self.get_float(context))
1097 def find_variable(self, context):
1098 raise NotImplementedError()
1099 @method(LValueVariable)
1100 def find_variable(self, context):
1101 name = self.children[0].str_value
1102 return context.find_variable(name)
1103 @method(LValueArray)
1104 def find_variable(self, context):
1105 name = self.children[0].str_value
1106 variable = context.find_array(name, len(self.children) - 1)
1107 for i in self.children[1:]:
1108 if not isinstance(variable, list):
1109 # too many subscripts
1111 f'?BAD SUBSCRIPT ERROR IN {context.line_number():d}'
1113 index = data_types.cint(i.get_float(context))
1114 if index < 0 or index >= len(variable):
1116 f'?BAD SUBSCRIPT ERROR IN {context.line_number():d}'
1118 variable = variable[index]
1119 if not isinstance(variable, Variable):
1120 # not enough subscripts
1122 f'?BAD SUBSCRIPT ERROR IN {context.line_number():d}'
1128 def set(self, context, value):
1129 name = self.children[0].str_value
1131 if not isinstance(value, str):
1133 f'?TYPE MISMATCH ERROR IN {context.line_number():d}'
1136 if not isinstance(value, float):
1138 f'?TYPE MISMATCH ERROR IN {context.line_number():d}'
1141 value = data_types.cint(value)
1142 self.find_variable(context).value = value