#
# AWL simulator - Operator translator
#
# Copyright 2012-2015 Michael Buesch <m@bues.ch>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
#

from __future__ import division, absolute_import, print_function, unicode_literals
from awlsim.common.compat import *

import math
import re

#from awlsim.core.statusword cimport * #@cy

from awlsim.common.cpuspecs import *

from awlsim.core.util import *
from awlsim.core.operators import *
from awlsim.core.parameters import *
from awlsim.core.datatypes import *
from awlsim.core.timers import *

from awlsim.awlcompiler import *


class OpDescriptor(object):
	"Instruction operator descriptor"

	# operator => AwlOperator or AwlIndirectOp
	# fieldCount => Number of consumed tokens
	def __init__(self, operator, fieldCount, stripLeadingChars=0):
		self.operator = operator
		self.fieldCount = fieldCount
		self.stripLeadingChars = stripLeadingChars

	# Make a deep copy
	def dup(self):
		return OpDescriptor(operator = self.operator.dup(),
				    fieldCount = self.fieldCount,
				    stripLeadingChars = self.stripLeadingChars)

class AwlOpTranslator(object):
	"Instruction operator translator"

	__constOperTab_german = {
		"B"	: OpDescriptor(AwlOperator(AwlOperator.UNSPEC, 8,
				       AwlOffset(-1, -1)), 2),
		"W"	: OpDescriptor(AwlOperator(AwlOperator.UNSPEC, 16,
				       AwlOffset(-1, -1)), 2),
		"D"	: OpDescriptor(AwlOperator(AwlOperator.UNSPEC, 32,
				       AwlOffset(-1, -1)), 2),
		"==0"	: OpDescriptor(AwlOperator(AwlOperator.MEM_STW_Z, 1,
				       AwlOffset(0, 0)), 1),
		"<>0"	: OpDescriptor(AwlOperator(AwlOperator.MEM_STW_NZ, 1,
				       AwlOffset(0, 0)), 1),
		">0"	: OpDescriptor(AwlOperator(AwlOperator.MEM_STW_POS, 1,
				       AwlOffset(0, 0)), 1),
		"<0"	: OpDescriptor(AwlOperator(AwlOperator.MEM_STW_NEG, 1,
				       AwlOffset(0, 0)), 1),
		">=0"	: OpDescriptor(AwlOperator(AwlOperator.MEM_STW_POSZ, 1,
				       AwlOffset(0, 0)), 1),
		"<=0"	: OpDescriptor(AwlOperator(AwlOperator.MEM_STW_NEGZ, 1,
				       AwlOffset(0, 0)), 1),
		"OV"	: OpDescriptor(AwlOperator(AwlOperator.MEM_STW, 1,
				       AwlOffset(0, 5)), 1),
		"OS"	: OpDescriptor(AwlOperator(AwlOperator.MEM_STW, 1,
				       AwlOffset(0, 4)), 1),
		"UO"	: OpDescriptor(AwlOperator(AwlOperator.MEM_STW_UO, 1,
				       AwlOffset(0, 0)), 1),
		"BIE"	: OpDescriptor(AwlOperator(AwlOperator.MEM_STW, 1,
				       AwlOffset(0, 8)), 1),
		"E"	: OpDescriptor(AwlOperator(AwlOperator.MEM_E, 1,
				       AwlOffset(-1, -1)), 2),
		"EB"	: OpDescriptor(AwlOperator(AwlOperator.MEM_E, 8,
				       AwlOffset(-1, 0)), 2),
		"EW"	: OpDescriptor(AwlOperator(AwlOperator.MEM_E, 16,
				       AwlOffset(-1, 0)), 2),
		"ED"	: OpDescriptor(AwlOperator(AwlOperator.MEM_E, 32,
				       AwlOffset(-1, 0)), 2),
		"A"	: OpDescriptor(AwlOperator(AwlOperator.MEM_A, 1,
				       AwlOffset(-1, -1)), 2),
		"AB"	: OpDescriptor(AwlOperator(AwlOperator.MEM_A, 8,
				       AwlOffset(-1, 0)), 2),
		"AW"	: OpDescriptor(AwlOperator(AwlOperator.MEM_A, 16,
				       AwlOffset(-1, 0)), 2),
		"AD"	: OpDescriptor(AwlOperator(AwlOperator.MEM_A, 32,
				       AwlOffset(-1, 0)), 2),
		"L"	: OpDescriptor(AwlOperator(AwlOperator.MEM_L, 1,
				       AwlOffset(-1, -1)), 2),
		"LB"	: OpDescriptor(AwlOperator(AwlOperator.MEM_L, 8,
				       AwlOffset(-1, 0)), 2),
		"LW"	: OpDescriptor(AwlOperator(AwlOperator.MEM_L, 16,
				       AwlOffset(-1, 0)), 2),
		"LD"	: OpDescriptor(AwlOperator(AwlOperator.MEM_L, 32,
				       AwlOffset(-1, 0)), 2),
		"M"	: OpDescriptor(AwlOperator(AwlOperator.MEM_M, 1,
				       AwlOffset(-1, -1)), 2),
		"MB"	: OpDescriptor(AwlOperator(AwlOperator.MEM_M, 8,
				       AwlOffset(-1, 0)), 2),
		"MW"	: OpDescriptor(AwlOperator(AwlOperator.MEM_M, 16,
				       AwlOffset(-1, 0)), 2),
		"MD"	: OpDescriptor(AwlOperator(AwlOperator.MEM_M, 32,
				       AwlOffset(-1, 0)), 2),
		"T"	: OpDescriptor(AwlOperator(AwlOperator.MEM_T, 16,
				       AwlOffset(-1, 0)), 2),
		"Z"	: OpDescriptor(AwlOperator(AwlOperator.MEM_Z, 16,
				       AwlOffset(-1, 0)), 2),
		"FC"	: OpDescriptor(AwlOperator(AwlOperator.BLKREF_FC, 16,
				       AwlOffset(-1, 0)), 2),
		"SFC"	: OpDescriptor(AwlOperator(AwlOperator.BLKREF_SFC, 16,
				       AwlOffset(-1, 0)), 2),
		"FB"	: OpDescriptor(AwlOperator(AwlOperator.BLKREF_FB, 16,
				       AwlOffset(-1, 0)), 2),
		"SFB"	: OpDescriptor(AwlOperator(AwlOperator.BLKREF_SFB, 16,
				       AwlOffset(-1, 0)), 2),
		"UDT"	: OpDescriptor(AwlOperator(AwlOperator.BLKREF_UDT, 16,
				       AwlOffset(-1, 0)), 2),
		"DB"	: OpDescriptor(AwlOperator(AwlOperator.BLKREF_DB, 16,
				       AwlOffset(-1, 0)), 2),
		"DI"	: OpDescriptor(AwlOperator(AwlOperator.BLKREF_DI, 16,
				       AwlOffset(-1, 0)), 2),
		"OB"	: OpDescriptor(AwlOperator(AwlOperator.BLKREF_OB, 16,
				       AwlOffset(-1, 0)), 2),
		"VAT"	: OpDescriptor(AwlOperator(AwlOperator.BLKREF_VAT, 16,
				       AwlOffset(-1, 0)), 2),
		"DBX"	: OpDescriptor(AwlOperator(AwlOperator.MEM_DB, 1,
				       AwlOffset(-1, -1)), 2),
		"DBB"	: OpDescriptor(AwlOperator(AwlOperator.MEM_DB, 8,
				       AwlOffset(-1, 0)), 2),
		"DBW"	: OpDescriptor(AwlOperator(AwlOperator.MEM_DB, 16,
				       AwlOffset(-1, 0)), 2),
		"DBD"	: OpDescriptor(AwlOperator(AwlOperator.MEM_DB, 32,
				       AwlOffset(-1, 0)), 2),
		"DIX"	: OpDescriptor(AwlOperator(AwlOperator.MEM_DI, 1,
				       AwlOffset(-1, -1)), 2),
		"DIB"	: OpDescriptor(AwlOperator(AwlOperator.MEM_DI, 8,
				       AwlOffset(-1, 0)), 2),
		"DIW"	: OpDescriptor(AwlOperator(AwlOperator.MEM_DI, 16,
				       AwlOffset(-1, 0)), 2),
		"DID"	: OpDescriptor(AwlOperator(AwlOperator.MEM_DI, 32,
				       AwlOffset(-1, 0)), 2),
		"DBLG"	: OpDescriptor(AwlOperator(AwlOperator.MEM_DBLG, 32,
				       AwlOffset(0, 0)), 1),
		"DBNO"	: OpDescriptor(AwlOperator(AwlOperator.MEM_DBNO, 32,
				       AwlOffset(0, 0)), 1),
		"DILG"	: OpDescriptor(AwlOperator(AwlOperator.MEM_DILG, 32,
				       AwlOffset(0, 0)), 1),
		"DINO"	: OpDescriptor(AwlOperator(AwlOperator.MEM_DINO, 32,
				       AwlOffset(0, 0)), 1),
		"PEB"	: OpDescriptor(AwlOperator(AwlOperator.MEM_PE, 8,
				       AwlOffset(-1, 0)), 2),
		"PEW"	: OpDescriptor(AwlOperator(AwlOperator.MEM_PE, 16,
				       AwlOffset(-1, 0)), 2),
		"PED"	: OpDescriptor(AwlOperator(AwlOperator.MEM_PE, 32,
				       AwlOffset(-1, 0)), 2),
		"PAB"	: OpDescriptor(AwlOperator(AwlOperator.MEM_PA, 8,
				       AwlOffset(-1, 0)), 2),
		"PAW"	: OpDescriptor(AwlOperator(AwlOperator.MEM_PA, 16,
				       AwlOffset(-1, 0)), 2),
		"PAD"	: OpDescriptor(AwlOperator(AwlOperator.MEM_PA, 32,
				       AwlOffset(-1, 0)), 2),
		"STW"	: OpDescriptor(AwlOperator(AwlOperator.MEM_STW, 16,
				       AwlOffset(0, 0)), 1),
		"AR2"	: OpDescriptor(AwlOperator(AwlOperator.MEM_AR2, 32,
				       AwlOffset(2, 0)), 1),
		"__STW"	 : OpDescriptor(AwlOperator(AwlOperator.MEM_STW, 1,
					AwlOffset(0, -1)), 2),
		"__ACCU" : OpDescriptor(AwlOperator(AwlOperator.VIRT_ACCU, 32,
					AwlOffset(-1, 0)), 2),
		"__AR"	 : OpDescriptor(AwlOperator(AwlOperator.VIRT_AR, 32,
					AwlOffset(-1, 0)), 2),
		"__DBR"	 : OpDescriptor(AwlOperator(AwlOperator.VIRT_DBR, 16,
					AwlOffset(-1, 0)), 2),
		"__CNST_PI" : OpDescriptor(AwlOperator(AwlOperator.IMM_REAL, 32,
					   pyFloatToDWord(math.pi)), 1),
		"__CNST_E" : OpDescriptor(AwlOperator(AwlOperator.IMM_REAL, 32,
					  pyFloatToDWord(math.e)), 1),
		"__CNST_PINF" : OpDescriptor(AwlOperator(AwlOperator.IMM_REAL, 32,
					     posInfDWord), 1),
		"__CNST_NINF" : OpDescriptor(AwlOperator(AwlOperator.IMM_REAL, 32,
					     negInfDWord), 1),
		"__CNST_PNAN" : OpDescriptor(AwlOperator(AwlOperator.IMM_REAL, 32,
					     pNaNDWord), 1),
		"__CNST_NNAN" : OpDescriptor(AwlOperator(AwlOperator.IMM_REAL, 32,
					     nNaNDWord), 1),
	}

	__english2german = {
		"I"	: "E",
		"IB"	: "EB",
		"IW"	: "EW",
		"ID"	: "ED",
		"Q"	: "A",
		"QB"	: "AB",
		"QW"	: "AW",
		"QD"	: "AD",
		"C"	: "Z",
		"BR"	: "BIE",
		"PIB"	: "PEB",
		"PIW"	: "PEW",
		"PID"	: "PED",
		"PQB"	: "PAB",
		"PQW"	: "PAW",
		"PQD"	: "PAD",
	}
	__german2english = pivotDict(__english2german)

	# Create a constOperTab for english mnemonics
	__constOperTab_english = {}
	for name, type in dictItems(__constOperTab_german):
		try:
			name = __german2english[name]
		except KeyError:
			pass
		__constOperTab_english[name] = type

	# Mnemonics identifier to constOperTab lookup.
	__mnemonics2constOperTab = {
		S7CPUSpecs.MNEMONICS_DE:	__constOperTab_german,
		S7CPUSpecs.MNEMONICS_EN:	__constOperTab_english,
	}

	def __init__(self, insn=None, mnemonics=None):
		self.insn = insn
		if mnemonics is None and insn is not None:
			mnemonics = insn.getCpu().getSpecs().getMnemonics()
		assert(mnemonics is not None)
		self.mnemonics = mnemonics

	def __translateIndirectAddressing(self, opDesc, rawOps):
		# rawOps starts _after_ the opening bracket '['
		try:
			if rawOps[0].upper() in ("AR1", "AR2"):
				# Register-indirect access:  "L W [AR1, P#0.0]"
				ar = {
					"AR1"	: AwlIndirectOp.AR_1,
					"AR2"	: AwlIndirectOp.AR_2,
				}[rawOps[0].upper()]
				if rawOps[1] != ',':
					raise AwlSimError("Missing comma in register-indirect "
						"addressing operator")
				offsetPtr, fields = AwlDataType.tryParseImmediate_Pointer(rawOps[2:])
				if fields != 1:
					raise AwlSimError("Invalid offset pointer in "
						"register indirect addressing operator")
				if offsetPtr.width != 32:
					raise AwlSimError("Only plain pointers allowed as "
						"indirect addressing offset pointer.")
				if offsetPtr.getArea():
					raise AwlSimError("Area internal pointer not "
						"allowed as indirect addressing offset pointer.")
				if rawOps[3] != ']':
					raise AwlSimError("Missing closing brackets in "
						"register indirect addressing operator")
				offsetOp = AwlOperator(type = AwlOperator.IMM_PTR,
						       width = 32,
						       value = offsetPtr,
						       insn = opDesc.operator.insn)
				try:
					area = AwlIndirectOp.optype2area[opDesc.operator.type]
				except KeyError:
					raise AwlSimError("Invalid memory area type in "
						"register indirect addressing operator")
				indirectOp = AwlIndirectOp(area = area,
							   width = opDesc.operator.width,
							   addressRegister = ar,
							   offsetOper = offsetOp,
							   insn = opDesc.operator.insn)
				fieldCount = 4	# ARx + comma + P# + ]
			else:
				# Indirect access:  "L MW [MD 42]"
				# Find the end of the brackets.
				i, lvl = 0, 1
				for i, tok in enumerate(rawOps):
					if tok == ']':
						lvl -= 1
					elif tok == '[':
						lvl += 1
					if lvl == 0:
						end = i
						break
				else:
					raise AwlSimError("Missing closing brackets in "
						"indirect addressing operator")
				# Translate the offset operator
				offsetOpDesc = self.translateOp(None, rawOps[:end])
				if offsetOpDesc.fieldCount != end:
					raise AwlSimError("Invalid indirect addressing "
						"operator format. AR1/AR2 missing?")
				offsetOp = offsetOpDesc.operator
				if offsetOp.type == AwlOperator.INDIRECT:
					raise AwlSimError("Only direct operators supported "
						"inside of indirect operator brackets.")
				try:
					area = AwlIndirectOp.optype2area[opDesc.operator.type]
				except KeyError:
					raise AwlSimError("Invalid memory area type in "
						"indirect addressing operator")
				if area == AwlIndirectOp.AREA_NONE:
					raise AwlSimError("No memory area code specified in "
						"indirect addressing operator")
				if area in (AwlIndirectOp.EXT_AREA_T,
					    AwlIndirectOp.EXT_AREA_Z,
					    AwlIndirectOp.EXT_AREA_BLKREF_DB,
					    AwlIndirectOp.EXT_AREA_BLKREF_DI,
					    AwlIndirectOp.EXT_AREA_BLKREF_FC,
					    AwlIndirectOp.EXT_AREA_BLKREF_FB):
					expectedOffsetOpWidth = 16
				else:
					expectedOffsetOpWidth = 32
				if offsetOp.type != AwlOperator.NAMED_LOCAL and\
				   offsetOp.width != expectedOffsetOpWidth:
					#TODO: We should also check for NAMED_LOCAL
					raise AwlSimError("Offset operator in "
						"indirect addressing operator has invalid width. "
						"Got %d bit, but expected %d bit." %\
						(offsetOp.width, expectedOffsetOpWidth))
				indirectOp = AwlIndirectOp(area = area,
							   width = opDesc.operator.width,
							   addressRegister = AwlIndirectOp.AR_NONE,
							   offsetOper = offsetOp,
							   insn = opDesc.operator.insn)
				fieldCount = offsetOpDesc.fieldCount + 1  # offsetOperator + ]
		except IndexError:
			raise AwlSimError("Invalid indirect addressing operator")
		# Adjust the operator descriptor
		opDesc.operator = indirectOp
		opDesc.fieldCount += fieldCount

	def __translateAddressOperator(self, opDesc, rawOps):
		if len(rawOps) < 1:
			raise AwlSimError("Missing address operator")
		if rawOps[0] == '[':
			# Indirect addressing
			self.__translateIndirectAddressing(opDesc, rawOps[1:])
			return
		if opDesc.operator.type == AwlOperator.UNSPEC:
			raise AwlSimError("No memory area specified in operator")
		# Direct addressing
		if opDesc.operator.width == 1:
			if opDesc.operator.value.byteOffset == 0 and\
			   opDesc.operator.value.bitOffset == -1:
				try:
					opDesc.operator.value.bitOffset = int(rawOps[0], 10)
				except ValueError as e:
					if opDesc.operator.type == AwlOperator.MEM_STW:
						opDesc.operator.value.bitOffset =\
							S7StatusWord.getBitnrByName(rawOps[0],
										    self.mnemonics)
					else:
						raise AwlSimError("Invalid bit address")
			else:
				assert(opDesc.operator.value.byteOffset == -1 and\
				       opDesc.operator.value.bitOffset == -1)
				offset = rawOps[0].split('.')
				if len(offset) != 2:
					raise AwlSimError("Invalid bit address")
				try:
					opDesc.operator.value.byteOffset = int(offset[0], 10)
					opDesc.operator.value.bitOffset = int(offset[1], 10)
				except ValueError as e:
					raise AwlSimError("Invalid bit address")
		elif opDesc.operator.width == 8:
			assert(opDesc.operator.value.byteOffset == -1 and\
			       opDesc.operator.value.bitOffset == 0)
			try:
				opDesc.operator.value.byteOffset = int(rawOps[0], 10)
			except ValueError as e:
				raise AwlSimError("Invalid byte address")
		elif opDesc.operator.width == 16:
			assert(opDesc.operator.value.byteOffset == -1 and\
			       opDesc.operator.value.bitOffset == 0)
			try:
				opDesc.operator.value.byteOffset = int(rawOps[0], 10)
			except ValueError as e:
				raise AwlSimError("Invalid word address")
		elif opDesc.operator.width == 32:
			assert(opDesc.operator.value.byteOffset == -1 and
			       opDesc.operator.value.bitOffset == 0)
			try:
				opDesc.operator.value.byteOffset = int(rawOps[0], 10)
			except ValueError as e:
				raise AwlSimError("Invalid doubleword address")
		else:
			assert(0)
		if opDesc.operator.type != AwlOperator.MEM_STW and\
		   opDesc.operator.value.bitOffset > 7:
			raise AwlSimError("Invalid bit offset %d. "
				"Biggest possible bit offset is 7." %\
				opDesc.operator.value.bitOffset)

	def __transVarIdents(self, tokens):
		tokens, count = AwlDataIdentChain.expandTokens(tokens)

		# Parse DBx.VARIABLE or "DBname".VARIABLE adressing
		offset = None
		if len(tokens) >= 3 and\
		   tokens[1] == '.':
			if tokens[0].startswith("DB") and\
			   isdecimal(tokens[0][2:]):
				# DBx.VARIABLE
				offset = AwlOffset(None, None)
				offset.dbNumber = int(tokens[0][2:])
			elif tokens[0].startswith('"') and\
			     tokens[0].endswith('"'):
				# "DBname".VARIABLE
				offset = AwlOffset(None, None)
				offset.dbName = tokens[0][1:-1]
			if offset:
				# Parse the variable idents.
				offset.identChain = AwlDataIdentChain.parseTokens(tokens[2:])
		if not offset and\
		   len(tokens) >= 1 and\
		   tokens[0].startswith('#'):
			tokens[0] = tokens[0][1:] # Strip the '#' from the first token
			offset = AwlOffset(None, None)
			offset.identChain = AwlDataIdentChain.parseTokens(tokens)
		if not offset:
			count = 0
		return offset, count

	def __doTrans(self, rawInsn, rawOps):
		assert(len(rawOps) >= 1)
		if rawInsn and rawInsn.block.hasLabel(rawOps[0]):
			# Label reference
			return OpDescriptor(AwlOperator(AwlOperator.LBL_REF, 0,
					    rawOps[0]), 1)
		token0 = rawOps[0].upper()

		# Constant operator (from table)
		operTable = self.__mnemonics2constOperTab[self.mnemonics]
		if token0 in operTable:
			return operTable[token0].dup()
		# Bitwise indirect addressing
		if token0 == '[':
			# This is special case for the "U [AR1,P#0.0]" bitwise addressing.
			# Create a descriptor for the (yet) unspecified bitwise access.
			opDesc = OpDescriptor(AwlOperator(AwlOperator.UNSPEC, 1,
							  AwlOffset(-1, -1)), 1)
			# And hand over to indirect address parsing.
			self.__translateIndirectAddressing(opDesc, rawOps[1:])
			assert(opDesc.operator.type != AwlOperator.UNSPEC)
			return opDesc
		# Local variable
		if token0.startswith('#'):
			offset, count = self.__transVarIdents(rawOps)
			if not offset:
				raise AwlSimError("Failed to parse variable name: %s" %\
					"".join(rawOps))
			return OpDescriptor(AwlOperator(AwlOperator.NAMED_LOCAL, 0,
							offset), count)
		# Pointer to local variable
		if token0.startswith("P##"):
			offset = AwlOffset(None, None)
			# Doesn't support struct or array indexing.
			# Parse it as one identification.
			offset.identChain = AwlDataIdentChain(
				[ AwlDataIdent(rawOps[0][3:]), ]
			)
			return OpDescriptor(AwlOperator(AwlOperator.NAMED_LOCAL_PTR, 0,
							offset), 1)
		# Symbolic name
		if token0.startswith('"') and token0.endswith('"'):
			offset = AwlOffset(None, None)
			offset.identChain = AwlDataIdentChain(
				[ AwlDataIdent(rawOps[0][1:-1],
					       doValidateName = False), ]
			)
			return OpDescriptor(AwlOperator(AwlOperator.SYMBOLIC, 0,
							offset), 1)
		# Immediate boolean
		immediate = AwlDataType.tryParseImmediate_BOOL(rawOps[0])
		if immediate is not None:
			return OpDescriptor(AwlOperator(AwlOperator.IMM, 1,
					                immediate), 1)
		# Immediate integer
		immediate = AwlDataType.tryParseImmediate_INT(rawOps[0])
		if immediate is not None:
			immediate &= 0xFFFF
			return OpDescriptor(AwlOperator(AwlOperator.IMM, 16,
					    immediate), 1)
		# Immediate float
		immediate = AwlDataType.tryParseImmediate_REAL(rawOps[0])
		if immediate is not None:
			return OpDescriptor(AwlOperator(AwlOperator.IMM_REAL, 32,
					    immediate), 1)
		# S5Time immediate
		immediate = AwlDataType.tryParseImmediate_S5T(rawOps[0])
		if immediate is not None:
			return OpDescriptor(AwlOperator(AwlOperator.IMM_S5T, 16,
					    immediate), 1)
		# Time immediate
		immediate = AwlDataType.tryParseImmediate_TIME(rawOps[0])
		if immediate is not None:
			return OpDescriptor(AwlOperator(AwlOperator.IMM_TIME, 32,
					    immediate), 1)
		# TIME_OF_DAY immediate
		immediate = AwlDataType.tryParseImmediate_TOD(rawOps[0])
		if immediate is not None:
			return OpDescriptor(AwlOperator(AwlOperator.IMM_TOD, 32,
					    immediate), 1)
		# DATE immediate
		immediate = AwlDataType.tryParseImmediate_DATE(rawOps[0])
		if immediate is not None:
			return OpDescriptor(AwlOperator(AwlOperator.IMM_DATE, 16,
					    immediate), 1)
		# DATE_AND_TIME immediate
		immediate = AwlDataType.tryParseImmediate_DT(rawOps)
		if immediate is not None:
			return OpDescriptor(AwlOperator(AwlOperator.IMM_DT,
					    len(immediate) * 8,
					    immediate), 5)
		# Pointer immediate
		pointer, fields = AwlDataType.tryParseImmediate_Pointer(rawOps)
		if pointer is not None:
			return OpDescriptor(AwlOperator(AwlOperator.IMM_PTR, pointer.width,
					    pointer), fields)
		# Binary immediate
		immediate = AwlDataType.tryParseImmediate_Bin(rawOps[0])
		if immediate is not None:
			size = 32 if (immediate > 0xFFFF) else 16
			return OpDescriptor(AwlOperator(AwlOperator.IMM, size,
					    immediate), 1)
		# Byte array immediate
		immediate, fields = AwlDataType.tryParseImmediate_ByteArray(rawOps)
		if immediate is not None:
			size = 32 if fields == 9 else 16
			return OpDescriptor(AwlOperator(AwlOperator.IMM, size,
					    immediate), fields)
		# Hex byte immediate
		immediate = AwlDataType.tryParseImmediate_HexByte(rawOps[0])
		if immediate is not None:
			return OpDescriptor(AwlOperator(AwlOperator.IMM, 8,
					    immediate), 1)
		# Hex word immediate
		immediate = AwlDataType.tryParseImmediate_HexWord(rawOps[0])
		if immediate is not None:
			return OpDescriptor(AwlOperator(AwlOperator.IMM, 16,
					    immediate), 1)
		# Hex dword immediate
		immediate = AwlDataType.tryParseImmediate_HexDWord(rawOps[0])
		if immediate is not None:
			return OpDescriptor(AwlOperator(AwlOperator.IMM, 32,
					    immediate), 1)
		# Long integer immediate
		immediate = AwlDataType.tryParseImmediate_DINT(rawOps[0])
		if immediate is not None:
			return OpDescriptor(AwlOperator(AwlOperator.IMM, 32,
					    immediate), 1)
		# BCD word immediate
		immediate = AwlDataType.tryParseImmediate_BCD_word(rawOps[0])
		if immediate is not None:
			return OpDescriptor(AwlOperator(AwlOperator.IMM, 16,
					    immediate), 1)
		# String immediate
		immediate = AwlDataType.tryParseImmediate_STRING(rawOps[0])
		if immediate is not None:
			return OpDescriptor(AwlOperator(AwlOperator.IMM_STR,
					    len(immediate) * 8,
					    immediate), 1)
		# DBx.DBX/B/W/D addressing
		match = re.match(r'^DB(\d+)\.DB([XBWD])$', rawOps[0])
		if match:
			dbNumber = int(match.group(1))
			width = {
				"X"	: 1,
				"B"	: 8,
				"W"	: 16,
				"D"	: 32,
			}[match.group(2)]
			offset = AwlOffset(-1, -1 if (width == 1) else 0)
			offset.dbNumber = dbNumber
			return OpDescriptor(AwlOperator(AwlOperator.MEM_DB, width,
					    offset), 2)

		# Try to parse DBx.VARIABLE or "DBname".VARIABLE adressing
		offset, count = self.__transVarIdents(rawOps)
		if offset:
			return OpDescriptor(AwlOperator(AwlOperator.NAMED_DBVAR, 0,
					    offset), count)

		# Try convenience operators.
		# A convenience operator is one that lacks otherwise required white space.
		# We thus actively support lazy programmers, yay.
		# For example:
		#	= M0.0
		# (Note the missing white space between M and 0.0)
		for name, opDesc in dictItems(operTable):
			if isinstance(opDesc.operator.value, AwlOffset) and\
			   opDesc.operator.value.byteOffset >= 0 and\
			   opDesc.operator.value.bitOffset >= 0:
				# Only for operators with bit/byte addresses.
				continue
			try:
				# Try convenience operator
				if token0.startswith(name) and\
				   token0[len(name)].isdigit():
					opDesc = opDesc.dup()
					opDesc.stripLeadingChars = len(name)
					opDesc.fieldCount -= 1
					return opDesc
			except IndexError:
				pass
		raise AwlSimError("Cannot parse operator: " +\
				str(rawOps[0]))

	def translateOp(self, rawInsn, rawOps):
		"""Translate operator tokens.
		Returns an OpDescriptor().
		"""
		if not rawOps:
			raise AwlSimError("Cannot parse operator: Operator is empty")
		opDesc = self.__doTrans(rawInsn, rawOps)

		if isinstance(opDesc.operator.value, AwlOffset) and\
		   (opDesc.operator.value.byteOffset == -1 or\
		    opDesc.operator.value.bitOffset == -1):
			if opDesc.stripLeadingChars:
				strippedRawOps = rawOps[:]
				strippedRawOps[0] = strippedRawOps[0][opDesc.stripLeadingChars : ]
				self.__translateAddressOperator(opDesc, strippedRawOps)
			else:
				self.__translateAddressOperator(opDesc, rawOps[1:])

		assert(opDesc.operator.type != AwlOperator.UNSPEC)
		opDesc.operator.setExtended(rawOps[0].startswith("__"))
		opDesc.operator.setInsn(self.insn)

		return opDesc

	def __translateParameterList(self, rawInsn, rawOps):
		while rawOps:
			if len(rawOps) < 3:
				raise AwlSimError("Invalid parameter assignment")
			if rawOps[1] != ':=':
				raise AwlSimError("Missing assignment operator (:=) "
					"in parameter assignment")

			# Extract l-value and r-value
			commaIdx = listIndex(rawOps, ',', 2)
			lvalueName = rawOps[0]
			if commaIdx < 0:
				rvalueTokens = rawOps[2:]
			else:
				rvalueTokens = rawOps[2:commaIdx]
			if not rvalueTokens:
				raise AwlSimError("No R-Value in parameter assignment")

			# Translate r-value
			opDesc = self.translateOp(None, rvalueTokens)

			# Create assignment
			param = AwlParamAssign(lvalueName, opDesc.operator)
			if self.insn:
				self.insn.params.append(param)

			rawOps = rawOps[opDesc.fieldCount + 2 : ]
			if rawOps:
				if rawOps[0] == ',':
					rawOps = rawOps[1:]
				else:
					raise AwlSimError("Missing comma in parameter list")

	def translateFromRawInsn(self, rawInsn):
		"""Translate operators from rawInsn and add them to self.insn.
		"""
		rawOps = rawInsn.getOperators()
		while rawOps:
			opDesc = self.translateOp(rawInsn, rawOps)

			if self.insn:
				self.insn.ops.append(opDesc.operator)

			if len(rawOps) > opDesc.fieldCount:
				if rawInsn.name.upper() == "CALL" and\
				   rawOps[opDesc.fieldCount] == '(':
					try:
						endIdx = rawOps.index(')', opDesc.fieldCount)
					except ValueError:
						raise AwlSimError("Missing closing parenthesis")
					# Translate the call parameters
					self.__translateParameterList(rawInsn,
								      rawOps[opDesc.fieldCount + 1: endIdx])
					# Consume all tokens between (and including) parenthesis.
					opDesc.fieldCount = endIdx + 1
					if len(rawOps) > opDesc.fieldCount:
						raise AwlSimError("Trailing character after closing parenthesis")
				elif rawOps[opDesc.fieldCount] == ',':
					opDesc.fieldCount += 1 # Consume comma
					if len(rawOps) <= opDesc.fieldCount:
						raise AwlSimError("Trailing comma")
				else:
					raise AwlSimError("Missing comma in operator list")

			rawOps = rawOps[opDesc.fieldCount : ]

	def __tokenizeString(self, string):
		"""Split a string into operator tokens.
		"""
		tokens, s, inSingleQuote, inDoubleQuote = [], [], False, False
		for c in string:
			if inSingleQuote or inDoubleQuote:
				if (inSingleQuote and c == "'") or\
				   (inDoubleQuote and c == '"'):
					inSingleQuote, inDoubleQuote = False, False
					s.append(c)
					tokens.append("".join(s))
					s = []
				else:
					s.append(c)
			else:
				if c in {'"', "'"}:
					inSingleQuote, inDoubleQuote = (c == "'"), (c == '"')
					if s:
						tokens.append("".join(s))
					s = [c]
				elif c.isspace():
					if s:
						tokens.append("".join(s))
						s = []
				else:
					s.append(c)
		if s:
			tokens.append("".join(s))
		return tokens

	def translateFromString(self, string):
		"""Translate an operator string.
		Returns an OpDescriptor().
		"""
		return self.translateOp(rawInsn=None,
					rawOps=self.__tokenizeString(string))
