1 /** 2 * AST nodes 3 * 4 * Copyright: 5 * Copyright (c) 2018, Maxim Tyapkin. 6 * Authors: 7 * Maxim Tyapkin 8 * License: 9 * This software is licensed under the terms of the BSD 3-clause license. 10 * The full terms of the license can be found in the LICENSE.md file. 11 */ 12 13 module djinja.ast.node; 14 15 public 16 { 17 import std.typecons : Nullable, nullable; 18 import djinja.lexer : Position; 19 } 20 21 private 22 { 23 import std.meta : AliasSeq; 24 25 import djinja.ast.visitor; 26 } 27 28 29 alias NodeTypes = AliasSeq!( 30 TemplateNode, 31 BlockNode, 32 StmtBlockNode, 33 RawNode, 34 ExprNode, 35 UnaryOpNode, 36 BinOpNode, 37 StringNode, 38 BooleanNode, 39 NilNode, 40 ListNode, 41 DictNode, 42 NumNode, 43 IdentNode, 44 IfNode, 45 ForNode, 46 SetNode, 47 AssignableNode, 48 MacroNode, 49 CallNode, 50 InlineIfNode, 51 FilterBlockNode, 52 ImportNode, 53 IncludeNode, 54 ExtendsNode, 55 ); 56 57 58 59 interface INode 60 { 61 void accept(IVisitor); 62 } 63 64 65 mixin template AcceptVisitor() 66 { 67 override void accept(IVisitor visitor) 68 { 69 visitor.visit(this); 70 } 71 } 72 73 74 75 abstract class Node : INode 76 { 77 public Position pos; 78 79 void accept(IVisitor visitor) {} 80 } 81 82 83 class TemplateNode : Node 84 { 85 Nullable!StmtBlockNode stmt; 86 BlockNode[string] blocks; 87 88 this(Position pos, StmtBlockNode stmt, BlockNode[string] blocks) 89 { 90 this.pos = pos; 91 this.stmt = stmt.toNullable; 92 this.blocks = blocks; 93 } 94 95 mixin AcceptVisitor; 96 } 97 98 99 100 class BlockNode : Node 101 { 102 string name; 103 Nullable!Node stmt; 104 105 this(Position pos, string name, Node stmt) 106 { 107 this.pos = pos; 108 this.name = name; 109 this.stmt = stmt.toNullable; 110 } 111 112 mixin AcceptVisitor; 113 } 114 115 116 117 class StmtBlockNode : Node 118 { 119 Node[] children; 120 121 this(Position pos) 122 { 123 this.pos = pos; 124 } 125 126 mixin AcceptVisitor; 127 } 128 129 130 131 class RawNode : Node 132 { 133 string raw; 134 135 this(Position pos, string raw) 136 { 137 this.pos = pos; 138 this.raw = raw; 139 } 140 141 mixin AcceptVisitor; 142 } 143 144 145 146 class ExprNode : Node 147 { 148 Nullable!Node expr; 149 150 this(Position pos, Node expr) 151 { 152 this.pos = pos; 153 this.expr = expr.toNullable; 154 } 155 156 mixin AcceptVisitor; 157 } 158 159 160 class InlineIfNode : Node 161 { 162 Nullable!Node expr, cond, other; 163 164 this(Position pos, Node expr, Node cond, Node other) 165 { 166 this.pos = pos; 167 this.expr = expr.toNullable; 168 this.cond = cond.toNullable; 169 this.other = other.toNullable; 170 } 171 172 mixin AcceptVisitor; 173 } 174 175 176 class BinOpNode : Node 177 { 178 string op; 179 Node lhs, rhs; 180 181 this(Position pos, string op, Node lhs, Node rhs) 182 { 183 this.pos = pos; 184 this.op = op; 185 this.lhs = lhs; 186 this.rhs = rhs; 187 } 188 189 mixin AcceptVisitor; 190 } 191 192 193 class UnaryOpNode : Node 194 { 195 string op; 196 Node expr; 197 198 this(Position pos, string op, Node expr) 199 { 200 this.pos = pos; 201 this.op = op; 202 this.expr = expr; 203 } 204 205 mixin AcceptVisitor; 206 } 207 208 209 class NumNode : Node 210 { 211 enum Type 212 { 213 Integer, 214 Float, 215 } 216 217 union Data 218 { 219 long _integer; 220 double _float; 221 } 222 223 Data data; 224 Type type; 225 226 this(Position pos, long num) 227 { 228 this.pos = pos; 229 data._integer = num; 230 type = Type.Integer; 231 } 232 233 this(Position pos, double num) 234 { 235 this.pos = pos; 236 data._float = num; 237 type = Type.Float; 238 } 239 240 mixin AcceptVisitor; 241 } 242 243 244 class BooleanNode : Node 245 { 246 bool boolean; 247 248 this(Position pos, bool boolean) 249 { 250 this.pos = pos; 251 this.boolean = boolean; 252 } 253 254 mixin AcceptVisitor; 255 } 256 257 258 class NilNode : Node 259 { 260 mixin AcceptVisitor; 261 } 262 263 264 class IdentNode : Node 265 { 266 string name; 267 Node[] subIdents; 268 269 270 this(Position pos, string name, Node[] subIdents) 271 { 272 this.pos = pos; 273 this.name = name; 274 this.subIdents = subIdents; 275 } 276 277 mixin AcceptVisitor; 278 } 279 280 281 class AssignableNode : Node 282 { 283 string name; 284 Node[] subIdents; 285 286 287 this(Position pos, string name, Node[] subIdents) 288 { 289 this.pos = pos; 290 this.name = name; 291 this.subIdents = subIdents; 292 } 293 294 mixin AcceptVisitor; 295 } 296 297 298 class IfNode : Node 299 { 300 Node cond, then, other; 301 302 this(Position pos, Node cond, Node then, Node other) 303 { 304 this.pos = pos; 305 this.cond = cond; 306 this.then = then; 307 this.other = other; 308 } 309 310 mixin AcceptVisitor; 311 } 312 313 314 class ForNode : Node 315 { 316 string[] keys; 317 Nullable!Node iterable; 318 Nullable!Node block; 319 Nullable!Node other; 320 Nullable!Node cond; 321 bool isRecursive; 322 323 this(Position pos, string[] keys, Node iterable, Node block, Node other, Node cond, bool isRecursive) 324 { 325 this.pos = pos; 326 this.keys = keys; 327 this.iterable = iterable.toNullable; 328 this.block = block.toNullable; 329 this.other = other.toNullable; 330 this.cond = cond.toNullable; 331 this.isRecursive = isRecursive; 332 } 333 334 mixin AcceptVisitor; 335 } 336 337 338 class StringNode : Node 339 { 340 string str; 341 342 this(Position pos, string str) 343 { 344 this.pos = pos; 345 this.str = str; 346 } 347 348 mixin AcceptVisitor; 349 } 350 351 352 class ListNode : Node 353 { 354 Node[] list; 355 356 this(Position pos, Node[] list) 357 { 358 this.pos = pos; 359 this.list = list; 360 } 361 362 mixin AcceptVisitor; 363 } 364 365 366 class DictNode : Node 367 { 368 Node[string] dict; 369 370 this(Position pos, Node[string] dict) 371 { 372 this.pos = pos; 373 this.dict = dict; 374 } 375 376 mixin AcceptVisitor; 377 } 378 379 380 class SetNode : Node 381 { 382 Node[] assigns; 383 Node expr; 384 385 this(Position pos, Node[] assigns, Node expr) 386 { 387 this.pos = pos; 388 this.assigns = assigns; 389 this.expr = expr; 390 } 391 392 mixin AcceptVisitor; 393 } 394 395 396 class MacroNode : Node 397 { 398 string name; 399 Arg[] args; 400 Nullable!Node block; 401 bool isReturn; 402 403 this(Position pos, string name, Arg[] args, Node block, bool isReturn) 404 { 405 this.pos = pos; 406 this.name = name; 407 this.args = args; 408 this.block = block.toNullable; 409 this.isReturn = isReturn; 410 } 411 412 mixin AcceptVisitor; 413 } 414 415 416 class CallNode : Node 417 { 418 string macroName; 419 Arg[] formArgs; 420 Nullable!Node factArgs; 421 Nullable!Node block; 422 423 this(Position pos, string macroName, Arg[] formArgs, Node factArgs, Node block) 424 { 425 this.pos = pos; 426 this.macroName = macroName; 427 this.formArgs = formArgs; 428 this.factArgs = factArgs.toNullable; 429 this.block = block.toNullable; 430 } 431 432 mixin AcceptVisitor; 433 } 434 435 436 class FilterBlockNode : Node 437 { 438 string filterName; 439 Nullable!Node args; 440 Nullable!Node block; 441 442 this(Position pos, string filterName, Node args, Node block) 443 { 444 this.pos = pos; 445 this.filterName = filterName; 446 this.args = args.toNullable; 447 this.block = block.toNullable; 448 } 449 450 mixin AcceptVisitor; 451 } 452 453 454 455 class ImportNode : Node 456 { 457 struct Rename 458 { 459 string was, become; 460 } 461 462 string fileName; 463 Rename[] macrosNames; 464 Nullable!TemplateNode tmplBlock; 465 bool withContext; 466 467 this(Position pos, string fileName, Rename[] macrosNames, TemplateNode tmplBlock, bool withContext) 468 { 469 this.pos = pos; 470 this.fileName = fileName; 471 this.macrosNames = macrosNames; 472 this.tmplBlock = tmplBlock.toNullable; 473 this.withContext = withContext; 474 } 475 476 mixin AcceptVisitor; 477 } 478 479 480 481 class IncludeNode : Node 482 { 483 string fileName; 484 Nullable!TemplateNode tmplBlock; 485 bool withContext; 486 487 this(Position pos, string fileName, TemplateNode tmplBlock, bool withContext) 488 { 489 this.pos = pos; 490 this.fileName = fileName; 491 this.tmplBlock = tmplBlock.toNullable; 492 this.withContext = withContext; 493 } 494 495 mixin AcceptVisitor; 496 } 497 498 499 500 class ExtendsNode : Node 501 { 502 string fileName; 503 Nullable!TemplateNode tmplBlock; 504 505 this(Position pos, string fileName, TemplateNode tmplBlock) 506 { 507 this.pos = pos; 508 this.fileName = fileName; 509 this.tmplBlock = tmplBlock.toNullable; 510 } 511 512 mixin AcceptVisitor; 513 } 514 515 516 517 struct Arg 518 { 519 string name; 520 Nullable!Node defaultExpr; 521 522 this(string name, Node def) 523 { 524 this.name = name; 525 this.defaultExpr = def.toNullable; 526 } 527 } 528 529 530 531 auto toNullable(T)(T val) 532 if (is(T == class)) 533 { 534 if (val is null) 535 return Nullable!T.init; 536 else 537 return Nullable!T(val); 538 }