add main files used before version control

This commit is contained in:
randomuser 2023-11-14 22:33:34 -06:00
parent 57ae4726dc
commit d0ea507968
10 changed files with 374 additions and 0 deletions

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
__pycache__/
venv/

16
.vscode/launch.json vendored Normal file
View File

@ -0,0 +1,16 @@
{
"version": "0.2.0",
"configurations": [
{
"name": "Python: Current File",
"type": "python",
"request": "launch",
"program": "${file}",
"args": [
"./data/computer.desmos"
],
"console": "integratedTerminal",
"justMyCode": true
}
]
}

15
cli.py Normal file
View File

@ -0,0 +1,15 @@
from server import main
import argparse
def entry():
parser = argparse.ArgumentParser(
prog="desmos-sync",
description="Synchronize from local file to Desmos calculator",
)
parser.add_argument("filename")
args = parser.parse_args()
main(args.filename)
if __name__ == "__main__":
entry()

32
computer-spec/computer.md Normal file
View File

@ -0,0 +1,32 @@
INSTRUCTION SET FOR DESMOS COMPUTER
FLAGS
-----
EQUALS
GREATER THAN
LESS THAN
INSTRUCTIONS
------------
STO ( value, address ) -> store a value into an address
ADD ( addra, addrb, toaddr ) -> add a value from two addresses to a third address
SUB ( addra, addrb, toaddr ) -> subtract a value from two addresses to a third address
MUL ( addra, addrb, toaddr ) -> multiply a value from two addresses to a third address
DIV ( addra, addrb, toaddr ) -> divide a value from two addresses to a third address
CMP ( addra, addrb ) -> compare two values, store result in flags
- set all flags to false
- if equal, set EQUALS
- if addra > addrb, set GREATER THAN
- if addra < addrb, set LESS THAN
ELD ( address ) -> store the value of EQUALS flag to address (1 if true, 0 if false)
GLD ( address ) -> store the value of GREATER THAN flag to address (1 if true, 0 if false)
LLD ( address ) -> store the value of LESS THAN flag to address (1 if true, 0 if false)
JMP ( address ) -> change execution to address
BE ( address ) -> branch if equal
BNE ( address ) -> branch if not equal
BG ( address ) -> branch if greater
BL ( address ) -> branch if less

46
console.js Normal file
View File

@ -0,0 +1,46 @@
function main() {
let socket = new WebSocket("ws://localhost:8765");
var ids = [];
socket.onopen = function(e) {
socket.send("client ping");
}
socket.onmessage = function(e) {
var message = JSON.parse(e.data);
console.log(message.message)
if (message.message === "clear") {
for(i in ids) {
console.log("removing")
Calc.removeExpression({
id: ids[i],
})
}
ids = [];
} else if (message.message === "expression") {
Calc.setExpression({
type: "expression",
latex: message.payload,
id: message.id,
})
ids.push(message.id)
console.log(ids)
} else if (message.message === "ticker") {
var state = Calc.getState();
state.expressions.ticker = {
handlerLatex: message.payload,
minStepLatex: message.rate,
open: true,
};
Calc.setState(JSON.stringify(state))
} else {
console.log("unknown message type.")
}
console.log(message);
}
}

8
data/computer.desmos Normal file
View File

@ -0,0 +1,8 @@
ticker 1 : loopaction, loop
# Main memory structure
: B=\left[0for i=\left[1...4000\right]\right]
# Operations on memory
: set(l, i, v) = \left[ifval(l, i, c, v) for c = \left[1...length(l)\right]\right]
: setlistval(i, v) = B -> set(B, i, v)
: incx(i,v) = setlistval(i, B[i]+v)

6
data/testing.desmos Normal file
View File

@ -0,0 +1,6 @@
: testing = 32
: b = testing
: m = 3
: y = m * x + b
: y = 2m * x + b
: \frac{x^{2+3}}{3}*4*testing

148
parser.py Normal file
View File

@ -0,0 +1,148 @@
# desmos reserved keywords. think function names, and other verbs
# like with, etc.
reserved_keywords = "sin cos tan csc sec cot mean median min max quertile quantile stdev stdevp var mad cov covp corr spearman stats count total join sort shuffle unique for histogram dotplot boxplot normaldist tdist poissondist binomialdist uniformdist pdf cdf inversecdf random ttest tscore ittest frac sinh cosh tanh csch sech coth polygon distance midpoint rgb hsv lcm gcd mod ceil floor round sign nPr nCr log with cdot to in length left right"
reserved_keywords = reserved_keywords.split(' ')
def continue_lines(string):
# if there's a '\' and then a newline, ignore the newline.
return string.replace('\\\n', '')
def replace_multiplication(string):
return string.replace('*', '\\cdot ')
def replace_actions(string):
return string.replace(' ->', '\\to ').replace('->', '\\to ')
def replace_tabs(string):
return string.replace('\t', ' ')
def split_linewise(string):
return string.split('\n')
def merge_multiple_spaces(string):
return ' '.join(string.split())
def make_spaces_permanant(string):
return string.replace(' ', '\\ ')
def subscriptize(string):
output = ""
buffer = ""
for char in (string + "\n"): # add a newline so there's always a non-alpha char at the end
if char.isalpha():
buffer += char
else:
if buffer:
# check if our buffer is a reserved word; if so, do
# not expand.
if not buffer in reserved_keywords:
if len(buffer) > 1:
output += "{}_{{{}}}".format(buffer[0], buffer[1:])
else:
output += buffer[0]
else:
output += buffer
buffer = ""
output += char
return output.rstrip()
class Statement:
def __init__(self, commands, latex):
self.commands = commands
self.latex = latex
def __getitem__(self, item):
try:
return self.commands[item]
except KeyError:
return None
def __repr__(self):
return "{} : {}".format(str(self.commands), self.latex)
@classmethod
def from_line(cls, line):
if not line:
return None
if line[0] == "#": # ignore comments
return None
splitted = line.split(':')
commands = splitted[0]
latex = ':'.join(splitted[1:])
commands = commands.split(' ')
iterator = iter(commands)
commands = dict(zip(iterator, iterator))
try:
del commands['']
except KeyError:
pass
return cls(commands, latex)
@classmethod
def from_lines(cls, lines):
output = []
for line in lines:
output.append(cls.from_line(line))
return list(
filter(
lambda item: item is not None,
output
)
)
class Parser:
"""
Implements the parsing of the Desmos local DSL.
General syntax:
option1 value1 option2 value2 option3 value3 : latex
in latex statements, multiplication between variables is *not* implicit.
this is because
`testing`
becomes
`t_{esting}`.
In order to multiple two variables together, use
`a * b`
instead of
`ab`.
"""
def __init__(self, file):
self.file = file
self.ast = []
def parse(self):
with open(self.file, "r") as f:
text = f.read()
text = continue_lines(text)
text = replace_multiplication(text)
text = replace_actions(text)
text = replace_tabs(text)
lines = split_linewise(text)
lines = Statement.from_lines(lines)
for index, line in enumerate(lines):
if not line["comment"]:
# now, remove multiple spaces in lines, replacing them with one.
lines[index].latex = merge_multiple_spaces(line.latex)
# convert things like testing to t_{esting}
lines[index].latex = subscriptize(line.latex)
# make the spaces escaped and 'permanant'
lines[index].latex = make_spaces_permanant(line.latex)
self.ast = lines

94
server.py Normal file
View File

@ -0,0 +1,94 @@
from websockets.server import serve
import asyncio
import os
import time
import json
import random
import queue
import functools
from parser import Parser
from watchdog.events import FileSystemEventHandler
from watchdog.events import FileModifiedEvent
from watchdog.observers import Observer
class FSEHandler(FileSystemEventHandler):
def __init__(self, queue, *args, **kwargs):
self.queue = queue
super().__init__(*args, **kwargs)
def on_modified(self, event):
self.queue.put("")
async def serv(websocket, file):
message = await websocket.recv()
lmtime = 0
epsilon = 0.25 # tweak this to what makes sense. 0.25 seconds makes sense to me.
q = queue.Queue()
# make it read the file initially
q.put("")
# setup the watchdog
observer = Observer()
event_handler = FSEHandler(q)
observer.schedule(event_handler, file)
observer.start()
print("client connected")
while True:
# there are sometimes multiple writes bundled close together -- so
# debounce them.
q.get()
if time.time() - lmtime < epsilon:
continue
else:
lmtime = time.time()
parser = Parser(file)
parser.parse()
await websocket.send(
json.dumps(
{
"message": "clear",
"payload": "none",
}
)
)
for line in parser.ast:
if line["ticker"]:
await websocket.send(
json.dumps(
{
"message": "ticker",
"rate": line.commands["ticker"],
"payload": line.latex,
}
)
)
continue
if not line["comment"]:
await websocket.send(
json.dumps(
{
"message": "expression",
"id": "placeholder" + str(random.randint(1, 100100)),
"payload": line.latex,
}
)
)
async def start_server(file):
wrapper = functools.partial(serv, file=file)
async with serve(wrapper, "localhost", 8765):
await asyncio.Future()
def main(file):
asyncio.run(start_server(file))
if __name__ == "__main__":
main("data/testing.desmos")

7
shell.nix Normal file
View File

@ -0,0 +1,7 @@
{ pkgs ? import <nixpkgs> {} }:
let
my-python-packages = ps: with ps; [
pip
];
my-python = pkgs.python3.withPackages my-python-packages;
in my-python.env