📜 ⬆️ ⬇️

Standards for syntax syntax. The first steps

For the seed and substantive discussion need a starting point. In the template engine we are interested in the supported types of structures, the way they are framed, some possibilities (preview without compilation).
The main types of structures to control the output in the template:
  1. Variables.
  2. Conditional statements
  3. Cycle operators.
  4. Inclodes, blocks.
  5. Inheritance.

Already these designs are more than enough to implement most projects. And by itself, we see them in almost every template maker, with some differences or peculiarities. But let's think about what opportunities in theory they should allow to use, and how these implementations should look ideally .

To highlight any design in the template uses different symbols of the frame. In different template engines it looks different:
<TMPL_VAR JOB> {{ namevar }} <% if(question) { %> 

The topic of choice of those deserves special attention, and in order not to be distracted we will choose one thing.
 %   % 

1. Variables.

The most commonly used design. In addition to simple calls, much less often, but there is a need to refer to the value of nested variables. Even less often, when the value of a variable is contained in another variable. Nested elements can be both arrays and hashes. Well, now imagine all the possible combinations of the conditions described.
 var data = { title: "  ", img : { src: "/img/picture.jpg" }, list : [ '', '', '', '' ], namevar : "theme", noname: { theme : "  " } }; 

 % title % // data.title % img.src % // data.img.src % list.3 % // data.list[3] % noname[ namevar ] % // data.noname[ data.namevar ] 

Problems. In beautiful javascript view
 data.list.12 == data['list']['12']; //  

It can correspond to both an array element and a hash element. Most server languages ​​cannot boast of such a property. Eliminate this possibility, or do checks on the type of element? How will this affect performance?
Probably, sigil definition for variable types (variable from config, service variable, standard variable) can be useful.

2. Conditional operators.

There are two questions here, should you use the if operator? And determine the list of valid logical operators.
  1. On the one hand, the unless statement is easily replaced with if with negation. You can argue that this is a jumble, albeit a small one. On the other hand, this is supposedly a reduction (? In javascript, for example, there is no such operator) of a functional.
  2. Most logical operators are somehow supported by all programming languages. But, since the concrete record of operators differs from language to language, it becomes clear that some intermediate variant is needed, which is translated by the parser into the necessary one. The need for an intermediate variant is also supported by the need to use regular expressions in the condition. And the syntax of regular expressions is the same for all PL? Not sure, and something suggests that something will have to sacrifice.

3. Cycle operators.

Not so many of them. You can rejoice. for / foreach / while . As data, there can be an array of elements, an array of arrays, an array of hashes. The most common is the array of hashes, and you need to pay attention to ensure maximum ease of access to the elements of the iterated hash. The name of the loop must be transferred to the construct (to refer to the nested elements inside other loops), if necessary, the source for iterations, the array slice, the name of the method / function of the handler of the item to be iterated.
 % nameobj => source.array.data [3..5] :loop_handler % 

Where nameobj is the name for the object. Next we can refer to the element:
 % nameobj.img.src % 

For a data source, the path to any variable source.array.data can be used .
Sometimes it happens that we need a slice of the array, from the third to the fifth element [3..5]
Before inserting data, it may be necessary to format them; for this, nm can serve as a certain handler that runs at each iteration, to which a reference is sent to the current iterated item.

4. Inclodes, blocks.

Very often we have to insert into the template other or part of other templates. The syntax should provide for the possibility of passing the argument to the inclusion.
Blocks are the same inclusions, with the only exception that they are not in a separate file. Accordingly, when referring to the block, you need to specify the name of the template in which it is located.
 % nameinclude % % nameinclude => source.data.inc % % var.for.inc => source.for.inc % 

It is necessary to give the opportunity to set the name of the connection via the variable, useful for alternatives to inheritance.

5. Inheritance.

Honestly, I always managed without the use of inheritance in templates, but this does not negate the need to have such an opportunity, I guess. But to make it as simple as possible. In this case, it is certainly good to be able not only to redefine individual blocks from the inherited template, but also to do the replacement of the block with an existing insert / block. This can be solved by setting the name of the connection through a variable. Yes, the inheritance problem is solved in this way: to define all replaced blocks as inclusions, and to manage their names ...

Some template engines allow you to use constructs in a template for writing code directly in PL. Obviously, such solutions are not what we need.

It is difficult to consider any constructions in more detail without understanding what symbols they will be framed in, and what prefixes will be used for variables and operators inside the contraction. Making a list of constructs that we are going to use, it will be possible to proceed to the consideration of framing symbols, tags, sigils.

UPD: I apologize, if not clearly expressed. The fact that the% symbol is represented as framing symbols in the examples is just an example. This is not at all an offer to use it, just to deliver something.

Source: https://habr.com/ru/post/104962/

All Articles