📜 ⬆️ ⬇️

Oberon Interpreter on Go

Introduction


The story moves in a spiral. The authors of Go, having risen on the shoulders of a reputable Google corporation, were able to bring their ideal programming language to life, as Niklaus Wirth once brought Oberon, his own steadfastness and prestige, into this world while fighting the emerging trend of complication and extensive development of mainstream languages.

Today, the Oberon language is forgotten, but the Wirth affair manifested itself in the Go language completely unexpectedly. In some aspects of the implementation of the similarity of approaches is not in doubt. Of course, each language is unique in its own way and there is no need to make another from one language. Time not to turn back. But at different times, people have sought to preserve the technology that is gone. What are carefully stored emulators Spectrum and MSX, museums of old computers in different countries. Emulation, as one way to stop the moment, has firmly established itself.

It is generally accepted that the Go language has found itself in the niche of network services. But we will go the other way.

Task


The history of the language Oberon has several language options. Each editor N. Wirth created for a specific project, his languages ​​were always created for the task based on its goals. Oberon-2 was created as an Oberon variant with a developed data type mechanism, implementing the concept of OOP. The students of N. Wirth decided that it was Oberon-2 that was suitable for industrial programming. They supplemented the language with features, increased the type system to compatible with the increasingly popular Java, implemented the idea of ​​true modularity and released the BlackBox product with the language Component Pascal (KP) inside.
')
As time went on, corporations from overseas entered the market more successfully and BlackBox went into the shadows, remaining the object of study for a small group of enthusiasts, including those from Russia and the CIS countries.

One of the KP principles is modularity, the ability to change the implementation of a component without changing its interface (as applied to objects, this is called pimpl). And so I thought, how does a particular framework differ from the implementation of a component? And the language description is its interface ... Simply put, it occurred to me to implement an interpreter with the functions of the framework. That is, with the support of modularity, as described in the message about the language.

Investigation


A feature of the BlackBox framework is the dynamic linking of modules in the process memory. As you know, the process of creating a program from the source code consists of several stages. In different implementations of different compilers, these stages can be hidden, skipped, primitivized. A link is the reduction of a machine code to a form suitable for direct execution by the processor. For example, in Go, linking is static, at compile time. And in the BlackBox dynamic, at the stage of loading the module body into memory. An interesting feature of this solution is that the code is independent of the operating system, when the same machine code is executed in different operating systems without recompilation, provided that the architecture is the same (x86, for example).

However, the BlackBox compiler provides another possibility - changing the code generator for a specific architecture. This result is achieved by using the structure of AST - an abstract syntax tree for the platform-independent preservation of the structure of programs and data inside the compiler. It was possible to find out that BlackBox was originally written for the Mac + PowerPC platform, and only then rewritten under Wintel. At the same time, the compiler remained almost unchanged. Removable backend today is not something surprising, but in the 90s it was unusual and new.

Thus, these two features allowed me to create my backend. Without much effort, I transferred AST to XML format, more precisely, to graphml, with an eye to a beautiful visualization of the interpretation process. There were plenty of plans.

Decision


To implement, I chose the Go language, although I could choose Java, C #, Dart, and others. Why? There are several reasons, the main one - the information that Go is similar in nature to Oberon, cuts off all the excess from the process of expressing the subject area. Take, for example, the exceptions, these lightweight, legalized, supposedly harmless counterparts of the GOTO operator.

A great reason to learn Go.

I called the project simply: Framework .

A brief excursion into the description of the language - and into battle. Deserializing xml into structures using tags is excellent. Hiding data in packets is excellent, apply our favorite pimpl. Duck typing interfaces at first confused, but after a couple of cones came understanding. The main trap here is a comparison of opposite interfaces with the same set of methods. This set can be formed historically, and even an experienced developer can find that a type A Tipswitch processes type B and naturally drops.

So, AST is loaded into memory, the main nodes are described, the rules of interpretation are known. As it turned out - there are a lot of node types. Data types even more. One of the basic principles that I adopted is the principle of let it fall; in any incomprehensible situation, the program ends its work. Of course, the home project allowed such liberties.

Interpreting the algorithms is interesting. AST language KP consists of operators, expressions and pointers to objects (statement, expression, designator). Operators follow each other, change objects, change the course of the program depending on the subordinate nodes (expressions or pointers). In this case, the subordinate nodes are two - left and right. There are also additional nodes, such as link and object.

To begin with, we will choose a data traversal scheme - I assumed that there is no need to invent anything, everything has already been invented, so I used the stack to process nodes in depth and non-recursively traverse nodes from one expression to another. Each node in the stack corresponds to a frame in which the result of processing the child nodes and instructions from the parent nodes is stored. The specific actions for a particular node are the main part of the program interpretation. The program runs until the statements that control the flow of execution add one or more of the following statements to the stack for execution.

As soon as the stack is empty or the result of the iteration returns an error code, the program stops. Nothing complicated.
To store data in the high-level environment of the Go language, one can describe various types of data, including primitive ones. In this I saw another advantage of the Go language for the task of implementing an interpreter.

A complete list of the rules according to which the AST tree is made can be seen here

Example


I will briefly describe the process of interpreting the nodes of a simple program.

MODULE XevDemo2; VAR i: INTEGER; PROCEDURE Init; BEGIN i:=1; END Init; PROCEDURE Stop; BEGIN i:=0; END Stop; BEGIN Init CLOSE Stop END XevDemo2. 




Results and prospects


At the moment, the interpreter supports the full set of KP language operators. The type system, inheritance, extension of methods and so on are also supported; I did not mention them in order not to overload the article.

Some problems are in the management of complex data structures, such as an array of arrays of records and so on. But it is already a matter of time - carefully check the work algorithms and correct errors.

The original plans to create specialized DSL for the different cases of life from the KP language had to be postponed. It turned out that even the simple task of interpreting the not very difficult in terms of features of the KP language comes up against the problem of the complexity of the subject area, when a bunch of implementation nuances have to exist and work together. Here it is worth shaking hands with the authors of the JVM, one can only imagine what tasks they solved in the process of developing the environment, especially in the early stages.

I added the Go language to the collection of knowledge, a bunch of materials about programming languages, interpretation, modified semantics of the language, and so on. At the end of the work, I realized that the abstract syntax tree in its current form can be applicable not only for KP, but also for similar imperative languages. Already after the completion of the main works, I learned that the Lua language interpreter for Go was released.

In a sense, history once again moved in a spiral, it turned out that the language of KP already in the 90s was an extract of the basic ideas of imperative programming, which, with a fair wind, could radically change all that we are dealing with now.

One can only speculate on how, like the butterfly effect, jokes about “pascal for learning” and “ridiculous syntax” have changed the future of the industry for decades to come.

Source: https://habr.com/ru/post/252677/


All Articles