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)