aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRobin Haberkorn <robin.haberkorn@googlemail.com>2013-12-05 18:39:11 +0100
committerRobin Haberkorn <robin.haberkorn@googlemail.com>2013-12-05 18:39:11 +0100
commitb96a4189d89146cdc6bba5e2c477a42b46f787cd (patch)
tree8965e9904b8fc1de286fbaa70cb28a46fbedc2e6
downloadgroff-tools-b96a4189d89146cdc6bba5e2c477a42b46f787cd.tar.gz
initial commit of files from my bachelor-thesis repo
-rwxr-xr-xebnf.sno138
-rw-r--r--groff.outlang72
-rwxr-xr-xhighlight.sno28
-rw-r--r--plantuml.cfg10
-rw-r--r--sql.lang46
-rw-r--r--syntax.pic110
-rw-r--r--uml-graph/sequence.pic407
-rwxr-xr-xuml.sno40
8 files changed, 851 insertions, 0 deletions
diff --git a/ebnf.sno b/ebnf.sno
new file mode 100755
index 0000000..682be78
--- /dev/null
+++ b/ebnf.sno
@@ -0,0 +1,138 @@
+#!/usr/local/bin/snobol4 -b
+
+***
+*** Definitions
+***
+ define('optspace(pattern)')
+ define('string()')
+ define('serialize()')
+ strings = array(2)
+ str_no = 0
+
+ define('emit_groff(str)')
+
+ define('begin_rule()')
+ define('end_rule()')
+
+ define('emit_nonterm()')
+ define('emit_term()')
+ define('emit_empty()')
+
+ define('begin_exp()')
+ define('end_exp()')
+
+ define('begin_alt()')
+ define('end_alt()')
+ define('repeat()')
+ define('repeat_with()')
+
+ &anchor = 1
+ &fullscan = 1
+
+***
+*** EBNF grammar
+***
+ letter = &lcase &ucase
+ digit = "0123456789"
+
+ space = span(" " char(9) char(10) char(13)) | ""
+ eol = break(char(10) char(13))
+ nonterm = ( "`" break("`") $ *string() "`"
++ | (any(letter) (span(letter digit "_") | "")) $ *string() )
++ (optspace("~") *nonterm | "")
+ term = ( "'" break("'") $ *string() "'"
++ | '"' break('"') $ *string() '"' )
++ (optspace("~") *term | "")
+
+ lhs = nonterm
+ exp = nonterm *emit_nonterm()
++ | term *emit_term()
++ | optspace("[") *emit_empty() *begin_alt() *rhs optspace("]") *end_alt()
++ | optspace("{") *begin_exp() *rhs optspace("}") *end_exp()
++ (optspace("~") term *repeat_with() | *repeat())
++ | optspace("(") *begin_exp() *rhs optspace(")") *end_exp()
++ | *emit_empty()
+ rhs = exp
++ ( optspace("|") *begin_alt() *rhs *end_alt()
++ | optspace(",") *rhs
++ | "")
+
+* NOTE: have to reset str_no if there is no lhs, since
+* strings may already contain a nonterminal
+ rule = ( lhs optspace("=" | ":=" | "::=")
++ | *?(str_no = 0) "" $ *string() )
++ *begin_rule() rhs optspace(";") *end_rule()
+ comment = optspace("(*" breakx("*") "*)" | "#" eol)
+ groff = optspace("." eol $ str) *emit_groff(str)
+ pic = optspace("%" eol $ output)
+ grammar = arbno(comment | groff | pic | rule) rpos(0)
+
+***
+*** MAIN
+***
+ lineno = 1
+
+loop line = input :f(end)
+ line ".lf " int . lineno :s(next)
+ line ".EBNF" :s(src.l)
+ lineno = lineno + 1
+next output = line :(loop)
+
+src.l line = input
+ lineno = lineno + 1
+ line ".EBNF" :s(compile)
+ src = src line char(10) :(src.l)
+
+compile
+ output = ".PS"
+ output = 'copy "syntax.pic";'
+ (src ? grammar, terminal = "FAILURE")
+ output = 'reset;'
+ output = ".PE"
+ output = ".lf " (lineno + 1)
+ src = "" :(loop)
+
+***
+*** Procedures
+***
+optspace
+ optspace = space pattern space :(return)
+
+string
+ string = .strings[str_no = str_no + 1] :(nreturn)
+serialize
+* NOTE: will leave str_no == 0
+ serialize = '"' strings[str_no] '" ' serialize
+ eq(str_no = str_no - 1, 0) :s(return)f(serialize)
+
+emit_groff
+ output = 'command ".' str '";' :(return)
+
+begin_rule
+ output = 'begin_rule(' serialize() ');' :(return)
+end_rule
+ output = 'end_rule();' :(return)
+
+emit_nonterm
+ output = 'nonterminal(' serialize() ');' :(return)
+emit_term
+ output = 'terminal(' serialize() ');' :(return)
+emit_empty
+ output = 'empty();' :(return)
+
+begin_exp
+ output = 'begin_group();' :(return)
+end_exp
+ output = 'end_group();' :(return)
+
+begin_alt
+ output = 'begin_alt(last []);' :(return)
+end_alt
+ output = 'end_alt(2nd last []);' :(return)
+
+repeat output = 'repeat(last []);' :(return)
+repeat_with
+ output = 'repeat_with(last [],' serialize() ');' :(return)
+
+end
+
diff --git a/groff.outlang b/groff.outlang
new file mode 100644
index 0000000..60e217e
--- /dev/null
+++ b/groff.outlang
@@ -0,0 +1,72 @@
+extension "mom"
+
+doctemplate
+"
+.NEWCOLOR green RGB #33CC00
+.NEWCOLOR red RGB #FF0000
+.NEWCOLOR darkred RGB #990000
+.NEWCOLOR blue RGB #0000FF
+.NEWCOLOR brown RGB #9A1900
+.NEWCOLOR pink RGB #CC33CC
+.NEWCOLOR yellow RGB #FFCC00
+.NEWCOLOR cyan RGB #66FFFF
+.NEWCOLOR purple RGB #993399
+.NEWCOLOR orange RGB #FF6600
+.NEWCOLOR brightorange RGB #FF9900
+.NEWCOLOR brightgreen RGB #33FF33
+.NEWCOLOR darkgreen RGB #009900
+.NEWCOLOR black RGB #000000
+.NEWCOLOR teal RGB #008080
+.NEWCOLOR gray RGB #808080
+.NEWCOLOR darkblue RGB #000080
+.NEWCOLOR white RGB #FFFFFF
+"
+
+"
+"
+end
+
+# NOTE: There must be a Typewrite Bold style
+# I'm using the Latin Modern fonts that have this style.
+# Otherwise you could use Mom's \*[BOLDER] escape.
+
+bold "\f[TTB]$text\fP"
+italics "\f[TTI]$text\fP"
+# FIXME: no way to implement underline generically
+#underline
+
+#notfixed "\fR$text\fP"
+#fixed "\fR$text\fP"
+
+color "\*[$style]$text\*[black]"
+
+colormap
+"green" "green"
+"red" "red"
+"darkred" "darkred"
+"blue" "blue"
+"brown" "brown"
+"pink" "pink"
+"yellow" "yellow"
+"cyan" "cyan"
+"purple" "purple"
+"orange" "orange"
+"brightorange" "brightorange"
+"brightgreen" "brightgreen"
+"darkgreen" "darkgreen"
+"black" "black"
+"teal" "teal"
+"gray" "gray"
+"darkblue" "darkblue"
+"white" "white"
+default "black"
+end
+
+# lines may be empty, so begin them with the non-spacing \&
+# also, this allows "." and "'" at the beginning of the line
+lineprefix "\&"
+
+translations
+"\\" "\\e"
+#"\t" " "
+end
diff --git a/highlight.sno b/highlight.sno
new file mode 100755
index 0000000..ce43a1b
--- /dev/null
+++ b/highlight.sno
@@ -0,0 +1,28 @@
+#!/usr/local/bin/snobol4 -b
+
+ &anchor = 1
+* flush OUTPUT immediately
+ output(.output, 6, "W")
+
+loop line = input :f(end)
+ line ".HIGHLIGHT " rem . language :s(pipe)
+ output = line :(loop)
+
+* empty line to fixup line numbering
+pipe output = "."
+* FIXME: hack to strip the last byte (linefeed) since source-highlight
+* is picky about newlines at the end of the file (will print another lineprefix)
+ output(.pipe, 100,,
++ "| head -c -1 | source-highlight --outlang-def groff.outlang -s " language)
+
+pipe_l line = input
+ line ".HIGHLIGHT" :s(close)
+ pipe = line :(pipe_l)
+
+close endfile(100)
+* empty line to fixup line numbering
+* FIXME: the linefeed is necessary because source-highlight did not terminate
+* the last line
+ output = char(10) "." :(loop)
+
+end
diff --git a/plantuml.cfg b/plantuml.cfg
new file mode 100644
index 0000000..9be6fcf
--- /dev/null
+++ b/plantuml.cfg
@@ -0,0 +1,10 @@
+' CMU Serif is a TrueType Computer Modern Unicode font
+' This results in additional fonts in the final PDF, but is much easier
+' than trying to use the same fonts Groff uses
+skinparam defaultFontName CMU Serif
+skinparam defaultFontSize 9
+
+skinparam monochrome true
+
+skinparam shadowing false
+
diff --git a/sql.lang b/sql.lang
new file mode 100644
index 0000000..53195f8
--- /dev/null
+++ b/sql.lang
@@ -0,0 +1,46 @@
+type = "VARCHAR|TINYINT|TEXT|DATE|SMALLINT|MEDIUMINT|INT|INTEGER|BIGINT",
+"FLOAT|DOUBLE|DECIMAL|DATETIME|TIMESTAMP|TIME|YEAR|UNSIGNED",
+"CHAR|CHARACTER|VARYING|TINYBLOB|TINYTEXT|BLOB|MEDIUMBLOB|MEDIUMTEXT",
+"LONGBLOB|LONGTEXT|ENUM|BOOL|BINARY|VARBINARY" nonsensitive
+
+keyword = "ALL|ASC|AS|ALTER|AND|ADD|AUTO_INCREMENT",
+ "BETWEEN|BINARY|BOTH|BY|BOOLEAN",
+ "CHANGE|CHECK|COLUMNS|COLUMN|CROSS|CREATE",
+ "DATABASES|DATABASE|DATA|DELAYED|DESCRIBE|DESC|DISTINCT|DELETE|DROP|DEFAULT",
+ "ENCLOSED|ESCAPED|EXISTS|EXPLAIN",
+ "FIELDS|FIELD|FLUSH|FOR|FOREIGN|FUNCTION|FROM",
+ "GROUP|GRANT",
+ "HAVING",
+ "IGNORE|INDEX|INFILE|INSERT|INNER|INTO|IDENTIFIED|IN|IS|IF",
+ "JOIN",
+ "KEYS|KILL|KEY",
+ "LEADING|LIKE|LIMIT|LINES|LOAD|LOCAL|LOCK|LOW_PRIORITY|LEFT|LANGUAGE",
+ "MODIFY",
+ "NATURAL|NOT|NULL|NEXTVAL",
+ "OPTIMIZE|OPTION|OPTIONALLY|ORDER|OUTFILE|OR|OUTER|ON",
+ "PROCEDURE","PROCEDURAL|PRIMARY",
+ "READ|REFERENCES|REGEXP|RENAME|REPLACE|RETURN|REVOKE|RLIKE|RIGHT",
+ "SHOW|SONAME|STATUS|STRAIGHT_JOIN|SELECT|SETVAL|SET",
+ "TABLES|TERMINATED|TO|TRAILING","TRUNCATE|TABLE|TEMPORARY|TRIGGER|TRUSTED",
+ "UNION|UNIQUE|UNLOCK|USE|USING|UPDATE",
+ "VALUES|VARIABLES|VIEW",
+ "WITH|WRITE|WHERE",
+ "ZEROFILL|TYPE",
+ "XOR"
+ nonsensitive
+
+include "c_string.lang"
+
+environment string delim "`" "`" begin
+ specialchar = $SPECIALCHAR
+end
+
+include "script_comment.lang"
+
+include "c_comment.lang"
+
+comment start "--"
+
+include "symbols.lang"
+
+include "number.lang" \ No newline at end of file
diff --git a/syntax.pic b/syntax.pic
new file mode 100644
index 0000000..6c2a6f3
--- /dev/null
+++ b/syntax.pic
@@ -0,0 +1,110 @@
+linewid = linewid/2;
+
+define begin_group {
+ [Start: Here;
+}
+define end_group {
+ End: Here;] with .Start at Here;
+ move to last [].End;
+}
+
+define terminal {
+ begin_group();
+ arrow;
+ command ".FT TT";
+ command ".DOC_PT_SIZE +4";
+ circle $1;
+ command ".DOC_PT_SIZE -4";
+ command ".FT R";
+ line;
+ end_group();
+}
+
+define nonterminal {
+ begin_group();
+ arrow;
+ command ".FT I";
+ box $1;
+ command ".FT R";
+ line;
+ end_group();
+}
+
+define keyword {
+ begin_group();
+ arrow;
+ command ".FT TT";
+ box $1;
+ command ".FT R";
+ line;
+ end_group();
+}
+
+define empty {
+ begin_group();
+ # straight line, as long as a box would be
+ line linewid+boxwid+linewid;
+ end_group();
+}
+
+###
+### repeat(block);
+### Block must have labels `Start` and `End`.
+###
+define repeat {
+ line from $1.End to $1.se - (0,circlerad*2);
+ arrow to ($1.Start,Here) then to $1.Start;
+ line from $1.End right linewid/2;
+}
+
+###
+### repeat(block,terminal_symbol);
+###
+define repeat_with {
+ command ".FT TT";
+ command ".DOC_PT_SIZE +4";
+ circle at bottom of $1 - (0,circlerad*2) $2;
+ command ".DOC_PT_SIZE -4";
+ command ".FT R";
+
+ arrow from $1.End to ($1.End,last circle) then \
+ to east of last circle;
+ arrow from west of last circle to ($1.Start,last circle .e) then \
+ to $1.Start;
+ line from $1.End right linewid/2;
+}
+
+### begin_alt(base);
+define begin_alt {
+ line from $1.Start to $1.sw - (0,boxht);
+ begin_group();
+}
+
+### end_alt(base);
+define end_alt {
+ end_group();
+ # base might be smaller than alternative
+ if (Here.x < $1.End.x) then {
+ line to ($1.End,Here);
+ }
+ arrow to (Here,$1.End);
+ # Extend end of base if it is smaller than alternative
+ # and leave linewid/2 space for next alternative
+ line from $1.End to Here + (linewid/2,0);
+}
+
+define begin_rule {
+ move down;
+ right;
+ command ".FT I";
+ {$1 at Here - (boxwid/2,0)}
+ command ".FT R";
+ begin_group();
+ line;
+}
+
+define end_rule {
+ arrow;
+ end_group();
+ move to last [].sw;
+}
diff --git a/uml-graph/sequence.pic b/uml-graph/sequence.pic
new file mode 100644
index 0000000..6aaa6a2
--- /dev/null
+++ b/uml-graph/sequence.pic
@@ -0,0 +1,407 @@
+#/usr/bin/pic2plot -Tps
+#
+# Pic macros for drawing UML sequence diagrams
+#
+# (C) Copyright 2004-2005 Diomidis Spinellis.
+#
+# Permission to use, copy, and distribute this software and its
+# documentation for any purpose and without fee is hereby granted,
+# provided that the above copyright notice appear in all copies and that
+# both that copyright notice and this permission notice appear in
+# supporting documentation.
+#
+# THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
+# WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+# MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+#
+# $Id$
+#
+
+
+# Default parameters (can be redefined)
+
+# Spacing between messages
+spacing = 0.25;
+# Active box width
+awid = .1;
+# Box height
+boxht = 0.3;
+# Commend folding
+corner_fold=awid
+# Comment distance
+define comment_default_move {up 0.25 right 0.25};
+# Comment height
+comment_default_ht=0.5;
+# Comment width
+comment_default_wid=1;
+# Underline object name
+underline=1;
+
+# Create a new object(name,label)
+define object {
+ $1: box $2; move;
+ # Could also underline text with \mk\ul\ul\ul...\rt
+ if (underline) then {
+ line from $1.w + (.1, -.07) to $1.e + (-.1, -.07);
+ }
+ move to $1.e;
+ move right;
+ # Active is the level of activations of the object
+ # 0 : inactive : draw thin line swimlane
+ # 1 : active : draw thick swimlane
+ # > 1: nested : draw nested swimlane
+ active_$1 = 0;
+ lifestart_$1 = $1.s.y;
+}
+
+# Create a new external actor(name,label)
+define actor {
+ $1: [
+ XSEQC: circle rad 0.06;
+ XSEQL: line from XSEQC.s down .12;
+ line from XSEQL.start - (.15,.02) to XSEQL.start + (.15,-.02);
+ XSEQL1: line from XSEQL.end left .08 down .15;
+ XSEQL2: line from XSEQL.end right .08 down .15;
+ line at XSEQC.n invis "" "" "" $2;
+ ]
+ move to $1.e;
+ move right;
+ active_$1 = 0;
+ lifestart_$1 = $1.s.y - .05;
+}
+
+# Create a new placeholder object(name)
+define placeholder_object {
+ $1: box invisible;
+ move;
+ move to $1.e;
+ move right;
+ active_$1 = 0;
+ lifestart_$1 = $1.s.y;
+}
+
+define pobject {
+ placeholder_object($1);
+}
+
+define extend_lifeline {
+ if (active_$1 > 0) then {
+ # draw the left edges of the boxes
+ move to ($1.x - awid/2, Here.y);
+ for level = 1 to active_$1 do {
+ line from (Here.x, lifestart_$1) to Here;
+ move right awid/2
+ }
+
+ # draw the right edge of the innermost box
+ move right awid/2;
+ line from (Here.x, lifestart_$1) to Here;
+ } else {
+ line from ($1.x, lifestart_$1) to ($1.x, Here.y) dashed;
+ }
+ lifestart_$1 = Here.y;
+}
+
+# complete(name)
+# Complete the lifeline of the object with the given name
+define complete {
+ extend_lifeline($1)
+ if (active_$1) then {
+ # draw bottom of all active boxes
+ line right ((active_$1 + 1) * awid/2) from ($1.x - awid/2, Here.y);
+ }
+}
+
+# Draw a message(from_object,to_object,label)
+define message {
+ down;
+ move spacing;
+ # Adjust so that lines and arrows do not fall into the
+ # active box. Should be .5, but the arrow heads tend to
+ # overshoot.
+ if ($1.x <= $2.x) then {
+ off_from = awid * .6;
+ off_to = -awid * .6;
+ } else {
+ off_from = -awid * .6;
+ off_to = awid * .6;
+ }
+
+ # add half a box width for each level of nesting
+ if (active_$1 > 1) then {
+ off_from = off_from + (active_$1 - 1) * awid/2;
+ }
+
+ # add half a box width for each level of nesting
+ if (active_$2 > 1) then {
+ off_to = off_to + (active_$2 - 1) * awid/2;
+ }
+
+ if ($1.x == $2.x) then {
+ arrow from ($1.x + off_from, Here.y) right then down .25 then left $3 ljust " " " ";
+ } else {
+ arrow from ($1.x + off_from, Here.y) to ($2.x + off_to, Here.y) $3 " ";
+ }
+}
+
+# Display a lifeline constraint(object,label)
+define lifeline_constraint {
+ off_from = awid;
+ # add half a box width for each level of nesting
+ if (active_$1 > 1) then {
+ off_from = off_from + (active_$1 - 1) * awid/2;
+ }
+
+ box at ($1.x + off_from, Here.y) invis $2 ljust " " ;
+}
+
+define lconstraint {
+ lifeline_constraint($1,$2);
+}
+
+# Display an object constraint(label)
+# for the last object drawn
+define object_constraint {
+ { box invis with .s at last box .nw $1 ljust; }
+}
+
+define oconstraint {
+ object_constraint($1);
+}
+
+# Draw a creation message(from_object,to_object,object_label)
+define create_message {
+ down;
+ move spacing;
+ if ($1.x <= $2.x) then {
+ off_from = awid * .6;
+ off_to = -boxwid * .51;
+ } else {
+ off_from = -awid * .6;
+ off_to = boxwid * .51;
+ }
+
+ # add half a box width for each level of nesting
+ if (active_$1 > 1) then {
+ off_from = off_from + (active_$1 - 1) * awid/2;
+ }
+
+ # See comment in destroy_message
+ XSEQA: arrow from ($1.x + off_from, Here.y) to ($2.x + off_to, Here.y) "«create»" " ";
+ if ($1.x <= $2.x) then {
+ { XSEQB: box $3 with .w at XSEQA.end; }
+ } else {
+ { XSEQB: box $3 with .e at XSEQA.end; }
+ }
+ {
+ line from XSEQB.w + (.1, -.07) to XSEQB.e + (-.1, -.07);
+ }
+ lifestart_$2 = XSEQB.s.y;
+ move (spacing + boxht) / 2;
+}
+
+define cmessage {
+ create_message($1,$2,$3);
+}
+
+# Draw an X for a given object
+define drawx {
+ {
+ line from($1.x - awid, lifestart_$1 - awid) to ($1.x + awid, lifestart_$1 + awid);
+ line from($1.x - awid, lifestart_$1 + awid) to ($1.x + awid, lifestart_$1 - awid);
+ }
+}
+
+# Draw a destroy message(from_object,to_object)
+define destroy_message {
+ down;
+ move spacing;
+ # The troff code is \(Fo \(Fc
+ # The groff code is also \[Fo] \[Fc]
+ # The pic2plot code is \Fo \Fc
+ # See http://www.delorie.com/gnu/docs/plotutils/plotutils_71.html
+ # To stay compatible with all we have to hardcode the characters
+ message($1,$2,"«destroy»");
+ complete($2);
+ drawx($2);
+}
+
+define dmessage {
+ destroy_message($1,$2);
+}
+
+# An object deletes itself: delete(object)
+define delete {
+ complete($1);
+ lifestart_$1 = lifestart_$1 - awid;
+ drawx($1);
+}
+
+# Draw a message return(from_object,to_object,label)
+define return_message {
+ down;
+ move spacing;
+ # See comment in message
+ if ($1.x <= $2.x) then {
+ off_from = awid * .6;
+ off_to = -awid * .6;
+ } else {
+ off_from = -awid * .6;
+ off_to = awid * .6;
+ }
+
+ # add half a box width for each level of nesting
+ if (active_$1 > 1) then {
+ off_from = off_from + (active_$1 - 1) * awid/2;
+ }
+
+ # add half a box width for each level of nesting
+ if (active_$2 > 1) then {
+ off_to = off_to + (active_$2 - 1) * awid/2;
+ }
+
+ arrow from ($1.x + off_from, Here.y) to ($2.x + off_to, Here.y) dashed $3 " ";
+}
+
+define rmessage {
+ return_message($1,$2,$3);
+}
+
+# Object becomes active
+# Can be nested to show recursion
+define active {
+ extend_lifeline($1);
+ # draw top of new active box
+ line right awid from ($1.x + (active_$1 - 1) * awid/2, Here.y);
+ active_$1 = active_$1 + 1;
+}
+
+# Object becomes inactive
+# Can be nested to show recursion
+define inactive {
+ extend_lifeline($1);
+ active_$1 = active_$1 - 1;
+ # draw bottom of innermost active box
+ line right awid from ($1.x + (active_$1 - 1) * awid/2, Here.y);
+}
+
+# Time step
+# Useful at the beginning and the end
+# to show object states
+define step {
+ down;
+ move spacing;
+}
+
+# Switch to asynchronous messages
+define async {
+ arrowhead = 0;
+ arrowwid = arrowwid * 2;
+}
+
+# Switch to synchronous messages
+define sync {
+ arrowhead = 1;
+ arrowwid = arrowwid / 2;
+}
+
+# same as lifeline_constraint, but Text and empty string are exchanged.
+define lconstraint_below{
+ off_from = awid;
+ # add half a box width for each level of nesting
+ if (active_$1 > 1) then {
+ off_from = off_from + (active_$1 - 1) * awid/2;
+ }
+
+ box at ($1.x + off_from, Here.y) invis "" $2 ljust;
+}
+
+# begin_frame(left_object,name,label_text);
+define begin_frame {
+ # The lifeline will be cut here
+ extend_lifeline($1);
+ # draw the frame-label
+ $2: box $3 invis with .n at ($1.x, Here.y);
+ d = $2.e.y - $2.se.y;
+ line from $2.ne to $2.e then down d left d then to $2.sw;
+ # continue the lifeline below the frame-label
+ move to $2.s;
+ lifestart_$1 = Here.y;
+}
+
+# end_frame(right_object,name);
+define end_frame {
+ # dummy-box for the lower right corner:
+ box invis "" with .s at ($1.x, Here.y);
+ # draw the frame
+ frame_wid = last box.se.x - $2.nw.x
+ frame_ht = - last box.se.y + $2.nw.y
+ box with .nw at $2.nw wid frame_wid ht frame_ht;
+ # restore Here.y
+ move to last box.s;
+}
+
+# comment(object,[name],[line_movement], [box_size] text);
+define comment {
+ old_y = Here.y
+ # draw the first connecting line, at which's end the box wil be positioned
+ move to ($1.x, Here.y)
+ if "$3" == "" then {
+ line comment_default_move() dashed;
+ } else {
+ line $3 dashed;
+ }
+
+ # draw the box, use comment_default_xx if no explicit
+ # size is given together with the text in parameter 4
+ old_boxht=boxht;
+ old_boxwid=boxwid;
+ boxht=comment_default_ht;
+ boxwid=comment_default_wid;
+ if "$2" == "" then {
+ box invis $4;
+ } else {
+ $2: box invis $4;
+ }
+ boxht=old_boxht;
+ boxwid=old_boxwid;
+
+ # draw the frame of the comment
+ line from last box.nw \
+ to last box.ne - (corner_fold, 0) \
+ then to last box.ne - (0, corner_fold) \
+ then to last box.se \
+ then to last box.sw \
+ then to last box.nw ;
+ line from last box.ne - (corner_fold, 0) \
+ to last box.ne - (corner_fold, corner_fold) \
+ then to last box.ne - (0, corner_fold) ;
+
+ # restore Here.y
+ move to ($1.x, old_y)
+}
+
+# connect_to_comment(object,name);
+define connect_to_comment {
+ old_y = Here.y
+ # start at the object
+ move to ($1.x, Here.y)
+ # find the best connection-point of the comment to use as line-end
+ if $1.x < $2.w.x then {
+ line to $2.w dashed;
+ } else {
+ if $1.x > $2.e.x then {
+ line to $2.e dashed;
+ } else {
+ if Here.y < $2.s.y then {
+ line to $2.s dashed;
+ } else {
+ if Here.y > $2.n.y then {
+ line to $2.n dashed;
+ }
+ }
+ }
+ }
+ # restore Here.y
+ move to ($1.x, old_y)
+}
diff --git a/uml.sno b/uml.sno
new file mode 100755
index 0000000..9461884
--- /dev/null
+++ b/uml.sno
@@ -0,0 +1,40 @@
+#!/usr/local/bin/snobol4 -b
+
+ &anchor = 1
+
+ float = span("0123456789.")
+
+ count = 0
+ lineno = 1
+
+loop line = input :f(end)
+ line ".lf " float . lineno :s(next)
+ line ".UML" rem . options :s(pipe)
+ lineno = lineno + 1
+next output = line :(loop)
+
+pipe filename = "uml_temp" (count = count + 1) ".pdf"
+ output(.pipe, 100,,
++ "| plantuml -pipe -tsvg -failonerror -config plantuml.cfg | "
++ "rsvg-convert -f pdf -o " filename)
+ pipe = "@startuml"
+
+pipe_l line = input
+ lineno = lineno + 1
+ line ".UML" :s(close)
+ pipe = line :(pipe_l)
+
+close pipe = "@enduml"
+ endfile(100)
+
+* get PDF dimensions
+ input(.pipe, 100,, "|pdfinfo " filename)
+get_info
+ pipe "Page size:" span(" ") float . width " x " float . height :f(get_info)
+ endfile(100)
+
+ output = ".PDF_IMAGE " filename " " width "p " height "p" options
+ output = ".lf " (lineno + 1) :(loop)
+
+end
+