EmptyNode needs a line/column number too. There are times its line

and column are printed out by other parser code.

Add a print of the line with the error, and where on the line the error
occurred.

Add a print of where the block scope started, if the error is due to
missing the block scope end token.
pull/1523/head
Aaron Small 8 years ago committed by Jussi Pakkanen
parent d668bea11d
commit 9adef3a8e8
  1. 1
      authors.txt
  2. 90
      mesonbuild/mparser.py

@ -68,3 +68,4 @@ Rodrigo Lourenço
Sebastian Stang
Marc Becker
Michal Sojka
Aaron Small

@ -16,15 +16,41 @@ import re
from .mesonlib import MesonException
class ParseException(MesonException):
def __init__(self, text, lineno, colno):
super().__init__(text)
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\nFor a block that started at %d,%d\n%s\n%s" % (text, line, '%s^' % (' ' * colno), start_lineno, start_colno, start_line, "%s^" % (' ' * start_colno)))
self.lineno = lineno
self.colno = colno
class Token:
def __init__(self, tid, subdir, lineno, colno, bytespan, value):
def __init__(self, tid, subdir, line_start, lineno, colno, bytespan, value):
self.tid = tid
self.subdir = subdir
self.line_start = line_start
self.lineno = lineno
self.colno = colno
self.bytespan = bytespan
@ -36,7 +62,8 @@ class Token:
return self.tid == other.tid
class Lexer:
def __init__(self):
def __init__(self, code):
self.code = code
self.keywords = {'true', 'false', 'if', 'else', 'elif',
'endif', 'and', 'or', 'not', 'foreach', 'endforeach'}
self.token_specification = [
@ -73,20 +100,24 @@ class Lexer:
('questionmark', re.compile(r'\?')),
]
def lex(self, code, subdir):
lineno = 1
def getline(self, line_start):
return self.code[line_start:self.code.find('\n', line_start)]
def lex(self, subdir):
line_start = 0
lineno = 1
loc = 0
par_count = 0
bracket_count = 0
col = 0
while loc < len(code):
while loc < len(self.code):
matched = False
value = None
for (tid, reg) in self.token_specification:
mo = reg.match(code, loc)
mo = reg.match(self.code, loc)
if mo:
curline = lineno
curline_start = line_start
col = mo.start() - line_start
matched = True
span_start = loc
@ -105,7 +136,7 @@ class Lexer:
elif tid == 'rbracket':
bracket_count -= 1
elif tid == 'dblquote':
raise ParseException('Double quotes are not supported. Use single quotes.', lineno, col)
raise ParseException('Double quotes are not supported. Use single quotes.', self.getline(line_start), lineno, col)
elif tid == 'string':
value = match_text[1:-1]\
.replace(r"\'", "'")\
@ -130,10 +161,10 @@ class Lexer:
tid = match_text
else:
value = match_text
yield Token(tid, subdir, curline, col, bytespan, value)
yield Token(tid, subdir, curline_start, curline, col, bytespan, value)
break
if not matched:
raise ParseException('lexer', lineno, col)
raise ParseException('lexer', self.getline(line_start), lineno, col)
class ElementaryNode:
def __init__(self, token):
@ -178,10 +209,10 @@ class ArrayNode:
self.args = args
class EmptyNode:
def __init__(self):
def __init__(self, lineno, colno):
self.subdir = ''
self.lineno = 0
self.colno = 0
self.lineno = lineno
self.colno = colno
self.value = None
class OrNode:
@ -288,7 +319,7 @@ class IfClauseNode:
self.lineno = lineno
self.colno = colno
self.ifs = []
self.elseblock = EmptyNode()
self.elseblock = EmptyNode(lineno, colno)
class UMinusNode:
def __init__(self, current_location, value):
@ -374,7 +405,8 @@ comparison_map = {'equal': '==',
class Parser:
def __init__(self, code, subdir):
self.stream = Lexer().lex(code, subdir)
self.lexer = Lexer(code)
self.stream = self.lexer.lex(subdir)
self.getsym()
self.in_ternary = False
@ -382,7 +414,7 @@ class Parser:
try:
self.current = next(self.stream)
except StopIteration:
self.current = Token('eof', '', 0, 0, (0, 0), None)
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)
def accept(self, s):
if self.current.tid == s:
@ -393,7 +425,12 @@ class Parser:
def expect(self, s):
if self.accept(s):
return True
raise ParseException('Expecting %s got %s.' % (s, self.current.tid), self.current.lineno, self.current.colno)
raise ParseException('Expecting %s got %s.' % (s, self.current.tid), self.lexer.getline(self.current.line_start), self.current.lineno, self.current.colno)
def block_expect(self, s, block_start):
if self.accept(s):
return True
raise BlockParseException('Expecting %s got %s.' % (s, self.current.tid), self.lexer.getline(self.current.line_start), self.current.lineno, self.current.colno, self.lexer.getline(block_start.line_start), block_start.lineno, block_start.colno)
def parse(self):
block = self.codeblock()
@ -489,9 +526,10 @@ class Parser:
def e7(self):
left = self.e8()
block_start = self.current
if self.accept('lparen'):
args = self.args()
self.expect('rparen')
self.block_expect('rparen', block_start)
if not isinstance(left, IdNode):
raise ParseException('Function call must be applied to plain id',
left.lineno, left.colno)
@ -508,13 +546,14 @@ class Parser:
return left
def e8(self):
block_start = self.current
if self.accept('lparen'):
e = self.statement()
self.expect('rparen')
self.block_expect('rparen', block_start)
return e
elif self.accept('lbracket'):
args = self.args()
self.expect('rbracket')
self.block_expect('rbracket', block_start)
return ArrayNode(args)
else:
return self.e9()
@ -531,7 +570,7 @@ class Parser:
return NumberNode(t)
if self.accept('string'):
return StringNode(t)
return EmptyNode()
return EmptyNode(self.current.lineno, self.current.colno)
def args(self):
s = self.statement()
@ -606,15 +645,16 @@ class Parser:
return self.codeblock()
def line(self):
block_start = self.current
if self.current == 'eol':
return EmptyNode()
return EmptyNode(self.current.lineno, self.current.colno)
if self.accept('if'):
block = self.ifblock()
self.expect('endif')
self.block_expect('endif', block_start)
return block
if self.accept('foreach'):
block = self.foreachblock()
self.expect('endforeach')
self.block_expect('endforeach', block_start)
return block
return self.statement()

Loading…
Cancel
Save