From 2ded735360901f60f5ff0615d36fa0cd8c628ec4 Mon Sep 17 00:00:00 2001 From: Jean Privat Date: Mon, 12 Aug 2013 14:44:20 -0400 Subject: [PATCH] new modules astbuilder, astvalidation, astprinter, and transform Signed-off-by: Jean Privat --- src/astbuilder.nit | 309 +++++++++++++++++++++++++++++++++++++++++++++++++ src/astprinter.nit | 166 ++++++++++++++++++++++++++ src/astvalidation.nit | 87 ++++++++++++++ src/nit.nit | 1 + src/nitg.nit | 1 + src/transform.nit | 308 ++++++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 872 insertions(+) create mode 100644 src/astbuilder.nit create mode 100644 src/astprinter.nit create mode 100644 src/astvalidation.nit create mode 100644 src/transform.nit diff --git a/src/astbuilder.nit b/src/astbuilder.nit new file mode 100644 index 0000000..e6182a0 --- /dev/null +++ b/src/astbuilder.nit @@ -0,0 +1,309 @@ +# This file is part of NIT ( http://www.nitlanguage.org ). +# +# 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. + +# Instantiation and transformation of semantic nodes in the AST of expressions and statements +module astbuilder + +intrude import typing +intrude import literal +intrude import parser +intrude import scope + +# General factory to build semantic nodes in the AST of expressions +class ASTBuilder + # The module used as reference for the building + # It is used to gather types and other stufs + var mmodule: MModule + + # The anchor used for some mechanism relying on types + var anchor: nullable MClassType + + # Make a new Int literal + fun make_int(value: Int): AIntExpr + do + return new AIntExpr.make(value, mmodule.get_primitive_class("Int").mclass_type) + end + + # Make a new instatiation + fun make_new(mtype: MClassType, constructor: MMethod, args: nullable Array[AExpr]): ANewExpr + do + return new ANewExpr.make(mtype, constructor, args) + end + + # Make a new message send + fun make_call(recv: AExpr, mmethod: MMethod, args: nullable Array[AExpr]): ACallExpr + do + var mtype = mmethod.intro.msignature.return_mtype + if mtype != null then mtype = mtype.resolve_for(recv.mtype.as(not null), anchor, mmodule, true) + return new ACallExpr.make(recv, mmethod, args, mtype) + end + + # Make a new, empty, sequence of statements + fun make_block: ABlockExpr + do + return new ABlockExpr.make + end + + # Make a new variable read + fun make_var_read(variable: Variable): AVarExpr + do + return new AVarExpr.make(variable) + end + + # Make a new variable assignment + fun make_var_assign(variable: Variable, value: AExpr): AVarAssignExpr + do + return new AVarAssignExpr.make(variable, value) + end + + # Make a new attribute read + fun make_attr_read(recv: AExpr, attribute: MAttribute): AAttrExpr + do + var mtype = attribute.intro.static_mtype.resolve_for(recv.mtype.as(not null), anchor, mmodule, true) + return new AAttrExpr.make(recv, attribute, mtype) + end + + # Make a new attribute assignment + fun make_attr_assign(recv: AExpr, attribute: MAttribute, value: AExpr): AAttrAssignExpr + do + return new AAttrAssignExpr.make(recv, attribute, value) + end + + # Make a new escapable block + fun make_do: ADoExpr + do + return new ADoExpr.make + end + + # Make a new condinionnal + # `mtype` is the return type of the whole if, in case of a ternary operator. + fun make_if(condition: AExpr, mtype: nullable MType): AIfExpr + do + return new AIfExpr.make(condition, mtype) + end +end + +redef class AExpr + # Return a new variable read that contains the value of the expression + # This method take care efficiently of creating and initalising an anonymous local variable + # + # Note: since this method do side-effects (AST replacement), there could be unexpected effects when used as + # argument of other methods related to AST transformations. + fun make_var_read: AVarExpr + do + var variable = self.variable_cache + if variable == null then + assert parent != null + var place = detach_with_placeholder + variable = new Variable("") + variable.declared_type = self.mtype + var nvar = new AVarAssignExpr.make(variable, self) + place.replace_with(nvar) + self.variable_cache = variable + end + return new AVarExpr.make(variable) + end + + private var variable_cache: nullable Variable + + # The `detach` method completely remove the node in the parent. + # Owever, sometime, it is useful to keep the emplacement of the removed child. + # + # The standard usecase is the insertion of a node beetwen a parent `p` and a child `p.c`. + # To create the new node `n`, we need to attach the child to it. + # But, to put `n` where `c` was in `p`, the place has to be remembered. + # + # var p: AExpr + # var c = p.c + # var h = c.detach_with_placeholder + # var n = astbuilder.make_XXX(c) + # h.replace_with(n) + fun detach_with_placeholder: AExpr + do + var h = new APlaceholderExpr.make + self.replace_with(h) + return h + end + + + # Add `expr` at the end of the block + # + # REQUIRE: self isa ABlockExpr + # + # Note: this method, aimed to `ABlockExpr` is promoted to `AExpr` because of the limitations of the hierarchies generated by sablecc3 + fun add(expr: AExpr) + do + abort + end +end + +# A placeholder for a `AExpr` node +# Instances are transiantly used to mark some specific emplacments in the AST +# during complex transformations. +# +# Their must not appear in a valid AST +# +# @see AExpr::detach_with_placeholder +class APlaceholderExpr + super AExpr + private init make + do + end +end + +redef class ABlockExpr + private init make + do + self.is_typed = true + end + + redef fun add(expr: AExpr) + do + n_expr.add expr + end +end + +redef class ADoExpr + private init make + do + _n_kwdo = new TKwdo + escapemark = new EscapeMark(null, false) + end + + # Make a new break expression of the given do + fun make_break: ABreakExpr + do + var escapemark = self.escapemark + if escapemark == null then + escapemark = new EscapeMark(null, false) + self.escapemark = escapemark + end + return new ABreakExpr.make(escapemark) + end +end + +redef class ABreakExpr + private init make(escapemark: EscapeMark) + do + self.escapemark = escapemark + end +end + +redef class AIfExpr + private init make(condition: AExpr, mtype: nullable MType) + do + _n_kwif = new TKwif + _n_expr = condition + _n_expr.parent = self + _n_then = new ABlockExpr.make + _n_else = new ABlockExpr.make + self.mtype = mtype + self.is_typed = true + end +end + +redef class AType + private init make + do + _n_id = new TClassid + end +end + +redef class AIntExpr + private init make(value: Int, t: MType) + do + self.value = value + self._n_number = new TNumber # dummy + self.mtype = t + end +end + +redef class ANewExpr + private init make(mtype: MClassType, mmethod: MMethod, args: nullable Array[AExpr]) + do + _n_kwnew = new TKwnew + _n_type = new AType.make + _n_args = new AListExprs + if args != null then + n_args.n_exprs.add_all(args) + end + callsite = new CallSite(self, mtype, true, mmethod, mmethod.intro, mmethod.intro.msignature.as(not null), false) + self.mtype = mtype + end +end + +redef class ACallExpr + private init make(recv: AExpr, mmethod: MMethod, args: nullable Array[AExpr], t: nullable MType) + do + self._n_expr = recv + recv.parent = self + self.raw_arguments = args or else new Array[AExpr] + _n_args = new AListExprs + _n_id = new TId + if args != null then + self.n_args.n_exprs.add_all(args) + end + var mtype = recv.mtype.as(not null) + callsite = new CallSite(self, mtype, true, mmethod, mmethod.intro, mmethod.intro.msignature.as(not null), false) + self.mtype = t + self.is_typed = true + end +end + +redef class AAttrExpr + private init make(recv: AExpr, attribute: MAttribute, t: MType) + do + _n_expr = recv + recv.parent = self + _n_id = new TAttrid + mproperty = attribute + mtype = t + end +end + +redef class AAttrAssignExpr + private init make(recv: AExpr, attribute: MAttribute, value: AExpr) + do + _n_expr = recv + recv.parent = self + _n_id = new TAttrid + _n_value = value + value.parent = self + _n_assign = new TAssign + mproperty = attribute + mtype = value.mtype + end +end + +redef class AVarExpr + private init make(v: Variable) + do + _n_id = new TId + variable = v + mtype = v.declared_type + end +end + +redef class AVarAssignExpr + private init make(v: Variable, value: AExpr) + do + _n_id = new TId + _n_value = value + value.parent = self + _n_assign = new TAssign + variable = v + mtype = value.mtype + end +end + diff --git a/src/astprinter.nit b/src/astprinter.nit new file mode 100644 index 0000000..97ff1fd --- /dev/null +++ b/src/astprinter.nit @@ -0,0 +1,166 @@ +# This file is part of NIT ( http://www.nitlanguage.org ). +# +# Copyright 2012 Jean Privat +# +# 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. + +# print AST in an human form +module astprinter + +import typing +import phase +intrude import parser +import literal + +private class ASTPrinterVisitor + super Visitor + redef fun visit(node) + do + node.accept_printer(self) + end + + var out = new List[String] + var indent_level = 0 + + var has_eol = true + + fun eol + do + if has_eol then return + out.add("\n") + for x in [0..indent_level[ do out.add("\t") + has_eol = true + end + + var last_current: nullable ANode + + fun write(s: String) + do + if last_current != current_node then + last_current = current_node + var l = current_node._location + if l != null then + eol + out.add(s) + out.add("\t# {l.colored_line("0;32").split_with('\n').first}") + has_eol = false + eol + return + end + end + out.add(s) + has_eol = false + end + + fun indent do indent_level += 1 + fun unindent do indent_level -= 1 +end + +redef class ANode + # print the tree (using the semantic information) on screen + # This method is used to debug AST transformations + fun print_tree + do + var v = new ASTPrinterVisitor + v.enter_visit(self) + v.eol + for s in v.out do + printn s + end + end + + private fun accept_printer(v: ASTPrinterVisitor) + do + v.eol + v.write("({inspect}") + v.indent + visit_all(v) + v.write(")") + v.unindent + end +end + +redef class ABlockExpr + redef fun accept_printer(v) + do + for x in n_expr do + v.enter_visit(x) + v.eol + end + end +end + +redef class AIntExpr + redef fun accept_printer(v) + do + v.write(value.to_s) + end +end + +redef class ANewExpr + redef fun accept_printer(v) + do + v.write("new {mtype.as(not null)}.{mproperty.as(not null)}") + if not n_args.n_exprs.is_empty then + v.write("(") + v.indent + var is_first = true + for a in n_args.n_exprs do + if is_first then is_first = false else v.write(",") + v.enter_visit(a) + end + v.unindent + v.write(")") + end + end +end + +redef class ASendExpr + redef fun accept_printer(v) + do + v.enter_visit(n_expr) + v.write(".{mproperty.name}") + if not raw_arguments.is_empty then + v.write("(") + v.indent + var is_first = true + for a in raw_arguments.as(not null) do + if is_first then is_first = false else v.write(",") + v.enter_visit(a) + end + v.unindent + v.write(")") + end + end +end + +redef class AVarExpr + redef fun accept_printer(v) + do + var name = variable.name + if name == "" then name = "t{variable.object_id}" + v.write(name) + end +end + +redef class AVarAssignExpr + redef fun accept_printer(v) + do + var name = variable.name + if name == "" then name = "t{variable.object_id}" + v.write("{name} = ") + v.indent + v.enter_visit(n_value) + v.unindent + end +end diff --git a/src/astvalidation.nit b/src/astvalidation.nit new file mode 100644 index 0000000..913ee21 --- /dev/null +++ b/src/astvalidation.nit @@ -0,0 +1,87 @@ +# This file is part of NIT ( http://www.nitlanguage.org ). +# +# 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. + +# Check the consitency of AST +module astvalidation + +private import typing +intrude import parser +import astbuilder + +class ASTValidationVisitor + super Visitor + redef fun visit(node) + do + path.unshift(node) + node.accept_ast_validation(self) + path.shift + end + private var path = new List[ANode] + private var seen = new HashSet[ANode] +end + +redef class ANode + private fun accept_ast_validation(v: ASTValidationVisitor) + do + var parent = self.parent + + if v.path.length > 1 then + var path_parent = v.path[1] + if parent == null then + self.parent = path_parent + #debug "PARENT: expected parent: {path_parent}" + else if parent != path_parent then + self.parent = path_parent + debug "PARENT: expected parent: {path_parent}, got {parent}" + end + end + + if v.seen.has(self) then + debug "DUPLICATE: already seen node. NOTATREE" + end + v.seen.add(self) + + if _location == null then + #debug "LOCATION: unlocated node {v.path.join(", ")}" + _location = self.parent.location + end + + visit_all(v) + end +end + +redef class AAnnotations + redef fun accept_ast_validation(v) + do + # Do not enter in annotations + end +end + +redef class AExpr + redef fun accept_ast_validation(v) + do + super + if mtype == null and not is_typed then + debug "TYPING: untyped expression" + end + end +end + +redef class APlaceholderExpr + redef fun accept_ast_validation(v) + do + super + debug "PARENT: remaining placeholder" + end +end diff --git a/src/nit.nit b/src/nit.nit index e3d6f6d..caaf869 100644 --- a/src/nit.nit +++ b/src/nit.nit @@ -19,6 +19,7 @@ module nit import modelbuilder import frontend +import transform import naive_interpreter import debugger #import interpretor_type_test diff --git a/src/nitg.nit b/src/nitg.nit index 58bf0f1..636b0b4 100644 --- a/src/nitg.nit +++ b/src/nitg.nit @@ -19,6 +19,7 @@ module nitg import modelbuilder import frontend +import transform import rapid_type_analysis import global_compiler import separate_erasure_compiler diff --git a/src/transform.nit b/src/transform.nit new file mode 100644 index 0000000..af19ef6 --- /dev/null +++ b/src/transform.nit @@ -0,0 +1,308 @@ +# This file is part of NIT ( http://www.nitlanguage.org ). +# +# 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. + +# Thansformations that simplify the AST of expressions +# This module transform complex AST `AExpr` nodes into simplier ones +module transform + +import astbuilder +import auto_super_init +import astvalidation + +redef class ToolContext + var transform_phase: Phase = new TransformPhase(self, [typing_phase, auto_super_init_phase]) +end + +private class TransformPhase + super Phase + + redef fun process_npropdef(npropdef: APropdef) + do + var val + + var v = new TransformVisitor(self, npropdef) + v.enter_visit(npropdef) + + val = new ASTValidationVisitor + val.enter_visit(npropdef) + end +end + +private class TransformVisitor + super Visitor + + var phase: TransformPhase + var mmodule: MModule + var mclassdef: MClassDef + var mpropdef: MPropDef + var builder: ASTBuilder + + init(phase: TransformPhase, npropdef: APropdef) + do + self.phase = phase + self.mpropdef = npropdef.mpropdef.as(not null) + self.mclassdef = mpropdef.mclassdef + self.mmodule = mclassdef.mmodule + self.builder = new ASTBuilder(mmodule, mpropdef.mclassdef.bound_mtype) + end + + redef fun visit(node) + do + if node isa AAnnotations then return + node.visit_all(self) + node.accept_transform_visitor(self) + end + + # Get a primitive class or display a fatal error on `location`. + fun get_class(location: AExpr, name: String): MClass + do + return mmodule.get_primitive_class(name) + end + + # Get a primitive method or display a fatal error on `location`. + fun get_method(location: AExpr, name: String, recv: MClass): MMethod + do + return phase.toolcontext.modelbuilder.force_get_primitive_method(location, name, recv, mmodule) + end +end + +redef class ANode + private fun accept_transform_visitor(v: TransformVisitor) + do + end +end + +redef class AVardeclExpr + # `var x = y` is replaced with `x = y` + # + # Declarations are only useful for scope rules + # Once names are associated with real objects, ther declaration become useless + # Therefore, if there is no initial value, then just detach it + # Else, replace it with a simple assignment + redef fun accept_transform_visitor(v) + do + var nexpr = n_expr + if nexpr == null then + detach + else + var nvar = v.builder.make_var_assign(self.variable.as(not null), nexpr) + replace_with(nvar) + end + end +end + +redef class AIfexprExpr + # is replaced with `AIfExpr` + # Expression if and statement-if use two distinct classes for historical reasons + # However, on can replace the `AIfexprExpr` with the simpler `AIfExpr` + redef fun accept_transform_visitor(v) + do + var nif = v.builder.make_if(n_expr, self.mtype) + nif.n_then.add(n_then) + nif.n_else.add(n_else) + + replace_with(nif) + end +end + +redef class AOrExpr + # `x or y` is replaced with `if x then x else y` + redef fun accept_transform_visitor(v) + do + var nif = v.builder.make_if(n_expr, self.mtype) + nif.n_then.add(n_expr.make_var_read) + nif.n_else.add(n_expr2) + + replace_with(nif) + end +end + +redef class AAndExpr + # `x and y` is replaced with `if x then y else x` + redef fun accept_transform_visitor(v) + do + var nif = v.builder.make_if(n_expr, self.mtype) + nif.n_then.add(n_expr2) + nif.n_else.add(n_expr.make_var_read) + + replace_with(nif) + end +end + +redef class AWhileExpr + redef fun accept_transform_visitor(v) + do + # TODO + end +end + +redef class AForExpr + redef fun accept_transform_visitor(v) + do + # TODO + end +end + +redef class AArrayExpr + # `[x,y]` is replaced with + # + # var t = new Array[X].with_capacity(2) + # t.add(x) + # t.add(y) + # t + redef fun accept_transform_visitor(v) + do + var mtype = self.mtype.as(MClassType) + var nblock = v.builder.make_block + + var meth = v.get_method(self, "with_capacity", mtype.mclass) + var nnew = v.builder.make_new(mtype, meth, [v.builder.make_int(n_exprs.n_exprs.length)]) + nblock.add nnew + + var madd = v.get_method(self, "push", mtype.mclass) + for nexpr in self.n_exprs.n_exprs do + var nadd = v.builder.make_call(nnew.make_var_read, madd, [nexpr]) + nblock.add nadd + end + var nres = nnew.make_var_read + nblock.add nres + + replace_with(nblock) + end +end + +redef class ASuperstringExpr + # `"x{y}z"` is replaced with + # + # var t = new Array[Object].with_capacity(3) + # t.add("x") + # t.add(y) + # t.add("z") + # t.to_s + redef fun accept_transform_visitor(v) + do + var nblock = v.builder.make_block + + var arraytype = v.get_class(self, "Array").get_mtype([v.get_class(self, "Object").mclass_type]) + var meth = v.get_method(self, "with_capacity", arraytype.mclass) + var nnew = v.builder.make_new(arraytype, meth, [v.builder.make_int(n_exprs.length)]) + nblock.add nnew + + var madd = v.get_method(self, "add", arraytype.mclass) + for nexpr in self.n_exprs do + var nadd = v.builder.make_call(nnew.make_var_read, madd, [nexpr]) + nblock.add nadd + end + + var mtos = v.get_method(self, "to_s", arraytype.mclass) + var ntos = v.builder.make_call(nnew.make_var_read, mtos, null) + nblock.add ntos + + replace_with(nblock) + end +end + +redef class ACrangeExpr + # `[x..y]` is replaced with `new Range[X](x,y)` + redef fun accept_transform_visitor(v) + do + var mtype = self.mtype.as(MClassType) + var meth = v.get_method(self, "init", mtype.mclass) + + replace_with(v.builder.make_new(mtype, meth, [n_expr, n_expr2])) + end +end + +redef class AOrangeExpr + # `[x..y[` is replaced with `new Range[X].without_last(x,y)` + redef fun accept_transform_visitor(v) + do + var mtype = self.mtype.as(MClassType) + var meth = v.get_method(self, "without_last", mtype.mclass) + + replace_with(v.builder.make_new(mtype, meth, [n_expr, n_expr2])) + end +end + +redef class AParExpr + # `(x)` is replaced with `x` + redef fun accept_transform_visitor(v) + do + replace_with(n_expr) + end +end + +redef class ASendReassignFormExpr + # `x.foo(y)+=z` is replaced with + # + # x.foo(y) = x.foo(y) + z + # + # witch is, in reality: + # + # x."foo="(y, x.foo(y)."+"(z)) + redef fun accept_transform_visitor(v) + do + var nblock = v.builder.make_block + nblock.add(n_expr) + + var read_args = new Array[AExpr] + var write_args = new Array[AExpr] + for a in raw_arguments.as(not null) do + nblock.add(a) + read_args.add(a.make_var_read) + write_args.add(a.make_var_read) + end + + var nread = v.builder.make_call(n_expr.make_var_read, callsite.mproperty, read_args) + + var nnewvalue = v.builder.make_call(nread, reassign_callsite.mproperty, [n_value]) + + write_args.add(nnewvalue) + var nwrite = v.builder.make_call(n_expr.make_var_read, write_callsite.mproperty, write_args) + nblock.add(nwrite) + + replace_with(nblock) + end +end + +redef class AVarReassignExpr + # `v += z` is replaced with `v = v + z` + redef fun accept_transform_visitor(v) + do + var variable = self.variable.as(not null) + + var nread = v.builder.make_var_read(variable) + var nnewvalue = v.builder.make_call(nread, reassign_callsite.mproperty, [n_value]) + var nwrite = v.builder.make_var_assign(variable, nnewvalue) + + replace_with(nwrite) + end +end + +redef class AAttrReassignExpr + # `x.a += z` is replaced with `x.a = x.a + z` + redef fun accept_transform_visitor(v) + do + var nblock = v.builder.make_block + nblock.add(n_expr) + var attribute = self.mproperty.as(not null) + + var nread = v.builder.make_attr_read(n_expr.make_var_read, attribute) + var nnewvalue = v.builder.make_call(nread, reassign_callsite.mproperty, [n_value]) + var nwrite = v.builder.make_attr_assign(n_expr.make_var_read, attribute, nnewvalue) + nblock.add(nwrite) + + replace_with(nblock) + end +end -- 1.7.9.5