Lythnology/Implementations

From Esolang
Jump to navigation Jump to search

Python (Made by Claude AI)

import random
import sys

def number_to_words(n):
    ones = ["", "ONE", "TWO", "THREE", "FOUR", "FIVE", "SIX", "SEVEN", "EIGHT", "NINE"]
    teens = ["TEN", "ELEVEN", "TWELVE", "THIRTEEN", "FOURTEEN", "FIFTEEN", "SIXTEEN", "SEVENTEEN", "EIGHTEEN", "NINETEEN"]
    tens = ["", "", "TWENTY", "THIRTY", "FORTY", "FIFTY", "SIXTY", "SEVENTY", "EIGHTY", "NINETY"]
    thousands = [
        "", "THOUSAND", "MILLION", "BILLION", "TRILLION", "QUADRILLION", "QUINTILLION", "SEXTILLION", "SEPTILLION",
        "OCTILLION", "NONILLION", "DECILLION", "UNDECILLION", "DUODECILLION", "TREDECILLION", "QUATTUORDECILLION",
        "QUINDECILLION", "SEXDECILLION", "SEPTENDECILLION", "OCTODECILLION", "NOVEMDECILLION", "VIGINTILLION",
        "UNVIGINTILLION", "DUOVIGINTILLION", "TREVIGINTILLION", "QUATTUORVIGINTILLION", "QUINVIGINTILLION",
        "SEXVIGINTILLION", "SEPTENVIGINTILLION", "OCTOVIGINTILLION", "NOVEMVIGINTILLION", "TRIGINTILLION",
        "UNTRIGINTILLION", "DUOTRIGINTILLION", "CENTILLION"
    ]
    def chunk_to_words(chunk):
        words = ""
        h, rem = divmod(chunk, 100)
        if h:
            words += ones[h] + "HUNDRED"
        if 10 <= rem < 20:
            words += teens[rem - 10]
        else:
            t, o = divmod(rem, 10)
            words += tens[t] + ones[o]
        return words
    if n == 0:
        return "ZERO"
    parts = []
    chunk_index = 0
    while n > 0:
        n, chunk = divmod(n, 1000)
        if chunk:
            w = chunk_to_words(chunk)
            if thousands[chunk_index]:
                w += thousands[chunk_index]
            parts.insert(0, w)
        chunk_index += 1
    return ''.join(parts)

def replace_numbers_with_words(s):
    result, i = '', 0
    while i < len(s):
        if s[i].isdigit():
            num = ''
            while i < len(s) and s[i].isdigit():
                num += s[i]; i += 1
            result += number_to_words(int(num))
        else:
            result += s[i]; i += 1
    return result

class LythnologyInterpreter:
    def __init__(self):
        self.words = {
            "IF": {"affordances": ["conditional"], "values": ["IF"]},
            "IS": {"affordances": ["assignment"], "values": ["IS"]},
            "THEN": {"affordances": ["jump_target"], "values": ["THEN"]},
            "TO": {"affordances": ["transform_connector"], "values": ["TO"]},
            "TRANSFORMS": {"affordances": ["transformer"], "values": ["TRANSFORMS"]},
            "WHEN": {"affordances": ["across_jump"], "values": ["WHEN"]},
            "SAY": {"affordances": ["print"], "values": ["SAY"]},
            "INPUT": {"affordances": ["input"], "values": ["INPUT"]},
            "AND": {"affordances": ["concat_all"], "values": ["AND"]},
            "WITH": {"affordances": ["concat"], "values": ["WITH"]},
            "OR": {"affordances": ["random_choice"], "values": ["OR"]},
            "NOT": {"affordances": ["negation"], "values": ["NOT"]},
            "PAUSE": {"affordances": ["new_line"], "values": ["PAUSE"]}
        }
        self.program = []
        self.position = 0
        self.word_positions = {}

    def parse_program(self, code):
        lines = code.split('\n')
        proc = []
        for line in lines:
            if '#' in line: line = line[:line.index('#')]
            proc.append(line)
        tokens = ' '.join(proc).split()
        self.program = tokens
        self.word_positions = {}
        for idx, w in enumerate(self.program):
            self.word_positions.setdefault(w, []).append(idx)

    def create_word(self, w):
        if w not in self.words:
            self.words[w] = {"affordances": [], "values": [w]}

    def get_word_value(self, w):
        if w not in self.words: self.create_word(w)
        return " ".join(self.words[w]["values"])

    def run(self, code):
        self.parse_program(code)
        self.position = 0
        while self.position < len(self.program):
            self.evaluate_next_word()
        #print(self.words)

    def evaluate_next_word(self):
        if self.position >= len(self.program): return False
        current = self.program[self.position]
        # ensure unknown words exist
        if current not in self.words:
            self.create_word(current)
            self.position += 1
            return True
        self.position += 1

        # 1) transformation peek
        if "transform" in self.words[current]["affordances"]:
            self.handle_transform()
            if "print" in self.words[current]["affordances"]:
                self.handle_print()
        elif "print" in self.words[current]["affordances"]:
            self.handle_print()
        elif "assignment" in self.words[current]["affordances"]:
            self.handle_assignment()
        elif "new_line" in self.words[current]["affordances"]:
            print("\n", end="")
        elif "conditional" in self.words[current]["affordances"]:
            self.handle_conditional()
        elif "across_jump" in self.words[current]["affordances"]:
            self.handle_across_jump()
        elif "transformer" in self.words[current]["affordances"]:
            self.handle_transformer()
        return True

    def handle_print(self):
        if self.position < len(self.program):
            w = self.program[self.position]; self.position += 1
            self.create_word(w)
            print(self.get_word_value(w), end=" ")

    def handle_assignment(self):
        # same as original assignment code
        if self.position < 2 or self.position >= len(self.program): return
        left = self.program[self.position - 2]
        right = self.program[self.position]; self.position += 1
        self.create_word(left); self.create_word(right)
        if right == "INPUT":
            user = input()
            user = replace_numbers_with_words(user)
            user = ''.join(c for c in user if c.isalpha() or c.isspace()).upper()
            user = user.split()[0] if user else ""
            self.words[left]["values"] = [user]; return
        if self.position < len(self.program) and self.program[self.position] == "OR":
            opts = [right]
            while self.position < len(self.program) and self.program[self.position] == "OR":
                self.position += 1
                if self.position < len(self.program):
                    o = self.program[self.position]; opts.append(o)
                    self.create_word(o); self.position += 1
            choice = random.choice(opts)
            if choice in self.words:
                self.words[left] = {"affordances": self.words[choice]["affordances"].copy(),
                                     "values": self.words[choice]["values"].copy()}
            else:
                self.create_word(choice)
                self.words[left]["values"] = [choice]
            return
        if self.position < len(self.program) and self.program[self.position] == "AND":
            vals = self.words[right]["values"].copy()
            affs = self.words[right]["affordances"].copy()
            while self.position < len(self.program) and self.program[self.position] == "AND":
                self.position += 1
                if self.position < len(self.program):
                    nxt = self.program[self.position]; self.create_word(nxt)
                    vals.extend(self.words[nxt]["values"])
                    affs.extend(a for a in self.words[nxt]["affordances"] if a not in affs)
                    self.position += 1
            self.words[left]["values"] = vals; self.words[left]["affordances"] = affs; return
        if self.position < len(self.program) and self.program[self.position] == "WITH":
            comb = ''.join(self.words[right]["values"])
            while self.position < len(self.program) and self.program[self.position] == "WITH":
                self.position += 1
                if self.position < len(self.program):
                    nxt = self.program[self.position]; self.create_word(nxt)
                    comb += ''.join(self.words[nxt]["values"])
                    self.position += 1
            self.words[left]["values"] = [comb]; return
        if right in self.words:
            self.words[left] = {"affordances": self.words[right]["affordances"].copy(),
                                 "values": self.words[right]["values"].copy()}
        else:
            self.create_word(right)
            self.words[left]["values"] = [right]

    def handle_conditional(self):
        if self.position + 1 >= len(self.program):
            self.position = len(self.program)
            return
        
        l = self.program[self.position]
        isw = self.program[self.position + 1]
        
        if isw != "IS":
            ti = self.position + 1
            while ti < len(self.program) and self.program[ti] != "THEN": 
                ti += 1
            self.position = ti + 1 if ti < len(self.program) else len(self.program)
            return
        
        self.position += 2
        neg = False
        
        if self.position < len(self.program) and self.program[self.position] == "NOT":
            neg = True
            self.position += 1
        
        # Check if we're doing an AND comparison
        using_and = False
        comps = []
        
        if self.position < len(self.program):
            cw = self.program[self.position]
            self.create_word(cw)
            comps.append(cw)
            self.position += 1
        
        # Collect all comparison words, noting whether we're using AND or OR
        while self.position + 1 < len(self.program) and (self.program[self.position] == "OR" or self.program[self.position] == "AND"):
            connector = self.program[self.position]
            if connector == "AND":
                using_and = True
            
            self.position += 1
            ncw = self.program[self.position]
            self.create_word(ncw)
            comps.append(ncw)
            self.position += 1
        
        self.create_word(l)
        left_vals = self.words[l]["values"]
        
        # Handle different logic based on AND vs OR
        if using_and:
            # For AND, ALL conditions must be met
            met = True
            for cw in comps:
                if not any(rv in left_vals for rv in self.words[cw]["values"]):
                    met = False
                    break
        else:
            # For OR, ANY condition must be met
            met = False
            for cw in comps:
                if any(rv in left_vals for rv in self.words[cw]["values"]):
                    met = True
                    break
        
        if neg:
            met = not met
        
        if not met:
            ti = self.position
            while ti < len(self.program) and self.program[ti] != "THEN":
                ti += 1
            if ti < len(self.program):
                self.position = ti + 1
                    
    def handle_across_jump(self):
        if self.position >= len(self.program):
            return
            
        tgt = self.program[self.position]
        self.position += 1
        
        if tgt not in self.word_positions:
            return
            
        # Find all positions of the target word
        positions = self.word_positions[tgt]
        
        # Current position is right after WHEN
        current_pos = self.position - 1
        
        # Need to find the next occurrence of target word
        # or wrap around to the first occurrence if no next
        next_positions = [p for p in positions if p > current_pos]
        
        if next_positions:
            # Jump to the next occurrence
            self.position = min(next_positions) + 1
        else:
            # Wrap around to the first occurrence
            self.position = min(positions) + 1

    def handle_transformer(self):
        if self.position<2: return
        tr=self.program[self.position-2]
        if "transform" not in self.words[tr]["affordances"]:
            self.words[tr]["affordances"].append("transform")
        seq_map={}
        pos=self.position
        while pos<len(self.program):
            src=self.program[pos]; self.create_word(src); chain=[src]; pos+=1
            while pos<len(self.program) and self.program[pos]=="TO": pos+=1; tgt=self.program[pos]; self.create_word(tgt); chain.append(tgt); pos+=1
            for i in range(len(chain)-1): seq_map[chain[i]]=chain[i+1]
            if not (pos+1<len(self.program) and self.program[pos+1]=="TO"): break
        self.position=pos
        self.words[tr]["transformations"]=seq_map

    def handle_transform(self):
        tr=self.program[self.position-1]
        if self.position>=len(self.program): return
        tgt=self.program[self.position]
        if "transformations" not in self.words[tr]: return
        self.create_word(tgt)
        cur=''.join(self.words[tgt]["values"])
        trans=self.words[tr]["transformations"]
        if cur in trans:
            nv=trans[cur]
            if nv in self.words: self.words[tgt]["values"]=self.words[nv]["values"].copy()
            else: self.create_word(nv); self.words[tgt]["values"]=[nv]

if __name__ == "__main__":
    interp=LythnologyInterpreter()
    if len(sys.argv)<2:
        print("Usage: python lyth.py <filename>"); sys.exit(1)
    with open(sys.argv[1],"r") as f: code=f.read()
    interp.run(code)