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()
\$\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

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.