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
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", "SIGL", "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 ..."];
TEditInput.Register[$SetCLooks, SetCLooksOp, TRUE];
TEditInput.Register[$SetCLooksAndIndentation, SetCLooksAndIndentationOp, TRUE];
END.