2017-01-24 02:00:00 +08:00
# Copyright 2014-2017 The Meson development team
2014-03-13 01:40:39 +08:00
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
# http://www.apache.org/licenses/LICENSE-2.0
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
2018-01-01 00:50:52 +08:00
import re
2018-03-04 07:00:55 +08:00
import codecs
2019-12-07 21:42:23 +08:00
import textwrap
2018-07-27 19:31:54 +08:00
import types
2019-12-07 21:42:23 +08:00
import typing as T
2016-03-23 01:31:55 +08:00
from . mesonlib import MesonException
2017-04-16 00:57:46 +08:00
from . import mlog
2014-03-13 01:40:39 +08:00
2020-01-09 00:44:50 +08:00
if T . TYPE_CHECKING :
2019-12-07 21:42:23 +08:00
from . ast import AstVisitor
2018-03-04 07:00:55 +08:00
# This is the regex for the supported escape sequences of a regular string
# literal, like 'abc\x00'
ESCAPE_SEQUENCE_SINGLE_RE = re . compile ( r '''
2019-04-29 04:06:36 +08:00
( \\U [ A - Fa - f0 - 9 ] { 8 } # 8-digit hex escapes
| \\u [ A - Fa - f0 - 9 ] { 4 } # 4-digit hex escapes
| \\x [ A - Fa - f0 - 9 ] { 2 } # 2-digit hex escapes
| \\[ 0 - 7 ] { 1 , 3 } # Octal escapes
| \\N \{ [ ^ } ] + \} # Unicode characters by name
| \\[ \\' abfnrtv] # Single-character escapes
2018-03-04 07:00:55 +08:00
) ''' , re.UNICODE | re.VERBOSE)
2018-04-16 02:57:33 +08:00
class MesonUnicodeDecodeError ( MesonException ) :
def __init__ ( self , match ) :
super ( ) . __init__ ( " %s " % match )
self . match = match
2018-03-04 07:00:55 +08:00
def decode_match ( match ) :
2018-04-16 02:57:33 +08:00
try :
return codecs . decode ( match . group ( 0 ) , ' unicode_escape ' )
2019-01-29 04:42:42 +08:00
except UnicodeDecodeError :
2018-04-16 02:57:33 +08:00
raise MesonUnicodeDecodeError ( match . group ( 0 ) )
2018-03-04 07:00:55 +08:00
2014-04-01 01:11:54 +08:00
class ParseException ( MesonException ) :
2017-03-12 00:38:28 +08:00
def __init__ ( self , text , line , lineno , colno ) :
# Format as error message, followed by the line with the error, followed by a caret to show the error column.
super ( ) . __init__ ( " %s \n %s \n %s " % ( text , line , ' %s ^ ' % ( ' ' * colno ) ) )
self . lineno = lineno
self . colno = colno
class BlockParseException ( MesonException ) :
def __init__ ( self , text , line , lineno , colno , start_line , start_lineno , start_colno ) :
# This can be formatted in two ways - one if the block start and end are on the same line, and a different way if they are on different lines.
if lineno == start_lineno :
# If block start and end are on the same line, it is formatted as:
# Error message
# Followed by the line with the error
# Followed by a caret to show the block start
# Followed by underscores
# Followed by a caret to show the block end.
super ( ) . __init__ ( " %s \n %s \n %s " % ( text , line , ' %s ^ %s ^ ' % ( ' ' * start_colno , ' _ ' * ( colno - start_colno - 1 ) ) ) )
else :
# If block start and end are on different lines, it is formatted as:
# Error message
# Followed by the line with the error
# Followed by a caret to show the error column.
# Followed by a message saying where the block started.
# Followed by the line of the block start.
# Followed by a caret for the block start.
super ( ) . __init__ ( " %s \n %s \n %s \n For a block that started at %d , %d \n %s \n %s " % ( text , line , ' %s ^ ' % ( ' ' * colno ) , start_lineno , start_colno , start_line , " %s ^ " % ( ' ' * start_colno ) ) )
2014-03-13 01:40:39 +08:00
self . lineno = lineno
self . colno = colno
2020-01-09 00:44:50 +08:00
TV_TokenTypes = T . TypeVar ( ' TV_TokenTypes ' , int , str , bool )
2019-12-10 06:17:25 +08:00
2020-01-09 00:44:50 +08:00
class Token ( T . Generic [ TV_TokenTypes ] ) :
2020-01-29 03:57:07 +08:00
def __init__ ( self , tid : str , filename : str , line_start : int , lineno : int , colno : int , bytespan : T . Tuple [ int , int ] , value : TV_TokenTypes ) :
2019-12-07 21:42:23 +08:00
self . tid = tid # type: str
self . filename = filename # type: str
self . line_start = line_start # type: int
self . lineno = lineno # type: int
self . colno = colno # type: int
2020-01-09 00:44:50 +08:00
self . bytespan = bytespan # type: T.Tuple[int, int]
2019-12-10 06:17:25 +08:00
self . value = value # type: TV_TokenTypes
2019-12-07 21:42:23 +08:00
def __eq__ ( self , other ) - > bool :
2014-03-13 04:05:19 +08:00
if isinstance ( other , str ) :
return self . tid == other
2014-03-13 02:42:40 +08:00
return self . tid == other . tid
2014-03-13 01:40:39 +08:00
class Lexer :
2020-01-29 03:57:07 +08:00
def __init__ ( self , code : str ) :
2017-03-12 00:38:28 +08:00
self . code = code
2014-03-13 01:40:39 +08:00
self . keywords = { ' true ' , ' false ' , ' if ' , ' else ' , ' elif ' ,
2018-07-18 01:54:56 +08:00
' endif ' , ' and ' , ' or ' , ' not ' , ' foreach ' , ' endforeach ' ,
2018-07-18 04:47:41 +08:00
' in ' , ' continue ' , ' break ' }
self . future_keywords = { ' return ' }
2014-03-13 01:40:39 +08:00
self . token_specification = [
# Need to be sorted longest to shortest.
( ' ignore ' , re . compile ( r ' [ \ t] ' ) ) ,
( ' id ' , re . compile ( ' [_a-zA-Z][_0-9a-zA-Z]* ' ) ) ,
2018-03-07 14:10:01 +08:00
( ' number ' , re . compile ( r ' 0[bB][01]+|0[oO][0-7]+|0[xX][0-9a-fA-F]+|0|[1-9] \ d* ' ) ) ,
2014-03-13 01:40:39 +08:00
( ' eol_cont ' , re . compile ( r ' \\ \ n ' ) ) ,
( ' eol ' , re . compile ( r ' \ n ' ) ) ,
( ' multiline_string ' , re . compile ( r " ' ' ' (.| \ n)*? ' ' ' " , re . M ) ) ,
2017-01-17 15:57:20 +08:00
( ' comment ' , re . compile ( r ' #.* ' ) ) ,
2014-03-13 01:40:39 +08:00
( ' lparen ' , re . compile ( r ' \ ( ' ) ) ,
( ' rparen ' , re . compile ( r ' \ ) ' ) ) ,
( ' lbracket ' , re . compile ( r ' \ [ ' ) ) ,
2014-03-14 06:02:44 +08:00
( ' rbracket ' , re . compile ( r ' \ ] ' ) ) ,
2018-04-28 07:56:56 +08:00
( ' lcurl ' , re . compile ( r ' \ { ' ) ) ,
( ' rcurl ' , re . compile ( r ' \ } ' ) ) ,
2015-06-09 00:02:45 +08:00
( ' dblquote ' , re . compile ( r ' " ' ) ) ,
2015-03-26 06:04:38 +08:00
( ' string ' , re . compile ( r " ' ([^ ' \\ ]|( \\ .))* ' " ) ) ,
2014-03-13 01:40:39 +08:00
( ' comma ' , re . compile ( r ' , ' ) ) ,
2015-08-14 21:05:58 +08:00
( ' plusassign ' , re . compile ( r ' \ += ' ) ) ,
2014-03-13 01:40:39 +08:00
( ' dot ' , re . compile ( r ' \ . ' ) ) ,
2014-11-08 09:41:05 +08:00
( ' plus ' , re . compile ( r ' \ + ' ) ) ,
( ' dash ' , re . compile ( r ' - ' ) ) ,
( ' star ' , re . compile ( r ' \ * ' ) ) ,
2017-01-17 15:57:20 +08:00
( ' percent ' , re . compile ( r ' % ' ) ) ,
2014-11-08 09:41:05 +08:00
( ' fslash ' , re . compile ( r ' / ' ) ) ,
2014-03-13 04:59:00 +08:00
( ' colon ' , re . compile ( r ' : ' ) ) ,
2014-03-14 03:55:06 +08:00
( ' equal ' , re . compile ( r ' == ' ) ) ,
2017-01-17 15:57:20 +08:00
( ' nequal ' , re . compile ( r ' != ' ) ) ,
2014-03-14 03:55:06 +08:00
( ' assign ' , re . compile ( r ' = ' ) ) ,
2016-01-26 04:12:40 +08:00
( ' le ' , re . compile ( r ' <= ' ) ) ,
( ' lt ' , re . compile ( r ' < ' ) ) ,
( ' ge ' , re . compile ( r ' >= ' ) ) ,
( ' gt ' , re . compile ( r ' > ' ) ) ,
2016-06-20 02:56:09 +08:00
( ' questionmark ' , re . compile ( r ' \ ? ' ) ) ,
2014-03-13 01:40:39 +08:00
]
2019-12-07 21:42:23 +08:00
def getline ( self , line_start : int ) - > str :
2017-03-12 00:38:28 +08:00
return self . code [ line_start : self . code . find ( ' \n ' , line_start ) ]
2019-12-07 21:42:23 +08:00
def lex ( self , filename : str ) - > T . Generator [ Token , None , None ] :
2014-03-13 01:40:39 +08:00
line_start = 0
2017-03-12 00:38:28 +08:00
lineno = 1
2017-01-01 03:27:52 +08:00
loc = 0
2014-03-13 01:40:39 +08:00
par_count = 0
bracket_count = 0
2018-04-28 07:56:56 +08:00
curl_count = 0
2014-03-13 01:40:39 +08:00
col = 0
2017-03-12 00:38:28 +08:00
while loc < len ( self . code ) :
2014-03-13 01:40:39 +08:00
matched = False
2020-01-09 00:44:50 +08:00
value = None # type: T.Union[str, bool, int]
2014-03-13 01:40:39 +08:00
for ( tid , reg ) in self . token_specification :
2017-03-12 00:38:28 +08:00
mo = reg . match ( self . code , loc )
2014-03-13 01:40:39 +08:00
if mo :
curline = lineno
2017-03-12 00:38:28 +08:00
curline_start = line_start
2017-01-01 03:02:15 +08:00
col = mo . start ( ) - line_start
2014-03-13 01:40:39 +08:00
matched = True
2016-11-20 07:46:16 +08:00
span_start = loc
2014-03-13 01:40:39 +08:00
loc = mo . end ( )
2016-11-20 07:46:16 +08:00
span_end = loc
bytespan = ( span_start , span_end )
2014-03-13 01:40:39 +08:00
match_text = mo . group ( )
2014-03-13 04:05:19 +08:00
if tid == ' ignore ' or tid == ' comment ' :
2014-03-13 01:40:39 +08:00
break
elif tid == ' lparen ' :
par_count + = 1
elif tid == ' rparen ' :
par_count - = 1
elif tid == ' lbracket ' :
bracket_count + = 1
elif tid == ' rbracket ' :
bracket_count - = 1
2018-04-28 07:56:56 +08:00
elif tid == ' lcurl ' :
curl_count + = 1
elif tid == ' rcurl ' :
curl_count - = 1
2015-06-09 00:02:45 +08:00
elif tid == ' dblquote ' :
2017-03-12 00:38:28 +08:00
raise ParseException ( ' Double quotes are not supported. Use single quotes. ' , self . getline ( line_start ) , lineno , col )
2014-03-14 02:50:00 +08:00
elif tid == ' string ' :
2018-02-19 09:18:51 +08:00
# Handle here and not on the regexp to give a better error message.
if match_text . find ( " \n " ) != - 1 :
2019-12-07 21:42:23 +08:00
mlog . warning ( textwrap . dedent ( """ \
Newline character in a string detected , use ''' (three single quotes) for multiline strings instead.
This will become a hard error in a future Meson release . \
""" ),
self . getline ( line_start ) ,
str ( lineno ) ,
str ( col )
)
2018-03-04 07:00:55 +08:00
value = match_text [ 1 : - 1 ]
2018-04-16 02:57:33 +08:00
try :
value = ESCAPE_SEQUENCE_SINGLE_RE . sub ( decode_match , value )
except MesonUnicodeDecodeError as err :
raise MesonException ( " Failed to parse escape sequence: ' {} ' in string: \n {} " . format ( err . match , match_text ) )
2014-03-13 01:40:39 +08:00
elif tid == ' multiline_string ' :
2014-03-13 04:05:19 +08:00
tid = ' string '
2014-03-14 02:50:00 +08:00
value = match_text [ 3 : - 3 ]
2014-03-13 01:40:39 +08:00
lines = match_text . split ( ' \n ' )
if len ( lines ) > 1 :
lineno + = len ( lines ) - 1
line_start = mo . end ( ) - len ( lines [ - 1 ] )
2014-03-14 02:50:00 +08:00
elif tid == ' number ' :
2018-03-07 14:10:01 +08:00
value = int ( match_text , base = 0 )
2019-01-04 23:09:05 +08:00
elif tid == ' eol_cont ' :
lineno + = 1
line_start = loc
break
elif tid == ' eol ' :
2014-03-13 01:40:39 +08:00
lineno + = 1
line_start = loc
2018-04-28 07:56:56 +08:00
if par_count > 0 or bracket_count > 0 or curl_count > 0 :
2014-03-13 01:40:39 +08:00
break
2014-03-13 04:46:38 +08:00
elif tid == ' id ' :
if match_text in self . keywords :
tid = match_text
2014-03-14 02:50:00 +08:00
else :
2018-07-27 19:31:54 +08:00
if match_text in self . future_keywords :
mlog . warning ( " Identifier ' {} ' will become a reserved keyword in a future release. Please rename it. " . format ( match_text ) ,
2020-02-21 02:50:24 +08:00
location = types . SimpleNamespace ( filename = filename , lineno = lineno ) )
2014-03-14 02:50:00 +08:00
value = match_text
2020-02-21 02:50:24 +08:00
yield Token ( tid , filename , curline_start , curline , col , bytespan , value )
2015-05-10 00:52:10 +08:00
break
2014-03-13 01:40:39 +08:00
if not matched :
2017-03-12 00:38:28 +08:00
raise ParseException ( ' lexer ' , self . getline ( line_start ) , lineno , col )
2014-03-13 01:40:39 +08:00
2019-01-16 01:45:25 +08:00
class BaseNode :
2020-01-29 03:57:07 +08:00
def __init__ ( self , lineno : int , colno : int , filename : str , end_lineno : T . Optional [ int ] = None , end_colno : T . Optional [ int ] = None ) :
2019-12-07 21:42:23 +08:00
self . lineno = lineno # type: int
self . colno = colno # type: int
self . filename = filename # type: str
self . end_lineno = end_lineno if end_lineno is not None else self . lineno
self . end_colno = end_colno if end_colno is not None else self . colno
2019-12-08 02:18:26 +08:00
# Attributes for the visitors
self . level = 0 # type: int
self . ast_id = ' ' # type: str
self . condition_level = 0 # type: int
2019-12-07 21:42:23 +08:00
def accept ( self , visitor : ' AstVisitor ' ) - > None :
2019-01-16 01:45:25 +08:00
fname = ' visit_ {} ' . format ( type ( self ) . __name__ )
if hasattr ( visitor , fname ) :
func = getattr ( visitor , fname )
2019-04-23 20:59:54 +08:00
if callable ( func ) :
2019-01-16 01:45:25 +08:00
func ( self )
2020-01-09 00:44:50 +08:00
class ElementaryNode ( T . Generic [ TV_TokenTypes ] , BaseNode ) :
2020-01-29 03:57:07 +08:00
def __init__ ( self , token : Token [ TV_TokenTypes ] ) :
2019-12-07 21:42:23 +08:00
super ( ) . __init__ ( token . lineno , token . colno , token . filename )
2019-12-10 06:17:25 +08:00
self . value = token . value # type: TV_TokenTypes
2020-01-09 00:44:50 +08:00
self . bytespan = token . bytespan # type: T.Tuple[int, int]
2016-11-20 07:46:16 +08:00
2019-12-10 06:17:25 +08:00
class BooleanNode ( ElementaryNode [ bool ] ) :
2020-01-29 03:57:07 +08:00
def __init__ ( self , token : Token [ bool ] ) :
2016-11-20 07:46:16 +08:00
super ( ) . __init__ ( token )
2019-12-10 06:17:25 +08:00
assert isinstance ( self . value , bool )
2014-03-14 02:50:00 +08:00
2019-12-10 06:17:25 +08:00
class IdNode ( ElementaryNode [ str ] ) :
2020-01-29 03:57:07 +08:00
def __init__ ( self , token : Token [ str ] ) :
2016-11-20 07:46:16 +08:00
super ( ) . __init__ ( token )
2019-12-10 06:17:25 +08:00
assert isinstance ( self . value , str )
2014-03-14 02:50:00 +08:00
2015-03-16 04:31:10 +08:00
def __str__ ( self ) :
return " Id node: ' %s ' ( %d , %d ). " % ( self . value , self . lineno , self . colno )
2019-12-10 06:17:25 +08:00
class NumberNode ( ElementaryNode [ int ] ) :
2020-01-29 03:57:07 +08:00
def __init__ ( self , token : Token [ int ] ) :
2016-11-20 07:46:16 +08:00
super ( ) . __init__ ( token )
2019-12-10 06:17:25 +08:00
assert isinstance ( self . value , int )
2014-03-14 02:50:00 +08:00
2019-12-10 06:17:25 +08:00
class StringNode ( ElementaryNode [ str ] ) :
2020-01-29 03:57:07 +08:00
def __init__ ( self , token : Token [ str ] ) :
2016-11-20 07:46:16 +08:00
super ( ) . __init__ ( token )
2019-12-10 06:17:25 +08:00
assert isinstance ( self . value , str )
2014-03-14 02:50:00 +08:00
2015-03-16 04:31:10 +08:00
def __str__ ( self ) :
return " String node: ' %s ' ( %d , %d ). " % ( self . value , self . lineno , self . colno )
2018-07-18 04:47:41 +08:00
class ContinueNode ( ElementaryNode ) :
pass
class BreakNode ( ElementaryNode ) :
pass
2019-12-07 21:42:23 +08:00
class ArgumentNode ( BaseNode ) :
2020-01-29 03:57:07 +08:00
def __init__ ( self , token : Token [ TV_TokenTypes ] ) :
2019-12-07 21:42:23 +08:00
super ( ) . __init__ ( token . lineno , token . colno , token . filename )
2020-01-09 00:44:50 +08:00
self . arguments = [ ] # type: T.List[BaseNode]
self . commas = [ ] # type: T.List[Token[TV_TokenTypes]]
self . kwargs = { } # type: T.Dict[BaseNode, BaseNode]
2019-12-07 21:42:23 +08:00
self . order_error = False
def prepend ( self , statement : BaseNode ) - > None :
if self . num_kwargs ( ) > 0 :
self . order_error = True
if not isinstance ( statement , EmptyNode ) :
self . arguments = [ statement ] + self . arguments
def append ( self , statement : BaseNode ) - > None :
if self . num_kwargs ( ) > 0 :
self . order_error = True
if not isinstance ( statement , EmptyNode ) :
self . arguments + = [ statement ]
def set_kwarg ( self , name : IdNode , value : BaseNode ) - > None :
if name . value in [ x . value for x in self . kwargs . keys ( ) if isinstance ( x , IdNode ) ] :
mlog . warning ( ' Keyword argument " {} " defined multiple times. ' . format ( name . value ) , location = self )
mlog . warning ( ' This will be an error in future Meson releases. ' )
self . kwargs [ name ] = value
def set_kwarg_no_check ( self , name : BaseNode , value : BaseNode ) - > None :
self . kwargs [ name ] = value
def num_args ( self ) - > int :
return len ( self . arguments )
def num_kwargs ( self ) - > int :
return len ( self . kwargs )
def incorrect_order ( self ) - > bool :
return self . order_error
def __len__ ( self ) - > int :
return self . num_args ( ) # Fixme
2019-01-16 01:45:25 +08:00
class ArrayNode ( BaseNode ) :
2020-01-29 03:57:07 +08:00
def __init__ ( self , args : ArgumentNode , lineno : int , colno : int , end_lineno : int , end_colno : int ) :
2019-12-07 21:42:23 +08:00
super ( ) . __init__ ( lineno , colno , args . filename , end_lineno = end_lineno , end_colno = end_colno )
self . args = args # type: ArgumentNode
2014-03-14 06:02:44 +08:00
2019-01-16 01:45:25 +08:00
class DictNode ( BaseNode ) :
2020-01-29 03:57:07 +08:00
def __init__ ( self , args : ArgumentNode , lineno : int , colno : int , end_lineno : int , end_colno : int ) :
2019-12-07 21:42:23 +08:00
super ( ) . __init__ ( lineno , colno , args . filename , end_lineno = end_lineno , end_colno = end_colno )
2018-04-28 07:56:56 +08:00
self . args = args
2019-01-16 01:45:25 +08:00
class EmptyNode ( BaseNode ) :
2020-01-29 03:57:07 +08:00
def __init__ ( self , lineno : int , colno : int , filename : str ) :
2019-12-07 21:42:23 +08:00
super ( ) . __init__ ( lineno , colno , filename )
2014-03-14 03:16:41 +08:00
self . value = None
2019-01-16 01:45:25 +08:00
class OrNode ( BaseNode ) :
2020-01-29 03:57:07 +08:00
def __init__ ( self , left : BaseNode , right : BaseNode ) :
2019-12-07 21:42:23 +08:00
super ( ) . __init__ ( left . lineno , left . colno , left . filename )
self . left = left # type: BaseNode
self . right = right # type: BaseNode
2014-03-14 04:30:10 +08:00
2019-01-16 01:45:25 +08:00
class AndNode ( BaseNode ) :
2020-01-29 03:57:07 +08:00
def __init__ ( self , left : BaseNode , right : BaseNode ) :
2019-12-07 21:42:23 +08:00
super ( ) . __init__ ( left . lineno , left . colno , left . filename )
self . left = left # type: BaseNode
self . right = right # type: BaseNode
2014-03-14 04:30:10 +08:00
2019-01-16 01:45:25 +08:00
class ComparisonNode ( BaseNode ) :
2020-01-29 03:57:07 +08:00
def __init__ ( self , ctype : str , left : BaseNode , right : BaseNode ) :
2019-12-07 21:42:23 +08:00
super ( ) . __init__ ( left . lineno , left . colno , left . filename )
self . left = left # type: BaseNode
self . right = right # type: BaseNode
self . ctype = ctype # type: str
2014-03-14 04:30:10 +08:00
2019-01-16 01:45:25 +08:00
class ArithmeticNode ( BaseNode ) :
2020-01-29 03:57:07 +08:00
def __init__ ( self , operation : str , left : BaseNode , right : BaseNode ) :
2019-12-07 21:42:23 +08:00
super ( ) . __init__ ( left . lineno , left . colno , left . filename )
self . left = left # type: BaseNode
self . right = right # type: BaseNode
self . operation = operation # type: str
2014-11-08 09:41:05 +08:00
2019-01-16 01:45:25 +08:00
class NotNode ( BaseNode ) :
2020-01-29 03:57:07 +08:00
def __init__ ( self , token : Token [ TV_TokenTypes ] , value : BaseNode ) :
2019-12-07 21:42:23 +08:00
super ( ) . __init__ ( token . lineno , token . colno , token . filename )
self . value = value # type: BaseNode
2014-03-14 04:30:10 +08:00
2019-01-16 01:45:25 +08:00
class CodeBlockNode ( BaseNode ) :
2020-01-29 03:57:07 +08:00
def __init__ ( self , token : Token [ TV_TokenTypes ] ) :
2019-12-07 21:42:23 +08:00
super ( ) . __init__ ( token . lineno , token . colno , token . filename )
2020-01-09 00:44:50 +08:00
self . lines = [ ] # type: T.List[BaseNode]
2014-03-14 03:55:06 +08:00
2019-01-16 01:45:25 +08:00
class IndexNode ( BaseNode ) :
2020-01-29 03:57:07 +08:00
def __init__ ( self , iobject : BaseNode , index : BaseNode ) :
2019-12-07 21:42:23 +08:00
super ( ) . __init__ ( iobject . lineno , iobject . colno , iobject . filename )
self . iobject = iobject # type: BaseNode
self . index = index # type: BaseNode
2015-08-20 04:34:49 +08:00
2019-01-16 01:45:25 +08:00
class MethodNode ( BaseNode ) :
2020-01-29 03:57:07 +08:00
def __init__ ( self , filename : str , lineno : int , colno : int , source_object : BaseNode , name : str , args : ArgumentNode ) :
2019-12-07 21:42:23 +08:00
super ( ) . __init__ ( lineno , colno , filename )
self . source_object = source_object # type: BaseNode
self . name = name # type: str
2014-03-14 03:55:06 +08:00
assert ( isinstance ( self . name , str ) )
2019-12-07 21:42:23 +08:00
self . args = args # type: ArgumentNode
2014-03-14 03:55:06 +08:00
2019-01-16 01:45:25 +08:00
class FunctionNode ( BaseNode ) :
2020-01-29 03:57:07 +08:00
def __init__ ( self , filename : str , lineno : int , colno : int , end_lineno : int , end_colno : int , func_name : str , args : ArgumentNode ) :
2019-12-07 21:42:23 +08:00
super ( ) . __init__ ( lineno , colno , filename , end_lineno = end_lineno , end_colno = end_colno )
self . func_name = func_name # type: str
2014-03-14 03:55:06 +08:00
assert ( isinstance ( func_name , str ) )
2019-12-07 21:42:23 +08:00
self . args = args # type: ArgumentNode
2014-03-14 03:55:06 +08:00
2019-01-16 01:45:25 +08:00
class AssignmentNode ( BaseNode ) :
2020-01-29 03:57:07 +08:00
def __init__ ( self , filename : str , lineno : int , colno : int , var_name : str , value : BaseNode ) :
2019-12-07 21:42:23 +08:00
super ( ) . __init__ ( lineno , colno , filename )
self . var_name = var_name # type: str
2014-03-14 03:55:06 +08:00
assert ( isinstance ( var_name , str ) )
2019-12-07 21:42:23 +08:00
self . value = value # type: BaseNode
2014-03-14 03:55:06 +08:00
2019-01-16 01:45:25 +08:00
class PlusAssignmentNode ( BaseNode ) :
2020-01-29 03:57:07 +08:00
def __init__ ( self , filename : str , lineno : int , colno : int , var_name : str , value : BaseNode ) :
2019-12-07 21:42:23 +08:00
super ( ) . __init__ ( lineno , colno , filename )
self . var_name = var_name # type: str
2015-08-14 21:05:58 +08:00
assert ( isinstance ( var_name , str ) )
2019-12-07 21:42:23 +08:00
self . value = value # type: BaseNode
2015-08-14 21:05:58 +08:00
2019-01-16 01:45:25 +08:00
class ForeachClauseNode ( BaseNode ) :
2020-01-29 03:57:07 +08:00
def __init__ ( self , token : Token , varnames : T . List [ str ] , items : BaseNode , block : CodeBlockNode ) :
2019-12-07 21:42:23 +08:00
super ( ) . __init__ ( token . lineno , token . colno , token . filename )
2020-01-09 00:44:50 +08:00
self . varnames = varnames # type: T.List[str]
2019-12-07 21:42:23 +08:00
self . items = items # type: BaseNode
self . block = block # type: CodeBlockNode
class IfNode ( BaseNode ) :
def __init__ ( self , linenode : BaseNode , condition : BaseNode , block : CodeBlockNode ) :
super ( ) . __init__ ( linenode . lineno , linenode . colno , linenode . filename )
self . condition = condition # type: BaseNode
self . block = block # type: CodeBlockNode
2014-11-06 02:38:35 +08:00
2019-01-16 01:45:25 +08:00
class IfClauseNode ( BaseNode ) :
2020-01-29 03:57:07 +08:00
def __init__ ( self , linenode : BaseNode ) :
2019-12-07 21:42:23 +08:00
super ( ) . __init__ ( linenode . lineno , linenode . colno , linenode . filename )
2019-12-13 00:03:53 +08:00
self . ifs = [ ] # type: T.List[IfNode]
self . elseblock = None # type: T.Union[EmptyNode, CodeBlockNode]
2014-03-14 04:30:10 +08:00
2019-01-16 01:45:25 +08:00
class UMinusNode ( BaseNode ) :
2019-12-07 21:42:23 +08:00
def __init__ ( self , current_location : Token , value : BaseNode ) :
super ( ) . __init__ ( current_location . lineno , current_location . colno , current_location . filename )
self . value = value # type: BaseNode
2014-03-14 04:30:10 +08:00
2019-01-16 01:45:25 +08:00
class TernaryNode ( BaseNode ) :
2019-12-07 21:42:23 +08:00
def __init__ ( self , condition : BaseNode , trueblock : BaseNode , falseblock : BaseNode ) :
super ( ) . __init__ ( condition . lineno , condition . colno , condition . filename )
self . condition = condition # type: BaseNode
self . trueblock = trueblock # type: BaseNode
self . falseblock = falseblock # type: BaseNode
2014-03-14 03:16:41 +08:00
2016-01-26 04:12:40 +08:00
comparison_map = { ' equal ' : ' == ' ,
' nequal ' : ' != ' ,
' lt ' : ' < ' ,
' le ' : ' <= ' ,
' gt ' : ' > ' ,
2018-07-18 01:54:56 +08:00
' ge ' : ' >= ' ,
' in ' : ' in ' ,
' notin ' : ' not in ' ,
2016-01-26 04:12:40 +08:00
}
2014-03-13 04:46:38 +08:00
# Recursive descent parser for Meson's definition language.
# Very basic apart from the fact that we have many precedence
# levels so there are not enough words to describe them all.
# Enter numbering:
#
# 1 assignment
# 2 or
# 3 and
2014-11-08 09:41:05 +08:00
# 4 comparison
# 5 arithmetic
# 6 negation
# 7 funcall, method call
# 8 parentheses
# 9 plain token
2014-03-13 04:46:38 +08:00
2014-03-13 02:42:40 +08:00
class Parser :
2020-01-29 03:57:07 +08:00
def __init__ ( self , code : str , filename : str ) :
2017-03-12 00:38:28 +08:00
self . lexer = Lexer ( code )
2020-02-21 02:50:24 +08:00
self . stream = self . lexer . lex ( filename )
2019-12-10 06:17:25 +08:00
self . current = Token ( ' eof ' , ' ' , 0 , 0 , 0 , ( 0 , 0 ) , None ) # type: Token
2014-03-13 02:42:40 +08:00
self . getsym ( )
2016-06-20 03:02:59 +08:00
self . in_ternary = False
2014-03-13 02:42:40 +08:00
2019-12-07 21:42:23 +08:00
def getsym ( self ) - > None :
2014-03-13 04:53:18 +08:00
try :
self . current = next ( self . stream )
except StopIteration :
2017-03-12 00:38:28 +08:00
self . current = Token ( ' eof ' , ' ' , self . current . line_start , self . current . lineno , self . current . colno + self . current . bytespan [ 1 ] - self . current . bytespan [ 0 ] , ( 0 , 0 ) , None )
2014-03-13 02:42:40 +08:00
2019-12-07 21:42:23 +08:00
def getline ( self ) - > str :
2017-04-20 23:49:02 +08:00
return self . lexer . getline ( self . current . line_start )
2019-12-07 21:42:23 +08:00
def accept ( self , s : str ) - > bool :
2014-03-13 02:42:40 +08:00
if self . current . tid == s :
self . getsym ( )
return True
return False
2014-03-13 04:53:18 +08:00
2020-04-03 02:41:22 +08:00
def accept_any ( self , tids : T . Sequence [ str ] ) - > str :
tid = self . current . tid
if tid in tids :
self . getsym ( )
return tid
return ' '
2019-12-07 21:42:23 +08:00
def expect ( self , s : str ) - > bool :
2014-03-13 02:42:40 +08:00
if self . accept ( s ) :
return True
2017-04-20 23:49:02 +08:00
raise ParseException ( ' Expecting %s got %s . ' % ( s , self . current . tid ) , self . getline ( ) , self . current . lineno , self . current . colno )
2017-03-12 00:38:28 +08:00
2019-12-07 21:42:23 +08:00
def block_expect ( self , s : str , block_start : Token ) - > bool :
2017-03-12 00:38:28 +08:00
if self . accept ( s ) :
return True
2017-04-20 23:49:02 +08:00
raise BlockParseException ( ' Expecting %s got %s . ' % ( s , self . current . tid ) , self . getline ( ) , self . current . lineno , self . current . colno , self . lexer . getline ( block_start . line_start ) , block_start . lineno , block_start . colno )
2014-03-13 02:42:40 +08:00
2019-12-07 21:42:23 +08:00
def parse ( self ) - > CodeBlockNode :
2014-03-14 03:55:06 +08:00
block = self . codeblock ( )
2014-03-14 06:06:59 +08:00
self . expect ( ' eof ' )
return block
2014-03-13 02:42:40 +08:00
2019-12-07 21:42:23 +08:00
def statement ( self ) - > BaseNode :
2014-03-14 03:55:06 +08:00
return self . e1 ( )
2014-03-13 04:46:38 +08:00
2019-12-07 21:42:23 +08:00
def e1 ( self ) - > BaseNode :
2014-03-14 03:55:06 +08:00
left = self . e2 ( )
2015-08-14 21:05:58 +08:00
if self . accept ( ' plusassign ' ) :
value = self . e1 ( )
if not isinstance ( left , IdNode ) :
2017-04-20 23:49:02 +08:00
raise ParseException ( ' Plusassignment target must be an id. ' , self . getline ( ) , left . lineno , left . colno )
2019-12-07 21:42:23 +08:00
assert isinstance ( left . value , str )
2020-02-21 02:50:24 +08:00
return PlusAssignmentNode ( left . filename , left . lineno , left . colno , left . value , value )
2015-08-14 21:05:58 +08:00
elif self . accept ( ' assign ' ) :
2014-03-14 03:55:06 +08:00
value = self . e1 ( )
if not isinstance ( left , IdNode ) :
raise ParseException ( ' Assignment target must be an id. ' ,
2017-04-20 23:49:02 +08:00
self . getline ( ) , left . lineno , left . colno )
2019-12-07 21:42:23 +08:00
assert isinstance ( left . value , str )
2020-02-21 02:50:24 +08:00
return AssignmentNode ( left . filename , left . lineno , left . colno , left . value , value )
2016-06-20 02:56:09 +08:00
elif self . accept ( ' questionmark ' ) :
2016-06-20 03:02:59 +08:00
if self . in_ternary :
raise ParseException ( ' Nested ternary operators are not allowed. ' ,
2017-04-20 23:49:02 +08:00
self . getline ( ) , left . lineno , left . colno )
2016-06-20 03:02:59 +08:00
self . in_ternary = True
2016-06-20 02:56:09 +08:00
trueblock = self . e1 ( )
self . expect ( ' colon ' )
falseblock = self . e1 ( )
2016-06-20 03:02:59 +08:00
self . in_ternary = False
2019-12-07 21:42:23 +08:00
return TernaryNode ( left , trueblock , falseblock )
2014-03-14 03:55:06 +08:00
return left
2014-03-13 04:46:38 +08:00
2019-12-07 21:42:23 +08:00
def e2 ( self ) - > BaseNode :
2014-03-14 03:55:06 +08:00
left = self . e3 ( )
2014-09-19 23:08:28 +08:00
while self . accept ( ' or ' ) :
2017-06-26 02:43:38 +08:00
if isinstance ( left , EmptyNode ) :
raise ParseException ( ' Invalid or clause. ' ,
self . getline ( ) , left . lineno , left . colno )
2016-12-05 00:28:25 +08:00
left = OrNode ( left , self . e3 ( ) )
2014-03-14 03:55:06 +08:00
return left
2014-03-13 04:46:38 +08:00
2019-12-07 21:42:23 +08:00
def e3 ( self ) - > BaseNode :
2014-03-14 03:55:06 +08:00
left = self . e4 ( )
2014-09-19 23:08:28 +08:00
while self . accept ( ' and ' ) :
2017-06-26 02:43:38 +08:00
if isinstance ( left , EmptyNode ) :
raise ParseException ( ' Invalid and clause. ' ,
self . getline ( ) , left . lineno , left . colno )
2017-03-22 05:01:21 +08:00
left = AndNode ( left , self . e4 ( ) )
2014-03-14 03:55:06 +08:00
return left
2014-03-13 04:46:38 +08:00
2019-12-07 21:42:23 +08:00
def e4 ( self ) - > BaseNode :
2014-03-14 03:55:06 +08:00
left = self . e5 ( )
2016-01-26 04:12:40 +08:00
for nodename , operator_type in comparison_map . items ( ) :
if self . accept ( nodename ) :
2016-12-05 00:28:25 +08:00
return ComparisonNode ( operator_type , left , self . e5 ( ) )
2018-07-18 01:54:56 +08:00
if self . accept ( ' not ' ) and self . accept ( ' in ' ) :
return ComparisonNode ( ' notin ' , left , self . e5 ( ) )
2014-03-14 03:55:06 +08:00
return left
2019-12-07 21:42:23 +08:00
def e5 ( self ) - > BaseNode :
2020-04-03 02:41:22 +08:00
return self . e5addsub ( )
def e5addsub ( self ) - > BaseNode :
op_map = {
' plus ' : ' add ' ,
' dash ' : ' sub ' ,
}
left = self . e5muldiv ( )
while True :
op = self . accept_any ( tuple ( op_map . keys ( ) ) )
if op :
left = ArithmeticNode ( op_map [ op ] , left , self . e5muldiv ( ) )
else :
break
2014-11-08 09:41:05 +08:00
return left
2020-04-03 02:41:22 +08:00
def e5muldiv ( self ) - > BaseNode :
op_map = {
' percent ' : ' mod ' ,
' star ' : ' mul ' ,
' fslash ' : ' div ' ,
}
2014-11-08 09:41:05 +08:00
left = self . e6 ( )
2020-04-03 02:41:22 +08:00
while True :
op = self . accept_any ( tuple ( op_map . keys ( ) ) )
if op :
left = ArithmeticNode ( op_map [ op ] , left , self . e6 ( ) )
else :
break
2014-11-08 09:41:05 +08:00
return left
2014-03-13 04:46:38 +08:00
2019-12-07 21:42:23 +08:00
def e6 ( self ) - > BaseNode :
2014-11-08 09:41:05 +08:00
if self . accept ( ' not ' ) :
2016-12-05 00:28:25 +08:00
return NotNode ( self . current , self . e7 ( ) )
2015-05-10 00:52:10 +08:00
if self . accept ( ' dash ' ) :
2016-12-05 00:28:25 +08:00
return UMinusNode ( self . current , self . e7 ( ) )
2014-11-08 09:41:05 +08:00
return self . e7 ( )
2019-12-07 21:42:23 +08:00
def e7 ( self ) - > BaseNode :
2014-11-08 09:41:05 +08:00
left = self . e8 ( )
2017-03-12 00:38:28 +08:00
block_start = self . current
2014-05-27 03:56:12 +08:00
if self . accept ( ' lparen ' ) :
2014-03-14 03:55:06 +08:00
args = self . args ( )
2017-03-12 00:38:28 +08:00
self . block_expect ( ' rparen ' , block_start )
2014-03-14 04:30:10 +08:00
if not isinstance ( left , IdNode ) :
raise ParseException ( ' Function call must be applied to plain id ' ,
2017-04-20 23:49:02 +08:00
self . getline ( ) , left . lineno , left . colno )
2019-12-07 21:42:23 +08:00
assert isinstance ( left . value , str )
2020-02-21 02:50:24 +08:00
left = FunctionNode ( left . filename , left . lineno , left . colno , self . current . lineno , self . current . colno , left . value , args )
2015-08-20 04:34:49 +08:00
go_again = True
while go_again :
go_again = False
if self . accept ( ' dot ' ) :
go_again = True
left = self . method_call ( left )
if self . accept ( ' lbracket ' ) :
go_again = True
left = self . index_call ( left )
2014-03-14 03:55:06 +08:00
return left
2014-03-13 04:46:38 +08:00
2019-12-07 21:42:23 +08:00
def e8 ( self ) - > BaseNode :
2017-03-12 00:38:28 +08:00
block_start = self . current
2014-03-14 06:02:44 +08:00
if self . accept ( ' lparen ' ) :
2014-03-14 06:22:35 +08:00
e = self . statement ( )
2017-03-12 00:38:28 +08:00
self . block_expect ( ' rparen ' , block_start )
2014-03-14 03:16:41 +08:00
return e
2014-03-14 06:02:44 +08:00
elif self . accept ( ' lbracket ' ) :
args = self . args ( )
2017-03-12 00:38:28 +08:00
self . block_expect ( ' rbracket ' , block_start )
2019-03-03 17:48:47 +08:00
return ArrayNode ( args , block_start . lineno , block_start . colno , self . current . lineno , self . current . colno )
2018-04-28 07:56:56 +08:00
elif self . accept ( ' lcurl ' ) :
key_values = self . key_values ( )
self . block_expect ( ' rcurl ' , block_start )
2019-03-03 17:48:47 +08:00
return DictNode ( key_values , block_start . lineno , block_start . colno , self . current . lineno , self . current . colno )
2014-03-13 04:46:38 +08:00
else :
2014-11-08 09:41:05 +08:00
return self . e9 ( )
2014-03-13 04:46:38 +08:00
2019-12-07 21:42:23 +08:00
def e9 ( self ) - > BaseNode :
2014-03-14 02:50:00 +08:00
t = self . current
2014-03-13 04:46:38 +08:00
if self . accept ( ' true ' ) :
2019-12-10 06:17:25 +08:00
t . value = True
return BooleanNode ( t )
2014-03-13 04:46:38 +08:00
if self . accept ( ' false ' ) :
2019-12-10 06:17:25 +08:00
t . value = False
return BooleanNode ( t )
2014-03-13 04:46:38 +08:00
if self . accept ( ' id ' ) :
2014-03-14 02:50:00 +08:00
return IdNode ( t )
2014-03-13 04:46:38 +08:00
if self . accept ( ' number ' ) :
2014-03-14 02:50:00 +08:00
return NumberNode ( t )
2014-03-13 04:46:38 +08:00
if self . accept ( ' string ' ) :
2014-03-14 02:50:00 +08:00
return StringNode ( t )
2019-12-07 21:42:23 +08:00
return EmptyNode ( self . current . lineno , self . current . colno , self . current . filename )
2014-03-13 04:05:19 +08:00
2019-12-07 21:42:23 +08:00
def key_values ( self ) - > ArgumentNode :
s = self . statement ( ) # type: BaseNode
a = ArgumentNode ( self . current )
2018-04-28 07:56:56 +08:00
while not isinstance ( s , EmptyNode ) :
2018-05-21 04:02:08 +08:00
if self . accept ( ' colon ' ) :
2019-12-07 21:42:23 +08:00
a . set_kwarg_no_check ( s , self . statement ( ) )
2018-04-28 07:56:56 +08:00
potential = self . current
if not self . accept ( ' comma ' ) :
return a
a . commas . append ( potential )
else :
2018-05-21 04:02:08 +08:00
raise ParseException ( ' Only key:value pairs are valid in dict construction. ' ,
2018-05-21 06:19:31 +08:00
self . getline ( ) , s . lineno , s . colno )
2018-04-28 07:56:56 +08:00
s = self . statement ( )
return a
2019-12-07 21:42:23 +08:00
def args ( self ) - > ArgumentNode :
s = self . statement ( ) # type: BaseNode
a = ArgumentNode ( self . current )
2015-03-16 04:31:10 +08:00
while not isinstance ( s , EmptyNode ) :
2016-11-20 08:59:08 +08:00
potential = self . current
2014-03-13 04:05:19 +08:00
if self . accept ( ' comma ' ) :
2016-11-20 08:59:08 +08:00
a . commas . append ( potential )
2015-03-16 04:31:10 +08:00
a . append ( s )
elif self . accept ( ' colon ' ) :
if not isinstance ( s , IdNode ) :
2018-05-21 04:02:08 +08:00
raise ParseException ( ' Dictionary key must be a plain identifier. ' ,
2017-04-20 23:49:02 +08:00
self . getline ( ) , s . lineno , s . colno )
2019-12-07 21:42:23 +08:00
a . set_kwarg ( s , self . statement ( ) )
2016-11-20 08:59:08 +08:00
potential = self . current
2015-03-16 04:31:10 +08:00
if not self . accept ( ' comma ' ) :
return a
2016-11-20 08:59:08 +08:00
a . commas . append ( potential )
2014-03-17 05:07:00 +08:00
else :
2015-03-16 04:31:10 +08:00
a . append ( s )
return a
s = self . statement ( )
2014-03-14 03:16:41 +08:00
return a
2014-03-13 04:05:19 +08:00
2019-12-07 21:42:23 +08:00
def method_call ( self , source_object ) - > MethodNode :
2014-11-08 09:41:05 +08:00
methodname = self . e9 ( )
2014-03-14 03:55:06 +08:00
if not ( isinstance ( methodname , IdNode ) ) :
raise ParseException ( ' Method name must be plain id ' ,
2017-04-20 23:49:02 +08:00
self . getline ( ) , self . current . lineno , self . current . colno )
2019-12-07 21:42:23 +08:00
assert isinstance ( methodname . value , str )
2014-03-13 04:05:19 +08:00
self . expect ( ' lparen ' )
2014-03-14 03:55:06 +08:00
args = self . args ( )
2014-03-13 04:05:19 +08:00
self . expect ( ' rparen ' )
2020-02-21 02:50:24 +08:00
method = MethodNode ( methodname . filename , methodname . lineno , methodname . colno , source_object , methodname . value , args )
2014-03-14 06:02:44 +08:00
if self . accept ( ' dot ' ) :
return self . method_call ( method )
return method
2014-03-13 04:05:19 +08:00
2019-12-07 21:42:23 +08:00
def index_call ( self , source_object ) - > IndexNode :
2015-08-20 04:34:49 +08:00
index_statement = self . statement ( )
self . expect ( ' rbracket ' )
return IndexNode ( source_object , index_statement )
2019-12-07 21:42:23 +08:00
def foreachblock ( self ) - > ForeachClauseNode :
2014-11-06 02:38:35 +08:00
t = self . current
self . expect ( ' id ' )
2019-12-07 21:42:23 +08:00
assert isinstance ( t . value , str )
2014-11-06 02:38:35 +08:00
varname = t
2020-01-09 00:44:50 +08:00
varnames = [ t . value ] # type: T.List[str]
2018-04-28 07:56:56 +08:00
2018-05-21 04:02:08 +08:00
if self . accept ( ' comma ' ) :
2018-04-28 07:56:56 +08:00
t = self . current
self . expect ( ' id ' )
2019-12-07 21:42:23 +08:00
assert isinstance ( t . value , str )
varnames . append ( t . value )
2018-04-28 07:56:56 +08:00
2014-11-06 02:38:35 +08:00
self . expect ( ' colon ' )
items = self . statement ( )
block = self . codeblock ( )
2019-12-07 21:42:23 +08:00
return ForeachClauseNode ( varname , varnames , items , block )
2014-11-06 02:38:35 +08:00
2019-12-07 21:42:23 +08:00
def ifblock ( self ) - > IfClauseNode :
2014-03-14 04:30:10 +08:00
condition = self . statement ( )
2019-12-07 21:42:23 +08:00
clause = IfClauseNode ( condition )
2017-06-26 02:43:38 +08:00
self . expect ( ' eol ' )
2014-03-14 04:30:10 +08:00
block = self . codeblock ( )
2019-12-07 21:42:23 +08:00
clause . ifs . append ( IfNode ( clause , condition , block ) )
2014-03-14 04:30:10 +08:00
self . elseifblock ( clause )
2019-12-13 00:03:53 +08:00
clause . elseblock = self . elseblock ( )
2014-03-14 04:30:10 +08:00
return clause
2019-12-07 21:42:23 +08:00
def elseifblock ( self , clause ) - > None :
2014-03-13 04:53:18 +08:00
while self . accept ( ' elif ' ) :
2014-03-14 04:30:10 +08:00
s = self . statement ( )
2014-03-13 04:05:19 +08:00
self . expect ( ' eol ' )
2014-03-14 04:30:10 +08:00
b = self . codeblock ( )
2019-12-07 21:42:23 +08:00
clause . ifs . append ( IfNode ( s , s , b ) )
2014-03-13 04:05:19 +08:00
2019-12-13 00:03:53 +08:00
def elseblock ( self ) - > T . Union [ CodeBlockNode , EmptyNode ] :
2014-03-13 04:46:38 +08:00
if self . accept ( ' else ' ) :
2014-03-13 04:05:19 +08:00
self . expect ( ' eol ' )
2014-03-14 04:30:10 +08:00
return self . codeblock ( )
2019-12-13 00:03:53 +08:00
return EmptyNode ( self . current . lineno , self . current . colno , self . current . filename )
2014-03-13 02:42:40 +08:00
2019-12-07 21:42:23 +08:00
def line ( self ) - > BaseNode :
2017-03-12 00:38:28 +08:00
block_start = self . current
2014-03-14 04:30:10 +08:00
if self . current == ' eol ' :
2019-12-07 21:42:23 +08:00
return EmptyNode ( self . current . lineno , self . current . colno , self . current . filename )
2014-03-13 02:42:40 +08:00
if self . accept ( ' if ' ) :
2019-12-07 21:42:23 +08:00
ifblock = self . ifblock ( )
2017-03-12 00:38:28 +08:00
self . block_expect ( ' endif ' , block_start )
2019-12-07 21:42:23 +08:00
return ifblock
2014-11-06 02:38:35 +08:00
if self . accept ( ' foreach ' ) :
2019-12-07 21:42:23 +08:00
forblock = self . foreachblock ( )
2017-03-12 00:38:28 +08:00
self . block_expect ( ' endforeach ' , block_start )
2019-12-07 21:42:23 +08:00
return forblock
2018-07-18 04:47:41 +08:00
if self . accept ( ' continue ' ) :
return ContinueNode ( self . current )
if self . accept ( ' break ' ) :
return BreakNode ( self . current )
2014-03-14 03:55:06 +08:00
return self . statement ( )
2014-03-13 02:42:40 +08:00
2019-12-07 21:42:23 +08:00
def codeblock ( self ) - > CodeBlockNode :
2016-12-05 00:28:25 +08:00
block = CodeBlockNode ( self . current )
2014-03-13 02:42:40 +08:00
cond = True
while cond :
2014-03-14 03:55:06 +08:00
curline = self . line ( )
if not isinstance ( curline , EmptyNode ) :
block . lines . append ( curline )
2014-03-13 04:53:18 +08:00
cond = self . accept ( ' eol ' )
2014-03-14 03:55:06 +08:00
return block