#!/usr/bin/env python
# -*- coding: latin-1; -*-
#
# PgWorksheet - PostgreSQL Front End
# http://pgworksheet.projects.postgresql.org/
#
# Copyright © 2004-2008 Henri Michelon & CML http://www.e-cml.org/
#
# 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 (read LICENSE.txt).
#
# 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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
#
# $Id: Syntax.py,v 1.30 2008/03/12 20:26:23 hmichelon Exp $
#
import gtk.gdk
import pango
import pgw
import pgw.Lexical

# built-in data types and extensions
TYPES = [ 'BIGINT', 'INT8', 'BIGSERIAL', 'SERIAL8', 'BIT', 'VARYING',
          'VARBIT', 'BOOL', 'BOOLEAN', 'BOX', 'BYTEA', 'CHARACTER', 'VARCHAR',
          'CHAR', 'CIDR', 'CIRCLE', 'DATE', 'DOUBLE', 'PRECISION',
          'INET', 'INTEGER', 'FLOAT8', 'INT', 'INT4', 'INTERVAL',
          'LINE', 'LSEG', 'MACADDR', 'MONEY', 'NUMERIC', 'DECIMAL',
          'PATH', 'POINT', 'POLYGON', 'REAL', 'FLOAT4', 'SMALLINT',
          'INT2', 'SERIAL', 'SERIAL4', 'TEXT','TIME', 'TIMESTAMP',
          'TIMETZ', 'TIMESTAMPTZ', 'WITHOUT', 'TIME', 'ZONE' 
        ]

# special constants
SPECIALS = [ 'FALSE', 'NULL', 'TRUE', 'UNKNOWN', 'ALL', 'ANY', 'SOME' ]


# named operators, constructs, conditionals and subqueries
OPERATORS2 = [ 'OVERLAPS', 'AND', 'OR', 'NOT', 'BETWEEN', 'IS', 'ISNULL', 
               'NOTNULL', 'CAST', 'LIKE', 'ILIKE', 'SIMILAR',
               'EXISTS', 'IN',
               'CASE', 'WHEN', 'THEN', 'ELSE', 'END',
               'UNION', 'ARRAY',
             ]  

# SQL and PostgreSQL statements
STATEMENTS = [ 'ABORT', 'ALTER', 'ANALYSE', 'BEGIN', 'CHECKPOINT', 'CLOSE', 
               'CLUSTER', 'COMMENT', 'COMMIT', 'COPY', 'CREATE', 'DEALLOCATE', 
               'DECLARE', 'DELETE', 'DROP', 'END', 'EXECUTE', 'EXPLAIN', 
               'FETCH', 'GRANT', 'INSERT', 'LISTEN', 'LOAD', 'LOCK', 'MOVE', 
               'NOTIFY', 'PREPARE', 'REINDEX', 'RELEASE', 'RESET', 'REVOKE', 
               'ROLLBACK', 'SAVEPOINT', 'SELECT', 'SET', 'SHOW', 'START', 
               'TRUNCATE', 'UNLISTEN', 'UPDATE', 'VACUUM' ]

# built-in functions
BUILTINS  = [ 
              # MATH
              'ABS', 'CBTR', 'CEIL', 'CEILING', 'DEGREES', 'EXP', 'FLOOR', 'LN',
              'LOG', 'MOD', 'PI', 'POWER', 'RADIANS', 'RANDOM', 'ROUND', 
              'SETSEED', 'SIGN', 'SQRT', 'TRUNC', 'WIDTH_BUCKET', 'ACOS', 
              'ASIN', 'ATAN', 'ATAN2', 'COS', 'COT', 'SIN', 'TAN',
              # STRING
              'BIT_LENGTH', 'CHAR_LENGTH', 'CONVERT', 'LOWER', 'OCTET_LENGTH',
              'OVERLAY', 'POSITION', 'SUBSTRING', 'TRIM', 'UPPER', 'ASCII',
              'BTRIM', 'CHR', 'DECODE', 'ENCODE', 'INITCAP', 'LENGTH',
              'LPAD', 'LTRIM', 'MD5', 'PG_CLIENT_ENCODING', 'QUOTE_IDENT',
              'QUOTE_LITERAL', 'REPEAT', 'RPAD', 'RTRIM', 'SPLIT_PART',
              'STRPOS', 'TO_ASCII', 'TO_HEX', 'TRANSLATE', 
              # BSTRING
              'GET_BYTE', 'SET_BYTE', 'GET_BIT', 'SET_BIT', 
              # DATE
              'TO_CHAR', 'TO_DATE', 'TO_TIMESTAMP', 'TO_NUMBER',
              'AGE', 'DATE_PART', 'DATE_TRUNC', 'EXTRACT', 'ISFINITE',
              'NOW', 'TIMEOFDAY',
              # GEOMETRIC
              'AREA', 'BOX_INTERSECT', 'CENTER', 'DIAMETER', 'HEIGHT',
              'ISCLOSED', 'ISOPEN', 'LENGTH', 'NPOINTS', 'PCLOSE',
              'POPEN', 'RADIUS', 'WIDTH', 'BOX', 'CIRCLE', 'LSEG',
              'PATH', 'POINT', 'POLYGON', 
              # NETWORK
              'BROADCAST', 'HOST', 'MASKLEN', 'SET_MASKLEN',
              'NETMASK', 'HOSTMASK', 'NETWORK', 'TEXT', 'ABBREV',
              'FAMILY', 'TRUNC',
              # SEQUENCES
              'NEXTVAL', 'CURRVAL',
              # CONDITIONAL
              'COALESCE', 'NULLIF',
              # ARRAY
              'ARRAY_CAT', 'ARRAY_APPEND', 'ARRAY_PREPEND', 'ARRAY_DIMS',
              'ARRAY_LOWER', 'ARRAY_UPPER', 'ARRAY_TO_STRING',
              'STRING_TO_ARRAY'
              # AGGREGATE
              'AVG', 'BIT_AND', 'BIT_OR', 'BOOL_AND', 'BOOL_OR',
              'COUNT', 'EVERY', 'MAX', 'MIN', 'STDDEV', 'SUM',
              'VARIANCE'
              # SETS
              'GENERATE_SERIES',
              # SYSTEM
              'CURRENT_DATABASE', 'CURRENT_SCHEMA',
              'INET_CLIENT_ADDR', 'INET_CLIENT_PORT',
              'INET_SERVER_ADDR', 'INET_SERVER_PORT', 'VERSION',
              'HAS_TABLE_PRIVILEGE', 'HAS_DATABASE_PRIVILEGE',
              'HAS_FUNCTION_PRIVILEGE', 'HAS_LANGUAGE_PRIVILEGE',
              'HAS_SCHEMA_PRIVILEGE', 'HAS_TABLESPACE_PRIVILEGE'
              'PG_TABLE_IS_VISIBLE', 'PG_TYPE_IS_VISIBLE',
              'PG_FUNCTION_IS_VISIBLE', 'PG_OPERATOR_IS_VISIBLE',
              'PG_OPCLASS_IS_VISIBLE', 'PG_CONVERSION_IS_VISIBLE',
              'FORMAT_TYPE', 'PG_GET_VIEWDEF', 'PG_GET_RULEGET',
              'PG_GET_INDEXDEF', 'PG_GET_TRIGGERDEF',
              'PG_GET_CONSTRAINTDEF', 'PG_GET_EXPR', 'PG_GET_USERBYID',
              'PG_GET_SERIAL_SEQUENCE', 'PG_TABLESPACE_DATABASES',
              'OBJ_DESCRIPITION',
              # SYSTEM ADMIN
              'CURRENT_SETTINGS', 'SET_CONFIG', 'PG_CANCEL_BACKEND',
              'PG_START_BACKUP', 'PG_STOP_BACKUP',

            ]

# built-in functions that can be used without ()
# or conditional expressions
BUILTINS2 = [ # DATE
              'CURRENT_DATE', 'CURRENT_TIME', 'CURRENT_TIMESTAMP',
              'LOCALTIME', 'LOCALTIMESTAMP',
              # SYSTEM
              'CURRENT_USER', 'SESSION_USER', 'USER',
            ]


"""
# PL/PgSQL keywords
PLPGSQL = [ # structure
            'DECLARE', 'BEGIN', 'END',
            # declarations
            'RECORD', 'DEFAULT', 'CONSTANT', 'ALIAS', 'FOR', 'TYPE', 'ROWTYPE',
            'RENAME', 'TO',
            # Statements
            'FOUND', 'PERFORM', 'GET', 'DIAGNOSTICS', 
            # Control Structures
            'RETURN', 'NEXT', 'IF', 'THEN', 'ELSE', 'END', 'ELSIF', 'LOOP',
            'EXIT', 'WHILE', 'REVERSE', 'IN', 'EXCEPTION', 'WHEN',
            # Cursors
            'CURSOR', 'OPEN', 'EXECUTE', 'FETCH', 'INTO', 'CLOSE',
            # Errors
            'RAISE', 'DEBUG', 'LOG', 'INFO', 'NOTICE', 'WARNING', 'EXCEPTION',
            # Trigger Procedures
            'NEW', 'OLD', 'TG_NAME', 'TG_WHEN', 'TG_LEVEL', 'TG_OP',
            'TG_RELID', 'TG_RELNAME', 'TG_NARGS', 'TG_ARGV',
          ]  
"""

# keywords for each statement
KEYWORDS = { 'ABORT'    : [ 'WORK', 'TRANSACTION' ],
             'ALTER'    : { 'AGGREGATE'   : [ [ 'RENAME', 'TO' ], 
                                              [ 'OWNER', 'TO' ]
                                            ],
                            'CONVERSION'  : [ [ 'RENAME', 'TO' ],
                                              [ 'OWNER', 'TO' ]
                                            ],
                            'DATABASE'    : [ 'SET', 'TO', 'DEFAULT',
                                              'RESET', [ 'RENAME', 'TO' ],
                                              [ 'OWNER', 'TO' ]
                                            ],
                            'DOMAIN'      : [ [ 'SET', 'DEFAULT' ],
                                              [ 'DROP', 'DEFAULT' ],
                                              'ADD',
                                              [ 'DROP', 'CONSTRAINT' ],
                                              'RESTRICT', 'CASCADE',
                                              [ 'OWNER', 'TO' ]
                                            ],
                            'FUNCTION'    : [ [ 'RENAME', 'TO' ],
                                              [ 'OWNER', 'TO' ]
                                            ],
                            'GROUP'       : [ [ 'ADD', 'USER' ],
                                              [ 'DROP', 'USER' ],
                                              [ 'RENAME', 'TO' ]
                                            ],
                            'INDEX'       : [ [ 'RENAME', 'TO' ],
                                              [ 'OWNER', 'TO' ],
                                              [ 'SET', 'TABLESPACE' ]
                                            ],
                            'LANGUAGE'    : [ [ 'RENAME', 'TO' ] ],
                            'OPERATOR'    : [ 'NONE', [ 'OWNER', 'TO' ], 
                                              'CLASS', 'USING'
                                            ],
                            'SCHEMA'      : [ [ 'RENAME', 'TO' ],
                                              [ 'OWNER', 'TO' ]
                                            ],
                            'SEQUENCE'    : [ [ 'INCREMENT', 'BY' ], 
                                              'MINVALUE', 'MAXVALUE',
                                              [ 'RESTART', 'WITH' ], 'CACHE',
                                              'NO', 'CYCLE'
                                            ],
                            'TABLE'       : [ 'ONLY', [ 'RENAME', 'COLUMN' ],
                                               [ 'RENAME', 'TO' ], 'TO',
                                               [ 'ADD', 'COLUMN' ],
                                               [ 'DROP', 'COLUMN' ], 'RESTRICT',
                                               'CASCADE', 'TYPE', 'USING',
                                               [ 'ALTER', 'COLUMN' ], 
                                               [ 'SET', 'DEFAULT' ],
                                               [ 'DROP', 'DEFAULT' ],
                                               'SET', 'DROP',
                                               [ 'SET', 'STATISTICS' ],
                                               [ 'SET', 'STORAGE' ], 'PLAIN', 
                                               'EXTERNAL', 'EXTERNAL', 'MAIN',
                                               [ 'DROP', 'CONSTRAINT' ],
                                               [ 'CLUSTER', 'ON' ],
                                               [ 'SET', 'WITHOUT', 'CLUSTER' ],
                                               [ 'SET', 'WITHOUT', 'OIDS' ],
                                               [ 'OWNER', 'TO' ],
                                               [ 'SET', 'TABLESPACE' ],
                                               'CHECK', 
                                               [ 'FOREIGN', 'KEY' ], 'CASCADE',
                                               [ 'PRIMARY', 'KEY' ],
                                               [ 'ON', 'DELETE' ],
                                               [ 'ON', 'UPDATE' ], 'REFERENCES',
                                             ],
                            'TABLESPACE'  : [ [ 'RENAME', 'TO' ],
                                              [ 'OWNER', 'TO' ]
                                            ],
                            'TRIGGER'     : [ 'ON', [ 'RENAME', 'TO' ] ],
                            'TYPE'        : [ [ 'OWNER', 'TO' ] ],
                            'USER'        : [ 'WITH', 'CREATEDB', 'NOCREATEDB',
                                              'CREATEUSER', 'NOCREATEUSER',
                                              'ENCRYPTED', 'UNENCRYPTED',
                                              'PASSWORD', [ 'VALID', 'UNTIL' ],
                                              [ 'RENAME', 'TO' ], 'SET',
                                              'TO', 'DEFAULT', 'RESET'
                                            ],
                          },
             'ANALYZE'  : [ 'VERBOSE' ],
             'BEGIN'    : [ 'WORK', 'TRANSACTION', [ 'ISOLATION', 'LEVEL' ],
                            'SERIALIZABLE', [ 'REPEATABLE', 'READ' ],
                            [ 'READ', 'COMMITTED' ], [ 'READ', 'UNCOMMITTED' ],
                            [ 'READ', 'WRITE' ], [ 'READ', 'ONLY' ]
                          ],
             'CHECKPOINT' : [],
             'CLOSE'    : [],
             'CLUSTER'  : [ 'ON' ],
             'COMMENT'  : { 'ON' : [ 'TABLE', 'COLUMN', 'AGGREGATE', 'CAST',
                                     'AS', 'CONSTRAINT', 'ON', 'CONVERSION',
                                     'DATABASE', 'DOMAIN', 'FUNCTION', 'INDEX',
                                     [ 'LARGE', 'OBJECT' ], 'OPERATOR',
                                     [ 'OPERATOR', 'CLASS' ], 'USING', 
                                     'PROCEDURAL', 'LANGUAGE', 'RULE',
                                     'SCHEMA', 'SEQUENCE', 'TRIGGER',
                                     'TYPE', 'VIEW', 'IS'
                                   ]
                          },         
             'COMMIT'   : [ 'WORK', 'TRANSACTION' ],
             'COPY'     : [ 'FROM', 'STDIN', 'WITH', 'BINARY', 'OIDS',
                            'DELIMITER', 'CSV', 'AS', 'ESCAPE'
                            'FORCE', 'TO', 'STDOUT', 'QUOTE'
                          ],  
             'CREATE'   : { 'AGGREGATE'   : [ 'BASETYPE', 'SFUNC', 'STYPE',
                                              'FINALFUNC', 'INITCOND' ],
                            'CAST'        : [ [ 'WITH', 'FUNCTION' ],
                                              [ 'AS', 'ASSIGNMENT' ],
                                              [ 'AS', 'IMPLICIT' ],
                                              [ 'WITHOUT', 'FUNCTION']
                                            ],
                            'CONSTRAINT'  : [ 'TRIGGER', 'AFTER', 'ON',
                                              [ 'FOR', 'EACH', 'ROW', 'EXECUTE'
                                                'PROCEDURE' ]
                                            ],
                            'CONVERSION'  : [ 'FOR', 'TO', 'FROM' ],
                            'DEFAULT'     : [ 'CONVERSION', 'FOR', 
                                              'TO', 'FROM' ],
                            'DATABASE'    : [ 'WITH', 'OWNER', 'TEMPLATE',
                                              'ENCODING', 'TABLESPACE', 
                                              'DEFAULT' ],
                            'DOMAIN'      : [ 'AS', 'DEFAULT', 'CONSTRAINT',
                                              'CHECK'
                                            ],
                            'OR'          : [ 'REPLACE', 'FUNCTION',
                                              'RETURNS', 'LANGUAGE', 'AS',
                                              'IMMUTABLE', 'STABLE',
                                              'VOLATILE', [ 'CALLED', 'ON',
                                              'NULL', 'INPUT' ], [ 'RETURNS',
                                              'NULL', 'ON', 'NULL', 'INPUT' ],
                                              'STRICT', 'EXTERNAL',
                                              [ 'SECURITY', 'INVOKER' ],
                                              [ 'SECURITY', 'DEFINER' ], 'WITH',
                                              # XXX: for RULE
                                              'RULE',
                                              'AS', 'ON', 'TO', 'WHERE',
                                              'DO', 'ALSO', 'INSTEAD', 
                                              'NOTHING',
                                              'VIEW'
                                            ],  
                            'FUNCTION'    : [ 'RETURNS', 'LANGUAGE', 'AS',
                                              'IMMUTABLE', 'STABLE',
                                              'VOLATILE', [ 'CALLED', 'ON',
                                              'NULL', 'INPUT' ], [ 'RETURNS',
                                              'NULL', 'ON', 'NULL', 'INPUT' ],
                                              'STRICT', 'EXTERNAL',
                                              [ 'SECURITY', 'INVOKER' ],
                                              [ 'SECURITY', 'DEFINER' ], 'WITH'
                                            ],
                            'GROUP'       : [ 'WITH', 'SYSID', 'USER' ],
                            'UNIQUE'      : [ 'ON', 'USING', 'TABLESPACE',
                                              'WHERE', 'INDEX' ],
                            'INDEX'       : [ 'ON', 'USING', 'TABLESPACE',
                                              'WHERE', 'RTREE', 'HASH' ],
                            'LANGUAGE'    : [ 'HANDLER', 'VALIDATOR' ],
                            'PROCEDURAL'  : [ 'HANDLER', 'VALIDATOR', 
                                              'LANGUAGE' ],
                            'TRUSTED'     : [ 'HANDLER', 'VALIDATOR', 
                                              'LANGUAGE' ],
                            'OPERATOR'    : [ 'PROCEDURE', 'LEFTARG', 
                                              'RIGHTARG', 'COMMUTATOR',
                                              'NEGATOR', 'RESTRICT',
                                              'JOIN', 'HASHES', 'MERGES',
                                              'SORT1', 'SORT2', 'LTCMP',
                                              'GTCMP',
                                              'CLASS', 'DEFAULT',
                                              [ 'FOR', 'TYPE' ], 'USING',
                                              'AS', 'OPERATOR', 'RECHECK',
                                              'FUNCTION', 'STORAGE' ],
                            'RULE'        : [ 'AS', 'ON', 'TO', 'WHERE',
                                              'DO', 'ALSO', 'INSTEAD', 
                                              'NOTHING' ],
                            'SCHEMA'      : [ 'AUTHORIZATION' ],
                            'SEQUENCE'    : [ 'INCREMENT', 'BY', 'MINVALUE',
                                              'NO', 'MAXVALUE', [ 'START', 
                                              'WITH' ], 'CACHE', 'CYCLE' ],
                            'TEMPORARY'    : [ 'SEQUENCE',
                                               'INCREMENT', 'BY', 'MINVALUE',
                                              'NO', 'MAXVALUE', [ 'START', 
                                              'WITH' ], 'CACHE', 'CYCLE',
                                              # CREATE TABLE
                                              'TABLE',
                                              'DEFAULT', 'INCLUDING',
                                              'EXCLUDING', 'DEFAULTS', 
                                              'INHERITS', 'WITH',
                                              'WITHOUT', 'OIDS',
                                              [ 'ON', 'COMMIT' ], 
                                              [ 'PRESERVE', 'ROWS' ],
                                              [ 'DELETE', 'ROWS' ], 'DROP',
                                              'TABLESPACE', 'CONSTRAINT',
                                              'UNIQUE',
                                              [ 'USING', 'INDEX', 'TABLESPACE'],
                                              [ 'PRIMARY', 'KEY' ], 'CHECK',
                                              'REFERENCES', [ 'MATCH', 'FULL' ],
                                              [ 'MATCH', 'PARTIAL' ], 
                                              [ 'MATCH', 'SIMPLE' ],
                                              [ 'ON', 'DELETE' ], 
                                              [ 'ON', 'UPDATE' ], 'DEFERRABLE', 
                                              [ 'INITIALLY', 'DEFERRED' ],
                                              [ 'INITIALLY', 'IMMEDIATE' ],
                                              [ 'FOREIGN', 'KEY' ],
                                               'CHECK', 
                                            ],  
                            'TEMP'        : [ 'SEQUENCE',
                                               'INCREMENT', 'BY', 'MINVALUE',
                                              'NO', 'MAXVALUE', [ 'START', 
                                              'WITH' ], 'CACHE', 'CYCLE',
                                              # CREATE TABLE
                                              'TABLE',
                                              'DEFAULT', 'INCLUDING',
                                              'EXCLUDING', 'DEFAULTS', 
                                              'INHERITS', 'WITH', 'OIDS',
                                              'WITHOUT', 
                                              [ 'ON', 'COMMIT' ], 
                                              [ 'PRESERVE', 'ROWS' ],
                                              [ 'DELETE', 'ROWS' ], 'DROP',
                                              'TABLESPACE', 'CONSTRAINT',
                                              'UNIQUE',
                                              [ 'USING', 'INDEX', 'TABLESPACE'],
                                              [ 'PRIMARY', 'KEY' ], 'CHECK',
                                              'REFERENCES', [ 'MATCH', 'FULL' ],
                                              [ 'MATCH', 'PARTIAL' ], 
                                              [ 'MATCH', 'SIMPLE' ],
                                              [ 'ON', 'DELETE' ], 
                                              [ 'ON', 'UPDATE' ], 'DEFERRABLE', 
                                              [ 'INITIALLY', 'DEFERRED' ],
                                              [ 'INITIALLY', 'IMMEDIATE' ],
                                              [ 'FOREIGN', 'KEY' ],
                                               'CHECK', 
                                            ],  
                            'LOCAL'       : [ 'TABLE',
                                              'DEFAULT', 'INCLUDING',
                                              'EXCLUDING', 'DEFAULTS', 
                                              'INHERITS', 'WITH',
                                              'WITHOUT', 'OIDS',
                                              [ 'ON', 'COMMIT' ], 
                                              [ 'PRESERVE', 'ROWS' ],
                                              [ 'DELETE', 'ROWS' ], 'DROP',
                                              'TABLESPACE', 'CONSTRAINT',
                                              'UNIQUE',
                                              [ 'USING', 'INDEX', 'TABLESPACE'],
                                              [ 'PRIMARY', 'KEY' ], 'CHECK',
                                              'REFERENCES', [ 'MATCH', 'FULL' ],
                                              [ 'MATCH', 'PARTIAL' ], 
                                              [ 'MATCH', 'SIMPLE' ],
                                              [ 'ON', 'DELETE' ], 
                                              [ 'ON', 'UPDATE' ], 'DEFERRABLE', 
                                              [ 'INITIALLY', 'DEFERRED' ],
                                              [ 'INITIALLY', 'IMMEDIATE' ],
                                              [ 'FOREIGN', 'KEY' ],
                                               'CHECK',
                                            ],  
                            'GLOBAL'      : [ 'TABLE',
                                              'DEFAULT', 'INCLUDING',
                                              'EXCLUDING', 'DEFAULTS', 
                                              'INHERITS', 'WITH', 'OIDS',
                                              'WITHOUT',
                                              [ 'ON', 'COMMIT' ], 
                                              [ 'PRESERVE', 'ROWS' ],
                                              [ 'DELETE', 'ROWS' ], 'DROP',
                                              'TABLESPACE', 'CONSTRAINT',
                                              'UNIQUE',
                                              [ 'USING', 'INDEX', 'TABLESPACE'],
                                              [ 'PRIMARY', 'KEY' ], 'CHECK',
                                              'REFERENCES', [ 'MATCH', 'FULL' ],
                                              [ 'MATCH', 'PARTIAL' ], 
                                              [ 'MATCH', 'SIMPLE' ],
                                              [ 'ON', 'DELETE' ], 
                                              [ 'ON', 'UPDATE' ], 'DEFERRABLE', 
                                              [ 'INITIALLY', 'DEFERRED' ],
                                              [ 'INITIALLY', 'IMMEDIATE' ],
                                              [ 'FOREIGN', 'KEY' ],
                                               'CHECK',
                                            ],  
                            'TABLE'       : [ 'DEFAULT', 'INCLUDING',
                                              'EXCLUDING', 'DEFAULTS', 
                                              'INHERITS', 'WITH', 'OIDS',
                                              'WITHOUT',
                                              [ 'ON', 'COMMIT' ], 
                                              [ 'PRESERVE', 'ROWS' ],
                                              [ 'DELETE', 'ROWS' ], 'DROP',
                                              'TABLESPACE', 'CONSTRAINT',
                                              'UNIQUE',
                                              [ 'USING', 'INDEX', 'TABLESPACE'],
                                              [ 'PRIMARY', 'KEY' ], 'CHECK',
                                              'REFERENCES', [ 'MATCH', 'FULL' ],
                                              [ 'MATCH', 'PARTIAL' ], 
                                              [ 'MATCH', 'SIMPLE' ],
                                              [ 'ON', 'DELETE' ], 
                                              [ 'ON', 'UPDATE' ], 'DEFERRABLE', 
                                              'INITIALLY', 'DEFERRED',
                                              'IMMEDIATE', 'RESTRICT',
                                              [ 'FOREIGN', 'KEY' ], 'CASCADE'
                                               'CHECK',
                                            ],
                            'TABLESPACE'  : [ 'OWNER', 'LOCATION' ],
                            'TRIGGER'     : [ 'BEFORE', 'AFTER', 'OR', 'ON',
                                              'FOR', 'EACH', 'ROW', 'STATEMENT',
                                              [ 'EXECUTE', 'PROCEDURE' ]
                                            ],  
                            'TYPE'        : [ 'AS', 'INPUT', 'OUTPUT', 
                                              'RECEIVE', 'SEND', 'ANALYZE',
                                              'INTERNALLENGTH', 'VARIABLE',
                                              'PASSEDBYVALUE', 'ALIGNMENT',
                                              'STORAGE', 'DEFAULT',
                                              'ELEMENT', 'DELIMITER' ],  
                            'USER'        : [ 'WITH', 'SYSID', 'CREATEDB',
                                              'NOCREATEDB', 'CREATEUSER',
                                              'NOCREATEUSER', [ 'IN', 'GROUP' ],
                                              'ENCRYPTED', 'UNENCRYPTED',
                                              'PASSWORD', [ 'VALID', 'UNTIL' ]
                                            ],  
                            'VIEW'        : [ 'AS' ,
                                              # select
                                              'SELECT', 'DISTINCT', 'ON',
                                              'AS', 'FROM', 'WHERE', 
                                              [ 'GROUP', 'BY' ], 'HAVING', 
                                              'UNION', 'INTERSECT', 'EXCEPT', 
                                              [ 'ORDER', 'BY' ], 'ASC', 
                                              'DESC', 'USING', 'LIMIT', 
                                              'OFFSET', [ 'FOR', 'UPDATE', 
                                              'OF' ], 'ONLY', 'NATURAL', 
                                              'USING', 'INNER', 'LEFT', 'OUTER',
                                              'RIGHT', 'FULL', 'JOIN', 'CROSS',
                                              'INTO' ],
                          },
             'DEALLOCATE': [ 'PREPARE' ],
             'DECLARE'  : [ 'BINARY', 'INSENSITIVE', 'NO', 'SCROLL',
                            'CURSOR', 'WITH', 'WITHOUT', 'HOLD', 'FOR',
                            [ 'READ', 'ONLY' ], 'UPDATE', 'OF' ],
             'DELETE'   : { 'FROM'        : [ 'ONLY', 'WHERE' ] },
             'DROP'     : { 'AGGREGATE'   : [ 'CASCADE', 'RESTRICT' ],
                            'CAST'        : [ 'AS', 'CASCADE', 'RESTRICT' ],
                            'CONVERSION'  : [ 'CASCADE', 'RESTRICT' ],
                            'DATABASE'    : [],
                            'DOMAIN'      : [ 'CASCADE', 'RESTRICT' ],
                            'FUNCTION'    : [ 'CASCADE', 'RESTRICT' ],
                            'GROUP'       : [],
                            'INDEX'       : [ 'CASCADE', 'RESTRICT' ],
                            'LANGUAGE'    : [ 'CASCADE', 'RESTRICT' ],
                            'PROCEDURAL'  : ['LANGUAGE', 'CASCADE', 'RESTRICT'],
                            'OPERATOR'    : [ 'NONE', 'CASCADE', 'RESTRICT',
                                              'CLASS', 'USING' ],
                            'RULE'        : [ 'ON', 'CASCADE', 'RESTRICT' ],
                            'SCHEMA'      : [ 'CASCADE', 'RESTRICT' ],
                            'SEQUENCE'    : [ 'CASCADE', 'RESTRICT' ],
                            'TABLE'       : [ 'CASCADE', 'RESTRICT' ],
                            'TABLESPACE'  : [],
                            'TRIGGER'     : [ 'ON', 'CASCADE', 'RESTRICT' ],
                            'TYPE'        : [ 'CASCADE', 'RESTRICT' ],
                            'USER'        : [],
                            'VIEW'        : [ 'CASCADE', 'RESTRICT' ],
                          },
             'END'      : [ 'WORK', 'TRANSACTION' ],
             'EXECUTE'  : [],
             'EXPLAIN'  : [ 'ANALYZE', 'VERBOSE' ],
             'FETCH'    : [ 'FROM', 'IN', 'NEXT', 'PRIOR', 'FIRST', 'LAST',
                            'ABSOLUTE', 'RELATIVE', 'FORWARD',
                            'BACKWARD' ],
             'GRANT'    : [ 'SELECT', 'INSERT', 'UPDATE', 'DELETE', 'RULE',
                            'REFERENCES', 'TRIGGER', 'PRIVILEGES',
                            'ON', 'TABLE', 'TO', 'GROUP', 'PUBLIC',
                            [ 'WITH', 'GRANT', 'OPTION' ],
                            'CREATE', 'TEMPORARY', 'TEMP', 'PRIVILEGES',
                            'DATABASE', 'EXECUTE', 'FUNCTION', 'USAGE',
                            'LANGUAGE', 'SCHEMA', 'TABLESPACE' ],
             'INSERT'   : { 'INTO' : [ 'DEFAULT', 'VALUES',
                                       'SELECT', 'DISTINCT', 'ON', 'AS',
                                       'FROM', 'WHERE', [ 'GROUP', 'BY' ], 
                                       'HAVING', 'UNION', 'INTERSECT', 'EXCEPT',
                                       [ 'ORDER', 'BY' ],
                                       'ASC', 'DESC', 'USING', 'LIMIT',
                                       'OFFSET', [ 'FOR', 'UPDATE', 'OF' ], 
                                       'ONLY', 'NATURAL', 'USING', 'INNER', 
                                       'LEFT', 'OUTER', 'RIGHT', 'FULL', 
                                       'JOIN', 'CROSS', 'INTO',
                                     ]
                          },           
             'LISTEN'   : [],
             'LOAD'     : [],
             'LOCK'     : [ 'TABLE', 'IN', 'MODE', 'NOWAIT',
                            [ 'ACCESS', 'SHARE' ], [ 'ROW', 'SHARE' ],
                            [ 'ROW', 'EXCLUSIVE' ], 
                            [ 'SHARE', 'UPDATE', 'EXCLUSIVE' ],
                            'SHARE', [ 'SHARE', 'ROW', 'EXCLUSIVE' ],
                            'EXCLUSIVE', [ 'ACCESS', 'EXCLUSIVE' ] ],
             'MOVE'     : [ 'FROM', 'IN' ],
             'NOTIFY'   : [],
             'PREPARE'  : [ 'AS' ],
             'REINDEX'  : [ 'DATABASE', 'TABLE', 'TABLE', 'FORCE' ],
             'RELEASE'  : [ 'SAVEPOINT' ],
             'RESET'    : [ ],
             'REVOKE'   : [ [ 'GRANT', 'OPTION', 'FOR' ], 'SELECT', 'INSERT',
                            'UPDATE', 'DELETE', 'RULE', 'REFERENCES',
                            'TRIGGER', 'PRIVILEGES', 'ON', 'TABLE',
                            'FROM', 'GROUP', 'PUBLIC', 'CASCADE', 'RESTRICT',
                            'CREATE', 'TEMPORARY', 'TEMP', 'EXECUTE',
                            'USAGE', 'SCHEMA', 'LANGUAGE', 'TABLESPACE' ],
             'ROLLBACK' : [ 'WORK', 'TRANSACTION', 'TO', 'SAVEPOINT' ],
             'SAVEPOINT': [],

             'SELECT'   : [ 'SELECT', 'INTO', 'DISTINCT', 'ON', 'AS', 'FROM',
                            'WHERE', [ 'GROUP', 'BY' ], 'HAVING', 'UNION',
                            'INTERSECT', 'EXCEPT', [ 'ORDER', 'BY' ],
                            'ASC', 'DESC', 'USING', 'LIMIT', 'OFFSET',
                            [ 'FOR', 'UPDATE', 'OF' ], 'ONLY', 'NATURAL',
                            'USING', 'INNER', 'LEFT', 'OUTER', 'RIGHT', 'FULL',
                            'JOIN', 'CROSS', 'INTO' ],
             'SET'      : [ 'SESSION', 'LOCAL', 'TO', 'DEFAULT', 'LOCAL',
                            [ 'TIME', 'ZONE' ], 'CONSTRAINTS', 
                            'DEFERRED', 'IMMEDIATE', 'AUTHORIZATION',
                            'TRANSACTION', 'CHARACTERISTICS',
                            [ 'ISOLATION', 'LEVEL' ], 'SERIALIZABLE',
                            [ 'REPEATABLE', 'READ' ], [ 'READ', 'COMMITTED' ],
                            [ 'READ', 'UNCOMMITTED' ], [ 'READ', 'WRITE' ],
                            [ 'READ', 'ONLY' ]
                          ],
             'SHOW'     : [ ],
             'START'    : { 'TRANSACTION' : [ [ 'ISOLATION', 'LEVEL' ], 
                                              'SERIALIZABLE', 
                                              [ 'REPEATABLE', 'READ' ], 
                                              [ 'READ', 'COMMITTED' ], 
                                              [ 'READ', 'UNCOMMITTED' ], 
                                              [ 'READ', 'WRITE' ], 
                                              [ 'READ', 'ONLY' ] 
                                            ],
                          },
             'TRUNCATE' : [ 'TABLE' ],
             'UNLISTEN' : [],
             'UPDATE'   : [ 'ONLY', 'SET', 'DEFAULT', 'FROM', 'WHERE' ],
             'VACUUM'   : [ 'FULL', 'FREEZE', 'VERBOSE', 'ANALYSE' ]
           }
      

class Syntax:
  """Syntax highlight"""

  def __init__(self, buffer):
    self.lexical = pgw.Lexical.Lexical()
    self.buffer = buffer
    self.last = None
    # default colors
    self.tag = {}
    self.tag['font'] = self.buffer.create_tag("font")
    if (pgw.mswindows()):
      self.tag['font'].set_property('font', 'Courier New 10')
    else:
      self.tag['font'].set_property('family', 'monospace')
    self.tag['function'] = self.buffer.create_tag("function")
    self.tag['function'].set_property('foreground', '#009999')
    self.tag['dollarquote'] = self.buffer.create_tag("dollarquote")
    self.tag['dollarquote'].set_property('foreground', '#000000')
    self.tag['identifier'] = self.buffer.create_tag("identifier")
    self.tag['identifier'].set_property('foreground', '#000000')
    self.tag['keyword'] = self.buffer.create_tag("keyword")
    self.tag['keyword'].set_property('foreground', '#0000FF')
    self.tag['type'] = self.buffer.create_tag("type")
    self.tag['type'].set_property('foreground', '#009900')
    self.tag['string'] = self.buffer.create_tag("string")
    self.tag['string'].set_property('foreground', '#F700BF')
    self.tag['numeric_constant'] = self.buffer.create_tag("numeric_constant")
    self.tag['numeric_constant'].set_property('foreground', '#c53838')
    self.tag['special'] = self.buffer.create_tag("special")
    self.tag['special'].set_property('foreground', '#c53838')
    self.tag['comment'] = self.buffer.create_tag("comment")
    self.tag['comment'].set_property('foreground', '#999999')
    self.tag['comment2'] = self.buffer.create_tag("comment2")
    self.tag['comment2'].set_property('style', pango.STYLE_ITALIC)
    self.tag['operator'] = self.buffer.create_tag("operator")
    self.tag['operator'].set_property('foreground', '#555555')
    self.tag['psql'] = self.buffer.create_tag("psql")
    self.tag['psql'].set_property('background', '#d0d4df')


  def start_of_prev_statement(self, iter):
    """Find the first character of a statement"""
    if (self.last is not None):
      self.found = None
      for token in self.last:
        if (token.start_iter.compare(iter) >= 0):
          if (self.found is None):
            break
          return self.found.start_iter
        if (token.value == ';'):
          self.found = token
      if (self.found):
        return self.found.start_iter
    return iter.get_buffer().get_start_iter()


  def start_of_next_statement(self, iter):
    """Find the last character of a statement"""
    if (self.last is not None):
      self.found = None
      for token in reversed(self.last):
        if (token.end_iter.compare(iter) <= 0):
          if (self.found is None):
            break
          return self.found.end_iter
        if (token.value == ';'):
          self.found = token
      if (self.found):
        return self.found.end_iter
    return iter.get_buffer().get_end_iter()


  def text_inserted(self, buffer, iter, text, length):
    """Called by Gtk when text is inserted in the buffer:
      prepare 'start' and 'end' for text_changed()"""
    self.start = self.start_of_prev_statement(iter.copy()).get_offset()
    self.end = self.start_of_next_statement(iter.copy()).get_offset() + length

    
  def text_deleted(self, buffer, start, end):
    """Called by Gtk when text is deleted from the buffer:
      prepare 'start' and 'end' for text_changed()"""
    self.start = self.start_of_prev_statement(start.copy()).get_offset()
    self.end = self.start_of_next_statement(end.copy()).get_offset()
    

  def text_changed(self, buffer):
    """Called by Gtk when (after) text is deleted or inserted
    in the buffer. Uses 'start' and 'end' prepared in text_inserted
    and text_deleted then run the analyse"""
    start = self.buffer.get_iter_at_offset(self.start)
    end = self.buffer.get_iter_at_offset(self.end)
    self.analyse(start, end)


  def refresh(self):
    """Used when you want to manually refresh the syntax highlight
    of the whole buffer"""
    start = self.buffer.get_start_iter()
    end = self.buffer.get_end_iter()
    self.analyse(start, end)

    
  def analyse(self, start, end):
    """Run the lexical and syntaxical analysers then
    apply the syntax highlight to the buffer"""
    self.tokens = self.lexical.analyse(self.buffer, start, end)
    self.syntaxical_analyser()
    self.buffer.remove_all_tags(start, end)
    self.buffer.apply_tag(self.tag['font'], start, end)
    self.last = self.tokens
    for token in self.tokens:
      self.buffer.apply_tag(self.tag[token.token], \
                            token.start_iter, \
                            token.end_iter)
      if (token.token == 'comment'):
        self.buffer.apply_tag(self.tag['comment2'], \
                              token.start_iter, \
                              token.end_iter)


  def syntaxical_analyser(self):
    """Find keywords"""
    tokens = self.tokens
    self.tokens = []
    try:
      while (len(tokens) > 0):
        token = tokens.pop(0)
        # only statements, other tokens
        # are analysed below or already founds in 
        # the lexical analyser
        if (token.value in STATEMENTS):
          token.token = 'keyword'
          self.tokens.append(token)
          statement = token.value
          token = tokens.pop(0)
          # get the list containing the
          # keywords for this statement
          try:
            keywords = KEYWORDS[statement]
          except KeyError:
            continue
          if (type(keywords) is dict):
            try:
              keywords = keywords[token.value]
              token.token = 'keyword'
              self.tokens.append(token)
              token = tokens.pop(0)
            except KeyError:
              pass
            self.tokens.append(token)
          # identify each token inside the statement  
          while (token.value != ';'):
            # only identifiers, other tokens are
            # analysed in the lexical analyser
            if (token.token == 'identifier'):
              # special constants
              if (token.value in SPECIALS):
                token.token = 'special'
              # buit-in data types  
              elif (token.value in TYPES):
                # TODO : manage composed types names
                token.token = 'type'
              # names operators  
              elif (token.value in OPERATORS2):
                token.token = 'operator'
              # built-in functions  
              elif (token.value in BUILTINS):
                next = tokens.pop(0)
                if (next.value == '('):
                  token.token = 'function'
                  self.tokens.append(next)
                else:
                  tokens.insert(0, next)
              # built-in function that can be used without ()
              if (token.value in BUILTINS2):
                token.token = 'function'
              # everything else : keywords or indentifiers  
              else:
                for keyword in keywords:  
                  if (type(keyword) is list):
                    # TODO : write the right code here
                    # to manage composed keywords
                    if (token.value in keyword):
                      token.token = 'keyword'
                  elif (token.value == keyword):
                    token.token = 'keyword'
            self.tokens.append(token)
            token = tokens.pop(0)
        self.tokens.append(token)
    except IndexError:
      pass
