Compare commits
4 Commits
2d64d09f24
...
0d73e80c59
Author | SHA1 | Date |
---|---|---|
stupidcomputer | 0d73e80c59 | |
stupidcomputer | e8da284365 | |
stupidcomputer | 200d01f9ad | |
stupidcomputer | 38877d36ab |
24
README.md
24
README.md
|
@ -1,15 +1,14 @@
|
||||||
# desmos-computer
|
# desmos-computer
|
||||||
|
|
||||||
## What is this?
|
## What is this?
|
||||||
- a client-server architecture for synchronizing a file containing Desmos expressions to a graph,
|
- a client-server architecture for synchronizing a file containing Desmos expressions to a graph, (`cli/lib/server.py` and `cli/lib/clientside.py`)
|
||||||
- a test suite for evaluating the 'correctness' of Desmos expressions,
|
- an instruction set architecture for Turing machines whose cells contain IEEE 754 compliant integers in each cell, (`cli/data/computer.py`)
|
||||||
- an instruction set architecture for Turing machines whose cells contain IEEE 754 compliant integers in each cell,
|
- an assembler for that instruction set architecture, (not yet!)
|
||||||
- an assembler for that instruction set architecture,
|
- and other utilities. (disassembler, etc.)
|
||||||
- and other utilities.
|
|
||||||
|
|
||||||
## How does the ISA work?
|
## How does the ISA work?
|
||||||
The 'CPU', implemented in Desmos, takes in a list (aka an array, Turing tape, etc.) and starts execution at cell 1 (lists have 1-based indexes in Desmos).
|
The 'CPU', implemented in Desmos, takes in a list (aka an array, Turing tape, etc.) and starts execution at cell 1 (lists have 1-based indexes in Desmos).
|
||||||
The list also serves as the memory, as well. (Think like Befunge's `p` command.
|
The list also serves as the memory, as well. (Think like Befunge's `p` command.)
|
||||||
|
|
||||||
*Todo: disconnect opcode definitions and opcodes from the actual CPU implementation. Because of this, don't rely on this table! Check the implementation in `data/computer.desmos`.*
|
*Todo: disconnect opcode definitions and opcodes from the actual CPU implementation. Because of this, don't rely on this table! Check the implementation in `data/computer.desmos`.*
|
||||||
|
|
||||||
|
@ -43,12 +42,21 @@ Things we're optimizing for:
|
||||||
In general: *embed the intelligence into the machine code, **not** the CPU/ISA!*
|
In general: *embed the intelligence into the machine code, **not** the CPU/ISA!*
|
||||||
|
|
||||||
## Things to do
|
## Things to do
|
||||||
- [ ] Write a test suite for evaluating arbitrary Desmos expressions and getting their expected outputs.
|
- [x] Write a test suite for the various instruction of the ISA executing *in Desmos*
|
||||||
- [ ] Write an assembler to compile a custom Assembly language to native Desmos list format.
|
- [ ] Write an assembler to compile a custom Assembly language to native Desmos list format.
|
||||||
- [ ] Simplify all this into a command line tool.
|
- [ ] Simplify all this into a command line tool.
|
||||||
- [ ] Simplify the synchronization stack.
|
- [x] Simplify the synchronization stack.
|
||||||
- [ ] Write documentation for all of this.
|
- [ ] Write documentation for all of this.
|
||||||
|
|
||||||
|
## Running tests
|
||||||
|
|
||||||
|
- Enter the nix-shell, then start a web browser.
|
||||||
|
- Navigate to `https://desmos.com/calculator`, and open the dev console
|
||||||
|
- Run `python3 -m cli sync -c` to copy the userscript to your clipboard
|
||||||
|
- Run the userscript in the console
|
||||||
|
- Run `python3 -m unittest ./cli/tests/isa.py`, or other test groups if needed
|
||||||
|
- Keep the Desmos tab focused, as it may impede the ISA testing process
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
This project is licensed under the AGPLv3. See the `LICENSE` file for more information.
|
This project is licensed under the AGPLv3. See the `LICENSE` file for more information.
|
||||||
|
|
99
cli/cli.py
99
cli/cli.py
|
@ -2,29 +2,86 @@ from .lib.server import DesmosGraphServer
|
||||||
from .lib.graphparser import DesmosGraph, DesmosGraphOverride
|
from .lib.graphparser import DesmosGraph, DesmosGraphOverride
|
||||||
|
|
||||||
from .lib.clientside import payload as JSGraphPayload
|
from .lib.clientside import payload as JSGraphPayload
|
||||||
from .tests.isa import test_entry_point
|
from .data.computer import payload as ComputerPayload
|
||||||
|
from .data.testing import payload as TestingPayload
|
||||||
|
|
||||||
|
import pyperclip
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
|
||||||
|
def handle_sync(parser):
|
||||||
|
if parser.copy_userscript:
|
||||||
|
pyperclip.copy(JSGraphPayload)
|
||||||
|
print("copied userscript to clipboard")
|
||||||
|
|
||||||
|
if parser.filename:
|
||||||
|
graph = DesmosGraph.from_file(parser.filename)
|
||||||
|
|
||||||
|
if parser.override:
|
||||||
|
override = DesmosGraphOverride.from_file(parser.override)
|
||||||
|
graph.include_override(override)
|
||||||
|
|
||||||
|
server = DesmosGraphServer()
|
||||||
|
server.instructions_to_run = []
|
||||||
|
server.append_inst({
|
||||||
|
"type": "insert_graph",
|
||||||
|
"graph": graph,
|
||||||
|
})
|
||||||
|
server.start(no_stop=True)
|
||||||
|
|
||||||
|
def handle_data(parser):
|
||||||
|
if parser.dataname:
|
||||||
|
if parser.dataname == "computer.desmos":
|
||||||
|
print(ComputerPayload)
|
||||||
|
elif parser.dataname == "testing.desmos":
|
||||||
|
print(TestingPayload)
|
||||||
|
return
|
||||||
|
|
||||||
|
if parser.list:
|
||||||
|
parsers = ["computer.desmos", "testing.desmos"]
|
||||||
|
print('\n'.join(parsers))
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
# graph = DesmosGraph.from_file("data/computer.desmos")
|
parser = argparse.ArgumentParser(
|
||||||
# override = DesmosGraphOverride.from_file("test.override")
|
prog="desmosisa",
|
||||||
#
|
description="a smörgåsbord of utilities for desmos, including some implementations of an desmos-based isa",
|
||||||
# graph.include_override(override)
|
)
|
||||||
# server = DesmosGraphServer()
|
subparsers = parser.add_subparsers(dest="subparser_name")
|
||||||
# server.append_inst({
|
sync_parser = subparsers.add_parser("sync", help="desmos calculator synchronization utilities")
|
||||||
# "type": "insert_graph",
|
sync_parser.add_argument(
|
||||||
# "graph": graph,
|
'filename',
|
||||||
# })
|
nargs='?',
|
||||||
# server.append_inst({
|
help="filename of DesmosExpressions to synchronize with client"
|
||||||
# "type": "test_graph",
|
)
|
||||||
# "graph": graph,
|
sync_parser.add_argument(
|
||||||
# "name": "test and assert addition",
|
'-o', '--override',
|
||||||
# "expectedOutput": [1, 4, 6, 0, 0, 4],
|
help="filename of DesmosOverride file, to override certain expressions in the DesmosExpression file",
|
||||||
# "expression": "B",
|
action="store"
|
||||||
# })
|
)
|
||||||
# server.start()
|
sync_parser.add_argument(
|
||||||
# print(server.outputs)
|
'-c', '--copy-userscript',
|
||||||
#
|
help="copy the userscript to the clipboard, to be pasted into the JS console within the calculator.",
|
||||||
test_entry_point()
|
action="store_true"
|
||||||
|
)
|
||||||
|
|
||||||
|
data_parser = subparsers.add_parser("data", help="access various prebuilt files")
|
||||||
|
data_parser.add_argument(
|
||||||
|
'dataname',
|
||||||
|
nargs='?',
|
||||||
|
help='name of the datafile requested'
|
||||||
|
)
|
||||||
|
data_parser.add_argument(
|
||||||
|
'-l', '--list',
|
||||||
|
help='list available datafiles',
|
||||||
|
action='store_true'
|
||||||
|
)
|
||||||
|
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
if args.subparser_name == "sync":
|
||||||
|
handle_sync(args)
|
||||||
|
if args.subparser_name == "data":
|
||||||
|
handle_data(args)
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
main()
|
main()
|
||||||
|
|
|
@ -22,10 +22,10 @@ id testing : B = []
|
||||||
: idiv(a, b, t_o) = setlistval(t_o, odiv(B[a], B[b]))
|
: idiv(a, b, t_o) = setlistval(t_o, odiv(B[a], B[b]))
|
||||||
: imul(a, b, t_o) = setlistval(t_o, omul(B[a], B[b]))
|
: imul(a, b, t_o) = setlistval(t_o, omul(B[a], B[b]))
|
||||||
|
|
||||||
: icmp(a, b, z) = { \
|
: icmp(a, b, z) = { \\
|
||||||
a = b : equals -> 1 , \
|
B[a] = B[b] : equals -> 1 , \\
|
||||||
a > b : greater -> 1 , \
|
B[a] > B[b] : greater -> 1 , \\
|
||||||
a < b : less -> 1 \
|
B[a] < B[b] : less -> 1 \\
|
||||||
}
|
}
|
||||||
: irst(a, b, c) = equals -> 0, greater -> 0, less -> 0
|
: irst(a, b, c) = equals -> 0, greater -> 0, less -> 0
|
||||||
: ield(addr, b) = setlistval(addr, equals)
|
: ield(addr, b) = setlistval(addr, equals)
|
||||||
|
@ -40,7 +40,6 @@ id testing : B = []
|
||||||
|
|
||||||
: isto(v, addr) = setlistval(addr, v)
|
: isto(v, addr) = setlistval(addr, v)
|
||||||
: imov(from, target) = setlistval(target, B[from])
|
: imov(from, target) = setlistval(target, B[from])
|
||||||
: ipsto(value, ptr) = setlistval(B[ptr], value)
|
|
||||||
|
|
||||||
# registers
|
# registers
|
||||||
# instruction pointer
|
# instruction pointer
|
||||||
|
@ -64,27 +63,26 @@ id testing : B = []
|
||||||
: paramthree = 0
|
: paramthree = 0
|
||||||
|
|
||||||
# main execution flows
|
# main execution flows
|
||||||
: load(addr) = jumped -> 0, inst -> B[addr], \
|
: load(addr) = jumped -> 0, inst -> B[addr], \\
|
||||||
paramone -> B[addr + 1], \
|
paramone -> B[addr + 1], \\
|
||||||
paramtwo -> B[addr + 2], \
|
paramtwo -> B[addr + 2], \\
|
||||||
paramthree -> B[addr + 3]
|
paramthree -> B[addr + 3]
|
||||||
: exec = { \
|
: exec = { \\
|
||||||
inst = sto : isto(paramone, paramtwo), \
|
inst = sto : isto(paramone, paramtwo), \\
|
||||||
inst = psto : ipsto(paramone, paramtwo), \
|
inst = mov : imov(paramone, paramtwo), \\
|
||||||
inst = mov : imov(paramone, paramtwo), \
|
inst = add : iadd(paramone, paramtwo, paramthree), \\
|
||||||
inst = add : iadd(paramone, paramtwo, paramthree), \
|
inst = cmp : icmp(paramone, paramtwo, paramthree), \\
|
||||||
inst = cmp : icmp(paramone, paramtwo, paramthree), \
|
inst = eld : ield(paramone, paramtwo), \\
|
||||||
inst = eld : ield(paramone, paramtwo), \
|
inst = gld : igld(paramone, paramtwo), \\
|
||||||
inst = gld : igld(paramone, paramtwo), \
|
inst = lld : illd(paramone, paramtwo), \\
|
||||||
inst = lld : illd(paramone, paramtwo), \
|
inst = jmp : ijmp(paramone, paramtwo, paramthree), \\
|
||||||
inst = jmp : ijmp(paramone, paramtwo, paramthree), \
|
inst = be : ibe(paramone, paramtwo, paramthree), \\
|
||||||
inst = be : ibe(paramone, paramtwo, paramthree), \
|
inst = bne : ibne(paramone, paramtwo, paramthree), \\
|
||||||
inst = bne : ibne(paramone, paramtwo, paramthree), \
|
inst = bg : ibg(paramone, paramtwo, paramthree), \\
|
||||||
inst = bg : ibg(paramone, paramtwo, paramthree), \
|
inst = bl : ibl(paramone, paramtwo, paramthree), \\
|
||||||
inst = bl : ibl(paramone, paramtwo, paramthree), \
|
inst = sub : isub(paramone, paramtwo, paramthree), \\
|
||||||
inst = sub : isub(paramone, paramtwo, paramthree), \
|
inst = mul : imul(paramone, paramtwo, paramthree), \\
|
||||||
inst = mul : imul(paramone, paramtwo, paramthree), \
|
inst = div : idiv(paramone, paramtwo, paramthree) \\
|
||||||
inst = div : idiv(paramone, paramtwo, paramthree) \
|
|
||||||
}
|
}
|
||||||
: incip = {jumped = 0 : ip -> ip + instwidth[inst] + 1}
|
: incip = {jumped = 0 : ip -> ip + instwidth[inst] + 1}
|
||||||
|
|
||||||
|
@ -97,7 +95,6 @@ id testing : B = []
|
||||||
: main = loopaction, loop
|
: main = loopaction, loop
|
||||||
|
|
||||||
: sto = 1
|
: sto = 1
|
||||||
: psto = 16
|
|
||||||
: mov = 17
|
: mov = 17
|
||||||
: add = 2
|
: add = 2
|
||||||
: cmp = 3
|
: cmp = 3
|
||||||
|
@ -114,5 +111,5 @@ id testing : B = []
|
||||||
: div = 14
|
: div = 14
|
||||||
: rst = 15
|
: rst = 15
|
||||||
|
|
||||||
: instwidth = [2,3,1,1,1,1,1,1,1,1,3,3,3,3,0,2,2]
|
: instwidth = [2,3,2,1,1,1,1,1,1,1,3,3,3,3,0,2,2]
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -4,5 +4,5 @@ payload = """
|
||||||
: m = 3
|
: m = 3
|
||||||
: y = m * x + b
|
: y = m * x + b
|
||||||
: y = 2m * x + b
|
: y = 2m * x + b
|
||||||
: \frac{x^{2+3}}{3}*4*testing
|
: \\frac{x^{2+3}}{3}*4*testing
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -11,6 +11,8 @@ function arraysEqual(a, b) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var refresh = true;
|
||||||
|
|
||||||
function main() {
|
function main() {
|
||||||
let socket = new WebSocket("ws://localhost:8764");
|
let socket = new WebSocket("ws://localhost:8764");
|
||||||
var toCompare = "";
|
var toCompare = "";
|
||||||
|
@ -21,10 +23,12 @@ function main() {
|
||||||
}
|
}
|
||||||
|
|
||||||
socket.onclose = function(e) {
|
socket.onclose = function(e) {
|
||||||
|
if (refresh) {
|
||||||
setTimeout(function() {
|
setTimeout(function() {
|
||||||
main();
|
main();
|
||||||
}, 1000);
|
}, 1000);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
socket.onmessage = function(e) {
|
socket.onmessage = function(e) {
|
||||||
var message = JSON.parse(e.data);
|
var message = JSON.parse(e.data);
|
||||||
|
@ -83,5 +87,5 @@ function main() {
|
||||||
console.log(`[LOG] couldn't parse message ${e.data}`)
|
console.log(`[LOG] couldn't parse message ${e.data}`)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} main();
|
}
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -89,18 +89,25 @@ class DesmosGraphServer:
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if self.stop:
|
||||||
self.stop.set_result("sotp please!!!!")
|
self.stop.set_result("sotp please!!!!")
|
||||||
|
|
||||||
async def main(self, stop):
|
async def main(self, stop):
|
||||||
async with serve(self.ws_main, "localhost", 8764):
|
async with serve(self.ws_main, "localhost", 8764):
|
||||||
self.stop = stop
|
self.stop = stop
|
||||||
|
if not stop:
|
||||||
|
await asyncio.Future()
|
||||||
|
else:
|
||||||
await stop
|
await stop
|
||||||
|
|
||||||
def start(self):
|
def start(self, no_stop=False):
|
||||||
loop = asyncio.get_event_loop()
|
loop = asyncio.get_event_loop()
|
||||||
stop = loop.create_future()
|
stop = loop.create_future()
|
||||||
|
|
||||||
loop.run_until_complete(self.main(stop))
|
if no_stop:
|
||||||
|
loop.run_until_complete(self.main(stop=None))
|
||||||
|
else:
|
||||||
|
loop.run_until_complete(self.main(stop=stop))
|
||||||
|
|
||||||
def append_inst(self, inst):
|
def append_inst(self, inst):
|
||||||
self.instructions_to_run.append(inst)
|
self.instructions_to_run.append(inst)
|
||||||
|
|
|
@ -31,7 +31,6 @@ def instruction_test_helper(override_text, expected_output):
|
||||||
})
|
})
|
||||||
server.start()
|
server.start()
|
||||||
|
|
||||||
time.sleep(1)
|
|
||||||
return server.outputs[-1]["output"] == "true"
|
return server.outputs[-1]["output"] == "true"
|
||||||
|
|
||||||
class ISATest(unittest.TestCase):
|
class ISATest(unittest.TestCase):
|
||||||
|
@ -82,3 +81,77 @@ class ISATest(unittest.TestCase):
|
||||||
[14, 1, 6, 5, 3.5, 4],
|
[14, 1, 6, 5, 3.5, 4],
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def test_jmp(self):
|
||||||
|
self.assertTrue(
|
||||||
|
instruction_test_helper(
|
||||||
|
[7, 10, 0, 0, 0, 0, 0, 0, 0, 1, 3, 3], # jump to addr 10
|
||||||
|
[7, 10, 3, 0, 0, 0, 0, 0, 0, 1, 3, 3]
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_cmp_eq(self):
|
||||||
|
self.assertTrue(
|
||||||
|
instruction_test_helper(
|
||||||
|
# check if 3 is equal to 3. if so, store 3 into addr 3, otherwise store 1 into address 2
|
||||||
|
[3, 1, 1, 8, 10, 1, 1, 2, 0, 1, 3, 3],
|
||||||
|
[3, 1, 3, 8, 10, 1, 1, 2, 0, 1, 3, 3],
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_cmp_neq(self):
|
||||||
|
self.assertTrue(
|
||||||
|
instruction_test_helper(
|
||||||
|
# check if 3 is equal to 1. if not, store 3 into addr 3, otherwise store 1 into address 2
|
||||||
|
[3, 1, 2, 9, 10, 1, 1, 2, 0, 1, 3, 3],
|
||||||
|
[3, 1, 3, 9, 10, 1, 1, 2, 0, 1, 3, 3],
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_cmp_gt(self):
|
||||||
|
self.assertTrue(
|
||||||
|
instruction_test_helper(
|
||||||
|
[3, 1, 2, 10, 10, 1, 1, 2, 0, 1, 3, 3],
|
||||||
|
[3, 1, 3, 10, 10, 1, 1, 2, 0, 1, 3, 3],
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_cmp_lt(self):
|
||||||
|
self.assertTrue(
|
||||||
|
instruction_test_helper(
|
||||||
|
[3, 1, 4, 11, 10, 1, 1, 2, 0, 1, 3, 3],
|
||||||
|
[3, 1, 4, 11, 10, 1, 1, 2, 0, 1, 3, 3],
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_eld(self):
|
||||||
|
self.assertTrue(
|
||||||
|
instruction_test_helper(
|
||||||
|
[3, 1, 1, 4, 1],
|
||||||
|
[1, 1, 1, 4, 1],
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_gld(self):
|
||||||
|
self.assertTrue(
|
||||||
|
instruction_test_helper(
|
||||||
|
[3, 4, 1, 5, 1],
|
||||||
|
[1, 4, 1, 5, 1],
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_lld(self):
|
||||||
|
self.assertTrue(
|
||||||
|
instruction_test_helper(
|
||||||
|
[3, 1, 4, 6, 1],
|
||||||
|
[1, 1, 4, 6, 1],
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_mov(self):
|
||||||
|
self.assertTrue(
|
||||||
|
instruction_test_helper(
|
||||||
|
[17, 1, 2],
|
||||||
|
[17, 17, 2]
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
|
@ -1,3 +0,0 @@
|
||||||
[build-system]
|
|
||||||
requires = ["setuptools"]
|
|
||||||
build-backend = "setuptools.build_meta"
|
|
16
setup.py
16
setup.py
|
@ -1,16 +0,0 @@
|
||||||
import setuptools
|
|
||||||
setuptools.setup(
|
|
||||||
name='desmos-sync',
|
|
||||||
version='0.1',
|
|
||||||
author='Ryan Marina',
|
|
||||||
description='synchronize Desmos expressions between the local filesystem and the web calculator',
|
|
||||||
packages=["desmossync"],
|
|
||||||
entry_points = {
|
|
||||||
"console-scripts": [ "desmos-sync=desmossync.cli.entry" ]
|
|
||||||
},
|
|
||||||
install_requires=[
|
|
||||||
'setuptools',
|
|
||||||
'websockets',
|
|
||||||
'watchdog'
|
|
||||||
]
|
|
||||||
)
|
|
Loading…
Reference in New Issue