Newer
Older
atlantis / src / interpreter.py
#!/usr/bin/python3
# 
# Atlantis is a framework for creating multi-user dungeon worlds.
# This module reads in a world file and parses it. The structure of 
# the Atlantis language ATL is defined here.
#
# Licensed under the terms of the GPLv3
# author: Daniel Vedder
# date: 02/05/2015
#

from world import World
from define import DefineCommand, define_command_registry


class Parser(object):
    '''
    The actual parser class. It reads in a world file and transforms it into
    a series of DefineCommands, which are then executed
    '''

    def __init__(self, world_file_name):
        self.line_command_registry = dict()
        self.world = World()
        self.add_line_commands()

    def load(self, world_file_name):
        '''
        Load a world file and pass it on to the interpreter
        '''
        try:
            world_file = open(world_file_name, 'r')
            atl_text = world_file.readlines()
            world_file.close()
            print("Loaded "+world_file_name)
        except IOError:
            print("Failed to load world file '"+world_file_name+"'!")
        self.interpret_atl(atl_text)
        return self.world

    def add_line_commands(self):
        '''
        Line commands are ATL constructs that only take up one line
        (unlike define commands). All line commands must be registered in
        this method by adding them to the line_command_registry and setting
        their key-value to the method to be called when their key appears
        in an ATL source file.
        '''
        self.line_command_registry["load"] = self.load
        self.line_command_registry["start-place"] = self.world.set_starting_place

    def interpret_atl(self, atl_source):
        '''
        This method interprets a list of ATL source lines, passing them
        on to the relevant define commands.
        '''
        define_command = None # The define command currently being executed
        line_no = 0
        for line in atl_source:
            # TODO allow for linebreaks
            # while line[-2] == "\\":
            #     line = line + atl_source[line_no+1]
            #     line_no = line_no+1
            line_no = line_no + 1
            if len(line) < 2 and define_command:
                # Empty lines finish off define blocks
                object_type, game_object = define_command.return_object()
                self.world.add_object(object_type, game_object)
                define_command = None
            elif len(line) < 2 or line[0] == "#":
                pass #comments and empty lines are ignored
            else:
                command = line.split()[0]
                print("Line "+str(line_no)+": '"+line+"'\nCommand: '"+command+"'")
                # execute a line command
                if command in self.line_command_registry.keys():
                    self.line_command_registry[command](line[line.find(" ")+1:-1])
                # start of a define block
                elif command in define_command_registry.keys():
                    define_command = define_command_registry[command]
                    object_name = line[line.find(" ")+1:-1]
                    define_command.init_object(object_name)
                    print("Found define command '"+define_command.name)
                # parse an option command
                elif line[0] == " " or line[0] == "\t":
                    while line[0] == " " or line[0] == "\t":
                        line = line[1:]
                    if line and define_command:
                        option_command = line.split()[0]
                        option_arg = line[line.find(" ")+1:-1]
                        define_command.pass_option(option_command, option_arg)
                    else:
                        # XXX: What should be done here? Do nothing, raise a
                        # syntax error, or something else?
                        print("Unrecognized syntax in line "+str(line_no)+"!")