Contributed by Scott Anderson
I had to implement my own control language and build a stack-based processor to get this one.
This is my first sed program (!) so go easy on the criticism... ;-)
Put the files in a directory and execute the following command:
sed -n -f shapes.sed shapes.soo
# November 12, 1999 - Scott Anderson
# OO sed engine with code for a polymorphic shape example.
# I'm going to Hell, I just know it.
# Features:
# Single inheritance, dynamic dispatch, multiple return values on
# functions, possible use of method signatures, superclass constructors
# are automatically called.
# Where to start... the program stores the class and instance
# information in a delimited form. See the shapes.soo file for details.
# Function calls and arguments are stacked on the front of the stream,
# while return values are stacked on the end. The datastream is held in
# the hold area while a new command is read, then carried around the call
# stack with each function call.
: start
# Ignore comments
/[^#]*#.*/{
s/\([^#]*\)#.*/\1/
}
# Skip blank lines
/^$/{n;b start}
# prepend the command with a delimiter
s/^/%/
: main
# Read the class definitions and put them in the hold area
/^%def ~.\+~/{
s/%def //
H
x
s/^\n*//g
s/~\n~/~~/g
x
b skipend
}
# Exit command, eh?
/^%exit.*/{
q
}
# Append the hold area data to the current command if it isn't already
there
/[\n~]/!{G;h;}
# instantiate a class
/^%obj/{
tobj1
:obj1
# Class with no super class to inherit
s/%obj \([^ ]\+\) \([^ ]\+\).*\n.*~\2~__\([^~]\+\)~.*/%\2&!\1!\3/
t endCreate
# inherit a superclass by copying the superclass data
s/%obj \([^ ]\+\) \([^ ]\+\).*\n~.*~\2~_\([^_]\+\)_.*/%\3&!\1!/
s/\(.*\)%obj [^ ]\+ \([^
]\+\).*\n.*~\1~_[^_]*_\([^~]\+\)~.*~\2~_\1_\([^~]\+\)~.*/\2%&\3\4/
: endCreate
# have everything in the system inherit a base object if you wish
s/%obj\( [^ ]\+ \)[^ ]\+ /%baseobj\1/
}
# Set a property on an object
# format: setprop objReference properyName newValue
# Simply performs a substitution on the proper field.
/^%setprop /{
s/\([^ ]\+\) \([^ ]\+\) \([^ ]\+\) \([^
%\n]\+\)\(.*!\)\2\(![^!]*-\)\3\(,\)[^-]\+\(-.*\)/\1\5\2\6\3\7\4\8/
b popfunc
}
# Get a property value
# format: getprop objReference properyName
# return: @value on the end of the datastream
/^%getprop /{
s/\([^ ]\+ \)\([^ ]\+\) \([^
%\n]\+\)\(.*!\)\2\(![^!]*-\)\3\(,\)\([^-]\+\)\(-.*\)/\1\2
\3\4\2\5\3\6\7\8@\7/
b popfunc
}
# Dynamic dispatch. Look up a method call in the datastream and
substitute the actual method call, then place the
# result back on the call stack.
/^%call /{
s/%call \([^ ]\+\) \([^
%\n]\+\)\(.*\n\)\(.\+!\)\1\(![^!]\+=\)\2\(,\)\([^=]\+\)\(.\+\)/%\7
\1\3\4\1\5\2\6\7\8/
b main
}
# constructors
# format: shape x y
/^%shape/{
tshape1
:shape1
# set the origin from the arguments by placing two setprop calls on the
stack
s/\(^%shape[^ ]* \)\([^ ]\+\) \([0-9]\+\) \([0-9]\+\)\(\n.*\)/%setprop
\2 x \3%setprop \2 y \4\1\5/
t main
b popfunc
}
# format: rectangle x y w h
/^%rectangle/{
trectangle1
:rectangle1
# set the width and height from the last two args. since the object
construction routine places the superclass
# constructor on the call stack, the remaining arguments will be passed
on to the shape constructor.
s/\(^%rectangle[^ ]\+ \)\([^ ]\+\)\( [0-9]\+ [0-9]\+\) \([0-9]\+\)
\([0-9]\+\)\(\n.*\)/%setprop \2 w \4%setprop \2 h \5\1\2\3\6/
t main
b popfunc
}
# format: circle x y r
/^%circle/{
tcircle1
:circle1
# set the radius from the last arg. since the object construction
routine places the superclass
# constructor on the call stack, the remaining arguments will be passed
on to the shape constructor.
s/\([^ ]\+ \)\([^ ]\+\)\( [0-9]\+ [0-9]\+\)
\([0-9]\+\)\(\n.*\)/%setprop \2 r \4\1\2\3\5/
t main
b popfunc
}
# null function for the base object.
/^%baseobj/{
b popfunc
}
# class methods
# move a shape relative to its origin
# the first part adds the numbers to the properties
/^%srmove [^%\n ]\+ [^%\n ]\+ [^%\n]\+[%\n]/{
trmove1
:rmove1
# get the origin by pushing two getprop calls on the stack
# note that this is a variant method of getting two properties and not
as clean
/@/!s/^\(%srmove \)\([^ ]\+\)\([^\n]*\n.*\)/%getprop \2 x\1\2\3/
/^[^@]\+@[0-9]\+$/s/^\(%srmove \)\([^ ]\+\)\([^\n]*\n.*\)/%getprop \2
y\1\2\3/
t main
# push the second part of the function call on the stack, followed by
two add calls.
s/%srmove \([^ ]\+\) \([0-9]\+\)
\([0-9]\+\)\([^@]\+\)@\([0-9]\+\)@\([0-9]\+\)$/%add \2 \5%add \3
\6%srmove \1\4/
t main
b popfunc
}
# finish function to actually modify the data
/^%srmove [^%\n ]\+[%\n]/{
trmove2
:rmove2
# store the results of the add calls to the origin
s/[^ ]\+ \([^ ]\+\)\([%\n].*\)@\([0-9]\+\)@\([0-9]\+\)/%setprop \1 x
\3%setprop \1 y \4\2/
t main
b popfunc
}
# set the width of a rectangle
/^%rsetw/{
trsetw1
:rsetw1
# push a setprop call on the call stack
s/\(^%rsetw\) \([^ ]\+\)\ \([0-9]\+\)\(\n.*\)/%setprop \2 w \3\1\4/
t main
b popfunc
}
# draw a rectangle
/^%rdraw/{
trdraw1
:rdraw1
# push a call to getprop for x, y, w, and h on the callstack
# I'll clean this up some time to make it one operation.
/@/!s/^\(%rdraw \)\([^ ]\+\)\([^\n]*\n.*\)/%getprop \2 x\1\2\3/
/^[^@]\+@[0-9]\+$/s/^\(%rdraw \)\([^ ]\+\)\([^\n]*\n.*\)/%getprop \2
y\1\2\3/
/^[^@]\+@[0-9]\+@[0-9]\+$/s/^\(%rdraw \)\([^
]\+\)\([^\n]*\n.*\)/%getprop \2 w\1\2\3/
/^[^@]\+@[0-9]\+@[0-9]\+@[0-9]\+$/s/^\(%rdraw \)\([^
]\+\)\([^\n]*\n.*\)/%getprop \2 h\1\2\3/
t main
# pull the results off and print it
h
s/.\+@\(.\+\)@\(.\+\)@\(.\+\)@\(.\+\)$/Drawing rectangle at: (\1,\2),
width: \3, height: \4/p
x
s/\([^@]\+\)@.\+$/\1/
h
b popfunc
}
#draw a circle
/^%cdraw/{
tcdraw1
:cdraw1
# push a call to getprop for x, y, and r on the callstack
# I'll clean this up some time to make it one operation.
/@/!s/^\(%cdraw \)\([^ ]\+\)\([^\n]*\n.*\)/%getprop \2 x\1\2\3/
/^[^@]\+@[0-9]\+$/s/^\(%cdraw \)\([^ ]\+\)\([^\n]*\n.*\)/%getprop \2
y\1\2\3/
/^[^@]\+@[0-9]\+@[0-9]\+$/s/^\(%cdraw \)\([^
]\+\)\([^\n]*\n.*\)/%getprop \2 r\1\2\3/
t main
# pull the results off and print it
h
s/.\+@\(.\+\)@\(.\+\)@\(.\+\)$/Drawing circle at: (\1,\2), radius: \3/p
x
s/\([^@]\+\)@.\+$/\1/
h
b popfunc
}
# Add two numbers.
# Works by decrementing the right number while incrementing the left
number.
# Does not handle negative numbers.
/^%add /{
h
s/%add \([0-9]\+\) \([0-9]\+\).\+/\1 \2/
: repeat
# prepare 9s on the number to be incremented
: eat9
s/9\(_* \)/_\1/
t eat9
# prepare 0s on the number to be decremented
: eat0
s/\(.\+ .*\)0\(=*$\)/\1=\2/
t eat0
# switch tailing _s to 0s on the increment number
s/^\(_* \)/0\1/
# decrement the left number
s/8\(_* \)/9\1/
s/7\(_* \)/8\1/
s/6\(_* \)/7\1/
s/5\(_* \)/6\1/
s/4\(_* \)/5\1/
s/3\(_* \)/4\1/
s/2\(_* \)/3\1/
s/1\(_* \)/2\1/
s/0\(_* \)/1\1/
# replace remaining _s with 0s
s/_/0/g
# increment the right number
s/1\(=*\)$/0\1/
s/2\(=*\)$/1\1/
s/3\(=*\)$/2\1/
s/4\(=*\)$/3\1/
s/5\(=*\)$/4\1/
s/6\(=*\)$/5\1/
s/7\(=*\)$/6\1/
s/8\(=*\)$/7\1/
s/9\(=*\)$/8\1/
# replace remaining =s with 9s
s/=/9/g
# if the right number isn't all 0s, repeat
/ 0*$/!b repeat
# store the results on the return value stack and pop the call
s/ .*$//
s/^/@/
G
s/^\(@[0-9]\+\)\n\(.\+\)/\2\1/
b popfunc
}
# catchall to manage the loop
{
b skipend
}
# pop a function off the function call stack
: popfunc
{
s/%[^%\n]*\n*//
h
b main
}
: skipend
# This is the control file for the shapes program. # The dynamic dispatch and specific methods are implemented # in shapes.sed. # class definitions: # Uses the form ~classname~_superclassIfAny_-field,defaultvalue-*=method,vtableName=~ def ~shape~__-x,0--y,0-=move,smove==rmove,srmove=~ def ~rectangle~_shape_-w,0--h,0-=setw,rsetw==seth,rseth==draw,rdraw=~ def ~circle~_shape_-r,0-=setr,csetr==draw,cdraw=~ # Program calls: # Create a rectangle and a circle obj r1 rectangle 10 20 5 6 obj c1 circle 15 25 8 # Draw and move each of the polymorphically call r1 draw call r1 rmove 100 100 call r1 draw call c1 draw call c1 rmove 100 100 call c1 draw # create another rectangle obj r2 rectangle 0 0 15 15 # call a rectangle specific function call r2 setw 30 # draw it call r2 draw # redundant as the program will exit anyways. exit
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