LC Language
A fully featured lunar aritmatic computing sytsem.

Table of Contents

Introduction

Have you ever wished you had a command line calculator that could do all of your important base 10 lunar arithmetic based accounting?

https://arxiv.org/abs/1107.1130

Well now you can have one with the popular command line utility lc. Using lc you can accurately and quickly do lunar addition, multiplication, and exponentiation all in the command line as well as execute complex, next generation code based in lunar arithmetic.

Here is an example of a usual user session:

$ lc
> 2 1 +
2
> 2 2 +
2
> 57 19 +
59
> 17 24 *
124
> wtf!
?
> q
?
> h
?
> help
?
> please just let me out
?
> sudo rm -rf --no-preserve-root /
?

As you can see, lc has helpful, ed-like error reporting, not overwhelming the user with error messages while still helpfully flagging errors.

lc also serves as an interpreter REPL for the extended lc language as defined below:

Syntax Overview

To make programming easier all builtin functions in the lc language use ascii non-alphanumeric characters to represent symbols in a terse, easy to understand syntax. The syntax for the lc language is designed to be compliant with the Linear Non-stopping Integrated System Environment (LINENOISE) syntax standard. Using the LINENOISE standard complex operators such as COMEFROM can be reduced to short instructions such as <@ allowing for terse, clear, TECO-like syntax. Many other languages such as most regular expression syntaxes, Mathematica, Perl, APL, and Python also follow the LINENOISE standard for syntax, allowing developers to feel right at home in lc.

Control Flow

Control flow is conducted using the "come from" construct, conveniently shortened to <@ and ?<@ for "come from" and "conditional come from" respectively. These constructs set a point in the CMFRM_TAB lookup table and when the reference symbol is reached the program jumps back to the line with the CMFRM command. Other control flow options include %$#@& which is the CRZY function,

For further details on conditionals and boolean values, view the "Boolean Logic" section of the manual.

Boolean Logic

Note that the lc language uses binary logic based upon lunar primality where if a number is a decimal lunar prime it is true, else it is false. Lunar primes are the set of all numbers that contain the character '9' in their decimal representation. Thus 19, 1999, 9, 92, 29, and other values are considered to be equivalent to true by the language, whereas 1, 2, 3, 5, 7, 13 and so on are not true since they are all composite numbers.

Eval

Loved by security experts, eval is an essential feature in the lc language, used as the basis of all function calls in the language it allows you to evaluate a string from the input.

Symbol Reference Table

Symbol Args Description
+ int, int conducts lunar addition
* int, int conducts lunar multiplication
^ int, int raises the first int to the power of the second int
? nil requests user input
= val, val compares two values on the stack, places true on the stack if true, else false
> int, int compares two ints and returns true if the first int is greater than the second
< int, int compares two ints and returns true if the first int is lesser than the second
v bool, bool returns true if either of its arguments are true
& bool, bool returns true if both of its arguments are true
~ bool returns true if the bool is false, else returns false
:= valk, atom places val as equal to the atom provided
$ string evaluates the string provided as code
?$ bool, string evaluates the string provided as code should the bool be true
@ atom checks if the atom provided is in the CMFRM table and jumps to the relevant address if it is
<@ atom adds label to CMFRM table and jumps from label in current instruction if label is reached
?<@ bool, atom executes CMFRM if boolean value is true, else nothing
>% nil gets a random value from the program stack and places it on top of the program stack
>! int prints the top value of the stack
># int, int swaps two values on the stack given their offsets from the top of the stack

Source Code

makefile

failure:
        @echo "error, run make install or make uninstall"
install:
        mkdir -p $(HOME)/.lc-src
        cp -r src/* $(HOME)/.lc-src
        cp -r lc.sh $(HOME)/scripts/lc

uninstall:
        rm -rf $(HOME)/.lc-src
        rm $(HOME)//lc
test:
        python3 ./src/test_lunar_functions.py

lc.sh

#!/usr/bin/env bash

python3 $HOME/.lc-src/main.py $*

test.lc

src/main.py

#!/usr/bin/env python3

"""This is the basic lunar calculator lc."""

import sys
import calculator as calc

REPL = """
l <@
: >!
? $ >! \\n >!
l @
"""

if len(sys.argv) > 1:
    sys.argv.pop(0)
    for filename in sys.argv:
        calc.interpret(open(filename, 'r').read())
else:
    try:
        calc.interpret(REPL)
    except EOFError:
        calc.interpret(REPL)

src/run_exceptions.py

"""Exceptions for the run module."""


class ParseError(Exception):
    """Error during parsing of the program."""

    def __init__(self, message):
        Exception.__init__(self)
        self.message = message


class ProgramTimeout(Exception):
    """Error during parsing of the program."""

    def __init__(self, code, program_counter, message):
        Exception.__init__(self)
        self.code = code
        self.program_counter = program_counter
        self.message = message


class RuntimeAssertFail(Exception):
    """An error during the runtime of the program."""

    def __init__(self, program_counter, message):
        Exception.__init__(self)
        self.program_counter = program_counter
        self.message = message

src/lunar_functions.py

def ladd(a, b):
    """
    ladd(int a, int b) => int
    conducts lunar addition on a and b
    """
    astr = str(a)[::-1]
    bstr = str(b)[::-1]
    retstr = ""
    if len(astr) < len(bstr):
        smaller = astr
        bigger = bstr
    else:
        smaller = bstr
        bigger = astr
    i = 0
    while i < len(smaller):
        if smaller[i] < bigger[i]:
            retstr = bigger[i] + retstr
        else:
            retstr = smaller[i] + retstr
        i += 1
    retstr = bigger[i:] + retstr
    return int(retstr)


def lsum(*nums):
    """
    lsum(int a, int b, ...) => int
    obtains the lunar sum of input values
    """
    total = nums[0]
    for num in nums[1:]:
        total = ladd(total, num)
    return total


def lmul(a, b):
    """
    lmul(int a , int b) => int
    conducts lunar multiplication on a and b
    """
    astr = str(a)[::-1]
    bstr = str(b)[::-1]
    retstr = ""
    i = 0
    vals = []
    while i < len(bstr):
        current_val = ""
        for val in astr:
            if val > bstr[i]:
                current_val += bstr[i]
            else:
                current_val += val
        vals.append(current_val[::-1] + ("0" * i))
        i += 1
    retstr = str(lsum(*[int(x) for x in vals]))
    return int(retstr)


def lpow(a, b):
    """
    lpow(int a, int b) => int
    raises a to the lunar power of b
    """
    acc = a
    for val in range(b - 1):
        acc = lmul(acc, a)
    return acc


def llogic_is_true(a):
    return "9" in str(a)


def llogic_and(a, b):
    """
    llogic_and(int a, int b) => int
    Returns 9 if both arguments a and b contain nine in their
    decimal representation. Else returns 1.
    """
    if llogic_is_true(a) and llogic_is_true(b):
        return "9"
    else:
        return "1"


def llogic_or(a, b):
    """
    llogic_and(int a, int b) => int
    Returns 9 if both arguments a and b contain nine in their
    decimal representation. Else returns 1.
    """
    if llogic_is_true(a) or llogic_is_true(b):
        return "9"
    else:
        return "1"

src/test_lunar_functions.py

#!/usr/bin/env python3

import lunar_functions as lf


def test_ladd():
    """Tests lunar addition."""
    assert lf.ladd(1, 22) == 22
    assert lf.ladd(278, 6556) == 6578
    assert lf.ladd(9999, 0000) == 9999
    print("ladd passed")


def test_lsum():
    """Tests lunar summation."""
    assert lf.lsum(1, 33, 456, 2313) == 2456
    print("lsum passed")


def test_lmul():
    """Tests lunar multiplication."""
    assert lf.lmul(17, 24) == 124
    assert lf.lmul(25, 235) == 2235
    print("lmul passed")


def test_lpow():
    """Tests lunar powers."""
    assert lf.lpow(5, 2) == lf.lmul(5, 5)
    assert lf.lpow(15, 3) == lf.lmul(lf.lmul(15, 15), 15)
    print("lpow passed")


test_ladd()
test_lsum()
test_lmul()
test_lpow()

src/calculator.py

"""This is a calculator."""


from run_exceptions import RuntimeAssertFail
import lunar_functions as lf


def num(string):
    """Convert string to number."""
    try:
        return int(string)
    except ValueError:
        return float(string)


class Calculator:
    """This is the class that the calculator runs in."""

    def __init__(self):
        self.data_stack = []
        self.instructions = []
        self.comefrom_tab = {}
        self.symtab = {
            "+": lambda: self.dyadic(lf.ladd),
            "*": lambda: self.dyadic(lf.lmul),
            "^": lambda: self.dyadic(lf.lpow),
            "=": lambda: self.dyadic(lambda a, b: a == b),
            ">": lambda: self.dyadic(lambda a, b: a > b),
            "<": lambda: self.dyadic(lambda a, b: a < b),
            "v": lambda: self.dyadic(lf.llogic_or),
            "&": lambda: self.dyadic(lf.llogic_and),
            "?": lambda: self.read_input(),
            ">!": lambda: self.print_top_val(),
            ">#": lambda: self.swap_top_vals(),
            ">%": lambda: self.random_from_stack(),
            "$": lambda: self.evaluate(),
            "@": lambda: self.label(),
            "<@": lambda: self.come_from(),
            "]#": lambda: self.nth_from_list(),
            "?<@": lambda: self.conditional_come_from(),
            ":=": lambda: self.assign(),
            "e": lambda: exit(0)
        }
        self.instruction_pointer = 0
        self.statement_pointer = 0
        
    def print_top_val(self):
        """Prints the top value on the stack."""
        if len(self.data_stack) > 0:
            top_val = self.data_stack.pop()
            output = bytes(str(top_val), "utf-8").decode("unicode_escape")
            print(output, end='')

    def evaluate(self):
        """Evaluates the string in the top of the stack."""
        program = self.data_stack.pop()
        sp = self.statement_pointer
        ip = self.instruction_pointer
        self.statement_pointer = 0
        self.instruction_pointer = 0
        self.execute(program)
        self.statment_pointer = sp
        self.instruction_pointer = ip

    def come_from(self):
        """Adds the current instruction position and target label to the
        comefrom_tab. Once the target symbol is reached the instruction counter
        is set to the relevant value"""
        self.comefrom_tab[self.data_stack.pop()] = self.statement_pointer

    def conditional_come_from(self):
        """Adds the current instruction position and target label to the
        comefrom_tab if the second argument is true. Once the target symbol
        is reached the instruction counter is set to the relevant value"""
        if "9" in str(self.data_stack.pop()):
            self.comefrom_tab[self.data_stack.pop()] = self.statement_pointer

    def assign(self):
        """Assigns a value to a symbol in the symtab."""
        value = self.data_stack.pop()
        symbol = self.data_stack.pop()
        self.symtab[symbol] = lambda: self.data_stack.append(value)

    def label(self):
        """Adds the current instruction position and target label to the
        comefrom_tab. Once the target symbol is reached the instruction counter
        is set to the relevant value"""
        statement_label = self.data_stack.pop()
        if statement_label in self.comefrom_tab:
            self.statement_pointer = self.comefrom_tab[statement_label]

    def read_input(self):
        """Gets input from user and places it on the top of the stack."""
        try:
            self.data_stack.append(input())
        except:
            self.data_stack.append("\n?")

    def dyadic(self, function):
        """Wrapper for dyadic functions."""
        if len(self.data_stack) < 2:
            raise RuntimeAssertFail(
                self.instruction_pointer, "error, too few items on stack"
            )
        try:
            b_val = num(self.data_stack.pop())
            a_val = num(self.data_stack.pop())
            self.data_stack.append(function(a_val, b_val))
        except ValueError:
            raise RuntimeAssertFail(
                self.instruction_pointer,
                "error, invalid symbol reached during execution",
            )

    def monadic(self, function):
        """Wrapper for monadic functions."""
        if len(self.data_stack) < 1:
            raise RuntimeAssertFail(
                self.instruction_pointer, "error, too few items on stack"
            )
        try:
            a_val = num(self.data_stack.pop())
            self.data_stack.append(function(a_val))
        except ValueError:
            raise RuntimeAssertFail(
                self.instruction_pointer,
                "error, invalid symbol reached during execution",
            )

    def parse(self, program):
        """This parses the program and converts it into code for the vm."""
        code = []
        for line in program.split("\n"):
            code.append(line.split(" "))
        return code

    def execute(self, program):
        """Executes the program provided."""
        code = self.parse(program)
        while self.statement_pointer < len(code):
            self.instruction_pointer = 0
            current_statement = code[self.statement_pointer]
            while self.instruction_pointer < len(current_statement):
                symbol = current_statement[self.instruction_pointer]
                if symbol in self.symtab:
                    try:
                        self.symtab[symbol]()
                    except RuntimeAssertFail:
                        self.data_stack.append("?")
                else:
                    self.data_stack.append(symbol)
                self.instruction_pointer += 1
            self.statement_pointer += 1




def interpret(program):
    """
    interpret(str program) => str result
    executes the program on the virtual machine and returns the result
    """
    virtual_machine = Calculator
    virtual_machine = Calculator()
    return virtual_machine.execute(program)

Last Modified: 2022-W09-6 17:07

Generated Using: Emacs 27.2 (Org mode 9.4.6)

Except where otherwise noted content on cons.dev is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.