Skip to content

Instantly share code, notes, and snippets.

@nfunato
Created November 29, 2020 05:13
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save nfunato/c2635cd6ac428adb0bdba327eeed2fef to your computer and use it in GitHub Desktop.
Save nfunato/c2635cd6ac428adb0bdba327eeed2fef to your computer and use it in GitHub Desktop.

From Gforth 0.7.3 manual

5.22 Structures

This section presents the structure package that comes with Gforth. A version of the package implemented in ANS Forth is available in `compat/struct.fs’. This package was inspired by a posting on comp.lang.forth in 1989 (unfortunately I don’t remember, by whom; possibly John Hayes). A version of this section has been published in M. Anton Ertl, Yet Another Forth Structures Package (http://www.complang.tuwien.ac.at/forth/objects/structs.html), Forth Dimensions 19(3), pages 13-16. Marcel Hendrix provided helpful comments.

ここではGforthに付属する構造体パッケージを紹介する。ANS Forth で実装されたこのパッケージのバージョンは,`compat/struct.fs’ にあります.このパッケージは,1989年に comp.lang.forth に投稿されたものに触発されたものです(残念ながら誰が書いたのかは覚えていませんが,おそらく John Hayes 氏でしょう)。このセクションのバージョンは、M. Anton Ertl, Yet Another Forth Structures Package (http://www.complang.tuwien.ac.at/forth/objects/structs.html), Forth Dimensions 19(3), pages 13-16に掲載されています。Marcel Hendrix が参考となるコメントを提供してくれました。

Why explicit structure support?

If we want to use a structure containing several fields, we could simply reserve memory for it, and access the fields using address arithmetic (*note Address arithmetic::). As an example, consider a structure with the following fields

いくつかのフィールドを含む構造体を使用したい場合は、単にメモリを確保して、アドレス演算を使用してフィールドにアクセスすることができます (*note Address arithmetic::)。例として、以下のフィールドを持つ構造体を考えてみましょう。

`a’ is a float

`b’ is a cell

`c’ is a float

Given the (float-aligned) base address of the structure we get the address of the field

構造体の (float-aligned) ベースアドレスが与えられると、フィールドのアドレスが得られます。

`a’ without doing anything further.

`b’ with `float+’

`c’ with `float+ cell+ faligned’

It is easy to see that this can become quite tiring.

これではかなり疲れるのは目に見えています。

Moreover, it is not very readable, because seeing a `cell+’ tells us neither which kind of structure is accessed nor what field is accessed; we have to somehow infer the kind of structure, and then look up in the documentation, which field of that structure corresponds to that offset.

さらに、`cell+’を見ると、どのような構造体がアクセスされているのか、どのようなフィールドがアクセスされているのかがわからないので、とても読みやすいものではありません。

Finally, this kind of address arithmetic also causes maintenance troubles: If you add or delete a field somewhere in the middle of the structure, you have to find and change all computations for the fields afterwards.

最後に、このようなアドレス演算はメンテナンス上のトラブルの原因にもなります。構造体の途中のどこかでフィールドを追加したり削除したりした場合、後からフィールドの計算をすべて見つけて変更しなければなりません。

So, instead of using `cell+’ and friends directly, how about storing the offsets in constants:

そこで、`cell+’ やフレンドを直接使うのではなく、オフセットを定数に格納するのはどうでしょうか。

0 constant a-offset 0 float+ constant b-offset 0 float+ cell+ faligned c-offset

Now we can get the address of field `x’ with `x-offset +’. This is much better in all respects. Of course, you still have to change all later offset definitions if you add a field. You can fix this by declaring the offsets in the following way:

これでフィールド `x’ のアドレスを `x-offset +’ で取得できるようになりました。これはあらゆる点ではるかに優れています。もちろん、フィールドを追加した場合には、その後のオフセット定義をすべて変更しなければなりません。これは以下のようにオフセットを宣言することで修正できる。

0 constant a-offset a-offset float+ constant b-offset b-offset cell+ faligned constant c-offset

Since we always use the offsets with `+’, we could use a defining word `cfield’ that includes the `+’ in the action of the defined word:

我々は常に `+’ でオフセットを使用しているので、定義語の動作に `+’ を含む定義語 `cfield’ を使用することができる。

cfield ( n "name" -- )

create , does> ( name execution: addr1 – addr2 ) @ + ;

0 cfield a 0 a float+ cfield b 0 b cell+ faligned cfield c

Instead of `x-offset +’, we now simply write `x’.

x-オフセット +’ の代わりに、単に `x’ と書く。

The structure field words now can be used quite nicely. However, their definition is still a bit cumbersome: We have to repeat the name, the information about size and alignment is distributed before and after the field definitions etc. The structure package presented here addresses these problems.

構造体フィールドの単語がかなりきれいに使えるようになりました。しかし、その定義はまだ少し面倒です。名前を繰り返さなければならないこと、サイズやアラインメントに関する情報がフィールド定義の前後に分散していることなどです。 ここで紹介する構造体パッケージは、これらの問題を解決します。

Structure Usage

You can define a structure for a (data-less) linked list with:

以下のように(データレスの)リンクされたリストの構造体を定義することができます。

struct cell% field list-next end-struct list%

With the address of the list node on the stack, you can compute the address of the field that contains the address of the next node with `list-next’. E.g., you can determine the length of a list with:

スタック上のリストノードのアドレスがあれば、次のノードのアドレスを含むフィールドのアドレスを `list-next’ で計算することができる。例えば、リストの長さを求めるには

list-length ( list -- n )

\ “list” is a pointer to the first element of a linked list \ “n” is the length of the list 0 BEGIN ( list1 n1 ) over WHILE ( list1 n1 ) 1+ swap list-next @ swap REPEAT nip ;

You can reserve memory for a list node in the dictionary with `list% %allot’, which leaves the address of the list node on the stack. For the equivalent allocation on the heap you can use `list% %alloc’ (or, for an `allocate’-like stack effect (i.e., with ior), use `list% %allocate’). You can get the the size of a list node with `list% %size’ and its alignment with `list% %alignment’.

list% %allot’ を用いて辞書内のリストノードのメモリを予約することができます。これと同等のヒープへの割り当てには、`list% %alloc’ を用いることができる (あるいは、`allocate’ のようなスタック効果 (つまり ior を用いる) には、`list% %allocate’ を用いることができる)。list% %size’ でリストノードのサイズを、`list% %alignment’ でアライメントを得ることができます。

Note that in ANS Forth the body of a `create’d word is `aligned’ but not necessarily `faligned’; therefore, if you do a:

ANS Forthでは、「create」されたWORDのbodyは「align」されていますが、必ずしも「falign」されているわけではないことに注意してください。

create name foo% %allot drop

then the memory alloted for `foo%’ is guaranteed to start at the body of `_name_’ only if `foo%’ contains only character, cell and double fields. Therefore, if your structure contains floats, better use

この場合、`foo%’ に割り当てられたメモリは、`foo%’ が文字、セル、ダブルフィールドのみを含む場合に限り、 `_name_’ のボディから始まることが保証されます。 したがって、構造体にフロートが含まれている場合は

foo% %allot constant name

You can include a structure `foo%’ as a field of another structure, like this: 構造体 `foo%’ を別の構造体のフィールドとして含めることができます。

struct … foo% field … … end-struct …

Instead of starting with an empty structure, you can extend an existing structure. E.g., a plain linked list without data, as defined above, is hardly useful; You can extend it to a linked list of integers, like this:(1)

空の構造体から始めるのではなく、既存の構造体を拡張することができます。例えば、上で定義されているようなデータのない単純なリンクされたリストはほとんど役に立ちません。

list% cell% field intlist-int end-struct intlist%

`intlist%’ is a structure with two fields: `list-next’ and `intlist-int’. You can specify an array type containing n elements of type `foo%’ like this:

intlist%’ は2つのフィールドを持つ構造体である。list-next’ と `intlist-int’ です。 このように `foo%’ 型の n 要素を含む配列型を指定することができます。

foo% n *

You can use this array type in any place where you can use a normal type, e.g., when defining a `field’, or with `%allot’.

この配列型は、`field’ を定義するときや `%allot’ など、通常の型を使うことができる場所であればどこでも使うことができます。

The first field is at the base address of a structure and the word for this field (e.g., `list-next’) actually does not change the address on the stack. You may be tempted to leave it away in the interest of run-time and space efficiency. This is not necessary, because the structure package optimizes this case: If you compile a first-field words, no code is generated. So, in the interest of readability and maintainability you should include the word for the field when accessing the field.

最初のフィールドは構造体のベースアドレスにあり、このフィールドのためのワード(例えば、`list-next’)は実際にはスタック上のアドレスを変更しません。ランタイムとスペースの効率化のために、このフィールドを残しておきたくなるかもしれません。構造体パッケージはこの場合を最適化するので、これは必要ありません。なぜなら、構造体パッケージはこの場合を最適化しているからです。ですから、可読性と保守性の観点から、フィールドにアクセスする際にはフィールドの単語を含めるべきです。

———- Footnotes ———-

(1) This feature is also known as extended records. It is the main innovation in the Oberon language; in other words, adding this feature to Modula-2 led Wirth to create a new language, write a new compiler etc. Adding this feature to Forth just required a few lines of code.

(1) この機能は、_extended records_としても知られています。言い換えれば、この機能をModula-2に追加することで、Wirthは新しい言語を作り、新しいコンパイラを書くことになったのです。 この機能を Forth に追加するには、数行のコードを書くだけでした。

Structure Naming Convention

The field names that come to (my) mind are often quite generic, and, if used, would cause frequent name clashes. E.g., many structures probably contain a `counter’ field. The structure names that come to (my) mind are often also the logical choice for the names of words that create such a structure.

(私の)頭に浮かぶフィールド名は、しばしば非常に一般的なものであり、使用されると頻繁に名前の衝突を引き起こすでしょう。例えば、多くの構造体はおそらく `counter’ フィールドを含んでいるでしょう。(私の)頭に浮かぶ構造体名は、そのような構造体を作成する単語の名前の論理的な選択であることが多い。

Therefore, I have adopted the following naming conventions:

そのため、以下のような命名規則を採用しています。

  • The names of fields are of the form `_struct_-_field_’, where `_struct_’ is the basic name of the structure, and `_field_’ is the basic name of the field. You can think of field words as converting the (address of the) structure into the (address of the) field.

    フィールドの名前は `_struct_-_field_’ の形式で、`_struct_’ は構造体の基本名、`_field_’ はフィールドの基本名です。フィールド語は(構造体のアドレス)を(フィールドのアドレス)に変換したものと考えることができます。

  • The names of structures are of the form `_struct_%’, where `_struct_’ is the basic name of the structure.

    構造体の名前は `_struct_%’ の形式をとり、`_struct_’ は構造体の基本名です。

This naming convention does not work that well for fields of extended structures; e.g., the integer list structure has a field `intlist-int’, but has `list-next’, not `intlist-next’.

例えば、整数リスト構造体はフィールド `intlist-int’ を持っていますが、`list-next’ を持っていて `intlist-next’ を持っていません。

Structure Implementation

The central idea in the implementation is to pass the data about the structure being built on the stack, not in some global variable. Everything else falls into place naturally once this design decision is made.

実装における中心的なアイデアは、スタック上に構築されている構造体に関するデータを、どこかのグローバル変数ではなく、スタック上に渡すことです。この設計上の決定がなされれば、他のすべてのことは自然にうまくいくようになります。

The type description on the stack is of the form align size. Keeping the size on the top-of-stack makes dealing with arrays very simple.

スタック上の型の記述は align size の形式です。サイズをスタックの先頭に置いておくことで、配列の扱いが非常に簡単になります。

`field’ is a defining word that uses `Create’ and `DOES>’. The body of the field contains the offset of the field, and the normal `DOES>’ action is simply:

field’ は `Create’ と `DOES>’ を利用する定義語である。フィールドのボディにはフィールドのオフセットが含まれ、通常の `DOES>’ の動作は単純です。

@ +

i.e., add the offset to the address, giving the stack effect addr1 - addr2 for a field.

つまり、オフセットをアドレスに追加して、フィールドのスタック効果 addr1 - addr2 を与えます。

This simple structure is slightly complicated by the optimization for fields with offset 0, which requires a different `DOES>’-part (because we cannot rely on there being something on the stack if such a field is invoked during compilation). Therefore, we put the different `DOES>’-parts in separate words, and decide which one to invoke based on the offset. For a zero offset, the field is basically a noop; it is immediate, and therefore no code is generated when it is compiled.

この単純な構造は、オフセット0のフィールドの最適化のために、別の `DOES>’ パーツを必要とするため、少し複雑になっています (コンパイル中にそのようなフィールドが呼び出された場合、スタック上に何かがあることを信頼できないためです)。そのため、異なる `DOES>’ 部分を別の言葉で記述し、オフセットに基づいてどれを呼び出すかを決定します。オフセットがゼロの場合、フィールドは基本的にnoopです; 即時なので、コンパイル時にコードは生成されません。

Structure Glossary

  • `%align’ align size - gforth “%align” Align the data space pointer to the alignment ALIGN.
  • `%alignment’ align size - align gforth “%alignment” The alignment of the structure.
  • `%alloc’ align size - addr gforth “%alloc” Allocate SIZE address units with alignment ALIGN, giving a data block at ADDR; `throw’ an ior code if not successful.
  • `%allocate’ align size - addr ior gforth “%allocate” Allocate SIZE address units with alignment ALIGN, similar to `allocate’.
  • `%allot’ align size - addr gforth “%allot” Allot SIZE address units of data space with alignment ALIGN; the resulting block of data is found at ADDR.
  • `cell%’ - align size gforth “cell%”
  • `char%’ - align size gforth “char%”
  • `dfloat%’ - align size gforth “dfloat%”
  • `double%’ - align size gforth “double%”
  • `end-struct’ align size “name” - gforth “end-struct” Define a structure/type descriptor NAME with alignment ALIGN and size SIZE1 (SIZE rounded up to be a multiple of ALIGN).
  • `name’ execution: - ALIGN SIZE1
  • `field’ algn1 ofs1 algn siz “name” - algn2 ofs2 gforth “field” Create a field NAME with offset OFFSET1, and the type given by ALIGN SIZE. OFFSET2 is the offset of the next field, and ALIGN2 is the alignment of all fields.
  • `name’ execution: ADDR1 - ADDR2 ADDR2=ADDR1+OFFSET1
  • `float%’ - align size gforth “float%”
  • `naligned’ addr1 n - addr2 gforth “naligned” ADDR2 is the aligned version of ADDR1 with respect to the alignment N.
  • `sfloat%’ - align size gforth “sfloat%”
  • `%size’ align size - size gforth “%size” The size of the structure.
  • `struct’ - align size gforth “struct” An empty structure, used to start a structure definition.

Forth200x Structures

The Forth 200x standard defines a slightly less convenient form of structures. In general (when using `field+’, you have to perform the alignment yourself, but there are a number of convenience words (e.g., `field:’ that perform the alignment for you.

Forth 200x標準では、構造体の少し便利ではない形式を定義しています。一般的に(`field+’ を使う場合は、自分で整列をしなければなりませんが、整列をしてくれる便利な言葉がいくつかあります(例えば、`field:’ など)。

A typical usage example is: 典型的な使用例としては:

0 field: s-a faligned 2 floats +field s-b constant s-struct

An alternative way of writing this structure is: この構造の代替的な書き方としては、次のようなものがあります:

begin-structure s-struct field: s-a faligned 2 floats +field s-b end-structure

  • `begin-structure’ “name” - struct-sys 0 X:structures “begin-structure”
  • `end-structure’ struct-sys +n - X:structures “end-structure”
  • `+field’ n1 n2 “name” - n3 X:structures “plus-field”
  • `cfield:’ u1 “name” - u2 X:structures “cfield:”
  • `field:’ u1 “name” - u2 X:structures “field:”
  • `2field:’ u1 “name” - u2 gforth “2field:”
  • `ffield:’ u1 “name” - u2 X:structures “ffield:”
  • `sffield:’ u1 “name” - u2 X:structures “sffield:”
  • `dffield:’ u1 “name” - u2 X:structures “dffield:”
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment