There is a follow up question here.
I've undertaken the project of creating my own programming language, Linny. It's a very, very simple language, with only variable creation, variable changing, and outputting to the console, but I've very proud of it. It's an interpreted language, with the interpreter being written in Python 3. The interpreter is really a hybrid of a compiler/interpreter. I can't really say where I got the idea for the syntax, I just meshed a bunch of ideas from a wide range of languages and that's what I got. I want as much criticism and nitpick as you can find. From performance to readability to bugs, anything.
How it works
You write a program in Linny, with the file extension .linny. You set the path to the file in the source code, and you run it. You can also uncomment the bottom part in the main guard, comment out the for line in lines: interpret(line)
part, and you'll be able to input line by line commands like Python.
Syntax (sample file, script.linny)
string text = "Hello" ; // semicolon MUST be one space away from ending
text = "Hello_There!" ;
out text ; // "out" outputs the variable to the screen
type text ; // "type" returns the type of variable (integer, string, etc)
boolean food = false ;
out food ;
type food ;
integer num = 16 ;
out num ;
type num ;
float f = 14.2 ;
out f ;
type f ;
The Interpreter
"""
This program compiles and interprets programs written in `Linny`
"""
def interpret(line_of_code):
"""Interprets user inputed Linny code """
words = line_of_code
if isinstance(words, str):
words = words.split()
#Basic empty line check
if words == []:
return
#Comment check
if "//" in words[0]:
return
#Interpret mode begin
#If user wants to output a value
if len(words) == 3 and \
words[0] == "out" and \
already_defined(words[1]) and \
words[2] == ";":
print(VARIABLES[words[1]]['value'])
return
#If user wants to get the type of value
if len(words) == 3 and \
already_defined(words[1]) and \
words[0] in MISC_KEYWORDS and \
words[2] == ";":
if words[0] == "type":
print(VARIABLES[words[1]]['data_type'])
return
#If user wants to create a value
if len(words) == 5 and words[4] == ";":
add_to_variables(
name=words[1],
value=words[3],
data_type=words[0],
line_number=0
)
return
#If user wants to edit a value
if len(words) == 4 and words[3] == ";":
change_value(words[0], words[2])
return
#Interpret mode end
def change_value(variable, new_value):
""" Changes the value of the variable to the `new_value` """
data_type = VARIABLES[variable]['data_type']
if data_type == "integer":
VARIABLES[variable]['value'] = int(new_value)
elif data_type == "string":
VARIABLES[variable]['value'] = str(new_value)
elif data_type == "float":
VARIABLES[variable]['value'] = float(new_value)
elif data_type == "boolean":
if new_value == "true":
VARIABLES[variable]['value'] = True
elif new_value == "false":
VARIABLES[variable]['value'] = False
else:
exit(f"Cannot assign boolean value to {new_value}")
elif data_type == "char":
if len(new_value) == 1:
VARIABLES[variable]['value'] = chr(new_value)
else:
exit(f"char can only be one character long, not {new_value}!")
else:
exit(f"Not a data type")
def add_to_variables(name, value, data_type, line_number):
""" Checks `data_type` of passed variable, and adds it to list of variables """
if data_type == "integer":
VARIABLES[name] = {'value': int(value), 'data_type': data_type}
elif data_type == "string":
VARIABLES[name] = {'value': value, 'data_type': data_type}
elif data_type == "float":
VARIABLES[name] = {'value': float(value), 'data_type': data_type}
elif data_type == "boolean":
if value == "true":
VARIABLES[name] = {'value': True, 'data_type': data_type}
elif value == "false":
VARIABLES[name] = {'value': False, 'data_type': data_type}
else:
exit(f"SyntaxError: Expected boolean true/false on line {line_number}")
elif data_type == "char":
VARIABLES[name] = {'value': chr(value), 'data_type': data_type}
else:
exit(f"SyntaxError: {data_type} is not a valid data type on line {line_number}")
def variable_syntax_check(line_number, line):
""" Returns if the syntax is correct in the passed `line` """
words = line.split()
if words == []:
return
if words[0] in list(VARIABLES.keys()):
#Check if next word is =
if words[1] == "=":
#Check if last index that holds ; exists
#try:
# words[len(words - 1)] = words[len(words - 1)]
#except IndexError:
# exit(f"SyntaxError: Expected ; at end of line {line_number}")
if words[3] == ";":
add_to_variables(
name=words[0],
value=words[2],
data_type=VARIABLES[words[0]['data_type']],
line_number=line_number
)
else:
exit(f"SyntaxError: Expected ; at end of line {line_number}")
#Check if keyword is first argument, or variable has already been defined
if words[0] in VARIABLE_KEYWORDS:
#Check if variable hasn't already been defined
if words[1] not in VARIABLES.keys():
#Check if next word is '='
if words[2] == "=":
#Check if ending is ;
try:
words[4] = words[4]
except IndexError:
exit(f"""SyntaxError: Excepted ; at end of line {line_number}""")
if words[4] == ";":
#Call method and pass relevent information to add to variables
add_to_variables(
name=words[1],
value=words[3],
data_type=words[0],
line_number=line_number
)
else:
exit(f"SyntaxError: Excepted ; at end of line {line_number}")
else:
exit(f"SyntaxError: Expected '=' on line {line_number}")
else:
exit(f"SyntaxError: Variable {words[1]} has already been defined.")
else:
exit(f"SyntaxError: Variable {words[0]} has not been defined.")
def if_logic_syntax_check(statement):
""" Determines if the syntax is correct for the if statement """
expression = statement[0].split()
#Determine is logic statements are correct
if expression[0] in LOGIC_KEYWORDS and \
expression[2] in LOGIC_KEYWORDS and \
expression[4] in LOGIC_KEYWORDS:
#Now check if variable names are correct
if already_defined(expression[1]) and already_defined(expression[3]):
return
else:
if not already_defined(expression[1]) and already_defined(expression[3]):
exit(f"SyntaxError: {expression[1]} has not been defined yet.")
if already_defined(expression[1]) and not already_defined(expression[3]):
exit(f"SyntaxError: {expression[3]} has not been defined yet.")
if not already_defined(expression[1]) and not already_defined(expression[3]):
exit(f"SyntaxError: {expression[1]} and {expression[3]} have not been defined.")
else:
exit(f"SyntaxError: Logic keyword not spelled correctly / not included.")
#Now check the body
del statement[0], statement[len(statement) - 1]
for i in range(len(statement)):
if not statement[i][:1] == "\t":
exit(f"SyntaxError: Inconsistent Tabbing")
def parse_if(index, lines):
""" Returns the if statement at the place in the file """
statement = []
for i in range(index, len(lines)):
if lines[i][0] != "endif":
statement.append(lines[i])
else:
break
return statement
def to_list(file):
""" Converts the lines in the source file to a list"""
lines = []
with open(file, "r") as file_:
for line in file_:
if line[len(line) - 1] == "\n":
lines.append(line[:len(line) - 1])
else:
lines.append(line)
return lines
def compile_file(source_file):
""" Starts compiling process """
lines = to_list(source_file)
for line_number, line in enumerate(lines):
if line != "":
if is_variable(line.split()[0]):
variable_syntax_check(line_number + 1, line)
if line.split()[0] == "if":
if_logic_syntax_check(parse_if(line_number, lines))
print("Code compiles!")
def is_variable(word):
""" Determines if the passed word is a/possibly can be a variable """
return word in VARIABLE_KEYWORDS and word not in LOGIC_KEYWORDS and word not in FUNC_KEYWORDS
def already_defined(variable):
""" Returns if the variable has already been defined """
return variable in list(VARIABLES.keys())
if __name__ == '__main__':
#Dict of variables that have been initialized in the program
VARIABLES = {}
FUNCTIONS = {}
VARIABLE_KEYWORDS = ["integer", "string", "float", "boolean", "char"]
LOGIC_KEYWORDS = ["if", "endif", "else", "while", "for", "then", "equals", "greaterthan", "lessthan"]
FUNC_KEYWORDS = ["func", "endfunc"]
MISC_KEYWORDS = ["type"]
ALL_KEYWORDS = VARIABLE_KEYWORDS + LOGIC_KEYWORDS + FUNC_KEYWORDS + MISC_KEYWORDS
SOURCE_FILE = "Code/Python/Linny/script.linny"
lines = to_list(SOURCE_FILE)
for line in lines:
interpret(line)
"""
print("[Linny Interpreter]")
print("Enter in one line of code at a time!")
while True:
code = input(">>> ")
variable_syntax_check(0, code)
"""