From 114a3e7f20de57f11bfb334cc085e7b04bad1cc1 Mon Sep 17 00:00:00 2001 From: Dave Cunningham Date: Mon, 21 Nov 2016 19:28:35 -0500 Subject: [PATCH] Fix #244 super/self in +: desugaring --- Makefile | 1 + core/BUILD | 2 + core/ast.h | 6 + core/desugarer.cpp | 86 ++++- core/formatter.cpp | 448 +++------------------------ core/pass.cpp | 426 +++++++++++++++++++++++++ core/pass.h | 115 +++++++ setup.py | 1 + test_suite/object.jsonnet | 7 + test_suite/object.jsonnet.fmt.golden | 7 + 10 files changed, 681 insertions(+), 418 deletions(-) create mode 100644 core/pass.cpp create mode 100644 core/pass.h diff --git a/Makefile b/Makefile index c8f7e27ac..27a19915b 100644 --- a/Makefile +++ b/Makefile @@ -47,6 +47,7 @@ LIB_SRC = \ core/lexer.cpp \ core/libjsonnet.cpp \ core/parser.cpp \ + core/pass.cpp \ core/static_analysis.cpp \ core/string_utils.cpp \ core/vm.cpp diff --git a/core/BUILD b/core/BUILD index addbd3938..78325ef27 100644 --- a/core/BUILD +++ b/core/BUILD @@ -8,6 +8,7 @@ cc_library( "lexer.cpp", "libjsonnet.cpp", "parser.cpp", + "pass.cpp", "static_analysis.cpp", "string_utils.cpp", "vm.cpp", @@ -19,6 +20,7 @@ cc_library( "json.h", "lexer.h", "parser.h", + "pass.h", "state.h", "static_analysis.h", "static_error.h", diff --git a/core/ast.h b/core/ast.h index 42124e670..7367f7b32 100644 --- a/core/ast.h +++ b/core/ast.h @@ -750,6 +750,12 @@ class Allocator { allocated.push_back(r); return r; } + + template T *clone(T * ast) { + auto r = new T(*ast); + allocated.push_back(r); + return r; + } /** Returns interned identifiers. * * The location used in the Identifier AST is that of the first one parsed. diff --git a/core/desugarer.cpp b/core/desugarer.cpp index 23532128f..9fb1326f1 100644 --- a/core/desugarer.cpp +++ b/core/desugarer.cpp @@ -20,6 +20,7 @@ limitations under the License. #include "desugarer.h" #include "lexer.h" #include "parser.h" +#include "pass.h" #include "string_utils.h" static const Fodder EF; // Empty fodder. @@ -199,7 +200,11 @@ class Desugarer { } } - void desugarFields(AST *ast, ObjectFields &fields, unsigned obj_level) + // For all occurrences, records the identifier that will replace super[e] + // If self occurs, also map the self identifier to nullptr. + typedef std::vector> SuperVars; + + SuperVars desugarFields(AST *ast, ObjectFields &fields, unsigned obj_level) { // Desugar children for (auto &field : fields) { @@ -283,15 +288,59 @@ class Desugarer { } } + class SubstituteSelfSuper : public CompilerPass { + Desugarer *desugarer; + SuperVars &superVars; + unsigned &counter; + const Identifier *newSelf; + public: + SubstituteSelfSuper(Desugarer *desugarer, SuperVars &super_vars, unsigned &counter) + : CompilerPass(*desugarer->alloc), desugarer(desugarer), superVars(super_vars), + counter(counter), newSelf(nullptr) + { + } + void visitExpr(AST *&expr) + { + if (dynamic_cast(expr)) { + if (newSelf == nullptr) { + newSelf = desugarer->id(U"$outer_self"); + superVars.emplace_back(newSelf, nullptr); + } + expr = alloc.make(expr->location, expr->openFodder, newSelf); + } else if (auto *super_index = dynamic_cast(expr)) { + StringStream ss; + ss << "$outer_super" << (counter++); + const Identifier *super_var = desugarer->id(ss.str()); + AST *index = super_index->index; + // Desugaring of expr should already have occurred. + assert(index != nullptr); + // Re-use super_index since we're replacing it here. + superVars.emplace_back(super_var, super_index); + expr = alloc.make(expr->location, expr->openFodder, super_var); + } + CompilerPass::visitExpr(expr); + } + }; + + SuperVars super_vars; + unsigned counter; + // Remove +: for (auto &field : fields) { if (!field.superSugar) continue; - AST *super_f = make(field.expr1->location, EF, EF, field.expr1, EF, - nullptr); - field.expr2 = make(ast->location, EF, super_f, EF, BOP_PLUS, - field.expr2); + // We have to bind self/super from expr1 outside the class, as we copy the expression + // into the field body. + AST *index = field.expr1; + // Clone it so that we maintain the AST as a tree. + ClonePass(*alloc).expr(index); + // This will remove self/super. + SubstituteSelfSuper(this, super_vars, counter).expr(index); + AST *super_f = make(field.expr1->location, EF, EF, index, EF, nullptr); + field.expr2 = make(ast->location, EF, super_f, EF, BOP_PLUS, field.expr2); field.superSugar = false; } + + return super_vars; } void desugar(AST *&ast_, unsigned obj_level) @@ -305,7 +354,7 @@ class Desugarer { desugar(ast->left, obj_level); desugar(ast->right, obj_level); ast_ = make(ast->location, ast->openFodder, - ast->left, EF, BOP_PLUS, ast->right); + ast->left, EF, BOP_PLUS, ast->right); } else if (auto *ast = dynamic_cast(ast_)) { for (auto &el : ast->elements) @@ -406,11 +455,11 @@ class Desugarer { [[[...out...]]] else local [[[...var...]]] = $l[i_{i}]; - [[[...in...]]];` - if std.type($l) != "array" then - error "In comprehension, can only iterate over array.." + [[[...in...]]]; + if std.type($l) == "array" then + aux_{i}(0, $r) tailstrict else - aux_{i}(0, r) tailstrict; + error "In comprehension, can only iterate over array.."; */ in = make( ast->location, @@ -635,7 +684,7 @@ class Desugarer { ast->fields.push_back(ObjectField::Local(EF, EF, hidden_var, EF, body, EF)); } - desugarFields(ast, ast->fields, obj_level); + SuperVars svs = desugarFields(ast, ast->fields, obj_level); DesugaredObject::Fields new_fields; ASTs new_asserts; @@ -650,6 +699,19 @@ class Desugarer { } } ast_ = make(ast->location, new_asserts, new_fields); + if (svs.size() > 0) { + Local::Binds binds; + for (const auto &pair : svs) { + if (pair.second == nullptr) { + // Self binding + binds.push_back(bind(pair.first, make(E, EF))); + } else { + // Super binding + binds.push_back(bind(pair.first, pair.second)); + } + } + ast_ = make(ast->location, EF, binds, ast_); + } } else if (auto *ast = dynamic_cast(ast_)) { // Hidden variable to allow outer/top binding. @@ -659,7 +721,7 @@ class Desugarer { ast->fields.push_back(ObjectField::Local(EF, EF, hidden_var, EF, body, EF)); } - desugarFields(ast, ast->fields, obj_level); + SuperVars svs = desugarFields(ast, ast->fields, obj_level); for (ComprehensionSpec &spec : ast->specs) desugar(spec.expr, obj_level); diff --git a/core/formatter.cpp b/core/formatter.cpp index 25e12d72c..5d65a1b6b 100644 --- a/core/formatter.cpp +++ b/core/formatter.cpp @@ -16,8 +16,9 @@ limitations under the License. #include -#include "lexer.h" #include "formatter.h" +#include "lexer.h" +#include "pass.h" #include "string_utils.h" #include "unicode.h" @@ -595,385 +596,20 @@ static void fodder_move_front(Fodder &a, Fodder &b) /** A generic Pass that does nothing but can be extended to easily define real passes. */ -class Pass { - public: - +class FmtPass : public CompilerPass { protected: - Allocator &alloc; FmtOpts opts; public: - Pass(Allocator &alloc, const FmtOpts &opts) - : alloc(alloc), opts(opts) { } - - virtual void fodderElement(FodderElement &f) - { - (void) f; - } - - virtual void fodder(Fodder &fodder) - { - for (auto &f : fodder) - fodderElement(f); - } - - virtual void specs(std::vector &specs) - { - for (auto &spec : specs) { - fodder(spec.openFodder); - switch (spec.kind) { - case ComprehensionSpec::FOR: - fodder(spec.varFodder); - fodder(spec.inFodder); - expr(spec.expr); - break; - case ComprehensionSpec::IF: - expr(spec.expr); - break; - } - } - } - - virtual void params(Fodder &fodder_l, ArgParams ¶ms, Fodder &fodder_r) - { - fodder(fodder_l); - for (auto ¶m : params) { - fodder(param.idFodder); - if (param.expr) { - fodder(param.eqFodder); - expr(param.expr); - } - fodder(param.commaFodder); - } - fodder(fodder_r); - } - - virtual void fieldParams(ObjectField &field) - { - if (field.methodSugar) { - params(field.fodderL, field.params, field.fodderR); - } - } - - virtual void fields(ObjectFields &fields) - { - for (auto &field : fields) { - - switch (field.kind) { - case ObjectField::LOCAL: { - fodder(field.fodder1); - fodder(field.fodder2); - fieldParams(field); - fodder(field.opFodder); - expr(field.expr2); - } break; - - case ObjectField::FIELD_ID: - case ObjectField::FIELD_STR: - case ObjectField::FIELD_EXPR: { - if (field.kind == ObjectField::FIELD_ID) { - fodder(field.fodder1); - - } else if (field.kind == ObjectField::FIELD_STR) { - expr(field.expr1); - - } else if (field.kind == ObjectField::FIELD_EXPR) { - fodder(field.fodder1); - expr(field.expr1); - fodder(field.fodder2); - } - fieldParams(field); - fodder(field.opFodder); - expr(field.expr2); - - } break; - - case ObjectField::ASSERT: { - fodder(field.fodder1); - expr(field.expr2); - if (field.expr3 != nullptr) { - fodder(field.opFodder); - expr(field.expr3); - } - } break; - } - - fodder(field.commaFodder); - } - } - - virtual void expr(AST *&ast_) - { - fodder(ast_->openFodder); - visitExpr(ast_); - } - - virtual void visit(Apply *ast) - { - expr(ast->target); - fodder(ast->fodderL); - for (auto &arg : ast->args) { - expr(arg.expr); - fodder(arg.commaFodder); - } - fodder(ast->fodderR); - if (ast->tailstrict) { - fodder(ast->tailstrictFodder); - } - } - - virtual void visit(ApplyBrace *ast) - { - expr(ast->left); - expr(ast->right); - } - - virtual void visit(Array *ast) - { - for (auto &element : ast->elements) { - expr(element.expr); - fodder(element.commaFodder); - } - fodder(ast->closeFodder); - } - - virtual void visit(ArrayComprehension *ast) - { - expr(ast->body); - fodder(ast->commaFodder); - specs(ast->specs); - fodder(ast->closeFodder); - } - - virtual void visit(Assert *ast) - { - expr(ast->cond); - if (ast->message != nullptr) { - fodder(ast->colonFodder); - expr(ast->message); - } - fodder(ast->semicolonFodder); - expr(ast->rest); - } - - virtual void visit(Binary *ast) - { - expr(ast->left); - fodder(ast->opFodder); - expr(ast->right); - } - - virtual void visit(BuiltinFunction *) { } - - virtual void visit(Conditional *ast) - { - expr(ast->cond); - fodder(ast->thenFodder); - if (ast->branchFalse != nullptr) { - expr(ast->branchTrue); - fodder(ast->elseFodder); - expr(ast->branchFalse); - } else { - expr(ast->branchTrue); - } - } - - virtual void visit(Dollar *) { } - - virtual void visit(Error *ast) - { - expr(ast->expr); - } - - virtual void visit(Function *ast) - { - params(ast->parenLeftFodder, ast->params, ast->parenRightFodder); - expr(ast->body); - } - - virtual void visit(Import *ast) - { - visit(ast->file); - } - - virtual void visit(Importstr *ast) - { - visit(ast->file); - } - - virtual void visit(Index *ast) - { - expr(ast->target); - if (ast->id != nullptr) { - } else { - if (ast->isSlice) { - if (ast->index != nullptr) - expr(ast->index); - if (ast->end != nullptr) - expr(ast->end); - if (ast->step != nullptr) - expr(ast->step); - } else { - expr(ast->index); - } - } - } - - virtual void visit(Local *ast) - { - assert(ast->binds.size() > 0); - for (auto &bind : ast->binds) { - fodder(bind.varFodder); - if (bind.functionSugar) { - params(bind.parenLeftFodder, bind.params, bind.parenRightFodder); - } - fodder(bind.opFodder); - expr(bind.body); - fodder(bind.closeFodder); - } - expr(ast->body); - } - - virtual void visit(LiteralBoolean *) { } - - virtual void visit(LiteralNumber *) { } - - virtual void visit(LiteralString *) { } - - virtual void visit(LiteralNull *) { } - - virtual void visit(Object *ast) - { - fields(ast->fields); - fodder(ast->closeFodder); - } - - virtual void visit(DesugaredObject *ast) - { - for (AST *assert : ast->asserts) { - expr(assert); - } - for (auto &field : ast->fields) { - expr(field.name); - expr(field.body); - } - } - - virtual void visit(ObjectComprehension *ast) - { - fields(ast->fields); - specs(ast->specs); - fodder(ast->closeFodder); - } - - virtual void visit(ObjectComprehensionSimple *ast) - { - expr(ast->field); - expr(ast->value); - expr(ast->array); - } - - virtual void visit(Parens *ast) - { - expr(ast->expr); - fodder(ast->closeFodder); - } - - virtual void visit(Self *) { } - - virtual void visit(SuperIndex *ast) - { - if (ast->id != nullptr) { - } else { - expr(ast->index); - } - } - - virtual void visit(Unary *ast) - { - expr(ast->expr); - } - - virtual void visit(Var *) { } - - virtual void visitExpr(AST *&ast_) - { - if (auto *ast = dynamic_cast(ast_)) { - visit(ast); - } else if (auto *ast = dynamic_cast(ast_)) { - visit(ast); - } else if (auto *ast = dynamic_cast(ast_)) { - visit(ast); - } else if (auto *ast = dynamic_cast(ast_)) { - visit(ast); - } else if (auto *ast = dynamic_cast(ast_)) { - visit(ast); - } else if (auto *ast = dynamic_cast(ast_)) { - visit(ast); - } else if (auto *ast = dynamic_cast(ast_)) { - visit(ast); - } else if (auto *ast = dynamic_cast(ast_)) { - visit(ast); - } else if (auto *ast = dynamic_cast(ast_)) { - visit(ast); - } else if (auto *ast = dynamic_cast(ast_)) { - visit(ast); - } else if (auto *ast = dynamic_cast(ast_)) { - visit(ast); - } else if (auto *ast = dynamic_cast(ast_)) { - visit(ast); - } else if (auto *ast = dynamic_cast(ast_)) { - visit(ast); - } else if (auto *ast = dynamic_cast(ast_)) { - visit(ast); - } else if (auto *ast = dynamic_cast(ast_)) { - visit(ast); - } else if (auto *ast = dynamic_cast(ast_)) { - visit(ast); - } else if (auto *ast = dynamic_cast(ast_)) { - visit(ast); - } else if (auto *ast = dynamic_cast(ast_)) { - visit(ast); - } else if (auto *ast = dynamic_cast(ast_)) { - visit(ast); - } else if (auto *ast = dynamic_cast(ast_)) { - visit(ast); - } else if (auto *ast = dynamic_cast(ast_)) { - visit(ast); - } else if (auto *ast = dynamic_cast(ast_)) { - visit(ast); - } else if (auto *ast = dynamic_cast(ast_)) { - visit(ast); - } else if (auto *ast = dynamic_cast(ast_)) { - visit(ast); - } else if (auto *ast = dynamic_cast(ast_)) { - visit(ast); - } else if (auto *ast = dynamic_cast(ast_)) { - visit(ast); - } else if (auto *ast = dynamic_cast(ast_)) { - visit(ast); - } else if (auto *ast = dynamic_cast(ast_)) { - visit(ast); - - } else { - std::cerr << "INTERNAL ERROR: Unknown AST: " << ast_ << std::endl; - std::abort(); - - } - } - - virtual void file(AST *&body, Fodder &final_fodder) - { - expr(body); - fodder(final_fodder); - } + FmtPass(Allocator &alloc, const FmtOpts &opts) + : CompilerPass(alloc), opts(opts) { } }; -class EnforceStringStyle : public Pass { - using Pass::visit; +class EnforceStringStyle : public FmtPass { + using FmtPass::visit; public: - EnforceStringStyle(Allocator &alloc, const FmtOpts &opts) : Pass(alloc, opts) { } + EnforceStringStyle(Allocator &alloc, const FmtOpts &opts) : FmtPass(alloc, opts) { } void visit(LiteralString *lit) { if (lit->tokenKind == LiteralString::BLOCK) return; @@ -996,11 +632,11 @@ class EnforceStringStyle : public Pass { } }; -class EnforceCommentStyle : public Pass { +class EnforceCommentStyle : public FmtPass { public: bool firstFodder; EnforceCommentStyle(Allocator &alloc, const FmtOpts &opts) - : Pass(alloc, opts), firstFodder(true) + : FmtPass(alloc, opts), firstFodder(true) { } /** Change the comment to match the given style, but don't break she-bang. * @@ -1035,9 +671,9 @@ class EnforceCommentStyle : public Pass { } }; -class EnforceMaximumBlankLines : public Pass { +class EnforceMaximumBlankLines : public FmtPass { public: - EnforceMaximumBlankLines(Allocator &alloc, const FmtOpts &opts) : Pass(alloc, opts) { } + EnforceMaximumBlankLines(Allocator &alloc, const FmtOpts &opts) : FmtPass(alloc, opts) { } void fodderElement(FodderElement &f) { if (f.kind != FodderElement::INTERSTITIAL) @@ -1045,9 +681,9 @@ class EnforceMaximumBlankLines : public Pass { } }; -class StripComments : public Pass { +class StripComments : public FmtPass { public: - StripComments(Allocator &alloc, const FmtOpts &opts) : Pass(alloc, opts) { } + StripComments(Allocator &alloc, const FmtOpts &opts) : FmtPass(alloc, opts) { } void fodder(Fodder &fodder) { Fodder copy = fodder; @@ -1059,15 +695,15 @@ class StripComments : public Pass { } }; -class StripEverything : public Pass { +class StripEverything : public FmtPass { public: - StripEverything(Allocator &alloc, const FmtOpts &opts) : Pass(alloc, opts) { } + StripEverything(Allocator &alloc, const FmtOpts &opts) : FmtPass(alloc, opts) { } void fodder(Fodder &fodder) { fodder.clear(); } }; -class StripAllButComments : public Pass { +class StripAllButComments : public FmtPass { public: - StripAllButComments(Allocator &alloc, const FmtOpts &opts) : Pass(alloc, opts) { } + StripAllButComments(Allocator &alloc, const FmtOpts &opts) : FmtPass(alloc, opts) { } Fodder comments; void fodder(Fodder &fodder) { @@ -1119,10 +755,10 @@ bool contains_newline(const Fodder &fodder) } /* Commas should appear at the end of an object/array only if the closing token is on a new line. */ -class FixTrailingCommas : public Pass { - using Pass::visit; +class FixTrailingCommas : public FmtPass { + using FmtPass::visit; public: - FixTrailingCommas(Allocator &alloc, const FmtOpts &opts) : Pass(alloc, opts) { } + FixTrailingCommas(Allocator &alloc, const FmtOpts &opts) : FmtPass(alloc, opts) { } Fodder comments; // Generalized fix that works across a range of ASTs. @@ -1164,13 +800,13 @@ class FixTrailingCommas : public Pass { } fix_comma(expr->elements.back().commaFodder, expr->trailingComma, expr->closeFodder); - Pass::visit(expr); + FmtPass::visit(expr); } void visit(ArrayComprehension *expr) { remove_comma(expr->commaFodder, expr->trailingComma, expr->specs[0].openFodder); - Pass::visit(expr); + FmtPass::visit(expr); } void visit(Object *expr) @@ -1181,23 +817,23 @@ class FixTrailingCommas : public Pass { } fix_comma(expr->fields.back().commaFodder, expr->trailingComma, expr->closeFodder); - Pass::visit(expr); + FmtPass::visit(expr); } void visit(ObjectComprehension *expr) { remove_comma(expr->fields.back().commaFodder, expr->trailingComma, expr->closeFodder); - Pass::visit(expr); + FmtPass::visit(expr); } }; /* Remove nested parens. */ -class FixParens : public Pass { - using Pass::visit; +class FixParens : public FmtPass { + using FmtPass::visit; public: - FixParens(Allocator &alloc, const FmtOpts &opts) : Pass(alloc, opts) { } + FixParens(Allocator &alloc, const FmtOpts &opts) : FmtPass(alloc, opts) { } void visit(Parens *expr) { if (auto *body = dynamic_cast(expr->expr)) { @@ -1206,17 +842,17 @@ class FixParens : public Pass { fodder_move_front(open_fodder(body->expr), body->openFodder); fodder_move_front(expr->closeFodder, body->closeFodder); } - Pass::visit(expr); + FmtPass::visit(expr); } }; /* Ensure ApplyBrace syntax sugar is used in the case of A + { }. */ -class FixPlusObject : public Pass { - using Pass::visit; +class FixPlusObject : public FmtPass { + using FmtPass::visit; public: - FixPlusObject(Allocator &alloc, const FmtOpts &opts) : Pass(alloc, opts) { } + FixPlusObject(Allocator &alloc, const FmtOpts &opts) : FmtPass(alloc, opts) { } void visitExpr(AST *&expr) { if (auto *bin_op = dynamic_cast(expr)) { @@ -1232,17 +868,17 @@ class FixPlusObject : public Pass { } } } - Pass::visitExpr(expr); + FmtPass::visitExpr(expr); } }; /* Remove final colon in slices. */ -class NoRedundantSliceColon : public Pass { - using Pass::visit; +class NoRedundantSliceColon : public FmtPass { + using FmtPass::visit; public: - NoRedundantSliceColon(Allocator &alloc, const FmtOpts &opts) : Pass(alloc, opts) { } + NoRedundantSliceColon(Allocator &alloc, const FmtOpts &opts) : FmtPass(alloc, opts) { } void visit(Index *expr) { @@ -1253,15 +889,15 @@ class NoRedundantSliceColon : public Pass { } } } - Pass::visit(expr); + FmtPass::visit(expr); } }; /* Ensure syntax sugar is used where possible. */ -class PrettyFieldNames : public Pass { - using Pass::visit; +class PrettyFieldNames : public FmtPass { + using FmtPass::visit; public: - PrettyFieldNames(Allocator &alloc, const FmtOpts &opts) : Pass(alloc, opts) { } + PrettyFieldNames(Allocator &alloc, const FmtOpts &opts) : FmtPass(alloc, opts) { } bool isIdentifier(const String &str) { bool first = true; @@ -1293,7 +929,7 @@ class PrettyFieldNames : public Pass { } } } - Pass::visit(expr); + FmtPass::visit(expr); } void visit(Object *expr) @@ -1323,7 +959,7 @@ class PrettyFieldNames : public Pass { } } } - Pass::visit(expr); + FmtPass::visit(expr); } }; diff --git a/core/pass.cpp b/core/pass.cpp new file mode 100644 index 000000000..451e6c754 --- /dev/null +++ b/core/pass.cpp @@ -0,0 +1,426 @@ +/* +Copyright 2015 Google Inc. All rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +#include "pass.h" + +void CompilerPass::fodder(Fodder &fodder) +{ + for (auto &f : fodder) + fodderElement(f); +} + +void CompilerPass::specs(std::vector &specs) +{ + for (auto &spec : specs) { + fodder(spec.openFodder); + switch (spec.kind) { + case ComprehensionSpec::FOR: + fodder(spec.varFodder); + fodder(spec.inFodder); + expr(spec.expr); + break; + case ComprehensionSpec::IF: + expr(spec.expr); + break; + } + } +} + +void CompilerPass::params(Fodder &fodder_l, ArgParams ¶ms, Fodder &fodder_r) +{ + fodder(fodder_l); + for (auto ¶m : params) { + fodder(param.idFodder); + if (param.expr) { + fodder(param.eqFodder); + expr(param.expr); + } + fodder(param.commaFodder); + } + fodder(fodder_r); +} + +void CompilerPass::fieldParams(ObjectField &field) +{ + if (field.methodSugar) { + params(field.fodderL, field.params, field.fodderR); + } +} + +void CompilerPass::fields(ObjectFields &fields) +{ + for (auto &field : fields) { + + switch (field.kind) { + case ObjectField::LOCAL: { + fodder(field.fodder1); + fodder(field.fodder2); + fieldParams(field); + fodder(field.opFodder); + expr(field.expr2); + } break; + + case ObjectField::FIELD_ID: + case ObjectField::FIELD_STR: + case ObjectField::FIELD_EXPR: { + if (field.kind == ObjectField::FIELD_ID) { + fodder(field.fodder1); + + } else if (field.kind == ObjectField::FIELD_STR) { + expr(field.expr1); + + } else if (field.kind == ObjectField::FIELD_EXPR) { + fodder(field.fodder1); + expr(field.expr1); + fodder(field.fodder2); + } + fieldParams(field); + fodder(field.opFodder); + expr(field.expr2); + + } break; + + case ObjectField::ASSERT: { + fodder(field.fodder1); + expr(field.expr2); + if (field.expr3 != nullptr) { + fodder(field.opFodder); + expr(field.expr3); + } + } break; + } + + fodder(field.commaFodder); + } +} + +void CompilerPass::expr(AST *&ast_) +{ + fodder(ast_->openFodder); + visitExpr(ast_); +} + +void CompilerPass::visit(Apply *ast) +{ + expr(ast->target); + fodder(ast->fodderL); + for (auto &arg : ast->args) { + expr(arg.expr); + fodder(arg.commaFodder); + } + fodder(ast->fodderR); + if (ast->tailstrict) { + fodder(ast->tailstrictFodder); + } +} + +void CompilerPass::visit(ApplyBrace *ast) +{ + expr(ast->left); + expr(ast->right); +} + +void CompilerPass::visit(Array *ast) +{ + for (auto &element : ast->elements) { + expr(element.expr); + fodder(element.commaFodder); + } + fodder(ast->closeFodder); +} + +void CompilerPass::visit(ArrayComprehension *ast) +{ + expr(ast->body); + fodder(ast->commaFodder); + specs(ast->specs); + fodder(ast->closeFodder); +} + +void CompilerPass::visit(Assert *ast) +{ + expr(ast->cond); + if (ast->message != nullptr) { + fodder(ast->colonFodder); + expr(ast->message); + } + fodder(ast->semicolonFodder); + expr(ast->rest); +} + +void CompilerPass::visit(Binary *ast) +{ + expr(ast->left); + fodder(ast->opFodder); + expr(ast->right); +} + +void CompilerPass::visit(Conditional *ast) +{ + expr(ast->cond); + fodder(ast->thenFodder); + if (ast->branchFalse != nullptr) { + expr(ast->branchTrue); + fodder(ast->elseFodder); + expr(ast->branchFalse); + } else { + expr(ast->branchTrue); + } +} + +void CompilerPass::visit(Error *ast) +{ + expr(ast->expr); +} + +void CompilerPass::visit(Function *ast) +{ + params(ast->parenLeftFodder, ast->params, ast->parenRightFodder); + expr(ast->body); +} + +void CompilerPass::visit(Import *ast) +{ + visit(ast->file); +} + +void CompilerPass::visit(Importstr *ast) +{ + visit(ast->file); +} + +void CompilerPass::visit(Index *ast) +{ + expr(ast->target); + if (ast->id != nullptr) { + } else { + if (ast->isSlice) { + if (ast->index != nullptr) + expr(ast->index); + if (ast->end != nullptr) + expr(ast->end); + if (ast->step != nullptr) + expr(ast->step); + } else { + expr(ast->index); + } + } +} + +void CompilerPass::visit(Local *ast) +{ + assert(ast->binds.size() > 0); + for (auto &bind : ast->binds) { + fodder(bind.varFodder); + if (bind.functionSugar) { + params(bind.parenLeftFodder, bind.params, bind.parenRightFodder); + } + fodder(bind.opFodder); + expr(bind.body); + fodder(bind.closeFodder); + } + expr(ast->body); +} + +void CompilerPass::visit(Object *ast) +{ + fields(ast->fields); + fodder(ast->closeFodder); +} + +void CompilerPass::visit(DesugaredObject *ast) +{ + for (AST *assert : ast->asserts) { + expr(assert); + } + for (auto &field : ast->fields) { + expr(field.name); + expr(field.body); + } +} + +void CompilerPass::visit(ObjectComprehension *ast) +{ + fields(ast->fields); + specs(ast->specs); + fodder(ast->closeFodder); +} + +void CompilerPass::visit(ObjectComprehensionSimple *ast) +{ + expr(ast->field); + expr(ast->value); + expr(ast->array); +} + +void CompilerPass::visit(Parens *ast) +{ + expr(ast->expr); + fodder(ast->closeFodder); +} + +void CompilerPass::visit(SuperIndex *ast) +{ + if (ast->id != nullptr) { + } else { + expr(ast->index); + } +} + +void CompilerPass::visit(Unary *ast) +{ + expr(ast->expr); +} + +void CompilerPass::visitExpr(AST *&ast_) +{ + if (auto *ast = dynamic_cast(ast_)) { + visit(ast); + } else if (auto *ast = dynamic_cast(ast_)) { + visit(ast); + } else if (auto *ast = dynamic_cast(ast_)) { + visit(ast); + } else if (auto *ast = dynamic_cast(ast_)) { + visit(ast); + } else if (auto *ast = dynamic_cast(ast_)) { + visit(ast); + } else if (auto *ast = dynamic_cast(ast_)) { + visit(ast); + } else if (auto *ast = dynamic_cast(ast_)) { + visit(ast); + } else if (auto *ast = dynamic_cast(ast_)) { + visit(ast); + } else if (auto *ast = dynamic_cast(ast_)) { + visit(ast); + } else if (auto *ast = dynamic_cast(ast_)) { + visit(ast); + } else if (auto *ast = dynamic_cast(ast_)) { + visit(ast); + } else if (auto *ast = dynamic_cast(ast_)) { + visit(ast); + } else if (auto *ast = dynamic_cast(ast_)) { + visit(ast); + } else if (auto *ast = dynamic_cast(ast_)) { + visit(ast); + } else if (auto *ast = dynamic_cast(ast_)) { + visit(ast); + } else if (auto *ast = dynamic_cast(ast_)) { + visit(ast); + } else if (auto *ast = dynamic_cast(ast_)) { + visit(ast); + } else if (auto *ast = dynamic_cast(ast_)) { + visit(ast); + } else if (auto *ast = dynamic_cast(ast_)) { + visit(ast); + } else if (auto *ast = dynamic_cast(ast_)) { + visit(ast); + } else if (auto *ast = dynamic_cast(ast_)) { + visit(ast); + } else if (auto *ast = dynamic_cast(ast_)) { + visit(ast); + } else if (auto *ast = dynamic_cast(ast_)) { + visit(ast); + } else if (auto *ast = dynamic_cast(ast_)) { + visit(ast); + } else if (auto *ast = dynamic_cast(ast_)) { + visit(ast); + } else if (auto *ast = dynamic_cast(ast_)) { + visit(ast); + } else if (auto *ast = dynamic_cast(ast_)) { + visit(ast); + } else if (auto *ast = dynamic_cast(ast_)) { + visit(ast); + + } else { + std::cerr << "INTERNAL ERROR: Unknown AST: " << ast_ << std::endl; + std::abort(); + + } +} + +void CompilerPass::file(AST *&body, Fodder &final_fodder) +{ + expr(body); + fodder(final_fodder); +} + +void ClonePass::expr(AST *&ast_) +{ + if (auto *ast = dynamic_cast(ast_)) { + ast_ = alloc.clone(ast); + } else if (auto *ast = dynamic_cast(ast_)) { + ast_ = alloc.clone(ast); + } else if (auto *ast = dynamic_cast(ast_)) { + ast_ = alloc.clone(ast); + } else if (auto *ast = dynamic_cast(ast_)) { + ast_ = alloc.clone(ast); + } else if (auto *ast = dynamic_cast(ast_)) { + ast_ = alloc.clone(ast); + } else if (auto *ast = dynamic_cast(ast_)) { + ast_ = alloc.clone(ast); + } else if (auto *ast = dynamic_cast(ast_)) { + ast_ = alloc.clone(ast); + } else if (auto *ast = dynamic_cast(ast_)) { + ast_ = alloc.clone(ast); + } else if (auto *ast = dynamic_cast(ast_)) { + ast_ = alloc.clone(ast); + } else if (auto *ast = dynamic_cast(ast_)) { + ast_ = alloc.clone(ast); + } else if (auto *ast = dynamic_cast(ast_)) { + ast_ = alloc.clone(ast); + } else if (auto *ast = dynamic_cast(ast_)) { + ast_ = alloc.clone(ast); + } else if (auto *ast = dynamic_cast(ast_)) { + ast_ = alloc.clone(ast); + } else if (auto *ast = dynamic_cast(ast_)) { + ast_ = alloc.clone(ast); + } else if (auto *ast = dynamic_cast(ast_)) { + ast_ = alloc.clone(ast); + } else if (auto *ast = dynamic_cast(ast_)) { + ast_ = alloc.clone(ast); + } else if (auto *ast = dynamic_cast(ast_)) { + ast_ = alloc.clone(ast); + } else if (auto *ast = dynamic_cast(ast_)) { + ast_ = alloc.clone(ast); + } else if (auto *ast = dynamic_cast(ast_)) { + ast_ = alloc.clone(ast); + } else if (auto *ast = dynamic_cast(ast_)) { + ast_ = alloc.clone(ast); + } else if (auto *ast = dynamic_cast(ast_)) { + ast_ = alloc.clone(ast); + } else if (auto *ast = dynamic_cast(ast_)) { + ast_ = alloc.clone(ast); + } else if (auto *ast = dynamic_cast(ast_)) { + ast_ = alloc.clone(ast); + } else if (auto *ast = dynamic_cast(ast_)) { + ast_ = alloc.clone(ast); + } else if (auto *ast = dynamic_cast(ast_)) { + ast_ = alloc.clone(ast); + } else if (auto *ast = dynamic_cast(ast_)) { + ast_ = alloc.clone(ast); + } else if (auto *ast = dynamic_cast(ast_)) { + ast_ = alloc.clone(ast); + } else if (auto *ast = dynamic_cast(ast_)) { + ast_ = alloc.clone(ast); + + } else { + std::cerr << "INTERNAL ERROR: Unknown AST: " << ast_ << std::endl; + std::abort(); + + } + + CompilerPass::expr(ast_); +} diff --git a/core/pass.h b/core/pass.h new file mode 100644 index 000000000..7b461f3cb --- /dev/null +++ b/core/pass.h @@ -0,0 +1,115 @@ +/* +Copyright 2015 Google Inc. All rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +#ifndef JSONNET_PASS_H +#define JSONNET_PASS_H + +#include "ast.h" + +/** A generic Pass that does nothing but can be extended to easily define real passes. + */ +class CompilerPass { + public: + + protected: + Allocator &alloc; + + public: + CompilerPass(Allocator &alloc) : alloc(alloc) { } + + virtual void fodderElement(FodderElement &) { } + + virtual void fodder(Fodder &fodder); + + virtual void specs(std::vector &specs); + + virtual void params(Fodder &fodder_l, ArgParams ¶ms, Fodder &fodder_r); + + virtual void fieldParams(ObjectField &field); + + virtual void fields(ObjectFields &fields); + + virtual void expr(AST *&ast_); + + virtual void visit(Apply *ast); + + virtual void visit(ApplyBrace *ast); + + virtual void visit(Array *ast); + + virtual void visit(ArrayComprehension *ast); + + virtual void visit(Assert *ast); + + virtual void visit(Binary *ast); + + virtual void visit(BuiltinFunction *) { } + + virtual void visit(Conditional *ast); + + virtual void visit(Dollar *) { } + + virtual void visit(Error *ast); + + virtual void visit(Function *ast); + + virtual void visit(Import *ast); + + virtual void visit(Importstr *ast); + + virtual void visit(Index *ast); + + virtual void visit(Local *ast); + + virtual void visit(LiteralBoolean *) { } + + virtual void visit(LiteralNumber *) { } + + virtual void visit(LiteralString *) { } + + virtual void visit(LiteralNull *) { } + + virtual void visit(Object *ast); + + virtual void visit(DesugaredObject *ast); + + virtual void visit(ObjectComprehension *ast); + + virtual void visit(ObjectComprehensionSimple *ast); + + virtual void visit(Parens *ast); + + virtual void visit(Self *) { } + + virtual void visit(SuperIndex *ast); + + virtual void visit(Unary *ast); + + virtual void visit(Var *) { } + + virtual void visitExpr(AST *&ast_); + + virtual void file(AST *&body, Fodder &final_fodder); +}; + + +/** A pass that clones the AST it is given. */ +class ClonePass : public CompilerPass { + public: + ClonePass(Allocator &alloc) : CompilerPass(alloc) { } + virtual void expr(AST *&ast); +}; +#endif diff --git a/setup.py b/setup.py index 37047e0ee..72f0131c2 100644 --- a/setup.py +++ b/setup.py @@ -25,6 +25,7 @@ 'core/libjsonnet.o', 'core/lexer.o', 'core/parser.o', + 'core/pass.o', 'core/static_analysis.o', 'core/string_utils.o', 'core/vm.o' diff --git a/test_suite/object.jsonnet b/test_suite/object.jsonnet index ccec5f06c..9ca0fa793 100644 --- a/test_suite/object.jsonnet +++ b/test_suite/object.jsonnet @@ -67,4 +67,11 @@ local obj = { }; std.assertEqual(obj, { ["f" + x + y + z]: { x: x, y: y, z: z } for x in [1, 2, 3] for y in [1, 4, 6] if x + 2 < y for z in [true, false] }) && + +std.assertEqual({ f: { foo: 7, bar: 1 } { [self.name] +: 3, name:: "foo"}, name:: "bar" }, + { f: { foo: 7, bar: 4 } }) && + +std.assertEqual({ name:: "supername"} { name:: "selfname", f: { wrongname: 7, supername: 1, name:: "wrongname" } { [super.name] +: 3}, }, + { f: { wrongname: 7, supername: 4 } }) && + true diff --git a/test_suite/object.jsonnet.fmt.golden b/test_suite/object.jsonnet.fmt.golden index 8220305ba..f75a9c1d1 100644 --- a/test_suite/object.jsonnet.fmt.golden +++ b/test_suite/object.jsonnet.fmt.golden @@ -67,4 +67,11 @@ local obj = { }; std.assertEqual(obj, { ["f" + x + y + z]: { x: x, y: y, z: z } for x in [1, 2, 3] for y in [1, 4, 6] if x + 2 < y for z in [true, false] }) && + +std.assertEqual({ f: { foo: 7, bar: 1 } { [self.name]+: 3, name:: "foo" }, name:: "bar" }, + { f: { foo: 7, bar: 4 } }) && + +std.assertEqual({ name:: "supername" } { name:: "selfname", f: { wrongname: 7, supername: 1, name:: "wrongname" } { [super.name]+: 3 } }, + { f: { wrongname: 7, supername: 4 } }) && + true