From f33f068b4b3a163103818015b785ac06f85d1d79 Mon Sep 17 00:00:00 2001 From: Parsa Alizadeh Date: Mon, 6 Jan 2025 21:37:55 +0330 Subject: [PATCH 1/3] Improve error messages --- rfpl/__main__.py | 54 +++++++++---------- rfpl/interpreter.py | 129 +++++++++++++++++++++++++++++--------------- 2 files changed, 112 insertions(+), 71 deletions(-) diff --git a/rfpl/__main__.py b/rfpl/__main__.py index 00a144e..3b9c42f 100644 --- a/rfpl/__main__.py +++ b/rfpl/__main__.py @@ -23,23 +23,23 @@ def syntaxError(self, recognizer, offendingSymbol, line, column, msg, e): def check_grammar(cmd, superc=True): - if superc and re.match(r'^\s*(exit|finish|end|list|save\s+[\w/\-]+)\s*$', - cmd): + if superc and re.match(r'^\s*(exit|finish|end|list|save\s+[\w/\-]+)\s*$', cmd): return True if re.match(r'^\s*load\s+[\w/\-]+\s*$', cmd): return True + input_stream = antlr4.InputStream(cmd) + lexer = RFPLLexer(input_stream) + stream = antlr4.CommonTokenStream(lexer) + parser = RFPLParser(stream) + error_listener = QuietErrorListener() + lexer.removeErrorListeners() + lexer.addErrorListener(error_listener) + parser.removeErrorListeners() + parser.addErrorListener(error_listener) try: - input_stream = antlr4.InputStream(cmd) - lexer = RFPLLexer(input_stream) - stream = antlr4.CommonTokenStream(lexer) - parser = RFPLParser(stream) - error_listener = QuietErrorListener() - parser.removeErrorListeners() - parser.addErrorListener(error_listener) parser.line() except Exception: return False - return not error_listener.has_errors @@ -95,27 +95,23 @@ def load(intr: Interpreter, filename: str, loaded=[]): for line in lines: if line[0] == ';': continue - cmd += line.strip() + ' ' - if cmd == ' ': - cmd = '' - continue - if not line.strip(): - print(f'\033[31mSorry, I didn\'t understood this one:\n{cmd.strip()}\033[0m') - cmd = '' + cmd += line.strip() + if not cmd: continue + cmd += ' ' if not check_grammar(cmd, superc=False): continue mtch = re.match(r'^\s*load\s+(?P[\w/\-]+)\s*$', cmd) if mtch: load(intr, mtch.group('FILE'), loaded + [filename]) else: - suc, res = intr.interpret(cmd) - if suc != 'Success': - print(f'\033[31m{suc}\033[0m') - elif isinstance(res, Natural): - print(f'\033[33m = {res}\033[0m') + ok, result = intr.interpret(cmd) + if not ok: + print(f'\033[31m{result}\033[0m') + elif isinstance(result, Natural): + print(f'\033[33m = {result}\033[0m') else: - print(f'\033[33m . {res}\033[0m') + print(f'\033[33m . {result}\033[0m') cmd = '' print() except Exception as e: @@ -161,10 +157,10 @@ def save(filename:str): save(mtch.group('FILE')) continue hist += line.strip() + '\n\n' - suc, res = intr.interpret(line) - if suc != 'Success': - print(f'\033[31m{suc}\033[0m\n') - elif isinstance(res, Natural): - print(f'\033[33m = {res}\033[0m\n') + ok, result = intr.interpret(line) + if not ok: + print(f'\033[31m{result}\033[0m') + elif isinstance(result, Natural): + print(f'\033[33m = {result}\033[0m\n') else: - print(f'\033[33m . {res}\033[0m\n') + print(f'\033[33m . {result}\033[0m\n') diff --git a/rfpl/interpreter.py b/rfpl/interpreter.py index c9566ee..3d98e3a 100644 --- a/rfpl/interpreter.py +++ b/rfpl/interpreter.py @@ -56,6 +56,8 @@ class BaseList: class HashCache: + CACHE = False + def __init__(self, basic_functions): self.basic_functions = basic_functions self.cache = {} @@ -81,7 +83,7 @@ def makeMockNaturalList(self, n: int): return NaturalList([Natural(a), Natural(b), Natural(c), Natural(-1), Natural(-1)]) def callAndCache(self, fun: SymbolEntry, blist: BaseList, args: List[Natural]): - if len(blist.args) or fun.builtin: + if len(blist.args) or fun.builtin or not self.CACHE: return fun.call(blist, args) if fun.ix not in self.possibleMatches: self.possibleMatches[fun.ix] = self.basic_functions.copy() @@ -112,6 +114,34 @@ def callAndCache(self, fun: SymbolEntry, blist: BaseList, args: List[Natural]): return res +@dataclass +class InputError: + message: str + start: int = None + stop: int = None + + def setContext(self, ctx: ParserRuleContext): + self.start = ctx.start.start + self.stop = ctx.stop.stop + return self + + def toString(self, stream: InputStream = None): + s = self.message + '\n' + if self.start is None or self.stop is None or stream is None: + return s + s += ' ' + str(stream) + '\n' + s += ' ' + ' ' * self.start + '^' + '~' * max(0, self.stop - self.start) + '\n' + return s + + +class ThrowingErrorListener(ErrorListener): + def __init__(self, interpreter: 'Interpreter'): + self.interpreter = interpreter + + def syntaxError(self, recognizer, offendingSymbol, line, column, msg, e): + self.interpreter.errors.append(InputError(f'ERROR: {msg}', offendingSymbol.start, offendingSymbol.stop)) + + class Interpreter: def __init__(self): self.symbol_table = SymbolTable() @@ -174,6 +204,7 @@ def __init__(self): self.cache = HashCache( self.basic_arithmetic_enteries + self.basic_sequential_enteries + self.basic_numbertheory_enteries ) + self.errors: List[InputError] = [] def load_basics(self): self.load_basic_arithmetic_enteries() @@ -199,17 +230,14 @@ def interpretFexpr(self, tree, blist: BaseList, args: NaturalList) -> Natural: if tree.fexprlist() is not None: fexprlist: RFPLParser.FexprlistContext = tree.fexprlist() base_nxt += fexprlist.getTypedRuleContexts(RFPLParser.FexprContext) - symb = tree.Symbol().getText() - syment = tree.children[-1] + syment = tree.c_syment return self.cache.callAndCache(syment, BaseList(base_nxt, blist), args) elif isinstance(tree, RFPLParser.BracketContext): - ind = Natural.interpret(tree.natural()).toInt() - return self.interpretFexpr(blist.args[ind], blist.prev, args) + return self.interpretFexpr(blist.args[tree.c_number], blist.prev, args) elif isinstance(tree, RFPLParser.IdentityContext): - ind = Natural.interpret(tree.natural()).toInt() - return args[ind] + return args[tree.c_number] elif isinstance(tree, RFPLParser.ConstantContext): - return Natural.interpret(tree.natural()) + return tree.c_natural elif isinstance(tree, RFPLParser.BuiltinCnContext): f, *gs = tree.fexprlist().getTypedRuleContexts(RFPLParser.FexprContext) fargs = [] @@ -238,7 +266,7 @@ def interpretFexpr(self, tree, blist: BaseList, args: NaturalList) -> Natural: def interpretNexpr(self, tree): if not isinstance(tree, RFPLParser.NexprContext): - raise Exception('tree must represent a nexpr, got {}'.format(type(tree))) + raise Exception('Tree must represent a nexpr, got {}'.format(type(tree))) if tree.natural() is not None: return Natural.interpret(tree.natural()) fexpr: RFPLParser.FexprContext = tree.fexpr() @@ -246,18 +274,13 @@ def interpretNexpr(self, tree): args = [] for nexpr in nexprlist.getTypedRuleContexts(RFPLParser.NexprContext): args.append(self.interpretNexpr(nexpr)) - self.preproc(fexpr) + self.preprocess(fexpr) + if self.errors: + return None args = NaturalList(args) return self.interpretFexpr(fexpr, None, args) - class ThrowingErrorListener(ErrorListener): - def syntaxError(self, recognizer, offendingSymbol, line, column, msg, e): - ex = ParseCancellationException(f'line {line}: {column} {msg}') - ex.line = line - ex.column = column - raise ex - - def preproc(self, tree): + def preprocess(self, tree): basesz = 0 tree = tree.getChild(0) if isinstance(tree, RFPLParser.FexprleafContext): @@ -268,70 +291,92 @@ def preproc(self, tree): symb = tree.Symbol().getText() syment = self.symbol_table.search(symb) if syment is None: - raise Exception(f'function {symb} not defined') - if len(base_nxt) < syment.basesz: - raise Exception(f'{symb} needs {syment.basesz} bases but got {len(base_nxt)}') + self.errors.append(InputError( + message=f'ERROR: Function {symb} is not defined', + ).setContext(tree)) + return basesz + if len(base_nxt) != syment.basesz: + self.errors.append(InputError( + message=f'ERROR: {symb} accepts {syment.basesz} bases but got {len(base_nxt)}' + ).setContext(tree)) + return basesz tree.children.append(syment) - for func in base_nxt: - basesz = max(basesz, self.preproc(func)) + tree.c_syment = syment # custom attribute added to the tree + for b in base_nxt: + basesz = max(basesz, self.preprocess(b)) elif isinstance(tree, RFPLParser.BracketContext): - basesz = max(basesz, Natural.interpret(tree.natural()).toInt() + 1) + tree.c_number = Natural.interpret(tree.natural()).toInt() + basesz = max(basesz, tree.c_number + 1) elif isinstance(tree, RFPLParser.IdentityContext): - pass + tree.c_number = Natural.interpret(tree.natural()).toInt() elif isinstance(tree, RFPLParser.ConstantContext): - pass + tree.c_natural = Natural.interpret(tree.natural()) elif isinstance(tree, RFPLParser.BuiltinCnContext): f, *gs = tree.fexprlist().getTypedRuleContexts(RFPLParser.FexprContext) for g in gs: - basesz = max(basesz, self.preproc(g)) - basesz = max(basesz, self.preproc(f)) + basesz = max(basesz, self.preprocess(g)) + basesz = max(basesz, self.preprocess(f)) elif isinstance(tree, RFPLParser.BuiltinPrContext): f = tree.fexpr(0) g = tree.fexpr(1) - basesz = max(basesz, self.preproc(f)) - basesz = max(basesz, self.preproc(g)) + basesz = max(basesz, self.preprocess(f)) + basesz = max(basesz, self.preprocess(g)) elif isinstance(tree, RFPLParser.BuiltinMnContext): f = tree.fexpr() - basesz = max(basesz, self.preproc(f)) + basesz = max(basesz, self.preprocess(f)) else: - raise Exception(f'unknown node {type(tree)}') + raise Exception(f'Unknown node {type(tree)}') return basesz + + def buildErrorMessage(self): + msg = '' + for err in self.errors: + msg += err.toString(self.input_stream) + return msg def interpret(self, line:str): + self.errors = [] try: - input_stream = InputStream(line.strip()) - lexer = RFPLLexer(input_stream) + self.input_stream = InputStream(line.strip()) + lexer = RFPLLexer(self.input_stream) lexer.removeErrorListeners() - lexer.addErrorListener(self.ThrowingErrorListener()) + lexer.addErrorListener(ThrowingErrorListener(self)) token_stream = CommonTokenStream(lexer) parser = RFPLParser(token_stream) parser.removeErrorListeners() - parser.addErrorListener(self.ThrowingErrorListener()) + parser.addErrorListener(ThrowingErrorListener(self)) tree = parser.line() + if self.errors: + return False, self.buildErrorMessage() + tree = tree.getChild(0) if isinstance(tree, RFPLParser.DefineContext): symb = tree.Symbol().getText() fexpr = tree.fexpr() - basesz = self.preproc(fexpr) + basesz = self.preprocess(fexpr) + if self.errors: + return False, self.buildErrorMessage() message = f'Function {symb} added' syment = self.symbol_table.search(symb) if syment is not None: if syment.builtin: - raise Exception('cannot redefine builtin function {}'.format(symb)) + return False, f'ERROR: Cannot redefine a builtin function {symb}' message = f'Function {symb} redefined' self.symbol_table.add( symbol=symb, call=lambda blist, args, fexpr=fexpr: self.interpretFexpr(fexpr, blist, args), basesz = basesz ) - return 'Success', message + return True, message elif isinstance(tree, RFPLParser.ExamineContext): result = self.interpretNexpr(tree.nexpr()) - return 'Success', result + if self.errors: + return False, self.buildErrorMessage() + return True, result else: - raise Exception('unknown node {}'.format(type(tree))) # I leave this one ! + raise Exception('Unknown node {}'.format(type(tree))) except Exception as e: - return f'ERROR: {traceback.format_exc()}' if DEBUG else f'ERROR: {e}', None + return False, f'CRITICAL: {traceback.format_exc()}' From 20b38d36164a5ba02bd93bd2419a7469d344e8d6 Mon Sep 17 00:00:00 2001 From: Parsa Alizadeh Date: Mon, 6 Jan 2025 23:05:01 +0330 Subject: [PATCH 2/3] Make Natural immutable --- rfpl/interpreter.py | 59 +++++++++++------ rfpl/natural.py | 155 +++++++++++++++++++++++++------------------- 2 files changed, 127 insertions(+), 87 deletions(-) diff --git a/rfpl/interpreter.py b/rfpl/interpreter.py index 3d98e3a..0dfc229 100644 --- a/rfpl/interpreter.py +++ b/rfpl/interpreter.py @@ -20,10 +20,16 @@ def debug(*args): print(*args, file=sys.stderr) +@dataclass +class BaseList: + args: List[RFPLParser.FexprContext] + prev: 'BaseList' = None + + @dataclass class SymbolEntry: symbol: str - call: Callable + call: Callable[[BaseList, NaturalList], Natural] builtin: bool = False ix: int = -1 basesz: int = 0 @@ -49,14 +55,8 @@ def add(self, *args, **kwargs): return self.addEntry(SymbolEntry(*args, **kwargs)) -@dataclass -class BaseList: - args: List[RFPLParser.FexprContext] - prev: 'BaseList' = None - - class HashCache: - CACHE = False + CACHE = True def __init__(self, basic_functions): self.basic_functions = basic_functions @@ -80,10 +80,10 @@ def hash(self, args: NaturalList): def makeMockNaturalList(self, n: int): m = int(n**0.5) a, b, c = random.randint(0,m), random.randint(0,m), random.randint(0,m) - return NaturalList([Natural(a), Natural(b), Natural(c), Natural(-1), Natural(-1)]) + return NaturalList([Natural(a), Natural(b), Natural(c), Natural(None), Natural(None)]) def callAndCache(self, fun: SymbolEntry, blist: BaseList, args: List[Natural]): - if len(blist.args) or fun.builtin or not self.CACHE: + if (blist is not None and len(blist.args)) or fun.builtin or not self.CACHE: return fun.call(blist, args) if fun.ix not in self.possibleMatches: self.possibleMatches[fun.ix] = self.basic_functions.copy() @@ -102,8 +102,8 @@ def callAndCache(self, fun: SymbolEntry, blist: BaseList, args: List[Natural]): self.possibleMatches[fun.ix].remove(ent) mocknatlst = self.makeMockNaturalList(self.counter[fun.ix]) for ent in self.possibleMatches[fun.ix]: - rel = ent.call([], mocknatlst) - rez = fun.call([], mocknatlst) + rel = ent.call(None, mocknatlst) + rez = fun.call(None, mocknatlst) if rel.toInt() != rez.toInt(): self.possibleMatches[fun.ix].remove(ent) if len(self.possibleMatches[fun.ix]): @@ -172,25 +172,45 @@ def __init__(self): builtin=True ) ] + + def _getEntry(_blist, args): + if args[1].isZero(): + return Natural(0) + return args[1].getEntry(args[0]) + + def _setEntry(_blist, args): + if args[2].isZero(): + return Natural(0) + return args[2].setEntry(args[0], args[1]) + + def _int(_blist, args): + args[0].simplify() + return args[0] + + def _list(_blist, args): + if args[0].isDefined() and not args[0].isZero(): + args[0].factor() + return args[0] + self.basic_sequential_enteries = [ SymbolEntry( symbol='Get', - call=lambda _blist, args : args[1].getEntry(args[0]), + call=_getEntry, builtin=True ), SymbolEntry( symbol='Set', - call=lambda _blist, args : args[2].setEntry(args[0], args[1]), + call=_setEntry, builtin=True ), SymbolEntry( symbol='Int', - call=lambda _blist, args : args[0].simplify() or args[0], + call=_int, builtin=True ), SymbolEntry( symbol='List', - call=lambda _blist, args : args[0].factor() or args[0], + call=_list, builtin=True ), ] @@ -250,8 +270,9 @@ def interpretFexpr(self, tree, blist: BaseList, args: NaturalList) -> Natural: f = tree.fexpr(0) g = tree.fexpr(1) n = args[0].toInt() - cur = self.interpretFexpr(f, blist, args.cuthead()) - args = NaturalList([Natural(-1), Natural(-1)]) + args.cuthead() + args = args.drop(1) + cur = self.interpretFexpr(f, blist, args) + args = NaturalList([Natural(None), Natural(None)]) + args for i in range(n): args[0] = cur args[1] = Natural(i) @@ -261,7 +282,7 @@ def interpretFexpr(self, tree, blist: BaseList, args: NaturalList) -> Natural: f = tree.fexpr() args = NaturalList([Natural(0)]) + args while not self.interpretFexpr(f, blist, args).isZero(): - args[0].natural = args[0].toInt() + 1 + args[0] = args[0].succ() return args[0] def interpretNexpr(self, tree): diff --git a/rfpl/natural.py b/rfpl/natural.py index 9817e61..a22ed0f 100644 --- a/rfpl/natural.py +++ b/rfpl/natural.py @@ -22,63 +22,81 @@ def getPrime(i): class Natural: + __slots__ = ('__natural',) + def __init__(self, natural: Union[int, List['Natural']]): - self.natural: Union[int, List['Natural']] = natural + if isinstance(natural, int) and natural < 0: + raise Exception(f'Cannot initialize natural with negative number {natural}') + self.__natural: Union[int, List['Natural']] = natural + + def isDefined(self): + return self.__natural is not None + + def isZero(self): + return self.__natural == 0 + + def isOne(self): + if isinstance(self.__natural, int): + return self.__natural == 1 + return self.isDefined() and all(x.isZero() for x in self.__natural) def toInt(self): - if isinstance(self.natural, int): - return self.natural + if not self.isDefined(): + return -1 + if isinstance(self.__natural, int): + return self.__natural num = 1 - for i, ent in enumerate(self.natural): + for i, ent in enumerate(self.__natural): num *= getPrime(i) ** ent.toInt() return num def simplify(self): - self.natural = self.toInt() + self.__natural = self.toInt() def factor(self): - if isinstance(self.natural, list): - return - if self.natural < 1: + if isinstance(self.__natural, list): return - cur = self.natural - self.natural = [] + if self.isZero() or not self.isDefined(): + raise Exception('Zero or undefined cannot be factored') + cur = self.__natural + self.__natural = [] pi = 0 while cur > 1: cnt = 0 while cur % getPrime(pi) == 0: cur //= getPrime(pi) cnt += 1 - self.natural.append(Natural(cnt)) + self.__natural.append(Natural(cnt)) pi += 1 def copy(self): - if isinstance(self.natural, int): - natural = self.natural - else: - natural = [] - for nat in self.natural: - natural.append(nat.copy()) + if not self.isDefined(): + return Natural(None) + if isinstance(self.__natural, int): + return Natural(self.__natural) + natural = [] + for nat in self.__natural: + natural.append(nat.copy()) return Natural(natural) def getEntry(self, ind: 'Natural'): - if self.natural == 0 or self.natural == -1: - return Natural(self.natural) + if not self.isDefined() or not ind.isDefined(): + return Natural(None) ind = ind.toInt() self.factor() - if ind >= len(self.natural): + if ind >= len(self.__natural): return Natural(0) - return self.natural[ind] + return self.__natural[ind] def setEntry(self, ind: 'Natural', nat: 'Natural'): - if self.natural == 0 or self.natural == -1: - return Natural(self.natural) + if not self.isDefined() or not ind.isDefined(): + return Natural(None) + self.factor() result = self.copy() ind = ind.toInt() - result.factor() - while len(result.natural) <= ind: - result.natural.append(Natural(0)) - result.natural[ind] = nat + while len(result.__natural) <= ind: + result.__natural.append(Natural(0)) + result.__natural[ind] = nat return result @staticmethod @@ -92,32 +110,27 @@ def interpret(tree: RFPLParser.NaturalContext): return Natural(nats) def succ(self): - nat = self.toInt() - return Natural(nat + 1 if nat >= 0 else -1) - - def isZero(self): - return self.natural == 0 - - def isOne(self): - if isinstance(self.natural, int): - return self.natural == 1 - return all(x.isZero() for x in self.natural) + if not self.isDefined(): + return Natural(None) + return Natural(self.toInt() + 1) def __add__(self, other: 'Natural'): - nat1, nat2 = self.toInt(), other.toInt() - return Natural(nat1 + nat2) if nat1 >= 0 and nat2 >= 0 else Natural(-1) + if not self.isDefined() or not other.isDefined(): + return Natural(None) + return Natural(self.toInt() + other.toInt()) def __sub__(self, other: 'Natural'): - nat1, nat2 = self.toInt(), other.toInt() - return Natural(max(nat1 - nat2, 0)) if nat1 >= 0 and nat2 >= 0 else Natural(-1) + if not self.isDefined() or not other.isDefined(): + return Natural(None) + return Natural(max(self.toInt() - other.toInt(), 0)) def __mul__(self, other: 'Natural'): - if self.natural == -1 or other.natural == -1: - return Natural(-1) - if isinstance(self.natural, int) or isinstance(other.natural, int): + if not self.isDefined() or not other.isDefined(): + return Natural(None) + if isinstance(self.__natural, int) or isinstance(other.__natural, int): return Natural(self.toInt() * other.toInt()) - a = self.natural.copy() - b = other.natural.copy() + a = self.__natural.copy() + b = other.__natural.copy() if len(a) < len(b): a += [Natural(0)] * (len(b) - len(a)) elif len(b) < len(a): @@ -125,35 +138,41 @@ def __mul__(self, other: 'Natural'): return Natural(list(x + y for x, y in zip(a, b))) def __pow__(self, other: 'Natural'): - if self.natural == -1 or other.natural == -1: - return Natural(-1) + if not self.isDefined() or not other.isDefined(): + return Natural(None) if other.isZero(): return Natural(1) if other.isOne(): - return Natural(self.natural) - if isinstance(self.natural, int): + return Natural(self.__natural) + if isinstance(self.__natural, int): p = other.toInt() - return Natural(self.toInt() ** p) - return Natural(list(x * other for x in self.natural)) + return Natural(self.__natural ** p) + return Natural(list(x * other for x in self.__natural)) def __mod__(self, other: 'Natural'): - if self.natural == -1 or other.natural == -1: - return Natural(-1) - return Natural(self.toInt() % other.toInt()) if other.natural != 0 else Natural(self.toInt()) + if not self.isDefined() or not other.isDefined(): + return Natural(None) + if other.isZero(): + return self + return Natural(self.toInt() % other.toInt()) def __repr__(self): - if isinstance(self.natural, int): - return 'N({})'.format(self.natural) + if not self.isDefined(): + return 'Undefined' + if isinstance(self.__natural, int): + return 'N({})'.format(self.__natural) subreps = [] - for ent in self.natural: + for ent in self.__natural: subreps.append(ent.__repr__()) return 'N<{}>'.format(', '.join(subreps)) def __str__(self): - if isinstance(self.natural, int): - return f'{self.natural}' if self.natural > -1 else 'NotDefined' + if not self.isDefined(): + return 'Undefined' + if isinstance(self.__natural, int): + return f'{self.__natural}' subreps = [] - for ent in self.natural: + for ent in self.__natural: subreps.append(ent.__str__()) return '<{}>'.format(', '.join(subreps)) @@ -166,16 +185,16 @@ def __add__(self, other: 'NaturalList'): return NaturalList(self.content.copy() + other.content.copy()) def __getitem__(self, index: int): - while len(self.content) <= index: - self.content.append(Natural(0)) + if index >= len(self.content): + return Natural(None) return self.content[index] def __setitem__(self, index: int, value: Natural): while len(self.content) <= index: - self.content.append(Natural(0)) + self.content.append(Natural(None)) self.content[index] = value - def cuthead(self, index: int = 1): - while len(self.content) < index: - self.content.append(Natural(0)) - return NaturalList(self.content.copy()[index:]) + def drop(self, nitem: int): + if len(self.content) < nitem: + return NaturalList([]) + return NaturalList(self.content.copy()[nitem:]) From 2771a80b89b95e81897a00acb1ada7aec4276448 Mon Sep 17 00:00:00 2001 From: Parsa Alizadeh Date: Mon, 6 Jan 2025 23:06:53 +0330 Subject: [PATCH 3/3] Feels wrong --- rfpl/natural.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rfpl/natural.py b/rfpl/natural.py index a22ed0f..fe22dc1 100644 --- a/rfpl/natural.py +++ b/rfpl/natural.py @@ -186,12 +186,12 @@ def __add__(self, other: 'NaturalList'): def __getitem__(self, index: int): if index >= len(self.content): - return Natural(None) + return Natural(0) return self.content[index] def __setitem__(self, index: int, value: Natural): while len(self.content) <= index: - self.content.append(Natural(None)) + self.content.append(Natural(0)) self.content[index] = value def drop(self, nitem: int):