|
|
Hi,
So I have a little experience programming, mostly in Python (which will be the language I am using to write the assembler). However, I am a still a bit of a novice, and the concepts of object, class, and method are relatively new to me.
I am inclined to think in a functional way (seasoned programmers, forgive me if I'm abusing the term functional here); i.e., write a function that accepts a given input and spits out the desired output. This is (very roughly) how I imagine my assembler (assuming for now that input assembly programs have no symbols) would work, thinking in this way:
1. Accepts a text file as input.
2. Passes this text file into a list, with each line of the file a separate element of the list
3. Cleans the list to remove any whitespace, comment lines, and comments that were in the text file
4. Iterates through this cleaned up list. For each element of the list, breaks down the element into its constituent parts as necessary, converts them to their binary equivalents and reassembles them
5. Writes this converted list element into a new text file.
I want to avoid falling into old habits (i.e., trying to write a giant function that works as described above) and instead use an OOP approach. It seems that using objects, methods, and classes will make my code much more modular. I can see from reading chapter 6 in the book, watching the coursera lecture videos, etc. that I should start by creating a Parser class in which I define a bunch of methods (advance(), commandType(), etc.) so that I can then create a Parser object on which I can use my Parser methods. I am struggling a bit with conceptualizing my code in this way, so I'd like some feedback on the following framework I've come up with so far. Is it way off base or a valid approach?
Create a Parser class and define all needed methods
Read in the assembly program text file, convert it to a list (with each line being a separate element), and "clean up"/remove list elements as necessary
Let each element of the finalized list be a Parser object. Apply all Parser methods to the object as necessary.
|
|
Your functional idea sounds good. You don't need to make it one giant function though. In fact, you should try and break it up in smaller ones, each of which do just one thing.
You can send me an email with your code and I'll take a look and try to help you with it.
|
|
Thanks for the input and the offer to review, Ivant! I will hopefully have a first draft written up by the end of this coming weekend. If I am still dealing with errors, I will send it along.
|
|
Hi Ivant,
I've got a working assembler that implements the framework I described in my current post. I still would be curious to see a program written in the OOP paradigm, however. If you have a version of the assembler written in this paradigm, would you be willing to swap code so I could compare and contrast with my own program?
|
|
My version is written in Java, but it's not OOP. I don't think you need an OOP approach for this program, because it is quite straight-forward. Also, a lot of the things OOP stands for (or at least used to stand for) have changed.
Early OOP used to emphasize on deep inheritance hierarchies. Later people found out that inheritance causes tight coupling between concepts and that weak coupling is preferable most of the time. This is often achieved by using single-responsibility interfaces, where different implementations can be plugged depending on the situation. And single-responsibility often translates to single-method interfaces, which are very similar to functions (or more generally closures).
Then you have encapsulation of mutable state. It turns out that mutable state creates a lot of problems even if it is encapsulated in objects. For example consider these two classes:
class A {
...
}
class B {
private final A a;
public B(A a) {
validate(a);
this.a = a;
}
public doSomething() {
// ... uses a
}
...
}
Class B receives an instance of class A (or of it's descendant) and it validates and stores it in a local reference. So, can it now use it without further validation? It depends. If A is an immutable "value" type, then yes, because we validated it, and it cannot change. But if it's not guaranteed to be immutable, then we'll have to at least partially validate it before usage.
And this is even in a single thread program! Things get much more complicated if you have concurrent modifications.
OOP languages start to give you a way to create guaranteed Value types. So much for the encapsulated mutable state.
Polymorphism is an important concept of OOP:
interface Closable {
void close();
}
class File implements Closable {
...
void close() {
...
}
...
}
class Connection implements Closable {
...
void close() {
...
}
...
}
Closable a = new File();
Closable b = new Connection();
a.close();
b.close();
Polymorphism means, that a.close() will call the method in File and b.close() will call the method in Connection.
But it is by no means unique to OOP. In fact, the polymorphism in OOP is limited to "this", the implicit first parameter. Languages like Common Lisp and Clojure (and many others, I'm sure) implement a much more powerful version.
In a nutshell, even thoguh I program in Java professionally and technically I write OOP code, I'm not sure what OOP really stands for anymore.
|
|