... |
... |
|
|
|
|
|
Parsing Declarations |
|
==================== |
|
|
|
The SCALE language consists entirely of "declaration" statements. A |
|
declaration is an expression with optional assignment prefixes, an optional |
|
"context" clause, and an optional nested block. Here are some examples of |
|
valid SCALE declarations:: |
|
|
|
>>> samples = dsl.parse_block(dsl.tokenize_string(""" |
|
... |
|
... foo(gee): |
|
... zing from Zang |
|
... bar = baz = squidge() from spammity>=1.2: |
|
... answer = 42 |
|
... |
|
... "whiz" = "oh boy" = 123 * 456 |
|
... |
|
... something = me from: |
|
... me = 789 |
|
... |
|
... """)) |
|
|
|
The ``parse_declarations()`` function yields 4-element tuples containing the |
|
assigned names, value expression, context clause, and nested block for each |
|
statement in a given block:: |
|
|
|
>>> def print_decl(names, expr, context, block): |
|
... if names: |
|
... print "Assigned to:", names |
|
... print "Expression:", `dsl.detokenize(expr)` |
|
... if context is not None: |
|
... print "Context:", `dsl.detokenize(context)` |
|
... if block: |
|
... print "Block:" |
|
... print dsl.detokenize(dsl.flatten_block(block),8).rstrip() |
|
... print |
|
|
|
>>> for names, expr, context, block in dsl.parse_declarations(samples): |
|
... print_decl(names, expr, context, block) |
|
... |
|
Expression: 'foo(gee)' |
|
Block: |
|
zing from Zang |
|
bar = baz = squidge() from spammity>=1.2: |
|
answer = 42 |
|
... |
|
Assigned to: ['whiz', 'oh boy'] |
|
Expression: '123 * 456' |
|
... |
|
Assigned to: ['something'] |
|
Expression: 'me' |
|
Context: '' |
|
Block: |
|
me = 789 |
|
... |
|
|
|
>>> for names,expr,context,block in dsl.parse_declarations(samples[0][1]): |
|
... print_decl(names, expr, context, block) |
|
... |
|
Expression: 'zing' |
|
Context: 'Zang' |
|
... |
|
Assigned to: ['bar', 'baz'] |
|
Expression: 'squidge()' |
|
Context: 'spammity>=1.2' |
|
Block: |
|
answer = 42 |
|
... |
|
|
|
As you can see, the `context` of a declaration is either a token list or None, |
|
depending on whether a "from" clause appeared in the declaration. `names` is |
|
a (possibly empty) list of the names to which the expression is being assigned, |
|
with quoted strings being treated as if they were normal identifiers. `expr`, |
|
meanwhile, is the token list for the declaration's value expression, and |
|
`block` is the nested block beneath the statement, if any. The `expr` and |
|
`context` token lists are stripped of whitespace tokens at the top level, and |
|
do not contain the ``:`` if one was present at the end of the declaration. |
|
|
|
If a parsed line is not a valid declaration, a ``TokenError`` is raised. Valid |
|
declarations must end with a ``:`` if and only if an indented block follows:: |
|
|
|
>>> def try_parsing(s): |
|
... list( |
|
... dsl.parse_declarations(dsl.parse_block(dsl.tokenize_string(s))) |
|
... ) |
|
|
|
>>> try_parsing("xyz:") |
|
Traceback (most recent call last): |
|
... |
|
TokenError: ("Expected indented block following ':'", (1, 4)) |
|
|
|
>>> try_parsing("xyz\n foo") |
|
Traceback (most recent call last): |
|
... |
|
TokenError: ("Expected ':' before indented block", (1, 3)) |
|
|
|
And the "from" clause of a declaration can only be empty if it introduces a |
|
block:: |
|
|
|
>>> try_parsing("xyz from:\n foo") # okay |
|
>>> try_parsing("xyz from 123") # also okay |
|
>>> try_parsing("xyz from") # not okay |
|
Traceback (most recent call last): |
|
... |
|
TokenError: ("Expected context or ':' after 'from'", (1, 8)) |
|
|
|
It's also not valid to omit the expression if the declaration assigns to any |
|
names:: |
|
|
|
>>> try_parsing("from foo") # okay |
|
>>> try_parsing("foo = from bar") # not okay |
|
Traceback (most recent call last): |
|
... |
|
TokenError: ('Expected expression', (1, 5)) |
|
>>> try_parsing("foo =") # also not okay |
|
Traceback (most recent call last): |
|
... |
|
TokenError: ('Expected expression', (1, 5)) |
|
|
|
It's also an error to assign to something that's not an identifier or quoted |
|
string:: |
|
|
|
>>> try_parsing("abc = '123' = 456") # okay, assigns 'abc' and '123' |
|
>>> try_parsing("123 = 456") # can't assign to number! |
|
Traceback (most recent call last): |
|
... |
|
TokenError: ("Expected name or string before '='", (1, 4)) |
|
|
|
It's important to note, however, that ``parse_declarations()`` does not |
|
prescribe or guarantee any particular syntax for the `expr` and `context` |
|
clauses. So, they are not guaranteed to be valid Python expressions. This |
|
means that you can create SCALE dialects with different expression or context |
|
syntax, but it also means that you should be prepared for the possibility of |
|
syntax errors within the expression or context clause. |
|
|