Permalink
Browse files

Added ruby2smalltalk - not complete yet.

[git-p4: depot-paths = "//src/ruby2smalltalk/dev/": change = 2747]
  • Loading branch information...
0 parents commit 1b84c8c65cb9d4aabff45b5e0c12b561499e079a @zenspider zenspider committed Nov 15, 2006
Showing with 821 additions and 0 deletions.
  1. +5 −0 History.txt
  2. +7 −0 Manifest.txt
  3. +48 −0 README.txt
  4. +15 −0 Rakefile
  5. +18 −0 bin/ruby2smalltalk
  6. +31 −0 demo.rb
  7. +442 −0 lib/ruby2smalltalk.rb
  8. +255 −0 test/test_ruby2smalltalk.rb
5 History.txt
@@ -0,0 +1,5 @@
+== 1.0.0 / 2006-10-23
+
+* 1 major enhancement
+ * Birthday!
+
7 Manifest.txt
@@ -0,0 +1,7 @@
+History.txt
+Manifest.txt
+README.txt
+Rakefile
+bin/ruby2smalltalk
+lib/ruby2smalltalk.rb
+test/test_ruby2smalltalk.rb
48 README.txt
@@ -0,0 +1,48 @@
+ruby2smalltalk
+ by FIX
+ FIX
+
+== DESCRIPTION:
+
+FIX
+
+== FEATURES/PROBLEMS:
+
+* FIX
+
+== SYNOPSYS:
+
+ FIX
+
+== REQUIREMENTS:
+
++ FIX
+
+== INSTALL:
+
++ FIX
+
+== LICENSE:
+
+(The MIT License)
+
+Copyright (c) 2006 FIX
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+'Software'), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
15 Rakefile
@@ -0,0 +1,15 @@
+# -*- ruby -*-
+
+require 'rubygems'
+require 'hoe'
+require './lib/ruby2smalltalk.rb'
+
+Hoe.new('ruby2smalltalk', RubyToSmalltalk::VERSION) do |p|
+ p.rubyforge_name = 'ruby2smalltalk'
+ p.summary = 'FIX'
+ p.description = p.paragraphs_of('README.txt', 2..5).join("\n\n")
+ p.url = p.paragraphs_of('README.txt', 0).first.split(/\n/)[1..-1]
+ p.changes = p.paragraphs_of('History.txt', 0..1).join("\n\n")
+end
+
+# vim: syntax=Ruby
18 bin/ruby2smalltalk
@@ -0,0 +1,18 @@
+#!/usr/local/bin/ruby -w
+
+require 'rubygems'
+require 'ruby2smalltalk'
+
+code = []
+
+ARGV.unshift "-" if ARGV.empty?
+ARGV.each do |path|
+ code << if path == "-" then
+ $stdin.read
+ else
+ File.read(path)
+ end
+end
+
+puts RubyToSmalltalk.translate(code.join("\n\n"))
+
31 demo.rb
@@ -0,0 +1,31 @@
+class R2SExamples
+
+ def fib(n)
+ return 1 if n <= 1
+ return fib(n-1) + fib(n-2)
+ end
+
+ def factorial(n)
+ f = 1
+ n.downto(2) { |x| f *= x }
+ return f
+ end
+
+end
+
+# if $0 == __FILE__ then
+# f = Factorial.new()
+
+# max = ARGV.shift || 1000000
+# max = max.to_i
+
+# tstart = Time.now
+
+# (1..max).each { |m| n = f.factorial(5); }
+
+# tend = Time.now
+
+# total = tend - tstart
+# avg = total / max
+# # printf "Iter = #{max}, T = %.8f sec, %.8f sec / iter\n", total, avg
+# end
442 lib/ruby2smalltalk.rb
@@ -0,0 +1,442 @@
+# -*- ruby -*-
+
+require 'rubygems'
+require 'sexp_processor'
+require 'parse_tree'
+
+class RubyToSmalltalk < SexpProcessor
+ VERSION = '1.0.0'
+ BINARY_MSGS = [ :==, :<, :>, :<=, :>=, :+, :-, :*, :/, :| ]
+ KEYWORD_MSGS = [ :puts, :upto, :downto, :p ]
+
+ def self.translate(klass_or_str, method=nil)
+ self.new.process(ParseTree.translate(klass_or_str, method))
+ end
+
+ def initialize
+ super
+ self.auto_shift_type = true
+ self.strict = true
+ self.expected = String
+ self.unsupported << :defined
+ @indent = " "
+ end
+
+ def process_and(exp)
+ "#{process(exp.shift)} & #{process(exp.shift)}"
+ end
+
+ def process_arglist(exp)
+ r = []
+ r << process(exp.shift) until exp.empty?
+ "{ #{r.join(". ")} }"
+ end
+
+ def process_args(exp)
+ args = []
+ args << exp.shift.to_s until exp.empty?
+ args.join(", ")
+ end
+
+ def process_array(exp)
+ r = []
+ r << process(exp.shift) until exp.empty?
+ "{ #{r.join('. ')} }"
+ end
+
+ def process_block(exp)
+ r = []
+ r << process(exp.shift) until exp.empty?
+ r.join(".\n")
+ end
+
+ def process_block_arg(exp)
+ exp.shift.to_s
+ end
+
+ def process_call(exp)
+ lhs = process(exp.shift)
+ msg = exp.shift
+
+ rhs = exp.shift
+ if rhs then
+ rhs[0] = :arglist
+ rhs = process(rhs)
+ end
+
+ KEYWORD_MSGS << msg
+
+ unless rhs then # unary
+ "#{lhs} #{msg}"
+ else # binary or keyword
+ case msg
+ when *BINARY_MSGS then
+ rhs = $1 if rhs =~ /^\{ (.*) \}$/
+ "( #{lhs} ) #{msg} ( #{rhs} )"
+ when *KEYWORD_MSGS then
+ case msg
+ when :upto, :downto then # HACK
+ "#{lhs} #{msg}: #{rhs} do"
+ else
+ "#{lhs} #{msg}: #{rhs}"
+ end
+ else
+ raise "message not handled: #{msg}"
+ end
+ end
+ end
+
+ def process_class(exp)
+ new_exp = Sexp.from_array exp
+ exp.clear
+ exp = new_exp
+
+ name = exp.shift
+ supr = process(exp.shift) || "Object"
+
+ exp = exp.scope
+ exp = exp.block if exp.block
+ exp.shift # :scope or :block
+
+ methods = []
+ methods << process(exp.shift) until exp.empty?
+
+ "#{supr} subclass: ##{name}
+ instanceVariableNames: ''
+ classVariableNames: ''
+ poolDictionaries: ''
+ category: 'ruby2smalltalk'!\n\n" + methods.map { |method|
+ "!#{name} methodsFor: 'translated'!\n#{method}! !"
+ }.join("\n\n")
+ end
+
+ def process_const(exp)
+ exp.shift.to_s
+ end
+
+ def process_dasgn_curr(exp)
+ exp.shift.to_s
+ end
+
+# [:defn, :xx, [:scope, [:block, [:args], [:nil]]]]
+
+# [:defn, :xx, [:ivar, :@reader]]
+# [:defn, :x=, [:attrset, :@writer]]
+
+# [:defn, :xx, [:scope, [:block, [:args, :a, :b, :"*c", [:block, [:lasgn, :b, [:lit, 42]]]], [:block_arg, :d], [:fcall, :p, [:array, [:lvar, :a], [:lvar, :b], [:lvar, :c], [:lvar, :d]]]]]]
+# [:defn, :x?, [:scope, [:block, [:args], [:nil]]]]
+# [:defn, :|, [:scope, [:block, [:args], [:nil]]]]
+
+# [:defn, :xx, [:bmethod, [:dasgn_curr, :x], [:call, [:dvar, :x], :+, [:array, [:lit, 1]]]]]
+# [:defn, :xx, [:dmethod, :bmethod_maker, [:scope, [:block, [:args], [:iter, [:fcall, :define_method, [:array, [:lit, :bmethod_added]]], [:dasgn_curr, :x], [:call, [:dvar, :x], :+, [:array, [:lit, 1]]]]]]]]
+# [:defn, :xx, [:fbody, [:scope, [:block, [:args], [:call, [:lit, 1], :+, [:array, [:lit, 1]]]]]]]
+
+ def process_defn(exp)
+ new_exp = Sexp.from_array(exp)
+ exp.clear
+ exp = new_exp # for convention
+
+ name = exp.shift
+ @method_name = name # for zsuper rewriters and such
+ meth_type = exp.first.first
+ case meth_type
+ when :scope then
+ args = exp.scope.block.args(true)
+ abort args.inspect unless args # FIX
+
+ opt_args = args.block(true)
+ block_arg = process(exp.scope.block.block_arg(true))
+ body = process(exp.shift)
+ arity = args.size - 1
+
+ name = case name.to_s
+ when /\?$/ then
+ "is_#{name.to_s[0..-2]}".intern
+ else
+ name
+ end
+
+ case arity
+ when 0 then
+ "#{name}\n\n#{indent(body)}"
+ when 1 then # binary or keyword?
+ if BINARY_MSGS.include? name then
+ "#{name} #{process(args)}\n\n#{indent(body)}"
+ else
+ arg = process(args)
+ "#{name}: args
+
+ | #{arg} |
+ ( args class == Array ) ifTrue: [ #{arg} := args at: 1. ] ifFalse: [ #{arg} := args ].
+
+#{indent(body)}"
+ end
+ else
+ r = "#{name}: args\n\n"
+ r << " | "
+ args.shift # :args
+ r << args.map { |arg|
+ case arg.to_s
+ when /^\*/ then
+ arg.to_s[1..-1]
+ else
+ arg.to_s
+ end
+ }.join(" ") + (block_arg ? " #{block_arg}" : "")
+
+ r << " |\n\n"
+
+ args.each do |arg|
+ case arg.to_s
+ when /^\*/ then
+ r << indent("#{block_arg} := args removeLast.\n") if block_arg
+ r << indent("#{arg.to_s[1..-1]} := args.\n")
+ else
+ r << indent("#{arg} := args removeFirst.\n")
+ end
+ end
+
+ if opt_args then
+ opt_args.each_with_index do |subexp, i|
+ next if i == 0
+ var = subexp[1]
+ opt_args[i] = s(:if, s(:call, s(:lvar, var), :isNil), subexp)
+ end
+ r << indent(process(opt_args))
+ end
+
+ r << ".\n\n"
+ r << "#{indent(body)}"
+ r
+ end
+ else
+ raise "unhandled defn type #{meth_type}"
+ end
+ end
+
+ def process_defs(exp)
+ # HACK - figger out the class chunk thingy
+ exp.shift # HACK target of class
+ process_defn(exp)
+ end
+
+ def process_dvar(exp)
+ exp.shift.to_s
+ end
+
+ def process_global(exp)
+ abort exp.inspect
+ end
+
+ def process_false(exp)
+ "false"
+ end
+
+ def process_fcall(exp)
+ exp.unshift [:self]
+ process_call(exp)
+ end
+
+ def process_iasgn(exp)
+ var = exp.shift
+ val = process exp.shift
+
+ "self #{var}: #{val}"
+ end
+
+ def process_if(exp)
+ c = process(exp.shift)
+ t = process(exp.shift)
+ f = process(exp.shift)
+
+ result = []
+ result << "( #{c} )"
+ result << " ifTrue: [ #{t} ]" if t
+ result << " ifFalse: [ #{f} ]" if f
+ result.join
+ end
+
+ def process_iter(exp)
+ iter = process exp.shift
+ args = process exp.shift
+ body = process exp.shift
+
+ r = []
+ r << "#{iter}: ["
+ r << " #{args.map { |a| ":#{a}" } } |" if args
+ r << "\n"
+ r << "#{indent(body)}\n" if body
+ r << "]"
+
+ r.join
+ end
+
+ def process_ivar(exp)
+ abort exp.inspect
+ end
+
+ def process_lasgn(exp)
+ lhs = exp.shift
+ rhs = process(exp.shift)
+ "#{lhs} := #{rhs}"
+ end
+
+ def process_lit(exp)
+ val = exp.shift
+ case val
+ when Numeric then
+ val.to_s
+ when Regexp then
+ regexp = val.to_s.split(/:/, 2).last[0..-2]
+ "'#{regexp}'"
+ when Symbol then
+ "##{val}"
+ when Range then
+ raise UnsupportedNodeError, "Currently we don't do ranges"
+ else
+ raise "no: #{val.inspect}/#{val.class}"
+ end
+ end
+
+ def process_lvar(exp)
+ "#{exp.shift}"
+ end
+
+ def process_masgn(exp)
+ lhs = exp.shift
+ rhs = exp.shift
+
+ assert_type lhs, :array
+ lhs.shift
+ lhs = lhs.map { |l| process(l) }
+
+ unless rhs.nil? then
+ # HACK - but seems to work (see to_ary test) assert_type rhs, :array
+ rhs.shift
+ rhs = rhs.map { |r| process(r) }
+ lhs.zip(rhs).map do |l,r|
+ "#{l} := #{r}."
+ end.join("\n")
+ else
+ return lhs.join(", ")
+ end
+ end
+
+ def process_nil(exp)
+ "nil"
+ end
+
+ def process_not(exp)
+ "#{process(exp.shift)} not"
+ end
+
+ # b[2] &&= 11
+ # b[3] += 12
+
+ def process_op_asgn1(exp)
+ # b[1] ||= 10 <=> b at: 1 put: ((b at: 1) rubyFalse or: [ 11 ])
+ lhs = process exp.shift # b
+ rhs = process exp.shift # 1
+ op = exp.shift # ||
+ val = process exp.shift # 10
+
+ case op
+ when :"||" then
+ op, val = "rubyFalse or:", "[ #{val} ]"
+ when :"&&" then
+ op, val = "rubyFalse and:", "[ #{val} ]"
+ else
+ raise "unsupported op #{op}"
+ end
+
+ "#{lhs} at: #{rhs} put: ((#{lhs} at: #{rhs}) rubyFalse #{op} #{val})"
+ end
+
+ def process_op_asgn2(exp)
+ # c.var ||= 20 <=> c.var: c.var rubyFalse or: [ 20 ]
+
+ "#{lhs} at: #{rhs} put: ((#{lhs} at: #{rhs}) rubyFalse #{op} #{val})"
+ end
+
+ def process_op_asgn_and(exp)
+ abort exp.inspect
+ end
+
+ def process_op_asgn_or(exp)
+ abort exp.inspect
+ end
+
+ def process_or(exp)
+ abort exp.inspect
+ end
+
+ def process_return(exp)
+ "^" + (exp.empty? ? "" : " #{process(exp.shift)}")
+ end
+
+ def process_scope(exp)
+ process exp.shift
+ end
+
+ def process_self(exp)
+ "self"
+ end
+
+ def process_str(exp)
+ str = exp.shift.inspect[1..-2]
+ "'#{str}'"
+ end
+
+ def process_super(exp)
+ args = exp.shift
+ args.shift # :array
+ "super #{@method_name}: #{process_arglist args}"
+ end
+
+ def process_true(exp)
+ "true"
+ end
+
+ def process_until(exp)
+ cond = process(exp.shift)
+ body = process(exp.shift)
+ head_controlled = exp.empty? ? false : exp.shift
+
+ if head_controlled then
+ "[ #{cond} ] whileFalse: [ #{body} ]"
+ else
+ "[ #{body}. #{cond} ] whileFalse"
+ end
+ end
+
+ def process_while(exp)
+ cond = process(exp.shift)
+ body = process(exp.shift)
+ head_controlled = exp.empty? ? false : exp.shift
+
+ if head_controlled then
+ "[ #{cond} ] whileTrue: [ #{body} ]"
+ else
+ "[ #{body}. #{cond} ] whileTrue"
+ end
+ end
+
+ def process_vcall(exp)
+ # TODO: args? should be a keyword expression I guess
+ "self #{exp.shift}"
+ end
+
+ def process_zarray(exp)
+ "{}"
+ end
+
+ def process_zsuper(exp)
+ "super #{@method_name}"
+ end
+
+ ############################################################
+
+ def indent(s)
+ s.to_s.map{|line| @indent + line}.join
+ end
+end
255 test/test_ruby2smalltalk.rb
@@ -0,0 +1,255 @@
+#!/usr/local/bin/ruby -w
+
+require 'test/unit' if $0 == __FILE__
+require 'ruby2smalltalk'
+require 'pt_testcase'
+
+ParseTreeTestCase.testcase_order << "RubyToSmalltalk"
+class TestRubyToSmalltalk < ParseTreeTestCase
+ def setup
+ super
+ @processor = RubyToSmalltalk.new
+ end
+
+ add_test("alias", :flunk)
+
+ add_test("and",
+ "self a & self b")
+
+ add_test("argscat", :flunk)
+ add_test("argspush", :flunk)
+
+ add_test("array",
+ "{ 1. #b. 'c' }")
+
+ add_test("attrasgn", :flunk)
+ add_test("attrset", :flunk)
+ add_test("back_ref", :flunk)
+ add_test("begin", :flunk)
+ add_test("block_pass", :flunk)
+ add_test("bmethod", :flunk)
+ add_test("break", :flunk)
+ add_test("break_arg", :flunk)
+
+ add_test("call",
+ "self method")
+
+ add_test("call_arglist",
+ "self puts: { 42 }")
+
+ add_test("case", :flunk)
+ add_test("cdecl", :flunk)
+
+ add_test("class",
+ "Array subclass: #X
+ instanceVariableNames: ''
+ classVariableNames: ''
+ poolDictionaries: ''
+ category: 'ruby2smalltalk'!
+
+!X methodsFor: 'translated'!
+blah
+
+ self puts: { 'hello' }! !")
+
+ add_test("class_sans",
+ "Object subclass: #X
+ instanceVariableNames: ''
+ classVariableNames: ''
+ poolDictionaries: ''
+ category: 'ruby2smalltalk'!
+
+!X methodsFor: 'translated'!
+blah
+
+ self puts: { 'hello' }! !")
+
+ add_test("colon2", :flunk)
+ add_test("colon3", :flunk)
+
+ add_test("conditional1",
+ "[ 42 == 0 ] ifTrue: [ ^ 1 ]")
+
+ add_test("conditional2",
+ "[ 42 == 0 ] ifFalse: [ ^ 2 ]")
+
+ add_test("conditional3",
+ "[ 42 == 0 ] ifTrue: [ ^ 3 ] ifFalse: [ ^ 4 ]")
+
+ add_test("conditional4",
+ "[ 42 == 0 ] ifTrue: [ ^ 2 ] ifFalse: [ [ 42 < 0 ] ifTrue: [ ^ 3 ] ifFalse: [ ^ 4 ] ]")
+
+ add_test("conditional5",
+ "[ true ] ifFalse: [ [ false ] ifTrue: [ ^ ] ]")
+
+ add_test("const",
+ "X")
+ add_test("cvar", :flunk)
+ add_test("cvasgn", :flunk)
+ add_test("cvdecl", :flunk)
+ add_test("dasgn", :flunk)
+
+ unsupported_tests "defined"
+
+ add_test("defn_args",
+"x: args
+
+ | a b c d |
+
+ a := args removeFirst.
+ b := args removeFirst.
+ d := args removeLast.
+ c := args.
+ [ b isNil ] ifTrue: [ b := 42 ].
+
+ self p: { a. b. c. d }")
+
+ add_test("defn_empty",
+ "empty\n\n nil")
+
+ add_test("defn_is_something",
+ "is_something\n\n nil")
+
+ add_test("defn_or",
+ "| o\n\n nil")
+
+ add_test("defn_zarray",
+ "zarray\n\n a := {}.\n ^ a")
+
+ add_test("defs",
+ "x: y\n\n y + 1")
+
+ add_test("dmethod", :flunk)
+ add_test("dot2", :flunk)
+ add_test("dot3", :flunk)
+ add_test("dregx", :flunk)
+ add_test("dregx_once", :flunk)
+ add_test("dstr", :flunk)
+ add_test("dsym", :flunk)
+ add_test("dxstr", :flunk)
+ add_test("ensure", :flunk)
+
+ add_test("false",
+ "false")
+
+ add_test("fbody", :flunk)
+ add_test("fcall",
+ "self p: { 4 }")
+ add_test("flip2", :flunk)
+ add_test("flip3", :flunk)
+ add_test("for", :flunk)
+ add_test("gasgn", :flunk)
+ add_test("global", :flunk)
+ add_test("gvar", :flunk)
+ add_test("hash", :flunk)
+ add_test("iasgn", :flunk)
+
+ add_test("iteration1",
+ "self loop: [\n]")
+
+ add_test("iteration2", # FIX
+ "array := { 1. 2. 3 }.\narray each: [ :x |\n self puts: { x to_s }\n]")
+
+ add_test("iteration3",
+ "1 upto: { 3 } do: [ :n |\n self puts: { n to_s }\n]") # FIX
+
+ add_test("iteration4",
+ "3 downto: { 1 } do: [ :n |\n self puts: { n to_s }\n]") # FIX
+
+ add_test("iteration5",
+ "argl := 10.\n[ argl >= 1 ] whileTrue: [ self puts: { 'hello' }.\nargl := argl - 1 ]")
+
+ add_test("iteration6",
+ "array1 := { 1. 2. 3 }.
+array2 := { 4. 5. 6. 7 }.
+array1 each: [ :x |
+ array2 each: [ :y |
+ self puts: { x to_s }.
+ self puts: { y to_s }
+ ]
+]")
+
+ add_test("ivar", :flunk)
+
+ add_test("lasgn_array",
+ "var := { 'foo'. 'bar' }")
+
+ add_test("lasgn_call",
+ "c := 2 + 3")
+
+ add_test("lit_bool_false",
+ "false")
+
+ add_test("lit_bool_true",
+ "true")
+
+ add_test("lit_float",
+ "1.1")
+
+ add_test("lit_long",
+ "1")
+
+ unsupported_tests "lit_range2", "lit_range3"
+ # add_test("lit_range2", :flunk)
+ # add_test("lit_range3", :flunk)
+ add_test("lit_regexp", "'x'") # FIX
+ add_test("lit_str", "'x'")
+ add_test("lit_sym", "#x")
+ add_test("masgn", :flunk)
+ add_test("match", :flunk)
+ add_test("match2", :flunk)
+ add_test("match3", :flunk)
+ add_test("module", :flunk)
+ add_test("next", :flunk)
+ add_test("not", :flunk)
+ add_test("nth_ref", :flunk)
+ add_test("op_asgn1", :flunk)
+ add_test("op_asgn2", :flunk)
+ add_test("op_asgn_and", :flunk)
+ add_test("op_asgn_or", :flunk)
+ add_test("or",
+ "a | b")
+ add_test("postexe", :flunk)
+ add_test("redo", :flunk)
+ add_test("rescue_block", :flunk)
+ add_test("rescue_exceptions", :flunk)
+ add_test("retry", :flunk)
+ add_test("sclass", :flunk)
+ add_test("splat", :flunk)
+
+ add_test("super",
+ "x\n\n super x: { 4 }")
+
+ add_test("super_multi",
+ "x\n\n super x: { 4. 2. 1 }")
+
+ add_test("svalue", :flunk)
+ add_test("to_ary", :flunk)
+
+ add_test("true",
+ "true")
+
+ add_test("undef", :flunk)
+ add_test("undef_multi", :flunk)
+ add_test("until_post",
+ "[ 1 + 1. false ] whileFalse")
+ add_test("until_pre",
+ "[ false ] whileFalse: [ 1 + 1 ]")
+ add_test("valias", :flunk)
+ add_test("vcall",
+ "self method")
+ add_test("while_post",
+ "[ 1 + 1. false ] whileTrue")
+ add_test("while_pre",
+ "[ false ] whileTrue: [ 1 + 1 ]")
+ add_test("xstr", :flunk)
+ add_test("yield", :flunk)
+ add_test("yield_arg", :flunk)
+ add_test("yield_args", :flunk)
+
+ add_test("zarray",
+ "a := {}")
+
+ add_test("zsuper",
+ "x\n\n super x")
+end

0 comments on commit 1b84c8c

Please sign in to comment.