mirror of
https://github.com/mopfel-winrux/NockPU.git
synced 2024-10-06 01:37:16 +03:00
added jammed noun converter
This commit is contained in:
parent
dd4bd97213
commit
2287f99bed
450
memory/noun.py
Normal file
450
memory/noun.py
Normal file
@ -0,0 +1,450 @@
|
||||
"""
|
||||
Urbit nouns with mug, jam, and cue.
|
||||
"""
|
||||
|
||||
import mmh3
|
||||
from bitstream import BitStream
|
||||
|
||||
def byte_length(i: int):
|
||||
"""how many bytes to represent i?
|
||||
|
||||
>>> byte_length(0)
|
||||
0
|
||||
>>> byte_length(255)
|
||||
1
|
||||
>>> byte_length(256)
|
||||
2
|
||||
"""
|
||||
|
||||
lyn = i.bit_length()
|
||||
byt = lyn >> 3
|
||||
return byt + 1 if lyn & 7 else byt
|
||||
|
||||
def intbytes(i: int):
|
||||
"""turn i into a (little endian) bytes object
|
||||
|
||||
>>> intbytes(0)
|
||||
b''
|
||||
>>> intbytes(256)
|
||||
b'\\x00\\x01'
|
||||
"""
|
||||
|
||||
return i.to_bytes(byte_length(i), 'little', signed=False)
|
||||
|
||||
def mum(syd: int, fal: int, key: int):
|
||||
"""try to hash key with syd, incrementing syd on zero
|
||||
hash up to 8 times, falling back to fal.
|
||||
|
||||
>>> mum(0xcafebabe, 0x7fff, 0)
|
||||
2046756072
|
||||
>>> mum(0xdeadbeef, 0xfffe, 8790750394176177384)
|
||||
422532488
|
||||
"""
|
||||
|
||||
k = intbytes(key)
|
||||
for s in range(syd, syd+8):
|
||||
haz = mmh3.hash(k, seed=s, signed=False)
|
||||
ham = (haz >> 31) ^ (haz & 0x7fffffff)
|
||||
if 0 != ham:
|
||||
return ham
|
||||
return fal
|
||||
|
||||
def mug_both(one: int, two: int):
|
||||
"""mug from two other mugs (for cells)
|
||||
|
||||
>>> mug_both(2046756072, 2046756072)
|
||||
422532488
|
||||
"""
|
||||
|
||||
return mum(0xdeadbeef, 0xfffe, (two << 32) | one)
|
||||
|
||||
class Cell:
|
||||
"""A cell is an ordered pair of two nouns.
|
||||
>>> x = Cell(1, Cell(2, 3))
|
||||
>>> x.head
|
||||
1
|
||||
>>> x.tail.head
|
||||
2
|
||||
>>> x.tail.tail
|
||||
3
|
||||
"""
|
||||
|
||||
def __init__(self, head, tail, mug=0):
|
||||
self.head = head
|
||||
self.tail = tail
|
||||
self.mug = mug
|
||||
|
||||
def __hash__(self):
|
||||
"""31-bit non-zero murmur3 (mug)
|
||||
|
||||
>>> x = Cell(0, 0)
|
||||
>>> x.mug
|
||||
0
|
||||
>>> hash(x)
|
||||
422532488
|
||||
>>> x.mug
|
||||
422532488
|
||||
"""
|
||||
|
||||
if 0 == self.mug:
|
||||
self.mug = mug_both(mug(self.head), mug(self.tail))
|
||||
return self.mug
|
||||
|
||||
def __eq__(self, other):
|
||||
"""unifying equality: after comparing equal, cells share storage.
|
||||
|
||||
>>> x = Cell(Cell(1,2),Cell(3,4))
|
||||
>>> y = Cell(Cell(1,2),Cell(3,4))
|
||||
>>> hash(y)
|
||||
1496649457
|
||||
>>> x.head is y.head or x.tail is y.tail
|
||||
False
|
||||
>>> x == y
|
||||
True
|
||||
>>> x.head is y.head and x.tail is y.tail
|
||||
True
|
||||
>>> x.mug != 0 and x.mug == y.mug
|
||||
True
|
||||
"""
|
||||
|
||||
if not deep(other):
|
||||
return False
|
||||
if self.mug != 0 and other.mug != 0 and self.mug != other.mug:
|
||||
return False
|
||||
if self.head != other.head:
|
||||
return False
|
||||
other.head = self.head
|
||||
if self.tail != other.tail:
|
||||
return False
|
||||
other.tail = self.tail
|
||||
if 0 != self.mug:
|
||||
other.mug = self.mug
|
||||
elif 0 != other.mug:
|
||||
self.mug = other.mug
|
||||
return True
|
||||
|
||||
def pretty(self, tail_pos):
|
||||
"""pretty print a cell in or out of tail position
|
||||
|
||||
>>> x = Cell(0, 0)
|
||||
>>> x.pretty(False)
|
||||
'[0 0]'
|
||||
>>> x.pretty(True)
|
||||
'0 0'
|
||||
"""
|
||||
|
||||
content = '%s %s' % \
|
||||
(pretty(self.head, False), pretty(self.tail, True))
|
||||
if tail_pos:
|
||||
return content
|
||||
return '[%s]' % content
|
||||
|
||||
def __str__(self):
|
||||
return self.pretty(False)
|
||||
|
||||
noun = int | Cell
|
||||
|
||||
def deep(n: noun):
|
||||
"""test whether noun is a cell, like nock 3
|
||||
|
||||
>>> deep(1)
|
||||
False
|
||||
>>> deep(Cell(1,2))
|
||||
True
|
||||
"""
|
||||
|
||||
return isinstance(n, Cell)
|
||||
|
||||
def mug(n: noun):
|
||||
"""get the mug for any noun
|
||||
|
||||
>>> mug(0)
|
||||
2046756072
|
||||
>>> mug(Cell(0, 0))
|
||||
422532488
|
||||
"""
|
||||
|
||||
if deep(n):
|
||||
return hash(n)
|
||||
return mum(0xcafebabe, 0x7fff, n)
|
||||
|
||||
def pretty(n: noun, tail_pos:bool):
|
||||
"""pretty-print a noun, in or out of tail position.
|
||||
|
||||
>>> pretty(1, True)
|
||||
'1'
|
||||
>>> pretty(Cell(1,Cell(2,3)), False)
|
||||
'[1 2 3]'
|
||||
>>> pretty(Cell(Cell(1,2), 3), True)
|
||||
'[1 2] 3'
|
||||
"""
|
||||
|
||||
if deep(n):
|
||||
return n.pretty(tail_pos)
|
||||
return str(n)
|
||||
|
||||
def translate(seq):
|
||||
"""turn python sequences into tuples.
|
||||
|
||||
>>> str(translate([1,[2,3],4]))
|
||||
'[1 [2 3] 4]'
|
||||
"""
|
||||
|
||||
def r(i, l):
|
||||
if 1 == l:
|
||||
return seq[i]
|
||||
return Cell(translate(seq[i]), r(i+1, l-1))
|
||||
|
||||
if isinstance(seq, noun):
|
||||
return seq
|
||||
c = len(seq)
|
||||
if 0 == c:
|
||||
return 0
|
||||
return r(0, c)
|
||||
|
||||
def parse(s: str):
|
||||
"""parse strings into nouns. dots in atoms are ignored,
|
||||
outermost braces can be omitted.
|
||||
|
||||
>>> parse('1.024')
|
||||
1024
|
||||
>>> x = parse('[[1 2] 3]')
|
||||
>>> [x.head.head, x.head.tail, x.tail]
|
||||
[1, 2, 3]
|
||||
>>> x = parse('[1 2] 3')
|
||||
>>> [x.head.head, x.head.tail, x.tail]
|
||||
[1, 2, 3]
|
||||
"""
|
||||
|
||||
sep = True
|
||||
start = 0
|
||||
wait = []
|
||||
top = (0, [])
|
||||
num = []
|
||||
|
||||
def frame(i: int):
|
||||
return (i, [])
|
||||
|
||||
def end_atom():
|
||||
nonlocal sep
|
||||
if not sep:
|
||||
sep = True
|
||||
top[1].append(int(''.join(num)))
|
||||
num.clear()
|
||||
|
||||
def end_cell():
|
||||
items = top[1]
|
||||
count = len(items)
|
||||
if 0 == count:
|
||||
return 0
|
||||
count -= 1
|
||||
tail = items[count]
|
||||
while count > 0:
|
||||
count -= 1
|
||||
tail = Cell(items[count], tail)
|
||||
return tail
|
||||
|
||||
for i in range(0, len(s)):
|
||||
c = s[i]
|
||||
match c:
|
||||
case '[':
|
||||
end_atom()
|
||||
wait.append(top)
|
||||
top = frame(i)
|
||||
case ']':
|
||||
if not wait:
|
||||
raise ValueError('unmatched ] at %d' % i)
|
||||
end_atom()
|
||||
val = end_cell()
|
||||
top = wait.pop()
|
||||
top[1].append(val)
|
||||
case ' ':
|
||||
end_atom()
|
||||
case '.':
|
||||
if sep:
|
||||
raise ValueError('floating dot at %d' % i)
|
||||
case _:
|
||||
if not str.isdigit(c):
|
||||
raise ValueError('unrecognized character %s at %d' % (c, i))
|
||||
else:
|
||||
if sep:
|
||||
start = i
|
||||
sep = False
|
||||
num.append(c)
|
||||
if wait:
|
||||
raise ValueError('unclosed [ at %d' % top[0])
|
||||
end_atom()
|
||||
return end_cell()
|
||||
|
||||
def jam_to_stream(n: noun, out: BitStream):
|
||||
"""jam but put the bits into a stream
|
||||
|
||||
>>> s = BitStream()
|
||||
>>> jam_to_stream(Cell(0,0), s)
|
||||
>>> s
|
||||
100101
|
||||
"""
|
||||
|
||||
cur = 0
|
||||
refs = {}
|
||||
|
||||
def bit(b: bool):
|
||||
nonlocal cur
|
||||
out.write(b, bool)
|
||||
cur += 1
|
||||
|
||||
def zero():
|
||||
bit(False)
|
||||
|
||||
def one():
|
||||
bit(True)
|
||||
|
||||
def bits(num: int, count: int):
|
||||
nonlocal cur
|
||||
for i in range(0, count):
|
||||
out.write(0 != (num & (1 << i)), bool)
|
||||
cur += count
|
||||
|
||||
def save(a: noun):
|
||||
refs[a] = cur
|
||||
|
||||
def mat(i: int):
|
||||
if 0 == i:
|
||||
one()
|
||||
else:
|
||||
a = i.bit_length()
|
||||
b = a.bit_length()
|
||||
above = b + 1
|
||||
below = b - 1
|
||||
bits(1 << b, above)
|
||||
bits(a & ((1 << below) - 1), below)
|
||||
bits(i, a)
|
||||
|
||||
def back(ref: int):
|
||||
one()
|
||||
one()
|
||||
mat(ref)
|
||||
|
||||
def r(a: noun):
|
||||
dupe = refs.get(a)
|
||||
if deep(a):
|
||||
if dupe:
|
||||
back(dupe)
|
||||
else:
|
||||
save(a)
|
||||
one()
|
||||
zero()
|
||||
r(a.head)
|
||||
r(a.tail)
|
||||
elif dupe:
|
||||
isize = a.bit_length()
|
||||
dsize = dupe.bit_length()
|
||||
if isize < dsize:
|
||||
zero()
|
||||
mat(a)
|
||||
else:
|
||||
back(dupe)
|
||||
else:
|
||||
save(a)
|
||||
zero()
|
||||
mat(a)
|
||||
r(n)
|
||||
|
||||
def read_int(length: int, s: BitStream):
|
||||
"""read length bits from s and make a python integer.
|
||||
|
||||
>>> s = BitStream()
|
||||
>>> s.write(False, bool)
|
||||
>>> s.write(False, bool)
|
||||
>>> s.write(True, bool)
|
||||
>>> read_int(3, s)
|
||||
4
|
||||
"""
|
||||
|
||||
r = 0
|
||||
for i in range(0, length):
|
||||
r |= s.read(bool) << i
|
||||
return r
|
||||
|
||||
def jam(n: noun):
|
||||
"""urbit serialization: * -> @
|
||||
|
||||
>>> jam(0)
|
||||
2
|
||||
>>> jam(Cell(0,0))
|
||||
41
|
||||
>>> jam(Cell(Cell(1234567890987654321,1234567890987654321), \\
|
||||
... Cell(1234567890987654321,1234567890987654321)))
|
||||
22840095095806892874257389573
|
||||
"""
|
||||
|
||||
out = BitStream()
|
||||
jam_to_stream(n, out)
|
||||
return read_int(len(out), out)
|
||||
|
||||
def cue_from_stream(s: BitStream):
|
||||
"""cue but read the bits from a stream
|
||||
|
||||
>>> s = BitStream()
|
||||
>>> s.write(False, bool)
|
||||
>>> s.write(True, bool)
|
||||
>>> cue_from_stream(s)
|
||||
0
|
||||
"""
|
||||
|
||||
refs = {}
|
||||
cur = 0
|
||||
|
||||
def bits(n: int):
|
||||
nonlocal cur
|
||||
cur += n
|
||||
return read_int(n, s)
|
||||
|
||||
def one():
|
||||
nonlocal cur
|
||||
cur += 1
|
||||
x= s.read(bool)
|
||||
return x
|
||||
|
||||
def rub():
|
||||
z = 0
|
||||
while not one():
|
||||
z += 1
|
||||
if 0 == z:
|
||||
return 0
|
||||
below = z - 1
|
||||
lbits = bits(below)
|
||||
bex = 1 << below
|
||||
return bits(bex ^ lbits)
|
||||
|
||||
def r(start: int):
|
||||
ret = None
|
||||
if one():
|
||||
if one():
|
||||
ret = refs[rub()]
|
||||
else:
|
||||
hed = r(cur)
|
||||
tal = r(cur)
|
||||
ret = Cell(hed, tal)
|
||||
else:
|
||||
ret = rub()
|
||||
refs[start] = ret
|
||||
return ret
|
||||
return r(cur)
|
||||
|
||||
def cue(i: int):
|
||||
"""urbit deserialization: @ -> *
|
||||
|
||||
>>> str(cue(22840095095806892874257389573))
|
||||
'[[1234567890987654321 1234567890987654321] 1234567890987654321 1234567890987654321]'
|
||||
"""
|
||||
|
||||
s = BitStream()
|
||||
while i > 0:
|
||||
s.write(i & 1, bool)
|
||||
i >>= 1
|
||||
return cue_from_stream(s)
|
||||
|
||||
if '__main__' == __name__:
|
||||
import doctest
|
||||
doctest.testmod()
|
80
memory/noun_converter.py
Normal file
80
memory/noun_converter.py
Normal file
@ -0,0 +1,80 @@
|
||||
from noun import * # make this a pip package some day
|
||||
import sys
|
||||
|
||||
def cue_noun(data):
|
||||
x = cue(int.from_bytes(data[5:], 'little'))
|
||||
|
||||
hed_len = (x.head.bit_length()+7)//8
|
||||
mark = x.head.to_bytes(hed_len,'little').decode()
|
||||
noun = x.tail
|
||||
return (mark,noun)
|
||||
|
||||
|
||||
memory = [0]
|
||||
bitmask = (1 << 28) -1
|
||||
def inorder_traversal(noun):
|
||||
if(isinstance(noun,int)):
|
||||
print("error")
|
||||
return
|
||||
|
||||
if(len(memory) == 1):
|
||||
memory.append(1<<63) # mark first cell as execute
|
||||
else:
|
||||
memory.append(0)
|
||||
|
||||
if(isinstance(noun.head, Cell) and isinstance(noun.tail, Cell)):
|
||||
memory[-1] = memory[-1] | len(memory) << 28
|
||||
cell_loc = len(memory)-1
|
||||
inorder_traversal(noun.head)
|
||||
memory[cell_loc] = memory[cell_loc] | len(memory)
|
||||
inorder_traversal(noun.tail)
|
||||
elif(isinstance(noun.head, Cell) and isinstance(noun.tail, int)):
|
||||
memory[-1] = memory[-1] | (1<<56) | len(memory) << 28
|
||||
inorder_traversal(noun.head)
|
||||
memory[-1] = memory[-1] | (noun.tail & bitmask)
|
||||
elif(isinstance(noun.head, int) and isinstance(noun.tail, Cell)):
|
||||
memory[-1] = memory[-1] | (1<<57) | (noun.head & bitmask) << 28 | len(memory)
|
||||
inorder_traversal(noun.tail)
|
||||
elif(isinstance(noun.head, int) and isinstance(noun.tail, int)):
|
||||
memory[-1] = memory[-1] | (1<<57) | (1<<56) | (noun.head & bitmask) << 28 | (noun.tail & bitmask)
|
||||
|
||||
|
||||
number = "59.500.485.596.334.891.570.437"
|
||||
|
||||
def main():
|
||||
# Check if two arguments (excluding the script name) are provided
|
||||
if len(sys.argv) != 3:
|
||||
print("Usage: python3 noun_converter.py <jammed-noun> <filename>")
|
||||
sys.exit(1)
|
||||
|
||||
# Extract arguments
|
||||
number_str = sys.argv[1]
|
||||
filename = sys.argv[2]
|
||||
noun = None
|
||||
# Validate if the first argument is a number
|
||||
try:
|
||||
# Attempt to convert the number argument to a float
|
||||
number = int(number_str.replace('.',''))
|
||||
noun = cue(number)
|
||||
#pretty(noun, False)
|
||||
print(noun)
|
||||
except ValueError:
|
||||
print("The first argument must be a number.")
|
||||
sys.exit(1)
|
||||
|
||||
inorder_traversal(noun)
|
||||
memory[0] = len(memory)
|
||||
#for mem in memory:
|
||||
# print(format(mem, '016x'))
|
||||
|
||||
# Work with the file
|
||||
try:
|
||||
with open(filename, 'w') as file:
|
||||
for mem in memory:
|
||||
file.write(format(mem, '016x')+'\n')
|
||||
except IOError as e:
|
||||
print(f"An error occurred while working with the file: {e}")
|
||||
sys.exit(1)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
Loading…
Reference in New Issue
Block a user