commit
261a03a5ae
12 changed files with 935 additions and 0 deletions
@ -0,0 +1,13 @@
@@ -0,0 +1,13 @@
|
||||
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE |
||||
Version 2, December 2004 |
||||
|
||||
Copyright (C) 2021 Matthilde Autumn Stella |
||||
|
||||
Everyone is permitted to copy and distribute verbatim or modified |
||||
copies of this license document, and changing it is allowed as long |
||||
as the name is changed. |
||||
|
||||
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE |
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION |
||||
|
||||
0. You just DO WHAT THE FUCK YOU WANT TO. |
@ -0,0 +1,9 @@
@@ -0,0 +1,9 @@
|
||||
turtle(turtle(turtle())); # :worry: |
||||
|
||||
mu TuRtlE(ttturtle, tttturtle) |
||||
{ |
||||
Woa( ttturtle, tttturtle); # This is a comment |
||||
tttttttUrtle = turtle(turtle(notTurtle())); # This is another comment |
||||
|
||||
turtle(tttttttUrtle); |
||||
} # Mu |
@ -0,0 +1,130 @@
@@ -0,0 +1,130 @@
|
||||
_______ __ __ ______ |
||||
|_ _.--.--.----| |_| .-----| | by matthilde |
||||
| | | | | _| _| | -__| ---| |
||||
|___| |_____|__| |____|__|_____|______| |
||||
|
||||
INTRODUCTION |
||||
------------ |
||||
|
||||
TurtleC is a C-like esoteric programming language that is ran by a |
||||
little turtle named... well.... Turtle! |
||||
|
||||
It is quite of a slow programming language because Turtle is not very |
||||
fast. It also tends to sleep while running the program. So be patient |
||||
when running the program! |
||||
|
||||
NOTE: While pretty much I could test in the interpreter has been tested, |
||||
I can't guarantee that the interpreter is 100% stable. |
||||
|
||||
COMPATIBILITY |
||||
------------- |
||||
|
||||
The programming language has been entirely developed on a Linux platform |
||||
using Python 3.9.5. It does not require any dependencies as it uses the |
||||
standard modules included in Python. |
||||
|
||||
NOTE: The programming language has not been tested on Microsoft Windows. |
||||
|
||||
FILES |
||||
----- |
||||
|
||||
- turtlec.py - Core TurtleC features, includes a debug interpreter. |
||||
- trc.py - TrCI, Turtle C Interpreter. The actual interpreter. |
||||
- parser_test.trt - Small script you can't run, designed to test the parser |
||||
- programs/*.trt - Example programs |
||||
|
||||
THE INTERPRETER |
||||
--------------- |
||||
|
||||
Turtle will introduce itself then proceed to run the program, you will |
||||
see in the interface the STDERR and STDOUT. This turtle, as I said in |
||||
the introduction, is quite slow and can sleep midway in program |
||||
execution. |
||||
|
||||
Usage of both the interpreter and the debug interpreter. |
||||
|
||||
./turtlec.py FILENAME |
||||
./trc.py FILENAME |
||||
|
||||
HOW TO PROGRAM IN TURTLEC |
||||
------------------------- |
||||
|
||||
If you are familiar with any C-like programming langauge such as C, |
||||
C++, JavaScript or Rust (there are plenty so i am not going to list |
||||
them all), you will get the grip quite easily. |
||||
|
||||
Integers |
||||
-------- |
||||
|
||||
The integer is the only type bundled in the programming language. |
||||
Here are how integers looks like: |
||||
|
||||
noTurtle() = 0 |
||||
turtle() = 1 |
||||
turtle(turtle()) = 2 |
||||
turtle(turtle(turtle(turtle()))) = 4 |
||||
|
||||
Arithmetics |
||||
----------- |
||||
|
||||
There are builtin functions to do basic arithmetic and logic. |
||||
|
||||
Woa(a, b) = a + b |
||||
so Woa(turtle(), turtle(turtle())) == turtle(turtle(turtle())) |
||||
WOa(a, b) = a - b |
||||
WoA(a, b) = a * b |
||||
WOA(a, b) = a / b (since these are integers, it does floor division) |
||||
|
||||
Wow(a, b) = a == b |
||||
Returns 1 if true, else it returns 0 |
||||
WoW(a, b) = a < b |
||||
|
||||
|
||||
Control Flow |
||||
------------ |
||||
|
||||
if a>0 then execute the code between brackets |
||||
Mu a { ... } |
||||
if a==0 then execute the code between brackets |
||||
mU a { ... } |
||||
while a>0, execute the code between brackets in loop |
||||
MU a { ... } |
||||
|
||||
Variable and Function assignement |
||||
--------------------------------- |
||||
|
||||
Variable assignement goes like this: |
||||
varname = expression; |
||||
|
||||
NOTE: variable naming has limitations. For example, if your variable |
||||
name does not match this regex: (?i)^t+urtle$ |
||||
the interpreter will crash. |
||||
THIS APPLIES TO BOTH VARIABLE AND FUNCTION NAMES. |
||||
|
||||
Variable names are case sensitive. |
||||
ttturtle is a valid variable name |
||||
Turtle is a valid variable name |
||||
brwhiubhwrui is not valid |
||||
TtUrtlE is valid |
||||
turtleeeee is not valid |
||||
|
||||
Assigning a function is also as easy as doing it in JavaScript. |
||||
|
||||
mu tTuRtle(tturtle, ttturtle) |
||||
{ |
||||
# ... |
||||
} |
||||
|
||||
Comments |
||||
-------- |
||||
|
||||
Like in Python, type a '#' then write your comment. The text after |
||||
# will be ignored. |
||||
|
||||
See examples in the programs |
||||
|
||||
|
||||
LICENSING |
||||
--------- |
||||
|
||||
This software is licensed under the WTFPL. see LICENSE for more detail. |
@ -0,0 +1,56 @@
@@ -0,0 +1,56 @@
|
||||
# Hello World in TurtleC |
||||
# |
||||
# This is very cursed |
||||
|
||||
# Print out ASCII character |
||||
mu tturtle(Tturtle) |
||||
{ |
||||
Turtle = turtle(turtle(turtle(turtle(turtle())))); |
||||
Turtle = WoA(Turtle, turtle(turtle())); |
||||
Turtle = WoA(Turtle, Turtle); |
||||
Turtle = WOa(Turtle, turtle(turtle(turtle()))); |
||||
|
||||
wOa(Woa(Turtle, Tturtle)); |
||||
} |
||||
|
||||
# Space |
||||
mu ttturtle() |
||||
{ |
||||
Turtle = turtle(turtle(turtle(turtle()))); |
||||
Turtle = WoA(Turtle, Turtle); |
||||
Turtle = WoA(Turtle, turtle(turtle())); |
||||
|
||||
wOa(Turtle); |
||||
} |
||||
|
||||
# Here goes the hello |
||||
Turtle = turtle(turtle(turtle(turtle(turtle(turtle(turtle())))))); |
||||
tturtle(Turtle); # h |
||||
Turtle = WOa(Turtle, turtle(turtle(turtle()))); |
||||
tturtle(Turtle); # e |
||||
Turtle = Woa(Turtle, turtle(turtle(turtle( |
||||
turtle(turtle(turtle(turtle()))))))); |
||||
tturtle(Turtle); # l |
||||
tturtle(Turtle); # l |
||||
Turtle = Woa(Turtle, turtle(turtle(turtle()))); |
||||
tturtle(Turtle); # o |
||||
|
||||
ttturtle(); # Space seperating two words. |
||||
|
||||
Turtle = WOa(Turtle, turtle(turtle(turtle()))); |
||||
Turtle = Woa(Turtle, Turtle); |
||||
tturtle(Turtle); # w |
||||
Turtle = WOA(Turtle, turtle(turtle())); |
||||
Turtle = Woa(Turtle, turtle(turtle(turtle()))); |
||||
tturtle(Turtle); # o |
||||
Turtle = Woa(Turtle, turtle(turtle(turtle()))); |
||||
tturtle(Turtle); # r |
||||
Turtle = WOa(Turtle, turtle(turtle(turtle( |
||||
turtle(turtle(turtle())))))); |
||||
tturtle(Turtle); # l |
||||
tturtle(turtle(turtle(turtle()))); # d |
||||
|
||||
Turtle = WOa(Turtle, turtle()); |
||||
wOa(Turtle); # \n |
||||
|
||||
# Here goes the world |
@ -0,0 +1,6 @@
@@ -0,0 +1,6 @@
|
||||
mu ttturtle() |
||||
{ |
||||
ttturtle(); |
||||
} |
||||
|
||||
ttturtle(); |
@ -0,0 +1,32 @@
@@ -0,0 +1,32 @@
|
||||
# Testing control flows |
||||
# Prints a square |
||||
|
||||
TURTLE = turtle(turtle(turtle(turtle()))); |
||||
TURTLE = WoA(TURTLE, WoA(TURTLE, TURTLE)); |
||||
|
||||
# Return |
||||
mu tturtle() |
||||
{ |
||||
wOa(turtle(turtle(turtle(turtle(turtle( |
||||
turtle(turtle(turtle(turtle(turtle())))))))))); |
||||
} |
||||
|
||||
# Character |
||||
|
||||
Tturtle = turtle(turtle(turtle(turtle(turtle())))); |
||||
TTurtle = Woa(Tturtle, Tturtle); |
||||
Turtle = noTurtle(); # x |
||||
tUrtle = noTurtle(); # y |
||||
|
||||
MU WoW(tUrtle, Tturtle) |
||||
{ |
||||
MU WoW(Turtle, TTurtle) |
||||
{ |
||||
wOa(TURTLE); |
||||
Turtle = Woa(turtle(), Turtle); |
||||
} |
||||
Turtle = noTurtle(); |
||||
tUrtle = Woa(turtle(), tUrtle); |
||||
|
||||
tturtle(); |
||||
} |
@ -0,0 +1,32 @@
@@ -0,0 +1,32 @@
|
||||
# Testing control flows |
||||
# Prints a square |
||||
|
||||
TURTLE = turtle(turtle(turtle(turtle()))); |
||||
TURTLE = WoA(TURTLE, WoA(TURTLE, TURTLE)); |
||||
|
||||
# Return |
||||
mu tturtle() |
||||
{ |
||||
wOa(turtle(turtle(turtle(turtle(turtle( |
||||
turtle(turtle(turtle(turtle(turtle())))))))))); |
||||
} |
||||
|
||||
# Character |
||||
|
||||
Tturtle = turtle(turtle(turtle(turtle(turtle())))); |
||||
TTurtle = Woa(Tturtle, Tturtle); |
||||
Turtle = noTurtle(); # x |
||||
tUrtle = noTurtle(); # y |
||||
|
||||
MU WoW(tUrtle, Tturtle) |
||||
{ |
||||
MU WoW(Turtle, TTurtle) |
||||
{ |
||||
wOa(TURTLE); |
||||
Turtle = Woa(turtle(), Turtle); |
||||
} |
||||
Turtle = noTurtle(); |
||||
tUrtle = Woa(turtle(), tUrtle); |
||||
|
||||
tturtle(); |
||||
} |
@ -0,0 +1,25 @@
@@ -0,0 +1,25 @@
|
||||
# TurtleC Truth Machine |
||||
|
||||
mu tturtle() |
||||
{ |
||||
# 3 |
||||
Turtle = turtle(turtle(turtle())); |
||||
# 3 * 4 = 12 |
||||
Turtle = WoA(Turtle, turtle(turtle(turtle(turtle())))); |
||||
# 12 * 4 = 48 = '0' |
||||
Turtle = WoA(Turtle, turtle(turtle(turtle(turtle())))); |
||||
# 48 + 1 = 49 = '1' |
||||
Turtle = Woa(Turtle, turtle()); |
||||
|
||||
# Takes input |
||||
tUrtle = woA(); |
||||
|
||||
wOa(tUrtle); |
||||
# If input == '1' then repeat infinitely |
||||
MU Wow(tUrtle, Turtle) |
||||
{ |
||||
wOa(tUrtle); |
||||
} |
||||
} |
||||
|
||||
tturtle(); |
@ -0,0 +1,130 @@
@@ -0,0 +1,130 @@
|
||||
#!/usr/bin/env python3 |
||||
# |
||||
# The TurtleC Interpreter by matthilde |
||||
# |
||||
import os, sys, time, random, math |
||||
from turtlec import * |
||||
|
||||
def _clear(): |
||||
os.system('cls' if os.name == "nt" else 'clear') |
||||
|
||||
turtle_stderr = "" |
||||
turtle_stdout = "" |
||||
turtle_pos = 1 |
||||
file_lines = 0 |
||||
|
||||
def turtle_update(): |
||||
global turtle_stderr |
||||
_clear() |
||||
n = random.randint(0, 50) |
||||
w = 5 if n == 24 else random.randint(5, 40) / 100 |
||||
if n == 24: |
||||
turtle_stderr += "*yawn* this is tiring, imma sleep for a bit...\n" |
||||
|
||||
tp = math.floor((turtle_pos / file_lines) * 20) |
||||
print(' '*tp + 'o') |
||||
print('-'*20 + '[GOAL!]\n') |
||||
|
||||
print("--- STDERR ---") |
||||
stdrr = turtle_stderr.strip() |
||||
if stdrr != '': |
||||
stdrr = stdrr.split('\n') |
||||
print("[Turtle]", "\n[Turtle] ".join(stdrr)) |
||||
|
||||
print("--- STDOUT ---") |
||||
print(turtle_stdout.strip()) |
||||
print("--------------") |
||||
|
||||
time.sleep(w) |
||||
|
||||
def turtle_ppre(a, b, linenum): |
||||
global turtle_pos, turtle_stderr |
||||
if linenum > 0: |
||||
turtle_pos = linenum |
||||
turtle_update() |
||||
|
||||
def turtle_print(*args, end='\n'): |
||||
global turtle_stdout |
||||
turtle_stdout += ' '.join([str(x) for x in args]) |
||||
turtle_stdout += end |
||||
turtle_update() |
||||
|
||||
### MODIFIED BUILT-INS |
||||
|
||||
def turtle_wOa(env, *args): |
||||
for x in args: |
||||
x = x(env).value |
||||
turtle_print(chr(x), end='') |
||||
|
||||
def turtle_wOA(env, *args): |
||||
for x in args: |
||||
x = x(env).value |
||||
print(x, end=' ') |
||||
print() |
||||
|
||||
def turtle_woA(env): |
||||
prompt = "[Turtle] Please type a character on your keyboard,\n" + \ |
||||
"[Turtle] I need it >.> " |
||||
return TurtleInteger(ord(input(prompt)[0])) |
||||
|
||||
###### |
||||
|
||||
def turtle_introduction(): |
||||
print(" o <- the turtle\n---") |
||||
quotes = [ |
||||
"Hewwo!", |
||||
"I am Turtle! The small turtle that will run your program!", |
||||
"Be patient with me, I am kinda slow >.<", |
||||
"Know that a function or a while loop can pull me back " + \ |
||||
"during my journey >.>"] |
||||
|
||||
for quote in quotes: |
||||
print("[Turtle]", quote, end=' ') |
||||
input() |
||||
|
||||
def main(fn): |
||||
global file_lines, turtle_stderr, turtle_pos |
||||
file_lines = len(fn.split('\n')) |
||||
|
||||
_clear() |
||||
turtle_introduction() |
||||
|
||||
env = default_env.copy() |
||||
env["wOa"] = turtle_wOa |
||||
env["wOA"] = turtle_wOA |
||||
env["woA"] = turtle_woA |
||||
try: |
||||
ast = build_ast(tokenize(fn), ppre = turtle_ppre) |
||||
for line in ast: |
||||
line(env) |
||||
except TurtleException as e: |
||||
dl = '\n | '.join(lines[max(e.linenum-3, 0):e.linenum+2]) |
||||
turtle_stderr = f""" |
||||
Ouchie!! |
||||
I have encountered an error that prevents me from continuing at these |
||||
lines: |
||||
| {dl} |
||||
|
||||
Error at line {e.linenum}: {e.err} |
||||
""" |
||||
turtle_update() |
||||
sys.exit(1) |
||||
except RecursionError: |
||||
turtle_stderr = f"Ouchie! Too much recursion >.<" |
||||
turtle_update() |
||||
sys.exit(1) |
||||
|
||||
turtle_pos = file_lines |
||||
turtle_stderr += "I finished!!, that was difficult >.<\n" |
||||
turtle_update() |
||||
|
||||
if __name__ == "__main__": |
||||
if len(sys.argv) != 2: |
||||
print("USAGE: trci FILENAME") |
||||
sys.exit(1) |
||||
else: |
||||
try: |
||||
with open(sys.argv[1]) as f: |
||||
main(f.read()) |
||||
except IOError: |
||||
print("Could not open the file") |
@ -0,0 +1,498 @@
@@ -0,0 +1,498 @@
|
||||
#!/usr/bin/env python3 |
||||
# TurtleC - C-like with turtles (i think, not sure) |
||||
# |
||||
# For the turtle-themed Esojam |
||||
# |
||||
# Integers |
||||
# turtle() = 1 |
||||
# turtle(turtle()) = 2 |
||||
# turtle(turtle(turtle(turtle()))) = 4 |
||||
# noTurtle() = 0 |
||||
# |
||||
# Arith |
||||
# Woa(a, b) = a + b |
||||
# Woa(turtle(turtle()), turtle(turtle(turtle()))) = |
||||
# turtle(turtle(turtle(turtle(turtle()))))) |
||||
# WOa(a, b) = a - b |
||||
# WoA(a, b) = a * b |
||||
# WOA(a, b) = a / b |
||||
# |
||||
# Wow(a, b) = a == b |
||||
# WoW(a, b) = a < b |
||||
# |
||||
# Control Flow |
||||
# Mu a { ... } # if (a>0) { ... } |
||||
# mU a { ... } # if (a==0) { ... } |
||||
# MU a { ... } # while (a) { ... } |
||||
# |
||||
# Functions and Variables |
||||
# |
||||
# tUrtle = turtle() # Assigns 1 to tUrtle |
||||
# mu Tturtle(tturtle, ttturtle) { ... } # Function named Tturtle |
||||
# Function will return the last instruction |
||||
# |
||||
# I/O |
||||
# wOa(a, b, c, ...) # Output a stream of integers as ASCII chars |
||||
# wOA(a, b, c, ...) # Output a stream of integers as integers |
||||
# woA() # Returns a byte from stdin |
||||
# |
||||
# Variable notation standards |
||||
# Variables are case-sensitive and must be named turtle. You can |
||||
# combine uppercase and lowercase to have different variables |
||||
# You can also have as much T as you want in the beginning of the name |
||||
# |
||||
# ttturtle # Valid name |
||||
# Turtle # Valid name |
||||
# notATurtle # Not a valid name |
||||
# TtUrtlE # Valid name |
||||
# |
||||
import sys, re, time |
||||
|
||||
blocks = ["Mu", "mU", "MU", "mu"] |
||||
int_funcs = ["turtle", "noTurtle"] |
||||
pchars = "(){},;=" |
||||
|
||||
_parser_re = re.compile(r"(#.*(\n|$)|[\(\),{}=;]|[^\(\),{}=;\s]+)") |
||||
|
||||
### BUILT-INS ### |
||||
|
||||
def _builtin_set_value(env, k, v): |
||||
k = k.name |
||||
assert is_turtle(k), "That variable name is not turtle enough. FIX IT." |
||||
|
||||
env[k] = v(env) |
||||
return env[k] |
||||
|
||||
# from pprint import pprint |
||||
|
||||
def _builtin_Woa(env, a, b): |
||||
return a(env).__add__(b(env)) |
||||
|
||||
def _builtin_WOa(env, a, b): |
||||
return a(env).__sub__(b(env)) |
||||
|
||||
def _builtin_WoA(env, a, b): |
||||
return a(env).__mul__(b(env)) |
||||
|
||||
def _builtin_WOA(env, a, b): |
||||
return a(env).__floordiv__(b(env)) |
||||
|
||||
def _builtin_Wow(env, a, b): |
||||
return a(env).__eq__(b(env)) |
||||
|
||||
def _builtin_WoW(env, a, b): |
||||
return a(env).__lt__(b(env)) |
||||
|
||||
def _builtin_wOa(env, *args): |
||||
for x in args: |
||||
x = x(env).value |
||||
print(chr(x), end='') |
||||
|
||||
def _builtin_wOA(env, *args): |
||||
for x in args: |
||||
x = x(env).value |
||||
print(x, end=' ') |
||||
print() |
||||
|
||||
def _builtin_woA(env): |
||||
return TurtleInteger(ord(input()[0])) |
||||
|
||||
default_env = { |
||||
"_set_value": _builtin_set_value, |
||||
"Woa": _builtin_Woa, |
||||
"WOa": _builtin_WOa, |
||||
"WoA": _builtin_WoA, |
||||
"WOA": _builtin_WOA, |
||||
|
||||
"Wow": _builtin_Wow, |
||||
"WoW": _builtin_WoW, |
||||
|
||||
"wOa": _builtin_wOa, |
||||
"wOA": _builtin_wOA, |
||||
"woA": _builtin_woA |
||||
} |
||||
|
||||
### ERROR ### |
||||
|
||||
class TurtleException(Exception): |
||||
def __init__(self, linenum, err): |
||||
self.linenum = linenum |
||||
self.err = err |
||||
|
||||
def __str__(self): |
||||
return self.err |
||||
|
||||
|
||||
### CLASSES ### |
||||
|
||||
class TurtleInteger: |
||||
def __init__(self, value, linenum = 0): |
||||
self.value = value |
||||
self.linenum = linenum |
||||
|
||||
def __str__(self): |
||||
return str(self.value) |
||||
|
||||
def __call__(self, env): |
||||
return self |
||||
|
||||
def __add__(self, v): |
||||
return TurtleInteger(self.value + v.value) |
||||
|
||||
def __sub__(self, v): |
||||
return TurtleInteger(self.value - v.value) |
||||
|
||||
def __mul__(self, v): |
||||
return TurtleInteger(self.value * v.value) |
||||
|
||||
def __floordiv__(self, v): |
||||
return TurtleInteger(self.value // v.value) |
||||
|
||||
def __eq__(self, v): |
||||
return TurtleInteger(int(self.value == v.value)) |
||||
|
||||
def __lt__(self, v): |
||||
return TurtleInteger(int(self.value < v.value)) |
||||
|
||||
class TurtleVariable: |
||||
def __init__(self, name, linenum = 0): |
||||
self.name = name |
||||
self.linenum = linenum |
||||
|
||||
def __str__(self): |
||||
return f"[[ variable {self.name} ]]" |
||||
|
||||
def __call__(self, env): |
||||
if self.name not in env: |
||||
raise TurtleException(self.linenum, |
||||
f"{self.name}: Variable does not exist.") |
||||
elif type(env[self.name]) != TurtleInteger: |
||||
raise TurtleException(self.linenum, |
||||
f"{self.name} is not an integer.") |
||||
return env[self.name] |
||||
|
||||
class TurtleFunction: |
||||
def __init__(self, args, code): |
||||
self.args = args |
||||
self.code = code |
||||
|
||||
def __call__(self, env, *args): |
||||
assert len(args) == len(self.args), "Invalid arguments" |
||||
|
||||
args = [x(env) for x in args] |
||||
args = dict(zip(self.args, args)) |
||||
env = {**env.copy(), **args} |
||||
|
||||
if type(self.code) == list: |
||||
for instruction in self.code: |
||||
ret = instruction(env) |
||||
else: |
||||
ret = code(env) |
||||
|
||||
return ret |
||||
|
||||
def __str__(self): |
||||
return f"Function {args}" |
||||
|
||||
class TurtleFunctionCall: |
||||
def __init__(self, name, args, linenum = 0, pre = None): |
||||
self.name = name |
||||
self.args = args |
||||
self.linenum = linenum |
||||
self.pre = pre |
||||
|
||||
def __str__(self): |
||||
return f"mu {self.name} {[str(x) for x in self.args]}" |
||||
|
||||
def error(self, msg): |
||||
raise TurtleException(self.linenum, f"{self.name}: {msg}") |
||||
|
||||
def _assert(self, cond, msg): |
||||
if not cond: |
||||
self.error(msg) |
||||
|
||||
def __call__(self, env): |
||||
self._assert(self.name in env, "Function does not exist.") |
||||
|
||||
if self.pre: |
||||
self.pre(self.name, self.args, self.linenum) |
||||
|
||||
# args = [x(env) for x in self.args] |
||||
args = self.args |
||||
|
||||
try: |
||||
ret = env[self.name](env, *args) |
||||
except AssertionError as e: |
||||
self.error(e) |
||||
except TypeError as e: |
||||
self.error("Invalid Arguments.") |
||||
|
||||
return ret if ret else TurtleInteger(0) |
||||
|
||||
class TurtleBlock: |
||||
# name expr |
||||
# { |
||||
# code |
||||
# } |
||||
def __init__(self, name, expr, code, linenum = 0): |
||||
assert name in blocks, "Unknown Block." |
||||
self.name = name |
||||
self.expr = expr |
||||
self.code = code |
||||
self.linenum = linenum |
||||
|
||||
def __str__(self): |
||||
return "[[ block ]]" |
||||
|
||||
def __call__(self, env): |
||||
name = self.name |
||||
if name == "Mu": |
||||
if self.expr(env).value > 0: |
||||
self.run_block(env) |
||||
elif name == "mU": |
||||
if self.expr(env).value == 0: |
||||
self.run_block(env) |
||||
elif name == "MU": |
||||
while self.expr(env).value > 0: |
||||
self.run_block(env) |
||||
elif name == "mu": |
||||
if type(self.expr) != TurtleFunctionCall: |
||||
raise TurtleException(self.linenum, |
||||
"Syntax Error.") |
||||
elif not is_turtle(self.expr.name): |
||||
raise TurtleException(self.linenum, |
||||
"Not a valid turtle name smh") |
||||
|
||||
args = [] |
||||
for x in self.expr.args: |
||||
if type(x) != TurtleVariable: |
||||
raise TurtleException(self.linenum, |
||||
"Syntax Error.") |
||||
elif not is_turtle(x.name): |
||||
raise TurtleException(self.linenum, |
||||
"Not a valid turtle name smh") |
||||
|
||||
args.append(x.name) |
||||
|
||||
env[self.expr.name] = TurtleFunction(args, self.code) |
||||
|
||||
else: |
||||
raise AssertionError("How?????") |
||||
|
||||
def run_block(self, env): |
||||
if type(self.code) == list: |
||||
for instruction in self.code: |
||||
instruction(env) |
||||
else: |
||||
self.code(env) |
||||
|
||||
class TurtleToken: |
||||
def __init__(self, value, linenum = 0, tp = None): |
||||
self.value = value |
||||
self.linenum = linenum |
||||
|
||||
if tp: |
||||
self.type = tp |
||||
else: |
||||
self.type = find_token_type(self.value) |
||||
|
||||
def __str__(self): |
||||
return f"TurtleToken('{self.value}', {self.linenum}, '{self.type}')" |
||||
|
||||
def t(self): |
||||
return (self.value, self.type) |
||||
|
||||
### FUNCTIONS ### |
||||
|
||||
def find_token_type(t): |
||||
if t in pchars: |
||||
return "delim" |
||||
elif t in blocks: |
||||
return "block" |
||||
elif t in int_funcs: |
||||
return "integer" |
||||
else: |
||||
return "symbol" |
||||
|
||||
def is_turtle(s): |
||||
return re.match(r"(?i)^t+urtle$", s) != None |
||||
|
||||
def tokenize(s): |
||||
output = [] |
||||
for linenum, line in enumerate(s.split("\n")): |
||||
line = line.strip() |
||||
if line == '': |
||||
continue |
||||
|
||||
matches = [(x[0], linenum+1) for x in _parser_re.findall(line) |
||||
if not x[0].startswith("#")] |
||||
output += matches |
||||
|
||||
return [TurtleToken(x[0], x[1]) for x in output] |
||||
|
||||
# TODO: AST Builder |
||||
|
||||
def find_parens(oline, tokens, syms = "()"): |
||||
b = 1 |
||||
output = [] |
||||
sym, isym = syms |
||||
while tokens != []: |
||||
token = tokens.pop(0) |
||||
|
||||
if token.type == 'delim': |
||||
if token.value == sym: |
||||
b += 1 |
||||
elif token.value == isym: |
||||
b -= 1 |
||||
if b == 0: |
||||
return output |
||||
else: |
||||
output.append(token) |
||||
|
||||
raise TurtleException(oline, "Expected parenthese, got EOF.") |
||||
|
||||
def find_until(oline, tokens, sym = '{', parens = '()', fr = False): |
||||
expr = [] |
||||
b = 0 |
||||
paren, iparen = parens |
||||
while tokens != []: |
||||
token = tokens.pop(0) |
||||
|
||||
if token.t() == (paren, 'delim'): |
||||
b += 1 |
||||
elif token.t() == (iparen, 'delim'): |
||||
b -= 1 |
||||
|
||||
if b < 0: |
||||
raise TurtleException(token.linenum, "Syntax Error.") |
||||
elif token.t() == (sym, 'delim') and b == 0: |
||||
return expr |
||||
else: |
||||
expr.append(token) |
||||
|
||||
if fr: |
||||
return expr |
||||
else: |
||||
raise TurtleException(oline, "Syntax Error.") |
||||
|
||||
def build_ast(tokens, noblock = False, ppre = None): |
||||
ast = [] |
||||
while tokens != []: |
||||
token = tokens.pop(0) |
||||
|
||||
if token.type == 'block' and not noblock: |
||||
btype = token.value |
||||
bline = token.linenum |
||||
|
||||
bexpr = find_until(bline, tokens) |
||||
bexpr = build_ast(bexpr, True, ppre = ppre)[0] |
||||
bcode = build_ast(find_parens(bline, tokens, "{}"), |
||||
ppre = ppre) |
||||
|
||||
ast.append(TurtleBlock(btype, bexpr, bcode, bline)) |
||||
|
||||
elif token.type == 'delim': |
||||
raise TurtleException(token.linenum, "Syntax Error.") |
||||
|
||||
elif token.type == 'integer': |
||||
line = find_until(token.linenum, tokens, ';', fr=True) |
||||
|
||||
if token.value == 'noTurtle': |
||||
if line[0].t() == ('(', 'delim') and \ |
||||
line[1].t() == (')', 'delim'): |
||||
ast.append(TurtleInteger(0, token.linenum)) |
||||
else: |
||||
raise TurtleException(token.linenum, "Syntax Error.") |
||||
elif token.value == 'turtle': |
||||
acc = 1 |
||||
|
||||
tline = token.linenum |
||||
while line != []: |
||||
token = line.pop(0) |
||||
if token.t() != ('(', 'delim'): |
||||
raise TurtleException(token.linenum, |
||||
"Invalid turtle notation.") |
||||
else: |
||||
token = line.pop(0) |
||||
if token.t() == (')', 'delim'): |
||||
break |
||||
elif token.t() == ('turtle', 'integer'): |
||||
acc += 1 |
||||
else: |
||||
raise TurtleException(token.linenum, |
||||
"Invalid turtle notation.") |
||||
|
||||
if token.t() != (')', 'delim'): |
||||
print(token) |
||||
raise TurtleException(token.linenum, "Syntax Error.") |
||||
else: |
||||
ast.append(TurtleInteger(acc, token.linenum)) |
||||
else: |
||||
# Unlikely to happen so, Imma put a random message. |
||||
raise TurtleException(1, ":worry:") |
||||
|
||||
elif token.type == "symbol": |
||||
line = find_until(token.linenum, tokens, ';', fr=True) |
||||
|
||||
if line == []: |
||||
ast.append(TurtleVariable(token.value, token.linenum)) |
||||
elif line[0].t() == ('=', 'delim') and len(line) != 1: |
||||
vname = token.value |
||||
vexpr = build_ast(line[1:], True)[0] |
||||
|
||||
ast.append(TurtleFunctionCall("_set_value", |
||||
[TurtleVariable(vname), vexpr], |
||||
token.linenum, ppre)) |
||||
elif line[0].t() == ('(', 'delim') and \ |
||||
line[-1].t() == (')', 'delim'): |
||||
vname = token.value |
||||
|
||||
line = line[1:-1] |
||||
mu = [] |
||||
while line != []: |
||||
mu.append(build_ast(find_until( |
||||
token.linenum, line, ',', fr=True))[0]) |
||||
|
||||
ast.append(TurtleFunctionCall(vname, mu, token.linenum, ppre)) |
||||
|
||||
else: |
||||
raise TurtleException(token.linenum, "Syntax Error.") |
||||
else: |
||||
raise TurtleException(1, "???????") |
||||
|
||||
return ast |
||||
|
||||
# TODO: Write tests |
||||
# TODO: Add a lil turtle to the interpreter :3 |
||||
|
||||
def debug_interpreter(prgm): |
||||
lines = prgm.split('\n') |
||||
env = default_env.copy() |
||||
try: |
||||
ast = build_ast(tokenize(prgm)) |
||||
for line in ast: |
||||
line(env) |
||||
except TurtleException as e: |
||||
print("ERROR!") |
||||
print(' |', |
||||
'\n | '.join(lines[max(e.linenum-3, 0):e.linenum+2])) |
||||
print(f" At line {e.linenum}") |
||||
print(f" -> {e.err}") |
||||
sys.exit(1) |
||||
except RecursionError: |
||||
print("Implementing TCO :despair:") |
||||
sys.exit(1) |
||||
|
||||
def main(filename): |
||||
with open(filename) as f: |
||||
debug_interpreter(f.read()) |
||||
|
||||
if __name__ == "__main__": |
||||
if len(sys.argv) != 2: |
||||
print("Usage: mtl FILENAME") |
||||
sys.exit(1) |
||||
|
||||
try: |
||||
main(sys.argv[1]) |
||||
except IOError: |
||||
print("Couldn't load the file :pensive:") |
Loading…
Reference in new issue