class MinerProtocol(Protocol): """ implementation of the Minecraft protocol """ def __init__(self, name): global debug self.log = debug self.bot = MinerBot(name, self.send) self.buffer = Buffer() self.world = World() self.packets = { # ID: (function, format 0x00: (self.onKeepAlive, Standard("Bi")), # Keep alive 0x01: (self.onLoginRequest, String("BiSqibbBB")), # Login request 0x02: (self.onHandshake, String("BS")), # Handshake 0x03: (self.onChat, String("BS")), # Chat message 0x04: (None, Standard("Bq")), # Time update 0x05: (None, Standard("Bihhh")), # Entity Equipment 0x06: (self.onSpawnPosition, Standard("Biii")), # Spawn position 0x07: (None, Standard("Bii?")), # Use entity (C --> S) 0x08: (self.onUpdateHealth, Standard("Bhhf")), # Update health 0x09: (None, Standard("Bbbbhl")), # Respawn (C --> S) 0x0A: (None, Standard("B?")), # Player (C --> S) 0x0B: (None, Standard("Bdddd?")), # Player position (C --> S) 0x0C: (None, Standard("Bff?")), # Player look (C --> S) 0x0D: (self.onPLayerPosition, Standard("Bddddff?")), # Player position & look 0x0E: (None, Standard("Bbibib")), # Player digging (C --> S) 0x0F: (None, BlockSlot("BibibW")), # Player block placement (C --> S) 0x10: (None, Standard("Bh")), # Holding change (C --> S) 0x11: (None, Standard("Bibibi")), # Use bed 0x12: (None, Standard("Bib")), # Animation 0x13: (None, Standard("Bib")), # Entity action (C --> S) 0x14: (self.onNamedEntitySpawn, String("BiSiiibbh")), # Named entity spawn 0x15: (None, Standard("Bihbhiiibbb")), # Pickup spawn 0x16: (None, Standard("Bii")), # Collect item 0x17: (self.onAddObject, Standard("Bibiiiihhh")), # Add object/vehicle 0x18: (None, MetaEntity("BibiiibbE")), # Mob spawn 0x19: (None, String("BiSiiii")), # Entity: painting 0x1A: (None, Standard("Biiiih")), # Experience Orb 0x1B: (None, Standard("Bffff??")), # Stance update 0x1C: (None, Standard("Bihhh")), # Entity velocity 0x1D: (None, Standard("Bi")), # Destroy entity 0x1E: (None, Standard("Bi")), # Entity 0x1F: (self.onEntityRelativeMove, Standard("Bibbb")), # Entity relative move 0x20: (None, Standard("Bibb")), # Entity look 0x21: (None, Standard("Bibbbbb")), # Entity look and relative move 0x22: (None, Standard("Biiiibb")), # Entity teleport 0x26: (None, Standard("Bib")), # Entity status 0x27: (None, Standard("Bii")), # Attach entity 0x28: (None, MetaEntity("BiE")), # Entity metadata 0x29: (None, Standard("Bibbh")), # Entity Effect 0x2A: (None, Standard("Bib")), # Remove Entity Effect 0x2B: (None, Standard("Bfhh")), # Experience 0x32: (self.onPreChunk, Standard("Bii?")), # Pre chunk 0x33: (self.onChunk, MetaChunk()), # Map chunk 0x34: (None, BlockArray()), # Multi-block change 0x35: (None, Standard("Bibibb")), # Block change 0x36: (None, Standard("Bihibb")), # Block action 0x3C: (None, BlockExplosion()), # Explosion 0x3D: (None, Standard("Biibii")), # Sound effect 0x46: (None, Standard("Bbb")), # New/invalid state 0x47: (None, Standard("Bi?iii")), # Thunderbolt 0x64: (None, String("BbbSb")), # Open window 0x65: (None, Standard("Bb")), # Close window 0x66: (None, BlockSlot("Bbhbh?W")), # Window click 0x67: (None, BlockSlot("BbhW")), # Set slot 0x68: (self.onWindowItems, Window()), # Window items 0x69: (None, Standard("Bbhh")), # Update window property 0x6A: (self.onTransaction, Standard("Bbh?")), # Transaction 0x6B: (None, BlockSlot("BhW")), # Creative inventory action 0x6C: (None, Standard("Bbb")), # Enchant Item 0x82: (None, String("BihiSSSS")), # Update sign 0x83: (None, ByteArray()), # item data 0xC8: (None, Standard("Bib")), # Increment statistic 0xC9: (self.onPlayerList, String("BS?h")), # Player List Item 0xFF: (self.onDisconnected, String("BS")), # Disconnect } def connectionMade(self): """ called when a connection has been established """ self.log.info("<%s> connected" % self.bot.getName()) # self.writer = PacketWriter(self.transport) self.log.debug("ATTEMPTING TO HANDSHAKE") self.sendHandshake(self.bot.getName()) def connectionLost(self, reason): self.log.error("connection lost %s" % reason) def dataReceived(self, data): """ new data are received """ self.buffer.append(data) while True: # get the packet Type try: data = self.buffer.peek()[0] packetType = ord(data) except IOError: # if empty buffer stop the loop break self.log.received(packetType) # get the packet information if packetType not in self.packets: self.log.error("Unknown packet 0x%02X" % packetType) self.transport.loseConnection() fct, fmt = self.packets[packetType] try: packet = fmt.unpack(self.buffer) except IOError: break # do action on packet received if fct != None: fct(self.bot, packet) def onDisconnected(self, bot, info): self.log.info("Disconnected by server. Reason=%s" % info[1]) def onKeepAlive(self, bot, info): self.send(0x00, 0) def onUpdateHealth(self, bot, info): bot.setHealth(health=info[1], food=info[2], foodSaturation=info[3]) def onLoginRequest(self, bot, info): bot.setUID(info[1]) #bot.setMaxSeed(info[3]) pass def onHandshake(self, bot, info): #bot.setConnectionHash(info[1]) self.sendLoginRequest(22, self.bot.getName()) self.bot.nameToUpper() def onSpawnPosition(self, bot, info): bot.setSpawnPosition(info) def onPLayerPosition(self, bot, info): cmd, x, stance, y, z, yaw, pitch, onGround = info bot.setPlayerPosition(x, y, z, stance, yaw, pitch, onGround) self.send(0x0D, x, y, stance, z, yaw, pitch, onGround) def onEntityRelativeMove(self, bot, info): bot.setEntityRelativeMove(info) def onWindowItems(self, bot, info): pass # if info[1] == 0: # # inventory window # bot.setInventory(info[2], info[3]) # else: # logging.error("Unknown window number=%d" % info[1]) # def onSetSlot(self, bot, info): # if info[1] == 0: # bot.setSlotItem(info[1], info[2], info[3], info[4], info[5]) # logging.debug("SetSlot %d, slot %d" %(info[1], info[2])) def onNamedEntitySpawn(self, bot, info): bot.setPlayerSpawn(info) def onAddObject(self): pass def onPreChunk(self, bot, info): pass def onChunk(self, bot, info): cmd, x, y, z, sx, sy, sz, compSize, chunkData = info # correct the real size sx += 1 sy += 1 sz += 1 self.log.chunk( "Chunk (x,y,z)=(%d, %d, %d) (sx,sy,sz)=(%d, %d, %d), len=%d" % (x, y, z, sx, sy, sz, len(chunkData))) self.world.addChunk(x, y, z, sx, sy, sz, chunkData) def onTransaction(self, bot, info): pass def send(self, cmd, *data): if cmd not in self.packets: self.log.error("Unknown packet to send 0x%02X" % cmd) self.transport.loseConnection() fct, pack = self.packets[cmd] res = pack.pack(cmd, *data) deb = "" for x in res: deb += "%02X " % ord(x) self.transport.write(res) self.log.send(cmd) def sendDisconnect(self, reason): self.send(0xFF, "Disconnection by client") self.transport.loseConnection() def sendHandshake(self, userName): self.send(0x02, userName) def sendLoginRequest(self, protocol_version, username): self.send(0x01, protocol_version, username, 0, 0, 0, 0, 0, 0) def onChat(self, bot, info): ''' Chat received. Analyse it to detect a message from the player ''' info = info[1].upper() if ord(info[0]) == 0xA7: #skip color code info = info[2:] # if the message is from a player it starts with <PlayerName> result = re.match("<(.*)>(.*)", info) if result == None: name = "" msg = info else: name = result.group(1) msg = result.group(2) result = msg.split() if BOT_NAME_NEEDED: # Command starts with the targeted Bot name if result[0] == bot.getName(): bot.parseCommand(name, result[1:]) else: if name <> bot.getName(): # do not compute self message bot.parseCommand(name, result) def onPlayerList(self, bot, info): bot.updatePlayerList(info[1], info[2])
class MinerProtocol(Protocol): """ implementation of the Minecraft protocol """ def __init__(self, name): global debug self.log = debug self.bot = MinerBot(name, self.send) self.buffer = Buffer() self.world = World() self.packets = { # ID: (function, format 0x00: (self.onKeepAlive, Standard("Bi")), # Keep alive 0x01: (self.onLoginRequest, String("BiSqibbBB")), # Login request 0x02: (self.onHandshake, String("BS")), # Handshake 0x03: (self.onChat, String("BS")), # Chat message 0x04: (None, Standard("Bq")), # Time update 0x05: (None, Standard("Bihhh")), # Entity Equipment 0x06: (self.onSpawnPosition, Standard("Biii")), # Spawn position 0x07: (None, Standard("Bii?")), # Use entity (C --> S) 0x08: (self.onUpdateHealth, Standard("Bhhf")), # Update health 0x09: (None, Standard("Bbbbhl")), # Respawn (C --> S) 0x0A: (None, Standard("B?")), # Player (C --> S) 0x0B: (None, Standard("Bdddd?")), # Player position (C --> S) 0x0C: (None, Standard("Bff?")), # Player look (C --> S) 0x0D: (self.onPLayerPosition, Standard("Bddddff?")), # Player position & look 0x0E: (None, Standard("Bbibib")), # Player digging (C --> S) 0x0F: (None, BlockSlot("BibibW")), # Player block placement (C --> S) 0x10: (None, Standard("Bh")), # Holding change (C --> S) 0x11: (None, Standard("Bibibi")), # Use bed 0x12: (None, Standard("Bib")), # Animation 0x13: (None, Standard("Bib")), # Entity action (C --> S) 0x14: (self.onNamedEntitySpawn, String("BiSiiibbh")), # Named entity spawn 0x15: (None, Standard("Bihbhiiibbb")), # Pickup spawn 0x16: (None, Standard("Bii")), # Collect item 0x17: (self.onAddObject, Standard("Bibiiiihhh")), # Add object/vehicle 0x18: (None, MetaEntity("BibiiibbE")), # Mob spawn 0x19: (None, String("BiSiiii")), # Entity: painting 0x1A: (None, Standard("Biiiih")), # Experience Orb 0x1B: (None, Standard("Bffff??")), # Stance update 0x1C: (None, Standard("Bihhh")), # Entity velocity 0x1D: (None, Standard("Bi")), # Destroy entity 0x1E: (None, Standard("Bi")), # Entity 0x1F: (self.onEntityRelativeMove, Standard("Bibbb")), # Entity relative move 0x20: (None, Standard("Bibb")), # Entity look 0x21: (None, Standard("Bibbbbb")), # Entity look and relative move 0x22: (None, Standard("Biiiibb")), # Entity teleport 0x26: (None, Standard("Bib")), # Entity status 0x27: (None, Standard("Bii")), # Attach entity 0x28: (None, MetaEntity("BiE")), # Entity metadata 0x29: (None, Standard("Bibbh")), # Entity Effect 0x2A: (None, Standard("Bib")), # Remove Entity Effect 0x2B: (None, Standard("Bfhh")), # Experience 0x32: (self.onPreChunk, Standard("Bii?")), # Pre chunk 0x33: (self.onChunk, MetaChunk()), # Map chunk 0x34: (None, BlockArray()), # Multi-block change 0x35: (None, Standard("Bibibb")), # Block change 0x36: (None, Standard("Bihibb")), # Block action 0x3C: (None, BlockExplosion()), # Explosion 0x3D: (None, Standard("Biibii")), # Sound effect 0x46: (None, Standard("Bbb")), # New/invalid state 0x47: (None, Standard("Bi?iii")), # Thunderbolt 0x64: (None, String("BbbSb")), # Open window 0x65: (None, Standard("Bb")), # Close window 0x66: (None, BlockSlot("Bbhbh?W")), # Window click 0x67: (None, BlockSlot("BbhW")), # Set slot 0x68: (self.onWindowItems, Window()), # Window items 0x69: (None, Standard("Bbhh")), # Update window property 0x6A: (self.onTransaction, Standard("Bbh?")), # Transaction 0x6B: (None, BlockSlot("BhW")), # Creative inventory action 0x6C: (None, Standard("Bbb")), # Enchant Item 0x82: (None, String("BihiSSSS")), # Update sign 0x83: (None, ByteArray()), # item data 0xC8: (None, Standard("Bib")), # Increment statistic 0xC9: (self.onPlayerList, String("BS?h")), # Player List Item 0xFF: (self.onDisconnected, String("BS")), # Disconnect } def connectionMade(self): """ called when a connection has been established """ self.log.info("<%s> connected" % self.bot.getName()) # self.writer = PacketWriter(self.transport) self.log.debug("ATTEMPTING TO HANDSHAKE") self.sendHandshake(self.bot.getName()) def connectionLost(self, reason): self.log.error("connection lost %s" % reason) def dataReceived(self, data): """ new data are received """ self.buffer.append(data) while True: # get the packet Type try: data = self.buffer.peek()[0] packetType = ord(data) except IOError: # if empty buffer stop the loop break self.log.received(packetType) # get the packet information if packetType not in self.packets: self.log.error("Unknown packet 0x%02X" % packetType) self.transport.loseConnection() fct, fmt = self.packets[packetType] try: packet = fmt.unpack(self.buffer) except IOError: break # do action on packet received if fct!= None: fct(self.bot, packet) def onDisconnected(self, bot, info): self.log.info("Disconnected by server. Reason=%s" % info[1]) def onKeepAlive(self, bot, info): self.send(0x00, 0) def onUpdateHealth(self, bot, info): bot.setHealth(health=info[1], food=info[2], foodSaturation=info[3]) def onLoginRequest(self, bot, info): bot.setUID(info[1]) #bot.setMaxSeed(info[3]) pass def onHandshake(self, bot, info): #bot.setConnectionHash(info[1]) self.sendLoginRequest(22, self.bot.getName()) self.bot.nameToUpper() def onSpawnPosition(self, bot, info): bot.setSpawnPosition(info) def onPLayerPosition(self, bot, info): cmd, x, stance, y, z, yaw, pitch, onGround = info bot.setPlayerPosition(x, y, z, stance, yaw, pitch, onGround) self.send(0x0D, x, y, stance, z, yaw, pitch, onGround) def onEntityRelativeMove(self, bot, info): bot.setEntityRelativeMove(info) def onWindowItems(self, bot, info): pass # if info[1] == 0: # # inventory window # bot.setInventory(info[2], info[3]) # else: # logging.error("Unknown window number=%d" % info[1]) # def onSetSlot(self, bot, info): # if info[1] == 0: # bot.setSlotItem(info[1], info[2], info[3], info[4], info[5]) # logging.debug("SetSlot %d, slot %d" %(info[1], info[2])) def onNamedEntitySpawn(self, bot, info): bot.setPlayerSpawn(info) def onAddObject(self): pass def onPreChunk(self, bot, info): pass def onChunk(self, bot, info): cmd, x, y, z, sx, sy, sz, compSize, chunkData = info # correct the real size sx += 1 sy += 1 sz += 1 self.log.chunk("Chunk (x,y,z)=(%d, %d, %d) (sx,sy,sz)=(%d, %d, %d), len=%d" % (x, y, z, sx, sy, sz, len(chunkData))) self.world.addChunk(x, y, z, sx, sy, sz, chunkData) def onTransaction(self, bot, info): pass def send(self, cmd, *data): if cmd not in self.packets: self.log.error("Unknown packet to send 0x%02X" % cmd) self.transport.loseConnection() fct, pack = self.packets[cmd] res = pack.pack(cmd, *data) deb = "" for x in res: deb += "%02X " % ord(x) self.transport.write(res) self.log.send(cmd) def sendDisconnect(self, reason): self.send(0xFF, "Disconnection by client") self.transport.loseConnection() def sendHandshake(self, userName): self.send(0x02, userName) def sendLoginRequest(self, protocol_version, username): self.send(0x01, protocol_version, username, 0, 0, 0, 0, 0, 0) def onChat(self, bot, info): ''' Chat received. Analyse it to detect a message from the player ''' info = info[1].upper() if ord(info[0]) == 0xA7: #skip color code info = info[2:] # if the message is from a player it starts with <PlayerName> result = re.match("<(.*)>(.*)", info) if result == None: name = "" msg = info else: name = result.group(1) msg = result.group(2) result = msg.split() if BOT_NAME_NEEDED: # Command starts with the targeted Bot name if result[0] == bot.getName(): bot.parseCommand(name, result[1:]) else: if name <> bot.getName(): # do not compute self message bot.parseCommand(name, result) def onPlayerList(self, bot, info): bot.updatePlayerList(info[1], info[2])
class Parser(object): def __init__(self, lexer, error_handler=None): self.input = Buffer(lexer.tokenize()) self.error_handler = error_handler self.is_eof = lambda t: isinstance(t, EOFToken) self.is_def = lambda t: isinstance(t, DefToken) self.is_extern = lambda t: isinstance(t, ExternToken) self.is_if = lambda t: isinstance(t, IfToken) self.is_then = lambda t: isinstance(t, ThenToken) self.is_else = lambda t: isinstance(t, ElseToken) self.is_for = lambda t: isinstance(t, ForToken) self.is_in = lambda t: isinstance(t, InToken) self.is_var = lambda t: isinstance(t, VarToken) self.is_identifer = lambda t: isinstance(t, IdentifierToken) self.is_number = lambda t: isinstance(t, NumberToken) self.is_character = lambda t: isinstance(t, CharacterToken) self.is_binary = lambda t: isinstance(t, BinaryToken) self.is_unary = lambda t: isinstance(t, UnaryToken) self.is_binop = lambda t: OperatorManager.is_binop(t) self.is_unop = lambda t: OperatorManager.is_unop(t) def collect(self): return self.input.accept() def look(self, condition=None): return self.input.peek(condition) def expect(self, condition=None): return self.input.move(condition) def try_number_expr(self): """ number_expr ::= number """ number = self.expect(self.is_number) if number is None: return None return NumberExprNode(number) def try_paren_expr(self): """ paren_expr ::= '(' expr ')' """ left_paren = self.expect(lambda t: t.name == '(') if left_paren is None: return None expr = self.try_expr() right_paren = self.expect(lambda t: t.name == ')') if right_paren is None: raise ExpectedRightParen(left_paren.line if expr is None else expr.line) return expr def try_identifier_expr(self): """ identifier_expr ::= identifier ::= identifier '(' ')' ::= identifier '(' expr (',' expr)* ')' """ identifier = self.expect(self.is_identifer) if identifier is None: return None left_paren = self.expect(lambda t: t.name == '(') if left_paren is None: return VariableExprNode(identifier) else: args = [] while True: arg = self.try_expr() if arg is not None: args.append(arg) self.expect(lambda t: t.name == ',') else: right_paren = self.expect(lambda t: t.name == ')') if right_paren is None: raise ExpectedRightParen(left_paren.line if len(args) == 0 else args[-1].line) return CallExprNode(identifier, args) def try_if_expr(self): """ if_expr ::= if expr then expr else expr """ token = self.expect(self.is_if) if token is None: return None condition = self.try_expr() if condition is None: raise ExpectedExpr(token.line) token = self.expect(self.is_then) if token is None: raise ExpectedThen(condition.line) true = self.try_expr() if true is None: raise ExpectedExpr(token.line) token = self.expect(self.is_else) if token is None: raise ExpectedElse(true.line) false = self.try_expr() if false is None: raise ExpectedExpr(token.line) return IfExprNode(condition, true, false) def try_for_expr(self): """ for_expr ::= for identifier '=' expr ',' expr (',' expr)? in expr """ token = self.expect(self.is_for) if token is None: return None variable = self.expect(self.is_identifer) if variable is None: raise ExpectedIdentifier(token.line) token = self.expect(lambda t: t.name == '=') if token is None: raise ExpectedEqualSign(variable.line) begin = self.try_expr() if begin is None: raise ExpectedExpr(token.line) token = self.expect(lambda t: t.name == ',') if token is None: raise ExpectedComma(begin.line) end = self.try_expr() if end is None: raise ExpectedExpr(token.line) token = self.expect(lambda t: t.name == ',') if token is not None: step = self.try_expr() if step is None: raise ExpectedExpr(token.line) else: step = None token = self.expect(self.is_in) if token is None: raise ExpectedIn(end.line if step is None else step.line) body = self.try_expr() if body is None: raise ExpectedExpr(token.line) return ForExprNode(variable, begin, end, step, body) def try_var_expr(self): """ var_expr ::= var identifier ('=' expr)? (',' identifier ('=' expr)?)* in expr """ var_token = self.expect(self.is_var) if var_token is None: return None variables = {} while True: identifier = self.expect(self.is_identifer) if identifier is None: break assignment = self.expect(lambda t: t.name == '=') if assignment is not None: expr = self.try_expr() if expr is None: raise ExpectedExpr(assignment.line) variables[identifier] = expr else: variables[identifier] = None self.expect(lambda t: t.name == ',') if len(variables) == 0: raise ExpectedVariableList(var_token.line) in_token = self.expect(self.is_in) if in_token is None: raise ExpectedIn(max([var.line for var in variables.keys()])) body = self.try_expr() if body is None: raise ExpectedExpr(in_token.line) return VarExprNode(variables, body) def try_primary_expr(self): """ primary_expr ::= number_expr ::= paren_expr ::= identifier_expr ::= if_expr ::= for_expr ::= var_expr """ expr = self.try_number_expr() if expr is not None: return expr expr = self.try_paren_expr() if expr is not None: return expr expr = self.try_identifier_expr() if expr is not None: return expr expr = self.try_if_expr() if expr is not None: return expr expr = self.try_for_expr() if expr is not None: return expr return self.try_var_expr() def try_unary_expr(self): """ unary_expr ::= primary_expr ::= unop unary_expr """ unop = self.expect(self.is_unop) if unop is None: return self.try_primary_expr() operand = self.try_unary_expr() if operand is None: raise ExpectedOperand(unop.line) return UnaryExprNode(unop, operand) def try_binop_rhs(self, lhs, lhs_prec): """ binop_rhs ::= (binop unary_expr)* """ while True: binop = self.look(self.is_binop) if binop is None: return lhs prec = OperatorManager.get_binop_precedence(binop) if prec < lhs_prec: return lhs self.expect(self.is_binop) # eat binop rhs = self.try_unary_expr() if rhs is None: raise ExpectedUnaryExpr(binop.line) next_binop = self.look(self.is_binop) if next_binop is not None and \ OperatorManager.get_binop_precedence(next_binop) > prec: rhs = self.try_binop_rhs(rhs, prec + 1) lhs = BinaryExprNode(binop, lhs, rhs) def try_expr(self): """ expr ::= unary_expr binop_rhs """ lhs = self.try_unary_expr() if lhs is None: return None return self.try_binop_rhs(lhs, 0) def try_normal_prototype(self): """ normal_prototype ::= identifier '(' identifier* ')' """ identifier = self.expect(self.is_identifer) if identifier is None: return None left_paren = self.expect(lambda t: t.name == '(') if left_paren is None: raise ExpectedLeftParen(identifier.line) args = [] while True: arg = self.expect(self.is_identifer) if arg is not None: args.append(arg) else: right_paren = self.expect(lambda t: t.name == ')') if right_paren is None: raise ExpectedRightParen(left_paren.line if len(args) == 0 else args[-1].line) return PrototypeNode(identifier, args) def try_binary_prototype(self): """ binary_prototype ::= binary character number? '(' identifier identifier ')' """ binary = self.expect(self.is_binary) if binary is None: return None operator = self.expect(self.is_character) if operator is None: raise ExpectedOperator(binary.line) precedence = self.expect(self.is_number) left_paren = self.expect(lambda t: t.name == '(') if left_paren is None: raise ExpectedLeftParen(operator.line if precedence is None else precedence.line) arg1 = self.expect(self.is_identifer) if arg1 is None: raise ExpectedIdentifier(left_paren.line) arg2 = self.expect(self.is_identifer) if arg2 is None: raise ExpectedIdentifier(arg1.line) right_paren = self.expect(lambda t: t.name == ')') if right_paren is None: raise ExpectedRightParen(arg2.line) return BinOpPrototypeNode(IdentifierToken(binary.name + operator.name, binary.line), precedence, [arg1, arg2]) def try_unary_prototype(self): """ unary_prototype ::= unary character '(' identifier ')' """ unary = self.expect(self.is_unary) if unary is None: return None operator = self.expect(self.is_character) if operator is None: raise ExpectedOperator(unary.line) left_paren = self.expect(lambda t: t.name == '(') if left_paren is None: raise ExpectedLeftParen(operator.line) arg = self.expect(self.is_identifer) if arg is None: raise ExpectedIdentifier(left_paren.line) right_paren = self.expect(lambda t: t.name == ')') if right_paren is None: raise ExpectedRightParen(arg.line) return UnOpPrototypeNode(IdentifierToken(unary.name + operator.name, unary.line), [arg]) def try_prototype(self): """ prototype ::= normal_prototype ::= binary_prototype ::= unary_prototype """ prototype = self.try_normal_prototype() if prototype is not None: return prototype prototype = self.try_binary_prototype() if prototype is not None: return prototype prototype = self.try_unary_prototype() if prototype is not None: return prototype def try_function(self): """ function ::= def prototype expr """ keyword = self.expect(self.is_def) if keyword is None: return None prototype = self.try_prototype() if prototype is None: raise ExpectedPrototype(keyword.line) expr = self.try_expr() if expr is None: raise ExpectedExpr(prototype.line) return FunctionNode(prototype, expr) def try_declaration(self): """ declaration ::= extern prototype """ keyword = self.expect(self.is_extern) if keyword is None: return None prototype = self.try_prototype() if prototype is None: raise ExpectedPrototype(keyword.line) return prototype def try_toplevel_expr(self): """ toplevel_expr ::= expr we make an anonymous prototype to represent a top-level expr """ expr = self.try_expr() if expr is None: return None return TopLevelExpr(expr) def try_negligible_character(self): # ignore top-level semicolons return self.expect(lambda t: t.name == ';') def try_eof(self): return self.expect(self.is_eof) def try_unknown(self): # try to recovery from syntax errors by eating an unknown token token = self.expect() if token is not None: raise UnknownToken(token) def parse(self): while True: try: node = self.try_function() if node is not None: yield node continue node = self.try_declaration() if node is not None: yield node continue node = self.try_toplevel_expr() if node is not None: yield node continue if self.try_negligible_character() is not None: continue if self.try_eof() is not None: break self.try_unknown() except BoidaeSyntaxError as e: if self.error_handler is not None: self.error_handler(e)