5
\$\begingroup\$

A previous question regarding Linny exists here.

After a wonderful response on my previous question regarding my developing programming language, I worked on it for a couple days, and am returning for more scrutiny. What I added:

  • If Statements ( no else )
  • Functions ( with calls )
  • For Loops
  • Math operations
  • User input (feed)
  • Changed file extension from .linny to .lin
  • Added character datatype
  • All additions to the language are reflected in the interpreter.

I would like feedback on anything possible, as I plan on writing a game with this language in the future (expect that to be reviewed on here as well!). Any and all feedback is welcome, appreciated, and considered!

script.lin

//Variable Assignments
string s = "Hello" ;
integer a = 12 ;
integer b = 13 ;
integer c = 13 ;
string aa = "S" ;
string bb = "S" ;
float x = 7.22 ;
boolean t = false ;
boolean s = true ;
character = "c" ;
// Math operations / type output
add a b => sum ;
out sum ;
type sum ;
subtract a b => sum ;
out sum ;
multiply a b => sum ;
out sum ;
divide a b => sum ;
out sum ;
//User input
feed string pw "Password: " ;
out pw ;
//Function calling
func test()
 out a ;
endfunc
call test ;
//If statements
if a greaterthan b then
 out a ;
 out b ;
endif
if a lessthan b then
 out t ;
endif
if aa equals bb then
 out s ;
endif
//For loops
for 2
 out t ;
endfor
for 3
 out aa ;
endfor

interpreter.py

""" [Linny Interpreter] """
#import json
VARIABLE_CACHE = {}
FUNCTION_CACHE = {}
VARIABLE_KEYWORDS = ["integer", "string", "float", "boolean", "character", "feed"]
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
def add_to_function_cache(lines_of_code):
 """ Gathers all functions in the program and stores them """
 for line in lines_of_code:
 if line.startswith("func"):
 function_name = parse_function_name(line)
 function_body = parse_function_body(function_name)
 FUNCTION_CACHE[function_name] = {
 'code': function_body
 }
# FUNCTION PARSER METHODS START #
def parse_function_name(line):
 """ Returns the function name """
 return line[5:][:-3]
def parse_function_body(function_name):
 """ Returns all the code in the body of the passed function """
 body = []
 all_lines = get_lines()
 for i in range(len(all_lines)):
 if all_lines[i].startswith("func") and function_name in all_lines[i]:
 for j in range(i + 1, len(all_lines)):
 if all_lines[j].startswith("endfunc"):
 return body
 body.append(all_lines[j][1:-1])
# FUNCTION PARSER METHODS END #
def add_to_variable_cache(line_of_code):
 """
 Adds a variable to the program cache, after tons of checks, returns
 True if it was able to add to cache, False if it couldn't
 """
 seperated_info = line_of_code.split()
 if len(seperated_info) == 5 and \
 seperated_info[0] in VARIABLE_KEYWORDS and \
 not seperated_info[1] in ALL_KEYWORDS and \
 seperated_info[2] == "=" and \
 not seperated_info[3] in ALL_KEYWORDS and \
 seperated_info[4] == ";":
 data_type = seperated_info[0]
 name = seperated_info[1]
 value = seperated_info[3]
 if data_type == "string":
 value = str(value)
 if data_type == "integer":
 value = int(value)
 if data_type == "float":
 value = float(value)
 if data_type == "boolean":
 if value == "true":
 value = True
 if value == "false":
 value = False
 if data_type == "character":
 value = chr(value)
 VARIABLE_CACHE[name] = {
 'data_type': data_type,
 'value': value
 }
 return True
 return False
SOURCE_FILE = "Code/Python/Linny/script.lin"
def get_lines():
 """ Returns all the lines in the file """
 with open(SOURCE_FILE, "r") as file:
 all_lines = []
 for line_ in file:
 all_lines.append(line_)
 return all_lines
def run_code(line):
 """ Runs the code in passed `line` """
 #Check if line is empty
 if line == "" or line == "\n" or line == []:
 return
 # "out (variable)" case
 # Usage: `out variable_name`, prints the value of the variable
 if line.startswith("out") and has_ending_semicolon(line):
 variable = line.split()[1]
 if variable in VARIABLE_CACHE.keys():
 if isinstance(variable, str):
 print(str(VARIABLE_CACHE[variable]['value']).replace('"', ''))
 return
 print(VARIABLE_CACHE[variable]['value'])
 return
 # "type (variable)" case
 # Usage: `type variable_name`, prints the data_type of the variable_name
 if line.startswith("type") and has_ending_semicolon(line):
 variable = line.split()[1]
 if variable in VARIABLE_CACHE.keys():
 print(VARIABLE_CACHE[variable]['data_type'])
 return
 # "feed (variable)" case
 # Usage: `feed data_type variable_name "prompt"`, gets user input
 if line.startswith("feed") and has_ending_semicolon(line):
 data_type = line.split()[1]
 variable = line.split()[2]
 prompt = line.split()[3].replace('"', '')
 value = input(prompt)
 VARIABLE_CACHE[variable] = {
 'data_type': data_type,
 'value': value
 }
 return
 # "call (function)" case
 # Usage: `call function_name`, runsfunction
 if line.startswith("call") and has_ending_semicolon(line):
 function_name = line.split()[1]
 if function_name in FUNCTION_CACHE.keys():
 for line_of_code in FUNCTION_CACHE[function_name]['code']:
 run_code(line_of_code)
 ###### MATH COMPARISONS START #####
 # "add/subtract/times/divide v1 v2 => v3" case
 if line.split()[0] in ["add", "subtract", "multiply", "divide"] and has_ending_semicolon(line):
 operator = line.split()[0]
 value_one = line.split()[1]
 value_two = line.split()[2]
 product = line.split()[4]
 #SyntaxError handling real quick
 if VARIABLE_CACHE[value_one] and VARIABLE_CACHE[value_two]:
 if VARIABLE_CACHE[value_one]['data_type'] == VARIABLE_CACHE[value_two]['data_type']:
 if operator == "add":
 VARIABLE_CACHE[product] = {
 'data_type': VARIABLE_CACHE[value_one]['data_type'],
 'value': VARIABLE_CACHE[value_one]['value'] + VARIABLE_CACHE[value_two]['value']
 }
 return
 if operator == "subtract":
 VARIABLE_CACHE[product] = {
 'data_type': VARIABLE_CACHE[value_one]['data_type'],
 'value': VARIABLE_CACHE[value_one]['value'] - VARIABLE_CACHE[value_two]['value']
 }
 return
 if operator == "multiply":
 VARIABLE_CACHE[product] = {
 'data_type': VARIABLE_CACHE[value_one]['data_type'],
 'value': VARIABLE_CACHE[value_one]['value'] * VARIABLE_CACHE[value_two]['value']
 }
 return
 if operator == "divide":
 VARIABLE_CACHE[product] = {
 'data_type': VARIABLE_CACHE[value_one]['data_type'],
 'value': VARIABLE_CACHE[value_one]['value'] / VARIABLE_CACHE[value_two]['value']
 }
 return
 ##### MATH COMPARISONS END #####
 ##### IF STATEMENTS START #####
 if line.startswith("if"):
 expressions = gather_expressions(line)
 inside_code = gather_inside_code(line, "endif")
 #Evaluate `if not variable then`
 if len(expressions) == 2:
 if expressions[1] in list(VARIABLE_CACHE.keys()):
 data_type = VARIABLE_CACHE[expressions[1]]['data_type']
 if data_type == "boolean":
 #Now check for not
 if expressions[0] == "not":
 if not VARIABLE_CACHE[expressions[1]]['value']:
 for code in inside_code:
 run_code(code)
 else:
 exit(f"SyntaxError: {expressions[0]}")
 #Evaluate `if variable then`
 if expressions[0] in list(VARIABLE_CACHE.keys()):
 data_type = VARIABLE_CACHE[expressions[0]]['data_type']
 if data_type == "boolean":
 if VARIABLE_CACHE[expressions[0]]['value']:
 for code in inside_code:
 run_code(code)
 #Evaluate greaterthan
 if expressions[1] == "greaterthan":
 larger = VARIABLE_CACHE[expressions[0]]
 smaller = VARIABLE_CACHE[expressions[2]]
 if larger['data_type'] == smaller['data_type']:
 if larger['value'] > smaller['value']:
 for code in inside_code:
 run_code(code)
 #Evaluate lessthan
 if expressions[1] == "lessthan":
 smaller = VARIABLE_CACHE[expressions[0]]
 larger = VARIABLE_CACHE[expressions[2]]
 if smaller['data_type'] == smaller['data_type']:
 if smaller['value'] < larger['value']:
 for code in inside_code:
 run_code(code)
 #Evaluate equals
 if expressions[1] == "equals":
 var_one = VARIABLE_CACHE[expressions[0]]
 var_two = VARIABLE_CACHE[expressions[2]]
 if var_one['data_type'] == var_two['data_type']:
 if var_one['value'] == var_two['value']:
 for code in inside_code:
 run_code(code)
 ##### IF STATEMENTS END #####
 ##### FOR STATEMENTS START #####
 if line.startswith("for"):
 iterations = int(line.split()[1])
 inside_code = gather_inside_code(line, "endfor")
 for _ in range(iterations):
 for code in inside_code:
 run_code(code)
 ##### FOR STATEMENTS END #####
def gather_expressions(line):
 """ Gathers all the expressions in the if statement """
 expressions = []
 for word in line.split():
 if word not in ["if", "then"]:
 expressions.append(word)
 return expressions
def gather_inside_code(main_line, ending):
 """ Gathers all the code inside the statement/loop """
 all_lines = get_lines()
 inside_code = []
 for i in range(len(all_lines)):
 if all_lines[i] == main_line:
 for j in range(i + 1, len(all_lines)):
 if all_lines[j].startswith(ending):
 return inside_code
 inside_code.append(all_lines[j][1:-1])
 return None
def has_ending_semicolon(line):
 """ Returns True if the line ends with a semicolon, else returning False """
 return False if line == "" else line.split()[-1] == ";"
def main():
 """ Combines all the steps for the interpreter """
 #Gathers all the declared variables
 for line in get_lines():
 add_to_variable_cache(line)
 #Gathers all the created functions
 add_to_function_cache(get_lines())
 #Runs the code
 for line in get_lines():
 run_code(line)
if __name__ == '__main__':
 main()
asked Jul 27, 2019 at 18:03
\$\endgroup\$
2
  • \$\begingroup\$ Is there anything else required to execute and test the interpreter? Do you have a language specification? Any documentation? \$\endgroup\$ Commented Nov 6, 2019 at 3:27
  • \$\begingroup\$ Some resources that may be of use: aosabook.org/en/500L/…. I started writing up an answer only to realize that there's no point in my speculating, I will eagerly return once I know more about the design of the language. \$\endgroup\$ Commented Nov 6, 2019 at 3:45

0

Know someone who can answer? Share a link to this question via email, Twitter, or Facebook.

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.