CFormattingImpl.mesa
Copyright Ó 1988, 1991, 1993 by Xerox Corporation. All rights reserved.
Shamelessly stolen from Tioga's TEditMesaOpsImpl
James Rauen, July 8, 1988 3:55:38 pm PDT
Last edited by: James Rauen July 10, 1988 11:11:00 am PDT
Eduardo Pelegri-Llopart January 24, 1989 1:43:45 pm PST
Foote, October 15, 1990 1:22 pm PDT
Michael Plass, July 23, 1993 10:58 am PDT
DIRECTORY
Atom USING [MakeAtomFromRefText],
Commander USING [CommandProc, Handle, Register],
CommanderOps USING [DoCommandRope, Failed, NextArgument],
ConvertUnsafe USING [ToRope],
EditSpan USING [ChangeLooks],
EditSpanSupport USING [Apply],
IO USING [GetCedarToken, GetIndex, RIS, SetIndex, STREAM, TokenKind],
NodeAddrs USING [GetTextAddr, PutTextAddr, RemTextAddr],
RefText USING [AppendChar, New, ObtainScratch, TrustTextAsRope],
Rope USING [Flatten, FromRefText, ROPE, Size, Substr],
RopeReader USING [Create, Ref],
RuntimeError USING [BoundsFault],
SymTab USING [Create, Fetch, Insert, Ref],
TEditDocument USING [Selection],
TEditInput USING [CommandProc, currentEvent, Register],
TEditInputOps USING [CallWithLocks],
TEditLocks USING [Lock, Unlock],
TEditMesaOps USING [],
TEditSelection USING [MakeSelection, pSel, SetSelLooks],
TextEdit USING [Size],
TextLooks USING [Looks, noLooks, RopeToLooks],
TextNode USING [EndPos, NodeItself, Offset, Root, Span],
TiogaAccess USING [Create, EndOf, FromFile, Get, Looks, Put, Reader, TiogaChar, WriteFile, Writer],
TiogaAccessViewers USING [FromSelection, WriteSelection],
Tioga USING [Event, Node];
CFormattingImpl: CEDAR PROGRAM
IMPORTS Atom, TiogaAccess, TiogaAccessViewers, Commander, CommanderOps, ConvertUnsafe, EditSpan, EditSpanSupport, IO, NodeAddrs, RefText, Rope, RopeReader, RuntimeError, SymTab, TEditInput, TEditInputOps, TEditLocks, TEditSelection, TextEdit, TextNode, TextLooks
= BEGIN
ROPE: TYPE = Rope.ROPE;
STREAM: TYPE = IO.STREAM;
KeywordClass: TYPE ~ {isKeyword, importantNonKeyword, maybeKeyword, noKeyword};
PreProcessorList: ARRAY [0..10) OF STRING = ["define", "elif", "endif", "error", "ifdef", "ifndef", "include", "line", "pragma", "undef"]; -- if, else are also one... Have to improve the parsing before..
StandardLibraryList: ARRAY [0..200) OF STRING = ["fopen", "freopen", "fflush", "fclose", "remove", "rename", "tmpfile", "tmpnam", "setvbuf", "setbuf", "fprintf", "printf", "sprintf", "vprintf", "vfprintf", "vsprintf", "fscanf", "scanf", "sscanf", "fgetc", "fgets", "fputc", "fputs", "getc", "getchar", "gets", "putc", "putchar", "puts", "ungetc", "fread", "fwrite", "fseek", "ftell", "rewind", "fgetpos", "fsetpos", "clearerr", "feof", "ferror", "perror", "errno", "size←t", "stderr", "stdin", "stdout", "FILE", "isalnum", "isalpha", "iscntrl", "isdigit", "isgraph", "islower", "isprint", "ispunct", "isspace", "isupper", "isxdigit", "tolower", "toupper", "strcpy", "strncpy", "strcat", "strncat", "strcmp", "strncmp", "strchr", "strrchr", "strspn", "strcspn", "strpbrk", "strstr", "strerror", "strtok", "NULL", "memcpy", "memmove", "memcmp", "memchr", "memset", "EDOM", "ERANGE", "HUGE←VAL", "sin", "cos", "tan", "asin", "acos", "atan", "atan2", "sinh", "cosh", "tanh", "exp", "log", "log10", "pow", "sqrt", "ceil", "floor", "fabs", "ldexp", "frexp", "modf", "fmod", "atof", "atoi", "atol", "strtod", "strtol", "strtoul", "rand", "srand", "calloc", "malloc", "realloc", "free", "abort", "exit", "atexit", "system", "getenv", "bsearch", "qsort", "abs", "labs", "div", "ldiv",
assert.h
"assert", "NDEBUG",
stdarg.h
"va←list", "ap", "va←start", "va𡤊rg", "va𡤎nd",
setjmp.h
"setjmp", "longjmp",
signal.h
"signal", "SIG�L", "SIGABRT", "SIGFPE", "SIGILL", "SIGINT", "SIGINT", "SIGSEGV", "SIGTERM", "SIG𡤎RR", "raise",
time
"clock←t", "time←t", "tm", "tm←sec", "tm←min", "tm←hour", "tm←mday", "tm←mon", "tm←year", "tm←wday", "tm←yday", "tm←isdst", "time", "difftime", "mktime", "asctime", "ctime", "gmtime", "localtime", "strftime",
limits
"CHAR𡤋IT", "CHAR←MAX", "CHAR←MIN", "INT←MAX", "INT←MIN", "LONG←MAX", "LONG←MIN", "SCHAR←MAX", "SCHAR←MIN", "SHRT←MAX", "SHRT←MIN", "UCHAR←MAX", "UCHAR←MIN", "UINT←MAX", "ULONG←MAX", "USHRT←MAX",
float
"FLT←RADIX", "FLT←ROUNDS", "FLT𡤍IG", "FLT𡤎PSILON", "FLT←MANT𡤍IG", "FLT←MAX", "FLT←MAX𡤎XP", "FLT←MIN", "FLT←MIN𡤎XP", "DBL𡤍IG", "DBL𡤎PSILON", "DBL←MANT𡤍IG", "DBL←MAX", "DBL←MAX𡤎XP", "DBL←MIN", "DBL←MIN𡤎XP"
];
AdditonalList: ARRAY [0..2) OF STRING = ["main", "reg"];
MaybeKeywordsList: ARRAY [0..3) OF STRING = ["asm", "fortran", "noalias"];
KeywordsList: ARRAY [0..33) OF STRING = ["auto", "break", "case", "char", "const", "continue", "default", "do", "double", "else", "enum", "extern", "float", "for", "fortran", "goto", "if", "int", "long", "register", "return", "short", "signed", "sizeof", "static", "struct", "switch", "typedef", "union", "unsigned", "void", "volatile", "while"];
keyTab: SymTab.Ref ~ SymTab.Create[];
AClass: TYPE ~ {PreProcessor, StandardLibrary, Additional, MaybeKeywords, Keywords, NotAKeyword};
ARec: TYPE ~ RECORD[class: AClass];
PreProcess: PROC ~ {
AddIt: PROC [s: STRING, key: AClass] ~ {
[] ¬ keyTab.Insert[key: ConvertUnsafe.ToRope[s], val: NEW[ARec ¬ [class: key]]]};
FOR i: CARD IN [0..LENGTH[PreProcessorList]) DO AddIt[PreProcessorList[i], PreProcessor] ENDLOOP;
FOR i: CARD IN [0..LENGTH[StandardLibraryList]) DO AddIt[StandardLibraryList[i], StandardLibrary] ENDLOOP;
FOR i: CARD IN [0..LENGTH[AdditonalList]) DO AddIt[AdditonalList[i], Additional] ENDLOOP;
FOR i: CARD IN [0..LENGTH[MaybeKeywordsList]) DO AddIt[MaybeKeywordsList[i], MaybeKeywords] ENDLOOP;
FOR i: CARD IN [0..LENGTH[KeywordsList]) DO AddIt[KeywordsList[i], Keywords] ENDLOOP;
};
Keyword: PROC [r: ROPE] RETURNS [AClass] = BEGIN
Return TRUE if r is a C keyword.
found: BOOL;
val: REF;
[found: found, val: val] ¬ keyTab.Fetch[key: r];
IF NOT found THEN RETURN [NotAKeyword];
RETURN [NARROW[val, REF ARec].class];
END;
SetSpanCLooks: PROC [span: TextNode.Span, event: Tioga.Event] = {
root: Tioga.Node = TextNode.Root[span.start.node];
rdr: RopeReader.Ref ¬ RopeReader.Create[];
SetMesaLooks: PROC [node: Tioga.Node, start, len: TextNode.Offset]
RETURNS [stop: BOOL] = {
p, c, k: INT ¬ 0;
IF node.comment AND start = 0 AND len = TextEdit.Size[node]
THEN { -- skip the entire comment node -- stop ¬ FALSE }
ELSE stop ¬ DoIt[node, start, len, rdr, event];
};
[] ¬ TEditLocks.Lock[root, "SetSpanMesaLooks"];
EditSpanSupport.Apply[span, SetMesaLooks];
TEditLocks.Unlock[root];
};
namifyTypes: BOOL ¬ FALSE;
DoIt: PROC [node: Tioga.Node, start, len: TextNode.Offset, rdr: RopeReader.Ref, event: Tioga.Event] RETURNS [stop: BOOL ¬ FALSE] = {
root: Tioga.Node = TextNode.Root[node];
pStart, pLen: TextNode.Offset ¬ 0;
lastChar: CHAR ¬ '\000;
token: REF TEXT ¬ RefText.New[40];
tokenKind: IO.TokenKind ¬ tokenERROR;
allComments: BOOL ¬ TRUE;
nameLooks: TextLooks.Looks ¬ TextLooks.noLooks;
cLooks: TextLooks.Looks ¬ TextLooks.noLooks;
stream: STREAM ¬ NIL;
size: INT ~ Rope.Size[node.rope];
first: INT ~ MIN[start, size];
length: INT ~ MIN[len, size-start];
end: INT ~ start+length;
state: {null} ¬ null;
These are the states of the finite state machine
stream ¬ IO.RIS[node.rope.Substr[0, end]];
IO.SetIndex[stream, first];
cLooks['n] ¬ cLooks['z] ¬ TRUE; -- Change this one to include all the font types affected
nameLooks['n] ← TRUE;  -- This one was the fonts of names
IF end > first THEN EditSpan.ChangeLooks[
root: root,
span: [[node, first], [node, end-1]],
remove: cLooks,
event: event
];
UNTIL tokenKind = tokenEOF DO
add, alpha, keyword: BOOL ¬ FALSE;
addLooks, remLooks: TextLooks.Looks ¬ TextLooks.noLooks;
tokStart, tokLen: INT ¬ 0;
[tokenKind, token] ¬ IO.GetCedarToken[stream, token, FALSE !
RuntimeError.BoundsFault => { tokenKind ¬ tokenERROR; token.length ¬ 0; CONTINUE }
];
tokLen ¬ token.length;
tokStart ¬ IO.GetIndex[stream]-tokLen;
remLooks ¬ cLooks;
SELECT tokenKind FROM
tokenID => {
SELECT Keyword[Rope.FromRefText[token]] FROM
Keywords, MaybeKeywords => {
add ¬ TRUE;
addLooks['n] ¬ TRUE;
remLooks['n] ¬ FALSE;
These are the fonts of keywords
state ¬ null;
};
NotAKeyword => {
For name definitions.
};
PreProcessor, StandardLibrary, Additional => {
add ¬ TRUE;
addLooks['z] ¬ TRUE;
remLooks['z] ¬ FALSE;
These are the fonts of keywords
state ¬ null;
};
ENDCASE => ERROR;
};
tokenSINGLE => {
NULL;
IF (state = name AND token[0] = ':)
THEN state ← nameColon
ELSE {
IF state = name THEN pLen ← pLen + 1;
};
};
ENDCASE => state ¬ null;
IF add THEN {
EditSpan.ChangeLooks[root, [[node,tokStart], [node,tokStart+tokLen-1]], remLooks, addLooks, event];
add ¬ FALSE;
addLooks ¬ TextLooks.noLooks;
remLooks ¬ cLooks;
};
ENDLOOP;
};
SetCLooksOp: TEditInput.CommandProc = {
DoSet: PROC [root: Tioga.Node, tSel: TEditDocument.Selection] = {
span: TextNode.Span ¬ [tSel.start.pos, tSel.end.pos];
firstText: Tioga.Node ~ tSel.start.pos.node;
lastText: Tioga.Node ~ tSel.end.pos.node;
IF firstText # NIL THEN NodeAddrs.PutTextAddr[firstText, $Start, tSel.start.pos.where];
IF lastText # NIL THEN NodeAddrs.PutTextAddr[lastText, $End, tSel.end.pos.where+1];
IF tSel.granularity=point OR (tSel.granularity=char AND tSel.start.pos=tSel.end.pos) THEN {
do the entire node
span.start.where ¬ 0;
span.end.where ¬ TextNode.EndPos[span.end.node] };
SetSpanCLooks[span, TEditInput.currentEvent];
tSel.start.pos ¬ [firstText,
IF firstText=NIL THEN TextNode.NodeItself
ELSE NodeAddrs.GetTextAddr[firstText,$Start].location];
tSel.end.pos ¬ [lastText,
IF lastText=NIL
THEN TextNode.NodeItself
ELSE MAX[NodeAddrs.GetTextAddr[lastText, $End].location, 1] - 1
];
IF firstText#NIL THEN NodeAddrs.RemTextAddr[firstText, $Start];
IF lastText#NIL THEN NodeAddrs.RemTextAddr[lastText, $End];
TEditSelection.MakeSelection[new: tSel];
TEditSelection.SetSelLooks[TEditSelection.pSel];
};
TEditInputOps.CallWithLocks[DoSet];
quit ¬ TRUE;
};
nullTiogaChar: TiogaAccess.TiogaChar ~ [charSet: 0, char: 0c, looks: ALL[FALSE], format: NIL, comment: FALSE, endOfNode: FALSE, deltaLevel: 0, propList: NIL];
stringLooks: TiogaAccess.Looks ¬ TextLooks.RopeToLooks["f"];
commentLooks: TiogaAccess.Looks ¬ TextLooks.RopeToLooks["c"];
DoLooksAndIndentation: PROC [reader: TiogaAccess.Reader, writer: TiogaAccess.Writer] RETURNS [TiogaAccess.Writer] ~ {
tokenBuffer: REF TEXT ¬ RefText.ObtainScratch[100];
PutToken: PROC [looks: TiogaAccess.Looks ¬ ALL[FALSE]] ~ {
tChar: TiogaAccess.TiogaChar ¬ nullTiogaChar;
tChar.looks ¬ looks;
FOR i: NAT IN [0..tokenBuffer.length) DO
tChar.char ¬ tokenBuffer[i];
TiogaAccess.Put[writer, tChar];
ENDLOOP;
tokenBuffer.length ¬ 0;
};
EmitToken: PROC ~ {
looks: TiogaAccess.Looks ¬ ALL[FALSE];
IF tokenBuffer.length # 0 THEN {
IF tokenBuffer[0] = '# THEN looks['z] ¬ TRUE ELSE {
SELECT Keyword[RefText.TrustTextAsRope[tokenBuffer]] FROM
Keywords, MaybeKeywords => {
looks['n] ¬ TRUE;
IF nest >= 0 AND matchDelim[nest] # semi THEN {
SELECT Atom.MakeAtomFromRefText[tokenBuffer] FROM
$if => {
The extra test for if allows else if to be treated nicely
IF matchDelim[nest] # else THEN { nest ¬ nest + 1 };
matchDelim[nest] ¬ semi;
};
$else => {
nest ¬ nest + 1;
matchDelim[nest] ¬ else;
};
$for, $while, $switch => {
nest ¬ nest + 1;
matchDelim[nest] ¬ semi;
};
ENDCASE;
};
};
NotAKeyword => {
For name definitions.
};
PreProcessor, StandardLibrary, Additional => {
looks['z] ¬ TRUE;
};
ENDCASE => ERROR;
};
};
PutToken[looks];
};
Emit1: PROC [looks: TiogaAccess.Looks ¬ ALL[FALSE]] ~ {
tChar.looks ¬ looks;
TiogaAccess.Put[writer, tChar];
};
EmitEOL: PROC [] ~ {
newNest: INT ~ MIN[nest0+1, nest];
TiogaAccess.Put[writer, [charSet: 0, char: '\n, looks: ALL[FALSE], format: NIL, comment: tChar.comment, endOfNode: TRUE, deltaLevel: newNest-nest0, propList: tChar.propList]];
nest0 ¬ newNest;
};
Collect: PROC ~ {
tokenBuffer ¬ RefText.AppendChar[tokenBuffer, tChar.char];
};
tChar: TiogaAccess.TiogaChar ¬ nullTiogaChar;
State: TYPE ~ { null, id, slash, comment, star, sstring, sstringesc, dstring, dstringesc, startOfLine };
state: State ¬ null;
afterElse: BOOL ¬ FALSE;
InType: TYPE ~ { null, white, semi, ordinary, alphanumeric, slash, star, backslash, squote, dquote, open, close, eol, eof };
inType: InType ¬ null;
nest: INT ¬ 0;
nest0: INT ¬ 0;
MatchDelim: TYPE ~ { null, semi, else };
matchDelim: PACKED ARRAY [0..100) OF MatchDelim ¬ ALL[null];
Get: PROC ~ {
IF TiogaAccess.EndOf[reader]
THEN {tChar ¬ nullTiogaChar; inType ¬ eof }
ELSE {
tChar ¬ TiogaAccess.Get[reader];
inType ¬ ordinary;
IF tChar.charSet = 0 THEN {
SELECT tChar.char FROM
'(, '{, '[ => inType ¬ open;
'), '}, '] => inType ¬ close;
'/ => inType ¬ slash;
'* => inType ¬ star;
'\\ => inType ¬ backslash;
'\' => inType ¬ squote;
'\" => inType ¬ dquote;
'\r, '\l => inType ¬ eol;
'\t, ' => inType ¬ white;
IN ['A..'Z], IN ['a..'z], IN ['0..'9], '←, '# => inType ¬ alphanumeric;
'; => inType ¬ semi;
ENDCASE;
};
};
};
Get[];
UNTIL inType = eof DO
SELECT state FROM
null => {
SELECT inType FROM
semi => {
IF nest>0 AND matchDelim[nest] >= semi THEN { nest ¬ nest - 1 };
Emit1[];
};
ordinary, star, backslash, white => { Emit1[] };
alphanumeric => { Collect[]; state ¬ id };
slash => { Collect[]; state ¬ slash };
squote => { Emit1[stringLooks]; state ¬ sstring };
dquote => { Emit1[stringLooks]; state ¬ dstring };
open => {
Emit1[];
IF tChar.char = '{ AND nest>0 AND matchDelim[nest] >= semi THEN NULL ELSE nest ¬ nest + 1;
IF nest >= 0 THEN matchDelim[nest] ¬ null;
};
close => {
Emit1[];
nest ¬ nest - 1;
};
eol => { EmitEOL[]; state ¬ startOfLine };
ENDCASE => ERROR;
};
id => {
SELECT inType FROM
alphanumeric => { Collect[] };
ENDCASE => { EmitToken[]; state ¬ null; LOOP };
};
slash => {
SELECT inType FROM
star => { Collect[]; PutToken[commentLooks]; state ¬ comment };
ENDCASE => { PutToken[]; state ¬ null; LOOP };
};
comment => {
SELECT inType FROM
star => { Emit1[commentLooks]; state ¬ star };
ENDCASE => { Emit1[commentLooks] };
};
star => {
Emit1[commentLooks];
SELECT inType FROM
slash => { state ¬ null };
star => { };
ENDCASE => { state ¬ comment };
};
sstring => {
SELECT inType FROM
backslash => { Emit1[stringLooks]; state ¬ sstringesc };
squote => { Emit1[stringLooks]; state ¬ null };
ENDCASE => { Emit1[stringLooks] };
};
sstringesc => { Emit1[stringLooks]; state ¬ sstring };
dstring => {
SELECT inType FROM
backslash => { Emit1[stringLooks]; state ¬ dstringesc };
dquote => { Emit1[stringLooks]; state ¬ null };
ENDCASE => { Emit1[stringLooks] };
};
dstringesc => { Emit1[stringLooks]; state ¬ dstring };
startOfLine => {
SELECT inType FROM
white => { };
ENDCASE => { state ¬ null; LOOP };
};
ENDCASE => ERROR;
Get[];
ENDLOOP;
RETURN [writer]
};
trace: ROPE ¬ NIL;
SetCLooksAndIndentationOp: TEditInput.CommandProc = {
DoSet: PROC [root: Tioga.Node, tSel: TEditDocument.Selection] = {
ENABLE UNCAUGHT => {
trace ¬ CommanderOps.DoCommandRope[commandLine: "StackTrace -verbose", parent: NIL].out;
CONTINUE;
};
TiogaAccessViewers.WriteSelection[DoLooksAndIndentation[TiogaAccessViewers.FromSelection[], TiogaAccess.Create[]]];
};
TEditInputOps.CallWithLocks[DoSet];
quit ¬ TRUE;
};
CLooksAndIndentationTraceCommand: Commander.CommandProc ~ {
msg ¬ trace;
trace ¬ NIL;
};
CLooksAndIndentationCommand: Commander.CommandProc ~ {
arg0: ROPE ¬ CommanderOps.NextArgument[cmd];
IF arg0 = NIL THEN CommanderOps.Failed[cmd.procData.doc];
FOR arg: ROPE ¬ arg0, CommanderOps.NextArgument[cmd] UNTIL arg = NIL DO
reader: TiogaAccess.Reader ~ TiogaAccess.FromFile[arg];
writer: TiogaAccess.Writer ¬ TiogaAccess.Create[];
root: TiogaAccess.TiogaChar ¬ TiogaAccess.Get[reader];
root.propList ¬ LIST[
[$NewlineDelimiter, Rope.Flatten["\n"]],
[$Prefix, Rope.Flatten["(cedar) style"]]];
TiogaAccess.Put[writer, root];
writer ¬ DoLooksAndIndentation[reader, writer];
TiogaAccess.WriteFile[writer, arg];
ENDLOOP;
};
Commander.Register["CLooksAndIndentationTrace", CLooksAndIndentationTraceCommand, "Stack trace for debugging SetCLooksAndIndentationOp"];
Commander.Register["CLooksAndIndentation", CLooksAndIndentationCommand, "Imposes c formatting on a tioga file\nargs: filename ..."];
PreProcess[];
TEditInput.Register[$SetCLooks, SetCLooksOp, TRUE];
TEditInput.Register[$SetCLooksAndIndentation, SetCLooksAndIndentationOp, TRUE];
END.