Kirill Simonov avatar Kirill Simonov committed dd6ddac

Fixed a problem with `:fn` followed by `/:cmd`.

Comments (0)

Files changed (7)

src/htsql/core/tr/parse.py

                         else:
                             rbranch = FlowParser << tokens
                             rbranches.append(rbranch)
-                mark = Mark.union(lbranch, identifier, tail_token, *rbranches)
-                branch = CommandSyntax(identifier, lbranch, rbranches, mark)
+                    mark = Mark.union(lbranch, identifier, tail_token,
+                                      *rbranches)
+                    branch = CommandSyntax(identifier, lbranch, rbranches, mark)
         mark = Mark.union(head_token, branch)
         segment = SegmentSyntax(branch, mark)
         return segment
         #   top         ::= flow ( direction | mapping )*
         #   direction   ::= '+' | '-'
         #   mapping     ::= ':' identifier ( flow | call )?
-        #   FOLLOW(mapping) = ['+','-',':',',',')','}']
+        #   FOLLOW(mapping) = ['+','-',':',',',')','}','/']
         top = FlowParser << tokens
         while tokens.peek(SymbolToken, [u'+', u'-', u':']):
             # Parse `direction` decorator.
                 else:
                     # Determine whether the mapping has a parameter or not.
                     # If not, must be followed by one of: `:` (next mapping),
-                    # `,`, `)`, `}` (punctuation), or `+`, `-` (direction
-                    # decorators).  We skip through `+` and `-` since they
-                    # could also start a parameter as an unary prefix operator.
+                    # `,`, `)`, `}` (punctuation), `/` (command) or `+`, `-`
+                    # (direction decorators).  We skip through `+` and `-`
+                    # since they could also start a parameter as an unary
+                    # prefix operator.  For `/`, we need to distinguish between
+                    # a trailing `/`, a segment expression and a command.
                     ahead = 0
                     while tokens.peek(SymbolToken, [u'+', u'-'], ahead=ahead):
                         ahead += 1
+                    if tokens.peek(SymbolToken, [u'/'], ahead=ahead):
+                        ahead += 1
                     if not (tokens.peek(SymbolToken, [u':', u',', u')', u'}'],
-                                                     ahead=ahead) or
+                                        ahead=ahead) or
                             tokens.peek(EndToken, ahead=ahead)):
                         rbranch = FlowParser << tokens
                         rbranches.append(rbranch)
         term = FactorParser << tokens
         while (tokens.peek(SymbolToken, [u'*'])
                or (tokens.peek(SymbolToken, [u'/'], ahead=0)
-                   and not tokens.peek(SymbolToken, [u':'], ahead=1))):
+                   and not (tokens.peek(EndToken, ahead=1) or
+                            tokens.peek(SymbolToken, [u':', u',', u')', u'}'],
+                                        ahead=1)))):
             symbol_token = tokens.pop(SymbolToken, [u'*', u'/'])
             symbol = symbol_token.value
             lbranch = term

test/input/translation.yaml

     - uri: /class^{year, season}
                 {*, /top(^.sort(count(enrollment)-),3).course{title}}
       ifndef: sqlite    # Too slow.
+    # parsing a command after an infix function call
+    - uri: /school:top/:csv
 
   - title: Table Expressions
     tests:

test/output/mssql.yaml

                       INNER JOIN [ad].[course]
                                  ON (([class_2].[department_code] = [course].[department_code]) AND ([class_2].[course_no] = [course].[no]))
                  ORDER BY 2 ASC, 3 ASC, [class_2].[count] DESC, [class_2].[department_code] ASC, [class_2].[course_no] ASC, [class_2].[year] ASC, [class_2].[season] ASC, [class_2].[section] ASC
+          - uri: /school:top/:csv
+            status: 200 OK
+            headers:
+            - [Content-Type, text/csv; charset=UTF-8]
+            - [Content-Disposition, 'attachment; filename="school:top.csv"']
+            body: "code,name,campus\r\nart,School of Art & Design,old\r\n"
         - id: table-expressions
           tests:
           - uri: /(school?code='art').department

test/output/mysql.yaml

                       INNER JOIN `course`
                                  ON ((`class_2`.`department_code` = `course`.`department_code`) AND (`class_2`.`course_no` = `course`.`no`))
                  ORDER BY 2 ASC, 3 ASC, `class_2`.`count` DESC, `class_2`.`department_code` ASC, `class_2`.`course_no` ASC, `class_2`.`year` ASC, `class_2`.`season` ASC, `class_2`.`section` ASC
+          - uri: /school:top/:csv
+            status: 200 OK
+            headers:
+            - [Content-Type, text/csv; charset=UTF-8]
+            - [Content-Disposition, 'attachment; filename="school:top.csv"']
+            body: "code,name,campus\r\nart,School of Art & Design,old\r\n"
         - id: table-expressions
           tests:
           - uri: /(school?code='art').department

test/output/oracle.yaml

                       INNER JOIN "COURSE"
                                  ON (("CLASS_2"."DEPARTMENT_CODE" = "COURSE"."DEPARTMENT_CODE") AND ("CLASS_2"."COURSE_NO" = "COURSE"."NO"))
                  ORDER BY 2 ASC, 3 ASC, "CLASS_2"."count" DESC, "CLASS_2"."DEPARTMENT_CODE" ASC, "CLASS_2"."COURSE_NO" ASC, "CLASS_2"."YEAR" ASC, "CLASS_2"."SEASON" ASC, "CLASS_2"."SECTION" ASC
+          - uri: /school:top/:csv
+            status: 200 OK
+            headers:
+            - [Content-Type, text/csv; charset=UTF-8]
+            - [Content-Disposition, 'attachment; filename="school:top.csv"']
+            body: "code,name,campus\r\nart,School of Art & Design,old\r\n"
         - id: table-expressions
           tests:
           - uri: /(school?code='art').department

test/output/pgsql.yaml

                       INNER JOIN "ad"."course"
                                  ON (("class_2"."department_code" = "course"."department_code") AND ("class_2"."course_no" = "course"."no"))
                  ORDER BY 2 ASC, 3 ASC, "class_2"."count" DESC, "class_2"."department_code" ASC, "class_2"."course_no" ASC, "class_2"."year" ASC, "class_2"."season" ASC, "class_2"."section" ASC
+          - uri: /school:top/:csv
+            status: 200 OK
+            headers:
+            - [Content-Type, text/csv; charset=UTF-8]
+            - [Content-Disposition, 'attachment; filename="school:top.csv"']
+            body: "code,name,campus\r\nart,School of Art & Design,old\r\n"
         - id: table-expressions
           tests:
           - uri: /(school?code='art').department

test/output/sqlite.yaml

                                                LIMIT 3))) AS "class_3"
                                  ON (("class_2"."department_code" = "class_3"."department_code") AND ("class_2"."course_no" = "class_3"."course_no") AND ("class_2"."year" = "class_3"."year") AND ("class_2"."season" = "class_3"."season") AND ("class_2"."section" = "class_3"."section"))
                  ORDER BY 2 ASC, 3 ASC, "class_3"."count" DESC, "class_2"."department_code" ASC, "class_2"."course_no" ASC, "class_2"."year" ASC, "class_2"."season" ASC, "class_2"."section" ASC
+          - uri: /school:top/:csv
+            status: 200 OK
+            headers:
+            - [Content-Type, text/csv; charset=UTF-8]
+            - [Content-Disposition, 'attachment; filename="school:top.csv"']
+            body: "code,name,campus\r\nart,School of Art & Design,old\r\n"
         - id: table-expressions
           tests:
           - uri: /(school?code='art').department
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.