💾 Archived View for sdf.org › rsdoiel › blog › 2020 › 04 › 12 › Mostly-Oberon-Modules.html captured on 2023-03-20 at 19:17:41.

View Raw

More Information

⬅️ Previous capture (2023-01-29)

🚧 View Differences

-=-=-=-=-=-=-

<!DOCTYPE html>
<html lang="en">
<head>
  <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
  <meta name="language" content="EN">
  <title>Mostly-Oberon-Modules</title>

  <link rel="stylesheet" type="text/css"  href="/printfonts/print.css" media="print" />
  <link rel="stylesheet" type="text/css"  href="/webfonts/fonts.css" media="screen" />
  <link rel="stylesheet" type="text/css"  href="/css/site.css" media="screen" />
  <link rel="stylesheet" type="text/css"  href="/css/tables.css" media="screen" />
  <link title="RSS feed for rsdoiel's blog" rel="alternate" type="application/rss+xml" href="https://rsdoiel.github.io/rss.xml" />
  <meta name="keywords" content="Oberon, programming">
  <link rel="alternative" type="application/markdown" href="/blog/2020/04/12/Mostly-Oberon-Modules.md">
  <link rel="search" type="application/opensearchdescription+xml"
        title="Robert's Rambling Search Engine"
        href="search.osdx">
</head>
<body>
<nav>
<ul>
<li><a href="/">R. S. Doiel</a></li>
<li><a href="/about.html">About</a></li>
<li><a href="/blog/">Blog</a></li>
<li><a href="/cv.html">CV</a></li>
<li><a href="https://github.com/rsdoiel">GitHub</a></li>
<li><a href="/library-terminology.html">Library Jargon</a></li>
<li><a href="/presentations.html">Presentations</a></li>
<li><a href="/projects.html">Projects</a></li>
<li><a href="/resume.html">Resume</a></li>
<li><a href="/search.html">Search</a></li>
<li><a href="/series/">Series</a></li>
</ul>
</nav>

<section>
  <article>
  <a data-pocket-label="pocket" data-pocket-count="none" class="pocket-btn" data-lang="en"></a>
<script type="text/javascript">!function(d,i){if(!d.getElementById(i)){var j=d.createElement("script");j.id=i;j.src="https://widgets.getpocket.com/v1/j/btn.js?v=1";var w=d.getElementById(i);d.body.appendChild(j);}}(document,"pocket-btn-js");</script>
<h1 id="oberon-modules-and-procedures">Oberon Modules and
Procedures</h1>
<p>By R. S. Doiel, 2020-04-12</p>
<p>This is the second post in the <a
href="../11/Mostly-Oberon.html">Mostly Oberon</a> series. Mostly Oberon
documents my exploration of the Oberon Language, Oberon System and the
various rabbit wholes I inevitably fell into.</p>
<h2 id="modules">Modules</h2>
<p>The module is a primary code unit of Oberon language. Modules allow
you to focus on functional units of code and can be readily composed
into larger solutions. A module’s name should match the filename you are
saving it under. A module starts with declaring it’s name and ends the
declaration with a semicolon the statement separator in Oberon. Our
simple “Hello World” example shows the basic code shape.</p>
<pre class="oberon"><code>
    MODULE HelloWorld;
      IMPORT Out;
    BEGIN
      Out.String(&quot;Hello World!&quot;); Out.Ln;
    END HelloWorld.
</code></pre>
<p>Modules end with a <code>END</code> followed by the module’s name and
a period. Any text following the <code>END</code> statement is ignored
by the compiler. This turns out to be very useful as a place to write up
ideas about the code you’re working on. You can also write any
additional special instructions there (e.g. document usage). You can
even use it as a scratch pad knowing that the compiler will ignore
it.</p>
<p>Here’s an example</p>
<pre class="oberon"><code>
    MODULE HelloWorld;
      IMPORT Out;
    BEGIN
      Out.String(&quot;Hello World!&quot;); Out.Ln;
    END HelloWorld.

    This program isn&#39;t very useful. It has no interactive ability.
    It&#39;d be nice if it could be more specific about who it was saying
    hello to.
</code></pre>
<p>For a module to be really useful you want to have the capability of
including both private and public code. Public code allows us to reuse
our code in other modules while the private code keeps internal things
inside the module safe from colliding with other modules private code.
This technique is classically known as “information hiding” and in
computer sciences texts as “scope”. Lets create a a more composable
module called <code>SayingHi.Mod</code>. In addition to display “Hello
World!” we want a public method (procedure in Oberon terminology) that
can ask for a name and print out a salutation. We will use the
<code>SayingHi.Mod</code> module along with a newer version of
<code>HelloWorld.Mod</code> named <code>HelloWorld2.Mod</code>.</p>
<h2 id="procedures">Procedures</h2>
<p>How do we write methods in Oberon? Methods are declared using the
keyword <code>PROCEDURE</code> followed by their name, a declaration of
any parameters and if the procedure returns a value (i.e. is a function)
it also includes that declaration. Next we declare any internal
variables needed by the procedure. This is followed by the procedure’s
body. The body of the procedure is defined by a <code>BEGIN</code> and
<code>END</code> statement structure. The body contains the steps the
procedure needs to execute.</p>
<p>We’ll create a procedure called “HelloWorld” in our new module. Since
we will use this procedure from our new <code>HelloWorld2.Mod</code> our
new “HelloWorld” procedure needs to be public. A public procedure in
<code>SayingHi.Mod</code> is available for use in our new
<code>HelloWorld2.Mod</code> (or by another module). Marking a procedure
public in Oberon is a little different than in other languages. A
Module’s procedure is public if its name ends with an asterisk. Below is
a sketch of our module <code>SayingHi.Mod</code> so far.</p>
<p>NOTE: This technique is also used to mark variables, records and
constants as public and available to other modules. Public variables are
“read only” in other modules.</p>
<pre class="oberon"><code>
    MODULE SayingHi;
      IMPORT Out;
    
      PROCEDURE HelloWorld*;
      BEGIN
        Out.String(&quot;Hello World!&quot;); Out.Ln;
      END HelloWorld;
    END SayingHi.
</code></pre>
<p>This modules looks allot like <code>HelloWorld.Mod</code> with a
couple key differences. Rather than relying on the module’s begin and
end statements we declare a procedure with its own begin and end
statements. Notice the procedures end statement includes the procedure
name and is terminated by semicolon rather than a period. Like
<code>HelloWorld.Mod</code> we import the <code>Out</code> module to
display our greeting.</p>
<h2 id="putting-it-all-together">Putting it all together</h2>
<p>Let’s create a new “Hello World” module called
<code>HelloWorld2.Mod</code> and use our <code>SayingHi</code> module
instead of directly importing <code>Out</code>.</p>
<pre class="oberon"><code>
    MODULE HelloWorld2;
      IMPORT SayingHi;
    BEGIN
      SayingHi.HelloWorld;
    END HelloWorld2.
</code></pre>
<p>We can compile our module with OBNC using the command</p>
<pre><code>
    obnc HelloWorld2.Mod
</code></pre>
<p>We can run our new “Hello World” with the command</p>
<pre><code>
    ./HelloWorld2
</code></pre>
<p>At this point we have a way of saying “Hello World!” whenever we need
in our Oberon programs. But just printing “Hello World!” to the screen
isn’t very interactive. It’d be nice if we could have the computer ask
our name and then respond with a greeting.</p>
<p>We’ll modify our SayingHi to include a new procedure called
“Greetings” and that procedure needs to ask us our name and then display
an appropriate greeting. “Greetings” will be a public procedure marked
by an asterisk like “HelloWorld”.</p>
<p>“Greetings” has three tasks</p>
<ol type="1">
<li>Ask politely for our name</li>
<li>Get the name typed in with our keyboard</li>
<li>Assemble and display a polite greeting</li>
</ol>
<p>To keep our “Greeting” procedure short we’ll split this up into some
private procedures. These will not be available outside
<code>SayingHi.Mod</code>. Here’s a sketch of our improved module.</p>
<pre class="oberon"><code>
    MODULE SayingHi;
      IMPORT In, Out;
    
      PROCEDURE HelloWorld*;
      BEGIN
        Out.String(&quot;Hello World!&quot;); Out.Ln;
      END HelloWorld;
    
      PROCEDURE AskOurName;
      BEGIN
        Out.String(&quot;Excuse me, may I ask your name? &quot;);
      END AskOurName;
    
      PROCEDURE GetName(VAR ourName : ARRAY OF CHAR);
      BEGIN
        In.Line(ourName);
      END GetName;
    
      PROCEDURE AssembleGreeting(ourName : ARRAY OF CHAR);
      BEGIN
        Out.String(&quot;Hello &quot;);Out.String(ourName);
        Out.String (&quot;, very nice to meeting you.&quot;); Out.Ln;
      END AssembleGreeting;
    
      PROCEDURE Greetings*;
        VAR ourName : ARRAY 256 OF CHAR;
      BEGIN
        AskOurName;
        GetName(ourName);
        AssembleGreeting(ourName);
      END Greetings;
    END SayingHi.
</code></pre>
<p>Now let’s add our Greetings procedure to
<code>HelloWorld2.Mod</code>.</p>
<pre class="oberon"><code>
    MODULE HelloWorld2;
      IMPORT SayingHi;
    BEGIN
      SayingHi.HelloWorld;
      SayingHi.Greetings;
    END HelloWorld2.
</code></pre>
<p>We compile and run it the same way as before</p>
<pre><code>
    obnc HelloWorld2
    ./HelloWorld2
</code></pre>
<p>When you run <code>HelloWorld2</code> you should now see something
like (I’ve answered “Robert” and pressed return after the second
line.</p>
<pre><code>
   Hello World!
   Excuse me, may I ask your name? Robert
   Hello Robert, very nice to meeting you.
</code></pre>
<h2 id="reading-our-code">Reading our code</h2>
<p>While our revised modules are still short they actually exercise a
number of language features. Let’s walk through the code block by block
and see what is going.</p>
<p><code>HelloWorld2.Mod</code> is responsible for the general
management of our program namely say “Hello World!” and also for
initiating and responding with a more personal greeting. It does this by
first importing our <code>SayingHi.Mod</code> module.</p>
<pre><code>
    IMPORT SayingHi;
</code></pre>
<p><a href="HelloWorld2.Mod">HelloWorld2.Mod</a> doesn’t have any of its
own procedures and like our original <a
href="HelloWorld.Mod">HelloWorld.Mod</a> relies on the module’s
initialization block to run our two public procedures from
<code>SayingHi</code>. It calls first <code>SayingHi.HelloWorld;</code>
then <code>SayingHi.Greetings'</code> before existing. Other than using
the <code>SayingHi</code> module it is similar in spirit to our first <a
href="HelloWorld.Mod">HelloWorld.Mod</a>.</p>
<p>Our second module <a href="SayingHi.Mod">SayingHi.Mod</a> does the
heavy lifting. It contains both public and private procedures. If you
tried to use <code>GetName</code> from <code>SayingHi</code> in
<code>HelloWorld2.Mod</code> you would get a compiler error. As far as
<code>HelloWorld2.Mod</code> is concerned <code>GetName</code> does not
exist. This is called information hiding and is an important capability
provided by Oberon’s Modules system.</p>
<h3 id="explore-sayinghi-more-deeply">explore <code>SayingHi</code> more
deeply</h3>
<p>In <code>SayingHi.Mod</code> we introduce two important concepts.</p>
<ol type="1">
<li>Public and Private procedures</li>
<li>variables to hold user input</li>
</ol>
<p><code>SayingHi.Mod</code> imports two module, <code>In</code> which
is for getting text input from the keyboard, and <code>Out</code> which
is used for displaying text to standard output.</p>
<pre class="oberon"><code>
    IMPORT In, Out;
</code></pre>
<p><code>In</code> and <code>Out</code> are to modules you will commonly
use to either receive input (<code>In</code>) from the keyboard or
display output (<code>Out</code>) to the terminal or shell. They provide
simple methods for working with variables and constants and built-in
Oberon data types. This is a very useful as it lets us focus our
procedures on operating on data rather than the low level steps needed
to interact with the operating system and hardware.</p>
<p>NOTE: <strong>basic types</strong>, Oberon has a number of basic
types, BYTE holds a byte as a series of bit, CHAR holds a single ASCII
character, INTEGER holds a signed integer value, REAL holds a floating
point number and BOOLEAN holds a True/False value.</p>
<p>The first procedure is <code>HelloWorld</code> and it’s pretty
straight forward. It displays a “Hello World!” message in our terminal.
It uses <code>Out</code>. <code>Out.String</code> to display the “Hello
World!” and <code>Out.Ln</code> to force a new line.
<code>Out.String</code> is responsible for displaying values that are of
type <code>ARRAY OF CHAR</code>. This includes text we provided in
double quotes.</p>
<pre class="oberon"><code>
    PROCEDURE HelloWorld*;
    BEGIN
      Out.String(&quot;Hello World!&quot;); Out.Ln;
    END HelloWorld;
</code></pre>
<p>The notable thing about <code>HelloWorld*</code> is its annotation
<code>*</code>. This asterisk indicates to the compiler that this is a
public procedure and should be made available to other modules.
Procedures, variables, constants, records (data structures) can be made
public with this simple annotation. If we left off the <code>*</code>
then we would not be able to use <code>HelloWorld</code> procedure from
other module.</p>
<p>Our second procedure is <code>AskOurName</code>. It’s private because
it lacks the <code>*</code>. It is invisible to
<code>HelloWorld2.Mod</code>. It is visible within <code>SayingHi</code>
module and we’ll use it later in <code>Greetings*</code>. Before a
procedure, variable, constant or record can be used it must be declared.
That is why we most define <code>AskOurName</code> before we define
<code>Greetings*</code>. <code>AskOurName</code> is in other respects
very similar to <code>HelloWorld*</code>.</p>
<pre class="oberon"><code>
    PROCEDURE AskOurName;
    BEGIN
      Out.String(&quot;Excuse me, may I ask your name? &quot;);
    END AskOurName;
</code></pre>
<p>Our third procedure <code>GetName</code> is a little more
interesting. It demonstrates several features of the Oberon language.
Most obvious is that it is the first procedure which contains a
parameter list.</p>
<pre class="oberon"><code>
    PROCEDURE GetName(VAR ourName: ARRAY OF CHAR);
</code></pre>
<p>There is allot packed in this single statement in addition to putting
a name to our procedure. Specifically it uses a <code>VAR</code> in the
parameter. Oberon provides two kinds of parameters in declaring
procedures. The two are <code>VAR</code> and static. A <code>VAR</code>
parameter means that the procedure is allowed to up date the value in
the memory location indicated by the name. A static variable (a
parameter without the <code>VAR</code> prefix passes in a read only
value. This allows us to distinguish between those procedures and
variables where that can be modified by the procedure and those which
will be left the same. Inside of <code>GetName</code> we call the
<code>In</code> module using the <code>Line</code>. This retrieves a
line of text (a sequence of keyboard strokes ended with the return
key).</p>
<pre class="oberon"><code>
    In.Line(ourName);
</code></pre>
<p>Because <code>ourName</code> was a variable parameter in
<code>GetName</code> it can be modified by <code>In.Line</code>.</p>
<p>Our next procedure <code>AssembleGreeting</code> is private like
<code>AskOurName</code> and <code>GetName</code>. Like
<code>HelloWorld*</code> and <code>AskOurName</code> it makes use of the
<code>Out</code> module to display content. Unlike
<code>HelloWorld*</code> it has a parameter but this time a static one.
Notice the missing <code>VAR</code>. This indicates that
<code>AssembleGreeting</code> doesn’t modify, cannot modify
<code>ourName</code>.</p>
<pre class="oberon"><code>
    PROCEDURE AssembleGreeting(ourName : ARRAY OF CHAR);
    BEGIN
      Out.String(&quot;Hello &quot;);Out.String(ourName);
      Out.String (&quot;, very nice to meeting you.&quot;); Out.Ln;
    END AssembleGreeting;
</code></pre>
<p>The use of <code>Out.String</code> is more elaborate then before.
Notice how we use trailing spaces to make the output more readable.</p>
<p>Our final procedure is public, <code>Greetings*</code>. It does not
have any parameters. Importantly it does include a variable for use
inside the procedure called <code>ourName</code>. The <code>VAR</code>
line declares <code>ourName</code> as an <code>ARRAY 256 OF CHAR</code>.
This declaration tells the compiler to allocate memory for storing
<code>ourName</code> while <code>Greetings*</code> is being executed.
The declaration tells us three things. First the storage is continuous
block of memory, that is what <code>ARRAY</code> means. The second is
the size of this memory block is 256 <code>CHAR</code> long and the that
we will be storing <code>CHAR</code> values in it.</p>
<p>The memory for <code>ourName</code> will be populated when we pass
the variable to <code>GetName</code> based on what we type at the
keyboard. If we type more than 256 ASCII characters they will be
ignored. After <code>GetName</code> records the typed character we use
the memory associated with the <code>ourName</code> variable we read
that memory to display what we typed in the procedure named
<code>AssembleGreeting</code>.</p>
<h3 id="going-a-little-deeper">Going a little deeper</h3>
<p>Oberon is a typed language meaning that variables are declared,
allocated and checked during compile time for specific characteristics.
The one variable we created <code>ourName</code> in the
<code>Greetings</code> procedure reserves the space for 256 <a
href="https://en.wikipedia.org/wiki/ASCII">ASCII</a> characters. In
Oberon we call a single ASCII character a <code>CHAR</code>. Since it
would be useful to work with more than one <code>CHAR</code> in
relationship to others Oberon also supports a variable type called
<code>ARRAY</code>. An <code>ARRAY</code> is represented as a block of
memory that is allocated by the Oberon run time. Because it is allocated
ahead of time we need to know its size (i.e. how many <code>CHAR</code>
are we storing). In our case we have declared
<code>ARRAY 256 OF CHAR</code>. That means we can hold names up to 256
ASCII characters.</p>
<p><code>Greetings*</code> does three things and the second thing,
<code>GetName</code> receives the characters typed at the keyboard.
<code>GetName</code> has a parameter list. In this case the only one
parameter is declared <code>VAR ourName : ARRAY OF CHAR</code>. Notice
the similarity and difference between the <code>VAR</code> statement in
<code>Greetings</code> versions the parameter list. Our
<code>GetName</code> can accept <strong>any</strong> length of
<code>ARRAY OF CHAR</code> and it <strong>only</strong> can accept an
<code>ARRAY OF CHAR</code>. If you try to pass another type of variable
to <code>GetName</code> the compiler will stop with an error
message.</p>
<p>Why is this important?</p>
<p>We’ve minimized the memory we’ve used in our program. Memory is
typically allocated on the stack (a block of memory made available by
the operating system to the program). We’ve told the operating system we
need 256 <code>CHAR</code> worth of consecutive memory locations when we
allocated room the variable <code>ourName</code> in
<code>Greetings</code>. When we invoke <code>GetName</code> Oberon knows
to use that same memory location for the value of <code>ourName</code>
defined in the parameter. In turn when <code>In.String(ourName);</code>
is called the module <code>In</code> knows to store the name typed on
the keyboard in that location of memory. When
<code>Out.String(outName);</code> is called the compiler knows to use
the same location of memory to send the contents to the display. When we
finally finish the <code>Greetings*</code> procedure the memory is
released back to the operating system for re-use by this or other
programs.</p>
<h3 id="what-weve-explored">What we’ve explored</h3>
<ol type="1">
<li>Using a module to break down a simple problem</li>
<li>Using a module’s ability to have public and private procedures</li>
<li>Touched on how memory is used in a simple interactive program</li>
</ol>
<h3 id="next-and-previous">Next and Previous</h3>
<ul>
<li>Next <a href="../18/Mostly-Oberon-Basic-Types.html">Basic
Types</a></li>
<li>Previous <a href="../11/Mostly-Oberon.html">Mostly Oberon</a></li>
</ul>
  </article>
</section>

<footer>
<p>copyright © 2016 - 2023 R. S. Doiel<br /> <a
href="/rssfeed.html">RSS</a> feeds and website built with <a
href="https://rsdoiel.github.io/pttk">pttk</a>, Bash, Make and <a
href="https://pandoc.org">Pandoc</a>.</p>
</footer>
<!-- START: PrettyFi from https://github.com/google/code-prettify -->
<script>
/* We want to add the class "prettyprint" to all the pre elements */
var pre_list = document.querySelectorAll("pre");

pre_list.forEach(function(elem) {
    elem.classList.add("prettyprint");
    elem.classList.add("linenums");/**/
    elem.classList.add("json"); /**/
});
</script>
<style>
li.L0, li.L1, li.L2, li.L3, li.L4, li.L5, li.L6, li.L7, li.L8, li.L9
{
    color: #555;
    list-style-type: decimal;
}
</style>
<link rel="stylesheet" type="text/css" href="/css/prettify.css">
<script src="https://cdn.jsdelivr.net/gh/google/code-prettify@master/loader/run_
prettify.js"></script>
<!--  END: PrettyFi from https://github.com/google/code-prettify -->

</body>
</html>