import element
import sys
+# this can be redefined in SECTION1
+def YY_AT_BOL():
+ return yy_buffer_stack[-1].at_bol
+
+def yy_set_bol(at_bol):
+ yy_buffer_stack[-1].at_bol = at_bol
+
+def YY_USER_ACTION():
+ pass
+
+def YY_RULE_START():
+ # note that this should also be done after yyless() and REJECT(),
+ # and state should be saved in case they result in a null string,
+ # however, it doesn't seem to be in flex, maintain compatibility:
+ if len(yytext):
+ yy_set_bol(yytext[-1] == '\n')
+ YY_USER_ACTION()
+
# GENERATE SECTION1
# GENERATE STARTCONDDECL
yytext = yy_group_text[:yy_group_stack[-1]]
yytext_len = yy_group_stack[-1]
del yy_group_stack[-2:]
- # note that this should also be done after yyless() and REJECT(),
- # and state should be saved in case they result in a null string,
- # however, it doesn't seem to be in flex, maintain compatibility:
- if len(yytext):
- yy_buffer_stack[-1].at_bol = yytext[-1] == '\n'
+ YY_RULE_START()
yy_element_stack.append([])
def yy_group_end():
global yystart
yystart = yystart_stack.pop()
-def YY_AT_BOL():
- return yy_buffer_stack[-1].at_bol
-
-def yy_set_bol(at_bol):
- yy_buffer_stack[-1].at_bol = at_bol
-
# GENERATE SECTION2
def yylex(_class = element.Element, *args, **kwargs):
--- /dev/null
+# Copyright (C) 2019 Nick Downing <nick@ndcode.org>
+# SPDX-License-Identifier: GPL-2.0-with-bison-exception
+#
+# This program is free software; you can redistribute it and/or modify it under
+# the terms of the GNU General Public License as published by the Free Software
+# Foundation; version 2.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+# details.
+#
+# You should have received a copy of the GNU General Public License along with
+# this program; if not, write to the Free Software Foundation, Inc., 51
+# Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+#
+# As a special exception, you may create a larger work that contains part or
+# all of the piyacc parser skeleton and distribute that work under terms of
+# your choice, so long as that work isn't itself a parser generator using the
+# skeleton or a modified version thereof as a parser skeleton. Alternatively,
+# if you modify or redistribute the parser skeleton itself, you may (at your
+# option) remove this special exception, which will cause the skeleton and the
+# resulting piyacc output files to be licensed under the GNU General Public
+# License without this special exception.
+
+import bisect
+import element
+import lex_yy
+#import xml.etree.ElementTree
+
+# this can be redefined in SECTION1
+class YYLTYPE:
+ def __init__(
+ self,
+ first_line = 0,
+ first_column = 0,
+ last_line = 0,
+ last_column = 0
+ ):
+ self.first_line = first_line
+ self.first_column = first_column
+ self.last_line = last_line
+ self.last_column = last_column
+ def copy(self):
+ return YYLTYPE(
+ self.first_line,
+ self.first_column,
+ self.last_line,
+ self.last_column
+ )
+
+def YYLLOC_DEFAULT(current, stack, n):
+ if n:
+ current.first_line = stack[-n][2].first_line
+ current.first_column = stack[-n][2].first_column
+ current.last_line = stack[-1][2].last_line
+ current.last_column = stack[-1][2].last_column
+ else:
+ current.first_line = current.last_line = stack[-1][2].last_line
+ current.first_column = current.last_column = stack[-1][2].last_column
+
+# GENERATE SECTION1
+
+# GENERATE TOKENS
+
+yystack = None
+yychar = None
+YYEMPTY = -1
+
+yyval = None
+yyloc = YYLTYPE()
+
+yylval = None
+yylloc = YYLTYPE()
+
+yy_element_stack = None
+
+# GENERATE SECTION2
+
+def yyparse(factory, *args, **kwargs):
+ global yystack, yychar, yyval, yyloc, yylval, yylloc, yy_element_stack
+
+ # GENERATE INITIALACTION
+
+ state = 0
+ yystack = [(-1, yylval, yylloc.copy())] # kludge for bison compatibility
+ yychar = -1
+ yy_element_stack = []
+ while True:
+ #print('state', state, 'yystack', yystack)
+ reduce = yy_lr1dfa_states[state][4]
+ if reduce == -1:
+ if yychar == -1:
+ yychar = lex_yy.yylex()
+ #print('yychar', yychar, 'yylval', yylval, 'yylloc', yylloc, 'lex_yy.yytext', lex_yy.yytext)
+ #print('lex_yy.yy_element_space')
+ #xml.etree.ElementTree.dump(lex_yy.yy_element_space)
+ #print('lex_yy.yy_element_token')
+ #xml.etree.ElementTree.dump(lex_yy.yy_element_token)
+ action = yy_lr1dfa_states[state][1][
+ bisect.bisect_right(yy_lr1dfa_states[state][0], yychar)
+ ]
+ if action == -1:
+ raise Exception('syntax error')
+ if (action & 1) == 0:
+ yystack.append((state, yylval, yylloc.copy()))
+
+ # push space then AST element contiguously onto yy_element_stack
+ # even numbered elements are spaces, odd numbered elements are AST
+ yy_element_stack.extend(
+ [lex_yy.yy_element_space, lex_yy.yy_element_token]
+ )
+
+ state = action >> 1
+ #print('shift', state)
+ yychar = -1
+ continue
+ reduce = action >> 1
+ #print('reduce', reduce)
+ len_symbols, ref_data = yy_lr1dfa_productions[reduce]
+ # GENERATE YYLLOC_DEFAULT BEGIN
+ YYLLOC_DEFAULT(yyloc, yystack, len_symbols)
+ # GENERATE END
+ base = len(yystack) - len_symbols
+ yystack.append((state, None, None)) # only has effect if len_symbols == 0
+ state, yyval, _ = yystack[base]
+ ref_data()
+ del yystack[base:]
+ if reduce == 0:
+ assert base == 1
+ break
+ yystack.append((state, yyval, yyloc.copy()))
+
+ # action creates empty space in yy_element_stack[base * 2] if needed
+ assert len(yy_element_stack) >= base * 2 - 1
+
+ # concatenate yy_element_stack[base * 2 - 1:] to a single AST element
+ yy_element_stack[base * 2 - 1:] = [
+ element.concatenate(
+ yy_element_stack[base * 2 - 1:],
+ element.Element
+ )
+ ]
+
+ state = yy_lr1dfa_states[state][3][
+ bisect.bisect_right(yy_lr1dfa_states[state][2], reduce)
+ ]
+ assert state != -1
+
+ # return space then AST then space in the user's choice of element type
+ yy_element_stack.append(lex_yy.yy_element_space)
+ return element.concatenate(yy_element_stack, factory, *args, **kwargs)
+
+# GENERATE SECTION3
# miscellaneous state accessed by scan-gram.l and parse-gram.y
class boundary:
- def __init__(self, file = '<stdin>', line = 0, column = 0):
+ def __init__(self, file = '<stdin>', line = 1, column = 1):
self.file = file
self.line = line
self.column = column
def copy(self):
return boundary(self.file, self.line, self.column)
+def equal_boundaries(a, b):
+ return a.column == b.column and a.line == b.line and a.file == b.file
+
class location:
def __init__(self, start = None, end = None):
self.start = boundary() if start is None else start
#
#if (flags & fatal)
# exit (EXIT_FAILURE);
- print(message)
+ sys.stderr.write(
+ '{0:s}({1:d},{2:d})..{3:s}({4:d},{5:d}): {6:s}\n'.format(
+ loc.start.file,
+ loc.start.line,
+ loc.start.column,
+ loc.end.file,
+ loc.end.line,
+ loc.end.column,
+ message
+ )
+ )
sys.exit(1)
undef_assoc = 0
def quote(str):
return '"{0:s}"'.format(str.replace('\\', '\\\\').replace('"', '\\"'))
+
+def location_compute(loc, cur, token):
+ line = cur.line;
+ column = cur.column;
+ p0 = 0
+ p = 0
+ loc.start = cur.copy()
+ while p < len(token):
+ if token[p] == '\n':
+ line += 1
+ column = 1
+ p0 = p + 1
+ elif token[p] == '\t':
+ column += p - p0
+ column += 8 - ((column - 1) & 7)
+ p0 = p + 1
+ p += 1
+ column += p - p0
+ cur.line = line
+ cur.column = column
+ loc.end = cur.copy()