MoreThanZero

From Esolang
Jump to navigation Jump to search
This is still a work in progress. It may be changed in the future.
This article is not detailed enough and needs to be expanded. Please help us by adding some more information.
MoreThanZero
Designed by User:DumbEsolangsOrgUser.
Appeared in 2025
Computational class Unknown
Reference implementation Unimplemented

MoreThanZero is one of stack-based esoteric programming languages, which just editing the numbers by stack, and can't create useful programms, and it makes MoreThanZero unuseful.

Language Overview

MoreThanZero is character-by-character esoteric programming language using *.morethanzero files (.morethanzero it's just a renamed .txt)

MoreThanZero using this commands:

Command Description
0, 1... Add digit to stack
< Delete first digit in stack
> Delete last digit in stack
+ Add all digits in stack, split the result into characters, and change stack to them.
- subtract all digits in stack, split the result into characters, and change stack to them.
. Output the stack
, Input character and put it in stack. If input has more than 1 character or has letters — ignore
[ Output first digit in stack
] Output last digit in stack
& Collect all the digits from stack and locally put them into one number and output the number it as ASCII (This command do nothing with stack)
# Pass next character

All characters other than 1234567890><+-.,[]&# is comments.

Why "MoreThanZero"?

I using "MoreThanZero" as name and as file extension because it have more than zero stacks

Implementations

Python GUI IDE+File reader+CLI

# morethanzero_tkinter_and_light.py
# Contains two parts in one file for convenience:
# 1) A full tkinter IDE + File runner + Lightweight GUI in a single app (modes switched by buttons)
# 2) A separate small command-line "lightweight" interpreter (starting with "if __name__ == '__main__' and --light" )
#
# Save this file as morethanzero_tkinter_and_light.py and run with:
#    python morethanzero_tkinter_and_light.py
# The GUI starts. To run only the tiny CLI runner (minimal footprint), use:
#    python morethanzero_tkinter_and_light.py --cli path/to/file.morethanzero
#
# Interpreter implementation follows the MoreThanZero spec from Esolangs: https://www.esolangs.org/wiki/MoreThanZero
# (commands: digits push, < remove first, > remove last, + sum -> stack digits, - subtract -> stack digits, . output stack,
#  , input char, [ output first, ] output last, & collect digits->number->print ASCII char, # skip next char)

import sys
import tkinter as tk
from tkinter import filedialog, messagebox, scrolledtext
import argparse
import threading
import time

class MoreThanZeroInterpreter:
    def __init__(self, code, input_text=''):
        self.code = code or ''
        self.ip = 0
        self.stack = []  # list of characters
        self.output = []
        self.input_text = input_text or ''
        self.input_pos = 0
        self.skip_next = False
        self.max_steps = 1000000
        self.steps = 0

    def _read_input_char(self):
        # obey spec: If input has more than 1 character or has letters — ignore
        if self.input_pos >= len(self.input_text):
            return None
        ch = self.input_text[self.input_pos]
        self.input_pos += 1
        if len(ch) != 1:
            return None
        if ch.isalpha():
            return None
        return ch

    def step(self):
        if self.ip >= len(self.code):
            return False
        ch = self.code[self.ip]
        # '#' passes next character
        if ch == '#':
            self.ip += 1  # move to next char
            if self.ip < len(self.code):
                # skip it
                self.ip += 1
            return True

        if ch.isdigit():
            self.stack.append(ch)
        elif ch == '<':
            if self.stack:
                del self.stack[0]
        elif ch == '>':
            if self.stack:
                self.stack.pop()
        elif ch == '+':
            # Add all digits in stack, split result into characters, change stack
            total = 0
            for d in self.stack:
                if d.isdigit():
                    total += int(d)
            s = str(total)
            self.stack = list(s)
        elif ch == '-':
            # Subtract all digits in stack left-to-right
            digits = [int(d) for d in self.stack if d.isdigit()]
            if not digits:
                result = 0
            else:
                result = digits[0]
                for d in digits[1:]:
                    result -= d
            s = str(result)
            self.stack = list(s)
        elif ch == '.':
            # Output the stack as-is (concatenated)
            self.output.append(''.join(self.stack))
        elif ch == ',':
            c = self._read_input_char()
            if c is not None and not c.isalpha() and len(c) == 1:
                # spec says: input character and put it in stack. If input has more than 1 character or has letters — ignore
                # here we accept single non-letter character (digits or punctuation), but pushing only if it's a digit
                # The spec suggests digits in stack, but it says "put it in stack" — we'll push the char as-is.
                self.stack.append(c)
        elif ch == '[':
            if self.stack:
                # output first digit in stack
                self.output.append(self.stack[0])
        elif ch == ']':
            if self.stack:
                self.output.append(self.stack[-1])
        elif ch == '&':
            # Collect all digits from stack and form a number, output it as ASCII char.
            numstr = ''.join([c for c in self.stack if c.isdigit()])
            if numstr:
                try:
                    val = int(numstr)
                    # guard against out-of-range
                    if 0 <= val <= 0x10FFFF:
                        try:
                            self.output.append(chr(val))
                        except Exception:
                            # fallback: append numeric representation in brackets
                            self.output.append(f'<{val}>')
                    else:
                        self.output.append(f'<{val}>')
                except Exception:
                    pass
        else:
            # other characters are comments
            pass
        self.ip += 1
        self.steps += 1
        if self.steps > self.max_steps:
            raise RuntimeError('Step limit exceeded (possible infinite loop)')
        return True

    def run(self, timeout_seconds=None):
        start = time.time()
        while self.ip < len(self.code):
            if timeout_seconds is not None and (time.time() - start) > timeout_seconds:
                raise TimeoutError('Execution timed out')
            cont = self.step()
            if not cont:
                break
        return ''.join(self.output)

# ------------------ Tkinter GUI ------------------
class MoreThanZeroGUI(tk.Tk):
    def __init__(self):
        super().__init__()
        self.title('MoreThanZero — Interpreter')
        self.geometry('900x600')

        # Top controls: mode buttons
        ctl_frame = tk.Frame(self)
        ctl_frame.pack(side=tk.TOP, fill=tk.X)
        self.mode_var = tk.StringVar(value='IDE')
        for mode in ('IDE', 'File runner', 'Lightweight'):
            b = tk.Radiobutton(ctl_frame, text=mode, variable=self.mode_var, value=mode, indicatoron=False, command=self._switch_mode)
            b.pack(side=tk.LEFT, padx=4, pady=4)

        # Container for frames
        container = tk.Frame(self)
        container.pack(fill=tk.BOTH, expand=True)

        # IDE Frame
        self.ide_frame = tk.Frame(container)
        self.file_frame = tk.Frame(container)
        self.light_frame = tk.Frame(container)

        for f in (self.ide_frame, self.file_frame, self.light_frame):
            f.place(relwidth=1, relheight=1)

        self._build_ide(self.ide_frame)
        self._build_file(self.file_frame)
        self._build_light(self.light_frame)

        self._switch_mode()

    def _switch_mode(self):
        mode = self.mode_var.get()
        if mode == 'IDE':
            self.ide_frame.lift()
        elif mode == 'File runner':
            self.file_frame.lift()
        else:
            self.light_frame.lift()

    def _build_ide(self, frame):
        top = tk.Frame(frame)
        top.pack(fill=tk.X)
        save_btn = tk.Button(top, text='Save', command=self._ide_save)
        save_btn.pack(side=tk.LEFT, padx=2, pady=2)
        load_btn = tk.Button(top, text='Load', command=self._ide_load)
        load_btn.pack(side=tk.LEFT, padx=2, pady=2)
        run_btn = tk.Button(top, text='Run', command=self._ide_run)
        run_btn.pack(side=tk.LEFT, padx=2, pady=2)
        step_btn = tk.Button(top, text='Step', command=self._ide_step)
        step_btn.pack(side=tk.LEFT, padx=2, pady=2)
        reset_btn = tk.Button(top, text='Reset', command=self._ide_reset)
        reset_btn.pack(side=tk.LEFT, padx=2, pady=2)

        mid = tk.PanedWindow(frame, orient=tk.HORIZONTAL)
        mid.pack(fill=tk.BOTH, expand=True)

        self.editor = scrolledtext.ScrolledText(mid)
        mid.add(self.editor)

        right = tk.Frame(mid)
        mid.add(right)
        tk.Label(right, text='Input (for ",")').pack(anchor=tk.W)
        self.ide_input = tk.Entry(right)
        self.ide_input.pack(fill=tk.X, padx=4)
        tk.Label(right, text='Output').pack(anchor=tk.W)
        self.ide_output = scrolledtext.ScrolledText(right, height=20)
        self.ide_output.pack(fill=tk.BOTH, expand=True, padx=4, pady=4)

        # Interpreter state for stepping
        self._ide_interp = None

    def _ide_save(self):
        path = filedialog.asksaveasfilename(defaultextension='.morethanzero', filetypes=[('MoreThanZero files', '*.morethanzero'), ('Text files','*.txt'), ('All files','*.*')])
        if not path:
            return
        try:
            with open(path,'w', encoding='utf-8') as f:
                f.write(self.editor.get('1.0', tk.END))
            messagebox.showinfo('Saved', f'Saved to {path}')
        except Exception as e:
            messagebox.showerror('Error', str(e))

    def _ide_load(self):
        path = filedialog.askopenfilename(filetypes=[('MoreThanZero files', '*.morethanzero'), ('Text files','*.txt'), ('All files','*.*')])
        if not path:
            return
        try:
            with open(path,'r', encoding='utf-8') as f:
                txt = f.read()
            self.editor.delete('1.0', tk.END)
            self.editor.insert(tk.END, txt)
        except Exception as e:
            messagebox.showerror('Error', str(e))

    def _ide_run(self):
        code = self.editor.get('1.0', tk.END)
        inp = self.ide_input.get()
        interp = MoreThanZeroInterpreter(code, input_text=inp)
        try:
            out = interp.run()
        except Exception as e:
            out = f'Error: {e}'
        self.ide_output.delete('1.0', tk.END)
        self.ide_output.insert(tk.END, out)
        # reset stepping state
        self._ide_interp = None

    def _ide_step(self):
        if self._ide_interp is None:
            code = self.editor.get('1.0', tk.END)
            inp = self.ide_input.get()
            self._ide_interp = MoreThanZeroInterpreter(code, input_text=inp)
            self.ide_output.delete('1.0', tk.END)
        try:
            cont = self._ide_interp.step()
            # append incremental output
            self.ide_output.delete('1.0', tk.END)
            self.ide_output.insert(tk.END, ''.join(self._ide_interp.output))
            if not cont or self._ide_interp.ip >= len(self._ide_interp.code):
                self._ide_interp = None
        except Exception as e:
            messagebox.showerror('Error', str(e))
            self._ide_interp = None

    def _ide_reset(self):
        self._ide_interp = None
        self.ide_output.delete('1.0', tk.END)

    def _build_file(self, frame):
        top = tk.Frame(frame)
        top.pack(fill=tk.X)
        open_btn = tk.Button(top, text='Open .morethanzero file', command=self._file_open)
        open_btn.pack(side=tk.LEFT, padx=4, pady=4)
        run_btn = tk.Button(top, text='Run', command=self._file_run)
        run_btn.pack(side=tk.LEFT, padx=4, pady=4)
        tk.Label(top, text='Input (for ",")').pack(side=tk.LEFT, padx=6)
        self.file_input = tk.Entry(top)
        self.file_input.pack(side=tk.LEFT, fill=tk.X, expand=True, padx=4)

        self.file_code_display = scrolledtext.ScrolledText(frame)
        self.file_code_display.pack(fill=tk.BOTH, expand=True)
        self.file_code_display.config(state=tk.DISABLED)

        bottom = tk.Frame(frame)
        bottom.pack(fill=tk.BOTH)
        tk.Label(bottom, text='Output').pack(anchor=tk.W)
        self.file_output = scrolledtext.ScrolledText(bottom, height=10)
        self.file_output.pack(fill=tk.BOTH, expand=True)

        self._current_file_path = None

    def _file_open(self):
        path = filedialog.askopenfilename(filetypes=[('MoreThanZero files','*.morethanzero'), ('Text files','*.txt'), ('All files','*.*')])
        if not path:
            return
        try:
            with open(path,'r', encoding='utf-8') as f:
                txt = f.read()
            self._current_file_path = path
            self.file_code_display.config(state=tk.NORMAL)
            self.file_code_display.delete('1.0', tk.END)
            self.file_code_display.insert(tk.END, txt)
            self.file_code_display.config(state=tk.DISABLED)
        except Exception as e:
            messagebox.showerror('Error', str(e))

    def _file_run(self):
        if not self._current_file_path:
            messagebox.showwarning('No file', 'Open a .morethanzero file first')
            return
        try:
            with open(self._current_file_path,'r', encoding='utf-8') as f:
                code = f.read()
        except Exception as e:
            messagebox.showerror('Error', str(e))
            return
        inp = self.file_input.get()
        interp = MoreThanZeroInterpreter(code, input_text=inp)
        try:
            out = interp.run()
        except Exception as e:
            out = f'Error: {e}'
        self.file_output.delete('1.0', tk.END)
        self.file_output.insert(tk.END, out)

    def _build_light(self, frame):
        # Minimal UI: small editor, input, run, output. Designed to be tiny.
        top = tk.Frame(frame)
        top.pack(fill=tk.X)
        tk.Label(top, text='Tiny editor (paste code)').pack(side=tk.LEFT)
        run_btn = tk.Button(top, text='Run', command=self._light_run)
        run_btn.pack(side=tk.RIGHT, padx=4)
        self.light_input = tk.Entry(top)
        self.light_input.pack(fill=tk.X, padx=4)

        self.light_editor = tk.Text(frame, height=8)
        self.light_editor.pack(fill=tk.X, padx=4, pady=4)
        tk.Label(frame, text='Output').pack(anchor=tk.W)
        self.light_output = tk.Text(frame, height=8)
        self.light_output.pack(fill=tk.BOTH, expand=True, padx=4, pady=4)

    def _light_run(self):
        code = self.light_editor.get('1.0', tk.END)
        inp = self.light_input.get()
        interp = MoreThanZeroInterpreter(code, input_text=inp)
        try:
            out = interp.run()
        except Exception as e:
            out = f'Error: {e}'
        self.light_output.delete('1.0', tk.END)
        self.light_output.insert(tk.END, out)

# ------------------ Small CLI lightweight runner ------------------
# This part is intentionally small and dependency-free. It can be extracted as a separate tiny file named morethanzero_light.py
# For convenience it's included here; run with --cli to use it.

def run_cli_file(path, input_text=''):
    try:
        with open(path,'r', encoding='utf-8') as f:
            code = f.read()
    except Exception as e:
        print('Error reading file:', e)
        return
    interp = MoreThanZeroInterpreter(code, input_text=input_text)
    try:
        out = interp.run()
        print(out)
    except Exception as e:
        print('Runtime error:', e)

if __name__ == '__main__':
    parser = argparse.ArgumentParser(description='MoreThanZero runner. GUI by default.')
    parser.add_argument('--cli', metavar='FILE', help='Run minimal CLI interpreter on FILE and exit')
    parser.add_argument('--input', metavar='INPUT', help='Input string for , commands (CLI mode)')
    args = parser.parse_args()
    if args.cli:
        run_cli_file(args.cli, input_text=(args.input or ''))
    else:
        app = MoreThanZeroGUI()
        app.mainloop()
This interpreter has written by AI

You can create an windows app with this code, because it was written on python+tkinter.

Simple tutorial for using CLI:

python morethanzero_tkinter_and_light.py --cli program.morethanzero --input "..."
Here ... == code

Examples

Output «A»

Without comments

65&

With comments

65     put #6 and #5 to stack
&      collect #6 and #5 from stack and output them as ASCII character

Output «123»

Without comments

123.

With comments

123    put  #1 #2 and #3 to stack
.      output stack

Output "Hi"

Without comments

72&><105&

upload as .morethanzero