Last active
September 3, 2023 20:05
-
-
Save Todd-Davies/7666505 to your computer and use it in GitHub Desktop.
I wanted to see what a programming language that was also parsable JSON would look like. This is what I came up with.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
""" | |
I wanted to see what a programming language that was also parsable JSON would look like. This is what I came up with. | |
Features: | |
+ Methods (nestable) | |
+ Variables | |
+ Built in functions (add, subtract, print etc) | |
+ Maybe more I've forgotten | |
Notable omissions as of yet: | |
+ Methods cannot return values | |
+ Arrays | |
+ Pretty much every feature *real* programming languages have | |
""" | |
import json | |
class Executor: | |
""" | |
Executes my little JSON based programming language | |
""" | |
def cmd_multiply(self, values): | |
""" | |
Multiplies two values | |
""" | |
if len(values)!=3: | |
raise Exception("Multiplication takes three values") | |
# Get the first parameter | |
number1 = self.get_variable(values[1]) | |
# Get the second parameter | |
number2 = self.get_variable(values[2]) | |
if values[0][0]!='$': | |
raise Exception("First argument of mul must be a variable") | |
else: | |
self.set_variable(values[0][1:], number1 * number2) | |
def cmd_division(self, values): | |
""" | |
Divide one value by another | |
""" | |
if len(values)!=3: | |
raise Exception("Division takes three values") | |
# Get the first parameter | |
number1 = self.get_variable(values[1]) | |
# Get the second parameter | |
number2 = self.get_variable(values[2]) | |
if values[0][0]!='$': | |
raise Exception("First argument of div must be a variable") | |
else: | |
self.set_variable(values[0][1:], number1 / number2) | |
def cmd_addition(self, values): | |
""" | |
Adds two values | |
""" | |
if len(values)!=3: | |
raise Exception("Addition takes three values") | |
# Get the first parameter | |
number1 = self.get_variable(values[1]) | |
# Get the second parameter | |
number2 = self.get_variable(values[2]) | |
if values[0][0]!='$': | |
raise Exception("First argument of add must be a variable") | |
else: | |
self.set_variable(values[0][1:], number1 + number2) | |
def cmd_subtraction(self, values): | |
""" | |
Subtracts one value from another | |
""" | |
if len(values)!=3: | |
raise Exception("Subtraction takes three values") | |
# Get the first parameter | |
number1 = self.get_variable(values[1]) | |
# Get the second parameter | |
number2 = self.get_variable(values[2]) | |
if values[0][0]!='$': | |
raise Exception("First argument of sub must be a variable") | |
else: | |
self.set_variable(values[0][1:], number1 - number2) | |
def cmd_print(self, values): | |
""" | |
Prints the values to std out | |
""" | |
output = [] | |
for token in values: | |
output.append(str(self.get_variable(token))) | |
print " ".join(output) | |
# Define the built in commands using a dict | |
commands = { "mul" : cmd_multiply, | |
"div" : cmd_division, | |
"add" : cmd_addition, | |
"sub" : cmd_subtraction, | |
"print": cmd_print} | |
# Holds the variables for this instance of Executor | |
local_variables = {} | |
# Holds the variables for this instance of Executor | |
local_methods = {} | |
def set_variable(self, key, value): | |
""" | |
Set a variable | |
""" | |
self.local_variables[key] = value | |
def get_variable(self, key): | |
""" | |
Gets a variable | |
If the variable is not set, then the name of the variable is returned and a warning sent | |
""" | |
# Convert to a string (incase it's an integer literal) | |
key = str(key) | |
# Check to see if the key is a variable | |
if key[0] == '$': | |
# Strip the '$' | |
key = key[1:] | |
# Check if the key is in the variables array | |
if key in self.local_variables: | |
return self.local_variables[key]; | |
else: | |
print "Variable %s not found." % key | |
return key | |
else: | |
# The key isn't a variable, return it | |
return key | |
def set_method(self, key, value): | |
""" | |
Set a method | |
""" | |
self.local_methods[key] = value | |
def execute_method(self, key, params=[]): | |
""" | |
Executes a method | |
""" | |
# Create a new instance of Executor for the method | |
method_ex = Executor() | |
# Get the statement from the statements list | |
statement_to_execute = self.local_methods[key] | |
# Extract the method parameters from the | |
method_params = statement_to_execute["params"] | |
# Set the variables in the method executor | |
for param_index in range(0, len(method_params)): | |
param = self.get_variable(params[param_index]) | |
# Set the parameter | |
method_ex.set_variable( | |
method_params[param_index], | |
param) | |
# Execute the method | |
method_ex.execute_statement(json.dumps(statement_to_execute["body"])) | |
def tokenize(self, statement): | |
""" | |
Splits the statement into words | |
""" | |
return statement.split(" ") | |
def run_command(self,tokens): | |
""" | |
Runs a command from a selection of built in commands and the methods | |
defined by the code | |
""" | |
# Is the command a defined method? | |
if tokens[0][0]=='!': | |
if tokens[0][1:] in self.local_methods: | |
self.execute_method(tokens[0][1:], tokens[1:]) | |
else: | |
print "Method %s not found" % tokens[0][1:] | |
# Nope, is it a operation? | |
elif tokens[0] in self.commands: | |
self.commands[tokens[0]](self, tokens[1:]) | |
else: | |
print "Unknown command: '%s'" % tokens[0] | |
def execute_statement(self, statement): | |
""" | |
Executes an array of commands | |
""" | |
# Parse the json into an array | |
code = json.loads(statement) | |
# For each item in the array | |
for stmnt in code: | |
# If the statement is a dict, then it's a method or an assignment | |
if isinstance(stmnt, dict): | |
# If the value inside the statement is a dict, then it's a method | |
if isinstance(stmnt.values()[0], dict): | |
self.set_method(stmnt.keys()[0], stmnt.values()[0]) | |
else: # Otherwise it's a variable assignment | |
self.set_variable(stmnt.keys()[0], stmnt.values()[0]) | |
# If it's a string then it must be a command | |
elif isinstance(stmnt, unicode): | |
# Split the command into tokens first | |
tokens = self.tokenize(stmnt.strip()) | |
# Run the command | |
self.run_command(tokens) | |
else: | |
# If it's none of those, feed it back into here | |
self.execute_statement(json.dumps(stmnt)) | |
# This is the code we'll run | |
app_code = ''' | |
[ | |
{"x":9}, | |
{"y":3}, | |
["print The value of x is: $x"], | |
["print The value of x is: $y"], | |
["print Now lets add x and y"], | |
["add $z $x $y"], | |
["print The answer is: $z"], | |
["print Now lets subtract y from x"], | |
["sub $z $x $y"], | |
["print The answer is: $z"], | |
["print Now lets divide x by y"], | |
["div $z $x $y"], | |
["print The answer is: $z"], | |
["print Now lets multiply x by y"], | |
["mul $z $x $y"], | |
["print The answer is: $z"], | |
{"myMethod": | |
{ | |
"params": [ | |
"p1", | |
"p2" | |
], | |
"body": [ | |
{"nestedMethod": | |
{ | |
"params": [ | |
"p1" | |
], | |
"body": [ | |
["print Methods can be nested, and arguments can be trickled down."], | |
["print The result was: $p1"] | |
] | |
} | |
}, | |
["print You passed me: $p1 and $p2"], | |
["print Lets add them and pass the result into another method!"], | |
["add $p2 $p1 $p2"], | |
["!nestedMethod $p2"] | |
] | |
} | |
}, | |
["!myMethod $x $y"] | |
]''' | |
# Create an executor object | |
ex = Executor() | |
# Run the program | |
ex.execute_statement(app_code) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment