2019-08-28 21:24:54 +03:00
#!/usr/bin/python3
# Slang AST
import abc
2020-03-18 07:00:12 +03:00
from . import sld
from . lexer import *
2019-08-28 21:24:54 +03:00
from utils import *
DEBUG_PRECEDENCE = False
def warn ( class_ , msg , node , ns ) :
if ( ns . warnclasses [ class_ ] == False ) : return
logexception ( Warning ( f " { msg } \033 [8m( { class_ } ) \033 [0m " ) , raw = True )
def literal_type ( x ) :
r = eval ( str ( x ) )
if ( isinstance ( r , str ) and len ( r ) == 1 and re . match ( r " ' .+? ' " , x . strip ( ) ) ) : return stdlib . char
return type ( r )
def common_type ( l , ns ) : # TODO
2020-03-18 07:00:12 +03:00
r = tuple ( Signature . build ( i , ns ) for i in l )
2019-08-28 21:24:54 +03:00
if ( not r ) : return None
if ( len ( r ) > 1 ) : raise TODO ( r )
2020-03-18 07:00:12 +03:00
return first ( r )
2019-08-28 21:24:54 +03:00
class ASTNode ( abc . ABC ) :
__slots__ = ( ' lineno ' , ' offset ' , ' flags ' )
@abc.abstractmethod
@init_defaults
def __init__ ( self , * , lineno , offset , flags : paramset ) :
self . lineno , self . offset , self . flags = lineno , offset , flags
def __repr__ ( self ) :
return f " < { self . typename } ' { self . __str__ ( ) } ' on line { self . lineno } , offset { self . offset } > "
@abc.abstractmethod
def __str__ ( self ) :
pass
@abc.abstractclassmethod
def build ( cls , tl ) :
if ( not tl ) : raise SlSyntaxNoToken ( )
def validate ( self , ns ) :
for i in self . __slots__ :
v = getattr ( self , i )
if ( isiterable ( v ) and not isinstance ( v , str ) ) :
for jj , j in enumerate ( v ) :
if ( hasattr ( j , ' validate ' ) ) :
j . validate ( ns )
elif ( hasattr ( v , ' validate ' ) and i != ' code ' ) :
v . validate ( ns )
def optimize ( self , ns ) :
for i in self . __slots__ :
v = getattr ( self , i )
if ( isiterable ( v ) and not isinstance ( v , str ) ) :
for jj , j in enumerate ( v . copy ( ) ) :
if ( hasattr ( j , ' optimize ' ) ) :
r = j . optimize ( ns )
if ( r is not None ) : v [ jj ] = r
if ( v [ jj ] . flags . optimized_out ) : del v [ jj ]
elif ( hasattr ( v , ' optimize ' ) and i != ' code ' ) :
r = v . optimize ( ns )
if ( r is not None ) : setattr ( self , i , r )
if ( getattr ( self , i ) . flags . optimized_out ) : setattr ( self , i , None )
@classproperty
def typename ( cls ) :
return cls . __name__ [ 3 : - 4 ]
@property
def length ( self ) :
return sum ( getattr ( self , i ) . length for i in self . __slots__ if hasattr ( getattr ( self , i ) , ' length ' ) )
class ASTRootNode ( ASTNode ) :
__slots__ = ( ' code ' , )
def __init__ ( self , code , * * kwargs ) :
super ( ) . __init__ ( lineno = None , offset = None , * * kwargs )
self . code = code
def __repr__ ( self ) :
return ' <Root> '
def __str__ ( self ) :
return ' <Root> '
@classmethod
def build ( cls , name = None ) :
return cls ( ( yield from ASTCodeNode . build ( name ) ) )
class ASTCodeNode ( ASTNode ) :
__slots__ = ( ' nodes ' , ' name ' )
def __init__ ( self , nodes , * , name = ' <code> ' , * * kwargs ) :
super ( ) . __init__ ( lineno = None , offset = None , * * kwargs )
self . nodes , self . name = nodes , name
def __repr__ ( self ) :
2020-03-18 07:00:12 +03:00
return f """ <Code { f " ' { self . name } ' " if ( self . name and self . name != ' <code> ' ) else ' ' } > """
2019-08-28 21:24:54 +03:00
def __str__ ( self ) :
2020-03-18 07:00:12 +03:00
return ( S ( ' \n ' ) . join ( self . nodes ) . indent ( ) . join ( ' \n \n ' ) if ( self . nodes ) else ' ' ) . join ( ' {} ' )
2019-08-28 21:24:54 +03:00
@classmethod
def build ( cls , name ) :
yield name
code = cls ( [ ] , name = name )
while ( True ) :
c = yield
if ( c is None ) : break
code . nodes . append ( c )
return code
def validate ( self , ns = None ) :
if ( ns is None ) : ns = Namespace ( self . name )
for i in self . nodes :
i . validate ( ns )
return ns
def optimize ( self , ns ) :
for ii , i in enumerate ( self . nodes ) :
r = i . optimize ( ns )
if ( r is not None ) : self . nodes [ ii ] = r
if ( self . nodes [ ii ] . flags . optimized_out ) : del self . nodes [ ii ]
class ASTTokenNode ( ASTNode ) :
@abc.abstractclassmethod
def build ( cls , tl ) :
super ( ) . build ( tl )
off = int ( )
for ii , i in enumerate ( tl . copy ( ) ) :
2020-03-18 07:00:12 +03:00
if ( i . typename == ' SPECIAL ' and ( i . token [ 0 ] == ' # ' or i . token == ' \\ ' ) ) : del tl [ ii - off ] ; off + = 1
2019-08-28 21:24:54 +03:00
if ( not tl ) : raise SlSyntaxEmpty ( )
@property
def length ( self ) :
return sum ( len ( getattr ( self , i ) ) for i in self . __slots__ )
class ASTIdentifierNode ( ASTTokenNode ) :
__slots__ = ( ' identifier ' , )
def __init__ ( self , identifier , * * kwargs ) :
super ( ) . __init__ ( * * kwargs )
self . identifier = identifier
def __str__ ( self ) :
return str ( self . identifier )
@classmethod
def build ( cls , tl ) :
super ( ) . build ( tl )
lineno , offset = tl [ 0 ] . lineno , tl [ 0 ] . offset
if ( tl [ 0 ] . typename != ' IDENTIFIER ' ) : raise SlSyntaxExpectedError ( ' IDENTIFIER ' , tl [ 0 ] )
identifier = tl . pop ( 0 ) . token
return cls ( identifier , lineno = lineno , offset = offset )
class ASTKeywordNode ( ASTTokenNode ) :
__slots__ = ( ' keyword ' , )
def __init__ ( self , keyword , * * kwargs ) :
super ( ) . __init__ ( * * kwargs )
self . keyword = keyword
def __str__ ( self ) :
return str ( self . keyword )
@classmethod
def build ( cls , tl ) :
super ( ) . build ( tl )
lineno , offset = tl [ 0 ] . lineno , tl [ 0 ] . offset
if ( tl [ 0 ] . typename != ' KEYWORD ' ) : raise SlSyntaxExpectedError ( ' KEYWORD ' , tl [ 0 ] )
keyword = tl . pop ( 0 ) . token
return cls ( keyword , lineno = lineno , offset = offset )
class ASTLiteralNode ( ASTTokenNode ) :
__slots__ = ( ' literal ' , )
def __init__ ( self , literal , * * kwargs ) :
super ( ) . __init__ ( * * kwargs )
self . literal = literal
def __str__ ( self ) :
return str ( self . literal )
@classmethod
def build ( cls , tl ) :
super ( ) . build ( tl )
lineno , offset = tl [ 0 ] . lineno , tl [ 0 ] . offset
if ( tl [ 0 ] . typename != ' LITERAL ' ) : raise SlSyntaxExpectedError ( ' LITERAL ' , tl [ 0 ] )
literal = tl . pop ( 0 ) . token
return cls ( literal , lineno = lineno , offset = offset )
class ASTOperatorNode ( ASTTokenNode ) :
__slots__ = ( ' operator ' , )
def __init__ ( self , operator , * * kwargs ) :
super ( ) . __init__ ( * * kwargs )
self . operator = operator
def __str__ ( self ) :
return str ( self . operator )
@classmethod
def build ( cls , tl ) :
super ( ) . build ( tl )
lineno , offset = tl [ 0 ] . lineno , tl [ 0 ] . offset
if ( tl [ 0 ] . typename != ' OPERATOR ' ) : raise SlSyntaxExpectedError ( ' OPERATOR ' , tl [ 0 ] )
operator = tl . pop ( 0 ) . token
return cls ( operator , lineno = lineno , offset = offset )
class ASTSpecialNode ( ASTTokenNode ) :
__slots__ = ( ' special ' , )
def __init__ ( self , special , * * kwargs ) :
super ( ) . __init__ ( * * kwargs )
self . special = special
def __str__ ( self ) :
return str ( self . special )
@classmethod
def build ( cls , tl ) :
super ( ) . build ( tl )
lineno , offset = tl [ 0 ] . lineno , tl [ 0 ] . offset
if ( tl [ 0 ] . typename != ' SPECIAL ' ) : raise SlSyntaxExpectedError ( ' SPECIAL ' , tl [ 0 ] )
special = tl . pop ( 0 ) . token
if ( special == ' \\ ' ) : raise SlSyntaxEmpty ( )
return cls ( special , lineno = lineno , offset = offset )
class ASTPrimitiveNode ( ASTNode ) : pass
class ASTValueNode ( ASTPrimitiveNode ) :
__slots__ = ( ' value ' , )
def __init__ ( self , value , * * kwargs ) :
super ( ) . __init__ ( * * kwargs )
self . value = value
def __str__ ( self ) :
return str ( self . value )
@classmethod
def build ( cls , tl , * , fcall = False ) :
super ( ) . build ( tl )
lineno , offset = tl [ 0 ] . lineno , tl [ 0 ] . offset
if ( tl [ 0 ] . typename == ' LITERAL ' ) : value = ASTLiteralNode . build ( tl )
else :
2020-03-18 07:00:12 +03:00
types = allsubclasses ( ASTLiteralStructNode ) + [ ASTLiteralNode , ASTFunccallNode , ASTAttrgetNode , ASTItemgetNode , ASTIdentifierNode , ASTLambdaNode ]
if ( fcall ) : types . remove ( ASTFunccallNode ) ; types . remove ( ASTLambdaNode ) # XXX lambda too?
err = set ( )
2019-08-28 21:24:54 +03:00
for i in types :
tll = tl . copy ( )
try : value = i . build ( tll )
2020-03-18 07:00:12 +03:00
except SlSyntaxExpectedError as ex : err . add ( ex ) ; continue
2019-08-28 21:24:54 +03:00
except SlSyntaxException : continue
else : tl [ : ] = tll ; break
2020-03-18 07:00:12 +03:00
else : raise SlSyntaxMultiExpectedError . from_list ( err )
2019-08-28 21:24:54 +03:00
return cls ( value , lineno = lineno , offset = offset )
def validate ( self , ns ) :
super ( ) . validate ( ns )
if ( isinstance ( self . value , ASTIdentifierNode ) ) :
if ( self . value . identifier not in ns ) : raise SlValidationNotDefinedError ( self . value , scope = ns . scope )
if ( self . value . identifier not in ns . values ) : warn ( ' uninitialized ' , f " using value of possibly uninitialized variable ' { self . value } ' " , self , ns )
def optimize ( self , ns ) :
super ( ) . optimize ( ns )
2020-03-18 07:00:12 +03:00
if ( isinstance ( self . value , ASTIdentifierNode ) and ns . values . get ( self . value ) ) : self . value = ASTLiteralNode ( repr ( ns . values [ self . value ] ) , lineno = self . lineno , offset = self . offset ) # TODO FIXME in functions
2019-08-28 21:24:54 +03:00
class ASTItemgetNode ( ASTPrimitiveNode ) :
__slots__ = ( ' value ' , ' key ' )
def __init__ ( self , value , key , * * kwargs ) :
super ( ) . __init__ ( * * kwargs )
self . value , self . key = value , key
def __str__ ( self ) :
return f " { self . value } [ { self . key } ] "
@classmethod
def build ( cls , tl ) :
super ( ) . build ( tl )
lineno , offset = tl [ 0 ] . lineno , tl [ 0 ] . offset
value = ASTIdentifierNode . build ( tl ) # TODO: value/expr
bracket = ASTSpecialNode . build ( tl )
if ( bracket . special != ' [ ' ) : raise SlSyntaxExpectedError ( " ' [ ' " , bracket )
2020-03-18 07:00:12 +03:00
start = None
stop = None
step = None
if ( tl and not ( tl [ 0 ] . typename == ' SPECIAL ' and tl [ 0 ] . token == ' : ' ) ) : start = ASTExprNode . build ( tl )
if ( tl and tl [ 0 ] . typename == ' SPECIAL ' and tl [ 0 ] . token == ' : ' ) :
ASTSpecialNode . build ( tl )
if ( tl and not ( tl [ 0 ] . typename == ' SPECIAL ' and tl [ 0 ] . token in ' ]: ' ) ) : stop = ASTExprNode . build ( tl )
if ( tl and tl [ 0 ] . typename == ' SPECIAL ' and tl [ 0 ] . token == ' : ' ) :
ASTSpecialNode . build ( tl )
if ( tl and not ( tl [ 0 ] . typename == ' SPECIAL ' and tl [ 0 ] . token == ' ] ' ) ) : step = ASTExprNode . build ( tl )
key = slice ( start , stop , step )
else : key = start
2019-08-28 21:24:54 +03:00
bracket = ASTSpecialNode . build ( tl )
if ( bracket . special != ' ] ' ) : raise SlSyntaxExpectedError ( " ' ] ' " , bracket )
return cls ( value , key , lineno = lineno , offset = offset )
def validate ( self , ns ) :
super ( ) . validate ( ns )
valsig = Signature . build ( self . value , ns )
keysig = Signature . build ( self . key , ns )
2020-03-18 07:00:12 +03:00
if ( ( keysig , self . key ) not in valsig . itemget ) : raise SlValidationError ( f " ' { valsig } ' does not support itemget by key of type ' { keysig } ' " , self , scope = ns . scope )
2019-08-28 21:24:54 +03:00
class ASTAttrgetNode ( ASTPrimitiveNode ) :
__slots__ = ( ' value ' , ' optype ' , ' attr ' )
def __init__ ( self , value , optype , attr , * * kwargs ) :
super ( ) . __init__ ( * * kwargs )
self . value , self . optype , self . attr = value , optype , attr
def __str__ ( self ) :
return f " { self . value } { self . optype } { self . attr } "
@classmethod
def build ( cls , tl ) :
super ( ) . build ( tl )
lineno , offset = tl [ 0 ] . lineno , tl [ 0 ] . offset
value = ASTIdentifierNode . build ( tl )
optype = ASTSpecialNode . build ( tl )
if ( optype . special not in attrops ) : raise SlSyntaxExpectedError ( f " one of { attrops } " , optype )
attr = ASTIdentifierNode . build ( tl )
return cls ( value , optype , attr , lineno = lineno , offset = offset )
def validate ( self , ns ) :
super ( ) . validate ( ns )
2020-03-18 07:00:12 +03:00
valsig = Signature . build ( self . value , ns )
if ( ( self . optype . special , self . attr . identifier ) not in valsig . attrops ) : raise SlValidationError ( f " ' { valsig } ' does not support attribute operation ' { self . optype } ' with attr ' { self . attr } ' " , self , scope = ns . scope )
2019-08-28 21:24:54 +03:00
class ASTExprNode ( ASTPrimitiveNode ) :
@classmethod
def build ( cls , tl , * , fcall = False ) :
super ( ) . build ( tl )
lineno , offset = tl [ 0 ] . lineno , tl [ 0 ] . offset
for ii , p in enumerate ( operators [ : : - 1 ] ) :
2020-03-18 07:00:12 +03:00
tll = tl . copy ( )
try : value = ASTBinaryExprNode . build ( tll , p )
except SlSyntaxException : continue
else : tl [ : ] = tll ; return value
2019-08-28 21:24:54 +03:00
tll = tl . copy ( )
try : value = ASTUnaryExprNode . build ( tll )
except SlSyntaxException : pass
else : tl [ : ] = tll ; return value
tll = tl . copy ( )
try : value = ASTValueNode . build ( tll , fcall = fcall )
2020-03-18 07:00:12 +03:00
except SlSyntaxException as ex : pass
2019-08-28 21:24:54 +03:00
else : tl [ : ] = tll ; return value
2020-03-18 07:00:12 +03:00
try :
parenthesis = ASTSpecialNode . build ( tl )
if ( parenthesis . special != ' ( ' ) : raise SlSyntaxExpectedError ( ' Expr ' , parenthesis )
parenthesized = list ( )
lvl = 1
while ( tl ) :
if ( tl [ 0 ] . typename == ' SPECIAL ' ) : lvl + = 1 if ( tl [ 0 ] . token == ' ( ' ) else - 1 if ( tl [ 0 ] . token == ' ) ' ) else 0
if ( lvl == 0 ) :
parenthesis = ASTSpecialNode . build ( tl )
if ( parenthesis . special != ' ) ' ) : raise SlSyntaxExpectedError ( " ' ) ' " , parenthesis )
break
assert ( lvl > 0 )
parenthesized . append ( tl . pop ( 0 ) )
value = ASTExprNode . build ( parenthesized )
if ( parenthesized ) : raise SlSyntaxExpectedNothingError ( parenthesized [ 0 ] )
except SlSyntaxException : pass # TODO
else : return value
raise SlSyntaxExpectedError ( ' Expr ' , lineno = lineno , offset = offset )
2019-08-28 21:24:54 +03:00
class ASTUnaryExprNode ( ASTExprNode ) :
__slots__ = ( ' operator ' , ' value ' )
def __init__ ( self , operator , value , * * kwargs ) :
2020-03-18 07:00:12 +03:00
super ( ) . __init__ ( * * kwargs )
2019-08-28 21:24:54 +03:00
self . operator , self . value = operator , value
def __str__ ( self ) :
return f " { self . operator } { ' ' if ( self . operator . operator . isalpha ( ) ) else ' ' } { str ( self . value ) . join ( ' () ' ) if ( DEBUG_PRECEDENCE or isinstance ( self . value , ASTBinaryExprNode ) and operator_precedence ( self . value . operator . operator ) > = operator_precedence ( self . operator . operator ) ) else self . value } "
@classmethod
def build ( cls , tl ) :
ASTPrimitiveNode . build ( tl )
lineno , offset = tl [ 0 ] . lineno , tl [ 0 ] . offset
operator = ASTOperatorNode . build ( tl )
2020-03-18 07:00:12 +03:00
if ( not isinstance ( operator . operator , UnaryOperator ) ) : raise SlSyntaxExpectedError ( ' UnaryOperator ' , operator )
2019-08-28 21:24:54 +03:00
value = ASTExprNode . build ( tl )
return cls ( operator , value , lineno = lineno , offset = offset )
def validate ( self , ns ) :
super ( ) . validate ( ns )
2020-03-18 07:00:12 +03:00
valsig = Signature . build ( self . value , ns )
2019-08-28 21:24:54 +03:00
op = self . operator . operator
2020-03-18 07:00:12 +03:00
if ( op not in valsig . operators ) : raise SlValidationError ( f " ' { valsig } ' does not support unary operator ' { op } ' " , self , scope = ns . scope )
2019-08-28 21:24:54 +03:00
def optimize ( self , ns ) :
super ( ) . optimize ( ns )
2020-03-18 07:00:12 +03:00
if ( ns . values . get ( self . value ) ) : return ASTValueNode ( ASTLiteralNode ( eval ( f " { ' not ' if ( self . operator . operator == ' ! ' ) else self . operator } { ns . values [ self . value ] } " ) , lineno = self . lineno , offset = self . offset ) , lineno = self . lineno , offset = self . offset )
2019-08-28 21:24:54 +03:00
class ASTBinaryExprNode ( ASTExprNode ) :
__slots__ = ( ' lvalue ' , ' operator ' , ' rvalue ' )
def __init__ ( self , lvalue , operator , rvalue , * * kwargs ) :
2020-03-18 07:00:12 +03:00
super ( ) . __init__ ( * * kwargs )
2019-08-28 21:24:54 +03:00
self . lvalue , self . operator , self . rvalue = lvalue , operator , rvalue
def __str__ ( self ) :
return f " { str ( self . lvalue ) . join ( ' () ' ) if ( DEBUG_PRECEDENCE or isinstance ( self . lvalue , ASTBinaryExprNode ) and operator_precedence ( self . lvalue . operator . operator ) > operator_precedence ( self . operator . operator ) ) else self . lvalue } { str ( self . operator ) . join ( ' ' ) if ( operator_precedence ( self . operator . operator ) > 0 ) else self . operator } { str ( self . rvalue ) . join ( ' () ' ) if ( DEBUG_PRECEDENCE or isinstance ( self . rvalue , ASTBinaryExprNode ) and operator_precedence ( self . rvalue . operator . operator ) > operator_precedence ( self . operator . operator ) ) else self . rvalue } "
@classmethod
2020-03-18 07:00:12 +03:00
def build ( cls , tl , opset ) :
2019-08-28 21:24:54 +03:00
ASTPrimitiveNode . build ( tl )
lineno , offset = tl [ 0 ] . lineno , tl [ 0 ] . offset
2020-03-18 07:00:12 +03:00
lasti = list ( )
2019-08-28 21:24:54 +03:00
lvl = int ( )
for ii , i in enumerate ( tl ) :
if ( i . typename == ' SPECIAL ' ) : lvl + = 1 if ( i . token == ' ( ' ) else - 1 if ( i . token == ' ) ' ) else 0
if ( lvl > 0 ) : continue
2020-03-18 07:00:12 +03:00
if ( i . typename == ' OPERATOR ' and isinstance ( i . token , BinaryOperator ) and i . token in opset ) : lasti . append ( ii )
for i in lasti [ : : - 1 ] :
tlr , tll = tl [ : i ] , tl [ i : ]
err = set ( )
try :
lvalue = ASTExprNode . build ( tlr )
if ( tlr ) : raise SlSyntaxExpectedNothingError ( tlr [ 0 ] )
operator = ASTOperatorNode . build ( tll )
rvalue = ASTExprNode . build ( tll )
except SlSyntaxException : pass
else : tl [ : ] = tll ; break
else : raise SlSyntaxExpectedError ( ' BinaryOperator ' , tl [ 0 ] )
2019-08-28 21:24:54 +03:00
return cls ( lvalue , operator , rvalue , lineno = lineno , offset = offset )
def validate ( self , ns ) :
super ( ) . validate ( ns )
lsig = Signature . build ( self . lvalue , ns )
rsig = Signature . build ( self . rvalue , ns )
op = self . operator . operator
if ( ( op , rsig ) not in lsig . operators ) : raise SlValidationError ( f " ' { lsig } ' does not support operator ' { op } ' with operand of type ' { rsig } ' " , self , scope = ns . scope )
def optimize ( self , ns ) :
super ( ) . optimize ( ns )
2020-03-18 07:00:12 +03:00
if ( self . operator . operator == ' ** ' and ns . values . get ( self . lvalue ) == 2 and ( ns . values . get ( self . rvalue ) or 0 ) > 0 ) : self . operator . operator , self . lvalue . value , ns . values [ self . lvalue ] = BinaryOperator ( ' << ' ) , ASTLiteralNode ( ' 1 ' , lineno = self . lvalue . value . lineno , offset = self . lvalue . value . offset ) , 1
if ( ns . values . get ( self . lvalue ) and ns . values . get ( self . rvalue ) and self . operator . operator != ' to ' ) : return ASTValueNode ( ASTLiteralNode ( repr ( eval ( str ( self ) ) ) , lineno = self . lineno , offset = self . offset ) , lineno = self . lineno , offset = self . offset )
class ASTLiteralStructNode ( ASTNode ) : pass
class ASTListNode ( ASTLiteralStructNode ) :
__slots__ = ( ' type ' , ' values ' )
def __init__ ( self , type , values , * * kwargs ) :
super ( ) . __init__ ( * * kwargs )
self . type , self . values = type , values
def __repr__ ( self ) :
return f " <List of ' { self . type } ' > "
def __str__ ( self ) :
return f " [ { self . type } { ' : ' if ( self . values ) else ' ' } { S ( ' , ' ) . join ( self . values ) } ] "
@classmethod
def build ( cls , tl ) :
super ( ) . build ( tl )
lineno , offset = tl [ 0 ] . lineno , tl [ 0 ] . offset
bracket = ASTSpecialNode . build ( tl )
if ( bracket . special != ' [ ' ) : raise SlSyntaxExpectedError ( " ' [ ' " , bracket )
type = ASTIdentifierNode . build ( tl )
values = list ( )
if ( not ( tl [ 0 ] . typename == ' SPECIAL ' and tl [ 0 ] . token == ' ] ' ) ) :
colon = ASTSpecialNode . build ( tl )
if ( colon . special != ' : ' ) : raise SlSyntaxExpectedError ( " ' : ' " , colon )
while ( tl and tl [ 0 ] . typename != ' SPECIAL ' ) :
values . append ( ASTExprNode . build ( tl ) )
if ( tl and tl [ 0 ] . typename == ' SPECIAL ' and tl [ 0 ] . token == ' , ' ) : ASTSpecialNode . build ( tl )
bracket = ASTSpecialNode . build ( tl )
if ( bracket . special != ' ] ' ) : raise SlSyntaxExpectedError ( " ' ] ' " , bracket )
return cls ( type , values , lineno = lineno , offset = offset )
def validate ( self , ns ) :
typesig = Signature . build ( self . type , ns )
for i in self . values :
if ( Signature . build ( i , ns ) != typesig ) : raise SlValidationError ( f " List item ' { i } ' does not match list type ' { self . type } ' " , self , scope = ns . scope )
class ASTTupleNode ( ASTLiteralStructNode ) :
__slots__ = ( ' types ' , ' values ' )
def __init__ ( self , types , values , * * kwargs ) :
super ( ) . __init__ ( * * kwargs )
self . types , self . values = types , values
def __repr__ ( self ) :
return f " <Tuple ( { S ( ' , ' ) . join ( self . types ) } )> "
def __str__ ( self ) :
return f " ( { S ( ' , ' ) . join ( ( str ( self . types [ i ] ) + ' ' if ( self . types [ i ] is not None ) else ' ' ) + str ( self . values [ i ] ) for i in range ( len ( self . values ) ) ) } { ' , ' * ( len ( self . values ) == 1 ) } ) "
@classmethod
def build ( cls , tl ) :
super ( ) . build ( tl )
lineno , offset = tl [ 0 ] . lineno , tl [ 0 ] . offset
parenthesis = ASTSpecialNode . build ( tl )
if ( parenthesis . special != ' ( ' ) : raise SlSyntaxExpectedError ( " ' ( ' " , parenthesis )
types = list ( )
values = list ( )
while ( tl and not ( tl [ 0 ] . typename == ' SPECIAL ' and tl [ 0 ] . token == ' ) ' ) ) :
types . append ( ASTIdentifierNode . build ( tl ) if ( len ( tl ) > = 2 and tl [ 0 ] . typename == ' IDENTIFIER ' and tl [ 1 ] . token != ' , ' ) else None )
values . append ( ASTExprNode . build ( tl ) )
if ( len ( values ) < 2 or tl and tl [ 0 ] . typename == ' SPECIAL ' and tl [ 0 ] . token == ' , ' ) : ASTSpecialNode . build ( tl )
parenthesis = ASTSpecialNode . build ( tl )
if ( parenthesis . special != ' ) ' ) : raise SlSyntaxExpectedError ( " ' ) ' " , parenthesis )
return cls ( types , values , lineno = lineno , offset = offset )
def validate ( self , ns ) :
for i in range ( len ( self . values ) ) :
if ( Signature . build ( self . values [ i ] , ns ) != Signature . build ( self . types [ i ] , ns ) ) : raise SlValidationError ( f " Tuple item ' { self . values [ i ] } ' does not match its type ' { self . types [ i ] } ' " , self , scope = ns . scope )
2019-08-28 21:24:54 +03:00
class ASTNonFinalNode ( ASTNode ) : pass
class ASTTypedefNode ( ASTNonFinalNode ) :
__slots__ = ( ' modifiers ' , ' type ' )
def __init__ ( self , modifiers , type , * * kwargs ) :
super ( ) . __init__ ( * * kwargs )
self . modifiers , self . type = modifiers , type
def __str__ ( self ) :
return f " { S ( ' ' ) . join ( self . modifiers ) } { ' ' if ( self . modifiers and self . type ) else ' ' } { self . type or ' ' } "
@classmethod
def build ( cls , tl ) :
super ( ) . build ( tl )
lineno , offset = tl [ 0 ] . lineno , tl [ 0 ] . offset
modifiers = list ( )
while ( tl and tl [ 0 ] . typename == ' KEYWORD ' ) :
if ( not isinstance ( tl [ 0 ] . token , Modifier ) ) : raise SlSyntaxExpectedError ( ' Modifier ' , tl [ 0 ] )
modifiers . append ( ASTKeywordNode . build ( tl ) )
type = ASTIdentifierNode . build ( tl )
return cls ( modifiers , type , lineno = lineno , offset = offset )
class ASTArgdefNode ( ASTNonFinalNode ) :
__slots__ = ( ' type ' , ' name ' , ' modifier ' , ' value ' )
def __init__ ( self , type , name , modifier , value , * * kwargs ) :
super ( ) . __init__ ( * * kwargs )
self . type , self . name , self . modifier , self . value = type , name , modifier , value
def __str__ ( self ) :
return f " { f ' { self . type } ' if ( self . type ) else ' ' } { self . name } { self . modifier or ' ' } { f ' = { self . value } ' if ( self . value is not None ) else ' ' } "
@classmethod
def build ( cls , tl ) :
super ( ) . build ( tl )
lineno , offset = tl [ 0 ] . lineno , tl [ 0 ] . offset
type = ASTTypedefNode . build ( tl )
name = ASTIdentifierNode . build ( tl )
modifier = ASTOperatorNode . build ( tl ) if ( tl and tl [ 0 ] . typename == ' OPERATOR ' and tl [ 0 ] . token in ' +** ' ) else ASTSpecialNode . build ( tl ) if ( tl and tl [ 0 ] . typename == ' SPECIAL ' and tl [ 0 ] . token in ' ?= ' ) else None
2020-03-18 07:00:12 +03:00
value = ASTValueNode . build ( tl ) if ( isinstance ( modifier , ASTSpecialNode ) and modifier . special == ' = ' ) else None
2019-08-28 21:24:54 +03:00
return cls ( type , name , modifier , value , lineno = lineno , offset = offset )
def validate ( self , ns ) :
super ( ) . validate ( ns )
if ( isinstance ( Signature . build ( self . type , ns ) , stdlib . void ) ) : raise SlValidationError ( f " Argument cannot have type ' { self . type } ' " , self , scope = ns . scope )
class ASTCallargsNode ( ASTNonFinalNode ) :
__slots__ = ( ' callargs ' , ' starargs ' )
def __init__ ( self , callargs , starargs , * * kwargs ) :
super ( ) . __init__ ( * * kwargs )
self . callargs , self . starargs = callargs , starargs
def __str__ ( self ) :
return S ( ' , ' ) . join ( ( * self . callargs , * ( f ' * { i } ' for i in self . starargs ) ) )
@classmethod
def build ( cls , tl ) :
super ( ) . build ( tl )
lineno , offset = tl [ 0 ] . lineno , tl [ 0 ] . offset
callargs = list ( )
starargs = list ( )
if ( tl and not ( tl [ 0 ] . typename == ' SPECIAL ' and tl [ 0 ] . token == ' ) ' ) ) :
while ( tl ) :
if ( tl [ 0 ] . typename == ' OPERATOR ' and tl [ 0 ] . token == ' * ' ) :
ASTOperatorNode . build ( tl )
starargs . append ( ASTExprNode . build ( tl ) )
elif ( len ( tl ) > = 2 and tl [ 1 ] . typename == ' SPECIAL ' and tl [ 1 ] . token == ' = ' ) : break
else : callargs . append ( ASTExprNode . build ( tl ) )
if ( not tl or tl [ 0 ] . typename != ' SPECIAL ' or tl [ 0 ] . token == ' ) ' ) : break
comma = ASTSpecialNode . build ( tl )
if ( comma . special != ' , ' ) : raise SlSyntaxExpectedError ( " ' , ' " , comma )
return cls ( callargs , starargs , lineno = lineno , offset = offset )
class ASTCallkwargsNode ( ASTNonFinalNode ) :
__slots__ = ( ' callkwargs ' , ' starkwargs ' )
def __init__ ( self , callkwargs , starkwargs , * * kwargs ) :
super ( ) . __init__ ( * * kwargs )
self . callkwargs , self . starkwargs = callkwargs , starkwargs
def __str__ ( self ) :
return S ( ' , ' ) . join ( ( * ( f ' { k } = { v } ' for k , v in self . callkwargs ) , * ( f ' ** { i } ' for i in self . starkwargs ) ) )
@classmethod
def build ( cls , tl ) :
super ( ) . build ( tl )
lineno , offset = tl [ 0 ] . lineno , tl [ 0 ] . offset
callkwargs = list ( )
starkwargs = list ( )
if ( tl and not ( tl [ 0 ] . typename == ' SPECIAL ' and tl [ 0 ] . token == ' ) ' ) ) :
while ( tl ) :
if ( tl [ 0 ] . typename == ' OPERATOR ' and tl [ 0 ] . token == ' ** ' ) :
ASTOperatorNode . build ( tl )
starkwargs . append ( ASTExprNode . build ( tl ) )
else :
key = ASTIdentifierNode . build ( tl )
eq = ASTSpecialNode . build ( tl )
if ( eq . special != ' = ' ) : raise SlSyntaxExpectedError ( " ' = ' " , eq )
value = ASTExprNode . build ( tl )
callkwargs . append ( ( key , value ) )
if ( not tl or tl [ 0 ] . typename != ' SPECIAL ' or tl [ 0 ] . token == ' ) ' ) : break
comma = ASTSpecialNode . build ( tl )
if ( comma . special != ' , ' ) : raise SlSyntaxExpectedError ( " ' , ' " , comma )
return cls ( callkwargs , starkwargs , lineno = lineno , offset = offset )
class ASTCallableNode ( ASTNode ) :
def validate ( self , ns ) :
super ( ) . validate ( ns )
code_ns = ns . derive ( self . code . name )
2020-03-18 07:00:12 +03:00
code_ns . define ( self , redefine = True )
for i in self . argdefs :
code_ns . define ( i , redefine = True )
2019-08-28 21:24:54 +03:00
self . code . validate ( code_ns )
def optimize ( self , ns ) :
super ( ) . optimize ( ns )
code_ns = ns . derive ( self . code . name )
self . code . optimize ( code_ns )
2020-03-18 07:00:12 +03:00
class ASTFunctionNode ( ASTCallableNode ) :
def validate ( self , ns ) :
super ( ) . validate ( ns )
code_ns = ns . derive ( self . code . name )
rettype = common_type ( ( i . value for i in self . code . nodes if ( isinstance ( i , ASTKeywordExprNode ) and i . keyword . keyword == ' return ' ) ) , code_ns ) or stdlib . void ( )
if ( self . type . type . identifier == ' auto ' ) : self . type . type . identifier = rettype . typename
else :
expected = Signature . build ( self . type , ns )
if ( rettype != expected ) : raise SlValidationError ( f " Returning value of type ' { rettype } ' from function with return type ' { expected } ' " , self , scope = ns . scope )
class ASTLambdaNode ( ASTNonFinalNode , ASTFunctionNode ) :
2019-08-28 21:24:54 +03:00
__slots__ = ( ' argdefs ' , ' type ' , ' code ' )
def __init__ ( self , argdefs , type , code , * * kwargs ) :
super ( ) . __init__ ( * * kwargs )
self . argdefs , self . type , self . code = argdefs , type , code
def __fsig__ ( self ) :
return f " ( { S ( ' , ' ) . join ( self . argdefs ) } ) -> { self . type } "
def __repr__ ( self ) :
return f " <Lambda ' { self . __fsig__ ( ) } {{ ... }} ' on line { self . lineno } , offset { self . offset } > "
def __str__ ( self ) :
2020-03-18 07:00:12 +03:00
return f " { self . __fsig__ ( ) } { f ' = { self . code . nodes [ 0 ] . value } ' if ( len ( self . code . nodes ) == 1 and isinstance ( self . code . nodes [ 0 ] , ASTKeywordExprNode ) and self . code . nodes [ 0 ] . keyword . keyword == ' return ' ) else self . code } "
2019-08-28 21:24:54 +03:00
@classmethod
def build ( cls , tl ) :
super ( ) . build ( tl )
lineno , offset = tl [ 0 ] . lineno , tl [ 0 ] . offset
parenthesis = ASTSpecialNode . build ( tl )
if ( parenthesis . special != ' ( ' ) : raise SlSyntaxExpectedError ( " ' ( ' " , parenthesis )
argdefs = list ( )
while ( tl and tl [ 0 ] . typename != ' SPECIAL ' ) :
argdefs . append ( ASTArgdefNode . build ( tl ) )
if ( tl and tl [ 0 ] . typename == ' SPECIAL ' and tl [ 0 ] . token == ' , ' ) : ASTSpecialNode . build ( tl )
parenthesis = ASTSpecialNode . build ( tl )
if ( parenthesis . special != ' ) ' ) : raise SlSyntaxExpectedError ( " ' ) ' " , parenthesis )
arrow = ASTOperatorNode . build ( tl )
if ( arrow . operator != ' -> ' ) : raise SlSyntaxExpectedError ( " ' -> ' " , arrow )
type = ASTTypedefNode . build ( tl )
if ( tl and ( tl [ 0 ] . typename != ' SPECIAL ' or tl [ 0 ] . token not in ( * ' = { ' , ) ) ) : raise SlSyntaxExpectedError ( " ' = ' or ' { ' " , tl [ 0 ] )
cdef = ASTSpecialNode . build ( tl )
if ( cdef . special != ' = ' ) : raise SlSyntaxExpectedError ( ' = ' , cdef )
code = ASTCodeNode ( [ ASTKeywordExprNode ( ASTKeywordNode ( ' return ' , lineno = lineno , offset = offset ) , ASTExprNode . build ( tl ) , lineno = lineno , offset = offset ) ] , name = ' <lambda> ' )
return cls ( argdefs , type , code , lineno = lineno , offset = offset )
class ASTBlockNode ( ASTNonFinalNode ) :
__slots__ = ( ' code ' , )
def __init__ ( self , code , * * kwargs ) :
super ( ) . __init__ ( * * kwargs )
self . code = code
def __str__ ( self ) :
2020-03-18 07:00:12 +03:00
return str ( self . code ) if ( len ( self . code . nodes ) > 1 ) else str ( self . code ) [ 1 : - 1 ] . strip ( )
2019-08-28 21:24:54 +03:00
@classmethod
def build ( cls , tl ) :
super ( ) . build ( tl )
lineno , offset = tl [ 0 ] . lineno , tl [ 0 ] . offset
if ( tl [ 0 ] . typename == ' SPECIAL ' and tl [ 0 ] . token == ' { ' ) :
ASTSpecialNode . build ( tl )
code = ( yield from ASTCodeNode . build ( ' <block> ' ) )
else :
yield ' <expr> '
code = ASTCodeNode ( [ ASTExprNode . build ( tl ) ] , name = ' ' )
return cls ( code , lineno = lineno , offset = offset )
class ASTFinalNode ( ASTNode ) : pass
class ASTDefinitionNode ( ASTNode ) :
def validate ( self , ns ) :
2020-03-18 07:00:12 +03:00
Signature . build ( self , ns )
2019-08-28 21:24:54 +03:00
super ( ) . validate ( ns )
2020-03-18 07:00:12 +03:00
class ASTFuncdefNode ( ASTFinalNode , ASTDefinitionNode , ASTFunctionNode ) :
2019-08-28 21:24:54 +03:00
__slots__ = ( ' type ' , ' name ' , ' argdefs ' , ' code ' )
def __init__ ( self , type , name , argdefs , code , * * kwargs ) :
super ( ) . __init__ ( * * kwargs )
self . type , self . name , self . argdefs , self . code = type , name , argdefs , code
def __fsig__ ( self ) :
return f " { self . type or ' def ' } { self . name } ( { S ( ' , ' ) . join ( self . argdefs ) } ) "
def __repr__ ( self ) :
return f " <Funcdef ' { self . __fsig__ ( ) } {{ ... }} ' on line { self . lineno } , offset { self . offset } > "
def __str__ ( self ) :
2020-03-18 07:00:12 +03:00
isexpr = ( len ( self . code . nodes ) == 1 and isinstance ( self . code . nodes [ 0 ] , ASTKeywordExprNode ) and self . code . nodes [ 0 ] . keyword . keyword == ' return ' )
r = f " { self . __fsig__ ( ) } { f ' = { self . code . nodes [ 0 ] . value } ' if ( isexpr ) else self . code } "
2019-08-28 21:24:54 +03:00
return r if ( isexpr ) else r . join ( ' \n \n ' )
@classmethod
def build ( cls , tl ) :
super ( ) . build ( tl )
lineno , offset = tl [ 0 ] . lineno , tl [ 0 ] . offset
type = ASTTypedefNode . build ( tl )
name = ASTIdentifierNode . build ( tl )
parenthesis = ASTSpecialNode . build ( tl )
if ( parenthesis . special != ' ( ' ) : raise SlSyntaxExpectedError ( " ' ( ' " , parenthesis )
argdefs = list ( )
while ( tl and tl [ 0 ] . typename != ' SPECIAL ' ) :
argdef = ASTArgdefNode . build ( tl )
if ( argdefs and argdef . value is None and argdefs [ - 1 ] . value is not None ) : raise SlSyntaxError ( f " Non-default argument { argdef } follows default argument { argdefs [ - 1 ] } " )
argdefs . append ( argdef )
if ( tl and tl [ 0 ] . typename == ' SPECIAL ' and tl [ 0 ] . token == ' , ' ) : ASTSpecialNode . build ( tl )
parenthesis = ASTSpecialNode . build ( tl )
if ( parenthesis . special != ' ) ' ) : raise SlSyntaxExpectedError ( " ' ) ' " , parenthesis )
if ( not tl ) : raise SlSyntaxExpectedError ( " ' = ' or ' { ' " , lineno = lineno , offset = - 1 )
if ( tl [ 0 ] . typename != ' SPECIAL ' or tl [ 0 ] . token not in ( * ' = { ' , ) ) : raise SlSyntaxExpectedError ( " ' = ' or ' { ' " , tl [ 0 ] )
cdef = ASTSpecialNode . build ( tl )
if ( cdef . special == ' = ' ) :
yield name . identifier
code = ASTCodeNode ( [ ASTKeywordExprNode ( ASTKeywordNode ( ' return ' , lineno = lineno , offset = offset ) , ASTExprNode . build ( tl ) , lineno = lineno , offset = offset ) ] , name = name . identifier )
elif ( cdef . special == ' { ' ) :
code = ( yield from ASTCodeNode . build ( name . identifier ) )
return cls ( type , name , argdefs , code , lineno = lineno , offset = offset )
2020-03-18 07:00:12 +03:00
class ASTClassdefNode ( ASTFinalNode , ASTDefinitionNode , ASTCallableNode ) :
__slots__ = ( ' name ' , ' bases ' , ' code ' , ' type ' )
argdefs = ( )
def __init__ ( self , name , bases , code , * * kwargs ) :
super ( ) . __init__ ( * * kwargs )
self . name , self . bases , self . code = name , bases , code
self . type = ASTTypedefNode ( [ ] , self . name , lineno = self . lineno , offset = self . offset )
def __repr__ ( self ) :
return f " <Classdef ' { self . name } ' on line { self . lineno } , offset { self . offset } > "
def __str__ ( self ) :
return f " class { self . name } { S ( ' , ' ) . join ( self . bases ) . join ( ' () ' ) if ( self . bases ) else ' ' } { self . code } "
@classmethod
def build ( cls , tl ) :
super ( ) . build ( tl )
lineno , offset = tl [ 0 ] . lineno , tl [ 0 ] . offset
class_ = ASTKeywordNode . build ( tl )
if ( class_ . keyword != ' class ' ) : raise SlSyntaxExpectedError ( " ' class ' " , class_ )
name = ASTIdentifierNode . build ( tl )
bases = list ( )
if ( tl and tl [ 0 ] . typename == ' SPECIAL ' and tl [ 0 ] . token == ' ( ' ) :
parenthesis = ASTSpecialNode . build ( tl )
if ( parenthesis . special != ' ( ' ) : raise SlSyntaxExpectedError ( " ' ( ' " , parenthesis )
while ( tl and tl [ 0 ] . typename != ' SPECIAL ' ) :
bases . append ( ASTIdentifierNode . build ( tl ) )
if ( tl and tl [ 0 ] . typename == ' SPECIAL ' and tl [ 0 ] . token == ' , ' ) : ASTSpecialNode . build ( tl )
parenthesis = ASTSpecialNode . build ( tl )
if ( parenthesis . special != ' ) ' ) : raise SlSyntaxExpectedError ( " ' ) ' " , parenthesis )
cdef = ASTSpecialNode . build ( tl )
if ( cdef . special != ' { ' ) : raise SlSyntaxExpectedError ( " ' { ' " , cdef )
code = ( yield from ASTCodeNode . build ( name . identifier ) )
return cls ( name , bases , code , lineno = lineno , offset = offset )
2019-08-28 21:24:54 +03:00
class ASTKeywordExprNode ( ASTFinalNode ) :
__slots__ = ( ' keyword ' , ' value ' )
def __init__ ( self , keyword , value , * * kwargs ) :
super ( ) . __init__ ( * * kwargs )
self . keyword , self . value = keyword , value
def __str__ ( self ) :
return f " { self . keyword } { f ' { self . value } ' if ( self . value is not None ) else ' ' } "
@classmethod
def build ( cls , tl ) :
super ( ) . build ( tl )
lineno , offset = tl [ 0 ] . lineno , tl [ 0 ] . offset
keyword = ASTKeywordNode . build ( tl )
if ( not isinstance ( keyword . keyword , ExprKeyword ) ) : raise SlSyntaxExpectedError ( ' ExprKeyword ' , keyword )
2020-03-18 07:00:12 +03:00
if ( keyword . keyword == ' import ' ) :
if ( not tl ) : raise SlSyntaxExpectedMoreTokensError ( ' import ' , lineno = lineno )
2019-08-28 21:24:54 +03:00
lineno_ , offset_ = tl [ 0 ] . lineno , tl [ 0 ] . offset
value = ASTIdentifierNode ( str ( ) . join ( tl . pop ( 0 ) . token for _ in range ( len ( tl ) ) ) , lineno = lineno_ , offset = offset_ ) # TODO Identifier? (or document it)
2020-03-18 07:00:12 +03:00
elif ( keyword . keyword == ' delete ' ) :
value = ASTIdentifierNode . build ( tl )
#if (not value): raise SlSyntaxExpectedError('identifier', lineno=lineno, offset=-1)
elif ( tl ) : value = ASTExprNode . build ( tl )
else : value = None
2019-08-28 21:24:54 +03:00
return cls ( keyword , value , lineno = lineno , offset = offset )
def validate ( self , ns ) :
if ( self . keyword . keyword == ' import ' ) :
2020-03-18 07:00:12 +03:00
m = re . fullmatch ( r ' (?:(?:( \ w+):)?(?:([ \ w./]+)/)?([ \ w.]+):)?([ \ w*]+) ' , self . value . identifier )
assert ( m is not None )
namespace , path , pkg , name = m . groups ( )
if ( namespace is None ) : namespace = ' sl '
if ( path is None ) : path = ' . '
if ( pkg is None ) : pkg = name
pkg = pkg . replace ( ' . ' , ' / ' )
if ( namespace != ' sl ' ) :
filename = f " { os . path . join ( path , pkg ) } .sld "
f = sld . parse ( open ( filename ) . read ( ) )
module_ns = f . namespace
else :
filename = f " { os . path . join ( path , pkg ) } .sl "
src = open ( filename , ' r ' ) . read ( )
try :
tl = parse_string ( src )
ast = build_ast ( tl , filename )
optimize_ast ( ast , validate_ast ( ast ) )
module_ns = validate_ast ( ast )
except ( SlSyntaxError , SlValidationError ) as ex :
ex . line = src . split ( ' \n ' ) [ ex . lineno - 1 ]
raise SlValidationError ( f " Error importing { self . value } " , self , scope = ns . scope ) from ex
if ( name != ' * ' ) : ns . define ( ASTIdentifierNode ( name , lineno = self . value . lineno , offset = self . value . offset ) ) # TODO object
else : ns . signatures . update ( module_ns . signatures ) # TODO?
#return # XXX?
elif ( self . keyword . keyword == ' delete ' ) :
if ( self . value . identifier not in ns ) : raise SlValidationNotDefinedError ( self . value , scope = ns . scope )
ns . delete ( self . value )
2019-08-28 21:24:54 +03:00
super ( ) . validate ( ns )
2020-03-18 07:00:12 +03:00
class ASTKeywordDefNode ( ASTFinalNode ) :
__slots__ = ( ' keyword ' , ' name ' , ' argdefs ' , ' code ' )
def __init__ ( self , keyword , name , argdefs , code , * * kwargs ) :
super ( ) . __init__ ( * * kwargs )
self . keyword , self . name , self . argdefs , self . code = keyword , name , argdefs , code
if ( self . name is None ) : self . name = ASTIdentifierNode ( self . code . name , lineno = self . lineno , offset = self . offset )
def __repr__ ( self ) :
return f " <KeywordDef ' { self . name } ' on line { self . lineno } , offset { self . offset } > "
def __str__ ( self ) :
return f " { self . keyword } { ' ' + S ( ' , ' ) . join ( self . argdefs ) . join ( ' () ' ) if ( isinstance ( self . keyword . keyword , DefArgsKeyword ) ) else f ' { self . name } ' if ( isinstance ( self . keyword . keyword , DefNamedKeyword ) ) else ' ' } { self . code } "
@classmethod
def build ( cls , tl ) :
super ( ) . build ( tl )
lineno , offset = tl [ 0 ] . lineno , tl [ 0 ] . offset
keyword = ASTKeywordNode . build ( tl )
if ( not isinstance ( keyword . keyword , DefKeyword ) ) : raise SlSyntaxExpectedError ( ' DefKeyword ' , keyword )
name = None
argdefs = None
if ( isinstance ( keyword . keyword , DefNamedKeyword ) ) :
name = ASTIdentifierNode . build ( tl )
elif ( isinstance ( keyword . keyword , DefArgsKeyword ) ) :
parenthesis = ASTSpecialNode . build ( tl )
if ( parenthesis . special != ' ( ' ) : raise SlSyntaxExpectedError ( " ' ( ' " , parenthesis )
argdefs = list ( )
while ( tl and tl [ 0 ] . typename != ' SPECIAL ' ) :
argdef = ASTArgdefNode . build ( tl )
if ( argdefs and argdef . value is None and argdefs [ - 1 ] . value is not None ) : raise SlSyntaxError ( f " Non-default argument { argdef } follows default argument { argdefs [ - 1 ] } " )
argdefs . append ( argdef )
if ( tl and tl [ 0 ] . typename == ' SPECIAL ' and tl [ 0 ] . token == ' , ' ) : ASTSpecialNode . build ( tl )
parenthesis = ASTSpecialNode . build ( tl )
if ( parenthesis . special != ' ) ' ) : raise SlSyntaxExpectedError ( " ' ) ' " , parenthesis )
cdef = ASTSpecialNode . build ( tl )
if ( cdef . special != ' { ' ) : raise SlSyntaxExpectedError ( ' { ' , cdef )
code = ( yield from ASTCodeNode . build ( f " < { keyword } > " ) )
return cls ( keyword , name , argdefs , code , lineno = lineno , offset = offset )
def validate ( self , ns ) :
super ( ) . validate ( ns )
code_ns = ns . derive ( self . code . name )
if ( isinstance ( self . keyword . keyword , DefArgsKeyword ) ) :
for i in self . argdefs :
code_ns . define ( i , redefine = True )
self . code . validate ( code_ns )
def optimize ( self , ns ) :
super ( ) . optimize ( ns )
code_ns = ns . derive ( self . code . name )
self . code . optimize ( code_ns )
2019-08-28 21:24:54 +03:00
class ASTAssignvalNode ( ASTNode ) :
def validate ( self , ns ) :
super ( ) . validate ( ns )
if ( self . name . identifier not in ns ) : raise SlValidationNotDefinedError ( self . name , scope = ns . scope )
2020-03-18 07:00:12 +03:00
vartype = Signature . build ( self . name , ns )
2019-08-28 21:24:54 +03:00
if ( self . value is not None ) :
valtype = Signature . build ( self . value , ns )
2020-03-18 07:00:12 +03:00
if ( valtype != vartype and vartype != valtype ) : raise SlValidationError ( f " Assignment of value ' { self . value } ' of type ' { valtype } ' to variable ' { self . name } ' of type ' { vartype } ' " , self , scope = ns . scope )
2019-08-28 21:24:54 +03:00
class ASTVardefNode ( ASTFinalNode , ASTAssignvalNode , ASTDefinitionNode ) :
__slots__ = ( ' type ' , ' name ' , ' value ' )
def __init__ ( self , type , name , value , * * kwargs ) :
super ( ) . __init__ ( * * kwargs )
self . type , self . name , self . value = type , name , value
def __str__ ( self ) :
return f " { self . type } { self . name } { f ' = { self . value } ' if ( self . value is not None ) else ' ' } "
@classmethod
def build ( cls , tl ) :
super ( ) . build ( tl )
lineno , offset = tl [ 0 ] . lineno , tl [ 0 ] . offset
type = ASTTypedefNode . build ( tl )
name = ASTIdentifierNode . build ( tl )
2020-03-18 07:00:12 +03:00
assignment = None
value = None
if ( tl and tl [ 0 ] . typename == ' SPECIAL ' ) :
assignment = ASTSpecialNode . build ( tl )
if ( assignment . special != ' = ' ) : raise SlSyntaxExpectedError ( ' assignment ' , assignment )
value = ASTExprNode . build ( tl )
2019-08-28 21:24:54 +03:00
return cls ( type , name , value , lineno = lineno , offset = offset )
def validate ( self , ns ) :
if ( self . type . type . identifier == ' auto ' ) : self . type . type . identifier = Signature . build ( self . value , ns ) . typename
2020-03-18 07:00:12 +03:00
ns . define ( self )
2019-08-28 21:24:54 +03:00
super ( ) . validate ( ns )
def optimize ( self , ns ) :
super ( ) . optimize ( ns )
2020-03-18 07:00:12 +03:00
#if (Signature.build(self.name, ns).modifiers.const): self.flags.optimized_out = True # TODO
2019-08-28 21:24:54 +03:00
class ASTAssignmentNode ( ASTFinalNode , ASTAssignvalNode ) :
2020-03-18 07:00:12 +03:00
__slots__ = ( ' name ' , ' isattr ' , ' assignment ' , ' inplace_operator ' , ' value ' )
2019-08-28 21:24:54 +03:00
2020-03-18 07:00:12 +03:00
def __init__ ( self , name , isattr , assignment , inplace_operator , value , * * kwargs ) :
2019-08-28 21:24:54 +03:00
super ( ) . __init__ ( * * kwargs )
2020-03-18 07:00:12 +03:00
self . name , self . isattr , self . assignment , self . inplace_operator , self . value = name , isattr , assignment , inplace_operator , value
2019-08-28 21:24:54 +03:00
def __str__ ( self ) :
2020-03-18 07:00:12 +03:00
return f " { ' . ' * self . isattr } { self . name } { self . inplace_operator or ' ' } = { self . value } "
2019-08-28 21:24:54 +03:00
@classmethod
def build ( cls , tl ) :
super ( ) . build ( tl )
lineno , offset = tl [ 0 ] . lineno , tl [ 0 ] . offset
2020-03-18 07:00:12 +03:00
isattr = bool ( )
if ( tl and tl [ 0 ] . typename == ' SPECIAL ' and tl [ 0 ] . token == ' . ' ) : ASTSpecialNode . build ( tl ) ; isattr = True
2019-08-28 21:24:54 +03:00
name = ASTIdentifierNode . build ( tl )
2020-03-18 07:00:12 +03:00
inplace_operator = None
if ( tl and tl [ 0 ] . typename == ' OPERATOR ' ) :
inplace_operator = ASTOperatorNode . build ( tl )
if ( not isinstance ( inplace_operator . operator , BinaryOperator ) ) : raise SlSyntaxExpectedError ( ' BinaryOperator ' , inplace_operator )
assignment = ASTSpecialNode . build ( tl )
if ( assignment . special not in ( ' = ' , ' := ' ) ) : raise SlSyntaxExpectedError ( ' assignment ' , assignment )
value = ASTExprNode . build ( tl )
return cls ( name , isattr , assignment , inplace_operator , value , lineno = lineno , offset = offset )
def validate ( self , ns ) :
valtype = Signature . build ( self . value , ns )
if ( self . assignment . special == ' := ' ) : ns . define ( self . name , valtype , redefine = True )
if ( self . isattr ) : return # TODO
super ( ) . validate ( ns )
vartype = Signature . build ( self . name , ns )
if ( vartype . modifiers . const ) : raise SlValidationError ( f " Assignment to const ' { self . name } ' " , self , scope = ns . scope )
class ASTUnpackAssignmentNode ( ASTFinalNode , ASTAssignvalNode ) :
__slots__ = ( ' names ' , ' assignment ' , ' inplace_operator ' , ' value ' )
def __init__ ( self , names , assignment , inplace_operator , value , * * kwargs ) :
super ( ) . __init__ ( * * kwargs )
self . names , self . assignment , self . inplace_operator , self . value = names , assignment , inplace_operator , value
def __str__ ( self ) :
return f " { S ( ' , ' ) . join ( self . names ) } { self . inplace_operator or ' ' } = { self . value } "
@classmethod
def build ( cls , tl ) :
super ( ) . build ( tl )
lineno , offset = tl [ 0 ] . lineno , tl [ 0 ] . offset
names = list ( )
while ( tl and tl [ 0 ] . typename != ' SPECIAL ' ) :
names . append ( ASTIdentifierNode . build ( tl ) )
if ( tl and tl [ 0 ] . typename == ' SPECIAL ' and tl [ 0 ] . token == ' , ' ) : ASTSpecialNode . build ( tl )
2019-08-28 21:24:54 +03:00
inplace_operator = ASTOperatorNode . build ( tl ) if ( tl and tl [ 0 ] . typename == ' OPERATOR ' ) else None
if ( inplace_operator is not None and not isinstance ( inplace_operator . operator , BinaryOperator ) ) : raise SlSyntaxExpectedError ( ' BinaryOperator ' , inplace_operator )
assignment = ASTSpecialNode . build ( tl )
2020-03-18 07:00:12 +03:00
if ( assignment . special not in ( ' = ' , ' := ' ) ) : raise SlSyntaxExpectedError ( ' assignment ' , assignment )
2019-08-28 21:24:54 +03:00
value = ASTExprNode . build ( tl )
2020-03-18 07:00:12 +03:00
return cls ( names , assignment , inplace_operator , value , lineno = lineno , offset = offset )
2019-08-28 21:24:54 +03:00
def validate ( self , ns ) :
valtype = Signature . build ( self . value , ns )
2020-03-18 07:00:12 +03:00
if ( self . assignment . special == ' := ' ) :
for name , type in zip ( self . names , valtype . valtypes ) :
ns . define ( name , type , redefine = True )
vartypes = tuple ( Signature . build ( i , ns ) for i in self . names )
for name , vartype in zip ( self . names , vartypes ) :
if ( vartype . modifiers . const ) : raise SlValidationError ( f " Assignment to const ' { name } ' " , self , scope = ns . scope )
if ( vartypes != valtype . valtypes ) : raise SlValidationError ( f " Unpacking assignment of ' { valtype } ' to variables of types { vartypes } " , self , scope = ns . scope )
class ASTAttrsetNode ( ASTFinalNode ) :
__slots__ = ( ' value ' , ' assignment ' )
def __init__ ( self , value , assignment , * * kwargs ) :
super ( ) . __init__ ( * * kwargs )
self . value , self . assignment = value , assignment
def __str__ ( self ) :
return f " { self . value } { self . assignment } "
@classmethod
def build ( cls , tl ) :
super ( ) . build ( tl )
lineno , offset = tl [ 0 ] . lineno , tl [ 0 ] . offset
value = ASTIdentifierNode . build ( tl )
assignment = ASTAssignmentNode . build ( tl )
if ( not assignment . isattr ) : raise SlSyntaxExpectedError ( ' attrset ' , assignment )
return cls ( value , assignment , lineno = lineno , offset = offset )
def validate ( self , ns ) :
assert ( self . assignment . isattr )
super ( ) . validate ( ns )
# TODO: attr check
#valsig = Signature.build(self.value, ns)
#if ((self.optype.special, self.attr.identifier) not in valsig.attrops): raise SlValidationError(f"'{valsig}' does not support attribute operation '{self.optype}' with attr '{self.attr}'", self, scope=ns.scope)
2019-08-28 21:24:54 +03:00
class ASTFunccallNode ( ASTFinalNode ) :
__slots__ = ( ' callable ' , ' callargs ' , ' callkwargs ' )
def __init__ ( self , callable , callargs , callkwargs , * * kwargs ) :
super ( ) . __init__ ( * * kwargs )
self . callable , self . callargs , self . callkwargs = callable , callargs , callkwargs
def __str__ ( self ) :
return f " { str ( self . callable ) . join ( ' () ' ) if ( isinstance ( self . callable , ASTValueNode ) and isinstance ( self . callable . value , ( ASTFunccallNode , ASTLambdaNode ) ) ) else self . callable } ( { self . callargs } { ' , ' if ( str ( self . callargs ) and str ( self . callkwargs ) ) else ' ' } { self . callkwargs } ) "
@classmethod
def build ( cls , tl ) :
super ( ) . build ( tl )
lineno , offset = tl [ 0 ] . lineno , tl [ 0 ] . offset
callable = ASTExprNode . build ( tl , fcall = True )
parenthesis = ASTSpecialNode . build ( tl )
if ( parenthesis . special != ' ( ' ) : raise SlSyntaxExpectedError ( " ' ( ' " , parenthesis )
callargs = ASTCallargsNode . build ( tl )
callkwargs = ASTCallkwargsNode . build ( tl )
parenthesis = ASTSpecialNode . build ( tl )
if ( parenthesis . special != ' ) ' ) : raise SlSyntaxExpectedError ( " ' ) ' " , parenthesis )
return cls ( callable , callargs , callkwargs , lineno = lineno , offset = offset )
def validate ( self , ns ) :
super ( ) . validate ( ns )
fsig = Signature . build ( self . callable , ns )
2020-03-18 07:00:12 +03:00
if ( not isinstance ( fsig , Callable ) ) : raise SlValidationError ( f " ' { self . callable } ' of type ' { fsig } ' is not callable " , self . callable , scope = ns . scope )
2019-08-28 21:24:54 +03:00
callargssig = CallArguments . build ( self , ns ) # TODO: starargs
if ( callargssig not in fsig . call ) : raise SlValidationError ( f " Parameters ' ( { callargssig } ) ' don ' t match any of ' { self . callable } ' signatures: \n { S ( fsig . callargssigstr ) . indent ( ) } \n " , self , scope = ns . scope )
class ASTConditionalNode ( ASTFinalNode ) :
__slots__ = ( ' condition ' , ' code ' )
def __init__ ( self , condition , code , * * kwargs ) :
super ( ) . __init__ ( * * kwargs )
self . condition , self . code = condition , code
2020-03-18 07:00:12 +03:00
def __repr__ ( self ) :
return f " <Conditional if ' { self . condition } ' on line { self . lineno } , offset { self . offset } > "
2019-08-28 21:24:54 +03:00
def __str__ ( self ) :
2020-03-18 07:00:12 +03:00
return f " if { self . condition } { self . code } "
2019-08-28 21:24:54 +03:00
@classmethod
def build ( cls , tl ) :
super ( ) . build ( tl )
lineno , offset = tl [ 0 ] . lineno , tl [ 0 ] . offset
if_ = ASTKeywordNode . build ( tl )
if ( if_ . keyword != ' if ' ) : raise SlSyntaxExpectedError ( " ' if ' " , if_ )
condition = ASTExprNode . build ( tl )
code = ( yield from ASTBlockNode . build ( tl ) )
return cls ( condition , code , lineno = lineno , offset = offset )
class ASTForLoopNode ( ASTFinalNode ) :
__slots__ = ( ' name ' , ' iterable ' , ' code ' )
def __init__ ( self , name , iterable , code , * * kwargs ) :
super ( ) . __init__ ( * * kwargs )
self . name , self . iterable , self . code = name , iterable , code
2020-03-18 07:00:12 +03:00
def __repr__ ( self ) :
return f " <ForLoop ' { self . name } ' in ' { self . iterable } ' on line { self . lineno } , offset { self . offset } > "
2019-08-28 21:24:54 +03:00
def __str__ ( self ) :
2020-03-18 07:00:12 +03:00
return f " for { self . name } in { self . iterable } { self . code } "
2019-08-28 21:24:54 +03:00
@classmethod
def build ( cls , tl ) :
super ( ) . build ( tl )
lineno , offset = tl [ 0 ] . lineno , tl [ 0 ] . offset
for_ = ASTKeywordNode . build ( tl )
if ( for_ . keyword != ' for ' ) : raise SlSyntaxExpectedError ( " ' for ' " , for_ )
name = ASTIdentifierNode . build ( tl )
2020-03-18 07:00:12 +03:00
in_ = ASTOperatorNode . build ( tl )
if ( in_ . operator != ' in ' ) : raise SlSyntaxExpectedError ( " ' in ' " , in_ )
2019-08-28 21:24:54 +03:00
iterable = ASTExprNode . build ( tl )
code = ( yield from ASTBlockNode . build ( tl ) )
return cls ( name , iterable , code , lineno = lineno , offset = offset )
2020-03-18 07:00:12 +03:00
def validate ( self , ns ) :
super ( ) . validate ( ns )
# TODO: validate iterability
ns . define ( self . name , Signature . build ( self . iterable , ns ) . valtype )
ns . weaken ( self . name )
2019-08-28 21:24:54 +03:00
class ASTWhileLoopNode ( ASTFinalNode ) :
__slots__ = ( ' condition ' , ' code ' )
def __init__ ( self , condition , code , * * kwargs ) :
super ( ) . __init__ ( * * kwargs )
self . condition , self . code = condition , code
2020-03-18 07:00:12 +03:00
def __repr__ ( self ) :
return f " <WhileLoop while ' { self . condition } ' on line { self . lineno } , offset { self . offset } > "
2019-08-28 21:24:54 +03:00
def __str__ ( self ) :
2020-03-18 07:00:12 +03:00
return f " while { self . condition } { self . code } "
2019-08-28 21:24:54 +03:00
@classmethod
def build ( cls , tl ) :
super ( ) . build ( tl )
lineno , offset = tl [ 0 ] . lineno , tl [ 0 ] . offset
while_ = ASTKeywordNode . build ( tl )
if ( while_ . keyword != ' while ' ) : raise SlSyntaxExpectedError ( " ' while ' " , while_ )
condition = ASTExprNode . build ( tl )
code = ( yield from ASTBlockNode . build ( tl ) )
return cls ( condition , code , lineno = lineno , offset = offset )
class ASTElseClauseNode ( ASTFinalNode ) :
__slots__ = ( ' code ' , )
def __init__ ( self , code , * * kwargs ) :
super ( ) . __init__ ( * * kwargs )
self . code = code
2020-03-18 07:00:12 +03:00
def __repr__ ( self ) :
return f " <ElseClause on line { self . lineno } , offset { self . offset } > "
2019-08-28 21:24:54 +03:00
def __str__ ( self ) :
return f " else { self . code } "
@classmethod
def build ( cls , tl ) :
super ( ) . build ( tl )
lineno , offset = tl [ 0 ] . lineno , tl [ 0 ] . offset
else_ = ASTKeywordNode . build ( tl )
if ( else_ . keyword != ' else ' ) : raise SlSyntaxExpectedError ( " ' else ' " , else_ )
code = ( yield from ASTBlockNode . build ( tl ) )
return cls ( code , lineno = lineno , offset = offset )
def build_ast ( code , name = None , * , interactive = False ) :
code = copy . deepcopy ( code )
root = ASTRootNode . build ( name )
code_stack = [ ( root , next ( root ) ) ]
next ( root )
final_nodes = ASTFinalNode . __subclasses__ ( )
if ( interactive ) : final_nodes + = ( ASTExprNode , )
2020-03-18 07:00:12 +03:00
2019-08-28 21:24:54 +03:00
for ii , tl in enumerate ( code ) :
if ( not tl ) : continue
lineno , offset = tl [ 0 ] . lineno , tl [ 0 ] . offset
err = set ( )
for i in final_nodes :
try :
c = tl . copy ( )
r = i . build ( c )
if ( inspect . isgenerator ( r ) ) :
code_stack . append ( ( r , next ( r ) ) )
try : r = next ( r )
except StopIteration as ex : code_stack . pop ( ) ; r = ex . args [ 0 ]
else :
2020-03-18 07:00:12 +03:00
assert ( r is None )
2019-08-28 21:24:54 +03:00
if ( c ) :
if ( c [ - 1 ] . typename == ' SPECIAL ' and c [ - 1 ] . token == ' } ' ) : code . insert ( ii + 1 , [ c . pop ( ) ] )
code . insert ( ii + 1 , c )
err . clear ( )
break
2020-03-18 07:00:12 +03:00
assert ( r is not None )
2019-08-28 21:24:54 +03:00
if ( c ) : raise SlSyntaxExpectedNothingError ( c [ 0 ] )
except SlSyntaxEmpty : err . clear ( ) ; break
2020-03-18 07:00:12 +03:00
except SlSyntaxNoToken : err . add ( SlSyntaxExpectedMoreTokensError ( i . __name__ [ 3 : - 4 ] , lineno = lineno , offset = - 2 ) )
except SlSyntaxMultiExpectedError as ex : pass #err.add(ex) # TODO FIXME
except SlSyntaxExpectedError as ex : ex . usage = i . __name__ [ 3 : - 4 ] ; err . add ( ex )
2019-08-28 21:24:54 +03:00
else : code_stack [ - 1 ] [ 0 ] . send ( r ) ; err . clear ( ) ; break
else :
if ( len ( code_stack ) > 1 and tl and tl [ 0 ] . typename == ' SPECIAL ' and tl [ 0 ] . token == ' } ' ) :
if ( tl [ 1 : ] ) : code . insert ( ii + 1 , tl [ 1 : ] )
try : next ( code_stack . pop ( ) [ 0 ] )
except StopIteration as ex : code_stack [ - 1 ] [ 0 ] . send ( ex . args [ 0 ] ) ; err . clear ( )
else : raise WTFException ( )
elif ( not err ) : raise SlSyntaxError ( " Unknown structure " , lineno = lineno , offset = offset , length = 0 , scope = ' . ' . join ( i [ 1 ] for i in code_stack if i [ 1 ] ) )
2020-03-18 07:00:12 +03:00
if ( err ) : raise SlSyntaxMultiExpectedError . from_list ( err , scope = ' . ' . join ( i [ 1 ] for i in code_stack if i [ 1 ] ) if ( code_stack [ 0 ] [ 1 ] is not None ) else None )
2019-08-28 21:24:54 +03:00
2020-03-18 07:00:12 +03:00
assert ( len ( code_stack ) == 1 )
2019-08-28 21:24:54 +03:00
try : next ( code_stack . pop ( ) [ 0 ] )
except StopIteration as ex : return ex . args [ 0 ]
def walk_ast_nodes ( node ) :
if ( isiterable ( node ) and not isinstance ( node , str ) ) :
for i in node : yield from walk_ast_nodes ( i )
if ( not isinstance ( node , ASTNode ) ) : return
yield node
for i in node . __slots__ : yield from walk_ast_nodes ( getattr ( node , i ) )
class _SignatureBase : pass
class Signature ( _SignatureBase ) :
__slots__ = ( ' typename ' , ' modifiers ' )
operators = dict ( )
@init_defaults
@autocast
2020-03-18 07:00:12 +03:00
def __init__ ( self , * , typename , modifiers : paramset ) :
self . typename , self . modifiers = typename , modifiers
2019-08-28 21:24:54 +03:00
@property
def __reprname__ ( self ) :
2020-03-18 07:00:12 +03:00
return self . __class__ . __name__
2019-08-28 21:24:54 +03:00
def __repr__ ( self ) :
2020-03-18 07:00:12 +03:00
return f " < { self . __reprname__ } ' { self . name } ' > "
2019-08-28 21:24:54 +03:00
def __eq__ ( self , x ) :
2020-03-18 07:00:12 +03:00
return ( x is not None and self . typename == x . typename )
2019-08-28 21:24:54 +03:00
def __hash__ ( self ) :
return hash ( tuple ( getattr ( self , i ) for i in self . __slots__ ) )
2020-03-18 07:00:12 +03:00
@property
def name ( self ) :
return self . typename
@staticitemget
def itemget ( x ) :
raise KeyError ( )
@staticitemget
def attrops ( optype , attr ) :
raise KeyError ( )
2019-08-28 21:24:54 +03:00
@classmethod
@dispatch
def build ( cls , x : ASTArgdefNode , ns ) : # TODO: modifiers
return cls . build ( x . type , ns )
@classmethod
@dispatch
2020-03-18 07:00:12 +03:00
def build ( cls , x : ASTAssignvalNode , ns ) :
2019-08-28 21:24:54 +03:00
r = cls . build ( x . type , ns )
2020-03-18 07:00:12 +03:00
#ns.signatures[x.name.identifier] = r
2019-08-28 21:24:54 +03:00
if ( x . value is not None ) : ns . values [ x . name ] = x . value if ( not r . modifiers . volatile and r . modifiers . const ) else None
return r
@classmethod
@dispatch
def build ( cls , x : ASTTypedefNode , ns ) :
2020-03-18 07:00:12 +03:00
r = cls . build ( x . type , ns )
r . modifiers . update ( x . modifiers )
return r
2019-08-28 21:24:54 +03:00
@classmethod
@dispatch
def build ( cls , x : ASTValueNode , ns ) :
return cls . build ( x . value , ns )
@classmethod
@dispatch
def build ( cls , x : ASTLiteralNode , ns ) :
return builtin_names [ literal_type ( x . literal ) . __name__ ] ( )
@classmethod
@dispatch
def build ( cls , x : ASTIdentifierNode , ns ) :
if ( x . identifier in builtin_names ) : return builtin_names [ x . identifier ] ( )
if ( x . identifier not in ns ) : raise SlValidationNotDefinedError ( x , scope = ns . scope )
return ns . signatures [ x . identifier ]
2020-03-18 07:00:12 +03:00
@classmethod
@dispatch
def build ( cls , x : ASTListNode , ns ) :
return Collection ( keytype = stdlib . int , valtype = Signature . build ( x . type , ns ) )
@classmethod
@dispatch
def build ( cls , x : ASTTupleNode , ns ) :
return MultiCollection ( keytype = stdlib . int , valtypes = tuple ( Signature . build ( t if ( t is not None ) else v , ns ) for t , v in zip ( x . types , x . values ) ) )
2019-08-28 21:24:54 +03:00
@classmethod
@dispatch
def build ( cls , x : ASTFunccallNode , ns ) :
return cls . build ( x . callable , ns ) . call [ CallArguments . build ( x , ns ) ]
@classmethod
@dispatch
def build ( cls , x : ASTFuncdefNode , ns ) :
return Function . build ( x , ns )
2020-03-18 07:00:12 +03:00
@classmethod
@dispatch
def build ( cls , x : ASTClassdefNode , ns ) :
return Class . build ( x , ns )
@classmethod
@dispatch
def build ( cls , x : ASTKeywordDefNode , ns ) :
return KeywordDef . build ( x , ns )
2019-08-28 21:24:54 +03:00
@classmethod
@dispatch
def build ( cls , x : ASTItemgetNode , ns ) :
2020-03-18 07:00:12 +03:00
return cls . build ( x . value , ns ) . itemget [ cls . build ( x . key , ns ) , x . key ]
2019-08-28 21:24:54 +03:00
@classmethod
@dispatch
def build ( cls , x : ASTAttrgetNode , ns ) :
2020-03-18 07:00:12 +03:00
return cls . build ( x . value , ns ) . attrops [ x . optype . special , x . attr . identifier ]
2019-08-28 21:24:54 +03:00
@classmethod
@dispatch
def build ( cls , x : ASTUnaryExprNode , ns ) :
return cls . build ( x . value , ns ) . operators [ x . operator . operator ]
@classmethod
@dispatch
def build ( cls , x : ASTBinaryExprNode , ns ) :
return cls . build ( x . lvalue , ns ) . operators [ x . operator . operator , cls . build ( x . rvalue , ns ) ]
@classmethod
@dispatch
def build ( cls , x : _SignatureBase , ns ) :
return x
2020-03-18 07:00:12 +03:00
class Callable ( Signature , abc . ABC ) :
__slots__ = ( ' call ' , )
@abc.abstractproperty
def callargssigstr ( self ) :
pass
class Function ( Callable ) :
__slots__ = ( ' name ' , )
2019-08-28 21:24:54 +03:00
def __init__ ( self , * , name , * * kwargs ) :
super ( ) . __init__ ( typename = ' function ' , * * kwargs )
self . name = name
self . call = listmap ( )
@property
def callargssigstr ( self ) :
return ' \n ' . join ( f " { self . name } ( { args } ) " for args , ret in self . call . items ( ) )
2020-03-18 07:00:12 +03:00
@staticitemget
def attrops ( optype , attr ) :
if ( optype == ' . ' ) :
if ( attr == ' map ' ) : return stdlib . _map ( )
raise KeyError ( )
2019-08-28 21:24:54 +03:00
@classmethod
@dispatch
def build ( cls , x : ASTFuncdefNode , ns , * , redefine = False ) :
name = x . name . identifier
if ( name not in ns ) : ns . signatures [ name ] = cls ( name = name )
callargssig = CallArguments ( args = tuple ( Signature . build ( i , ns ) for i in x . argdefs ) )
2020-03-18 07:00:12 +03:00
if ( not redefine and callargssig in ns . signatures [ name ] . call ) : raise SlValidationRedefinedError ( x . name , ns . signatures [ name ] . call [ callargssig ] , scope = ns . scope )
2019-08-28 21:24:54 +03:00
ns . signatures [ name ] . call [ callargssig ] = Signature . build ( x . type , ns )
return ns . signatures [ name ]
2020-03-18 07:00:12 +03:00
class KeywordDef ( Callable ) :
__slots__ = ( ' name ' , )
def __init__ ( self , * , name , * * kwargs ) :
super ( ) . __init__ ( typename = ' keyworddef ' , * * kwargs )
self . name = name
self . call = listmap ( )
@property
def callargssigstr ( self ) :
return ' \n ' . join ( f " { self . name } ( { args } ) " for args , ret in self . call . items ( ) )
@classmethod
@dispatch
def build ( cls , x : ASTKeywordDefNode , ns , * , redefine = False ) :
name = x . name . identifier
if ( name not in ns ) : ns . signatures [ name ] = cls ( name = name )
callargssig = CallArguments ( args = tuple ( Signature . build ( i , ns ) for i in x . argdefs or ( ) ) )
if ( not redefine and callargssig in ns . signatures [ name ] . call ) : raise SlValidationRedefinedError ( x . name , ns . signatures [ name ] . call [ callargssig ] , scope = ns . scope )
ns . signatures [ name ] . call [ callargssig ] = stdlib . void
return ns . signatures [ name ]
class Object ( Signature ) : pass
class Collection ( Object ) :
__slots__ = ( ' keytype ' , ' valtype ' )
def __init__ ( self , * , keytype , valtype ) :
self . keytype , self . valtype = keytype , valtype
@property
def typename ( self ) :
return self . valtype . typename
@itemget
@instantiate
def itemget ( self , keysig , key ) :
if ( keysig == self . keytype ) : return self . valtype
raise KeyError ( )
class MultiCollection ( Collection ) :
__slots__ = ( ' valtypes ' , ' typename ' )
def __init__ ( self , * , keytype , valtypes ) :
Object . __init__ ( self , typename = ' tuple ' )
self . keytype , self . valtypes = keytype , valtypes
@itemget
@instantiate
def itemget ( self , keysig , key ) :
if ( keysig == self . keytype ) : return self . valtypes [ int ( key ) ]
raise KeyError ( )
class Class ( Object , Callable ) :
__slots__ = ( ' name ' , ' scope ' , ' constructor ' )
def __init__ ( self , * , name , scope , * * kwargs ) :
super ( ) . __init__ ( typename = ' class ' , * * kwargs )
self . name , self . scope = name , scope
self . constructor = listmap ( )
def __str__ ( self ) :
return self . name
@property
def callargssigstr ( self ) :
return ' \n ' . join ( f " { self . name } ( { args } ) " for args , ret in self . call . items ( ) )
@itemget
def call ( self , callargssig ) :
return self . constructor [ callargssig ]
@itemget
def attrops ( self , optype , attr ) :
if ( optype == ' . ' ) :
return self . scope . signatures [ attr ]
raise KeyError ( )
@classmethod
@dispatch
def build ( cls , x : ASTClassdefNode , ns , * , redefine = False ) :
name = x . name . identifier
if ( not redefine and name in ns ) : raise SlValidationRedefinedError ( x . name , ns . signatures [ name ] , scope = ns . scope )
else : ns . signatures [ name ] = cls ( name = name , scope = ns . derive ( x . code . name ) )
for i in x . code . nodes :
if ( isinstance ( i , ASTKeywordDefNode ) and i . keyword . keyword == ' constr ' ) :
callargssig = CallArguments ( args = tuple ( Signature . build ( j , ns ) for j in i . argdefs or ( ) ) )
if ( not redefine and callargssig in ns . signatures [ name ] . constructor ) : raise SlValidationRedefinedError ( x . name , ns . signatures [ name ] . constructor [ callargssig ] , scope = ns . scope )
ns . signatures [ name ] . constructor [ callargssig ] = ns . signatures [ name ]
return ns . signatures [ name ]
2019-08-28 21:24:54 +03:00
class CallArguments :
__slots__ = ( ' args ' , ' starargs ' , ' kwargs ' , ' starkwargs ' )
@init_defaults
@autocast
def __init__ ( self , * , args : tuple , starargs : tuple , kwargs : tuple , starkwargs : tuple ) :
self . args , self . starargs , self . kwargs , self . starkwargs = args , starargs , kwargs , starkwargs
def __repr__ ( self ) :
return S ( ' , ' ) . join ( ( * self . args , * ( ' * ' + i for i in self . starargs ) , * ( f " { v } { k } " for k , v in self . kwargs ) , * ( ' ** ' + i for i in self . starkwargs ) ) )
def __eq__ ( self , x ) :
return all ( getattr ( self , i ) == getattr ( x , i ) for i in self . __slots__ )
def __hash__ ( self ) :
return hash ( tuple ( getattr ( self , i ) for i in self . __slots__ ) )
@property
def nargs ( self ) :
return sum ( len ( getattr ( self , i ) ) for i in self . __slots__ )
@classmethod
@dispatch
def build ( cls , x : ASTFunccallNode , ns ) : # TODO: starargs
return cls ( args = tuple ( Signature . build ( i , ns ) for i in x . callargs . callargs ) , kwargs = tuple ( ( k , Signature . build ( v , ns ) ) for k , v in x . callkwargs . callkwargs ) )
class Namespace :
2020-03-18 07:00:12 +03:00
__slots__ = ( ' signatures ' , ' scope ' , ' values ' , ' weak ' , ' refcount ' , ' flags ' , ' warnclasses ' )
2019-08-28 21:24:54 +03:00
class _Values :
@init_defaults
def __init__ ( self , values : dict ) :
self . values = values
def __contains__ ( self , x ) :
try : self [ x ]
except ( DispatchError , KeyError ) : return False
else : return True
@dispatch
def __getitem__ ( self , x : ASTLiteralNode ) :
return eval ( str ( x . literal ) )
@dispatch
def __getitem__ ( self , x : ASTValueNode ) :
return self [ x . value ]
@dispatch
def __getitem__ ( self , x : ASTIdentifierNode ) :
2020-03-18 07:00:12 +03:00
return self [ x . identifier ]
2019-08-28 21:24:54 +03:00
@dispatch
def __getitem__ ( self , x : str ) :
return self . values [ x ]
@dispatch
def __setitem__ ( self , k , v : ASTValueNode ) :
self [ k ] = v . value
@dispatch
def __setitem__ ( self , x : ASTIdentifierNode , v : ASTLiteralNode ) :
self . values [ x . identifier ] = eval ( str ( v . literal ) )
@dispatch
def __setitem__ ( self , x : ASTIdentifierNode , v : NoneType ) :
pass
@dispatch
def __setitem__ ( self , k : str , v ) :
self . values [ k ] = v
@dispatch
def __setitem__ ( self , k , v ) :
pass
@dispatch
def __delitem__ ( self , x : ASTValueNode ) :
del self [ x . value ]
@dispatch
def __delitem__ ( self , x : ASTIdentifierNode ) :
2020-03-18 07:00:12 +03:00
del self [ x . identifier ]
@dispatch
def __delitem__ ( self , x : str ) :
del self . values [ x ]
def get ( self , x ) :
try : return self [ x ]
except ( DispatchError , KeyError ) : return None
def items ( self ) :
return self . values . items ( )
2019-08-28 21:24:54 +03:00
def copy ( self ) :
return self . __class__ ( values = self . values . copy ( ) )
@init_defaults
2020-03-18 07:00:12 +03:00
def __init__ ( self , scope , * , signatures : dict , values : _Values , weak : set , refcount : lambda : Sdict ( int ) , warnclasses : paramset , * * kwargs ) :
self . scope , self . signatures , self . values , self . weak , self . refcount , self . warnclasses = scope , signatures , values , weak , refcount , warnclasses
2019-08-28 21:24:54 +03:00
self . flags = paramset ( k for k , v in kwargs . items ( ) if v )
2020-03-18 07:00:12 +03:00
#self.derive.clear_cache() # TODO FIXME (also cachedproperty)
2019-08-28 21:24:54 +03:00
def __repr__ ( self ) :
return f " <Namespace of scope ' { self . scope } ' > "
def __contains__ ( self , x ) :
2020-03-18 07:00:12 +03:00
return ( x in builtin_names or x in self . signatures )
2019-08-28 21:24:54 +03:00
@cachedfunction
def derive ( self , scope ) :
2020-03-18 07:00:12 +03:00
return Namespace ( signatures = self . signatures . copy ( ) , values = self . values . copy ( ) , weak = self . weak . copy ( ) , scope = self . scope + ' . ' + scope )
2019-08-28 21:24:54 +03:00
2020-03-18 07:00:12 +03:00
#@dispatch
#def define(self, x: ASTIdentifierNode):
# if (x.identifier in self and x.identifier not in self.weak): raise SlValidationRedefinedError(x, self.signatures[x.identifier], scope=self.scope)
# self.values[x] = None
# self.signatures[x.identifier] = None
# self.weak.discard(x.identifier)
2019-08-28 21:24:54 +03:00
@dispatch
def define ( self , x : ASTFuncdefNode ) :
return self . define ( x , redefine = True )
2020-03-18 07:00:12 +03:00
#@dispatch
#def define(self, x: str, *, value=None):
# assert (x not in self)
# self.values[x] = value
# self.signatures[x] = None
2019-08-28 21:24:54 +03:00
@dispatch
2020-03-18 07:00:12 +03:00
def define ( self , x : lambda x : hasattr ( x , ' name ' ) , sig = None , * , redefine = False ) :
2019-08-28 21:24:54 +03:00
if ( redefine ) :
try : del self . values [ x . name ]
except KeyError : pass
2020-03-18 07:00:12 +03:00
try : del self . signatures [ x . name . identifier ]
except KeyError : pass
self . define ( x . name , sig if ( sig is not None ) else Signature . build ( x , self ) , redefine = redefine )
@dispatch
def define ( self , x : ASTIdentifierNode , sig , * , redefine = False ) :
if ( redefine ) :
try : del self . values [ x ]
except KeyError : pass
try : del self . signatures [ x . identifier ]
except KeyError : pass
elif ( x . identifier in self and x . identifier not in self . weak ) : raise SlValidationRedefinedError ( x , self . signatures [ x . identifier ] , scope = self . scope )
self . signatures [ x . identifier ] = sig
self . weak . discard ( x . identifier )
@dispatch
def weaken ( self , x : ASTIdentifierNode ) :
self . weak . add ( x . identifier )
@dispatch
def delete ( self , x : ASTIdentifierNode ) :
ok = bool ( )
try : del self . values [ x ]
except KeyError : pass
else : ok = True
try : del self . signatures [ x . identifier ]
except KeyError : pass
else : ok = True
self . weak . discard ( x . identifier )
if ( not ok ) : raise SlValidationNotDefinedError ( x , scope = self . scope )
2019-08-28 21:24:54 +03:00
from . import stdlib
from . stdlib import builtin_names
def validate_ast ( ast , ns = None ) :
Namespace . derive . clear_cache ( )
return ast . code . validate ( ns )
class SlValidationException ( Exception ) : pass
class SlValidationError ( SlValidationException ) :
__slots__ = ( ' desc ' , ' node ' , ' line ' , ' scope ' )
def __init__ ( self , desc , node , line = ' ' , * , scope = None ) :
self . desc , self . node , self . line , self . scope = desc , node , line , scope
def __str__ ( self ) :
l , line = lstripcount ( self . line . partition ( ' \n ' ) [ 0 ] . replace ( ' \t ' , ' ' ) , ' \t ' )
2020-03-18 07:00:12 +03:00
offset = ( self . node . offset - l ) if ( self . node . offset > = 0 ) else ( len ( line ) + self . node . offset + 1 )
2019-08-28 21:24:54 +03:00
return ( f ' \033 [2m(in { self . scope } ) \033 [0m ' if ( self . scope is not None ) else ' ' ) + f " Validation error: { self . desc } { self . at } " + ( ' : \n ' + \
2020-03-18 07:00:12 +03:00
' \033 [1m ' + line [ : offset ] + ' \033 [91m ' * ( self . node . offset > = 0 ) + line [ offset : ] + ' \033 [0m \n ' + \
' ' + ' ' * offset + ' \033 [95m^ ' + ' ~ ' * ( self . node . length - 1 ) + ' \033 [0m ' if ( line ) else ' ' ) + \
( f " \n \n \033 [1;95mCaused by: \033 [0m \n { self . __cause__ } " if ( self . __cause__ is not None ) else ' ' )
2019-08-28 21:24:54 +03:00
@property
def at ( self ) :
return f " at line { self . node . lineno } , offset { self . node . offset } "
@property
def lineno ( self ) :
return self . node . lineno
class SlValidationNotDefinedError ( SlValidationError ) :
def __init__ ( self , identifier , * * kwargs ) :
super ( ) . __init__ ( f " ' { identifier . identifier } ' is not defined " , identifier , * * kwargs )
class SlValidationRedefinedError ( SlValidationError ) :
def __init__ ( self , identifier , definition , * * kwargs ) :
super ( ) . __init__ ( f " ' { identifier } ' redefined (defined as ' { definition } ' ) " , identifier , * * kwargs ) # at lineno {definition.lineno}
def optimize_ast ( ast , ns ) : return ast . code . optimize ( ns )
2020-03-18 07:00:12 +03:00
# by Sdore, 2020