def validate_varname(name, ctx): if not valid_varname(name): erstr = "Invalid resource name in " + ctx.label() + "." erstr += " Name '" + name + "' is not a valid GML name." return [erstr] return []
def feed(self, symbol): returned_output = "" warnings = [] errors = [] # Helper function for appending output. Will dynamically prefix with a @import_evt line def output(returned_output, string, is_import=False): mod_string = None if is_import and not self.last_out_import: mod_string = "@import_evt\n" + string elif not is_import and self.last_out_import: mod_string = "@endimport\n" + string else: mod_string = string returned_output += mod_string self.last_out_import = is_import self.prev_codeblock_symbols = [" "] return returned_output # Get context information ctx = self.context_stack[-1] if self.context_stack else None #print "S, C, I:", symbol, str(ctx.typ) + "s" + str(ctx.stage) if ctx else None, self.indent_level # Parse global symbols if not ctx: if symbol == 'object': self.context_stack.append(Ctx(CtxType.OBJ_DEC)) elif symbol == 'room': self.context_stack.append(Ctx(CtxType.RM_DEC)) elif symbol == 'script': self.context_stack.append(Ctx(CtxType.SCR_DEC)) elif symbol == '#define' and self.allow_gmldef: self.context_stack.append(Ctx(CtxType.SCR_DEC_GM)) # Error cases else: erstr = "Unexpected symbol '" + symbol + "'." if symbol in KEYWORDS: erstr += " Keywords of this type belong in resource declarations." errors.append(erstr) # Parse object declaration symbols elif ctx.typ == CtxType.OBJ_DEC: # Name of object if ctx.stage == 0: errors.extend(validate_varname(symbol, ctx)) returned_output = output(returned_output, "@obj_declare " + symbol + "\n") self.obj_names.add(symbol) ctx.advance() # Symbol following object name elif ctx.stage == 1: errors.extend(validate_symbol(symbol, ctx, [':', '{'])) if symbol == ':': ctx.advance() else: ctx.stage = 4 # Name of parent elif ctx.stage == 2: errors.extend(validate_varname(symbol, ctx)) if symbol not in self.obj_names: errors.append("No declared object matches name '" + symbol + "'.") returned_output = output( returned_output, "object_set_parent(this_resource," + symbol + ");\n", True) ctx.advance() elif ctx.stage == 3: errors.extend(validate_symbol(symbol, ctx, ['{'])) ctx.advance() # Look for nested declarations elif ctx.stage == 4: errors.extend( validate_symbol(symbol, ctx, ['}', 'event', 'properties', 'import'])) if symbol == '}': self.context_stack.pop() elif symbol == 'event': self.context_stack.append(Ctx(CtxType.EVENT)) elif symbol == 'properties': self.context_stack.append(Ctx(CtxType.PROP)) elif symbol == 'import': self.context_stack.append(Ctx(CtxType.IMPORT)) else: raise NotImplementedError() # Parse room declaration symbols elif ctx.typ == CtxType.RM_DEC: # Name of room if ctx.stage == 0: errors.extend(validate_varname(symbol, ctx)) returned_output = output(returned_output, "@rm_declare " + symbol + "\n") self.rm_names.add(symbol) ctx.advance() # Curly following room name elif ctx.stage == 1: errors.extend(validate_symbol(symbol, ctx, ['{'])) ctx.advance() # Look for nested declarations OR list of objects elif ctx.stage == 2: # Check if symbol is object if symbol in self.obj_names: returned_output = output(returned_output, symbol + "\n") ctx.advance() # Check nested declarations elif symbol == '}': self.context_stack.pop() elif symbol == 'properties': self.context_stack.append(Ctx(CtxType.PROP)) elif symbol == 'import': self.context_stack.append(Ctx(CtxType.IMPORT)) # Error cases else: if valid_varname(symbol): errors.append("Unknown object in room declaration: '" + symbol + "'.") else: errors.append( "Unexpected symbol in room declaration: '" + symbol + "'.") elif ctx.stage == 3: # Check if symbol is object if symbol in self.obj_names: errors.append("Object names must be separated by commas") # Check nested declarations elif symbol == '}': self.context_stack.pop() elif symbol == 'properties': self.context_stack.append(Ctx(CtxType.PROP)) elif symbol == 'import': self.context_stack.append(Ctx(CtxType.IMPORT)) elif symbol == ',': ctx.advance() # Error cases else: errors.append("Unexpected symbol in room declaration: '" + symbol + "'.") # Following a comma elif ctx.stage == 4: # Only GML object can follow commas if symbol in self.obj_names: returned_output = output(returned_output, symbol + "\n") ctx.stage = 3 else: errors.append( "Expecting object name after ',' but instead found '" + symbol + "'.") else: raise NotImplementedError() # Parse property symbols elif ctx.typ == CtxType.PROP: if ctx.stage == 0: errors.extend(validate_symbol(symbol, ctx, ['{'])) self.codeblock = "" ctx.advance() # Add data to block elif ctx.stage == 1: if symbol == '}': # Output parsed data prefix = "object" if self.context_stack[ -2].typ == CtxType.OBJ_DEC else "room" import_block = parse_properties(self.codeblock, prefix, errors) if import_block != None: returned_output = output(returned_output, import_block, True) self.context_stack.pop() else: self.codeblock_add(symbol) # Parse import symbols elif ctx.typ == CtxType.IMPORT: if ctx.stage == 0: errors.extend(validate_symbol(symbol, ctx, ['{'])) self.codeblock = "" ctx.advance() # Add data to block elif ctx.stage == 1: if symbol == '}' and self.indent_level == 0: returned_output = output(returned_output, self.codeblock + "\n", True) self.context_stack.pop() else: if symbol == '{': self.indent_level += 1 elif symbol == '}': self.indent_level -= 1 self.codeblock_add(symbol) # Parse event symbols elif ctx.typ == CtxType.EVENT: if ctx.stage == 0: # Prefix with ev_ mod_symbol = symbol if symbol[:3] == "ev_" else "ev_" + symbol # Match with event type names if mod_symbol not in EVENT_TYPES.keys(): errors.append("No valid event type matches name: '" + mod_symbol + "'.") else: self.event_type_name = mod_symbol returned_output = output( returned_output, "@obj_evt " + str(EVENT_TYPES[mod_symbol]) + " ") ctx.advance() elif ctx.stage == 1: errors.extend(validate_symbol(symbol, ctx, ['('])) ctx.advance() elif ctx.stage == 2: if is_number(symbol): returned_output = output(returned_output, symbol + "\n") ctx.advance() else: # Check no arguments if symbol == ')': returned_output = output(returned_output, "0\n") ctx.stage = 4 if not (self.event_type_name == "ev_create" or self.event_type_name == "ev_destroy" or self.event_type_name == "ev_draw"): errors.append( "Expecting event number argument for event of type " + self.event_type_name) # Check if it is a single char for a keycode elif self.event_type_name[:6] == "ev_key" and len( symbol ) == 3 and symbol[0] == '"' and symbol[2] == '"': returned_output = output( returned_output, str(ord(symbol[1].upper())) + "\n") ctx.advance() # Check for event constants else: prefix = symbol[:3] if self.event_type_name == "ev_create" or self.event_type_name == "ev_destroy" or self.event_type_name == "ev_draw": errors.append( "Unexpected event number argument provided for event of type " + self.event_type_name) elif self.event_type_name[:6] == "ev_key": if prefix == "ev_": errors.append( "Unexpected event number constant beginning with 'ev_' for event type " + self.event_type_name) elif prefix == "vk_": returned_output = output( returned_output, str(KEYS[symbol]) + "\n") else: mod_symbol = "vk_" + symbol if mod_symbol not in KEYS.keys(): errors.append("Unknown key constant " + mod_symbol) else: returned_output = output( returned_output, str(KEYS[mod_symbol]) + "\n") else: if prefix == "vk_": errors.append( "Unexpected event number constant beginning with 'vk_' for event type " + self.event_type_name) elif prefix == "ev_": returned_output = output( returned_output, str(EVENT_NUMS[symbol]) + "\n") else: mod_symbol = "ev_" + symbol if mod_symbol not in EVENT_NUMS.keys(): errors.append( "Unknown event number constant " + mod_symbol) else: returned_output = output( returned_output, str(EVENT_NUMS[mod_symbol]) + "\n") ctx.advance() elif ctx.stage == 3: errors.extend(validate_symbol(symbol, ctx, [')'])) ctx.advance() elif ctx.stage == 4: errors.extend(validate_symbol(symbol, ctx, ['{'])) self.codeblock = "" ctx.advance() # Add data to block elif ctx.stage == 5: if symbol == '}' and self.indent_level == 0: returned_output = output(returned_output, self.codeblock + "\n") self.context_stack.pop() else: if symbol == '{': self.indent_level += 1 elif symbol == '}': self.indent_level -= 1 self.codeblock_add(symbol) # Parse script declaration symbols elif ctx.typ == CtxType.SCR_DEC: # Name of script if ctx.stage == 0: errors.extend(validate_varname(symbol, ctx)) returned_output = output(returned_output, "@scr_declare " + symbol + "\n") self.scr_names.add(symbol) self.scr_args_current = [] ctx.advance() # Opening parenthesis following script name elif ctx.stage == 1: errors.extend(validate_symbol(symbol, ctx, ['('])) ctx.advance() # Argument name or closing paren elif ctx.stage == 2: # Check if end of arguments if symbol == ")": ctx.stage = 5 else: # Verify name errors.extend(validate_varname(symbol, ctx)) self.scr_args_current.append(symbol) ctx.advance() # Comma or end paren elif ctx.stage == 3: errors.extend(validate_symbol(symbol, ctx, [')', ','])) if symbol == ")": ctx.stage = 5 elif symbol == ",": ctx.advance() # Argument name elif ctx.stage == 4: errors.extend(validate_varname(symbol, ctx)) self.scr_args_current.append(symbol) ctx.stage = 3 # Open curly elif ctx.stage == 5: errors.extend(validate_symbol(symbol, ctx, ['{'])) self.codeblock = "" var_declarations = "" for index, argument in enumerate(self.scr_args_current): var_declarations += "var " + argument + ";" var_declarations += argument + "=argument" + str( index) + ";\n" returned_output = output(returned_output, var_declarations) ctx.advance() # Actual code symbols elif ctx.stage == 6: if symbol == '}' and self.indent_level == 0: returned_output = output(returned_output, self.codeblock + "\n") self.context_stack.pop() else: if symbol == '{': self.indent_level += 1 elif symbol == '}': self.indent_level -= 1 else: self.codeblock_add(symbol) # Parse GML-style script declaration symbols elif ctx.typ == CtxType.SCR_DEC_GM: # Name of script if ctx.stage == 0: errors.extend(validate_varname(symbol, ctx)) returned_output = output(returned_output, "@scr_declare " + symbol + "\n") self.scr_names.add(symbol) self.scr_args_current = [] self.indent_level = 0 self.codeblock = "" ctx.advance() # Actual code symbols elif ctx.stage == 1: if symbol in KEYWORDS_SE and self.indent_level == 0: returned_output = output(returned_output, self.codeblock + "\n") self.context_stack.pop() self.indent_level = 0 self.feed( symbol) # Feed the symbol again in the new context else: if symbol == '{': self.indent_level += 1 elif symbol == '}': self.indent_level -= 1 self.codeblock_add(symbol) else: raise NotImplementedError( "Not all context types implemented in compiler") # Prefix with initial if first time outputting if not self.has_out: returned_output = "\n@gml\n" + returned_output self.has_out = True return (returned_output, warnings, errors)
def feed(self, symbol): returned_output = "" warnings = [] errors = [] # Helper function for appending output. Will dynamically prefix with a @import_evt line def output(returned_output, string, is_import=False): mod_string = None if is_import and not self.last_out_import: mod_string = "@import_evt\n" + string elif not is_import and self.last_out_import: mod_string = "@endimport\n" + string else: mod_string = string returned_output += mod_string self.last_out_import = is_import self.prev_codeblock_symbols = [" "] return returned_output # Get context information ctx = self.context_stack[-1] if self.context_stack else None #print "S, C, I:", symbol, str(ctx.typ) + "s" + str(ctx.stage) if ctx else None, self.indent_level # Parse global symbols if not ctx: if symbol == 'object': self.context_stack.append(Ctx(CtxType.OBJ_DEC)) elif symbol == 'room': self.context_stack.append(Ctx(CtxType.RM_DEC)) elif symbol == 'script': self.context_stack.append(Ctx(CtxType.SCR_DEC)) elif symbol == '#define' and self.allow_gmldef: self.context_stack.append(Ctx(CtxType.SCR_DEC_GM)) # Error cases else: erstr = "Unexpected symbol '" + symbol + "'." if symbol in KEYWORDS: erstr += " Keywords of this type belong in resource declarations." errors.append(erstr) # Parse object declaration symbols elif ctx.typ == CtxType.OBJ_DEC: # Name of object if ctx.stage == 0: errors.extend(validate_varname(symbol, ctx)) returned_output = output(returned_output, "@obj_declare " + symbol + "\n") self.obj_names.add(symbol) ctx.advance() # Symbol following object name elif ctx.stage == 1: errors.extend(validate_symbol(symbol, ctx, [':', '{'])) if symbol == ':': ctx.advance() else: ctx.stage = 4 # Name of parent elif ctx.stage == 2: errors.extend(validate_varname(symbol, ctx)) if symbol not in self.obj_names: errors.append("No declared object matches name '" + symbol + "'.") returned_output = output(returned_output, "object_set_parent(this_resource," + symbol + ");\n", True) ctx.advance() elif ctx.stage == 3: errors.extend(validate_symbol(symbol, ctx, ['{'])) ctx.advance() # Look for nested declarations elif ctx.stage == 4: errors.extend(validate_symbol(symbol, ctx, ['}', 'event', 'properties', 'import'])) if symbol == '}': self.context_stack.pop() elif symbol == 'event': self.context_stack.append(Ctx(CtxType.EVENT)) elif symbol == 'properties': self.context_stack.append(Ctx(CtxType.PROP)) elif symbol == 'import': self.context_stack.append(Ctx(CtxType.IMPORT)) else: raise NotImplementedError() # Parse room declaration symbols elif ctx.typ == CtxType.RM_DEC: # Name of room if ctx.stage == 0: errors.extend(validate_varname(symbol, ctx)) returned_output = output(returned_output, "@rm_declare " + symbol + "\n") self.rm_names.add(symbol) ctx.advance() # Curly following room name elif ctx.stage == 1: errors.extend(validate_symbol(symbol, ctx, ['{'])) ctx.advance() # Look for nested declarations OR list of objects elif ctx.stage == 2: # Check if symbol is object if symbol in self.obj_names: returned_output = output(returned_output, symbol + "\n") ctx.advance() # Check nested declarations elif symbol == '}': self.context_stack.pop() elif symbol == 'properties': self.context_stack.append(Ctx(CtxType.PROP)) elif symbol == 'import': self.context_stack.append(Ctx(CtxType.IMPORT)) # Error cases else: if valid_varname(symbol): errors.append("Unknown object in room declaration: '" + symbol + "'.") else: errors.append("Unexpected symbol in room declaration: '" + symbol + "'.") elif ctx.stage == 3: # Check if symbol is object if symbol in self.obj_names: errors.append("Object names must be separated by commas") # Check nested declarations elif symbol == '}': self.context_stack.pop() elif symbol == 'properties': self.context_stack.append(Ctx(CtxType.PROP)) elif symbol == 'import': self.context_stack.append(Ctx(CtxType.IMPORT)) elif symbol == ',': ctx.advance() # Error cases else: errors.append("Unexpected symbol in room declaration: '" + symbol + "'.") # Following a comma elif ctx.stage == 4: # Only GML object can follow commas if symbol in self.obj_names: returned_output = output(returned_output, symbol + "\n") ctx.stage = 3 else: errors.append("Expecting object name after ',' but instead found '" + symbol + "'.") else: raise NotImplementedError() # Parse property symbols elif ctx.typ == CtxType.PROP: if ctx.stage == 0: errors.extend(validate_symbol(symbol, ctx, ['{'])) self.codeblock = "" ctx.advance() # Add data to block elif ctx.stage == 1: if symbol == '}': # Output parsed data prefix = "object" if self.context_stack[-2].typ == CtxType.OBJ_DEC else "room" import_block = parse_properties(self.codeblock, prefix, errors) if import_block != None: returned_output = output(returned_output, import_block, True) self.context_stack.pop() else: self.codeblock_add(symbol) # Parse import symbols elif ctx.typ == CtxType.IMPORT: if ctx.stage == 0: errors.extend(validate_symbol(symbol, ctx, ['{'])) self.codeblock = "" ctx.advance() # Add data to block elif ctx.stage == 1: if symbol == '}' and self.indent_level == 0: returned_output = output(returned_output, self.codeblock + "\n", True) self.context_stack.pop() else: if symbol == '{': self.indent_level += 1 elif symbol == '}': self.indent_level -= 1 self.codeblock_add(symbol) # Parse event symbols elif ctx.typ == CtxType.EVENT: if ctx.stage == 0: # Prefix with ev_ mod_symbol = symbol if symbol[:3] == "ev_" else "ev_" + symbol # Match with event type names if mod_symbol not in EVENT_TYPES.keys(): errors.append("No valid event type matches name: '" + mod_symbol + "'.") else: self.event_type_name = mod_symbol returned_output = output(returned_output, "@obj_evt " + str(EVENT_TYPES[mod_symbol]) + " ") ctx.advance() elif ctx.stage == 1: errors.extend(validate_symbol(symbol, ctx, ['('])) ctx.advance() elif ctx.stage == 2: if is_number(symbol): returned_output = output(returned_output, symbol + "\n") ctx.advance() else: # Check no arguments if symbol == ')': returned_output = output(returned_output, "0\n") ctx.stage = 4 if not (self.event_type_name == "ev_create" or self.event_type_name == "ev_destroy" or self.event_type_name == "ev_draw"): errors.append("Expecting event number argument for event of type " + self.event_type_name) # Check if it is a single char for a keycode elif self.event_type_name[:6] == "ev_key" and len(symbol) == 3 and symbol[0] == '"' and symbol[2] == '"': returned_output = output(returned_output, str(ord(symbol[1].upper())) + "\n") ctx.advance() # Check for event constants else: prefix = symbol[:3] if self.event_type_name == "ev_create" or self.event_type_name == "ev_destroy" or self.event_type_name == "ev_draw": errors.append("Unexpected event number argument provided for event of type " + self.event_type_name) elif self.event_type_name[:6] == "ev_key": if prefix == "ev_": errors.append("Unexpected event number constant beginning with 'ev_' for event type " + self.event_type_name) elif prefix == "vk_": returned_output = output(returned_output, str(KEYS[symbol]) + "\n") else: mod_symbol = "vk_" + symbol if mod_symbol not in KEYS.keys(): errors.append("Unknown key constant " + mod_symbol) else: returned_output = output(returned_output, str(KEYS[mod_symbol]) + "\n") else: if prefix == "vk_": errors.append("Unexpected event number constant beginning with 'vk_' for event type " + self.event_type_name) elif prefix == "ev_": returned_output = output(returned_output, str(EVENT_NUMS[symbol]) + "\n") else: mod_symbol = "ev_" + symbol if mod_symbol not in EVENT_NUMS.keys(): errors.append("Unknown event number constant " + mod_symbol) else: returned_output = output(returned_output, str(EVENT_NUMS[mod_symbol]) + "\n") ctx.advance() elif ctx.stage == 3: errors.extend(validate_symbol(symbol, ctx, [')'])) ctx.advance() elif ctx.stage == 4: errors.extend(validate_symbol(symbol, ctx, ['{'])) self.codeblock = "" ctx.advance() # Add data to block elif ctx.stage == 5: if symbol == '}' and self.indent_level == 0: returned_output = output(returned_output, self.codeblock + "\n") self.context_stack.pop() else: if symbol == '{': self.indent_level += 1 elif symbol == '}': self.indent_level -= 1 self.codeblock_add(symbol) # Parse script declaration symbols elif ctx.typ == CtxType.SCR_DEC: # Name of script if ctx.stage == 0: errors.extend(validate_varname(symbol, ctx)) returned_output = output(returned_output, "@scr_declare " + symbol + "\n") self.scr_names.add(symbol) self.scr_args_current = [] ctx.advance() # Opening parenthesis following script name elif ctx.stage == 1: errors.extend(validate_symbol(symbol, ctx, ['('])) ctx.advance() # Argument name or closing paren elif ctx.stage == 2: # Check if end of arguments if symbol == ")": ctx.stage = 5 else: # Verify name errors.extend(validate_varname(symbol, ctx)) self.scr_args_current.append(symbol) ctx.advance() # Comma or end paren elif ctx.stage == 3: errors.extend(validate_symbol(symbol, ctx, [')', ','])) if symbol == ")": ctx.stage = 5 elif symbol == ",": ctx.advance() # Argument name elif ctx.stage == 4: errors.extend(validate_varname(symbol, ctx)) self.scr_args_current.append(symbol) ctx.stage = 3 # Open curly elif ctx.stage == 5: errors.extend(validate_symbol(symbol, ctx, ['{'])) self.codeblock = "" var_declarations = "" for index, argument in enumerate(self.scr_args_current): var_declarations += "var " + argument + ";" var_declarations += argument + "=argument" + str(index) + ";\n" returned_output = output(returned_output, var_declarations) ctx.advance() # Actual code symbols elif ctx.stage == 6: if symbol == '}' and self.indent_level == 0: returned_output = output(returned_output, self.codeblock + "\n") self.context_stack.pop() else: if symbol == '{': self.indent_level += 1 elif symbol == '}': self.indent_level -= 1 else: self.codeblock_add(symbol) # Parse GML-style script declaration symbols elif ctx.typ == CtxType.SCR_DEC_GM: # Name of script if ctx.stage == 0: errors.extend(validate_varname(symbol, ctx)) returned_output = output(returned_output, "@scr_declare " + symbol + "\n") self.scr_names.add(symbol) self.scr_args_current = [] self.indent_level = 0 self.codeblock = "" ctx.advance() # Actual code symbols elif ctx.stage == 1: if symbol in KEYWORDS_SE and self.indent_level == 0: returned_output = output(returned_output, self.codeblock + "\n") self.context_stack.pop() self.indent_level = 0 self.feed(symbol) # Feed the symbol again in the new context else: if symbol == '{': self.indent_level += 1 elif symbol == '}': self.indent_level -= 1 self.codeblock_add(symbol) else: raise NotImplementedError("Not all context types implemented in compiler") # Prefix with initial if first time outputting if not self.has_out: returned_output = "\n@gml\n" + returned_output self.has_out = True return (returned_output, warnings, errors)