diff --git a/src/astprinter.nit b/src/astprinter.nit index 91aee2f9a6..bd5c4b7281 100644 --- a/src/astprinter.nit +++ b/src/astprinter.nit @@ -28,28 +28,82 @@ private class ASTPrinterVisitor node.accept_printer(self) end + fun visit_block_noend(keyword: String, n_block: nullable AExpr): Bool + do + space + write(keyword) + var nl = number_of_lines(n_block) + if nl == 0 then + space + return true + else if nl == 1 then + space + indent + enter_visit(n_block) + unindent + return false + else + eol + indent + enter_visit(n_block) + unindent + return true + end + end + + fun visit_block(keyword: String, n_block: nullable AExpr) + do + if visit_block_noend(keyword, n_block) then + write("end") + end + eol + end + + fun number_of_lines(n_expr: nullable AExpr): Int + do + if n_expr == null then return 0 else return n_expr.number_of_lines + end + + fun varname(variable: nullable Variable): String + do + if variable == null then return "?" + var name = variable.name + if name == "" then name = "t{variable.object_id}" + return name + end + var out = new List[String] var indent_level = 0 var has_eol = true + var has_space = false fun eol do if has_eol then return - out.add("\n") - for x in [0..indent_level[ do out.add("\t") has_eol = true + out.add("\n") + end + fun space + do + has_space = true end var last_current: nullable ANode = null fun write(s: String) do + if has_eol then + for x in [0..indent_level[ do out.add("\t") + else if has_space then + out.add(" ") + end if last_current != current_node then last_current = current_node end out.add(s) has_eol = false + has_space = false end fun indent do indent_level += 1 @@ -80,7 +134,40 @@ redef class ANode end end +redef class Token + redef fun accept_printer(v) + do + #v.write(text) + super + end +end + +redef class ADoc + redef fun accept_printer(v) do visit_all(v) +end + +redef class AType + redef fun accept_printer(v) do v.write(mtype.to_s) +end + + +redef class AMainMethPropdef + redef fun accept_printer(v) do v.enter_visit(n_block) +end + +redef class AExpr + fun number_of_lines: Int do return 1 +end + redef class ABlockExpr + redef fun number_of_lines + do + var res = 0 + for x in n_expr do + res += x.number_of_lines + end + return res + end redef fun accept_printer(v) do for x in n_expr do @@ -90,10 +177,74 @@ redef class ABlockExpr end end -redef class AIntegerExpr - redef fun accept_printer(v) +redef class ADoExpr + redef fun number_of_lines do return n_block.number_of_lines + redef fun accept_printer(v) do + v.visit_block("do", n_block) + end +end + +redef class ALoopExpr + redef fun number_of_lines do return n_block.number_of_lines + redef fun accept_printer(v) do + v.visit_block("loop", n_block) + end +end + +redef class AForExpr + redef fun number_of_lines do return n_block.number_of_lines + redef fun accept_printer(v) do + v.write("for ") + var is_first_group = true + for g in n_groups do + if is_first_group then is_first_group = false else v.write(", ") + var is_first_variable = true + for va in g.variables do + if is_first_variable then is_first_variable = false else v.write(", ") + v.write(v.varname(va)) + end + v.write(" in ") + v.visit(g.n_expr) + end + v.visit_block("do", n_block) + end +end + +redef class AIfExpr + redef fun number_of_lines do - v.write(value.to_s) + var nt = n_then.number_of_lines + var ne = if n_else != null then n_else.number_of_lines else 0 + if nt <= 1 or ne <= 1 then return nt.max(ne) + return nt + ne + 1 + end + redef fun accept_printer(v) do + v.write("if ") + v.enter_visit(n_expr) + if n_else != null and n_else.number_of_lines > 0 then + v.visit_block_noend("then", n_then) + v.visit_block("else", n_else) + else + v.visit_block("then", n_then) + end + end +end + +redef class AIfexprExpr + redef fun number_of_lines + do + var nt = n_then.number_of_lines + var ne = n_else.number_of_lines + if nt <= 1 or ne <= 1 then return nt.max(ne) + return nt + ne + 1 + end + redef fun accept_printer(v) do + v.write("if ") + v.enter_visit(n_expr) + v.write(" then ") + v.visit(n_then) + v.write(" else ") + v.visit(n_else) end end @@ -106,7 +257,7 @@ redef class ANewExpr v.indent var is_first = true for a in n_args.n_exprs do - if is_first then is_first = false else v.write(",") + if is_first then is_first = false else v.write(", ") v.enter_visit(a) end v.unindent @@ -118,14 +269,17 @@ end redef class ASendExpr redef fun accept_printer(v) do - v.enter_visit(n_expr) - v.write(".{callsite.mproperty.name}") + if not n_expr isa AImplicitSelfExpr then + v.enter_visit(n_expr) + v.write "." + end + v.write(callsite.mproperty.name) if not raw_arguments.is_empty then v.write("(") v.indent var is_first = true for a in raw_arguments do - if is_first then is_first = false else v.write(",") + if is_first then is_first = false else v.write(", ") v.enter_visit(a) end v.unindent @@ -134,23 +288,196 @@ redef class ASendExpr end end +redef class ANotExpr + redef fun accept_printer(v) + do + v.write("not ") + v.visit(n_expr) + end +end + +redef class AAndExpr + redef fun accept_printer(v) + do + v.visit(n_expr) + v.write(" and ") + v.visit(n_expr2) + end +end + +redef class AOrExpr + redef fun accept_printer(v) + do + v.visit(n_expr) + v.write(" or ") + v.visit(n_expr2) + end +end + +redef class AIsaExpr + redef fun accept_printer(v) + do + v.visit(n_expr) + v.write(" isa ") + v.visit(n_type) + end +end + +redef class AAsCastExpr + redef fun accept_printer(v) + do + v.visit(n_expr) + v.write(".as(") + v.visit(n_type) + v.write(")") + end +end + +redef class AAsNotnullExpr + redef fun accept_printer(v) + do + v.visit(n_expr) + v.write(".as(not null)") + end +end + +redef class AOrangeExpr + redef fun accept_printer(v) + do + v.write("[") + v.visit(n_expr) + v.write("..") + v.visit(n_expr2) + v.write("[") + end +end + +redef class ACrangeExpr + redef fun accept_printer(v) + do + v.write("[") + v.visit(n_expr) + v.write("..") + v.visit(n_expr2) + v.write("]") + end +end + +redef class AArrayExpr + redef fun accept_printer(v) + do + v.write("[") + var is_first = true + for n in n_exprs do + if is_first then is_first = false else v.write(", ") + v.visit(n) + end + v.write("]") + 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) + v.write(v.varname(variable)) + end +end + +redef class AVardeclExpr + redef fun accept_printer(v) + do + v.write("var ") + v.write(v.varname(variable)) + if n_type != null then + v.write(": ") + v.enter_visit(n_type) + end + if n_expr != null then + v.write(" = ") + v.enter_visit(n_expr) + end 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.write(v.varname(variable)) + v.write(" = ") + v.indent + v.enter_visit(n_value) + v.unindent + end +end + +redef class AVarReassignExpr + redef fun accept_printer(v) + do + v.write(v.varname(variable)) + v.write(" ") + v.write(reassign_callsite.mproperty.name) + v.write("= ") v.indent v.enter_visit(n_value) v.unindent end end + +redef class AReturnExpr + redef fun accept_printer(v) + do + v.write("return ") + v.enter_visit(n_expr) + end +end + +redef class ABreakExpr + redef fun accept_printer(v) do v.write("break") +end +redef class AContinueExpr + redef fun accept_printer(v) do v.write("continue") +end + +redef class AAssertExpr + redef fun accept_printer(v) + do + v.write("assert ") + v.enter_visit(n_expr) + end +end + +redef class ASelfExpr + redef fun accept_printer(v) do v.write("self") +end + +redef class AImplicitSelfExpr + redef fun accept_printer(v) do end +end + +redef class ATrueExpr + redef fun accept_printer(v) do v.write("true") +end + +redef class AFalseExpr + redef fun accept_printer(v) do v.write("false") +end + +redef class ANullExpr + redef fun accept_printer(v) do v.write("null") +end + +redef class AIntegerExpr + redef fun accept_printer(v) + do + v.write(value.to_s) + end +end + +redef class AStringExpr + redef fun accept_printer(v) + do + v.write("\"") + v.write(value.escape_to_nit) + v.write("\"") + end +end diff --git a/src/test_astprinter.nit b/src/test_astprinter.nit new file mode 100644 index 0000000000..d4e620687d --- /dev/null +++ b/src/test_astprinter.nit @@ -0,0 +1,37 @@ +# 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. + +# Test the AST printer by reading modules and printing the body of their methods +module test_astprinter + +import test_phase +import frontend +#import transform # Not yet :) + +import astprinter + +redef fun do_work(mainmodule, given_mmodules, modelbuilder) +do + # We iterate the model instead of the ast for classes and methods + for m in given_mmodules do + for cd in m.mclassdefs do + for pd in cd.mpropdefs do + print "# {pd}" + var node = modelbuilder.mentity2node(pd) + assert node != null + node.print_tree + end + end + end +end diff --git a/tests/nitcg.skip b/tests/nitcg.skip index 16a81df3ba..3c378aa029 100644 --- a/tests/nitcg.skip +++ b/tests/nitcg.skip @@ -10,3 +10,4 @@ test_loader get_mclasses ^nit test_astbuilder +test_astprinter diff --git a/tests/niti.skip b/tests/niti.skip index 6e60c30b9c..551351b0d6 100644 --- a/tests/niti.skip +++ b/tests/niti.skip @@ -47,3 +47,4 @@ repeating_key_xor_solve nitpm nitdoc test_astbuilder +test_astprinter diff --git a/tests/sav/syntax_expr.res b/tests/sav/syntax_expr.res new file mode 100644 index 0000000000..6ed281c757 --- /dev/null +++ b/tests/sav/syntax_expr.res @@ -0,0 +1,2 @@ +1 +1 diff --git a/tests/sav/test_astprinter.res b/tests/sav/test_astprinter.res new file mode 100644 index 0000000000..fd8dbb514a --- /dev/null +++ b/tests/sav/test_astprinter.res @@ -0,0 +1,2 @@ +Usage: [OPTION]... ... +Use --help for help diff --git a/tests/sav/test_astprinter_args1.res b/tests/sav/test_astprinter_args1.res new file mode 100644 index 0000000000..f7e6fdddee --- /dev/null +++ b/tests/sav/test_astprinter_args1.res @@ -0,0 +1,87 @@ +# syntax_expr$Sys$main +var a +var b: Bool +var c: Collection[Int] = [1] +var d = 1 +a = 1.+(2).-(3.unary -./(4).*(5).%(6.**(2))) +b = true and not false or true or a.<(0) or a.<=(0) or a.==(0) or a.!=(0) or a.>=(0) or a.>(0) +a = 1.&(2).|(3.unary ~.^(4.<<(5).>>(6))) +a = 1.<=>(2) +c = [1..2[ +c = [1..2] +c = [1, 2, 3] +a += 1 +a -= 1 +a *= 1 +a /= 1 +a %= 1 +a **= 1 +a &= 1 +a |= 1 +a ^= 1 +a <<= 1 +a >>= 1 +a = 1 +print(a) +print(a) +a.abs +a.max(2) +a.max(2) +a.setbit(0, 1) +c = new Array[Int].defaultinit +c = new Array[Int].with_capacity(10) +a = new Array[Int].with_capacity(10).length +c.add(1) +a = a isa Array[Int] +a = null +if b then a = 1 +d = a.as(not null) +d = a.as(Int) +do end +do a = 1 +do + a = 1 + a = 1 +end +if b then end +if b then a = 1 +if b then + a = 2 + a = 2 +end +if b then a = 3 +if b then a = 4 else a = 5 +if b then a = 6 else + a = 7 + a = 7 +end +if b then end +if b then end +if b then else a = 8 +if b then else + a = 9 + a = 9 +end +if b then if b then a = 1 +if b then + if b then + a = 2 + a = 3 + end +end +a = if b then 1 else 2 +a = if b then if b then 3 else 4 else if b then 5 else 6 +for i in c do end +for i in c do + a = 1 + a = 1 +end +for i in c, j in c do end +var m = new HashMap[String, String].defaultinit +for k, v in m do end +loop + if b then break + if b then continue +end +assert b +if not b then return diff --git a/tests/syntax_expr.nit b/tests/syntax_expr.nit new file mode 100644 index 0000000000..61bdda49ca --- /dev/null +++ b/tests/syntax_expr.nit @@ -0,0 +1,118 @@ +# 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. + +var a +var b: Bool +var c: Collection[Int] = [1] +var d = 1 +a = 1 + 2 - - 3 / 4 * 5 % 6 ** 2 +b = true and not false or true or a < 0 or a <= 0 or a == 0 or a != 0 or a >= 0 or a > 0 +a = 1 & 2 | ~3 ^ 4 << 5 >> 6 +a = 1 <=> 2 +c = [1..2[ +c = [1..2] +c = [1,2,3] + +a += 1 +a -= 1 +a *= 1 +a /= 1 +a %= 1 +a **= 1 + +a &= 1 +a |= 1 +a ^= 1 +a <<= 1 +a >>= 1 + +a = 1 +print(a) +print a +a.abs +a.max(2) +a.max 2 +a.setbit(0, 1) + +c = new Array[Int] +c = new Array[Int].with_capacity(10) +a = new Array[Int].with_capacity(10).length +c.add(1) + +a = a isa Array[Int] +a = null +if b then a = 1 +d = a.as(not null) +d = a.as(Int) + + +do end +do a = 1 +do + a = 1 + a = 1 +end + +if b then end +if b then a = 1 +if b then + a = 2 + a = 2 +end +if b then a = 3 else end +if b then a = 4 else a = 5 +if b then a = 6 else + a = 7 + a = 7 +end +if b then +else +end +if b then else end +if b then else a = 8 +if b then else + a = 9 + a = 9 +end +if b then + if b then + a = 1 + end +end +if b then + if b then + a = 2 + a = 3 + end +end + +a = if b then 1 else 2 +a = if b then if b then 3 else 4 else if b then 5 else 6 + +for i in c do end +for i in c do + a = 1 + a = 1 +end +for i in c, j in c do end +var m = new HashMap[String,String] +for k, v in m do end + +loop + if b then break + if b then continue +end + +assert b +if not b then return diff --git a/tests/test_astprinter.args b/tests/test_astprinter.args new file mode 100644 index 0000000000..a405ed848a --- /dev/null +++ b/tests/test_astprinter.args @@ -0,0 +1 @@ +syntax_expr.nit