Reading an article “Simulating Coroutines in Unmanaged C++”, I
got the idea for this article. No we don’t use here Coroutines. We
get only the example which tell for the text in the following frame
need to formatted at screen, or any other output, to 44 characters as
line length, using wrapping for words and excluding double or more
spaces. The text supposed to be inserted from a file.
The Input File:
|
|
44
He [Sarek] was delighted to discover how very much like
him they [computer people] were ... All they
cared about was the art of their
work, and doing it right. It was hard not
to admire such dedication and love of
computing for its own sake.
The programmers were the first
Earth people he came to understand as being
really human.
Diane Duane.- "Spock's World."
|
The Output:
|
|
He [Sarek] was delighted to discover how
very much like him they [computer people]
were ... All they cared about was the art of
their work, and doing it right. It was hard
not to admire such dedication and love of
computing for its own sake. The programmers
were the first Earth people he came to
understand as being really human. Diane
Duane.- "Spock's World."
|
Our first solution
has to be easy to implement. So we say that the input file is a
string variable holding the text. Also we have to say that the output
has to be performed to screen. The M2000 console can display text at
a proportional or non proportional manner on character width. By
default we use the non proportional (like in cmd.exe).
We can use a FORM statement to make the line width above 44
characters, say FORM 60, 40 is good for this.
The
First Program:
|
|
Module
FirstProgram {
a$={44
He
[Sarek] was delighted to
discover how very much like
him
they [computer people] were ... All
they
cared
about was the art of their
work,
and doing it
right. It was hard not
to
admire such dedication and love of
computing
for its own sake.
The
programmers were the first
Earth people he
came to understand as
being
really
human.
Diane
Duane.- "Spock's
World."
}
word=false
buff$=""
line$=""
\\
using "??" we get the integer
value
T=val(a$,"??",m)
\\ m is the index of first char not
included to value
DRule$=""
for
i=0 to
T div
10
DRule$+=If$(i=0->string$("
",10),string$(str$(i,""),10))
next
DRule$=Mid$(DRule$,2,
T)
Rule$=string$("1234567890",
T div
10)+Mid$("1234567890",1,
T mod
10)
Print
DRule$
Print
Rule$
for
i=m
to len(a$)
if
chrcode(mid$(a$,
i,
1))>32
then
if
word
then
Gosub
SimpleSub
word=false
buff$=mid$(a$,i,1)
else
buff$+=mid$(a$,i,1)
end
if
else
word=true
end
if
next
i
Gosub
SimpleSub
If
line$<>""
Then Print
line$
End
SimpleSub:
if
len(buff$)>0
then
if
len(line$)+1+len(buff$)>T
then
if
len(line$)>0
then Print
line$
line$=""
end
if
if
len(line$)=0
then
line$=buff$
else
line$+="
"+buff$
end
if
end
if
Return
}
FirstProgram
|
As we
see from the FirstProgram module, we use some variables:
a$
|
String
|
The Input Text
|
word
|
Boolean
|
True means: Search for Word
False means: Word founded, look for other
characters
|
buff$
|
String
|
A buffer for the word
|
line$
|
String
|
A buffer for line
|
T
|
Double
|
Hold the length for the proper output
|
DRule$
|
String
|
A computed string for first line of ruler
|
Rule$
|
String
|
A computed string for second line of ruler
|
m
|
Double
|
A helper which hold the index of not used char in Val().
|
i
|
Double
|
The index used in Mid$(a$, i, 1) to copy a character from a$
|
The
ruler used for checking the output:
|
11111111112222222222333333333344444
12345678901234567890123456789012345678901234
|
To
check if a character has to stored in the word buffer we have to get
the character code and if the code is bigger than 32 then this we say
is a character for word. In our text we have 32 for Space, 13 and 10
for newline (two characters for a Crlf, or carriage return and line
feed). So using the formula chrcode(mid$(a$, i, 1))>32 we check
the code in index I on a$ string.
We need
to use a boolean variable Word to determine if we have the first
character for a word.
Charcode > 32
|
Word
|
Result
|
False
|
False
|
Word=True
|
False
|
True
|
Word=True
|
True
|
False
|
Append character to buff$
|
True
|
True
|
First character for a Word / see more
|
The
last row has an additional logic. Before we pass the first character
to buff$ we have to see if the buff$ has a word. We use Len(buff$) to
check if we have something, so the value is non zero. So if we have a
previous word we have to check if we can add to line$. For easy
calculation we say that a word never get the T length
or more tan T length. So we have only to
check the total length of line, using
len(line$) plus one for a space plus the len$(buff$). If
this value is more than T then we need to print the line$ and then
make the line$ as the buff$ (so this is the word wrapping), so the
new character (the First character) now assigned to buff$, and Word
change to False.
When
Charcode function return a value bigger than 32 and Word is False
then we append character to buff$. When we get a value from Charcode
function of 32 or
less we just turn Word to False.
We
use a simple subroutine, because the same code used after the
scanning of input text (a$) when we have a buff$ (a word) of length>0
so we have to put it to current line or print the current line and
put it to the last line (as new current one). Simple
subroutines in M2000 used like in BASIC, with a label and the Return
statement. A simple subroutine execute code like the code is in the
place we call it, using the same module name space, without using
local variables.
Look
how to make the ruler, using the two variables, DRule$
and Rule$. We use
the String$() with a string expression and a number expression as
parameters. We get a string times number.
Another
Try
Because
we need something better, we looking for using a Group (an Object) to
mimic a file. So now we have a second program, to do that.
The
Second Program
|
|
Module
SecondProgram {
Group
MyFile
{
Private:
index=1
a$={44
He
[Sarek] was delighted to
discover how very much like
him
they [computer people] were ... All
they
cared
about was the art of their
work,
and doing it
right. It was hard not
to
admire such dedication and love of
computing
for its
own sake.
The
programmers were the first
Earth people he
came to understand as
being
really
human.
Diane
Duane.- "Spock's
World."
}
Public:
\\
= 0=1 return boolean false
Property
EOF{value}=0=1
Function
GetNumber
{
m=0
=val(mid$(.a$,
.index),"??",m)
.index+=m
}
Function
GetWord$ {
while
chrcode(mid$(.a$,
.index,
1))<33
.index++
if
.index>len(.a$)
then
.[EOF]<=True:
Break
end
while
m=.index
while
chrcode(mid$(.a$,
.index,
1))>32
.index++
if
.index>len(.a$)
then
.[EOF]<=True:
Exit
end
while
=mid$(.a$,
m,
.index-m)
}
}
buff$=""
line$=""
T=MyFile.GetNumber()
DRule$=""
for
i=0 to
T div
10
DRule$+=If$(i=0->string$("
",10),string$(str$(i,""),10))
next
DRule$=Mid$(DRule$,2,
T)
Rule$=string$("1234567890",
T div
10)+Mid$("1234567890",1,
T mod
10)
Print
DRule$
Print
Rule$
Repeat
buff$=MyFile.GetWord$()
if
MyFile.EOF then
exit
if
len(line$)+1+len(buff$)>T
then
if
len(line$)>0
then Print
line$
line$=""
end
if
if
len(line$)=0
then
line$=buff$
else
line$+="
"+buff$
end
if
Always
If
line$<>""
Then Print line$
}
SecondProgram
|
Now
we have the MyFile group. This group has two private members, the a$
as the input text and the index member. Also this group has three
public members, a property EOF (for End Of File), and two methods,
GetNumber() and GetWord$().
The
main code has many items from first program. The subroutine not used
here, because the loop, Repeat Always, works fine and has the part of
subroutine part of it.
The
new code didn’t use a Word boolean variable, because the method
GetWord$() return a word or nothing. Also this function change the
hidden variable [EOF] which return by the read only property EOF, and
for MyFile is the MyFile.EOF. And the m variable not used in the main
code because now exist only in method GetNumber().
How
the method GetWord$() works?
Each
time we check a character at index, we add one to index and check if
the index is bigger than length of a$. The first time we check the
character code
for less than 33. We want to skip all these
characters with code less than 33. If we get an EOF we just make a
Break (this break the code of method GetWord$() and return the last
value which we place as return value, or the default value, the empty
string for a string function. So if we pass the first loop without
breaking it we have the first character of a word. We get in a second
loop while we find a character code bigger than 32. Here we use Exit
because we need to return the word. We
didn’t use a buff$ variable for word, we just get two indexes and
then we get a sub string from a$ using Mid$().
Looking
the two programs we see that the second one has an outer loop which
are not connected to index variable (like variable i in first
program). The index is hidden in Group object.
So
now lets see another version
Third
Try
What
if we have a second object for rendering the output?
Here
we put another public method in MyFile object, the OpeFile, which
prepare the index, to value one, an reset the EOF internal
(hidden/private) variable [EOF]. Also we make another group, the
Render group with three members, the T for the width for rendering,
the Ruler() and the Screen() methods. We pass the MyFile object by
reference to Render object. We can display or not the ruler, but we
have to call Render.Ruler to get the proper T from file in MyFile.
We
use Type: File to make the type of group MyFile as File (so we can
check when get a group in Ruler and Screen methods). Classes in M2000
always insert a proper type directive, but for groups we define by
using Group statement we have to put it if we want it, by using Type:
(we can add one or more names)
The
code inside Render isn’t new to us.
Third
Program
|
|
Module
ThirdProgram {
Group
MyFile {
Type:
File
Private:
index=100000
a$={44
He
[Sarek] was delighted to
discover how very much like
him
they [computer people] were ... All
they
cared
about was the art of their
work,
and doing it
right. It was hard not
to
admire such dedication and love of
computing
for its
own sake.
The
programmers were the first
Earth people he
came to understand as
being
really
human.
Diane
Duane.- "Spock's
World."
}
Public:
Property
EOF{value}=1=1
Module
OpenFile
{
.index<=1
.[EOF]<=False
}
Function
GetNumber
{
m=0
=val(mid$(.a$,
.index),"??",m)
.index+=m
}
Function
GetWord$ {
while
chrcode(mid$(.a$,
.index,
1))<33
.index++
if
.index>len(.a$)
then
.[EOF]<=True:
Break
end
while
m=.index
while
chrcode(mid$(.a$,
.index,
1))>32
.index++
if
.index>len(.a$)
then
.[EOF]=True:
Exit
end
while
=mid$(.a$,
m,
.index-m)
}
}
Group
Render
{
T=0
Module
Ruler (&File
as File,
ToScreen=False){
.T<=File.GetNumber()
If
.T>89
then Error "Cant
handle such
width"
DRule$=""
for
i=0 to
.T div
10
DRule$+=If$(i=0->string$("
",10),string$(str$(i,""),10))
next
If
Not ToScreen Then
Exit
Print
Mid$(DRule$,2,
.T)
Print
string$("1234567890",
.T div
10)+Mid$("1234567890",1,
.T mod
10)
}
Module
Screen (&File
as File)
{
line$=""
buff$=""
Repeat
buff$=File.GetWord$()
if
File.EOF then
exit
if
len(line$)+1+len(buff$)>.T
then
if
len(line$)>0
then Print
line$
line$=""
end
if
if
len(line$)=0
then
line$=buff$
else
line$+="
"+buff$
end
if
Always
If
line$<>""
Then Print
line$
}
}
Flush
MyFile.OpenFile
Render.Ruler
&MyFile,
%ToScreen=True
Render.Screen
&MyFile
Print
MyFile.EOF
}
ThirdProgram
|
Forth
Try
So can
we refine our program to print line by line, calling ScreenLine()
method?
Now
Render group get the buff$ and line$ as members, because ScreenLine
method need to use them for each call.
|
|
Module
ForthProgram {
Group
MyFile {
Type:
File
Private:
index=100000
a$={44
He
[Sarek] was delighted to
discover how very much like
him
they [computer people] were ... All
they
cared
about was the art of their
work,
and doing it
right. It was hard not
to
admire such dedication and love of
computing
for its
own sake.
The
programmers were the first
Earth people he
came to understand as
being
really
human.
Diane
Duane.- "Spock's
World."
}
Public:
Property
EOF{value}=1=1
Module
OpenFile
{
.index<=1
.[EOF]<=False
}
Function
GetNumber
{
m=0
=val(mid$(.a$,
.index),"??",m)
.index+=m
}
Function
GetWord$ {
while
chrcode(mid$(.a$,
.index,
1))<33
.index++
if
.index>len(.a$)
then
.[EOF]<=True:
Break
end
while
m=.index
while
chrcode(mid$(.a$,
.index,
1))>32
.index++
if
.index>len(.a$)
then
.[EOF]=True:
Exit
end
while
=mid$(.a$,
m,
.index-m)
}
}
Group
Render {
T,
line$,
buff$
Module
Ruler (&File
as File,
ToScreen=False){
.line$<=""
.buff$<=""
.T<=File.GetNumber()
If
.T>89
then Error "Cant
handle such
width"
DRule$=""
for
i=0 to
.T div
10
DRule$+=If$(i=0->string$("
",10),string$(str$(i,""),10))
next
If
Not ToScreen Then
Exit
Print
Mid$(DRule$,2,
.T)
Print
string$("1234567890",
.T div
10)+Mid$("1234567890",1,
.T mod
10)
}
Function
ScreenLine (&File
as File)
{
Repeat
if
.buff$=""
then
.buff$<=File.GetWord$():
=True
if
File.EOF then
=False :
exit
if
len(.line$)+1+len(.buff$)>.T
then
exit
.line$+=If$(len(.line$)=0->"",
"
")+.buff$
.buff$<=""
Always
if
.line$<>""
then Print .line$
:
.line$<=""
}
}
Flush
MyFile.OpenFile
Render.Ruler
&MyFile,
%ToScreen=True
While
Render.ScreenLine(&MyFile)
Wait
10
End
While
Print
MyFile.EOF
}
ForthProgram
|
Fifth
Try
Lets
say now that we want to change the loop to print word by word (not
line by line as the forth program).
Now we
use line$ only for calculations. So we print word as P$ (with or
wthout a leading space) and a “;” at the end so we didn’t get a
new line. We use Print with no parameters when we want to place a new
line.
Fifth
Program
|
|
Module
FifthProgram
{
Group
MyFile
{
Type:
File
Private:
index=100000
a$={44
He
[Sarek] was delighted to
discover how very much like
him
they [computer people] were ... All
they
cared
about was the art of their
work,
and doing it
right. It was hard not
to
admire such dedication and love of
computing
for its
own sake.
The
programmers were the first
Earth people he
came to understand as
being
really
human.
Diane
Duane.- "Spock's
World."
}
Public:
Property
EOF{value}=1=1
Module
OpenFile
{
.index<=1
.[EOF]<=False
}
Function
GetNumber
{
m=0
=val(mid$(.a$,
.index),"??",m)
.index+=m
}
Function
GetWord$
{
while
chrcode(mid$(.a$,
.index,
1))<33
.index++
if
.index>len(.a$)
then
.[EOF]<=True:
Break
end
while
m=.index
while
chrcode(mid$(.a$,
.index,
1))>32
.index++
if
.index>len(.a$)
then
.[EOF]=True:
Exit
end
while
=mid$(.a$,
m,
.index-m)
}
}
Group
Render
{
T,
line$,
buff$
Module
Ruler
(&File
as
File,
ToScreen=False){
.line$<=""
.buff$<=""
.T<=File.GetNumber()
If
.T>89
then
Error "Cant
handle such
width"
DRule$=""
for
i=0
to
.T
div
10
DRule$+=If$(i=0->string$("
",10),string$(str$(i,""),10))
next
If
Not ToScreen
Then
Exit
Print
Mid$(DRule$,2,
.T)
Print
string$("1234567890",
.T
div
10)+Mid$("1234567890",1,
.T
mod
10)
}
Function
ScreenWord
(&File
as
File)
{
.buff$<=File.GetWord$():
=True
if
File.EOF
then
=False :
Print
:
exit
if
len(.line$)+1+len(.buff$)>.T
then
Print
:
.line$<=""
P$=If$(len(.line$)=0->"",
"
")+.buff$
Print
P$;
.line$+=P$
}
}
Flush
MyFile.OpenFile
Render.Ruler
&MyFile,
%ToScreen=True
While
Render.ScreenWord(&MyFile)
Wait
2
End
While
Print
MyFile.EOF
}
FifthProgram
|
Sixth
Try
So now
its time to change MyFile group. We want to have a GetChar() member.
Logic for End of File now moved from GetWord() method to GetChar()
method.
Method
GetWord$ change to not use the index variable, but the GetChar
method.
The Z
variable display the number of Words.
Sixth
Program
|
|
Module
SixthProgram {
Group
MyFile {
Type:
File
Private:
index=100000
a$={44
He
[Sarek] was delighted to
discover how very much like
him
they [computer people] were ... All
they
cared
about was the art of their
work,
and doing it
right. It was hard not
to
admire such dedication and love of
computing
for its
own sake.
The
programmers were the first
Earth people he
came to understand as
being
really
human.
Diane
Duane.- "Spock's
World."
}
Public:
Property
EOF{value}=1=1
Module
OpenFile
{
.index<=1
.[EOF]<=False
}
Function
GetNumber
{
=int(val(.GetWord$()))
}
Function
GetChar(&C$)
{
If
Not .[EOF]
Then
=True
C$=mid$(.a$,
.index,
1)
.index++
if
.index>len(.a$)
then
.[EOF]<=True
End
If
}
Function
GetWord$
{
W$=""
Word$=""
Do
If
Not .GetChar(&W$)
Then Break
if
chrcode(W$)>32
Then Exit
Always
Word$+=W$
Do
If
Not .GetChar(&W$)
Then Exit
if
chrcode(W$)<33
Then
Exit
Word$+=W$
Always
=Word$
}
}
Group
Render {
T,
line$,
buff$
Module
Ruler (&File
as File,
ToScreen=False){
(.T,
.line$,
.buff$)=(File.GetNumber(),
"",
"")
If
.T>89
then Error "Cant
handle such
width"
DRule$=""
for
i=0 to
.T div
10
DRule$+=If$(i=0->string$("
",10),string$(str$(i,""),10))
next
If
Not ToScreen Then
Exit
Print
Mid$(DRule$,2,
.T)
Print
string$("1234567890",
.T div
10)+Mid$("1234567890",1,
.T mod
10)
}
Function
ScreenWord (&File
as File)
{
\\
by default a arithmetic funcion return 0
(false)
.buff$<=File.GetWord$()
if
File.EOF then
Print :
exit
if
len(.line$)+1+len(.buff$)>.T
then Print
:
.line$<=""
P$=If$(len(.line$)=0->"",
"
")+.buff$
Print
P$;
.line$+=P$
=True
}
}
Flush
MyFile.OpenFile
Render.Ruler
&MyFile,
%ToScreen=True
Z=0
While
Render.ScreenWord(&MyFile)
Z++
End
While
Print
MyFile.EOF
Print
"Z=";Z
}
SixthProgram
|
Final
Try
We skip
some tries, to get the final try. We change MyFile Group and make
the File Class. The File Class has two members, the F as the file
handler, and the ANSI boolean variable to indicate if the file is
ANSI or not (a UTF16LE encoding). Now the a$ not exist. We have a
file, named spoke, in current directory (dir$ return this). Also we
use a ANSI variable in Module FinalProgram to get a random number,
and then we prepare a file as ANSI or UTF16LE.
Class
File make MyFile object. We have new methods OpenFile, CloseFile and
the constructor File (exist only when we make the object, but not to
the final object because we have it after a Class: directive). The
File constructor get the type of file.
So what
we have to change to read from a file?
We
change only the GetChar method. Files have the Eof() function so we
prepare property EOF according to Eof() function. Also we have to
Open the file, and at the end we have to close it.
Final
Program
|
|
Module
FinalProgram
{
Ansi=Random(-1,
0)
Print
"We
make a file in dir:"
Print
Dir$
\\
Locale used for converting properly internal UTF16LE to and from
ANSI
Locale
1033
If
Ansi
Then
Open
"spoke"
for
output as #F
Print
"ANSI
file - Using Locale=";Locale
Else
Open
"spoke"
for
wide output as #F
Print
"UTF
16LE File"
End
if
Print
#F,
{44
He
[Sarek] was delighted to
discover how very much like
him
they [computer people] were ... All
they
cared
about was the art of their
work,
and doing it
right. It was hard not
to
admire such dedication and love of
computing
for its
own sake.
The
programmers were the first
Earth people he
came to understand as
being
really
human.
Diane
Duane.- "Spock's World."
}
Close
#F
Print
"File
Length in bytes:";
Filelen("spoke")
Class
File
{
Private:
F=0
Ansi=False
Public:
Property
EOF{value}=1=1
Module
OpenFile(name$)
{
If
.F>0
then
Error "Close
current file"
If
.Ansi
Then
Open
name$
for
input as #.F
else
Open
name$
for
wide input as #.F
end
if
}
Module
CloseFile
{
If
.F>0
then
Close #.F
}
Function
GetNumber
{
=int(val(.GetWord$()))
}
Function
GetChar(&C$)
{
.[EOF]<=Eof(#.F)
If
Not .[EOF]
Then
=True
C$=Input$(.F,
If(.Ansi->2,
1))
.[EOF]<=Eof(#.F)
End
If
}
Function
GetWord$
{
W$=""
Word$=""
Do
If
Not .GetChar(&W$)
Then
Break
if
chrcode(W$)>32
Then
Exit
Always
Word$+=W$
Do
If
Not .GetChar(&W$)
Then
Exit
if
chrcode(W$)<33
Then
Exit
Word$+=W$
Always
=Word$
}
Class:
Module
File(Ftype)
{
.Ansi<=Ftype
}
}
Group
Render
{
T,
line$,
buff$
Module
Ruler
(&File
as
File,
ToScreen=False){
(.T,
.line$,
.buff$)=(File.GetNumber(),
"",
"")
If
.T>89
then
Error "Cant
handle such
width"
DRule$=""
for
i=0
to
.T
div
10
DRule$+=If$(i=0->string$("
",10),string$(str$(i,""),10))
next
If
Not ToScreen
Then
Exit
Print
Mid$(DRule$,2,
.T)
Print
string$("1234567890",
.T
div
10)+Mid$("1234567890",1,
.T
mod
10)
}
Function
ScreenWord
(&File
as
File)
{
\\
by default a arithmetic funcion return 0
(false)
.buff$<=File.GetWord$()
if
File.EOF
then
Print
:
exit
if
len(.line$)+1+len(.buff$)>.T
then
Print
:
.line$<=""
P$=If$(len(.line$)=0->"",
"
")+.buff$
Print
P$;
.line$+=P$
=True
}
}
Flush
Print
"Process"
Myfile=File(ANSI)
MyFile.OpenFile
"Spoke"
Render.Ruler
&MyFile,
%ToScreen=True
Z=0
While
Render.ScreenWord(&MyFile)
Z++
End
While
Print
MyFile.EOF
MyFile.CloseFile
Print
"Count
**words**=";Z
}
FinalProgram
|
Δεν υπάρχουν σχόλια:
Δημοσίευση σχολίου
You can feel free to write any suggestion, or idea on the subject.