std: str 과 list, 그리고 자동 drop 이 사용자 타입까지 닿는다
std.str 은 eq/starts_with/find/trim/parse_int 을 바이트 슬라이스 위에서 한다. std.list 는 자라는 제네릭 시퀀스다 -- 버퍼를 소유하므로 리스트를 놓으면 원소도 놓인다. 성장은 두 배씩이라 push 당 복사량이 상수로 눌린다. 찾은 버그 넷: - 메서드가 자기 타입의 유닛이 아니라 호출한 유닛에 속한 것으로 계산됐다. 다른 유닛의 제네릭 타입을 쓰면 필드가 전부 private 으로 보였다. - 참조로 도달한 메서드를 찾지 못했다. self.grow() 가 안 됐다. - 이미 참조인 수신자의 주소를 한 번 더 떠서 넘겼다. 포인터의 포인터를 받은 메서드가 그것을 구조체로 읽었다. - 유닛으로 한정된 제네릭 타입(list.List(i32))이 타입 자리에서도 식 자리에서도 해석되지 않았다. drop 을 가진 타입은 인스턴스마다 그 메서드가 존재해야 한다 -- 이름으로 부르는 사람이 없어도 스코프 정리가 부른다. 그리고 자기 drop 안에서는 필드를 꺼낼 수 있다. 객체가 사라지는 중이라 뒤에 남아 읽힐 것이 없다. run.py 207/207, exec.py 19/19.
This commit is contained in:
+92
-9
@@ -70,9 +70,27 @@ static int known(FeType *t)
|
|||||||
return t && t->kind != FE_TYPE_UNKNOWN && t->kind != FE_TYPE_ERROR;
|
return t && t->kind != FE_TYPE_UNKNOWN && t->kind != FE_TYPE_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Is this a projection of `self` inside that type's own `drop`? */
|
||||||
|
static int in_own_drop(FeCheckerState *s, FeNode *n)
|
||||||
|
{
|
||||||
|
FeNode *base;
|
||||||
|
if (!s->fn_node || !s->fn_node->text || strcmp(s->fn_node->text,"drop")!=0)
|
||||||
|
return 0;
|
||||||
|
base = n ? n->a : 0;
|
||||||
|
while (base && (base->kind==FE_N_MEMBER || base->kind==FE_N_INDEX))
|
||||||
|
base = base->a;
|
||||||
|
return base && base->kind==FE_N_IDENT && base->text &&
|
||||||
|
strcmp(base->text,"self")==0;
|
||||||
|
}
|
||||||
|
|
||||||
static void mark_moved(FeCheckerState *s, FeNode *n, FeType *t)
|
static void mark_moved(FeCheckerState *s, FeNode *n, FeType *t)
|
||||||
{
|
{
|
||||||
FeSym *sym=0;
|
FeSym *sym=0;
|
||||||
|
/* Inside a type's own `drop` the object is going away, so taking a field
|
||||||
|
out of it leaves nothing behind that anyone could read. That is the one
|
||||||
|
place R7 has nothing to protect. */
|
||||||
|
if (n && (n->kind==FE_N_MEMBER || n->kind==FE_N_INDEX) && in_own_drop(s,n))
|
||||||
|
return;
|
||||||
if (n && n->kind==FE_N_IDENT)
|
if (n && n->kind==FE_N_IDENT)
|
||||||
sym=find_symbol(s->scope,n->text ? n->text : "");
|
sym=find_symbol(s->scope,n->text ? n->text : "");
|
||||||
if (s->defer_depth != 0) {
|
if (s->defer_depth != 0) {
|
||||||
@@ -1283,6 +1301,12 @@ static FeType *check_expr_core(FeCheckerState *s, FeNode *n)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
et=check_expr(s,n->a->a);
|
et=check_expr(s,n->a->a);
|
||||||
|
/* A method can be reached through a reference or an owner as well
|
||||||
|
as through the value itself. */
|
||||||
|
if (et && (et->kind==FE_TYPE_REF || et->kind==FE_TYPE_OWNED) &&
|
||||||
|
et->elem && et->elem->kind==FE_TYPE_STRUCT &&
|
||||||
|
find_method(c,et->elem,n->a->b ? n->a->b->text : ""))
|
||||||
|
et=et->elem;
|
||||||
method=et && et->kind==FE_TYPE_STRUCT ?
|
method=et && et->kind==FE_TYPE_STRUCT ?
|
||||||
find_method(c,et,n->a->b ? n->a->b->text : "") : 0;
|
find_method(c,et,n->a->b ? n->a->b->text : "") : 0;
|
||||||
if(method) {
|
if(method) {
|
||||||
@@ -2361,6 +2385,23 @@ static FeType *build_struct_instance(FeCheck *c, FeUnit *home, FeNode *decl,
|
|||||||
pop_bindings(c,&save);
|
pop_bindings(c,&save);
|
||||||
}
|
}
|
||||||
fe_type_layout_all(&c->types);
|
fe_type_layout_all(&c->types);
|
||||||
|
/* A type that says how to let go of itself needs that method to exist for
|
||||||
|
every instance, whether or not anyone calls it by name: scope cleanup
|
||||||
|
will. */
|
||||||
|
{
|
||||||
|
FeNode *release;
|
||||||
|
for (release=decl->children;release;release=release->next)
|
||||||
|
if (release->kind==FE_N_FN && release->text &&
|
||||||
|
!strcmp(release->text,"drop") && release->c) {
|
||||||
|
FeCheckerState s;
|
||||||
|
memset(&s,0,sizeof s);
|
||||||
|
s.c=c;
|
||||||
|
s.scope=c->unit_scope[unit_index(c,home)];
|
||||||
|
s.globals=s.scope;
|
||||||
|
check_instance_method(&s,t,release,decl->loc,0);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
return t;
|
return t;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2391,18 +2432,30 @@ static FeType *instantiate_type_node(void *owner, const FeNode *node)
|
|||||||
{
|
{
|
||||||
FeCheck *c=(FeCheck *)owner;
|
FeCheck *c=(FeCheck *)owner;
|
||||||
FeUnit *home=current_unit(c);
|
FeUnit *home=current_unit(c);
|
||||||
|
const char *name=node->text;
|
||||||
FeNode *arg;
|
FeNode *arg;
|
||||||
FeType *args[FE_TYPE_PARAM_MAX];
|
FeType *args[FE_TYPE_PARAM_MAX];
|
||||||
unsigned count=0;
|
unsigned count=0;
|
||||||
FeType *result;
|
FeType *result;
|
||||||
|
/* `binding.Name` names a type in another unit. The binding is not itself a
|
||||||
|
type, so it has to be peeled off before anything is looked up. */
|
||||||
|
if (node->a && node->a->kind==FE_N_IDENT && node->a->text && c->build &&
|
||||||
|
c->unit) {
|
||||||
|
FeUnit *bound=fe_build_binding(c->build,c->unit,node->text);
|
||||||
|
if (bound) { home=bound; name=node->a->text; }
|
||||||
|
}
|
||||||
if (!node->children) {
|
if (!node->children) {
|
||||||
/* A generic declaration is not a type until it has its arguments. */
|
FeNode *decl=unit_type_decl(c,home,name ? name : "");
|
||||||
FeNode *decl=unit_type_decl(c,home,node->text ? node->text : "");
|
|
||||||
if (decl && decl_is_generic(decl)) {
|
if (decl && decl_is_generic(decl)) {
|
||||||
|
/* A generic declaration is not a type until it has arguments. */
|
||||||
err(c,node->loc,"generic type requires type arguments");
|
err(c,node->loc,"generic type requires type arguments");
|
||||||
return unknown(c);
|
return unknown(c);
|
||||||
}
|
}
|
||||||
return fe_type_intern(&c->types,node->text);
|
if (name!=node->text) {
|
||||||
|
FeType *there=unit_type(c,home,name);
|
||||||
|
if (there) return there;
|
||||||
|
}
|
||||||
|
return fe_type_intern(&c->types,name);
|
||||||
}
|
}
|
||||||
if (!instance_descend(c,node->loc)) return unknown(c);
|
if (!instance_descend(c,node->loc)) return unknown(c);
|
||||||
for (arg=node->children;arg;arg=arg->next) {
|
for (arg=node->children;arg;arg=arg->next) {
|
||||||
@@ -2415,7 +2468,7 @@ static FeType *instantiate_type_node(void *owner, const FeNode *node)
|
|||||||
--c->instance_depth;
|
--c->instance_depth;
|
||||||
return unknown(c);
|
return unknown(c);
|
||||||
}
|
}
|
||||||
result=instantiate_struct(c,home,node->text ? node->text : "",args,count,
|
result=instantiate_struct(c,home,name ? name : "",args,count,
|
||||||
node->loc);
|
node->loc);
|
||||||
--c->instance_depth;
|
--c->instance_depth;
|
||||||
return result;
|
return result;
|
||||||
@@ -2448,13 +2501,27 @@ static FeType *type_from_expr(FeCheckerState *s, FeNode *n, int *ok)
|
|||||||
if (t && t->kind!=FE_TYPE_UNKNOWN) { *ok=1; return t; }
|
if (t && t->kind!=FE_TYPE_UNKNOWN) { *ok=1; return t; }
|
||||||
return unknown(c);
|
return unknown(c);
|
||||||
}
|
}
|
||||||
if (n->kind==FE_N_CALL && n->a && n->a->kind==FE_N_IDENT && n->a->text) {
|
if (n->kind==FE_N_CALL && n->a &&
|
||||||
|
(n->a->kind==FE_N_IDENT ||
|
||||||
|
(n->a->kind==FE_N_MEMBER && n->a->a &&
|
||||||
|
n->a->a->kind==FE_N_IDENT && n->a->b && n->a->b->text))) {
|
||||||
FeType *args[FE_TYPE_PARAM_MAX];
|
FeType *args[FE_TYPE_PARAM_MAX];
|
||||||
unsigned count=0;
|
unsigned count=0;
|
||||||
FeNode *arg;
|
FeNode *arg;
|
||||||
FeType *result;
|
FeType *result;
|
||||||
FeUnit *home=current_unit(c);
|
FeUnit *home=current_unit(c);
|
||||||
if (!unit_type_decl(c,home,n->a->text)) return unknown(c);
|
const char *want;
|
||||||
|
/* `Name(args)` here, `binding.Name(args)` when the declaration is in
|
||||||
|
another unit. */
|
||||||
|
if (n->a->kind==FE_N_MEMBER) {
|
||||||
|
FeUnit *bound=binding_unit(s,n->a->a);
|
||||||
|
if (!bound) return unknown(c);
|
||||||
|
home=bound;
|
||||||
|
want=n->a->b->text;
|
||||||
|
} else {
|
||||||
|
want=n->a->text;
|
||||||
|
}
|
||||||
|
if (!want || !unit_type_decl(c,home,want)) return unknown(c);
|
||||||
if (!instance_descend(c,n->loc)) { *ok=1; return unknown(c); }
|
if (!instance_descend(c,n->loc)) { *ok=1; return unknown(c); }
|
||||||
for (arg=n->children;arg;arg=arg->next) {
|
for (arg=n->children;arg;arg=arg->next) {
|
||||||
int inner=0;
|
int inner=0;
|
||||||
@@ -2464,7 +2531,7 @@ static FeType *type_from_expr(FeCheckerState *s, FeNode *n, int *ok)
|
|||||||
++count;
|
++count;
|
||||||
}
|
}
|
||||||
if (count>FE_TYPE_PARAM_MAX) { --c->instance_depth; return unknown(c); }
|
if (count>FE_TYPE_PARAM_MAX) { --c->instance_depth; return unknown(c); }
|
||||||
result=instantiate_struct(c,home,n->a->text,args,count,n->loc);
|
result=instantiate_struct(c,home,want,args,count,n->loc);
|
||||||
--c->instance_depth;
|
--c->instance_depth;
|
||||||
*ok=1;
|
*ok=1;
|
||||||
return result;
|
return result;
|
||||||
@@ -2578,17 +2645,30 @@ static FeType *check_generic_call(FeCheckerState *s, FeNode *n, FeSym *sym,
|
|||||||
|
|
||||||
/* `Type.method(...)` where Type is a generic instance and the method takes no
|
/* `Type.method(...)` where Type is a generic instance and the method takes no
|
||||||
self parameter. */
|
self parameter. */
|
||||||
|
/* The unit a name belongs to, by name. */
|
||||||
|
static FeUnit *unit_named(FeCheck *c, const char *name)
|
||||||
|
{
|
||||||
|
unsigned u;
|
||||||
|
if (!name) return 0;
|
||||||
|
for (u=0;u<c->build->count;++u)
|
||||||
|
if (!strcmp(c->build->units[u].name,name)) return &c->build->units[u];
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static FeType *check_static_method_call(FeCheckerState *s, FeNode *n,
|
static FeType *check_static_method_call(FeCheckerState *s, FeNode *n,
|
||||||
FeType *owner, FeNode *method)
|
FeType *owner, FeNode *method)
|
||||||
{
|
{
|
||||||
FeCheck *c=s->c;
|
FeCheck *c=s->c;
|
||||||
FeUnit *home=current_unit(c);
|
/* A method belongs to the unit that declared its type, not to whichever
|
||||||
|
unit happens to be calling it. */
|
||||||
|
FeUnit *home=unit_named(c,owner ? owner->unit : 0);
|
||||||
FeBindSave save;
|
FeBindSave save;
|
||||||
FeType *result;
|
FeType *result;
|
||||||
char key[FE_GENERIC_KEY_MAX];
|
char key[FE_GENERIC_KEY_MAX];
|
||||||
FeType *self_args[1];
|
FeType *self_args[1];
|
||||||
int fresh;
|
int fresh;
|
||||||
FeSym fake;
|
FeSym fake;
|
||||||
|
if (!home) home=current_unit(c);
|
||||||
self_args[0]=owner;
|
self_args[0]=owner;
|
||||||
instance_key(key,home->name,method->text,self_args,1);
|
instance_key(key,home->name,method->text,self_args,1);
|
||||||
memset(&fake,0,sizeof fake);
|
memset(&fake,0,sizeof fake);
|
||||||
@@ -2618,11 +2698,14 @@ static void check_instance_method(FeCheckerState *s, FeType *owner,
|
|||||||
FeNode *method, FeLoc site, FeNode *call)
|
FeNode *method, FeLoc site, FeNode *call)
|
||||||
{
|
{
|
||||||
FeCheck *c=s->c;
|
FeCheck *c=s->c;
|
||||||
FeUnit *home=current_unit(c);
|
/* A method belongs to the unit that declared its type, not to whichever
|
||||||
|
unit happens to be calling it. */
|
||||||
|
FeUnit *home=unit_named(c,owner ? owner->unit : 0);
|
||||||
FeBindSave save;
|
FeBindSave save;
|
||||||
char key[FE_GENERIC_KEY_MAX];
|
char key[FE_GENERIC_KEY_MAX];
|
||||||
FeType *self_args[1];
|
FeType *self_args[1];
|
||||||
self_args[0]=owner;
|
self_args[0]=owner;
|
||||||
|
if (!home) home=current_unit(c);
|
||||||
instance_key(key,home->name,method->text,self_args,1);
|
instance_key(key,home->name,method->text,self_args,1);
|
||||||
{
|
{
|
||||||
FeBindSave probe;
|
FeBindSave probe;
|
||||||
|
|||||||
+38
-3
@@ -687,9 +687,17 @@ static Slot lower_call(Lower *L, FeNode *n)
|
|||||||
if (n->a && n->a->kind == FE_N_MEMBER && n->sem_decl) {
|
if (n->a && n->a->kind == FE_N_MEMBER && n->sem_decl) {
|
||||||
FeNode *first = n->sem_decl->a ? n->sem_decl->a->children : 0;
|
FeNode *first = n->sem_decl->a ? n->sem_decl->a->children : 0;
|
||||||
if (first && first->text && !strcmp(first->text, "self")) {
|
if (first && first->text && !strcmp(first->text, "self")) {
|
||||||
|
FeType *rt = n->a->a ? n->a->a->sem_type : 0;
|
||||||
Slot recv = lower_expr(L, n->a->a);
|
Slot recv = lower_expr(L, n->a->a);
|
||||||
args[count++] = recv.is_place ? as_address(L, recv, n->a->a)
|
/* A receiver that is already a reference or an owner is a pointer
|
||||||
: recv.temp;
|
already; taking its address would pass a pointer to the
|
||||||
|
pointer. */
|
||||||
|
if (rt && (rt->kind == FE_TYPE_REF ||
|
||||||
|
(rt->kind == FE_TYPE_OWNED && ir_type(rt) == FE_IR_PTR)))
|
||||||
|
args[count++] = as_value(L, recv, n->a->a);
|
||||||
|
else
|
||||||
|
args[count++] = recv.is_place ? as_address(L, recv, n->a->a)
|
||||||
|
: recv.temp;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/* A generic call passes its type arguments first. They were consumed when
|
/* A generic call passes its type arguments first. They were consumed when
|
||||||
@@ -990,6 +998,24 @@ static Slot lower_expr_core(Lower *L, FeNode *n)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* The link name of the `drop` method for this type, found through the instance
|
||||||
|
the checker recorded. */
|
||||||
|
static const char *drop_name(Lower *L, const FeType *t)
|
||||||
|
{
|
||||||
|
unsigned i;
|
||||||
|
FeNode *method = 0;
|
||||||
|
if (!t || !t->decl_node) return 0;
|
||||||
|
for (method = t->decl_node->children; method; method = method->next)
|
||||||
|
if (method->kind == FE_N_FN && method->text &&
|
||||||
|
!strcmp(method->text, "drop")) break;
|
||||||
|
if (!method) return 0;
|
||||||
|
for (i = 0; i < L->c->instance_count; ++i)
|
||||||
|
if (L->c->instances[i].decl == method &&
|
||||||
|
L->c->instances[i].owner == t)
|
||||||
|
return L->c->instances[i].cname;
|
||||||
|
return method->cname;
|
||||||
|
}
|
||||||
|
|
||||||
/* Settle what a scope owes, most recent first. A `return` in the middle of a
|
/* Settle what a scope owes, most recent first. A `return` in the middle of a
|
||||||
function still owes everything, so every exit path calls this. */
|
function still owes everything, so every exit path calls this. */
|
||||||
static void run_deferred(Lower *L, unsigned from)
|
static void run_deferred(Lower *L, unsigned from)
|
||||||
@@ -1019,7 +1045,16 @@ static void run_deferred(Lower *L, unsigned from)
|
|||||||
args[0] = fe_ir_load(L->m, L->b, FE_IR_PTR,
|
args[0] = fe_ir_load(L->m, L->b, FE_IR_PTR,
|
||||||
fe_ir_at_local(L->owed[i - 1].local, 0));
|
fe_ir_at_local(L->owed[i - 1].local, 0));
|
||||||
}
|
}
|
||||||
fe_ir_call(L->m, L->b, FE_IR_VOID, "fe_rt_free", args, 1);
|
if (t && t->has_drop) {
|
||||||
|
/* A type that says how to let go of itself is asked to; the
|
||||||
|
name is the one its instance was given. */
|
||||||
|
const char *how = drop_name(L, t);
|
||||||
|
args[0] = fe_ir_addr(L->m, L->b,
|
||||||
|
fe_ir_at_local(L->owed[i - 1].local, 0));
|
||||||
|
if (how) fe_ir_call(L->m, L->b, FE_IR_VOID, how, args, 1);
|
||||||
|
} else {
|
||||||
|
fe_ir_call(L->m, L->b, FE_IR_VOID, "fe_rt_free", args, 1);
|
||||||
|
}
|
||||||
fe_ir_jmp(L->b, skip->id);
|
fe_ir_jmp(L->b, skip->id);
|
||||||
L->b = skip;
|
L->b = skip;
|
||||||
}
|
}
|
||||||
|
|||||||
+50
-3
@@ -1,6 +1,53 @@
|
|||||||
unit list;
|
unit std.list;
|
||||||
|
|
||||||
|
// A growable sequence. The buffer is owned, so a List owns its elements and
|
||||||
|
// releasing it releases them (SPEC 5 R1). Growth doubles, which keeps the
|
||||||
|
// total copying proportional to the number of pushes.
|
||||||
|
|
||||||
pub struct List(T) {
|
pub struct List(T) {
|
||||||
items: ^[]T,
|
items: ^[]mut T,
|
||||||
len: usize,
|
len: usize,
|
||||||
pub fn at(self: &Self, i: usize) -> &T;
|
|
||||||
|
pub fn with_capacity(n: usize) -> !Self {
|
||||||
|
let room: ^[]mut T = try mem.alloc_slice(T, n);
|
||||||
|
return Self{ items: room, len: 0 };
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn count(self: &Self) -> usize { return self.len; }
|
||||||
|
|
||||||
|
pub fn at(self: &Self, i: usize) -> T {
|
||||||
|
return self.items.^[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set(self: &mut Self, i: usize, v: T) -> void {
|
||||||
|
self.items.^[i] = v;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn push(self: &mut Self, v: T) -> !void {
|
||||||
|
if self.len == self.items.^.n { try self.grow(); }
|
||||||
|
self.items.^[self.len] = v;
|
||||||
|
self.len = self.len + 1;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Move to a buffer twice the size. Kept apart from `push` because the
|
||||||
|
/// borrow that hands over the old buffer must not be live while the old
|
||||||
|
/// buffer is still being read (SPEC 5 R6).
|
||||||
|
fn grow(self: &mut Self) -> !void {
|
||||||
|
var room: usize = self.items.^.n * 2;
|
||||||
|
if room == 0 { room = 4; }
|
||||||
|
let bigger: ^[]mut T = try mem.alloc_slice(T, room);
|
||||||
|
var i: usize = 0;
|
||||||
|
while i < self.len {
|
||||||
|
bigger.^[i] = self.items.^[i];
|
||||||
|
i = i + 1;
|
||||||
|
}
|
||||||
|
let old: ^[]mut T = mem.replace(&mut self.items, bigger);
|
||||||
|
mem.destroy(old);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn drop(self: &mut Self) -> void {
|
||||||
|
mem.destroy(self.items);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+66
-3
@@ -1,3 +1,66 @@
|
|||||||
unit str;
|
unit std.str;
|
||||||
pub fn eq(a: str, b: str) -> bool;
|
|
||||||
pub fn trim(s: str) -> str;
|
// `str` is `[]u8` (SPEC 4.2), so these take and give plain byte slices.
|
||||||
|
|
||||||
|
pub fn eq(a: []u8, b: []u8) -> bool {
|
||||||
|
if a.n != b.n { return false; }
|
||||||
|
var i: usize = 0;
|
||||||
|
while i < a.n {
|
||||||
|
if a[i] != b[i] { return false; }
|
||||||
|
i = i + 1;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn starts_with(s: []u8, prefix: []u8) -> bool {
|
||||||
|
if prefix.n > s.n { return false; }
|
||||||
|
return eq(s[0..prefix.n], prefix);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Where `needle` first appears in `s`, or the length of `s` when it does not.
|
||||||
|
/// An index past the end is how "not found" is said without an optional.
|
||||||
|
pub fn find(s: []u8, needle: []u8) -> usize {
|
||||||
|
if needle.n == 0 { return 0; }
|
||||||
|
if needle.n > s.n { return s.n; }
|
||||||
|
var at: usize = 0;
|
||||||
|
let last: usize = s.n - needle.n;
|
||||||
|
while at <= last {
|
||||||
|
if eq(s[at..at + needle.n], needle) { return at; }
|
||||||
|
at = at + 1;
|
||||||
|
}
|
||||||
|
return s.n;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn trim(s: []u8) -> []u8 {
|
||||||
|
var from: usize = 0;
|
||||||
|
var to: usize = s.n;
|
||||||
|
while from < to {
|
||||||
|
if s[from] != 32 and s[from] != 9 and s[from] != 10 and s[from] != 13 {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
from = from + 1;
|
||||||
|
}
|
||||||
|
while to > from {
|
||||||
|
let c: u8 = s[to - 1];
|
||||||
|
if c != 32 and c != 9 and c != 10 and c != 13 { break; }
|
||||||
|
to = to - 1;
|
||||||
|
}
|
||||||
|
return s[from..to];
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn parse_int(s: []u8) -> ?i32 {
|
||||||
|
if s.n == 0 { return null; }
|
||||||
|
var value: i32 = 0;
|
||||||
|
var i: usize = 0;
|
||||||
|
var negative: bool = false;
|
||||||
|
if s[0] == 45 { negative = true; i = 1; }
|
||||||
|
if i >= s.n { return null; }
|
||||||
|
while i < s.n {
|
||||||
|
let c: u8 = s[i];
|
||||||
|
if c < 48 or c > 57 { return null; }
|
||||||
|
value = value * 10 + ((c - 48) as i32);
|
||||||
|
i = i + 1;
|
||||||
|
}
|
||||||
|
if negative { return 0 - value; }
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,42 @@
|
|||||||
|
// EXIT:0
|
||||||
|
// OUTPUT:count 20 sum 190 balanced
|
||||||
|
unit listuse;
|
||||||
|
import std.io;
|
||||||
|
import std.fmt;
|
||||||
|
import std.list;
|
||||||
|
import std.sys;
|
||||||
|
|
||||||
|
fn show(label: []u8, v: i32) -> void {
|
||||||
|
var buf: [16]u8 = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0];
|
||||||
|
let n: usize = fmt.fmt_i32(buf[..], v);
|
||||||
|
io.print(label);
|
||||||
|
io.print(" ");
|
||||||
|
io.print(buf[0..n]);
|
||||||
|
io.print(" ");
|
||||||
|
}
|
||||||
|
|
||||||
|
fn build() -> !i32 {
|
||||||
|
var xs: list.List(i32) = try list.List(i32).with_capacity(2);
|
||||||
|
var i: i32 = 0;
|
||||||
|
while i < 20 {
|
||||||
|
try xs.push(i);
|
||||||
|
i = i + 1;
|
||||||
|
}
|
||||||
|
var sum: i32 = 0;
|
||||||
|
var k: usize = 0;
|
||||||
|
while k < xs.count() {
|
||||||
|
sum = sum + xs.at(k);
|
||||||
|
k = k + 1;
|
||||||
|
}
|
||||||
|
show("count", xs.count() as i32);
|
||||||
|
show("sum", sum);
|
||||||
|
return sum;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() -> i32 {
|
||||||
|
let sum: i32 = build() catch |e| { return 1; };
|
||||||
|
if sum != 190 { return 2; }
|
||||||
|
if sys.allocs() != sys.frees() { io.print("leaked\n"); return 3; }
|
||||||
|
io.print("balanced\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
// EXIT:0
|
||||||
|
// OUTPUT:ok 42 -7
|
||||||
|
unit strings;
|
||||||
|
import std.io;
|
||||||
|
import std.str;
|
||||||
|
import std.fmt;
|
||||||
|
|
||||||
|
fn show(v: i32) -> void {
|
||||||
|
var buf: [16]u8 = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0];
|
||||||
|
let n: usize = fmt.fmt_i32(buf[..], v);
|
||||||
|
io.print(" ");
|
||||||
|
io.print(buf[0..n]);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() -> i32 {
|
||||||
|
if not str.eq(str.trim(" hello "), "hello") { return 1; }
|
||||||
|
if not str.starts_with("ferro", "fer") { return 2; }
|
||||||
|
if str.find("abcdef", "cd") != 2 { return 3; }
|
||||||
|
if str.find("abcdef", "zz") != 6 { return 4; }
|
||||||
|
let a: i32 = str.parse_int("42") orelse 0;
|
||||||
|
let b: i32 = str.parse_int("-7") orelse 0;
|
||||||
|
let bad: i32 = str.parse_int("12x") orelse 0;
|
||||||
|
if bad != 0 { return 5; }
|
||||||
|
io.print("ok");
|
||||||
|
show(a);
|
||||||
|
show(b);
|
||||||
|
io.print("\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user