diff options
Diffstat (limited to 'abdl/_parser.py')
-rw-r--r-- | abdl/_parser.py | 91 |
1 files changed, 67 insertions, 24 deletions
diff --git a/abdl/_parser.py b/abdl/_parser.py index 3e179a2..a8b17ce 100644 --- a/abdl/_parser.py +++ b/abdl/_parser.py @@ -14,61 +14,104 @@ # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see <https://www.gnu.org/licenses/>. -import pyparsing +"""[Internal] pyparsing-based Parser. -from abdl import exceptions +Provides `BUILT_SYNTAX`. +""" + +from pyparsing import Suppress, Literal, Forward, CharsNotIn, StringEnd +from pyparsing import Combine, Optional, Group, Word, srange, Empty + +from abdl import exceptions as exc from abdl import _vm +def _err_str_esc(match_str, pos, toks): + raise exc.PatternError("Error in string escape", pos=pos, toks=toks) + +def _err_str_end(match_str, pos, toks): + raise exc.PatternError("Unfinished string", pos=pos, toks=toks) + +def _err_re_esc(match_str, pos, toks): + raise exc.PatternError("Error in regex escape", pos=pos, toks=toks) + +def _err_re_end(match_str, pos, toks): + raise exc.PatternError("Unfinished regex", pos=pos, toks=toks) + +def _err_tok(match_str, pos, toks): + raise exc.PatternError("Unexpected token", pos=pos, toks=toks) + def _build_syntax(): - # pylint: disable=protected-access - from pyparsing import Suppress, Literal, Forward, CharsNotIn, StringEnd, Combine, Optional, Group, Word, srange, Empty + # pylint: disable=too-many-locals subtree = Forward() skippable = Optional("?", default="") escape_char = Literal("%") + any_char = CharsNotIn("", exact=1) str_token = Literal("'") re_token = Literal("/") - unexpected_token = CharsNotIn("", exact=1).setParseAction(exceptions.PatternError._unexpected_tok) - unexpected_end = StringEnd().setParseAction(exceptions.PatternError._unexpected_tok) + unexpected_token = any_char.copy().setParseAction(_err_tok) + unexpected_end = StringEnd().setParseAction(_err_tok) + + # TODO reformat these + unexpected_str_escape = any_char.copy().setParseAction(_err_str_esc) + str_escape = Suppress(escape_char) + (str_token | escape_char) + str_escape |= escape_char + unexpected_str_escape + str_char = (str_escape | CharsNotIn("%'")) + + str_literal = (Combine(Suppress(str_token) + str_char[...] + + (Suppress(str_token) + | StringEnd().setParseAction(_err_str_end))) + + skippable) + str_literal.setParseAction(_vm.StringKey.action) - str_literal = (Combine(Suppress(str_token) - + (Suppress(escape_char) + (str_token | escape_char) | escape_char + CharsNotIn("", exact=1).setParseAction(exceptions.PatternError._str_escape) | CharsNotIn("%'"))[...] - + (Suppress(str_token) | StringEnd().setParseAction(exceptions.PatternError._str_end))) + skippable) - str_literal.setParseAction(lambda toks: [_vm.StringKey(toks)]) + unexpected_re_escape = any_char.copy().setParseAction(_err_re_esc) + re_escape = Suppress(escape_char) + (re_token | escape_char) + re_escape |= escape_char + unexpected_re_escape + re_char = (re_escape | CharsNotIn("%/")) - re_literal = (Combine(Suppress(re_token) - + (Suppress(escape_char) + (re_token | escape_char) | escape_char + CharsNotIn("", exact=1).setParseAction(exceptions.PatternError._re_escape) | CharsNotIn("%/"))[...] - + (Suppress(re_token) | StringEnd().setParseAction(exceptions.PatternError._re_end))) + skippable) - re_literal.setParseAction(lambda toks: [_vm.RegexKey(toks)]) + re_literal = (Combine(Suppress(re_token) + re_char[...] + + (Suppress(re_token) + | StringEnd().setParseAction(_err_re_end))) + + skippable) + re_literal.setParseAction(_vm.RegexKey.action) arrow = Literal("->") - arrow.setParseAction(lambda: [_vm.Arrow()]) + arrow.setParseAction(_vm.Arrow.action) identifier = Word(srange("[A-Za-z_]"), srange("[A-Za-z0-9_]")) - identifier.setParseAction(lambda toks: [_vm.Ident(toks)]) + identifier.setParseAction(_vm.Ident.action) parameter = (Suppress("$") + skippable + identifier) - parameter.setParseAction(lambda toks: [_vm.Param(toks)]) + parameter.setParseAction(_vm.Param.action) type_ = (Suppress(":") + skippable + Suppress("$") + identifier) - type_.setParseAction(lambda toks: [_vm.ApplyPredicate(toks)]) + type_.setParseAction(_vm.ApplyPredicate.action) # support for objects-as-keys - keysubtree = (Suppress("[") + Group(type_[...] + subtree) + (Suppress("]") | unexpected_token | unexpected_end) + skippable) - keysubtree.setParseAction(lambda toks: [_vm.KeySubtree(toks)]) + keysubtree = (Suppress("[") + Group(type_[...] + subtree) + + (Suppress("]") | unexpected_token | unexpected_end) + + skippable) + keysubtree.setParseAction(_vm.KeySubtree.action) # represents key matching - switches from "key" to "value" - tag = (identifier + Optional(parameter | str_literal | re_literal | keysubtree) | parameter | str_literal | re_literal | keysubtree) + type_[...] + Empty().setParseAction(lambda: [_vm.End()]) + tag = ((identifier + + Optional(parameter | str_literal | re_literal | keysubtree) + | parameter | str_literal | re_literal | keysubtree) + type_[...] + + Empty().setParseAction(_vm.End.action)) # multiple value matching - valuesubtree = (Suppress("(") + Group(subtree) + (Suppress(")") | unexpected_token | unexpected_end) + Optional("?", default="")) - valuesubtree.setParseAction(lambda toks: [_vm.ValueSubtree(toks)]) + valuesubtree = (Suppress("(") + Group(subtree) + + (Suppress(")") | unexpected_token | unexpected_end) + + Optional("?", default="")) + valuesubtree.setParseAction(_vm.ValueSubtree.action) # arrow and tag, value subtree - subtree <<= (arrow + tag)[...] + (valuesubtree + Empty().setParseAction(lambda: [_vm.End()]))[...] + subtree <<= ((arrow + tag)[...] + + (valuesubtree + + Empty().setParseAction(_vm.End.action))[...]) return ((subtree | unexpected_token) + StringEnd()).parseWithTabs() |