-- Author: John Maxwell
-- Last Edited by: Maxwell, November 22, 1983 10:23 am

DIRECTORY
	-- BcplFontFileDefs USING [OpenSDFontFile, CloseSDFontFile, 
	-- SplineCommandPtr, SplineDataPtr, GetSplineCommands], 
	-- Cubic USING [Bezier, BezierToCoeffs, Coeffs], 
	-- Device USING [Free, Handle, Object], 
	Beam USING [Free],
	Chord USING [Free],
	Event USING [GetScoreIndex, GetOctava, Staves, Sync], 
	Graphics USING [
	  Context, CurveTo, DrawChar, DrawArea, 
	  DrawTo, FontRef, LineTo, MakeFont, MoveTo, NewPath, Path, SetCP, SetDefaultFont], 
	Heap USING [Create], 
	Mopcodes USING [zMISC, zPOP], 
	MusicDefs, 
	-- OpaqueDevice, 
	-- PressDefs USING [PressFileDescriptor, PutText, SetFont], 
	-- PressDevice USING [NewPressDevice], 
	-- PressDeviceImpl USING [DataRef], 
	Score USING [], 
	Space USING [
	  Create, Delete, GetHandle, Handle, LongPointer, 
	  Map, PageFromLongPointer, Unmap, virtualMemory], 
	TTY USING [Create, Handle, NumberFormat, PutLine, PutLongNumber, PutString],
	Utility USING [];

UtilityImpl: PROGRAM
  IMPORTS Beam, Chord, Graphics, Heap, Event, TTY
  EXPORTS MusicDefs, Score, -- OpaqueDevice-- Utility  = 
  -- SHARES PressDeviceImpl = 

BEGIN
OPEN Graphics, MusicDefs;

-- ****************************************************************************
-- graphics procedures
-- ****************************************************************************

-- context: PUBLIC Context;
text, music: PUBLIC FontRef;
music8: FontRef ← Graphics.MakeFont["music8"];
text12: FontRef ← Graphics.MakeFont["timesroman12"];
light: PUBLIC CARDINAL ← 102041B;

DrawLine: PUBLIC PROCEDURE[dc: Graphics.Context, x1, y1, x2, y2: INTEGER] = 
BEGIN
SetCP[dc, x1, y1];
DrawTo[dc, x2, y2];
END;

DrawCubic: PUBLIC PROC[dc: Graphics.Context, x1, y1, x2, y2, height: INTEGER] = 
BEGIN
path: Path ← NewPath[10];
MoveTo[path, x1, y1];
CurveTo[path, (4*x1+x2)/5, height+y1, (x1+4*x2)/5, height+y1, x2, y2];
LineTo[path, x2, y2-width1];
CurveTo[path, (x1+4*x2)/5, height-width2+y1, 
   (4*x1+x2)/5, height-width2+y1, x1, y1-width1];
DrawArea[dc, path]; 
END;

width1: INTEGER ← 0;
width2: INTEGER ← 3;

SetFont: PUBLIC PROCEDURE[dc: Context, font: FontRef, size: INTEGER] = 
BEGIN -- a hack
IF font = text AND size  = 12 THEN {Graphics.SetDefaultFont[dc, text12]; RETURN};
IF font = music AND size = 8 THEN {Graphics.SetDefaultFont[dc, music8]; RETURN};
Graphics.SetDefaultFont[dc, font];
END;

DrawString: PUBLIC PROCEDURE[dc: Context, s: STRING] = 
BEGIN
FOR i: CARDINAL IN [0..s.length) DO DrawChar[dc, s[i]]; ENDLOOP;
END;

DrawChar: PUBLIC PROCEDURE[dc: Graphics.Context, c: CHARACTER] = 
BEGIN
Graphics.DrawChar[dc, c]; 
END;

PointSize: PROCEDURE[sheet: SheetPTR, n: INTEGER] RETURNS[INTEGER] = 
INLINE BEGIN
SELECT sheet.scale FROM
	1 => RETURN[n];
	2 => RETURN[2*n/3];
	4 => RETURN[n/4];
	ENDCASE;
RETURN[n];
END;

-- **********************************************************
-- printing the score 
-- **********************************************************

print: PUBLIC BOOLEAN ← FALSE;
printChar: BOOLEAN ← FALSE;

-- ****************************************************************************
-- Basic Allocation Procedures
-- ****************************************************************************

zone: PUBLIC UNCOUNTED ZONE ← Heap.Create[32];
endOfBeam: PUBLIC VariousPTR ← [note[NIL]];

NewSegment: PUBLIC PROCEDURE[size, max, maxOffset: CARDINAL] 
	RETURNS[p: LONG POINTER] = 
BEGIN
space: Space.Handle ← Space.Create[(size + 255)/256, Space.virtualMemory];
Space.Map[space];
p ← Space.LongPointer[space];
LongZero[p, size];
LOOPHOLE[p+maxOffset, LONG POINTER TO CARDINAL] ↑ ← max;
END;

FreeSegment: PUBLIC PROCEDURE[segment: LONG POINTER] = 
BEGIN
space: Space.Handle ← Space.GetHandle[Space.PageFromLongPointer[segment]];
Space.Unmap[space];
Space.Delete[space];
END;

LongZero: PROC [where: LONG POINTER, nwords: CARDINAL] = 
	MACHINE CODE {Mopcodes.zMISC, 102B -- aZERO --; Mopcodes.zPOP; Mopcodes.zPOP}; 

-- ****************************************************************************
-- filestats, consistency checking
-- ****************************************************************************

FileStats: PUBLIC PROCEDURE[score: ScorePTR] = 
BEGIN
OPEN Utility;
memory: LONG INTEGER;
highWater, notes: INTEGER ← 0;
WriteLine[""];
memory ← SIZE[ScoreRec[score.length]];
memory ← memory+ LONG[score.length]*SIZE[EventRec];
WriteString["scoreLength = "];
WriteNumber[score.length, [10, FALSE, TRUE, 4]];
FOR i: CARDINAL IN [0..score.length) DO
	sync: SyncPTR;
	IF score.event[i].type # sync THEN LOOP;
	sync ← Event.Sync[score.event[i]];
	notes ← notes + sync.length;
	highWater ← MAX[highWater, sync.length];
	ENDLOOP;
memory ← memory + LONG[notes]*SIZE[NoteRec];
WriteString["; # notes = "];
WriteNumber[notes, [10, FALSE, TRUE, 5]];
WriteString["; maxUsageOfSyncs = "];
WriteNumber[highWater, [10, FALSE, TRUE, 2]];
WriteLine[""];
highWater ← 0;
WriteString["                   # chords = "];
WriteNumber[score.chordHeap.length, [10, FALSE, TRUE, 3]];
FOR i: CARDINAL IN [0..score.chordHeap.length) DO
	highWater ← MAX[highWater, score.chordHeap.chord[i].length];
	ENDLOOP;
WriteString["; maxUsageOfChords = "];
WriteNumber[highWater, [10, FALSE, TRUE, 2]];
memory ← memory + SIZE[ChordRec[score.chordHeap.length]];
memory ← memory + SIZE[BeamRec[score.beamHeap.length]];
WriteLine[""];
highWater ← 0;
WriteString["                   # beams = "];
WriteNumber[score.beamHeap.length, [10, FALSE, TRUE, 3]];
FOR i: CARDINAL IN [0..score.beamHeap.length) DO
    highWater ← MAX[highWater, score.beamHeap.beam[i].length];
	ENDLOOP;
WriteString["; maxUsageOfBeams = "];
WriteNumber[highWater, [10, FALSE, TRUE, 2]];
WriteLine[""];
WriteString["event = "];
WriteNumber[SIZE[EventRec], [10, FALSE, TRUE, 2]];
WriteString["; note = "];
WriteNumber[SIZE[NoteRec], [10, FALSE, TRUE, 2]];
WriteString["; chord = "];
WriteNumber[SIZE[ChordRec], [10, FALSE, TRUE, 2]];
WriteString["; beam = "];
WriteNumber[SIZE[BeamRec], [10, FALSE, TRUE, 2]];
WriteString["; memory = "];
WriteNumber[memory, [10, FALSE, TRUE, 8]];
WriteLine[""];
END;

Test: PUBLIC PROCEDURE[score: ScorePTR] RETURNS[BOOLEAN] = 
BEGIN
n: NotePTR;
s: EventPTR;
lastBeam: BeamPTR ← NIL;
beamFound: BOOLEAN;
sync1, sync2: BOOLEAN;
dataError ← FALSE;
FOR i: CARDINAL IN [0..score.max) DO
    IF score.event[i] # NIL AND i >= score.length THEN 
       WriteError[sync, i, -1, "beyond scoreLength"];
    IF score.event[i] = NIL AND i < score.length THEN WriteError[sync, i, -1, " = NIL"];
    IF score.event[i] = NIL THEN LOOP;
--    IF i # 0 AND score[i-1].time > score[i].time THEN
--       WriteError[sync, i, -1, "out of place"];
	WITH ev: score.event[i] SELECT FROM
		staves => IF ev.staves IN [octava1..octava2] AND 
			Event.GetOctava[score, Event.GetOctava[score, Event.Staves[score.event[i]]]] # score.event[i] 
				THEN WriteError[sync, i, -1, " = octava with no matching end."];
		sync => {
			IF ev.length = 0 THEN WriteError[sync, i, -1, "empty"];
			FOR j: CARDINAL IN [0..ev.max) DO
    			n ← ev.note[j];
				IF n # NIL AND j >= ev.length THEN WriteError[sync, i, j, "beyond syncLength"];
				IF n = NIL AND j < ev.length THEN WriteError[sync, i, j, " = NIL"];
				IF n = NIL THEN LOOP;
				IF n.sync # score.event[i] THEN WriteError[sync, i, j, "n.sync # score.event[i]"];
				IF n.beam = NIL THEN LOOP;
				IF n.beam = lastBeam THEN LOOP;
				beamFound ← FALSE;
				FOR k: CARDINAL IN [0..score.beamHeap.length) DO
	    			IF score.beamHeap.beam[k] # n.beam THEN LOOP;
	    			beamFound ← TRUE;
	    			lastBeam ← n.beam;
	    			EXIT; ENDLOOP;
				IF NOT beamFound THEN WriteError[sync, i, j, "non-existant beam"];
				ENDLOOP};
		ENDCASE;
	ENDLOOP;
FOR i: CARDINAL IN [0..score.chordHeap.max) DO
    IF score.chordHeap.chord[i] # NIL AND i >= score.chordHeap.length THEN
       WriteError[chord, i, -1, "beyond score.chordHeapLength"];
    IF score.chordHeap.chord[i] = NIL AND i < score.chordHeap.length THEN 
       WriteError[chord, i, -1, " = NIL"];
    IF score.chordHeap.chord[i] = NIL THEN LOOP;
    IF score.chordHeap.chord[i].length = 0 THEN WriteError[chord, i, -1, "empty"];
    s ← NIL;
    FOR j: CARDINAL IN [0..score.chordHeap.length) DO
    	chord: ChordPTR = score.chordHeap.chord[i];
		n ← chord.note[j];
		IF n # NIL AND j >= chord.length THEN WriteError[chord, i, j, "beyond chordLength"];
		IF n = NIL AND j < chord.length THEN WriteError[chord, i, j, " = NIL"];
		IF n = NIL THEN LOOP;
		IF s = NIL THEN s ← n.sync;
		IF s # n.sync THEN WriteError[chord, i, j, "wrong sync"];
		ENDLOOP;
	ENDLOOP;
FOR i: CARDINAL IN [0..score.beamHeap.length) DO
    IF score.beamHeap.beam[i] # NIL AND i >= score.beamHeap.length THEN
       WriteError[beam, i, -1, "beyond score.beamHeapLength"];
    IF score.beamHeap.beam[i] = NIL AND i < score.beamHeap.length THEN 
       WriteError[beam, i, -1, " = NIL"];
    IF score.beamHeap.beam[i] = NIL THEN LOOP;
    IF score.beamHeap.beam[i].length = 0 THEN WriteError[beam, i, -1, "empty"];
    TestBeam[score, i];
    sync1 ← Event.GetScoreIndex[score, score.beamHeap.beam[i].sync1] # score.length;
    sync2 ← Event.GetScoreIndex[score, score.beamHeap.beam[i].sync2] # score.length;
    IF NOT sync1 THEN WriteError[beam, i, -1, "sync1 not in score"];
    IF NOT sync2 THEN WriteError[beam, i, -1, "sync2 not in score"];
    ENDLOOP;
RETURN[dataError];
END;

TestBeam: PROCEDURE[score: ScorePTR, i: CARDINAL] = 
BEGIN
beam: BeamPTR ← score.beamHeap.beam[i];
sync1, sync2: BOOLEAN ← FALSE;
FOR j: CARDINAL IN [0..beam.max) DO
    IF beam.chord[j] # endOfBeam AND j >= beam.length THEN
       WriteError[beam, i, j, "beyond beamLength"];
    IF beam.chord[j] = endOfBeam AND j < beam.length THEN
    	WriteError[beam, i, j, " = NIL"];
    IF beam.chord[j] = endOfBeam THEN LOOP;
    WITH ev: beam.chord[j] SELECT FROM
		note => 	BEGIN
			IF ev.n.beam # beam THEN WriteError[beam, i, j, "wrong beam"];
			IF ev.n.sync = beam.sync1 THEN sync1 ← TRUE;
			IF ev.n.sync = beam.sync2 THEN sync2 ← TRUE;
			END;
		chord =>  BEGIN
			IF ev.c.note[0].sync = beam.sync1 THEN sync1 ← TRUE;
			IF ev.c.note[0].sync = beam.sync2 THEN sync2 ← TRUE;
			FOR k: CARDINAL IN [0..ev.c.length) DO
		    	IF ev.c.note[k] = NIL THEN LOOP;
		    	IF ev.c.note[k].beam # beam THEN WriteError[beam, i, j, "wrong beam"];
		    	ENDLOOP;
			END;
		beam => 	BEGIN
			IF ev.b.beam # beam THEN WriteError[beam, i, j, "wrong beam"];
			IF ev.b.sync1 = beam.sync1 THEN sync1 ← TRUE;
			IF ev.b.sync2 = beam.sync2 THEN sync2 ← TRUE;
			END;
		ENDCASE;
    ENDLOOP;
IF NOT sync1 THEN WriteError[beam, i, -1, "bad sync1"];
IF NOT sync2 THEN WriteError[beam, i, -1, "bad sync2"];
END;

WriteError: PROCEDURE[t: Type, i, j: INTEGER, s: STRING] = 
BEGIN
SELECT t FROM
	sync => BEGIN
		WriteString["sync"];
		WriteNumber[i, [10, FALSE, TRUE, 4]];
		IF j > -1 THEN
		   BEGIN
		   WriteString[", event"];
		   WriteNumber[j, [10, FALSE, TRUE, 2]];
		   END;
		END;
	chord => BEGIN
		WriteString["chord"];
		WriteNumber[i, [10, FALSE, TRUE, 4]];
		IF j > -1 THEN
		   BEGIN
		   WriteString[", note"];
		   WriteNumber[j, [10, FALSE, TRUE, 2]];
		   END;
		END;
	beam => BEGIN
		WriteString["beam"];
		WriteNumber[i, [10, FALSE, TRUE, 4]];
		IF j > -1 THEN
		   BEGIN
		   WriteString[", chord"];
		   WriteNumber[j, [10, FALSE, TRUE, 2]];
		   END;
		END;
       ENDCASE;
dataError ← TRUE;
WriteString[": "];
WriteLine[s];
END;

Type: TYPE = {sync, chord, beam, none};
dataError: BOOLEAN ← FALSE;

CleanUp: PROCEDURE[score: ScorePTR] = 
BEGIN
FOR i: CARDINAL IN [0..score.length) DO
	IF score.event[i].type # sync THEN LOOP;
	IF Event.Sync[score.event[i]].length = 0 THEN zone.FREE[@score.event[i]];
	ENDLOOP;
FOR i: CARDINAL IN [0..score.chordHeap.length) DO
	 IF score.chordHeap.chord[i].length = 0 
	 	THEN Chord.Free[score, score.chordHeap.chord[i]];
    ENDLOOP;
FOR i: CARDINAL IN [0..score.beamHeap.length) DO
    IF score.beamHeap.beam[i].length = 0 
    	THEN Beam.Free[score, score.beamHeap.beam[i]];
    ENDLOOP;
END;

-- **********************************************************
-- interface to Cedar for output 
-- **********************************************************

log: TTY.Handle ← TTY.Create[NIL];
Dummy: TYPE = RECORD[base: INTEGER, x, y: BOOLEAN, width: INTEGER];

WriteLine: PROCEDURE[s: STRING] = INLINE {TTY.PutLine[log, s]};
WriteString: PROCEDURE[s: STRING] = INLINE {TTY.PutString[log, s]};
WriteNumber: PROCEDURE[n: LONG UNSPECIFIED, format: TTY.NumberFormat] = 
   INLINE {TTY.PutLongNumber[log, n, format]};

END..


CleanUpSheets: PROCEDURE = 
BEGIN
sheet: Staves;
FOR i: CARDINAL DECREASING IN [0..scoreLength) DO
      IF score[i].type NOT IN SheetSwitch THEN LOOP;
      sheet ← LOOPHOLE[score[i].event];
      FOR j: CARDINAL IN [0..sheet.sl] DO
	IF sheet.staff[j].pitch # 0 THEN LOOP;
	Utility.FreeEvent[@score[i]]; EXIT;
	ENDLOOP;
     ENDLOOP;
END;




-- **********************************************************
-- printing the score 
-- **********************************************************

print: PUBLIC BOOLEAN ← FALSE;
olddc: Graphics.Context;
printChar: BOOLEAN ← FALSE;
DeviceObject: PUBLIC TYPE = Device.Object; -- exported to OpaqueDevice
device: Device.Handle;

OpenPressDevice: PUBLIC PROCEDURE[splines: BOOLEAN] RETURNS[Device.Handle] = 
BEGIN
l: REAL ← 1;
pos: Graphics.Vec;
printChar ← ~splines AND scale = 2;
print ← TRUE;
olddc ← context;
device ← PressDevice.NewPressDevice["music.press"];
context ← Graphics.NewContext[device];
pos ← Graphics.Map[olddc, context, [0, 0]];
pos.x ← pos.x+8;
pos.y ← pos.y+28;
Graphics.Translate[context, pos];
Graphics.SetLineWidth[context, 1];
IF scale = 2 THEN Scale[context, [(2*l)/3, (2*l)/3]] ELSE Scale[context, [l/scale, l/scale]];
SetColor[context, [0, 0, 0]];
IF NOT printChar THEN {
	Graphics.Scale[context, [12, 12]]; -- to offset bug in something
	BcplFontFileDefs.OpenSDFontFile["music8.sd"];
	[ndp, ncp] ← BcplFontFileDefs.GetSplineCommands[0154C, SystemDefs.AllocateHeapNode]};
Utility.SetFont[context, music, 8];
RETURN[device];
END;

ClosePressDevice: PUBLIC PROCEDURE[device: POINTER TO Device.Handle] = 
BEGIN
tncp: BcplFontFileDefs.SplineCommandPtr;
Graphics.FreeContext[@context];
Device.Free[device];
IF NOT printChar THEN BEGIN
	BcplFontFileDefs.CloseSDFontFile[];
	UNTIL ncp = NIL DO
		tncp ← ncp.next;
		SystemDefs.FreeHeapNode[ncp];
		ncp ← tncp;
		ENDLOOP;
	END;
context ← olddc;
print ← FALSE;
END;

-- ****************************************************************************
-- graphics procedures
-- ****************************************************************************

SetFont: PUBLIC PROCEDURE[dc: Context, font: FontRef, size: INTEGER] = 
BEGIN
OPEN PressDeviceImpl;
l: REAL ← 1;
Graphics.SetFont[dc, font, size];
IF NOT print THEN {Graphics.SetFont[dc, font, size]; RETURN};
IF printChar THEN BEGIN
	fontname: STRING ← [16];
	ph: POINTER TO PressDefs.PressFileDescriptor;
	ptsize, face, rotation: CARDINAL ← 0;
	ph ← LOOPHOLE[device.data, PressDeviceImpl.DataRef].pressHandle;
	IF font = music THEN fontname ← "MOCKINGBIRD" ELSE fontname ← "TIMESROMAN";
	ptsize ← IF font = music THEN LOOPHOLE[-24] ELSE 8;
	face ← 0; -- PressDefs.EncodeFace['n, 'n, 'n]
	PressDefs.SetFont[ph, fontname, ptsize, face, rotation];
	END 
   ELSE BEGIN
	BcplFontFileDefs.CloseSDFontFile[];
	IF font = music THEN BcplFontFileDefs.OpenSDFontFile["music8.sd"]
	      ELSE BcplFontFileDefs.OpenSDFontFile["timesroman.sd"];
	IF font = text THEN Scale[context, [12, 12]] ELSE Scale[context, [l/12, l/12]];
	END;
END;

DrawCubic: PUBLIC PROC[x1, y1, x2, y2, height: INTEGER] = 
BEGIN
b1, b2: Cubic.Bezier;
c1, c2: Cubic.Coeffs;
StartAreaPath[context]; 
EnterPoint[context, [x1, y1]];
b1 ← [[x1, y1], [(4*x1+x2)/5, height+y1], [(x1+4*x2)/5, height+y1], [x2, y2]];
c ← Cubic.BezierToCoeffs[b1];
EnterCubic[context, @c];
EnterPoint[context, [x2, y2-width1]];
b2 ← [[x2, y2-width1], [(x1+4*x2)/5, height-width2+y1], 
      	[(4*x1+x2)/5, height-width2+y1], [x1, y1-width1]];
c ← Cubic.BezierToCoeffs[b2];
EnterCubic[context, @c];
DrawArea[context]; 
END;

DrawString: PUBLIC PROCEDURE[dc: Context, s: STRING] = 
BEGIN
screen, pos: Vec ← GetPosition[context];
IF NOT print THEN {DisplayString[dc, s]; RETURN};
IF printChar THEN {
	ph: POINTER TO PressDefs.PressFileDescriptor;
	ph ← LOOPHOLE[device.data, PressDeviceImpl.DataRef].pressHandle;
	screen ← UserToScreen[context, pos];
	IF screen.x < 0 OR screen.y < 0 THEN RETURN;
	PressDefs.PutText[ph, s, Real.FixC[screen.x], Real.FixC[screen.y]]}
   ELSE {
	FOR i: CARDINAL IN [0..s.length) DO 
		DrawChar[dc, s[i]];
		RelMoveTo[dc, [5, 0]]; 
		ENDLOOP};
END;

DrawChar: PUBLIC PROCEDURE[dc: Graphics.Context, c: CHARACTER] = 
BEGIN
screen, pos: Vec ← GetPosition[context];
Move: PROC[v: POINTER TO Vec] = { NewBoundary[dc]; EnterPoint[dc, v↑] };
Draw: PROC[v: POINTER TO Vec] = { EnterPoint[dc, v↑] };
ECubic: PROC[c: POINTER TO Cubic.Coeffs] = { EnterCubic[dc, c] };
IF NOT print THEN BEGIN DrawChar[dc, c]; RETURN; END;
IF printChar THEN BEGIN
	s: STRING ← [1];
	ph: POINTER TO PressDefs.PressFileDescriptor;
	ph ← LOOPHOLE[device.data, PressDeviceImpl.DataRef].pressHandle;
	s.length ← 1; s[0] ← c;
	screen ← UserToScreen[context, pos];
	IF screen.x < 0 OR screen.y < 0 THEN RETURN;
	PressDefs.PutText[ph, s, Real.FixC[screen.x], Real.FixC[screen.y]];
	END
   ELSE BEGIN
	Translate[context, pos];
	StartAreaPath[context, IF c = 'P OR c = 'X THEN FALSE ELSE TRUE];
	DoSDChar[c, Move, Draw, ECubic];
	DrawArea[context];
	Translate[context, [-pos.x, -pos.y]];
	END;
END;

ndp: BcplFontFileDefs.SplineDataPtr ← NIL;
ncp: BcplFontFileDefs.SplineCommandPtr ← NIL;

DoSDChar: PROCEDURE[char: CHARACTER, 
Move: PROC[v: POINTER TO Vec], 
Draw: PROC[v: POINTER TO Vec], 
DCubic: PROC[c: POINTER TO Cubic.Coeffs]] = 
BEGIN
pos: Vec ← [0, 0];
tscp, scp: BcplFontFileDefs.SplineCommandPtr;
sdp: BcplFontFileDefs.SplineDataPtr;
IF char = 154C AND ndp # NIL THEN {sdp ← ndp; scp ← ncp}
		     ELSE [sdp, scp] ← BcplFontFileDefs.GetSplineCommands[char, 		
				  SystemDefs.AllocateHeapNode];
IF char = 154C AND ndp = NIL THEN {ndp ← sdp; ncp ← scp};
tscp ← scp;
UNTIL scp = NIL DO
	WITH scp SELECT FROM
		MoveTo => {pos ← [x, y]; Move[@pos]};
		DrawTo => {pos ← [x, y]; Draw[@pos]};
		DrawCurve => {
			c: Cubic.Coeffs ← [c3: [x2, y2], c2: [x1, y1], c1: [x0, y0], c0: pos];
			DCubic[@c]; pos ← [pos.x+x0+x1+x2, pos.y+y0+y1+y2];
			};
		NewObject => NULL;
		EndDefinition => EXIT;
		ENDCASE;
	scp ← scp.next;
	ENDLOOP;
	IF char # 154C THEN UNTIL (scp ← tscp) = NIL DO
		tscp ← scp.next;
		SystemDefs.FreeHeapNode[scp];
		ENDLOOP;
END;