mirror of https://github.com/aamine/cbc
* net/loveruby/cflat/compiler/TypeChecker.java: transform pointer arithmetic to normal arithmetic (e.g. ptr+7 => ptr+(7*sizeof(*ptr))).
* net/loveruby/cflat/compiler/TypeChecker.java: int-ptr is invalid. * net/loveruby/cflat/compiler/CodeGenerator.java: implement pointer arithmetic correctly (e.g. *(ptr+1) for various types work now). * net/loveruby/cflat/ast/PrefixOpNode.java: inherit UnaryArithmeticOpNode. * net/loveruby/cflat/ast/SuffixOpNode.java: ditto. * net/loveruby/cflat/ast/UnaryArithmeticOpNode.java: new file. * net/loveruby/cflat/ast/CastNode.java: is not assignable. * net/loveruby/cflat/type/TypeTable.java: new method #pointerSize, #ptrDiffTypeRef. * net/loveruby/cflat/type/ArrayType.java: fetch pointer size from a TypeTable. * net/loveruby/cflat/type/ArrayType.java: we can cast any pointers to void*. * net/loveruby/cflat/type/PointerType.java: ditto. * net/loveruby/cflat/type/IntegerTypeRef.java (equals): check name equality by #equals, not ==. * test: test pointer arithmetic. * test: CastNode does not becomes LHS, do not check it. * import/string.hb: add mem* functions. git-svn-id: file:///Users/aamine/c/gitwork/public/cbc/trunk@3979 1b9489fe-b721-0410-924e-b54b9192deb8
This commit is contained in:
parent
0229e05728
commit
eca722f362
42
ChangeLog
42
ChangeLog
|
@ -1,3 +1,45 @@
|
|||
Sun Aug 31 19:24:30 2008 Minero Aoki <aamine@loveruby.net>
|
||||
|
||||
* net/loveruby/cflat/compiler/TypeChecker.java: transform pointer
|
||||
arithmetic to normal arithmetic (e.g. ptr+7 =>
|
||||
ptr+(7*sizeof(*ptr))).
|
||||
|
||||
* net/loveruby/cflat/compiler/TypeChecker.java: int-ptr is
|
||||
invalid.
|
||||
|
||||
* net/loveruby/cflat/compiler/CodeGenerator.java: implement
|
||||
pointer arithmetic correctly (e.g. *(ptr+1) for various types work
|
||||
now).
|
||||
|
||||
* net/loveruby/cflat/ast/PrefixOpNode.java: inherit
|
||||
UnaryArithmeticOpNode.
|
||||
|
||||
* net/loveruby/cflat/ast/SuffixOpNode.java: ditto.
|
||||
|
||||
* net/loveruby/cflat/ast/UnaryArithmeticOpNode.java: new file.
|
||||
|
||||
* net/loveruby/cflat/ast/CastNode.java: is not assignable.
|
||||
|
||||
* net/loveruby/cflat/type/TypeTable.java: new method #pointerSize,
|
||||
#ptrDiffTypeRef.
|
||||
|
||||
* net/loveruby/cflat/type/ArrayType.java: fetch pointer size from
|
||||
a TypeTable.
|
||||
|
||||
* net/loveruby/cflat/type/ArrayType.java: we can cast any pointers
|
||||
to void*.
|
||||
|
||||
* net/loveruby/cflat/type/PointerType.java: ditto.
|
||||
|
||||
* net/loveruby/cflat/type/IntegerTypeRef.java (equals): check name
|
||||
equality by #equals, not ==.
|
||||
|
||||
* test: test pointer arithmetic.
|
||||
|
||||
* test: CastNode does not becomes LHS, do not check it.
|
||||
|
||||
* import/string.hb: add mem* functions.
|
||||
|
||||
Sun Aug 31 15:17:05 2008 Minero Aoki <aamine@loveruby.net>
|
||||
|
||||
* net/loveruby/cflat/ast/Declarations.java: reject duplicated
|
||||
|
|
|
@ -16,4 +16,7 @@ extern char* strstr(char* str, char* pattern);
|
|||
extern size_t strspn(char* str, char* accept);
|
||||
extern size_t strcspn(char* str, char* reject);
|
||||
extern char* strerror(int errnum);
|
||||
extern char* strerror_r(int errnum, char* buf, size_t size);
|
||||
extern char* strerror_r(int errnum, char* buf, size_t len);
|
||||
extern void* memcpy(void* dest, void* src, size_t len);
|
||||
extern void* memccpy(void* dest, void* src, int c, size_t len);
|
||||
extern void* memmove(void* dest, void* src, size_t len);
|
||||
|
|
|
@ -35,10 +35,6 @@ public class CastNode extends ExprNode {
|
|||
return expr.isConstant();
|
||||
}
|
||||
|
||||
public boolean isAssignable() {
|
||||
return expr.isAssignable();
|
||||
}
|
||||
|
||||
public boolean isConstantAddress() {
|
||||
return expr.isConstantAddress();
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
package net.loveruby.cflat.ast;
|
||||
|
||||
public class PrefixOpNode extends UnaryOpNode {
|
||||
public class PrefixOpNode extends UnaryArithmeticOpNode {
|
||||
public PrefixOpNode(String op, ExprNode expr) {
|
||||
super(op, expr);
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
package net.loveruby.cflat.ast;
|
||||
|
||||
public class SuffixOpNode extends UnaryOpNode {
|
||||
public class SuffixOpNode extends UnaryArithmeticOpNode {
|
||||
public SuffixOpNode(String op, ExprNode expr) {
|
||||
super(op, expr);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
package net.loveruby.cflat.ast;
|
||||
|
||||
public class UnaryArithmeticOpNode extends UnaryOpNode {
|
||||
protected long amount;
|
||||
|
||||
public UnaryArithmeticOpNode(String op, ExprNode expr) {
|
||||
super(op, expr);
|
||||
amount = 1;
|
||||
}
|
||||
|
||||
public long amount() {
|
||||
return this.amount;
|
||||
}
|
||||
|
||||
public void setAmount(long amount) {
|
||||
this.amount = amount;
|
||||
}
|
||||
}
|
|
@ -643,34 +643,38 @@ static public void p(String s) { System.err.println(s); }
|
|||
}
|
||||
|
||||
public void visit(PrefixOpNode node) {
|
||||
compileIncDec(node.operator(), node.expr());
|
||||
load(node.expr().type(), node.expr().address(), reg("ax"));
|
||||
AsmEntity dest = compileLHS2(node.expr());
|
||||
load(node.expr().type(), dest, reg("ax"));
|
||||
compileUnaryArithmetic(node, reg("ax"));
|
||||
save(node.expr().type(), reg("ax"), dest);
|
||||
}
|
||||
|
||||
public void visit(SuffixOpNode node) {
|
||||
load(node.expr().type(), node.expr().address(), reg("ax"));
|
||||
compileIncDec(node.operator(), node.expr());
|
||||
AsmEntity dest = compileLHS2(node.expr());
|
||||
load(node.expr().type(), dest, reg("ax"));
|
||||
compileUnaryArithmetic(node, dest);
|
||||
}
|
||||
|
||||
protected void compileIncDec(String op, ExprNode e) {
|
||||
if (op.equals("++")) {
|
||||
if (e.type().isInteger()) {
|
||||
inc(e.type(), e.address());
|
||||
}
|
||||
else {
|
||||
add(imm(e.type().size()), e.address());
|
||||
}
|
||||
}
|
||||
else if (op.equals("--")) {
|
||||
if (e.type().isInteger()) {
|
||||
dec(e.type(), e.address());
|
||||
}
|
||||
else {
|
||||
sub(imm(e.type().size()), e.address());
|
||||
}
|
||||
protected AsmEntity compileLHS2(ExprNode expr) {
|
||||
if (expr.isConstantAddress()) {
|
||||
return expr.address();
|
||||
}
|
||||
else {
|
||||
throw new Error("unknown unary operator: " + op);
|
||||
compileLHS(expr);
|
||||
return baseptr();
|
||||
}
|
||||
}
|
||||
|
||||
protected void compileUnaryArithmetic(UnaryArithmeticOpNode node,
|
||||
AsmEntity dest) {
|
||||
if (node.operator().equals("++")) {
|
||||
add(imm(node.amount()), dest);
|
||||
}
|
||||
else if (node.operator().equals("--")) {
|
||||
sub(imm(node.amount()), dest);
|
||||
}
|
||||
else {
|
||||
throw new Error("unknown unary operator: " + node.operator());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -722,7 +726,6 @@ static public void p(String s) { System.err.println(s); }
|
|||
save(node.type(), reg("ax"), node.lhs().address());
|
||||
}
|
||||
|
||||
// FIXME: use -4(%edx,%esi,4) addressing
|
||||
public void visit(ArefNode node) {
|
||||
if (node.expr().type().isPointerAlike()) {
|
||||
compile(node.expr());
|
||||
|
@ -767,9 +770,8 @@ static public void p(String s) { System.err.println(s); }
|
|||
}
|
||||
|
||||
protected void compileLHS(Node node) {
|
||||
comment("compileLHS: " + node.getClass().getName() + " {");
|
||||
comment("compileLHS: " + node.getClass().getSimpleName() + " {");
|
||||
if (node instanceof VariableNode) {
|
||||
// FIXME: support static variables
|
||||
VariableNode n = (VariableNode)node;
|
||||
lea(n.address(), baseptr());
|
||||
}
|
||||
|
@ -810,33 +812,6 @@ comment("compileLHS: " + node.getClass().getName() + " {");
|
|||
mov(reg("ax"), baseptr());
|
||||
pop(reg("ax"));
|
||||
}
|
||||
else if (node instanceof PrefixOpNode) {
|
||||
PrefixOpNode n = (PrefixOpNode)node;
|
||||
compileLHS(n.expr());
|
||||
if (n.operator().equals("++")) {
|
||||
add(imm(n.expr().type().size()), mem(baseptr()));
|
||||
add(imm(n.expr().type().size()), baseptr());
|
||||
}
|
||||
else {
|
||||
sub(imm(n.expr().type().size()), mem(baseptr()));
|
||||
sub(imm(n.expr().type().size()), baseptr());
|
||||
}
|
||||
}
|
||||
else if (node instanceof SuffixOpNode) {
|
||||
SuffixOpNode n = (SuffixOpNode)node;
|
||||
compileLHS(n.expr());
|
||||
if (n.operator().equals("++")) {
|
||||
add(imm(n.expr().type().size()), baseptr());
|
||||
}
|
||||
else {
|
||||
sub(imm(n.expr().type().size()), baseptr());
|
||||
}
|
||||
}
|
||||
else if (node instanceof CastNode) {
|
||||
CastNode n = (CastNode)node;
|
||||
compileLHS(n.expr());
|
||||
// FIXME: cast here
|
||||
}
|
||||
else {
|
||||
throw new Error("wrong type for compileLHS: " + node.getClass().getName());
|
||||
}
|
||||
|
|
|
@ -152,10 +152,7 @@ class TypeChecker extends Visitor {
|
|||
|| node.operator().equals("-")) {
|
||||
if (node.lhs().type().isPointer()) {
|
||||
if (! mustBeInteger(node.rhs(), node.operator())) return;
|
||||
Type t = integralPromotion(node.rhs().type());
|
||||
if (! t.isSameType(node.rhs().type())) {
|
||||
node.setRHS(new CastNode(t, node.rhs()));
|
||||
}
|
||||
node.setRHS(multiplyPtrBaseSize(node.rhs(), node.lhs()));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -276,14 +273,30 @@ class TypeChecker extends Visitor {
|
|||
* * integer + integer
|
||||
* * pointer + integer
|
||||
* * integer + pointer
|
||||
* * integer - integer
|
||||
* * pointer - integer
|
||||
*/
|
||||
protected Type expectsSameIntegerOrPointerDiff(BinaryOpNode node) {
|
||||
if (node.left().type().isPointer()) {
|
||||
if (node.left().type().isDereferable()) {
|
||||
if (node.left().type().baseType().isVoid()) {
|
||||
wrongTypeError(node.left(), node.operator());
|
||||
return null;
|
||||
}
|
||||
mustBeInteger(node.right(), node.operator());
|
||||
node.setRight(multiplyPtrBaseSize(node.right(), node.left()));
|
||||
return node.left().type();
|
||||
}
|
||||
else if (node.right().type().isPointer()) {
|
||||
else if (node.right().type().isDereferable()) {
|
||||
if (node.operator().equals("-")) {
|
||||
error(node, "invalid operation integer-pointer");
|
||||
return null;
|
||||
}
|
||||
if (node.right().type().baseType().isVoid()) {
|
||||
wrongTypeError(node.right(), node.operator());
|
||||
return null;
|
||||
}
|
||||
mustBeInteger(node.left(), node.operator());
|
||||
node.setLeft(multiplyPtrBaseSize(node.left(), node.right()));
|
||||
return node.right().type();
|
||||
}
|
||||
else {
|
||||
|
@ -291,6 +304,36 @@ class TypeChecker extends Visitor {
|
|||
}
|
||||
}
|
||||
|
||||
protected BinaryOpNode multiplyPtrBaseSize(ExprNode expr, ExprNode ptr) {
|
||||
return new BinaryOpNode(integralPromotedExpr(expr), "*", ptrBaseSize(ptr));
|
||||
}
|
||||
|
||||
protected ExprNode integralPromotedExpr(ExprNode expr) {
|
||||
Type t = integralPromotion(expr.type());
|
||||
if (t.isSameType(expr.type())) {
|
||||
return expr;
|
||||
}
|
||||
else {
|
||||
return new CastNode(t, expr);
|
||||
}
|
||||
}
|
||||
|
||||
protected IntegerLiteralNode ptrBaseSize(ExprNode ptr) {
|
||||
return integerLiteral(ptr.location(),
|
||||
typeTable.ptrDiffTypeRef(),
|
||||
ptr.type().baseType().size());
|
||||
}
|
||||
|
||||
protected IntegerLiteralNode integerLiteral(Location loc, TypeRef ref, long n) {
|
||||
IntegerLiteralNode node = new IntegerLiteralNode(loc, ref, n);
|
||||
bindType(node.typeNode());
|
||||
return node;
|
||||
}
|
||||
|
||||
protected void bindType(TypeNode t) {
|
||||
t.setType(typeTable.get(t.typeRef()));
|
||||
}
|
||||
|
||||
// +, -, *, /, %, &, |, ^, <<, >>
|
||||
// #@@range/expectsSameInteger{
|
||||
protected Type expectsSameInteger(BinaryOpNode node) {
|
||||
|
@ -304,12 +347,12 @@ class TypeChecker extends Visitor {
|
|||
protected Type expectsComparableScalars(BinaryOpNode node) {
|
||||
if (! mustBeScalar(node.left(), node.operator())) return null;
|
||||
if (! mustBeScalar(node.right(), node.operator())) return null;
|
||||
if (node.left().type().isPointer()) {
|
||||
if (node.left().type().isDereferable()) {
|
||||
ExprNode right = forcePointerType(node.left(), node.right());
|
||||
node.setRight(right);
|
||||
return node.left().type();
|
||||
}
|
||||
if (node.right().type().isPointer()) {
|
||||
if (node.right().type().isDereferable()) {
|
||||
ExprNode left = forcePointerType(node.right(), node.left());
|
||||
node.setLeft(left);
|
||||
return node.right().type();
|
||||
|
@ -371,7 +414,7 @@ class TypeChecker extends Visitor {
|
|||
expectsScalarLHS(node);
|
||||
}
|
||||
|
||||
protected void expectsScalarLHS(UnaryOpNode node) {
|
||||
protected void expectsScalarLHS(UnaryArithmeticOpNode node) {
|
||||
if (node.expr().isParameter()) {
|
||||
// parameter is always a scalar.
|
||||
}
|
||||
|
@ -388,6 +431,18 @@ class TypeChecker extends Visitor {
|
|||
if (! node.expr().type().isSameType(opType)) {
|
||||
node.setOpType(opType);
|
||||
}
|
||||
node.setAmount(1);
|
||||
}
|
||||
else if (node.expr().type().isDereferable()) {
|
||||
if (node.expr().type().baseType().isVoid()) {
|
||||
// We cannot increment/decrement void*
|
||||
wrongTypeError(node.expr(), node.operator());
|
||||
return;
|
||||
}
|
||||
node.setAmount(node.expr().type().baseType().size());
|
||||
}
|
||||
else {
|
||||
throw new Error("must not happen");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -3,16 +3,17 @@ package net.loveruby.cflat.type;
|
|||
public class ArrayType extends Type {
|
||||
protected Type baseType;
|
||||
protected long length;
|
||||
protected long pointerSize;
|
||||
static final protected long undefined = -1;
|
||||
|
||||
public ArrayType(Type baseType) {
|
||||
this.baseType = baseType;
|
||||
length = undefined;
|
||||
public ArrayType(Type baseType, long pointerSize) {
|
||||
this(baseType, undefined, pointerSize);
|
||||
}
|
||||
|
||||
public ArrayType(Type baseType, long length) {
|
||||
public ArrayType(Type baseType, long length, long pointerSize) {
|
||||
this.baseType = baseType;
|
||||
this.length = length;
|
||||
this.pointerSize = pointerSize;
|
||||
}
|
||||
|
||||
public boolean isArray() { return true; }
|
||||
|
@ -32,7 +33,7 @@ public class ArrayType extends Type {
|
|||
}
|
||||
|
||||
public long size() {
|
||||
return 4; // FIXME: get from TypeTable
|
||||
return pointerSize;
|
||||
}
|
||||
|
||||
public long allocSize() {
|
||||
|
@ -59,6 +60,9 @@ public class ArrayType extends Type {
|
|||
|
||||
public boolean isCompatible(Type target) {
|
||||
if (! target.isDereferable()) return false;
|
||||
if (target.baseType().isVoid()) {
|
||||
return true;
|
||||
}
|
||||
return baseType.isCompatible(target.baseType())
|
||||
&& baseType.size() == target.baseType().size();
|
||||
}
|
||||
|
|
|
@ -84,7 +84,7 @@ public class IntegerTypeRef extends TypeRef {
|
|||
public boolean equals(Object other) {
|
||||
if (! (other instanceof IntegerTypeRef)) return false;
|
||||
IntegerTypeRef ref = (IntegerTypeRef)other;
|
||||
return name == ref.name;
|
||||
return name.equals(ref.name);
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
|
|
|
@ -35,7 +35,10 @@ public class PointerType extends Type {
|
|||
|
||||
public boolean isCompatible(Type other) {
|
||||
if (! other.isDereferable()) return false;
|
||||
if (baseType.isVoid() && ! other.baseType().isPointer()) {
|
||||
if (baseType.isVoid()) {
|
||||
return true;
|
||||
}
|
||||
if (other.baseType().isVoid()) {
|
||||
return true;
|
||||
}
|
||||
return baseType.isCompatible(other.baseType());
|
||||
|
|
|
@ -70,7 +70,9 @@ public class TypeTable {
|
|||
}
|
||||
else if (ref instanceof ArrayTypeRef) {
|
||||
ArrayTypeRef aref = (ArrayTypeRef)ref;
|
||||
Type t = new ArrayType(get(aref.baseType()), aref.length());
|
||||
Type t = new ArrayType(get(aref.baseType()),
|
||||
aref.length(),
|
||||
pointerSize);
|
||||
table.put(aref, t);
|
||||
return t;
|
||||
}
|
||||
|
@ -86,6 +88,22 @@ public class TypeTable {
|
|||
return type;
|
||||
}
|
||||
|
||||
public long pointerSize() {
|
||||
return this.pointerSize;
|
||||
}
|
||||
|
||||
// returns a IntegerTypeRef whose size is equals to pointer.
|
||||
public TypeRef ptrDiffTypeRef() {
|
||||
return new IntegerTypeRef(ptrDiffTypeName());
|
||||
}
|
||||
|
||||
protected String ptrDiffTypeName() {
|
||||
if (signedLong().size == pointerSize) return "long";
|
||||
if (signedInt().size == pointerSize) return "int";
|
||||
if (signedShort().size == pointerSize) return "short";
|
||||
throw new Error("must not happen: integer.size != pointer.size");
|
||||
}
|
||||
|
||||
public Iterator types() {
|
||||
return table.values().iterator();
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import stdio;
|
||||
import string;
|
||||
|
||||
int global_x = 0;
|
||||
int common_x;
|
||||
|
@ -13,16 +14,22 @@ main(int argc, char **argv)
|
|||
int x, y;
|
||||
static int static_x = 0;
|
||||
static int scomm_x;
|
||||
int*[1] ptrs;
|
||||
int*[2] ptrs;
|
||||
struct s s;
|
||||
int* ptr;
|
||||
int[8] integers;
|
||||
char[8] buf;
|
||||
char *p = buf;
|
||||
memcpy(buf, "Hello", 8);
|
||||
|
||||
// local variable
|
||||
x = 1;
|
||||
y = 1;
|
||||
printf("%d", x);
|
||||
|
||||
x = 77;
|
||||
y = 77;
|
||||
x = y = 2;
|
||||
printf("%d;%d", x, y);
|
||||
printf(";%d;%d", x, y);
|
||||
|
||||
// parameter
|
||||
argc = 3;
|
||||
|
@ -60,6 +67,16 @@ main(int argc, char **argv)
|
|||
*ptr++ = 11;
|
||||
printf(";%d;%d", integers[0], integers[1]);
|
||||
|
||||
// local array with pointer arighmetic
|
||||
*(p + 1) = 'S';
|
||||
printf(";%c", p[1]);
|
||||
|
||||
// local array with pointer arighmetic
|
||||
ptrs[0] = NULL;
|
||||
ptrs[1] = &x;
|
||||
**(ptrs + 1) = 12;
|
||||
printf(";%d", *ptrs[1]);
|
||||
|
||||
puts("");
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -1,15 +0,0 @@
|
|||
import stdio;
|
||||
|
||||
int
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
int i;
|
||||
int* ptr = &i;
|
||||
|
||||
(int)i = 666;
|
||||
printf("%d", i);
|
||||
*(int*)ptr = 777;
|
||||
printf(";%d", *ptr);
|
||||
puts("");
|
||||
return 0;
|
||||
}
|
|
@ -26,7 +26,38 @@ main(int argc, char **argv)
|
|||
printf(";%d", i); // 1
|
||||
i <<= 2;
|
||||
printf(";%d", i); // 4
|
||||
puts("");
|
||||
|
||||
// pointer diff arithmetic (size=1)
|
||||
{
|
||||
char *string = "Hello, World!";
|
||||
char *p;
|
||||
|
||||
p = string;
|
||||
p += 1;
|
||||
printf(";%c", *p);
|
||||
|
||||
p -= 1;
|
||||
printf(";%c", *p);
|
||||
}
|
||||
|
||||
// pointer diff arithmetic (size=4)
|
||||
{
|
||||
int[4] xs;
|
||||
int* p;
|
||||
|
||||
xs[0] = 75;
|
||||
xs[1] = 76;
|
||||
xs[2] = 77;
|
||||
xs[3] = 78;
|
||||
|
||||
p = xs;
|
||||
p += 1;
|
||||
printf(";%d", *p);
|
||||
|
||||
p -= 1;
|
||||
printf(";%d", *p);
|
||||
}
|
||||
|
||||
puts("");
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -92,8 +92,8 @@ test_09_cmp() {
|
|||
}
|
||||
|
||||
test_10_assign() {
|
||||
assert_out "2;2;3;4;5;6;7;8;8;9;10;11;777;S" ./assign
|
||||
assert_out "3;4;3;12;4;1;1;7;5;1;4" ./opassign
|
||||
assert_out "1;2;2;3;4;5;6;7;8;8;9;10;11;777;S;12" ./assign
|
||||
assert_out "3;4;3;12;4;1;1;7;5;1;4;e;H;76;75" ./opassign
|
||||
assert_out "0;1;2;2;3;3;4" ./inc
|
||||
assert_out "4;3;2;2;1;1;0" ./dec
|
||||
}
|
||||
|
@ -207,7 +207,6 @@ test_23_limits() {
|
|||
test_24_cast() {
|
||||
assert_out "25000000" ./cast
|
||||
assert_out "777;666" ./cast2
|
||||
assert_out "666;777" ./cast3
|
||||
}
|
||||
|
||||
test_25_block() {
|
||||
|
|
Loading…
Reference in New Issue