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 *
2024-01-11 02:55:07 +03:00
DEFAULT_OLEVEL = 0
TAB_SIZE = 4
2019-08-28 21:24:54 +03:00
DEBUG_PRECEDENCE = False
def warn ( class_ , msg , node , ns ) :
2024-01-11 02:55:07 +03:00
if ( ns . warnclasses and class_ not in ns . warnclasses ) : return
logexception ( Warning ( f " { msg } \033 [2m(at line { node . lineno } , offset { node . offset } ) \033 [0m \033 [8m( { class_ } ) \033 [0m " ) , raw = True , once = True )
def eval_literal ( x ) :
return eval ( literal_repr ( x ) )
def literal_repr ( x ) :
return ( str if ( isinstance ( x , ASTNode ) ) else repr ) ( x )
2019-08-28 21:24:54 +03:00
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
2024-01-11 02:55:07 +03:00
r = Slist ( Signature . build ( i , ns ) for i in l ) . uniquize ( )
r . reverse ( ) ; r = r . uniquize ( )
2019-08-28 21:24:54 +03:00
if ( not r ) : return None
2024-01-11 02:55:07 +03:00
r = [ i for i in r if not isinstance ( i , stdlib . auto ) ]
2019-08-28 21:24:54 +03:00
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
2024-01-11 02:55:07 +03:00
class ASTNode ( ABCSlots ) :
lineno : . . .
offset : . . .
flags : . . .
2019-08-28 21:24:54 +03:00
@abc.abstractmethod
@init_defaults
def __init__ ( self , * , lineno , offset , flags : paramset ) :
self . lineno , self . offset , self . flags = lineno , offset , flags
def __repr__ ( self ) :
2024-01-11 02:55:07 +03:00
return f " < { self . typename } ` { self . __str__ ( ) } ' on line { self . lineno } , offset { self . offset } > "
2019-08-28 21:24:54 +03:00
@abc.abstractmethod
def __str__ ( self ) :
2024-01-11 02:55:07 +03:00
return ' '
2019-08-28 21:24:54 +03:00
@abc.abstractclassmethod
def build ( cls , tl ) :
if ( not tl ) : raise SlSyntaxNoToken ( )
def validate ( self , ns ) :
2024-01-11 02:55:07 +03:00
for i in allslots ( self ) :
2019-08-28 21:24:54 +03:00
v = getattr ( self , i )
2024-01-11 02:55:07 +03:00
if ( isiterablenostr ( v ) ) :
2019-08-28 21:24:54 +03:00
for jj , j in enumerate ( v ) :
if ( hasattr ( j , ' validate ' ) ) :
j . validate ( ns )
2024-01-11 02:55:07 +03:00
elif ( hasattr ( v , ' validate ' ) and not isinstance ( v , ASTCodeNode ) ) :
2019-08-28 21:24:54 +03:00
v . validate ( ns )
def optimize ( self , ns ) :
2024-01-11 02:55:07 +03:00
for i in allslots ( self ) :
2019-08-28 21:24:54 +03:00
v = getattr ( self , i )
2024-01-11 02:55:07 +03:00
if ( isiterablenostr ( v ) ) :
2019-08-28 21:24:54 +03:00
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 ]
2024-01-11 02:55:07 +03:00
elif ( hasattr ( v , ' optimize ' ) and not isinstance ( v , ASTCodeNode ) ) :
2019-08-28 21:24:54 +03:00
r = v . optimize ( ns )
if ( r is not None ) : setattr ( self , i , r )
if ( getattr ( self , i ) . flags . optimized_out ) : setattr ( self , i , None )
2024-01-11 02:55:07 +03:00
self . flags . optimized = 1
2019-08-28 21:24:54 +03:00
@classproperty
def typename ( cls ) :
return cls . __name__ [ 3 : - 4 ]
@property
def length ( self ) :
2024-01-11 02:55:07 +03:00
#dlog(max((getattr(self, i) for i in allslots(self) if hasattr(getattr(self, i), 'offset')), key=lambda x: (x.lineno, x.offset)
return sum ( getattr ( self , i ) . length for i in allslots ( self ) if hasattr ( getattr ( self , i ) , ' length ' ) )
2019-08-28 21:24:54 +03:00
class ASTRootNode ( ASTNode ) :
2024-01-11 02:55:07 +03:00
code : . . .
2019-08-28 21:24:54 +03:00
def __init__ ( self , code , * * kwargs ) :
2024-01-11 02:55:07 +03:00
super ( ) . __init__ ( * * kwargs )
2019-08-28 21:24:54 +03:00
self . code = code
def __repr__ ( self ) :
return ' <Root> '
def __str__ ( self ) :
return ' <Root> '
@classmethod
def build ( cls , name = None ) :
2024-01-11 02:55:07 +03:00
code = ( yield from ASTCodeNode . build ( [ ] , name = name ) )
return cls ( code , lineno = code . lineno , offset = code . offset )
def validate ( self , ns = None ) :
if ( ns is None ) : ns = Namespace ( self . code . name )
super ( ) . validate ( ns )
self . code . validate ( ns )
return ns
def optimize ( self , ns , level = DEFAULT_OLEVEL ) :
ns . olevel = level
super ( ) . optimize ( ns )
self . code . optimize ( ns )
2019-08-28 21:24:54 +03:00
class ASTCodeNode ( ASTNode ) :
2024-01-11 02:55:07 +03:00
nodes : . . .
name : . . .
2019-08-28 21:24:54 +03:00
def __init__ ( self , nodes , * , name = ' <code> ' , * * kwargs ) :
2024-01-11 02:55:07 +03:00
super ( ) . __init__ ( * * kwargs )
2019-08-28 21:24:54 +03:00
self . nodes , self . name = nodes , name
def __repr__ ( self ) :
2024-01-11 02:55:07 +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 ) :
2024-01-11 02:55:07 +03:00
return ( S ( ' \n ' ) . join ( map ( lambda x : x . join ( ' \n \n ' ) if ( ' \n ' in x ) else x , map ( str , self . nodes ) ) ) . indent ( ) . replace ( ' \n \n \n ' , ' \n \n ' ) . strip ( ' \n ' ) . join ( ' \n \n ' ) if ( self . nodes ) else ' ' ) . join ( ' {} ' )
2019-08-28 21:24:54 +03:00
@classmethod
2024-01-11 02:55:07 +03:00
def build ( cls , tl , * , name ) :
if ( tl ) :
cdef = ASTSpecialNode . build ( tl )
if ( cdef . special != ' { ' ) : raise SlSyntaxExpectedError ( " ' { ' " , cdef )
lineno , offset = cdef . lineno , cdef . offset
else : lineno = offset = None
2019-08-28 21:24:54 +03:00
yield name
2024-01-11 02:55:07 +03:00
nodes = list ( )
2019-08-28 21:24:54 +03:00
while ( True ) :
c = yield
if ( c is None ) : break
2024-01-11 02:55:07 +03:00
if ( lineno is None ) : lineno , offset = c . lineno , c . offset
nodes . append ( c )
2019-08-28 21:24:54 +03:00
2024-01-11 02:55:07 +03:00
return cls ( nodes , name = name , lineno = lineno , offset = offset )
#def validate(self, ns): # super
# for i in self.nodes:
# i.validate(ns)
2019-08-28 21:24:54 +03:00
def optimize ( self , ns ) :
for ii , i in enumerate ( self . nodes ) :
r = i . optimize ( ns )
if ( r is not None ) : self . nodes [ ii ] = r
2024-01-11 02:55:07 +03:00
self . nodes = [ i for i in self . nodes if not i . flags . optimized_out ]
2019-08-28 21:24:54 +03:00
class ASTTokenNode ( ASTNode ) :
2024-01-11 02:55:07 +03:00
length : . . .
def __init__ ( self , * * kwargs ) :
super ( ) . __init__ ( * * kwargs )
self . length = sum ( len ( getattr ( self , i ) ) for i in allslots ( self ) if isinstance ( getattr ( self , i , None ) , str ) )
2019-08-28 21:24:54 +03:00
@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 ( )
class ASTIdentifierNode ( ASTTokenNode ) :
2024-01-11 02:55:07 +03:00
identifier : . . .
2019-08-28 21:24:54 +03:00
def __init__ ( self , identifier , * * kwargs ) :
self . identifier = identifier
2024-01-11 02:55:07 +03:00
super ( ) . __init__ ( * * kwargs )
2019-08-28 21:24:54 +03:00
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 ) :
2024-01-11 02:55:07 +03:00
keyword : . . .
2019-08-28 21:24:54 +03:00
def __init__ ( self , keyword , * * kwargs ) :
self . keyword = keyword
2024-01-11 02:55:07 +03:00
super ( ) . __init__ ( * * kwargs )
2019-08-28 21:24:54 +03:00
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 ) :
2024-01-11 02:55:07 +03:00
literal : . . .
2019-08-28 21:24:54 +03:00
def __init__ ( self , literal , * * kwargs ) :
self . literal = literal
2024-01-11 02:55:07 +03:00
super ( ) . __init__ ( * * kwargs )
2019-08-28 21:24:54 +03:00
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 ) :
2024-01-11 02:55:07 +03:00
operator : . . .
2019-08-28 21:24:54 +03:00
def __init__ ( self , operator , * * kwargs ) :
self . operator = operator
2024-01-11 02:55:07 +03:00
super ( ) . __init__ ( * * kwargs )
2019-08-28 21:24:54 +03:00
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 )
2024-01-11 02:55:07 +03:00
class ASTUnaryOperatorNode ( ASTOperatorNode ) :
@classmethod
def build ( cls , tl ) :
operator = super ( ) . build ( tl )
if ( not isinstance ( operator . operator , UnaryOperator ) ) : raise SlSyntaxExpectedError ( ' UnaryOperator ' , operator )
operator . operator = UnaryOperator ( operator . operator )
return operator
class ASTBinaryOperatorNode ( ASTOperatorNode ) :
@classmethod
def build ( cls , tl ) :
operator = super ( ) . build ( tl )
if ( not isinstance ( operator . operator , BinaryOperator ) ) : raise SlSyntaxExpectedError ( ' BinaryOperator ' , operator )
operator . operator = BinaryOperator ( operator . operator )
return operator
2019-08-28 21:24:54 +03:00
class ASTSpecialNode ( ASTTokenNode ) :
2024-01-11 02:55:07 +03:00
special : . . .
2019-08-28 21:24:54 +03:00
def __init__ ( self , special , * * kwargs ) :
self . special = special
2024-01-11 02:55:07 +03:00
super ( ) . __init__ ( * * kwargs )
2019-08-28 21:24:54 +03:00
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 ) :
2024-01-11 02:55:07 +03:00
value : . . .
2019-08-28 21:24:54 +03:00
def __init__ ( self , value , * * kwargs ) :
super ( ) . __init__ ( * * kwargs )
self . value = value
def __str__ ( self ) :
return str ( self . value )
@classmethod
2024-01-11 02:55:07 +03:00
def build ( cls , tl , * , fcall = False , attrget = False ) :
2019-08-28 21:24:54 +03:00
super ( ) . build ( tl )
lineno , offset = tl [ 0 ] . lineno , tl [ 0 ] . offset
2024-01-11 02:55:07 +03:00
types = [ * allsubclasses ( ASTUnaryOperationNode ) , ASTFunccallNode , ASTItemgetNode , ASTAttrgetNode , ASTIdentifierNode , ASTLambdaNode , ASTLiteralNode , * allsubclasses ( ASTLiteralStructNode ) ]
if ( fcall ) : types . remove ( ASTFunccallNode ) ; types . remove ( ASTLambdaNode ) # XXX lambda too?
if ( attrget ) : types . remove ( ASTAttrgetNode )
err = set ( )
for i in types :
tll = tl . copy ( )
try : value = i . build ( tll , * * { ' attrget ' : attrget } if ( i in ( ASTFunccallNode , ) ) else { } )
except SlSyntaxExpectedError as ex : err . add ( ex ) ; continue
except SlSyntaxException : continue
else : tl [ : ] = tll ; break
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 ) ) :
2024-01-11 02:55:07 +03:00
if ( self . value . identifier not in ns ) : raise SlValidationNotDefinedError ( self . value , self , scope = ns . scope )
if ( ns . values . get ( self . value . identifier ) is None ) : raise SlValidationError ( f " { self . value . identifier } is not initialized " , self . value , self , scope = ns . scope )
2019-08-28 21:24:54 +03:00
def optimize ( self , ns ) :
super ( ) . optimize ( ns )
2024-01-11 02:55:07 +03:00
if ( isinstance ( self . value , ASTIdentifierNode ) and ns . values . get ( self . value ) not in ( None , . . . ) ) : self . value = ns . values [ self . value ] if ( isinstance ( ns . values [ self . value ] , ASTNode ) ) else ASTLiteralNode ( literal_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 ) :
2024-01-11 02:55:07 +03:00
value : . . .
key : . . .
2019-08-28 21:24:54 +03:00
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 )
2024-01-11 02:55:07 +03:00
if ( ( keysig , self . key ) not in valsig . itemget ) : raise SlValidationError ( f " ` { valsig } ' does not support itemget by key of type ` { keysig } ' " , self . key , self , scope = ns . scope )
2019-08-28 21:24:54 +03:00
class ASTAttrgetNode ( ASTPrimitiveNode ) :
2024-01-11 02:55:07 +03:00
value : . . .
optype : . . .
attr : . . .
2019-08-28 21:24:54 +03:00
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
2024-01-11 02:55:07 +03:00
value = ASTValueNode . build ( tl , attrget = True )
2019-08-28 21:24:54 +03:00
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 )
2024-01-11 02:55:07 +03:00
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 . optype , self , scope = ns . scope )
2019-08-28 21:24:54 +03:00
class ASTExprNode ( ASTPrimitiveNode ) :
@classmethod
2024-01-11 02:55:07 +03:00
def build ( cls , tl , * , fcall = False , attrget = False ) :
2019-08-28 21:24:54 +03:00
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
2024-01-11 02:55:07 +03:00
for i in allsubclasses ( ASTUnaryOperationNode ) :
tll = tl . copy ( )
try : value = i . build ( tll )
except SlSyntaxException : pass
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 ( )
2024-01-11 02:55:07 +03:00
try : value = ASTValueNode . build ( tll , fcall = fcall , attrget = attrget )
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 ) :
2024-01-11 02:55:07 +03:00
operator : . . .
value : . . .
2019-08-28 21:24:54 +03:00
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
2024-01-11 02:55:07 +03:00
operator = ASTUnaryOperatorNode . build ( tl )
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
2024-01-11 02:55:07 +03:00
if ( op not in valsig . operators ) : raise SlValidationError ( f " ` { valsig } ' does not support unary operator ` { op } ' " , self . operator , self , scope = ns . scope )
2019-08-28 21:24:54 +03:00
def optimize ( self , ns ) :
super ( ) . optimize ( ns )
2024-01-11 02:55:07 +03:00
if ( isinstance ( self . value , ASTUnaryExprNode ) ) : return self . value . value
elif ( ns . values . get ( self . value ) not in ( None , . . . ) ) : return ASTValueNode ( ASTLiteralNode ( literal_repr ( eval ( f " { ' not ' if ( self . operator . operator == ' ! ' ) else self . operator } ( { literal_repr ( 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 ) :
2024-01-11 02:55:07 +03:00
lvalue : . . .
operator : . . .
rvalue : . . .
2019-08-28 21:24:54 +03:00
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 ) :
2024-01-11 02:55:07 +03:00
opp = operator_precedence ( self . operator . operator )
return f " { str ( self . lvalue ) . join ( ' () ' ) if ( DEBUG_PRECEDENCE or isinstance ( self . lvalue , ASTBinaryExprNode ) and operator_precedence ( self . lvalue . operator . operator ) > opp ) else self . lvalue } { str ( self . operator ) . join ( ' ' ) if ( self . operator . operator not in ' +-**/ ' ) else self . operator } { str ( self . rvalue ) . join ( ' () ' ) if ( DEBUG_PRECEDENCE or isinstance ( self . rvalue , ASTBinaryExprNode ) and operator_precedence ( self . rvalue . operator . operator ) > opp ) else self . rvalue } "
2019-08-28 21:24:54 +03:00
@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 ] )
2024-01-11 02:55:07 +03:00
operator = ASTBinaryOperatorNode . build ( tll )
2020-03-18 07:00:12 +03:00
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
2024-01-11 02:55:07 +03:00
if ( ( op , rsig ) not in lsig . operators ) : raise SlValidationError ( f " ` { lsig } ' does not support operator ` { op } ' with operand of type ` { rsig } ' " , self . operator , self , scope = ns . scope )
2019-08-28 21:24:54 +03:00
def optimize ( self , ns ) :
super ( ) . optimize ( ns )
2024-01-11 02:55:07 +03:00
if ( self . operator . operator == ' ** ' and ns . values . get ( self . lvalue ) == 2 and ns . values . get ( self . rvalue ) is not . . . and ( ns . values . get ( self . rvalue ) or 0 ) > 0 ) : self . operator . operator , self . lvalue . value = BinaryOperator ( ' << ' ) , ASTLiteralNode ( ' 1 ' , lineno = self . lvalue . value . lineno , offset = self . lvalue . value . offset )
if ( ns . values . get ( self . lvalue ) not in ( None , . . . ) and ns . values . get ( self . rvalue ) not in ( None , . . . ) and self . operator . operator != ' to ' ) : return ASTValueNode ( ASTLiteralNode ( literal_repr ( eval ( f " ( { literal_repr ( ns . values [ self . lvalue ] ) } ) { self . operator } ( { literal_repr ( ns . values [ self . rvalue ] ) } ) " ) ) , lineno = self . lineno , offset = self . offset ) , lineno = self . lineno , offset = self . offset )
2020-03-18 07:00:12 +03:00
class ASTLiteralStructNode ( ASTNode ) : pass
class ASTListNode ( ASTLiteralStructNode ) :
2024-01-11 02:55:07 +03:00
type : . . .
values : . . .
2020-03-18 07:00:12 +03:00
def __init__ ( self , type , values , * * kwargs ) :
super ( ) . __init__ ( * * kwargs )
self . type , self . values = type , values
def __repr__ ( self ) :
2024-01-11 02:55:07 +03:00
return f " <List of ` { self . type } ' > "
2020-03-18 07:00:12 +03:00
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 ) :
2024-01-11 02:55:07 +03:00
super ( ) . validate ( ns )
2020-03-18 07:00:12 +03:00
typesig = Signature . build ( self . type , ns )
for i in self . values :
2024-01-11 02:55:07 +03:00
if ( Signature . build ( i , ns ) != typesig ) : raise SlValidationError ( f " List item ` { i } ' does not match list type ` { self . type } ' " , i , self , scope = ns . scope )
2020-03-18 07:00:12 +03:00
class ASTTupleNode ( ASTLiteralStructNode ) :
2024-01-11 02:55:07 +03:00
types : . . .
values : . . .
2020-03-18 07:00:12 +03:00
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 ) :
2024-01-11 02:55:07 +03:00
super ( ) . validate ( ns )
2020-03-18 07:00:12 +03:00
for i in range ( len ( self . values ) ) :
2024-01-11 02:55:07 +03:00
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 . values [ i ] , self , scope = ns . scope )
2019-08-28 21:24:54 +03:00
class ASTNonFinalNode ( ASTNode ) : pass
class ASTTypedefNode ( ASTNonFinalNode ) :
2024-01-11 02:55:07 +03:00
modifiers : . . .
type : . . .
2019-08-28 21:24:54 +03:00
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 ) :
2024-01-11 02:55:07 +03:00
type : . . .
name : . . .
modifier : . . .
defvalue : . . .
2019-08-28 21:24:54 +03:00
2024-01-11 02:55:07 +03:00
def __init__ ( self , type , name , modifier , defvalue , * * kwargs ) :
2019-08-28 21:24:54 +03:00
super ( ) . __init__ ( * * kwargs )
2024-01-11 02:55:07 +03:00
self . type , self . name , self . modifier , self . defvalue = type , name , modifier , defvalue
2019-08-28 21:24:54 +03:00
def __str__ ( self ) :
2024-01-11 02:55:07 +03:00
return f " { f ' { self . type } ' if ( self . type ) else ' ' } { self . name } { self . modifier or ' ' } { f ' = { self . defvalue } ' if ( self . defvalue is not None ) else ' ' } "
2019-08-28 21:24:54 +03:00
@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
2024-01-11 02:55:07 +03:00
defvalue = ASTExprNode . build ( tl ) if ( isinstance ( modifier , ASTSpecialNode ) and modifier . special == ' = ' ) else None
2019-08-28 21:24:54 +03:00
2024-01-11 02:55:07 +03:00
return cls ( type , name , modifier , defvalue , lineno = lineno , offset = offset )
2019-08-28 21:24:54 +03:00
def validate ( self , ns ) :
super ( ) . validate ( ns )
2024-01-11 02:55:07 +03:00
assert ( self . modifier != ' = ' or self . defvalue is not None )
if ( isinstance ( Signature . build ( self . type , ns ) , stdlib . void ) ) : raise SlValidationError ( f " Argument cannot have type ` { self . type } ' " , self . type , self , scope = ns . scope )
@property
def mandatory ( self ) :
return not ( self . modifier is not None and self . modifier in ' ?= ' )
2019-08-28 21:24:54 +03:00
class ASTCallargsNode ( ASTNonFinalNode ) :
2024-01-11 02:55:07 +03:00
callargs : . . .
starargs : . . .
2019-08-28 21:24:54 +03:00
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 ) )
2024-01-11 02:55:07 +03:00
elif ( len ( tl ) > = 2 and tl [ 1 ] . typename == ' SPECIAL ' and tl [ 1 ] . token in ' =: ' ) : break
2019-08-28 21:24:54 +03:00
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 ) :
2024-01-11 02:55:07 +03:00
callkwargs : . . .
starkwargs : . . .
2019-08-28 21:24:54 +03:00
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 )
2024-01-11 02:55:07 +03:00
if ( eq . special not in ' =: ' ) : raise SlSyntaxExpectedError ( " ' = ' or ' : ' " , eq )
2019-08-28 21:24:54 +03:00
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 ) :
2024-01-11 02:55:07 +03:00
def validate ( self , ns ) : # XXX.
2019-08-28 21:24:54 +03:00
super ( ) . validate ( ns )
code_ns = ns . derive ( self . code . name )
2024-01-11 02:55:07 +03:00
if ( hasattr ( self , ' name ' ) ) :
code_ns . define ( self , redefine = True )
code_ns . values [ self . name ] = . . .
2020-03-18 07:00:12 +03:00
for i in self . argdefs :
code_ns . define ( i , redefine = True )
2024-01-11 02:55:07 +03:00
code_ns . values [ i . name ] = . . .
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 )
2024-01-11 02:55:07 +03:00
for i in self . argdefs :
code_ns . values [ i . name ] = . . .
code_ns . values . parent = None # XXX
2019-08-28 21:24:54 +03:00
self . code . optimize ( code_ns )
2020-03-18 07:00:12 +03:00
class ASTFunctionNode ( ASTCallableNode ) :
2024-01-11 02:55:07 +03:00
def validate ( self , ns ) : # XXX.
2020-03-18 07:00:12 +03:00
super ( ) . validate ( ns )
code_ns = ns . derive ( self . code . name )
2024-01-11 02:55:07 +03:00
rettype = Signature . build ( self . type , ns )
return_nodes = tuple ( i . value for i in self . code . nodes if ( isinstance ( i , ASTKeywordExprNode ) and i . keyword . keyword == ' return ' ) )
if ( not return_nodes and rettype != stdlib . void ( ) ) : raise SlValidationError ( f " Not returning value from function with return type ` { rettype } ' " , self . code , self , scope = ns . scope )
for i in return_nodes :
fsig = Signature . build ( i , code_ns )
if ( rettype == stdlib . void ( ) and fsig != rettype ) : raise SlValidationError ( f " Returning value from function with return type ` { rettype } ' " , i , self , scope = ns . scope )
if ( common_type ( ( fsig , rettype ) , code_ns ) is None ) : raise SlValidationError ( f " Returning value of incompatible type ` { fsig } ' from function with return type ` { rettype } ' " , i , self , scope = ns . scope )
2020-03-18 07:00:12 +03:00
class ASTLambdaNode ( ASTNonFinalNode , ASTFunctionNode ) :
2024-01-11 02:55:07 +03:00
argdefs : . . .
type : . . .
code : . . .
2019-08-28 21:24:54 +03:00
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 ) :
2024-01-11 02:55:07 +03:00
return f " <Lambda ` { self . __fsig__ ( ) } {{ ... }} ' 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 " { 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 ' ) :
2024-01-11 02:55:07 +03:00
argdef = ASTArgdefNode . build ( tl )
if ( argdefs and argdef . defvalue is None and argdefs [ - 1 ] . defvalue is not None ) : raise SlSyntaxError ( f " Non-default argument { argdef } follows default argument { argdefs [ - 1 ] } " )
argdefs . append ( argdef )
2019-08-28 21:24:54 +03:00
if ( tl and tl [ 0 ] . typename == ' SPECIAL ' and tl [ 0 ] . token == ' , ' ) : ASTSpecialNode . build ( tl )
parenthesis = ASTSpecialNode . build ( tl )
if ( parenthesis . special != ' ) ' ) : raise SlSyntaxExpectedError ( " ' ) ' " , parenthesis )
2024-01-11 02:55:07 +03:00
arrow = ASTSpecialNode . build ( tl )
if ( arrow . special != ' -> ' ) : raise SlSyntaxExpectedError ( " ' -> ' " , arrow )
2019-08-28 21:24:54 +03:00
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 )
2024-01-11 02:55:07 +03:00
code = ASTCodeNode ( [ ASTKeywordExprNode ( ASTKeywordNode ( ' return ' , lineno = lineno , offset = offset ) , ASTExprNode . build ( tl ) , lineno = lineno , offset = offset ) ] , name = ' <lambda> ' , lineno = lineno , offset = offset )
2019-08-28 21:24:54 +03:00
return cls ( argdefs , type , code , lineno = lineno , offset = offset )
class ASTBlockNode ( ASTNonFinalNode ) :
2024-01-11 02:55:07 +03:00
code : . . .
2019-08-28 21:24:54 +03:00
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
2024-01-11 02:55:07 +03:00
if ( tl [ 0 ] . typename == ' SPECIAL ' and tl [ 0 ] . token == ' { ' ) : code = ( yield from ASTCodeNode . build ( tl , name = ' <block> ' ) )
2019-08-28 21:24:54 +03:00
else :
yield ' <expr> '
2024-01-11 02:55:07 +03:00
expr = ASTExprNode . build ( tl )
code = ASTCodeNode ( [ expr ] , name = ' ' , lineno = expr . lineno , offset = expr . offset )
2019-08-28 21:24:54 +03:00
return cls ( code , lineno = lineno , offset = offset )
2024-01-11 02:55:07 +03:00
def validate ( self , ns ) :
super ( ) . validate ( ns )
self . code . validate ( ns )
def optimize ( self , ns ) :
super ( ) . optimize ( ns )
self . code . optimize ( ns )
2019-08-28 21:24:54 +03:00
class ASTFinalNode ( ASTNode ) : pass
class ASTDefinitionNode ( ASTNode ) :
2024-01-11 02:55:07 +03:00
def validate ( self , ns ) : # XXX.
2020-03-18 07:00:12 +03:00
Signature . build ( self , ns )
2024-01-11 02:55:07 +03:00
ns . define ( self )
2019-08-28 21:24:54 +03:00
super ( ) . validate ( ns )
2020-03-18 07:00:12 +03:00
class ASTFuncdefNode ( ASTFinalNode , ASTDefinitionNode , ASTFunctionNode ) :
2024-01-11 02:55:07 +03:00
type : . . .
name : . . .
argdefs : . . .
code : . . .
2019-08-28 21:24:54 +03:00
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 ) :
2024-01-11 02:55:07 +03:00
return f " <Funcdef ` { self . __fsig__ ( ) } {{ ... }} ' 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
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 } "
2024-01-11 02:55:07 +03:00
return r
2019-08-28 21:24:54 +03:00
@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 )
2024-01-11 02:55:07 +03:00
if ( argdefs and argdef . defvalue is None and argdefs [ - 1 ] . defvalue is not None ) : raise SlSyntaxError ( f " Non-default argument { argdef } follows default argument { argdefs [ - 1 ] } " )
2019-08-28 21:24:54 +03:00
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 ] )
2024-01-11 02:55:07 +03:00
if ( tl [ 0 ] . token == ' { ' ) : code = ( yield from ASTCodeNode . build ( tl , name = name . identifier ) )
else :
cdef = ASTSpecialNode . build ( tl )
assert ( cdef . special == ' = ' )
2019-08-28 21:24:54 +03:00
yield name . identifier
2024-01-11 02:55:07 +03:00
code = ASTCodeNode ( [ ASTKeywordExprNode ( ASTKeywordNode ( ' return ' , lineno = lineno , offset = offset ) , ASTExprNode . build ( tl ) , lineno = lineno , offset = offset ) ] , name = name . identifier , lineno = lineno , offset = offset )
2019-08-28 21:24:54 +03:00
return cls ( type , name , argdefs , code , lineno = lineno , offset = offset )
2020-03-18 07:00:12 +03:00
class ASTClassdefNode ( ASTFinalNode , ASTDefinitionNode , ASTCallableNode ) :
2024-01-11 02:55:07 +03:00
name : . . .
bases : . . .
code : . . .
type : . . .
2020-03-18 07:00:12 +03:00
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 ) :
2024-01-11 02:55:07 +03:00
return f " <Classdef ` { self . name } ' on line { self . lineno } , offset { self . offset } > "
2020-03-18 07:00:12 +03:00
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 )
2024-01-11 02:55:07 +03:00
if ( not tl or tl [ 0 ] . typename != ' SPECIAL ' or tl [ 0 ] . token != ' { ' ) : raise SlSyntaxExpectedError ( " ' { ' " , tl [ 0 ] )
code = ( yield from ASTCodeNode . build ( tl , name = name . identifier ) )
2020-03-18 07:00:12 +03:00
return cls ( name , bases , code , lineno = lineno , offset = offset )
2019-08-28 21:24:54 +03:00
class ASTKeywordExprNode ( ASTFinalNode ) :
2024-01-11 02:55:07 +03:00
keyword : . . .
value : . . .
2019-08-28 21:24:54 +03:00
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)
2024-01-11 02:55:07 +03:00
elif ( keyword . keyword == ' delete ' ) : value = ASTIdentifierNode . build ( tl )
2020-03-18 07:00:12 +03:00
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 :
2024-01-11 02:55:07 +03:00
ex . srclines = src . split ( ' \n ' )
raise SlValidationError ( f " Error importing { self . value } " , self . value , self , scope = ns . scope ) from ex
2020-03-18 07:00:12 +03:00
if ( name != ' * ' ) : ns . define ( ASTIdentifierNode ( name , lineno = self . value . lineno , offset = self . value . offset ) ) # TODO object
else : ns . signatures . update ( module_ns . signatures ) # TODO?
elif ( self . keyword . keyword == ' delete ' ) :
2024-01-11 02:55:07 +03:00
if ( self . value . identifier not in ns ) : raise SlValidationNotDefinedError ( self . value , self , scope = ns . scope )
2020-03-18 07:00:12 +03:00
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 ) :
2024-01-11 02:55:07 +03:00
keyword : . . .
name : . . .
argdefs : . . .
code : . . .
2020-03-18 07:00:12 +03:00
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 ) :
2024-01-11 02:55:07 +03:00
return f " <KeywordDef ` { self . name } ' on line { self . lineno } , offset { self . offset } > "
2020-03-18 07:00:12 +03:00
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 )
2024-01-11 02:55:07 +03:00
if ( argdefs and argdef . defvalue is None and argdefs [ - 1 ] . defvalue is not None ) : raise SlSyntaxError ( f " Non-default argument { argdef } follows default argument { argdefs [ - 1 ] } " )
2020-03-18 07:00:12 +03:00
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 )
2024-01-11 02:55:07 +03:00
if ( not tl or tl [ 0 ] . typename != ' SPECIAL ' or tl [ 0 ] . token != ' { ' ) : raise SlSyntaxExpectedError ( ' { ' , tl [ 0 ] )
code = ( yield from ASTCodeNode . build ( tl , name = f " < { keyword } > " ) )
2020-03-18 07:00:12 +03:00
return cls ( keyword , name , argdefs , code , lineno = lineno , offset = offset )
2024-01-11 02:55:07 +03:00
def validate ( self , ns ) : # XXX.
2020-03-18 07:00:12 +03:00
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 )
2024-01-11 02:55:07 +03:00
code_ns . values [ i . name ] = . . .
2020-03-18 07:00:12 +03:00
self . code . validate ( code_ns )
def optimize ( self , ns ) :
super ( ) . optimize ( ns )
code_ns = ns . derive ( self . code . name )
2024-01-11 02:55:07 +03:00
if ( isinstance ( self . keyword . keyword , DefArgsKeyword ) ) :
for i in self . argdefs :
code_ns . values [ i . name ] = . . .
2020-03-18 07:00:12 +03:00
self . code . optimize ( code_ns )
2019-08-28 21:24:54 +03:00
class ASTAssignvalNode ( ASTNode ) :
def validate ( self , ns ) :
super ( ) . validate ( ns )
2024-01-11 02:55:07 +03:00
if ( self . name . identifier not in ns ) : raise SlValidationNotDefinedError ( self . name , self , scope = ns . scope )
varsig = Signature . build ( self . name , ns )
2019-08-28 21:24:54 +03:00
if ( self . value is not None ) :
2024-01-11 02:55:07 +03:00
valsig = Signature . build ( self . value , ns )
if ( valsig != varsig and varsig != valsig ) : raise SlValidationError ( f " Assignment of value ` { self . value } ' of type ` { valsig } ' to variable ` { self . name } ' of type ` { varsig } ' " , self . value , self , scope = ns . scope )
varsig . flags . modified = True
ns . values [ self . name ] = self . value if ( not varsig . modifiers . volatile ) else . . .
def optimize ( self , ns ) :
super ( ) . optimize ( ns )
varsig = Signature . build ( self . name , ns )
varsig . flags . modified = True
ns . values [ self . name ] = self . value if ( not varsig . modifiers . volatile ) else . . .
2019-08-28 21:24:54 +03:00
class ASTVardefNode ( ASTFinalNode , ASTAssignvalNode , ASTDefinitionNode ) :
2024-01-11 02:55:07 +03:00
type : . . .
name : . . .
value : . . .
2019-08-28 21:24:54 +03:00
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 )
2024-01-11 02:55:07 +03:00
def validate ( self , ns ) : # XXX.
2019-08-28 21:24:54 +03:00
if ( self . type . type . identifier == ' auto ' ) : self . type . type . identifier = Signature . build ( self . value , ns ) . typename
2024-01-11 02:55:07 +03:00
self . flags . optimized_out = False
2019-08-28 21:24:54 +03:00
super ( ) . validate ( ns )
2024-01-11 02:55:07 +03:00
if ( self . value is not None ) :
varsig = Signature . build ( self . name , ns )
varsig . flags . modified = False
2019-08-28 21:24:54 +03:00
def optimize ( self , ns ) :
super ( ) . optimize ( ns )
2024-01-11 02:55:07 +03:00
varsig = Signature . build ( self . name , ns )
self . flags . optimized_out = False
if ( self . value is not None ) : varsig . flags . modified = False
#if (not varsig.flags.modified and not varsig.modifiers.volatile): # TODO
# self.value = None
# self.flags.optimized_out = True
2019-08-28 21:24:54 +03:00
class ASTAssignmentNode ( ASTFinalNode , ASTAssignvalNode ) :
2024-01-11 02:55:07 +03:00
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 ) :
2024-01-11 02:55:07 +03:00
return f " { ' . ' * self . isattr } { self . name } { self . inplace_operator or ' ' } { self . assignment } { 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
2024-01-11 02:55:07 +03:00
isattr = False
2020-03-18 07:00:12 +03:00
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
2024-01-11 02:55:07 +03:00
if ( tl and tl [ 0 ] . typename == ' OPERATOR ' ) : inplace_operator = ASTBinaryOperatorNode . build ( tl )
2020-03-18 07:00:12 +03:00
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 )
2024-01-11 02:55:07 +03:00
def validate ( self , ns ) : # XXX.
valsig = Signature . build ( self . value , ns )
if ( self . assignment . special == ' := ' ) : ns . define ( self . name , valsig , redefine = True )
2020-03-18 07:00:12 +03:00
if ( self . isattr ) : return # TODO
super ( ) . validate ( ns )
2024-01-11 02:55:07 +03:00
varsig = Signature . build ( self . name , ns )
if ( varsig . modifiers . const ) : raise SlValidationError ( f " Assignment to const ` { self . name } ' " , self . name , self , scope = ns . scope )
if ( self . inplace_operator is not None ) : ns . values [ self . name ] = . . . # TODO folding
def optimize ( self , ns ) :
super ( ) . optimize ( ns )
if ( self . inplace_operator is not None ) : ns . values [ self . name ] = . . . # TODO folding
2020-03-18 07:00:12 +03:00
class ASTUnpackAssignmentNode ( ASTFinalNode , ASTAssignvalNode ) :
2024-01-11 02:55:07 +03:00
names : . . .
assignment : . . .
inplace_operator : . . .
value : . . .
2020-03-18 07:00:12 +03:00
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 ) :
2024-01-11 02:55:07 +03:00
return f " { S ( ' , ' ) . join ( self . names ) } { self . inplace_operator or ' ' } { self . assignment } { self . value } "
2020-03-18 07:00:12 +03:00
@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
2024-01-11 02:55:07 +03:00
def validate ( self , ns ) : # XXX.
valsig = Signature . build ( self . value , ns )
2020-03-18 07:00:12 +03:00
if ( self . assignment . special == ' := ' ) :
2024-01-11 02:55:07 +03:00
for name , type in zip ( self . names , valsig . valtypes ) :
2020-03-18 07:00:12 +03:00
ns . define ( name , type , redefine = True )
2024-01-11 02:55:07 +03:00
ASTNode . validate ( self , ns )
for name , ( ii , valtype ) in zip ( self . names , enumerate ( valsig . valtypes ) ) :
if ( name . identifier not in ns ) : raise SlValidationNotDefinedError ( name , self , scope = ns . scope )
varsig = Signature . build ( name , ns )
if ( varsig . modifiers . const ) : raise SlValidationError ( f " Assignment to const ` { name } ' " , name , self , scope = ns . scope )
if ( varsig != valtype ) : raise SlValidationError ( f " Assignment of ` { valtype } ' to variable { name } of type { varsig } " , self . value . value . values [ ii ] if ( isinstance ( self . value , ASTValueNode ) and hasattr ( self . value . value , ' values ' ) ) else name , self , scope = ns . scope )
varsig . flags . modified = True
if ( self . inplace_operator is not None ) : ns . values [ name ] = . . . # TODO folding
def optimize ( self , ns ) :
super ( ) . optimize ( ns )
if ( self . inplace_operator is not None ) : ns . values [ self . name ] = . . . # TODO folding
class ASTUnaryOperationNode ( ASTPrimitiveNode ) :
name : . . .
isattr : . . .
unary_operator : . . .
def __init__ ( self , name , isattr , unary_operator , * * kwargs ) :
super ( ) . __init__ ( * * kwargs )
self . name , self . isattr , self . unary_operator = name , isattr , unary_operator
def validate ( self , ns ) :
if ( self . isattr ) : return # TODO
super ( ) . validate ( ns )
varsig = Signature . build ( self . name , ns )
if ( varsig . modifiers . const ) : raise SlValidationError ( f " Unary operation ` { self . unary_operator } ' on const ` { self . name } ' " , self . name , self , scope = ns . scope )
class ASTUnaryPreOperationNode ( ASTUnaryOperationNode , ASTFinalNode ) :
def __str__ ( self ) :
return f " { self . unary_operator } { ' . ' * self . isattr } { self . name } "
@classmethod
def build ( cls , tl ) :
super ( ) . build ( tl )
lineno , offset = tl [ 0 ] . lineno , tl [ 0 ] . offset
unary_operator = ASTUnaryOperatorNode . build ( tl )
if ( unary_operator . operator not in unaryops ) : raise SlSyntaxExpectedError ( ' Unary operation ' , unary_operator )
isattr = False
if ( tl and tl [ 0 ] . typename == ' SPECIAL ' and tl [ 0 ] . token == ' . ' ) : ASTSpecialNode . build ( tl ) ; isattr = True
name = ASTIdentifierNode . build ( tl )
return cls ( name , isattr , unary_operator , lineno = lineno , offset = offset )
class ASTUnaryPostOperationNode ( ASTUnaryOperationNode , ASTFinalNode ) :
def __str__ ( self ) :
return f " { ' . ' * self . isattr } { self . name } { self . unary_operator } "
@classmethod
def build ( cls , tl ) :
super ( ) . build ( tl )
lineno , offset = tl [ 0 ] . lineno , tl [ 0 ] . offset
isattr = False
if ( tl and tl [ 0 ] . typename == ' SPECIAL ' and tl [ 0 ] . token == ' . ' ) : ASTSpecialNode . build ( tl ) ; isattr = True
name = ASTIdentifierNode . build ( tl )
unary_operator = ASTUnaryOperatorNode . build ( tl )
if ( unary_operator . operator not in unaryops ) : raise SlSyntaxExpectedError ( ' Unary operation ' , unary_operator )
return cls ( name , isattr , unary_operator , lineno = lineno , offset = offset )
2020-03-18 07:00:12 +03:00
class ASTAttrsetNode ( ASTFinalNode ) :
2024-01-11 02:55:07 +03:00
value : . . .
assignment : . . .
2020-03-18 07:00:12 +03:00
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)
2024-01-11 02:55:07 +03:00
#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.optype, self, scope=ns.scope)
2019-08-28 21:24:54 +03:00
class ASTFunccallNode ( ASTFinalNode ) :
2024-01-11 02:55:07 +03:00
callable : . . .
callargs : . . .
callkwargs : . . .
2019-08-28 21:24:54 +03:00
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
2024-01-11 02:55:07 +03:00
def build ( cls , tl , * , attrget = False ) :
2019-08-28 21:24:54 +03:00
super ( ) . build ( tl )
lineno , offset = tl [ 0 ] . lineno , tl [ 0 ] . offset
2024-01-11 02:55:07 +03:00
callable = ASTExprNode . build ( tl , fcall = True , attrget = attrget )
2019-08-28 21:24:54 +03:00
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 )
2024-01-11 02:55:07 +03:00
if ( not isinstance ( fsig , Callable ) ) : raise SlValidationError ( f " ` { self . callable } ' of type ` { fsig } ' is not callable " , self . callable , self , scope = ns . scope )
callarguments = CallArguments . build ( self , ns )
if ( fsig . compatible_call ( callarguments , ns ) is None ) : raise SlValidationError ( f " Parameters `( { callarguments } ) ' don ' t match any of ` { self . callable } ' signatures: \n { S ( fsig . callargssigstr ) . indent ( ) } \n " , self , scope = ns . scope )
def optimize ( self , ns ) :
fsig = Signature . build ( self . callable , ns )
if ( fsig . code is not None ) :
code_ns = ns . derive ( fsig . code . name )
fsig . code . validate ( code_ns )
super ( ) . optimize ( ns )
2019-08-28 21:24:54 +03:00
class ASTConditionalNode ( ASTFinalNode ) :
2024-01-11 02:55:07 +03:00
condition : . . .
code : . . .
2019-08-28 21:24:54 +03:00
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 ) :
2024-01-11 02:55:07 +03:00
return f " <Conditional if ` { self . condition } ' on line { self . lineno } , offset { self . offset } > "
2020-03-18 07:00:12 +03:00
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 ) :
2024-01-11 02:55:07 +03:00
name : . . .
iterable : . . .
code : . . .
2019-08-28 21:24:54 +03:00
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 ) :
2024-01-11 02:55:07 +03:00
return f " <ForLoop ` { self . name } ' in ` { self . iterable } ' on line { self . lineno } , offset { self . offset } > "
2020-03-18 07:00:12 +03:00
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 ) :
# TODO: validate iterability
ns . define ( self . name , Signature . build ( self . iterable , ns ) . valtype )
ns . weaken ( self . name )
2024-01-11 02:55:07 +03:00
ns . values [ self . name ] = . . .
super ( ) . validate ( ns )
def optimize ( self , ns ) :
self . code . validate ( ns )
super ( ) . optimize ( ns )
2020-03-18 07:00:12 +03:00
2019-08-28 21:24:54 +03:00
class ASTWhileLoopNode ( ASTFinalNode ) :
2024-01-11 02:55:07 +03:00
code : . . . # needs to be validated/optimized first (case when the condition is modified from loop body)
condition : . . .
2019-08-28 21:24:54 +03:00
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 ) :
2024-01-11 02:55:07 +03:00
return f " <WhileLoop while ` { self . condition } ' on line { self . lineno } , offset { self . offset } > "
2020-03-18 07:00:12 +03:00
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 )
2024-01-11 02:55:07 +03:00
#def validate(self, ns):
# Signature.build(self.condition, ns).modifiers.volatile = True
# super().validate(ns)
def optimize ( self , ns ) :
self . code . validate ( ns )
super ( ) . optimize ( ns )
2019-08-28 21:24:54 +03:00
class ASTElseClauseNode ( ASTFinalNode ) :
2024-01-11 02:55:07 +03:00
code : . . .
2019-08-28 21:24:54 +03:00
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 )
2024-01-11 02:55:07 +03:00
except StopIteration as ex : code_stack . pop ( ) ; r = ex . value
2019-08-28 21:24:54 +03:00
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
2024-01-11 02:55:07 +03:00
except SlSyntaxNoToken : err . add ( SlSyntaxExpectedMoreTokensError ( i . __name__ [ 3 : - 4 ] , lineno = lineno , offset = - 1 ) )
2020-03-18 07:00:12 +03:00
except SlSyntaxMultiExpectedError as ex : pass #err.add(ex) # TODO FIXME
except SlSyntaxExpectedError as ex : ex . usage = i . __name__ [ 3 : - 4 ] ; err . add ( ex )
2024-01-11 02:55:07 +03:00
else :
code_stack [ - 1 ] [ 0 ] . send ( r )
err . clear ( )
break
2019-08-28 21:24:54 +03:00
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 ] )
2024-01-11 02:55:07 +03:00
except StopIteration as ex : code_stack [ - 1 ] [ 0 ] . send ( ex . value ) ; err . clear ( )
2019-08-28 21:24:54 +03:00
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
2024-01-11 02:55:07 +03:00
if ( len ( code_stack ) > 1 ) : raise SlSyntaxExpectedMoreTokensError ( code_stack [ - 1 ] [ 1 ] , lineno = lineno )
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 ] )
2024-01-11 02:55:07 +03:00
except StopIteration as ex : return ex . value
2019-08-28 21:24:54 +03:00
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
2024-01-11 02:55:07 +03:00
for i in allslots ( node ) : yield from walk_ast_nodes ( getattr ( node , i ) )
2019-08-28 21:24:54 +03:00
2024-01-11 02:55:07 +03:00
class _SignatureBase ( ABCSlots ) : pass
2019-08-28 21:24:54 +03:00
class Signature ( _SignatureBase ) :
2024-01-11 02:55:07 +03:00
operators = { }
typename : . . .
modifiers : paramset
flags : paramset
2019-08-28 21:24:54 +03:00
2024-01-11 02:55:07 +03:00
@init ( typename = . . . , modifiers = . . . , flags = . . . )
def __init__ ( self ) :
super ( ) . __init__ ( )
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 ) :
2024-01-11 02:55:07 +03:00
return f " < { self . __reprname__ } ` { self . name } ' > "
def __str__ ( self ) :
return 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 ) :
2024-01-11 02:55:07 +03:00
return hash ( tuple ( getattr ( self , i ) for i in allslots ( self ) ) )
2019-08-28 21:24:54 +03:00
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
2024-01-11 02:55:07 +03:00
#if (x.value is not None): ns.values[x.name] = x.value if (False and not r.modifiers.volatile and r.modifiers.const) else ... # XXX TODO (see False)
2019-08-28 21:24:54 +03:00
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 ) :
2024-01-11 02:55:07 +03:00
#return Collection(keytype=stdlib.int(), valtype=Signature.build(x.type, ns))
return stdlib . list ( valtype = Signature . build ( x . type , ns ) )
2020-03-18 07:00:12 +03:00
@classmethod
@dispatch
def build ( cls , x : ASTTupleNode , ns ) :
2024-01-11 02:55:07 +03:00
#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)))
return stdlib . tuple ( valtypes = tuple ( Signature . build ( t if ( t is not None ) else v , ns ) for t , v in zip ( x . types , x . values ) ) )
2020-03-18 07:00:12 +03:00
2019-08-28 21:24:54 +03:00
@classmethod
@dispatch
def build ( cls , x : ASTFunccallNode , ns ) :
2024-01-11 02:55:07 +03:00
callarguments = CallArguments . build ( x , ns )
return cls . build ( x . callable , ns ) . compatible_call ( callarguments , ns ) [ 1 ]
@classmethod
@dispatch
def build ( cls , x : ASTLambdaNode , ns ) :
return Lambda . build ( x , ns )
2019-08-28 21:24:54 +03:00
@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 )
2024-01-11 02:55:07 +03:00
@classmethod
@dispatch
def build ( cls , x : ASTUnaryOperationNode , ns ) :
return cls . build ( x . name , 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
2024-01-11 02:55:07 +03:00
class Callable ( Signature ) :
call : . . .
code : . . .
def __init__ ( self , * , code = None , * * kwargs ) :
super ( ) . __init__ ( * * kwargs )
self . code = code
def compatible_call ( self , callarguments , ns ) :
try : return first ( ( k , v ) for k , v in self . call . items ( ) if callarguments . compatible ( k ) )
except StopIteration : return None
2020-03-18 07:00:12 +03:00
@abc.abstractproperty
def callargssigstr ( self ) :
pass
class Function ( Callable ) :
2024-01-11 02:55:07 +03:00
typename = ' function '
name : . . .
2019-08-28 21:24:54 +03:00
2024-01-11 02:55:07 +03:00
def __init__ ( self , * , name , code = None , * * kwargs ) :
super ( ) . __init__ ( * * kwargs )
self . name , self . code = name , code
2019-08-28 21:24:54 +03:00
self . call = listmap ( )
@property
def callargssigstr ( self ) :
2024-01-11 02:55:07 +03:00
return ' \n ' . join ( f " { ret . typename } { self . name } ( { S ( ' , ' ) . join ( args ) } ) " for args , ret in self . call . items ( ) )
2019-08-28 21:24:54 +03:00
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
2024-01-11 02:55:07 +03:00
if ( name not in ns ) : fsig = ns . signatures [ name ] = cls ( name = name , code = x . code )
else : fsig = ns . signatures [ name ]
argdefs = tuple ( x . argdefs )
if ( not redefine and argdefs in fsig . call and name not in ns . weak ) : raise SlValidationRedefinedError ( x . name , x . __fsig__ ( ) , scope = ns . scope )
if ( x . type . type . identifier == ' auto ' ) : # XXX@ TODO FIXME
code_ns = ns . derive ( x . code . name )
rettype = common_type ( ( i . value for i in x . code . nodes if ( isinstance ( i , ASTKeywordExprNode ) and i . keyword . keyword == ' return ' ) ) , code_ns ) or stdlib . void ( )
else : rettype = Signature . build ( x . type , ns )
fsig . call [ argdefs ] = rettype
dlog ( fsig . call , x )
return fsig
class Lambda ( Callable ) :
typename = ' lambda '
def __init__ ( self , * * kwargs ) :
super ( ) . __init__ ( * * kwargs )
self . call = listmap ( )
@property
def callargssigstr ( self ) :
return ' \n ' . join ( f " ( { S ( ' , ' ) . join ( args ) } ) -> { ret . typename } " for args , ret in self . call . items ( ) )
@staticitemget
def attrops ( optype , attr ) :
if ( optype == ' . ' ) :
if ( attr == ' map ' ) : return stdlib . _map ( )
raise KeyError ( )
@classmethod
@dispatch
def build ( cls , x : ASTLambdaNode , ns , * , redefine = False ) :
fsig = cls ( code = x . code )
argdefs = tuple ( x . argdefs )
if ( x . type . type . identifier == ' auto ' ) :
code_ns = ns . derive ( x . code . name )
rettype = common_type ( ( i . value for i in x . code . nodes if ( isinstance ( i , ASTKeywordExprNode ) and i . keyword . keyword == ' return ' ) ) , code_ns ) or stdlib . void ( )
else : rettype = Signature . build ( x . type , ns )
fsig . call [ argdefs ] = rettype
return fsig
2019-08-28 21:24:54 +03:00
2020-03-18 07:00:12 +03:00
class KeywordDef ( Callable ) :
2024-01-11 02:55:07 +03:00
typename = ' keyworddef '
name : . . .
2020-03-18 07:00:12 +03:00
def __init__ ( self , * , name , * * kwargs ) :
2024-01-11 02:55:07 +03:00
super ( ) . __init__ ( * * kwargs )
2020-03-18 07:00:12 +03:00
self . name = name
self . call = listmap ( )
@property
def callargssigstr ( self ) :
2024-01-11 02:55:07 +03:00
return ' \n ' . join ( f " { self . name } ( { S ( ' , ' ) . join ( args ) } ) " for args , ret in self . call . items ( ) )
2020-03-18 07:00:12 +03:00
@classmethod
@dispatch
def build ( cls , x : ASTKeywordDefNode , ns , * , redefine = False ) :
name = x . name . identifier
2024-01-11 02:55:07 +03:00
if ( name not in ns ) : fsig = ns . signatures [ name ] = cls ( name = name , code = x . code )
else : fsig = ns . signatures [ name ]
argdefs = tuple ( x . argdefs ) if ( x . argdefs is not None ) else ( )
if ( not redefine and argdefs in fsig . call and name not in ns . weak ) : raise SlValidationRedefinedError ( x . name , fsig . call [ argdefs ] , scope = ns . scope )
fsig . call [ argdefs ] = stdlib . void
return fsig
2020-03-18 07:00:12 +03:00
2024-01-11 02:55:07 +03:00
class Object ( Signature ) :
typename = ' Object '
2020-03-18 07:00:12 +03:00
class Collection ( Object ) :
2024-01-11 02:55:07 +03:00
typename = ' collection '
keytype : . . .
valtype : . . .
2020-03-18 07:00:12 +03:00
2024-01-11 02:55:07 +03:00
def __init__ ( self , * , keytype , valtype , * * kwargs ) :
super ( ) . __init__ ( * * kwargs )
2020-03-18 07:00:12 +03:00
self . keytype , self . valtype = keytype , valtype
2024-01-11 02:55:07 +03:00
def __repr__ ( self ) :
return f " <Collection of ` { self . valtype . typename } ' by ` { self . keytype } ' > "
2020-03-18 07:00:12 +03:00
@property
def typename ( self ) :
return self . valtype . typename
2024-01-11 02:55:07 +03:00
@typename.setter
def typename ( self , x ) :
pass
2020-03-18 07:00:12 +03:00
@itemget
@instantiate
def itemget ( self , keysig , key ) :
if ( keysig == self . keytype ) : return self . valtype
raise KeyError ( )
class MultiCollection ( Collection ) :
2024-01-11 02:55:07 +03:00
typename = ' MultiCollection '
valtypes : . . .
2020-03-18 07:00:12 +03:00
def __init__ ( self , * , keytype , valtypes ) :
2024-01-11 02:55:07 +03:00
super ( ) . __init__ ( keytype = keytype , valtype = stdlib . Any )
self . valtypes = valtypes
2020-03-18 07:00:12 +03:00
@itemget
@instantiate
def itemget ( self , keysig , key ) :
if ( keysig == self . keytype ) : return self . valtypes [ int ( key ) ]
raise KeyError ( )
class Class ( Object , Callable ) :
2024-01-11 02:55:07 +03:00
typename = ' class '
name : . . .
scope : . . .
constructor : . . .
2020-03-18 07:00:12 +03:00
def __init__ ( self , * , name , scope , * * kwargs ) :
2024-01-11 02:55:07 +03:00
super ( ) . __init__ ( * * kwargs )
2020-03-18 07:00:12 +03:00
self . name , self . scope = name , scope
self . constructor = listmap ( )
def __str__ ( self ) :
return self . name
2024-01-11 02:55:07 +03:00
def compatible_call ( self , callarguments , ns ) :
try : return first ( ( k , v ) for k , v in self . constructor . items ( ) if callarguments . compatible ( k ) )
except StopIteration : return None
2020-03-18 07:00:12 +03:00
@property
def callargssigstr ( self ) :
2024-01-11 02:55:07 +03:00
return ' \n ' . join ( f " { self . name } ( { S ( ' , ' ) . join ( args ) } ) " for args , ret in self . constructor . items ( ) )
2020-03-18 07:00:12 +03:00
@itemget
2024-01-11 02:55:07 +03:00
def call ( self , argdefs ) :
return self . constructor [ argdefs ]
2020-03-18 07:00:12 +03:00
@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
2024-01-11 02:55:07 +03:00
#if (not redefine and name in ns and name not in ns.weak): raise SlValidationRedefinedError(x.name, ns.signatures[name], scope=ns.scope)
fsig = ns . signatures [ name ] = cls ( name = name , scope = ns . derive ( x . code . name ) , code = x . code )
2020-03-18 07:00:12 +03:00
for i in x . code . nodes :
if ( isinstance ( i , ASTKeywordDefNode ) and i . keyword . keyword == ' constr ' ) :
2024-01-11 02:55:07 +03:00
argdefs = tuple ( i . argdefs ) if ( i . argdefs is not None ) else ( )
if ( not redefine and argdefs in fsig . constructor and name not in ns . weak ) : raise SlValidationRedefinedError ( x . name , fsig . constructor [ argdefs ] , scope = ns . scope )
fsig . constructor [ argdefs ] = fsig
return fsig
2019-08-28 21:24:54 +03:00
2024-01-11 02:55:07 +03:00
class CallArguments ( Slots ) :
args : . . .
starargs : . . .
kwargs : . . .
starkwargs : . . .
ns : . . .
2019-08-28 21:24:54 +03:00
@init_defaults
@autocast
2024-01-11 02:55:07 +03:00
def __init__ ( self , * , args : tuple , starargs : tuple , kwargs : tuple , starkwargs : tuple , ns ) :
self . args , self . starargs , self . kwargs , self . starkwargs , self . ns = args , starargs , kwargs , starkwargs , ns
2019-08-28 21:24:54 +03:00
2024-01-11 02:55:07 +03:00
def __str__ ( self ) :
2019-08-28 21:24:54 +03:00
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 ) :
2024-01-11 02:55:07 +03:00
return all ( getattr ( self , i ) == getattr ( x , i ) for i in allslots ( self ) )
2019-08-28 21:24:54 +03:00
2024-01-11 02:55:07 +03:00
@dispatch
def compatible ( self , x : typing . Iterable [ ASTArgdefNode ] ) :
# type(x[i]) = ASTArgdefNode
# type(args[i]) = ASTExprNode
# type(starargs[i]) = ASTExprNode
# type(kwargs[i]) = tuple(ASTIdentifierNode, ASTExprNode)
# type(starkwargs[i]) = ASTExprNode
x = Slist ( x )
args , starargs , kwargs , starkwargs = list ( self . args ) , list ( self . starargs ) , dict ( self . kwargs ) , list ( self . starkwargs )
has_mandatory_left = bool ( x )
optional_left = max ( 0 , len ( args ) - len ( tuple ( i for i in x if i . mandatory ) ) )
to_fill_optionals = list ( )
#dlog(1, x, args)
while ( x ) :
x . discard ( )
for ii , arg in enumerate ( x ) : # type, name, modifier, defvalue
if ( not arg . mandatory ) :
if ( has_mandatory_left ) :
if ( len ( args ) > optional_left ) : to_fill_optionals . append ( args . pop ( 0 ) )
continue
elif ( to_fill_optionals ) :
args + = to_fill_optionals
to_fill_optionals . clear ( )
if ( args ) :
posarg = args . pop ( 0 )
sig = Signature . build ( posarg , self . ns )
if ( common_type ( ( arg . type , sig ) , self . ns ) is None ) : return False
x . to_discard ( ii )
continue
if ( starargs ) :
stararg = starargs . pop ( 0 )
sig = Signature . build ( stararg , self . ns )
#if (common_type((arg.type, *stararg.type), self.ns) is not None): return False # TODO: typecheck!
x . to_discard ( ii )
continue
if ( kwargs ) :
continue
if ( starkwargs ) :
continue
# XXX!
break
else : has_mandatory_left = False ; continue
x . discard ( )
break
return not any ( ( has_mandatory_left , args , starargs , kwargs , starkwargs ) )
#@property # XXX needed? | FIXME star[kw]args
#def nargs(self):
# return len(self.args) + sum(i.length for i in self.starargs) + len(self.kwargs) + sum(i.length for i in self.starkwargs)
# #return sum(len(getattr(self, i)) for i in allslots(self))
2019-08-28 21:24:54 +03:00
@classmethod
@dispatch
2024-01-11 02:55:07 +03:00
def build ( cls , x : ASTFunccallNode , ns ) :
return cls ( args = x . callargs . callargs ,
starargs = x . callargs . starargs ,
kwargs = x . callkwargs . callkwargs ,
starkwargs = x . callkwargs . starkwargs ,
ns = ns )
class Namespace ( Slots ) :
class _Signatures ( Slots ) :
signatures : dict
parent : None
@init ( signatures = . . . , parent = . . . )
def __init__ ( self ) :
super ( ) . __init__ ( )
assert ( self . parent not in ( self , self . signatures ) )
if ( isinstance ( self . parent , Namespace . _Signatures ) ) : assert ( self . parent . parent not in ( self , self . signatures ) )
def __iter__ ( self ) :
return iter ( self . keys ( ) )
2019-08-28 21:24:54 +03:00
2024-01-11 02:55:07 +03:00
@dispatch
def __contains__ ( self , x : str ) :
return x in self . signatures or ( self . parent is not None and x in self . parent )
2019-08-28 21:24:54 +03:00
2024-01-11 02:55:07 +03:00
@dispatch
def __getitem__ ( self , x : str ) :
try : return self . signatures [ x ]
except KeyError :
if ( self . parent is not None ) :
try : return self . parent [ x ]
except KeyError : pass
raise
@dispatch
def __setitem__ ( self , k : str , v : Signature ) :
self . signatures [ k ] = v
2019-08-28 21:24:54 +03:00
2024-01-11 02:55:07 +03:00
@dispatch
def __delitem__ ( self , x : ASTIdentifierNode ) :
del self [ x . identifier ]
@dispatch
def __delitem__ ( self , x : str ) :
del self . signatures [ x ]
def items ( self ) :
return self . signatures . items ( ) if ( self . parent is None ) else { * * self . parent , * * self . signatures } . items ( )
def keys ( self ) :
return self . signatures . keys ( ) if ( self . parent is None ) else ( * self . signatures . keys ( ) , * self . parent . keys ( ) )
def copy ( self ) :
return self . __class__ ( signatures = self . signatures . copy ( ) , parent = self . parent )
class _Values ( Slots ) :
values : dict
parent : None
@init ( values = . . . , parent = . . . )
def __init__ ( self ) :
super ( ) . __init__ ( )
assert ( self . parent not in ( self , self . values ) )
if ( isinstance ( self . parent , Namespace . _Values ) ) : assert ( self . parent . parent not in ( self , self . values ) )
2019-08-28 21:24:54 +03:00
@dispatch
def __getitem__ ( self , x : ASTLiteralNode ) :
2024-01-11 02:55:07 +03:00
return eval_literal ( x )
2019-08-28 21:24:54 +03:00
@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 ) :
2024-01-11 02:55:07 +03:00
try : return self . values [ x ]
except KeyError :
if ( self . parent is not None ) :
try : return self . parent [ x ]
except KeyError : pass
#except RecursionError: pass # TODO FIXME XXX! ??? (tests/fib.sl)
raise
2019-08-28 21:24:54 +03:00
@dispatch
def __setitem__ ( self , k , v : ASTValueNode ) :
self [ k ] = v . value
@dispatch
def __setitem__ ( self , x : ASTIdentifierNode , v : ASTLiteralNode ) :
2024-01-11 02:55:07 +03:00
self [ x . identifier ] = eval_literal ( v )
2019-08-28 21:24:54 +03:00
@dispatch
2024-01-11 02:55:07 +03:00
def __setitem__ ( self , x : ASTIdentifierNode , v ) :
self [ x . identifier ] = v
2019-08-28 21:24:54 +03:00
@dispatch
def __setitem__ ( self , k : str , v ) :
2024-01-11 02:55:07 +03:00
if ( self . parent is None or k in self . values ) : self . values [ k ] = v
else : self . parent [ k ] = v
2019-08-28 21:24:54 +03:00
@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 ]
2024-01-11 02:55:07 +03:00
except KeyError : return None
2020-03-18 07:00:12 +03:00
def items ( self ) :
2024-01-11 02:55:07 +03:00
return self . values . items ( ) if ( self . parent is None ) else { * * self . parent , * * self . values } . items ( )
2019-08-28 21:24:54 +03:00
def copy ( self ) :
2024-01-11 02:55:07 +03:00
return self . __class__ ( values = self . values . copy ( ) , parent = self . parent )
2019-08-28 21:24:54 +03:00
2024-01-11 02:55:07 +03:00
scope : . . .
signatures : lambda : Namespace . _Signatures ( parent = builtin_names )
values : lambda : Namespace . _Values ( parent = { i : . . . for i in builtin_names } )
weak : set
refcount : lambda : Sdict ( int )
warnclasses : paramset
flags : lambda : Sdict ( paramset )
olevel : int
@init ( signatures = . . . , values = . . . , weak = . . . , refcount = . . . , warnclasses = . . . , flags = . . . , olevel = . . . )
def __init__ ( self , scope ) :
self . scope = scope
2019-08-28 21:24:54 +03:00
def __repr__ ( self ) :
2024-01-11 02:55:07 +03:00
return f " <Namespace of scope ` { self . scope } ' > "
2019-08-28 21:24:54 +03:00
def __contains__ ( self , x ) :
2024-01-11 02:55:07 +03:00
return x in self . signatures
2019-08-28 21:24:54 +03:00
2024-01-11 02:55:07 +03:00
@lrucachedfunction
def derive ( self , scope , * , append = True ) :
assert ( type ( scope ) is str )
#return Namespace(signatures=self.signatures.copy(), values=self._Values(parent=self.values), weak=self.weak, scope=self.scope+'.'+scope if (append) else scope)
return Namespace ( signatures = self . signatures , values = self . _Values ( parent = self . values ) , weak = self . weak | set ( self . signatures ) , scope = self . scope + ' . ' + scope if ( append ) else scope ) # XXX.
#return Namespace(signatures=self._Signatures(parent=self.signatures), values=self._Values(parent=self.values), weak=self.weak | set(self.signatures), scope=self.scope+'.'+scope if (append) else scope)
2019-08-28 21:24:54 +03:00
@dispatch
def define ( self , x : ASTFuncdefNode ) :
2024-01-11 02:55:07 +03:00
self . define ( x , redefine = True )
self . values [ x . name ] = . . .
2020-03-18 07:00:12 +03:00
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 ) :
2024-01-11 02:55:07 +03:00
try : del self . signatures [ x . name ]
2019-08-28 21:24:54 +03:00
except KeyError : pass
2024-01-11 02:55:07 +03:00
try : del self . values [ x . name ]
2020-03-18 07:00:12 +03:00
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 ) :
2024-01-11 02:55:07 +03:00
if ( not redefine and x . identifier in self and x . identifier not in self . weak ) : raise SlValidationRedefinedError ( x , self . signatures [ x . identifier ] , scope = self . scope )
2020-03-18 07:00:12 +03:00
self . signatures [ x . identifier ] = sig
2024-01-11 02:55:07 +03:00
self . values . values [ x . identifier ] = None
2020-03-18 07:00:12 +03:00
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 ) :
2024-01-11 02:55:07 +03:00
return ast . validate ( ns )
2019-08-28 21:24:54 +03:00
2024-01-11 02:55:07 +03:00
class SlNodeException ( Exception , ABCSlots ) :
node : . . .
ctxnode : . . .
srclines : . . .
scope : . . .
2019-08-28 21:24:54 +03:00
2024-01-11 02:55:07 +03:00
def __init__ ( self , node , ctxnode = None , * , srclines = ( ) , scope = None ) :
self . node , self . ctxnode , self . srclines , self . scope = node , ctxnode if ( ctxnode is not None ) else node , srclines , scope
2019-08-28 21:24:54 +03:00
def __str__ ( self ) :
2024-01-11 02:55:07 +03:00
line = self . srclines [ self . lineno - 1 ] . partition ( ' \n ' ) [ 0 ] . rstrip ( ) if ( self . srclines ) else ' '
l = lstripcount ( line ) [ 0 ]
ctx = self . ctxnode
minlineno = min ( ( getattr ( ctx , i ) . lineno for i in allslots ( ctx ) if getattr ( getattr ( ctx , i ) , ' lineno ' , 0 ) > 0 ) , default = min ( ctx . lineno , self . lineno ) )
maxlineno = max ( ( getattr ( ctx , i ) . lineno for i in allslots ( ctx ) if getattr ( getattr ( ctx , i ) , ' lineno ' , 0 ) > 0 ) , default = max ( ctx . lineno , self . lineno ) )
minoffset = min ( ( getattr ( ctx , i ) . offset for i in allslots ( ctx ) if getattr ( getattr ( ctx , i ) , ' offset ' , - 1 ) > = 0 and getattr ( getattr ( ctx , i ) , ' lineno ' ) == self . lineno ) , default = self . node . offset )
maxoffsetlength , maxoffset = max ( ( ( getattr ( ctx , i ) . length , getattr ( ctx , i ) . offset ) for i in allslots ( ctx ) if getattr ( getattr ( ctx , i ) , ' offset ' , - 1 ) > = 0 and getattr ( getattr ( ctx , i ) , ' lineno ' ) == self . lineno ) , default = ( self . node . length , self . node . offset ) )
loff = min ( ( lstripcount ( i ) [ 0 ] for i in self . srclines [ minlineno - 1 : maxlineno ] ) , default = 0 )
srclines = tuple ( i [ loff : ] for i in self . srclines )
line , srclines = line [ loff : ] . expandtabs ( TAB_SIZE ) , tuple ( i . expandtabs ( TAB_SIZE ) for i in srclines )
loff = lstripcount ( line ) [ 0 ]
return ( f ' \033 [2m(in { self . scope } ) \033 [0m ' if ( self . scope is not None ) else ' ' ) + \
f " { self . __exline__ ( ) } { self . at } " + ( ' : \n ' + \
' \033 [1m ' + ( ' ' + ' \n ' . join ( srclines [ minlineno - 1 : self . lineno - 1 ] ) + ' \n ' if ( minlineno < self . lineno ) else ' ' ) + \
' ' + line [ : minoffset - l ] + ' \033 [91m ' * ( self . node . offset > = 0 ) + line [ minoffset - l : maxoffset + maxoffsetlength ] + ' \033 [0m ' + line [ maxoffset + maxoffsetlength : ] + ' \033 [0m \n ' + \
' \033 [95m ' + ' ' * ( 2 + loff + minoffset - l ) + ' ~ ' * ( self . node . offset - minoffset ) + ' ^ ' + ' ~ ' * ( maxoffset + maxoffsetlength - ( 2 + loff + minoffset - l ) - ( self . node . offset - minoffset ) + 1 ) + \
( ' \n \033 [0;91m ' + ' \n ' . join ( srclines [ self . lineno : maxlineno ] ) if ( maxlineno > self . lineno and len ( srclines ) > self . lineno ) else ' ' ) + \
' \033 [0m ' if ( srclines ) else ' ' ) + \
self . __exsubline__ ( ) + \
( f " \n \n \033 [1;95mCaused by: \033 [0m \n { self . __cause__ if ( isinstance ( self . __cause__ , ( SlSyntaxException , SlNodeException ) ) ) else ' ' + str ( ) . join ( traceback . format_exception ( type ( self . __cause__ ) , self . __cause__ , self . __cause__ . __traceback__ ) ) } " if ( self . __cause__ is not None ) else ' ' )
@abc.abstractmethod
def __exline__ ( self ) :
return ' '
def __exsubline__ ( self ) :
return ' '
2019-08-28 21:24:54 +03:00
@property
def at ( self ) :
2024-01-11 02:55:07 +03:00
return f " at line { self . node . lineno } , offset { self . node . offset } "
2019-08-28 21:24:54 +03:00
@property
def lineno ( self ) :
return self . node . lineno
2024-01-11 02:55:07 +03:00
class SlValidationException ( SlNodeException ) : pass
class SlValidationError ( SlValidationException ) :
desc : . . .
def __init__ ( self , desc , * args , * * kwargs ) :
super ( ) . __init__ ( * args , * * kwargs )
self . desc = desc
def __exline__ ( self ) :
return f " Validation error: { self . desc } "
2019-08-28 21:24:54 +03:00
class SlValidationNotDefinedError ( SlValidationError ) :
2024-01-11 02:55:07 +03:00
def __init__ ( self , identifier , * args , * * kwargs ) :
super ( ) . __init__ ( f " ` { identifier . identifier } ' is not defined " , identifier , * args , * * kwargs )
2019-08-28 21:24:54 +03:00
class SlValidationRedefinedError ( SlValidationError ) :
2024-01-11 02:55:07 +03:00
def __init__ ( self , identifier , definition , * args , * * kwargs ) :
super ( ) . __init__ ( f " ` { identifier } ' redefined (defined as ` { definition } ' ) " , identifier , * args , * * kwargs ) # at lineno {definition.lineno}
2019-08-28 21:24:54 +03:00
2024-01-11 02:55:07 +03:00
def optimize_ast ( ast , ns , level = DEFAULT_OLEVEL ) : return ast . optimize ( ns )
2019-08-28 21:24:54 +03:00
2024-01-11 02:55:07 +03:00
# by Sdore, 2021