{ |one, step, back| }

The Shape Example in awk

Contributed by Scott Anderson

THIS one is a monstrosity: OO-awk.

Note that the framework can be used by any OO awk program, since the objects are defined in a separate file.

Features:

Put all the files in a single directory, and run it with the command "awk -f test.awk".

Don't blame me if your machine melts down; it's quite slow. :-)

Code for awk

File: circle-draw.awk

# draw a circle
BEGIN { 
  # get the members from the runtime data
  "awk -f getdict.awk -v variable="name" -v member=slot
-v field=x "runtimedata | getline x;
  "awk -f getdict.awk -v variable="name" -v member=slot
-v field=y "runtimedata | getline y;
  "awk -f getdict.awk -v variable="name" -v member=slot
-v field=radius "runtimedata | getline radius;

  printf("Drawing circle at: (%s,%s), radius: %s\n", x, y, radius);
}

File: createobject.awk

# create an object

# for whatever reason, 'foo in bar' does not seem to work as it should.
# I need to save the index regardless
function findit(val, arr)
{
  for (g in arr) {
    if (arr[g] == val) return g;
  }

  return 0;
}

BEGIN { 
  FS = ":";

  # split up the arguments: arg1:val1,arg2:val2,etc.
  split(oargs, apairs, ",");

  i = 0;
  for (p in apairs)
    {
      split(apairs[p], apair, ":");
      aargs[i] = apair[1];
      avals[i] = apair[2];
      i = i + 1;
    }

  # place the type of the new object in the runtime data
  "awk -f setdict.awk -v variable="name" -v member=class
-v field=type -v data="type" "runtimedata | getline x;
}

# for each slot in the type
(($1 == type) && ($2 == "slot")) {
  i = findit($3, aargs);

  # if we have been given an argument of the same name of the slot
  if (i)
    {
      # insert the value in the slot for the new object
      "awk -f setdict.awk -v variable="name" -v
member=slot -v field="$3" -v data="avals[i]"
"runtimedata | getline x;
    }
  else
    {
      # insert the default value in the slot for the new object
      "awk -f setdict.awk -v variable="name" -v
member=slot -v field="$3" -v data="$4"
"runtimedata | getline x;
    }
}

END {
}

File: doinherit.awk

# run through the class data, and copy all slots and methods for the
inherited class
BEGIN { 
  FS = ":";
}

($1 == super) {
  "awk -f setdict.awk -v variable="class" -v
member="$2" -v field="$3" -v data="$4"
"FILENAME | getline x;
}

END {
}

File: getdict.awk

# pull a particular value from the runtime data hash file
BEGIN { 
  FS=":";
}

(($1 == variable) && ($2 == member) && ($3 == field)) {
  print $4;
  fflush();
}

END {
}

File: readaoo.awk

# read in the object definitions and place them in the object data file
BEGIN { 
  FS = ":";
  system("rm "dictfile" > /dev/null");
  system("touch "dictfile" > /dev/null");
}

substr($0, 1, 1) == "#" {}

# inherit line: go copy all the slots and method for the superclass
($2 == "inherit") {
  print "inherit: "$0;
  "awk -f doinherit.awk -v super="$3" -v
class="$1" "dictfile | getline x;
}

# insert a method into the vtable
($2 == "method") {
  print "method: "$0;
  "awk -f setdict.awk -v variable="$1" -v member=method
-v field="$3" -v data="$4" "dictfile | getline
x;
}

# insert a slot into the vtable
($2 == "slot") {
  print "slot: "$0;
  "awk -f setdict.awk -v variable="$1" -v member=slot -v
field="$3" -v data="$4" "dictfile | getline x;
}

END {
}

File: rectangle-draw.awk

# draw a rectangle
BEGIN { 
  # get the members from the runtime data
  "awk -f getdict.awk -v variable="name" -v member=slot
-v field=x "runtimedata | getline x;
  "awk -f getdict.awk -v variable="name" -v member=slot
-v field=y "runtimedata | getline y;
  "awk -f getdict.awk -v variable="name" -v member=slot
-v field=width "runtimedata | getline width;
  "awk -f getdict.awk -v variable="name" -v member=slot
-v field=height "runtimedata | getline height;

  printf("Drawing rectangle at: (%s,%s), width: %s, height:
%s\n", x, y, width, height);
}

File: rectangle-set-height.awk

# set the height for a rectangle
BEGIN { 
  "awk -f setdict.awk -v variable="name" -v member=slot
-v field=height -v data="aargs[1]" "runtimedata | getline
x;
}

File: rectangle-set-width.awk

# set the width for a rectangle
BEGIN { 
  "awk -f setdict.awk -v variable="name" -v member=slot
-v field=width -v data="args" "runtimedata | getline x;
}

File: setdict.awk

# set a value in a dictionary file
BEGIN { 
  FS=":";
}

# look for the proper dictionary entry and copy it with the new value
{
  if (($1 == variable) && ($2 == member) && ($3 == field)) {
    printf("%s:%s:%s:%s\n", variable, member, field, data) >>
FILENAME".tmp";
    foundit = "YES";
  }
  else {
    # not a match; simply copy the existing values
    print >> FILENAME".tmp";
  }
}
  

END {
  # if we didn't find it, add a new entry to the dictionary
  if (foundit != "YES") {
    printf("%s:%s:%s:%s\n", variable, member, field, data) >>
FILENAME".tmp";
  }

  # move the new information into the old dictionary file
  close(FILENAME".tmp");
  system("mv "FILENAME".tmp "FILENAME);
}

File: shape-rel-move-to.awk

# move a shape relative to its origin
BEGIN { 
  split(args, aargs, ",");

  # get the old origin
  "awk -f getdict.awk -v variable="name" -v member=slot
-v field=x "runtimedata | getline x;
  "awk -f getdict.awk -v variable="name" -v member=slot
-v field=y "runtimedata | getline y;

  # add the deltas
  newx = aargs[1] + x;
  newy = aargs[2] + y;

  # write the new values
  "awk -f setdict.awk -v variable="name" -v member=slot
-v field=x -v data="newx" "runtimedata | getline a;
  "awk -f setdict.awk -v variable="name" -v member=slot
-v field=y -v data="newy" "runtimedata | getline b;
}

File: shapes.aoo

# object definition file for Scott's god-awful OO-awk engine

# define the base class
class:shape

# give it two slots for the origin
shape:slot:x:0
shape:slot:y:0

# give it two move methods
shape:method:move-to:shape-move-to
shape:method:rel-move-to:shape-rel-move-to

# define the rectangle class
class:rectangle

# inherit the slots and methods from shape
rectangle:inherit:shape

# give it width and height slots
rectangle:slot:width:0
rectangle:slot:height:0

# give it draw and set methods
rectangle:method:draw:rectangle-draw
rectangle:method:set-width:rectangle-set-width
rectangle:method:set-height:rectangle-set-height

# define the circle class
class:circle

# inherit the slots and methods from shape
circle:inherit:shape

# give it a radius slot
circle:slot:radius:0

# give it a draw method
circle:method:draw:circle-draw

File: test.awk

# polymorphic example in awk
# November 8, 1999 - Scott Anderson
# Good god, why did I do this...

# create a new object
function new_object(type, name, args)
{
  "awk -f createobject.awk -v type="type" -v
name="name" -v oargs="args" -v
runtimedata="runtimedata" "shapedict | getline x;
  return name;
}

# clear and create a file
function prepare_file(file)
{
  system("rm "file" > /dev/null");
  system("touch "file" > /dev/null");
}

BEGIN { 
  shapedict = "shape.dict";
  shapedata = "shapes.aoo";
  runtimedata = "shape.run";

  prepare_file(shapedict);
  prepare_file(runtimedata);

  # read the object definitions into a dictionary
  "awk -f readaoo.awk -v dictfile="shapedict"
"shapedata | getline x;

  # create some shapes
  scribble[0] = new_object("rectangle", "rec1",
"x:10,y:20,width:5,height:6");
  scribble[1] = new_object("circle", "circ1",
"x:15,y:25,radius:8");

  # loop through and polymorphically draw and move the shapes
  for (s in scribble)
    {
      system("awk -f callmethod.awk -v obj="scribble[s]"
-v method=draw -v args="" -v
runtimedata="runtimedata" -v shapedict="shapedict);

      system("awk -f callmethod.awk -v obj="scribble[s]"
-v method=rel-move-to -v args=100,100 -v
runtimedata="runtimedata" -v shapedict="shapedict);

      system("awk -f callmethod.awk -v obj="scribble[s]"
-v method=draw -v args="" -v
runtimedata="runtimedata" -v shapedict="shapedict);

    }

  # create a rectangle
  # note the use of the default values
  rec = new_object("rectangle", "rec2",
"width:15,height:15");

  # call a rectangle-specific method
  system("awk -f callmethod.awk -v obj="rec" -v
method=set-width -v args=30 -v runtimedata="runtimedata" -v
shapedict="shapedict);

  # draw the rectangle
  system("awk -f callmethod.awk -v obj="rec" -v
method=draw -v args="" -v runtimedata="runtimedata"
-v shapedict="shapedict);

  exit;
}

Output

Drawing rectangle at: (10,20), width: 5, height: 6
Drawing rectangle at: (110,120), width: 5, height: 6
Drawing circle at: (15,25), radius: 8
Drawing circle at: (115,125), radius: 8
Drawing rectangle at: (0,0), width: 30, height: 15