Esempio n. 1
0
 def __init__(self, langdef):
     self.langdef = langdef
     self._scriptgroup = ScriptGroup()
Esempio n. 2
0
class ScriptParser():
    '''
    Private class, do not use this outside this module!
    '''
    def __init__(self, langdef):
        self.langdef = langdef
        self._scriptgroup = ScriptGroup()
    
    def scriptgroup(self):
        '''Returns the scriptgroup with all registered resources.'''
        return self._scriptgroup
        
        
    def routineDef(self, name):
        '''Get a new routine, which will be registered as a known resource.'''
        r = Routine()
        self._scriptgroup.register(r, varname=name)
        return r
    
    def textDef(self, name):
        '''Get a new string, which will be registered as a known resource.'''
        r = PokeString()
        self._scriptgroup.register(r, varname=name)
        return r
        
    def movementDef(self, name):
        '''Get a new movement, which will be registered as a known resource.'''
        r = PokeMovement()
        self._scriptgroup.register(r, varname=name)
        return r
        
        
    def parselines(self, lines, resource):
        '''
        Parses a set of lines, returning a list resources.
        Note that the AST is not analyzed yet for errors!
        
        Returns the last working resource.
        '''
        for line in lines:
            resource = self.parseline(line, resource)
        return resource
    
    
    
    def parseline(self, line_raw, resource):
        line = line_raw
        
        #remove any comments, if any
        commentstart = line.find("'")
        if commentstart >= 0:
            line = line[:commentstart]
        
        line = line.lstrip()
        lineargs = self._line2args(line)
        
        if len(lineargs) == 0:
            return resource
        
        command = lineargs[0]
        args = lineargs[1:]
        
        if command == "#script":
            offset = args[0]  #TODO Validate
            return self.routineDef(offset)

        if command == "#text":
            offset = args[0]  #TODO: Validate
            return self.textDef(offset)
        
        if command == "#movement":
            offset = args[0]
            return self.movementDef(offset)
        
        if command[0] == "$":
            #$var = Hi I'm John! is sugar for #inline $var
            return self.parseline ("#inline "+line, resource)

        #Handle inline commands: #inline $Variable = Hi I'm John!
        if command == "#inline":   
            offset  = args[0]  #TODO: Validate
            #ctype   = args[1]  #TODO: Make use of it :)
            
            raise Exception("TODO, not supported yet") #TODO
            
            #All done, inline is over. Continue with normal param
            return resource
        
        #A set of raw commands is added to the stream
        if command == "#binary" or command == "#raw":            
            if not isinstance(resource, Routine):
                raise WrongCommandException()
                
            for byte in args:
                resource.ast().append(ast.ASTByte(toint(byte)))
            return resource
        
        #Parse movements
        if command[0] == ":":
            if not isinstance(resource, PokeMovement):
                raise WrongCommandException
            
            for byte in args:
                resource.append(toint(byte))
            return resource
        
        #Parse strings
        if command[0] == "=":
            if not isinstance(resource, PokeString):
                raise WrongCommandException()
            
            resource.append(line_raw[1:].lstrip())
            return resource
        
        
        # Still here? -> No pre-defined elements, check aliases and commands for
        #                possible command-compile 

        #Try to find an alias for this line, if it is there, take it.
        # Only if there is no matching alias, we look for a command to match
        for alias in self.langdef.aliases:
            if alias.matches(line):
                rawargs = self._prepargs(alias, alias.stripParams(line))
                resource.ast().append(ast.ASTCommand(alias, rawargs))
                return resource
            
        
        #Parse a command
        if command in self.langdef.commands:
            if not isinstance(resource, Routine):
                raise WrongCommandException()
            
            lcommand = self.langdef.commands[command]
            rawargs = self._prepargs(lcommand, args)

            resource.ast().append(ast.ASTCommand(lcommand, rawargs))
            return resource
        
        raise Exception("Could not parse line, no rules matched: %s"%line)
    
    
    def _prepargs(self, command, args):
        #Rewrite references to AST Reference nodes
        rawargs = []
        i_args = 0
        for i in range(0, len(command.params)):
            commandparam = command.getParam(i)
            #print(repr(commandparam))
            if commandparam.defaultvalue == None:
                #print(" -> consume! "+repr(args[i_args]))
                if ParamType.ispointer(commandparam.ptype):
                    rawargs.append(ast.ASTRef(args[i_args]))
                elif commandparam.definevaluestype != None:
                    defines = self.langdef.getDefines(commandparam.definevaluestype)
                    #Reversed defines lookup, not as elegant as I hoped it
                    key = args[i_args].upper()
                    val = None
                    try:
                        #If defines is a list:
                        try:
                            val = defines.index(key)
                        except:
                            val = next((dval for dval, dkey in defines.items() if dkey == key))
                        rawargs.append(ast.ASTDefinedValue(key, val))
                    except:
                        #There is no define for the value, parse it as is
                        # but then ofc. it should be an int.
                        try:
                            rawargs.append(toint(args[i_args]))
                        except:
                            raise Exception("%s is not defined, and could not be converted to an integer!"%args[i_args])
                else:
                    rawargs.append(toint(args[i_args]))
                i_args += 1
        return rawargs
        
    
    def _line2args(self, line):
        '''
        Normalizes the arguments. Returns a list of lowercased arguments.
        Note that this will not be sufficient for String parsing: multiple spaces
        are treated as single spaces.
        '''        
        line = line.strip()
        if not line:
            return []
              
        return line.lower().replace("\t", " ").strip().split()