aboutsummaryrefslogtreecommitdiff
path: root/mparser.py
diff options
context:
space:
mode:
authorJussi Pakkanen <jpakkane@gmail.com>2016-01-15 21:12:23 +0200
committerJussi Pakkanen <jpakkane@gmail.com>2016-01-15 21:22:09 +0200
commit8b1039fa30a405e2d07ac70eb0284ee4654c619a (patch)
tree6cc326bf97c235c9e67b4a34534316fdaaa2eb19 /mparser.py
parent926b55076f1fa94f887dd62443c66a886af0b78b (diff)
downloadmeson-8b1039fa30a405e2d07ac70eb0284ee4654c619a.zip
meson-8b1039fa30a405e2d07ac70eb0284ee4654c619a.tar.gz
meson-8b1039fa30a405e2d07ac70eb0284ee4654c619a.tar.bz2
Organise files into a module structure.
Diffstat (limited to 'mparser.py')
-rw-r--r--mparser.py565
1 files changed, 0 insertions, 565 deletions
diff --git a/mparser.py b/mparser.py
deleted file mode 100644
index 455182a..0000000
--- a/mparser.py
+++ /dev/null
@@ -1,565 +0,0 @@
-# Copyright 2014-2015 The Meson development team
-
-# 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.
-
-import re
-from coredata import MesonException
-
-class ParseException(MesonException):
- def __init__(self, text, lineno, colno):
- super().__init__(text)
- self.lineno = lineno
- self.colno = colno
-
-class Token:
- def __init__(self, tid, lineno, colno, value):
- self.tid = tid
- self.lineno = lineno
- self.colno = colno
- self.value = value
-
- def __eq__(self, other):
- if isinstance(other, str):
- return self.tid == other
- return self.tid == other.tid
-
-class Lexer:
- def __init__(self):
- self.keywords = {'true', 'false', 'if', 'else', 'elif',
- 'endif', 'and', 'or', 'not', 'foreach', 'endforeach'}
- 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]*')),
- ('number', re.compile(r'\d+')),
- ('eol_cont', re.compile(r'\\\n')),
- ('eol', re.compile(r'\n')),
- ('multiline_string', re.compile(r"'''(.|\n)*?'''", re.M)),
- ('comment', re.compile(r'\#.*')),
- ('lparen', re.compile(r'\(')),
- ('rparen', re.compile(r'\)')),
- ('lbracket', re.compile(r'\[')),
- ('rbracket', re.compile(r'\]')),
- ('dblquote', re.compile(r'"')),
- ('string', re.compile(r"'([^'\\]|(\\.))*'")),
- ('comma', re.compile(r',')),
- ('plusassign', re.compile(r'\+=')),
- ('dot', re.compile(r'\.')),
- ('plus', re.compile(r'\+')),
- ('dash', re.compile(r'-')),
- ('star', re.compile(r'\*')),
- ('fslash', re.compile(r'/')),
- ('colon', re.compile(r':')),
- ('equal', re.compile(r'==')),
- ('nequal', re.compile(r'\!=')),
- ('assign', re.compile(r'=')),
- ]
-
- def lex(self, code):
- lineno = 1
- line_start = 0
- loc = 0;
- par_count = 0
- bracket_count = 0
- col = 0
- while(loc < len(code)):
- matched = False
- value = None
- for (tid, reg) in self.token_specification:
- mo = reg.match(code, loc)
- if mo:
- curline = lineno
- col = mo.start()-line_start
- matched = True
- loc = mo.end()
- match_text = mo.group()
- if tid == 'ignore' or tid == 'comment':
- 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
- elif tid == 'dblquote':
- raise ParseException('Double quotes are not supported. Use single quotes.', lineno, col)
- elif tid == 'string':
- value = match_text[1:-1].replace(r"\'", "'").replace(r" \\ ".strip(), r" \ ".strip())\
- .replace("\\n", "\n")
- elif tid == 'multiline_string':
- tid = 'string'
- value = match_text[3:-3]
- lines = match_text.split('\n')
- if len(lines) > 1:
- lineno += len(lines) - 1
- line_start = mo.end() - len(lines[-1])
- elif tid == 'number':
- value = int(match_text)
- elif tid == 'eol' or tid == 'eol_cont':
- lineno += 1
- line_start = loc
- if par_count > 0 or bracket_count > 0:
- break
- elif tid == 'id':
- if match_text in self.keywords:
- tid = match_text
- else:
- value = match_text
- yield Token(tid, curline, col, value)
- break
- if not matched:
- raise ParseException('lexer', lineno, col)
-
-class BooleanNode:
- def __init__(self, token, value):
- self.lineno = token.lineno
- self.colno = token.colno
- self.value = value
- assert(isinstance(self.value, bool))
-
-class IdNode:
- def __init__(self, token):
- self.lineno = token.lineno
- self.colno = token.colno
- self.value = token.value
- assert(isinstance(self.value, str))
-
- def __str__(self):
- return "Id node: '%s' (%d, %d)." % (self.value, self.lineno, self.colno)
-
-class NumberNode:
- def __init__(self, token):
- self.lineno = token.lineno
- self.colno = token.colno
- self.value = token.value
- assert(isinstance(self.value, int))
-
-class StringNode:
- def __init__(self, token):
- self.lineno = token.lineno
- self.colno = token.colno
- self.value = token.value
- assert(isinstance(self.value, str))
-
- def __str__(self):
- return "String node: '%s' (%d, %d)." % (self.value, self.lineno, self.colno)
-
-class ArrayNode:
- def __init__(self, args):
- self.lineno = args.lineno
- self.colno = args.colno
- self.args = args
-
-class EmptyNode:
- def __init__(self):
- self.lineno = 0
- self.colno = 0
- self.value = None
-
-class OrNode:
- def __init__(self, lineno, colno, left, right):
- self.lineno = lineno
- self.colno = colno
- self.left = left
- self.right = right
-
-class AndNode:
- def __init__(self, lineno, colno, left, right):
- self.lineno = lineno
- self.colno = colno
- self.left = left
- self.right = right
-
-class ComparisonNode:
- def __init__(self, lineno, colno, ctype, left, right):
- self.lineno = lineno
- self.colno = colno
- self.left = left
- self.right = right
- self.ctype = ctype
-
-class ArithmeticNode:
- def __init__(self, lineno, colno, operation, left, right):
- self.lineno = lineno
- self.colno = colno
- self.left = left
- self.right = right
- self.operation = operation
-
-class NotNode:
- def __init__(self, lineno, colno, value):
- self.lineno = lineno
- self.colno = colno
- self.value = value
-
-class CodeBlockNode:
- def __init__(self, lineno, colno):
- self.lineno = lineno
- self.colno = colno
- self.lines = []
-
-class IndexNode:
- def __init__(self, iobject, index):
- self.iobject = iobject
- self.index = index
- self.lineno = iobject.lineno
- self.colno = iobject.colno
-
-class MethodNode:
- def __init__(self, lineno, colno, source_object, name, args):
- self.lineno = lineno
- self.colno = colno
- self.source_object = source_object
- self.name = name
- assert(isinstance(self.name, str))
- self.args = args
-
-class FunctionNode:
- def __init__(self, lineno, colno, func_name, args):
- self.lineno = lineno
- self.colno = colno
- self.func_name = func_name
- assert(isinstance(func_name, str))
- self.args = args
-
-class AssignmentNode:
- def __init__(self, lineno, colno, var_name, value):
- self.lineno = lineno
- self.colno = colno
- self.var_name = var_name
- assert(isinstance(var_name, str))
- self.value = value
-
-class PlusAssignmentNode:
- def __init__(self, lineno, colno, var_name, value):
- self.lineno = lineno
- self.colno = colno
- self.var_name = var_name
- assert(isinstance(var_name, str))
- self.value = value
-
-class ForeachClauseNode():
- def __init__(self, lineno, colno, varname, items, block):
- self.lineno = lineno
- self.colno = colno
- self.varname = varname
- self.items = items
- self.block = block
-
-class IfClauseNode():
- def __init__(self, lineno, colno):
- self.lineno = lineno
- self.colno = colno
- self.ifs = []
- self.elseblock = EmptyNode()
-
-class UMinusNode():
- def __init__(self, lineno, colno, value):
- self.lineno = lineno
- self.colno = colno
- self.value = value
-
-class IfNode():
- def __init__(self, lineno, colno, condition, block):
- self.lineno = lineno
- self.colno = colno
- self.condition = condition
- self.block = block
-
-class ArgumentNode():
- def __init__(self, token):
- self.lineno = token.lineno
- self.colno = token.colno
- self.arguments = []
- self.kwargs = {}
- self.order_error = False
-
- def prepend(self, statement):
- if self.num_kwargs() > 0:
- self.order_error = True
- if not isinstance(statement, EmptyNode):
- self.arguments = [statement] + self.arguments
-
- def append(self, statement):
- if self.num_kwargs() > 0:
- self.order_error = True
- if not isinstance(statement, EmptyNode):
- self.arguments = self.arguments + [statement]
-
- def set_kwarg(self, name, value):
- self.kwargs[name] = value
-
- def num_args(self):
- return len(self.arguments)
-
- def num_kwargs(self):
- return len(self.kwargs)
-
- def incorrect_order(self):
- return self.order_error
-
- def __len__(self):
- return self.num_args() # Fixme
-
-# 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
-# 4 comparison
-# 5 arithmetic
-# 6 negation
-# 7 funcall, method call
-# 8 parentheses
-# 9 plain token
-
-class Parser:
- def __init__(self, code):
- self.stream = Lexer().lex(code)
- self.getsym()
-
- def getsym(self):
- try:
- self.current = next(self.stream)
- except StopIteration:
- self.current = Token('eof', 0, 0, None)
-
- def accept(self, s):
- if self.current.tid == s:
- self.getsym()
- return True
- return False
-
- 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)
-
- def parse(self):
- block = self.codeblock()
- self.expect('eof')
- return block
-
- def statement(self):
- return self.e1()
-
- def e1(self):
- left = self.e2()
- if self.accept('plusassign'):
- value = self.e1()
- if not isinstance(left, IdNode):
- raise ParseException('Plusassignment target must be an id.', left.lineno, left.colno)
- return PlusAssignmentNode(left.lineno, left.colno, left.value, value)
- elif self.accept('assign'):
- value = self.e1()
- if not isinstance(left, IdNode):
- raise ParseException('Assignment target must be an id.',
- left.lineno, left.colno)
- return AssignmentNode(left.lineno, left.colno, left.value, value)
- return left
-
- def e2(self):
- left = self.e3()
- while self.accept('or'):
- left = OrNode(left.lineno, left.colno, left, self.e3())
- return left
-
- def e3(self):
- left = self.e4()
- while self.accept('and'):
- left = AndNode(left.lineno, left.colno, left, self.e4())
- return left
-
- def e4(self):
- left = self.e5()
- if self.accept('equal'):
- return ComparisonNode(left.lineno, left.colno, '==', left, self.e5())
- if self.accept('nequal'):
- return ComparisonNode(left.lineno, left.colno, '!=', left, self.e5())
- return left
-
- def e5(self):
- return self.e5add()
-
- def e5add(self):
- left = self.e5sub()
- if self.accept('plus'):
- return ArithmeticNode(left.lineno, left.colno, 'add', left, self.e5add())
- return left
-
- def e5sub(self):
- left = self.e5mul()
- if self.accept('dash'):
- return ArithmeticNode(left.lineno, left.colno, 'sub', left, self.e5sub())
- return left
-
- def e5mul(self):
- left = self.e5div()
- if self.accept('star'):
- return ArithmeticNode(left.lineno, left.colno, 'mul', left, self.e5mul())
- return left
-
- def e5div(self):
- left = self.e6()
- if self.accept('fslash'):
- return ArithmeticNode(left.lineno, left.colno, 'div', left, self.e5div())
- return left
-
- def e6(self):
- if self.accept('not'):
- return NotNode(self.current.lineno, self.current.colno, self.e7())
- if self.accept('dash'):
- return UMinusNode(self.current.lineno, self.current.colno, self.e7())
- return self.e7()
-
- def e7(self):
- left = self.e8()
- if self.accept('lparen'):
- args = self.args()
- self.expect('rparen')
- if not isinstance(left, IdNode):
- raise ParseException('Function call must be applied to plain id',
- left.lineno, left.colno)
- left = FunctionNode(left.lineno, left.colno, left.value, args)
- 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)
- return left
-
- def e8(self):
- if self.accept('lparen'):
- e = self.statement()
- self.expect('rparen')
- return e
- elif self.accept('lbracket'):
- args = self.args()
- self.expect('rbracket')
- return ArrayNode(args)
- else:
- return self.e9()
-
- def e9(self):
- t = self.current
- if self.accept('true'):
- return BooleanNode(t, True);
- if self.accept('false'):
- return BooleanNode(t, False)
- if self.accept('id'):
- return IdNode(t)
- if self.accept('number'):
- return NumberNode(t)
- if self.accept('string'):
- return StringNode(t)
- return EmptyNode()
-
- def args(self):
- s = self.statement()
- a = ArgumentNode(s)
-
- while not isinstance(s, EmptyNode):
- if self.accept('comma'):
- a.append(s)
- elif self.accept('colon'):
- if not isinstance(s, IdNode):
- raise ParseException('Keyword argument must be a plain identifier.',
- s.lineno, s.colno)
- a.set_kwarg(s.value, self.statement())
- if not self.accept('comma'):
- return a
- else:
- a.append(s)
- return a
- s = self.statement()
- return a
-
- def method_call(self, source_object):
- methodname = self.e9()
- if not(isinstance(methodname, IdNode)):
- raise ParseException('Method name must be plain id',
- self.current.lineno, self.current.colno)
- self.expect('lparen')
- args = self.args()
- self.expect('rparen')
- method = MethodNode(methodname.lineno, methodname.colno, source_object, methodname.value, args)
- if self.accept('dot'):
- return self.method_call(method)
- return method
-
- def index_call(self, source_object):
- index_statement = self.statement()
- self.expect('rbracket')
- return IndexNode(source_object, index_statement)
-
- def foreachblock(self):
- t = self.current
- self.expect('id')
- varname = t
- self.expect('colon')
- items = self.statement()
- block = self.codeblock()
- return ForeachClauseNode(varname.lineno, varname.colno, varname, items, block)
-
- def ifblock(self):
- condition = self.statement()
- clause = IfClauseNode(condition.lineno, condition.colno)
- block = self.codeblock()
- clause.ifs.append(IfNode(clause.lineno, clause.colno, condition, block))
- self.elseifblock(clause)
- clause.elseblock = self.elseblock()
- return clause
-
- def elseifblock(self, clause):
- while self.accept('elif'):
- s = self.statement()
- self.expect('eol')
- b = self.codeblock()
- clause.ifs.append(IfNode(s.lineno, s.colno, s, b))
-
- def elseblock(self):
- if self.accept('else'):
- self.expect('eol')
- return self.codeblock()
-
- def line(self):
- if self.current == 'eol':
- return EmptyNode()
- if self.accept('if'):
- block = self.ifblock()
- self.expect('endif')
- return block
- if self.accept('foreach'):
- block = self.foreachblock()
- self.expect('endforeach')
- return block
- return self.statement()
-
- def codeblock(self):
- block = CodeBlockNode(self.current.lineno, self.current.colno)
- cond = True
- while cond:
- curline = self.line()
- if not isinstance(curline, EmptyNode):
- block.lines.append(curline)
- cond = self.accept('eol')
- return block