Skip to content

Commit

Permalink
0.6.1-dev.5
Browse files Browse the repository at this point in the history
  • Loading branch information
ykmnkmi committed Sep 27, 2024
1 parent de5106d commit 71a9e3c
Show file tree
Hide file tree
Showing 16 changed files with 357 additions and 343 deletions.
6 changes: 3 additions & 3 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@
- `UndefinedError` exception
- `UndefinedFactory` typedef
- `Environment`:
- `Environment({undefined})` argument
- `undefined` field
- `Environment({UndefinedFactory undefined})` argument
- `UndefinedFactory undefined` field
- `Template`:
- `Template({undefined})` argument
- `Template({UndefinedFactory undefined})` argument
- Filters:
- `null` (`none` alias)

Expand Down
18 changes: 17 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,22 @@ See also examples with [conduit][conduit_example] and
- `stream` method
- Relative template paths
- Async Support
- Template Inheritance
- Super Blocks
- `{{ super.super() }}`
- List of Control Structures
- Extends
- Execute non-`block` statements and expressions
```jinja
{% extends 'base.html' %}
{% set title = 'Index' %}
{% macro header() %}
<h1>{{ title }}</h1>
{% endmacro %} ```
{% block body %}
{{ header() }}
{% endblock %}
```
- Expressions
- Dart Methods and Properties
- `!.`/`?.`
Expand All @@ -73,7 +89,6 @@ See also examples with [conduit][conduit_example] and
- ...
- List of Global Functions
- `lipsum`
- `dict`
- `cycler`
- `joiner`
- Extensions
Expand Down Expand Up @@ -165,6 +180,7 @@ See also examples with [conduit][conduit_example] and
- `{{ string.toUpperCase() }}`
- `{{ list.add(item) }}`
- List of Global Functions
- ~~`dict`~~
- `print`
- `range`
- `list`
Expand Down
42 changes: 28 additions & 14 deletions lib/src/compiler.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,18 @@ import 'package:jinja/src/nodes.dart';
import 'package:jinja/src/visitor.dart';
import 'package:meta/meta.dart';

// TODO(renderer): Rename to `StringSinkRendererCompiler`
// and move to `renderer.dart`. Add `ContextNode` for `ContextCallback`s.
@doNotStore
class RuntimeCompiler implements Visitor<void, Node> {
RuntimeCompiler()
: _imports = <String>{},
_macroses = <String>{},
_macros = <String>{},
_inMacro = false;

final Set<String> _imports;

final Set<String> _macroses;
final Set<String> _macros;

bool _inMacro;

Expand Down Expand Up @@ -46,8 +48,8 @@ class RuntimeCompiler implements Visitor<void, Node> {

@override
Call visitCall(Call node, void context) {
// Modifies Template AST from `loop.cycle(first, second, *list)`
// to `loop['cycle']([first, second], list)`, which matches
// Modifies Template AST from `loop.cycle(first, second)`
// to `loop['cycle']([first, second])`, which matches
// [LoopContext.cycle] definition.
//
// TODO(compiler): check name arguments
Expand All @@ -59,7 +61,7 @@ class RuntimeCompiler implements Visitor<void, Node> {
: <Expression>[Array(values: calling.arguments)];

return node.copyWith(
value: visitNode(node.value, context),
value: Item(key: Constant(value: 'cycle'), value: value),
calling: Calling(arguments: visitNodes(arguments, context)),
);
}
Expand Down Expand Up @@ -94,7 +96,7 @@ class RuntimeCompiler implements Visitor<void, Node> {
);
}

if (_inMacro && name == 'caller' || _macroses.contains(name)) {
if (_inMacro && name == 'caller' || _macros.contains(name)) {
var calling = visitNode<Calling>(node.calling, context);
var arguments = calling.arguments;
var keywords = calling.keywords;
Expand Down Expand Up @@ -344,7 +346,7 @@ class RuntimeCompiler implements Visitor<void, Node> {
@override
FromImport visitFromImport(FromImport node, void context) {
for (var (name, alias) in node.names) {
_macroses.add(alias ?? name);
_macros.add(alias ?? name);
}

return node.copyWith(template: visitNode(node.template, context));
Expand Down Expand Up @@ -378,7 +380,7 @@ class RuntimeCompiler implements Visitor<void, Node> {
@override
Macro visitMacro(Macro node, void context) {
_inMacro = true;
_macroses.add(node.name);
_macros.add(node.name);

var positional = <Expression>[];
var named = <(Expression, Expression)>[];
Expand Down Expand Up @@ -420,17 +422,29 @@ class RuntimeCompiler implements Visitor<void, Node> {
}

@override
Output visitOutput(Output node, void context) {
Node visitOutput(Output node, void context) {
if (node.nodes.isEmpty) {
return Data();
}

if (node.nodes.length == 1) {
return visitNode(node.nodes[0], context);
}

return node.copyWith(nodes: visitNodes(node.nodes, context));
}

@override
TemplateNode visitTemplateNode(TemplateNode node, void context) {
return node.copyWith(
blocks: visitNodes(node.blocks, context),
macros: visitNodes(node.macros, context),
body: visitNode(node.body, context),
);
var body = visitNode(node.body, context) as Node;
var blocks = <Block>[if (body is Block) body, ...body.findAll<Block>()];
var macros = <Macro>[if (body is Macro) body, ...body.findAll<Macro>()];

if (body is Output && body.nodes.first is Extends) {
body = body.nodes.first;
}

return node.copyWith(blocks: blocks, macros: macros, body: body);
}

@override
Expand Down
6 changes: 2 additions & 4 deletions lib/src/environment.dart
Original file line number Diff line number Diff line change
Expand Up @@ -342,10 +342,8 @@ base class Environment {
}

/// Parse the source code and return the AST node.
///
/// This can be useful for debugging or to extract information from templates.
Node parse(String source, {String? path}) {
return scan(lex(source), path: path);
return Parser(this, path: path).parse(source);
}

/// Load a template from a source string without using [loader].
Expand Down Expand Up @@ -564,7 +562,7 @@ base class Template {
/// The global variables for this template.
final Map<String, Object?> globals;

/// Template body node.
// @nodoc
@internal
final TemplateNode body;

Expand Down
200 changes: 4 additions & 196 deletions lib/src/loop.dart
Original file line number Diff line number Diff line change
@@ -1,197 +1,5 @@
final class LoopContext extends Iterable<Object?> {
LoopContext(this.values, this.depth0, this.recurse)
: length = values.length,
index0 = -1;
@Deprecated('Use `package:jinja/src/runtime.dart` instead')
library;

final List<Object?> values;

@override
final int length;

final int depth0;

final String Function(Object? data, [int depth]) recurse;

int index0;

@override
LoopIterator get iterator {
return LoopIterator(this);
}

int get index {
return index0 + 1;
}

int get depth {
return depth0 + 1;
}

int get revindex0 {
return length - index;
}

int get revindex {
return length - index0;
}

@override
bool get first {
return index0 == 0;
}

@override
bool get last {
return index == length;
}

Object? get next {
if (last) {
return null;
}

return values[index0 + 1];
}

Object? get prev {
if (first) {
return null;
}

return values[index0 - 1];
}

Object? operator [](String key) {
switch (key) {
case 'length':
return length;
case 'index0':
return index0;
case 'depth0':
return depth0;
case 'index':
return index;
case 'depth':
return depth;
case 'revindex0':
return revindex0;
case 'revindex':
return revindex;
case 'first':
return first;
case 'last':
return last;
case 'prev':
case 'previtem':
return prev;
case 'next':
case 'nextitem':
return next;
case 'call':
return call;
case 'cycle':
return cycle;
case 'changed':
return changed;
default:
var invocation = Invocation.getter(Symbol(key));
throw NoSuchMethodError.withInvocation(this, invocation);
}
}

String call(Object? data) {
return recurse(data, depth);
}

Object? cycle(Iterable<Object?> values) {
var list = values.toList();

if (list.isEmpty) {
// TODO(loop): update error
throw TypeError();
}

return list[index0 % list.length];
}

bool changed(Object? item) {
if (index0 == 0) {
return true;
}

if (item == prev) {
return false;
}

return true;
}
}

final class LoopIterator implements Iterator<Object?> {
LoopIterator(this.context);

final LoopContext context;

@override
Object? get current {
return context.values[context.index0];
}

@override
bool moveNext() {
if (context.index < context.length) {
context.index0 += 1;
return true;
}

return false;
}
}

final class Cycler extends Iterable<Object?> {
Cycler(Iterable<Object?> values)
: values = List<Object?>.of(values),
length = values.length,
index = 0;

final List<Object?> values;

@override
final int length;

int index;

Object? get current {
return values[index];
}

@override
Iterator<Object?> get iterator {
return CyclerIterator(this);
}

Object? next() {
var result = current;
index = (index + 1) % length;
return result;
}

void reset() {
index = 0;
}
}

final class CyclerIterator implements Iterator<Object?> {
CyclerIterator(this.cycler);

final Cycler cycler;

@override
Object? current;

@override
bool moveNext() {
current = cycler.next();
return true;
}
}
export 'package:jinja/src/runtime.dart'
show LoopContext, LoopIterator, Cycler, CyclerIterator;
Loading

0 comments on commit 71a9e3c

Please sign in to comment.