|
33 | 33 | #define newToken() (objPoolGet (TokenPool)) |
34 | 34 | #define deleteToken(t) (objPoolPut (TokenPool, (t))) |
35 | 35 |
|
| 36 | +/* A token holding a soft keyword |
| 37 | + * has TOKEN_IDENTIFIER as its type member and |
| 38 | + * SOFT_KEYWORD_* as its keyword member. |
| 39 | + */ |
| 40 | +#define isSoftKeyword(K) (K < 0 && K != KEYWORD_NONE) |
36 | 41 | enum { |
37 | | - KEYWORD_as, |
| 42 | + SOFT_KEYWORD_type = -2, |
| 43 | + /* KEYWORD_NONE occupies -1. */ |
| 44 | + KEYWORD_as = 0, |
38 | 45 | KEYWORD_async, |
39 | 46 | KEYWORD_cdef, |
40 | 47 | KEYWORD_class, |
@@ -157,6 +164,7 @@ static const keywordTable PythonKeywordTable[] = { |
157 | 164 | { "lambda", KEYWORD_lambda }, |
158 | 165 | { "pass", KEYWORD_pass }, |
159 | 166 | { "return", KEYWORD_return }, |
| 167 | + { "type", SOFT_KEYWORD_type }, |
160 | 168 | }; |
161 | 169 |
|
162 | 170 | /* Taken from https://docs.python.org/3/reference/lexical_analysis.html#keywords */ |
@@ -661,7 +669,8 @@ static void readTokenFull (tokenInfo *const token, bool inclWhitespaces) |
661 | 669 | /* FIXME: handle U, B, R and F string prefixes? */ |
662 | 670 | readIdentifier (token->string, c); |
663 | 671 | token->keyword = lookupKeyword (vStringValue (token->string), Lang_python); |
664 | | - if (token->keyword == KEYWORD_NONE) |
| 672 | + if (token->keyword == KEYWORD_NONE |
| 673 | + || isSoftKeyword(token->keyword)) |
665 | 674 | token->type = TOKEN_IDENTIFIER; |
666 | 675 | else |
667 | 676 | token->type = TOKEN_KEYWORD; |
@@ -1747,6 +1756,41 @@ static void setIndent (tokenInfo *const token) |
1747 | 1756 | } |
1748 | 1757 | } |
1749 | 1758 |
|
| 1759 | +static bool parseType (tokenInfo *const token, pythonKind kind) |
| 1760 | +{ |
| 1761 | + TRACE_ENTER(); |
| 1762 | + /* https://docs.python.org/3.14/reference/simple_stmts.html#type */ |
| 1763 | + int index = makeSimplePythonTag (token, kind); |
| 1764 | + tagEntryInfo *e = getEntryInCorkQueue (index); |
| 1765 | + if (e) |
| 1766 | + { |
| 1767 | + /* Technically the type statement creates variables of type typing.TypeAliasType, |
| 1768 | + * which is a different thing from typing.TypeAlias (which is deprecated). |
| 1769 | + * Thus it would be misleading to claim they're of type TypeAlias. */ |
| 1770 | + e->extensionFields.typeRef [0] = eStrdup ("typename"); |
| 1771 | + e->extensionFields.typeRef [1] = eStrdup ("TypeAliasType"); |
| 1772 | + } |
| 1773 | + |
| 1774 | + readToken (token); |
| 1775 | + |
| 1776 | + if (token->type == '[') |
| 1777 | + { |
| 1778 | + if (skipOverPair (token, '[', ']', NULL, false)) |
| 1779 | + readToken (token); |
| 1780 | + } |
| 1781 | + if (token->type == TOKEN_EOF) |
| 1782 | + { |
| 1783 | + TRACE_LEAVE_TEXT("Unexpected EOF"); |
| 1784 | + return false; |
| 1785 | + } |
| 1786 | + |
| 1787 | + vString *tspec = vStringNew (); |
| 1788 | + bool r = skipVariableTypeAnnotation (token, tspec); |
| 1789 | + vStringDelete (tspec); |
| 1790 | + TRACE_LEAVE(); |
| 1791 | + return r; |
| 1792 | +} |
| 1793 | + |
1750 | 1794 | static void findPythonTags (void) |
1751 | 1795 | { |
1752 | 1796 | TRACE_ENTER(); |
@@ -1797,11 +1841,35 @@ static void findPythonTags (void) |
1797 | 1841 | NestingLevel *lv = nestingLevelsGetCurrent (PythonNestingLevels); |
1798 | 1842 | tagEntryInfo *lvEntry = getEntryOfNestingLevel (lv); |
1799 | 1843 | pythonKind kind = PYTHON_VARIABLE_KIND; |
| 1844 | + bool isTypeStatement = false; |
1800 | 1845 |
|
1801 | 1846 | if (lvEntry && lvEntry->kindIndex != PYTHON_CLASS_KIND) |
1802 | 1847 | kind = PYTHON_LOCAL_VARIABLE_KIND; |
1803 | 1848 |
|
1804 | | - readNext = parseVariable (token, kind); |
| 1849 | + if (token->keyword == SOFT_KEYWORD_type) |
| 1850 | + { |
| 1851 | + /* Is a type statement? */ |
| 1852 | + tokenInfo *const type = newToken (); |
| 1853 | + |
| 1854 | + copyToken (type, token); |
| 1855 | + readToken (token); |
| 1856 | + if (token->type == TOKEN_IDENTIFIER) |
| 1857 | + { |
| 1858 | + /* Yes. this is a type statement. */ |
| 1859 | + isTypeStatement = true; |
| 1860 | + readNext = parseType (token, kind); |
| 1861 | + } |
| 1862 | + else |
| 1863 | + { |
| 1864 | + /* No. */ |
| 1865 | + ungetToken (token); |
| 1866 | + copyToken (token, type); |
| 1867 | + } |
| 1868 | + deleteToken (type); |
| 1869 | + } |
| 1870 | + |
| 1871 | + if (!isTypeStatement) |
| 1872 | + readNext = parseVariable (token, kind); |
1805 | 1873 | } |
1806 | 1874 | else if (token->type == '@' && atStatementStart && |
1807 | 1875 | PythonFields[F_DECORATORS].enabled) |
|
0 commit comments