def parse_seq(self): result = [] while True: token = self.next_noend() tag = token.tag if tag == "RawComma": self.tokeniser.feeder.message("Syntax", "com") result.append(Symbol("Null")) self.consume() elif tag in ("RawRightAssociation", "RawRightBrace", "RawRightBracket"): if result: self.tokeniser.feeder.message("Syntax", "com") result.append(Symbol("Null")) break else: result.append(self.parse_exp(0)) token = self.next_noend() tag = token.tag if tag == "RawComma": self.consume() continue elif tag in ("RawRightAssociation", "RawRightBrace", "RawRightBracket"): break return result
def p_Pattern(self, token): self.consume() text = token.text if "." in text: name = text[:-2] if name: return Node( "Optional", Node("Pattern", Symbol(name, context=None), Node("Blank")), ) else: return Node("Optional", Node("Blank")) pieces = text.split("_") count = len(pieces) - 1 if count == 1: name = "Blank" elif count == 2: name = "BlankSequence" elif count == 3: name = "BlankNullSequence" if pieces[-1]: blank = Node(name, Symbol(pieces[-1], context=None)) else: blank = Node(name) if pieces[0]: return Node("Pattern", Symbol(pieces[0], context=None), blank) else: return blank
def p_PatternTest(self, token): self.consume() q = prefix_ops["Definition"] child = self.parse_exp(q) return Node( "Information", child, Node("Rule", Symbol("LongForm"), Symbol("False")) )
def parse_seq(self): result = [] while True: token = self.next_noend() tag = token.tag if tag == 'RawComma': self.tokeniser.feeder.message('Syntax', 'com') result.append(Symbol('Null')) self.consume() elif tag in ('RawRightAssociation', 'RawRightBrace', 'RawRightBracket'): if result: self.tokeniser.feeder.message('Syntax', 'com') result.append(Symbol('Null')) break else: result.append(self.parse_exp(0)) token = self.next_noend() tag = token.tag if tag == 'RawComma': self.consume() continue elif tag in ('RawRightAssociation', 'RawRightBrace', 'RawRightBracket'): break return result
def p_Pattern(self, token): self.consume() text = token.text if '.' in text: name = text[:-2] if name: return Node( 'Optional', Node('Pattern', Symbol(name, context=None), Node('Blank'))) else: return Node('Optional', Node('Blank')) pieces = text.split('_') count = len(pieces) - 1 if count == 1: name = 'Blank' elif count == 2: name = 'BlankSequence' elif count == 3: name = 'BlankNullSequence' if pieces[-1]: blank = Node(name, Symbol(pieces[-1], context=None)) else: blank = Node(name) if pieces[0]: return Node('Pattern', Symbol(pieces[0], context=None), blank) else: return blank
def testFunction(self): self.check('x &', Node('Function', Symbol('x'))) self.check('x \\[Function] y', 'Function[x, y]') self.check('x \uf4a1 y', 'Function[x, y]') self.incomplete_error('x \uf4a1') self.check('x & y', Node('Times', Node('Function', Symbol('x')), Symbol('y')))
def e_Span(self, expr1, token, p): q = ternary_ops['Span'] if q < p: return None if expr1.get_head_name() == 'Span' and not expr1.parenthesised: return None self.consume() # Span[expr1, expr2] token = self.next() if token.tag == 'Span': expr2 = Symbol('All') elif token.tag == 'END' and self.bracket_depth == 0: # So that e.g. 'x = 1 ;;' doesn't wait for newline in the frontend expr2 = Symbol('All') return Node('Span', expr1, expr2) else: messages = list(self.feeder.messages) try: expr2 = self.parse_exp(q + 1) except TranslateError: expr2 = Symbol('All') self.backtrack(token.pos) self.feeder.messages = messages token = self.next() if token.tag == 'Span': self.consume() messages = list(self.feeder.messages) try: expr3 = self.parse_exp(q + 1) return Node('Span', expr1, expr2, expr3) except TranslateError: self.backtrack(token.pos) self.feeder.messages = messages return Node('Span', expr1, expr2)
def testFunction(self): self.check("x &", Node("Function", Symbol("x"))) self.check("x \\[Function] y", "Function[x, y]") self.check("x \uf4a1 y", "Function[x, y]") self.incomplete_error("x \uf4a1") self.check("x & y", Node("Times", Node("Function", Symbol("x")), Symbol("y")))
def testBang(self): self.check('5!', Node('Factorial', Number('5'))) self.check('5 !', Node('Factorial', Number('5'))) self.check('5 ! !', Node('Factorial', Node('Factorial', Number('5')))) self.check('!1', Node('Not', Number('1'))) self.check('5 !!', Node('Factorial2', Number('5'))) self.check('x ! y', Node('Times', Node('Factorial', Symbol('x')), Symbol('y')))
def parse_inequality(self, expr1, token, p): tag = token.tag q = flat_binary_ops[tag] if q < p: return None self.consume() head = expr1.get_head_name() expr2 = self.parse_exp(q + 1) if head == 'Inequality' and not expr1.parenthesised: expr1.children.append(Symbol(tag)) expr1.children.append(expr2) elif head in inequality_ops and head != tag and not expr1.parenthesised: children = [] first = True for child in expr1.children: if not first: children.append(Symbol(head)) children.append(child) first = False children.append(Symbol(tag)) children.append(expr2) expr1 = Node('Inequality', *children) else: expr1 = Node(tag, expr1, expr2).flatten() return expr1
def p_Information(self, token): self.consume() q = prefix_ops["Information"] child = self.parse_exp(q) if child.__class__ is not Symbol: raise InvalidSyntaxError() return Node("Information", child, Node("Rule", Symbol("LongForm"), Symbol("True")))
def testCompound(self): self.check('a ; {b}', Node('CompoundExpression', Symbol('a'), Node('List', Symbol('b')))) self.check('1 ;', Node('CompoundExpression', Number('1'), Symbol('Null'))) self.check('1 ; 5', Node('CompoundExpression', Number('1'), Number('5'))) self.check('4; 1 ; 5', Node('CompoundExpression', Number('4'), Number('1'), Number('5'))) self.check('4;1;', Node('CompoundExpression', Number('4'), Number('1'), Symbol('Null'))) self.check('(a;b);c', Node('CompoundExpression', Node('CompoundExpression', Symbol('a'), Symbol('b')), Symbol('c'))) self.check('f[a;]', 'f[CompoundExpression[a, Null]]')
def testBang(self): self.check("5!", Node("Factorial", Number("5"))) self.check("5 !", Node("Factorial", Number("5"))) self.check("5 ! !", Node("Factorial", Node("Factorial", Number("5")))) self.check("!1", Node("Not", Number("1"))) self.check("5 !!", Node("Factorial2", Number("5"))) self.check("x ! y", Node("Times", Node("Factorial", Symbol("x")), Symbol("y")))
def testTimes(self): self.check('1 2', Node('Times', Number('1'), Number('2'))) self.check('1*2', Node('Times', Number('1'), Number('2'))) self.check('1 2 3', Node('Times', Number('1'), Number('2'), Number('3'))) self.check('(1 2) 3', Node('Times', Node('Times', Number('1'), Number('2')), Number('3'))) self.check('1*2*3', Node('Times', Number('1'), Number('2'), Number('3'))) self.check('x ^ 2 y', Node('Times', Node('Power', Symbol('x'), Number('2')), Symbol('y')))
def testExpression(self): self.check('expr1[expr2]', Node('expr1', Symbol('expr2'))) self.check('expr1[expr2][expr3]', Node(Node('expr1', Symbol('expr2')), Symbol('expr3'))) self.check('expr1[[expr2]]', Node('Part', Symbol('expr1'), Symbol('expr2'))) self.check('expr1[[expr2, expr3]]', Node('Part', Symbol('expr1'), Symbol('expr2'), Symbol('expr3'))) self.check('expr1[[expr2]][[expr3]]', Node('Part', Node('Part', Symbol('expr1'), Symbol('expr2')), Symbol('expr3'))) self.check('expr1 ~ expr2 ~ expr3', Node('expr2', Symbol('expr1'), Symbol('expr3'))) self.check('x~f~y', 'f[x, y]')
def testPlus(self): self.check("+1", Node("Plus", Number("1"))) self.check("1 + 2", Node("Plus", Number("1"), Number("2"))) self.check("1 + 2 + 3", Node("Plus", Number("1"), Number("2"), Number("3"))) self.check("1 + 2 + 3 + 4", "Plus[1, 2, 3, 4]") self.check("-a", Node("Times", Number("-1"), Symbol("a"))) self.check( "a - b", Node("Plus", Symbol("a"), Node("Times", Number("-1"), Symbol("b")))) self.check( "a*b+c", Node("Plus", Node("Times", Symbol("a"), Symbol("b")), Symbol("c"))) self.check( "a*+b+c", Node( "Plus", Node("Times", Symbol("a"), Node("Plus", Symbol("b"))), Symbol("c"), ), ) self.check("a+b*c", "a+(b*c)") self.check("a*b+c", "(a*b) + c") self.check("1-2", "1 - 2")
def testSpan(self): self.check(";;", Node("Span", Number("1"), Symbol("All"))) self.check( "a;;b;;", Node( "Times", Node("Span", Symbol("a"), Symbol("b")), Node("Span", Number("1"), Symbol("All")), ), ) self.check("1;;2;;3", Node("Span", Number("1"), Number("2"), Number("3"))) self.check("1;; ;;3", Node("Span", Number("1"), Symbol("All"), Number("3"))) self.check("1;;;;3", Node("Span", Number("1"), Symbol("All"), Number("3"))) self.check( "1;;2;;", Node( "Times", Node("Span", Number("1"), Number("2")), Node("Span", Number("1"), Symbol("All")), ), ) self.check(" ;;2;;3", Node("Span", Number("1"), Number("2"), Number("3"))) self.check(" ;;2", Node("Span", Number("1"), Number("2"))) self.check("1;; ", Node("Span", Number("1"), Symbol("All"))) self.check(" ;; ", Node("Span", Number("1"), Symbol("All"))) self.check("1;;2;;3;;4;;5;;6", "Times[Span[1, 2, 3], Span[1, 4, 5], Span[1, 6]]") self.check("(a;;b);;c", "Span[Span[a, b], c]")
def b_FormBox(self, box1, token, p): q = misc_ops['FormBox'] if q < p: return None if box1 is None: box1 = Symbol('StandardForm') # RawForm elif is_symbol_name(box1.value): box1 = Symbol(box1.value, context=None) else: box1 = Node('Removed', String('$$Failure')) self.consume() box2 = self.parse_box(q) return Node('FormBox', box2, box1)
def b_FormBox(self, box1, token, p): q = misc_ops["FormBox"] if q < p: return None if box1 is None: box1 = Symbol("StandardForm") # RawForm elif is_symbol_name(box1.value): box1 = Symbol(box1.value, context=None) else: box1 = Node("Removed", String("$$Failure")) self.consume() box2 = self.parse_box(q) return Node("FormBox", box2, box1)
def testCompound(self): self.check( "a ; {b}", Node("CompoundExpression", Symbol("a"), Node("List", Symbol("b"))), ) self.check("1 ;", Node("CompoundExpression", Number("1"), Symbol("Null"))) self.check("1 ; 5", Node("CompoundExpression", Number("1"), Number("5"))) self.check( "4; 1 ; 5", Node("CompoundExpression", Number("4"), Number("1"), Number("5")), ) self.check( "4;1;", Node("CompoundExpression", Number("4"), Number("1"), Symbol("Null"))) self.check( "(a;b);c", Node( "CompoundExpression", Node("CompoundExpression", Symbol("a"), Symbol("b")), Symbol("c"), ), ) self.check("f[a;]", "f[CompoundExpression[a, Null]]")
def testTimes(self): self.check("1 2", Node("Times", Number("1"), Number("2"))) self.check("1*2", Node("Times", Number("1"), Number("2"))) self.check("1 2 3", Node("Times", Number("1"), Number("2"), Number("3"))) self.check( "(1 2) 3", Node("Times", Node("Times", Number("1"), Number("2")), Number("3")), ) self.check("1*2*3", Node("Times", Number("1"), Number("2"), Number("3"))) self.check( "x ^ 2 y", Node("Times", Node("Power", Symbol("x"), Number("2")), Symbol("y")), )
def testBlank(self): self.check('f_', Node('Pattern', Symbol('f'), Node('Blank'))) self.check('f__', Node('Pattern', Symbol('f'), Node('BlankSequence'))) self.check('f___', Node('Pattern', Symbol('f'), Node('BlankNullSequence'))) self.check('_', 'Blank[]') self.check('_expr', 'Blank[expr]') self.check('__', 'BlankSequence[]') self.check('__expr', 'BlankSequence[expr]') self.check('___', 'BlankNullSequence[]') self.check('___expr', 'BlankNullSequence[expr]') self.check('_.', 'Optional[Blank[]]') self.check('symb_', 'Pattern[symb, Blank[]]') self.check('symb_expr', 'Pattern[symb, Blank[expr]]') self.check('symb__', 'Pattern[symb, BlankSequence[]]') self.check('symb__expr', 'Pattern[symb, BlankSequence[expr]]') self.check('symb___', 'Pattern[symb, BlankNullSequence[]]') self.check('symb___expr', 'Pattern[symb, BlankNullSequence[expr]]') self.check('symb_.', 'Optional[Pattern[symb, Blank[]]]')
def testPlus(self): self.check('+1', Node('Plus', Number('1'))) self.check('1 + 2', Node('Plus', Number('1'), Number('2'))) self.check('1 + 2 + 3', Node('Plus', Number('1'), Number('2'), Number('3'))) self.check('1 + 2 + 3 + 4', 'Plus[1, 2, 3, 4]') self.check('-a', Node('Times', Number('-1'), Symbol('a'))) self.check('a - b', Node('Plus', Symbol('a'), Node('Times', Number('-1'), Symbol('b')))) self.check('a*b+c', Node('Plus', Node('Times', Symbol('a'), Symbol('b')), Symbol('c'))) self.check('a*+b+c', Node('Plus', Node('Times', Symbol('a'), Node('Plus', Symbol('b'))), Symbol('c'))) self.check('a+b*c', 'a+(b*c)') self.check('a*b+c', '(a*b) + c') self.check('1-2', '1 - 2')
def testBlank(self): self.check("f_", Node("Pattern", Symbol("f"), Node("Blank"))) self.check("f__", Node("Pattern", Symbol("f"), Node("BlankSequence"))) self.check("f___", Node("Pattern", Symbol("f"), Node("BlankNullSequence"))) self.check("_", "Blank[]") self.check("_expr", "Blank[expr]") self.check("__", "BlankSequence[]") self.check("__expr", "BlankSequence[expr]") self.check("___", "BlankNullSequence[]") self.check("___expr", "BlankNullSequence[expr]") self.check("_.", "Optional[Blank[]]") self.check("symb_", "Pattern[symb, Blank[]]") self.check("symb_expr", "Pattern[symb, Blank[expr]]") self.check("symb__", "Pattern[symb, BlankSequence[]]") self.check("symb__expr", "Pattern[symb, BlankSequence[expr]]") self.check("symb___", "Pattern[symb, BlankNullSequence[]]") self.check("symb___expr", "Pattern[symb, BlankNullSequence[expr]]") self.check("symb_.", "Optional[Pattern[symb, Blank[]]]")
def testOptional(self): self.check('x:expr', Node('Pattern', Symbol('x'), Symbol('expr'))) self.check( 'x_:expr', Node('Optional', Node('Pattern', Symbol('x'), Node('Blank')), Symbol('expr'))) self.check( 'f:a|b', Node('Pattern', Symbol('f'), Node('Alternatives', Symbol('a'), Symbol('b')))) self.check('rev:(True|False):False', 'Optional[Pattern[rev, Alternatives[True, False]], False]')
def testSpan(self): self.check(';;', Node('Span', Number('1'), Symbol('All'))) self.check('a;;b;;', Node('Times', Node('Span', Symbol('a'), Symbol('b')), Node('Span', Number('1'), Symbol('All')))) self.check('1;;2;;3', Node('Span', Number('1'), Number('2'), Number('3'))) self.check('1;; ;;3', Node('Span', Number('1'), Symbol('All'), Number('3'))) self.check('1;;;;3', Node('Span', Number('1'), Symbol('All'), Number('3'))) self.check('1;;2;;', Node('Times', Node('Span', Number('1'), Number('2')), Node('Span', Number('1'), Symbol('All')))) self.check(' ;;2;;3', Node('Span', Number('1'), Number('2'), Number('3'))) self.check(' ;;2', Node('Span', Number('1'), Number('2'))) self.check('1;; ', Node('Span', Number('1'), Symbol('All'))) self.check(' ;; ', Node('Span', Number('1'), Symbol('All'))) self.check('1;;2;;3;;4;;5;;6', 'Times[Span[1, 2, 3], Span[1, 4, 5], Span[1, 6]]') self.check('(a;;b);;c', 'Span[Span[a, b], c]')
def e_Semicolon(self, expr1, token, p): q = flat_binary_ops['CompoundExpression'] if q < p: return None self.consume() # XXX this has to come before call to self.next() pos = self.tokeniser.pos messages = list(self.feeder.messages) # So that e.g. 'x = 1;' doesn't wait for newline in the frontend tag = self.next().tag if tag == 'END' and self.bracket_depth == 0: expr2 = Symbol('Null') return Node('CompoundExpression', expr1, expr2).flatten() # XXX look for next expr otherwise backtrack try: expr2 = self.parse_exp(q + 1) except TranslateError: self.backtrack(pos) self.feeder.messages = messages expr2 = Symbol('Null') return Node('CompoundExpression', expr1, expr2).flatten()
def testOptional(self): self.check("x:expr", Node("Pattern", Symbol("x"), Symbol("expr"))) self.check( "x_:expr", Node("Optional", Node("Pattern", Symbol("x"), Node("Blank")), Symbol("expr")), ) self.check( "f:a|b", Node("Pattern", Symbol("f"), Node("Alternatives", Symbol("a"), Symbol("b"))), ) self.check( "rev:(True|False):False", "Optional[Pattern[rev, Alternatives[True, False]], False]", )
def testSymbol(self): self.check("xX", Symbol("xX")) self.check("context`name", Symbol("context`name")) self.check("`name", Symbol("`name")) self.check("`context`name", Symbol("`context`name"))
def testNonAscii(self): self.check("z \\[Conjugate]", Node("Conjugate", Symbol("z"))) self.check("z \\[Transpose]", Node("Transpose", Symbol("z"))) self.check("z \\[ConjugateTranspose]", Node("ConjugateTranspose", Symbol("z"))) self.check("z \uf3c7 ", Node("Transpose", Symbol("z"))) self.check("z \uf3c8 ", Node("Conjugate", Symbol("z"))) self.check("z \uf3c9 ", Node("ConjugateTranspose", Symbol("z"))) self.check( "\\[Integral] x \\[DifferentialD] x", Node("Integrate", Symbol("x"), Symbol("x")), ) self.check("\\[Del] x", Node("Del", Symbol("x"))) self.check("\\[Square] x", Node("Square", Symbol("x"))) self.check("1 \\[SmallCircle] 2", Node("SmallCircle", Number("1"), Number("2"))) self.check( "1 \\[SmallCircle] 2 \\[SmallCircle] 3", Node("SmallCircle", Number("1"), Number("2"), Number("3")), ) self.check("1 \u2218 2", Node("SmallCircle", Number("1"), Number("2"))) self.check("1 \\[CircleDot] 2", Node("CircleDot", Number("1"), Number("2"))) self.check("1 \u2299 2", Node("CircleDot", Number("1"), Number("2"))) self.check("1 \\[Diamond] 2", Node("Diamond", Number("1"), Number("2"))) self.check("1 \\[Wedge] 2", Node("Wedge", Number("1"), Number("2"))) self.check("1 \\[Vee] 2", Node("Vee", Number("1"), Number("2"))) self.check("1 \\[CircleTimes] 2", Node("CircleTimes", Number("1"), Number("2"))) self.check("1 \\[CenterDot] 2", Node("CenterDot", Number("1"), Number("2"))) self.check("1 \\[Star] 2", Node("Star", Number("1"), Number("2"))) self.check("a \\[Cap] b", "Cap[a,b]") self.check("a \\[Cup] b \\[Cup] c", "Cup[a,b,c]") self.check("a \u2322 b \u2322 c", "Cap[a,b,c]") self.check("a \u2323 b", "Cup[a, b]") self.check("1 \u22C4 2", Node("Diamond", Number("1"), Number("2"))) self.check("1 \u22C0 2", Node("Wedge", Number("1"), Number("2"))) self.check("1 \u22c1 2", Node("Vee", Number("1"), Number("2"))) self.check("1 \u2297 2", Node("CircleTimes", Number("1"), Number("2"))) self.check("1 \u00B7 2", Node("CenterDot", Number("1"), Number("2"))) self.check("1 \u22C6 2", Node("Star", Number("1"), Number("2"))) self.check( "expr1 ** expr2", Node("NonCommutativeMultiply", Symbol("expr1"), Symbol("expr2")), ) self.check( "expr1 ** expr2 ** expr3", Node( "NonCommutativeMultiply", Symbol("expr1"), Symbol("expr2"), Symbol("expr3"), ), ) self.check("1 \\[Cross] 2", Node("Cross", Number("1"), Number("2"))) self.check("1 \uf4a0 2", Node("Cross", Number("1"), Number("2"))) self.check( "3\\[Divide]2", Node("Times", Number("3"), Node("Power", Number("2"), Number("-1"))), ) self.check("3 \u00f7 2", "Times[3, Power[2, -1]]") self.scan_error("3\\2") self.check("1 \\[Times] 2", Node("Times", Number("1"), Number("2"))) self.check("1 \u00d7 2", Node("Times", Number("1"), Number("2"))) self.check("1 \\[PlusMinus] 2", Node("PlusMinus", Number("1"), Number("2"))) self.check("1 \\[MinusPlus] 2", Node("MinusPlus", Number("1"), Number("2"))) self.check("\\[PlusMinus] 1", Node("PlusMinus", Number("1"))) self.check("\\[MinusPlus] 1", Node("MinusPlus", Number("1"))) self.check("\u00b1 1", Node("PlusMinus", Number("1"))) self.check("\u2213 1", Node("MinusPlus", Number("1"))) self.check("1 \\[And] 2", Node("And", Number("1"), Number("2"))) self.check("1 \u2227 2", Node("And", Number("1"), Number("2"))) self.check("1 \\[Or] 2", Node("Or", Number("1"), Number("2"))) self.check("1 \u2228 2", Node("Or", Number("1"), Number("2"))) self.check("a \\[Colon] b", Node("Colon", Symbol("a"), Symbol("b"))) self.check("a \u2236 b", Node("Colon", Symbol("a"), Symbol("b"))) self.check("x1 \\[RightTee] x2", "RightTee[x1, x2]") self.check("x1 \\[DoubleRightTee] x2", "DoubleRightTee[x1, x2]") self.check("x1 \\[LeftTee] x2", "LeftTee[x1, x2]") self.check("x1 \\[DoubleLeftTee] x2", "DoubleLeftTee[x1, x2]")