Skip to content

Commit 0c025d9

Browse files
committed
Else support
1 parent ddde100 commit 0c025d9

File tree

3 files changed

+91
-53
lines changed

3 files changed

+91
-53
lines changed

README.md

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,18 @@ So, with this principles in mind, you can try this proof of concept thing, due i
1818
sys.meta_path.insert(0, backslant.PymlFinder('./templates', hook='backslant_import'))
1919
from backslant_import.home import index
2020

21-
for chunk in index.render():
21+
for chunk in index.render(title='The Real Thing'):
2222
print(chunk)
2323

24+
And templates/home/index.bs:
2425

25-
I will complete feture set soon, stay tuned. Pssst, we will have ruby-like blocks, dont tell anyone.
26+
html
27+
head
28+
title
29+
- yield options['title']
30+
body
31+
div.content
32+
h1
33+
"Header"
34+
35+
I will complete feture set soon, stay tuned.

backslant.py

Lines changed: 74 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -47,51 +47,57 @@ def parse_file(filename):
4747
return idented_tree
4848

4949

50-
html_chars = pp.alphanums + '-_'
51-
tag_def = pp.Word(html_chars)
52-
tag_class = pp.Combine(pp.Literal('.') + pp.Word(html_chars))
53-
tag_id = pp.Combine(pp.Literal('#') + pp.Word(html_chars))
54-
attribute_name = pp.Word(html_chars)
55-
56-
def parse_attribute_value(s, l, t):
57-
return ast.parse(s[t._original_start:t._original_end]).body[0].value
58-
attribute_value = pp.originalTextFor(
59-
pp.sglQuotedString()
60-
^ pp.dblQuotedString()
61-
^ pp.nestedExpr()
62-
^ pp.nestedExpr('[', ']')
63-
).setParseAction(parse_attribute_value)
64-
65-
def parse_tag_attribute(s, l, t):
66-
return ast.Dict(
67-
keys=[ast.Str(s=str(k)) for k, v in t],
68-
values=[v for k, v in t]
50+
def create_parser():
51+
html_chars = pp.alphanums + '-_'
52+
tag_def = pp.Word(html_chars)
53+
tag_class = pp.Combine(pp.Literal('.') + pp.Word(html_chars))
54+
tag_id = pp.Combine(pp.Literal('#') + pp.Word(html_chars))
55+
attribute_name = pp.Word(html_chars)
56+
57+
def parse_attribute_value(s, l, t):
58+
return ast.parse(s[t._original_start:t._original_end]).body[0].value
59+
attribute_value = pp.originalTextFor(
60+
pp.sglQuotedString()
61+
^ pp.dblQuotedString()
62+
^ pp.nestedExpr()
63+
^ pp.nestedExpr('[', ']')
64+
).setParseAction(parse_attribute_value)
65+
66+
def parse_tag_attribute(s, l, t):
67+
return ast.Dict(
68+
keys=[ast.Str(s=str(k)) for k, v in t],
69+
values=[v for k, v in t]
70+
)
71+
tag_attribute = pp.Group(attribute_name + pp.Suppress('=') + attribute_value).setParseAction(parse_tag_attribute)
72+
tag_dict = pp.nestedExpr('{', '}').setParseAction(
73+
lambda s, l, t: ast.parse(s[l:]).body[0].value
74+
)
75+
tag_grammar = (
76+
tag_def
77+
+ pp.Group(pp.ZeroOrMore(tag_class ^ tag_id))
78+
+ (tag_dict ^ pp.Group(pp.ZeroOrMore(tag_attribute)))
6979
)
70-
tag_attribute = pp.Group(attribute_name + pp.Suppress('=') + attribute_value).setParseAction(parse_tag_attribute)
71-
tag_dict = pp.nestedExpr('{', '}').setParseAction(
72-
lambda s, l, t: ast.parse(s[l:]).body[0].value
73-
)
74-
tag_grammar = (
75-
tag_def
76-
+ pp.Group(pp.ZeroOrMore(tag_class ^ tag_id))
77-
+ (tag_dict ^ pp.Group(pp.ZeroOrMore(tag_attribute)))
78-
)
79-
def parse_tag(node_string):
80-
node, attrs_keys, node_attrs = tag_grammar.parseString(node_string)
81-
node_attrs = None if not node_attrs else node_attrs
82-
node_attrs = node_attrs[0] if isinstance(node_attrs, pp.ParseResults) else node_attrs
83-
return node, node_attrs
80+
def parse_tag(node_string):
81+
node, attrs_keys, node_attrs = tag_grammar.parseString(node_string)
82+
node_attrs = None if not node_attrs else node_attrs
83+
node_attrs = node_attrs[0] if isinstance(node_attrs, pp.ParseResults) else node_attrs
84+
return node, node_attrs
85+
return parse_tag
86+
87+
parse_tag = create_parser()
8488

8589

8690
def dump_ast(node, tabs=0):
87-
space = ' ' * tabs
91+
space = ' ' * tabs
8892
print(space, '--', node, getattr(node, 'lineno', None), getattr(node, 'col_offset', None))
8993
if hasattr(node, '_fields'):
9094
for field in node._fields:
91-
print(space, field, dump_ast(getattr(node, field), tabs + 1))
95+
if getattr(node, field):
96+
print(space, '=', field)
97+
dump_ast(getattr(node, field), tabs + 1)
9298
elif isinstance(node, list):
9399
for node_ in node:
94-
print(space, dump_ast(node_, tabs + 1))
100+
dump_ast(node_, tabs + 1)
95101

96102

97103
NoValue = object()
@@ -117,18 +123,36 @@ def stop(self):
117123

118124

119125
def _convert_to_ast(idented_tree, order=0):
120-
elements = []
126+
node_with_else = None # track if and for
121127
for lineno, node_string, childs in idented_tree:
122128
ast_childs = _convert_to_ast(childs, order=order)
129+
elif_ = False
123130
if node_string.startswith('-'):
124-
if node_string.endswith(':'):
125-
node_string = node_string + ' pass'
126-
res = ast.parse(node_string[1:].strip(), filename='text.pyml').body[0]
127-
res.lineno = lineno
128-
res.col_offset = 0
129-
if childs and isinstance(res.body[0], ast.Pass):
130-
res.body = list(ast_childs) or []
131-
yield res
131+
node_string = node_string[1:].strip()
132+
if node_string == 'else':
133+
node_with_else.orelse = list(ast_childs) or []
134+
else:
135+
if node_string.startswith('elif '):
136+
node_string = node_string[2:]
137+
elif_ = True
138+
else:
139+
# swap out (if|for)node
140+
node_with_else = None
141+
if node_string.endswith(':'):
142+
node_string = node_string + ' pass'
143+
res = ast.parse(node_string, filename='text.pyml').body[0]
144+
res.lineno = lineno + 1
145+
res.col_offset = 0
146+
if not elif_ and isinstance(res, (ast.If, ast.For)):
147+
node_with_else = res
148+
if childs and isinstance(res.body[0], ast.Pass):
149+
res.body = list(ast_childs) or []
150+
ast.fix_missing_locations(res)
151+
if elif_:
152+
node_with_else.orelse = [res]
153+
node_with_else = res
154+
else:
155+
yield res
132156
continue
133157
if node_string.startswith('"'):
134158
yield ast.Expr(
@@ -148,7 +172,6 @@ def _convert_to_ast(idented_tree, order=0):
148172
keywords=[],
149173
starargs=None,
150174
kwargs=node_attrs,
151-
**line_n_offset
152175
),
153176
**line_n_offset
154177
)
@@ -179,16 +202,16 @@ def convert_to_ast(idented_tree, order=0):
179202

180203

181204

182-
def convert_internal_ast_to_python_code(idented_tree):
205+
def convert_internal_ast_to_python_code(idented_tree, filename='<unknown>'):
183206
result_ast = ast.Module(
184207
body=[ast.FunctionDef(
185208
name='render',
186209
args=ast.arguments(
187210
args=[],
188-
vararg=ast.arg(arg='arguments'),
211+
vararg=ast.arg(arg='arguments', annotation=None),
189212
kwonlyargs=[],
190213
kw_defaults=[],
191-
kwarg=ast.arg(arg='options'),
214+
kwarg=ast.arg(arg='options', annotation=None),
192215
defaults=[],
193216
),
194217
body=convert_to_ast(idented_tree),
@@ -199,7 +222,7 @@ def convert_internal_ast_to_python_code(idented_tree):
199222
)
200223
result_ast = ast.fix_missing_locations(result_ast)
201224
# dump_ast(result_ast)
202-
return compile(result_ast, 'test.pyml', 'exec')
225+
return compile(result_ast, filename, 'exec')
203226

204227

205228
def convert_to_function(code):
@@ -262,7 +285,7 @@ def load_module(self, fullname):
262285
mod.__loader__ = self
263286
mod.__package__ = '.'.join(fullname.split('.')[:-1])
264287
mod.__dict__['Tag'] = self.tag_class
265-
code = convert_internal_ast_to_python_code(parse_file(self.filename))
288+
code = convert_internal_ast_to_python_code(parse_file(self.filename), filename=self.filename)
266289
exec(code, mod.__dict__)
267290
return mod
268291

templates/test.bs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,4 +31,9 @@ html
3131

3232
- if True:
3333
"conditional"
34+
- elif 'title' in options:
35+
p
36+
"whoa"
37+
- else
38+
"WAT?"
3439
- yield from ku('there')

0 commit comments

Comments
 (0)