Last Edited by: teitelman, April 19, 1983 12:14 pm
DIRECTORY
AMBridge USING [TVForReferent],
AMTypes USING [Class, Coerce, Domain, EnclosingBody, Error, First, GlobalParent, Globals, GroundStar, IndexToName, IndexToType, Last, Locals, NameToIndex, NComponents, Next, Procedure, Referent, Signal, TVToName, TVType, TypeClass, UnderType, Value],
Atom USING [GetPName, TypeGetProp],
BasicUserExec USING [Interface, InterfaceRec, CorrectionProc],
BBContext USING [Context, FindAction, FindMatchingGlobalFrames, FrameAction, EnumerateFramesInContext, GetContents],
BBEval USING [AbortProc, NewEvalHead, HelpFatal, HelpWrongType, HelpId, HelpSelector, HelpDefault, EvalHead, RopeOrTV],
IO USING [BreakProc, GetChar, card, GetOutputStreamRope, NUL, Put, PutF, PutRope, refAny, Reset, RIS, ROPE, rope, ROS, STREAM, text, tv, type, GetToken, UserAborted],
List USING [CompareProc, Sort],
Process USING [Detach],
Rope USING [Compare, Equal, Find, Substr, Concat, Cat, Fetch, Length, ROPE, Upper],
RTBasic USING [nullType],
RTMiniModel USING [AcquireIRType, GetLoadstateDefsNames],
Spell USING [GeneratorFromProcs, SpellingList, SpellingGenerator],
SymTab USING [Create, Fetch, Store],
UserExec USING [EvalExpr, CreateExpr, HistoryEvent, Expression, ExecHandle, UserAbort, ResetUserAbort, TV, Type, GetTheOne, FinishAskUser, SetupAskUser, EvaluationFailed, GetExecHandle, Viewer, GetStreams],
UserExecPrivate USING [GetPrivateStuff, PrintDeclFromSource, ExecPrivateRecord, GeneratorsRecord, Zone, EvalHeadData],
WorldVM USING [LocalWorld],
ViewerAbort USING [UserAbort, ResetUserAbort]
;
DwimImpl: CEDAR MONITOR
IMPORTS AMBridge, AMTypes, Atom, BBContext, BBEval, IO, List, Process, Rope, RTMiniModel, Spell, SymTab, UserExec, UserExecPrivate, WorldVM, ViewerAbort
EXPORTS UserExec, UserExecPrivate
SHARES UserExec, Atom =
Atom: TypeGetProp
UserExec: AcquireStreams, ReleaseStreams
BEGIN OPEN IO, UserExec, UserExecPrivate;
connecting concrete and opaque types
ExecPrivateRecord: PUBLIC TYPE = UserExecPrivate.ExecPrivateRecord;
help procedures
HelpFatal: BBEval.HelpFatal -- [head: EvalHead, parent: Tree, msg: ROPE] -- = {
evalHeadData: REF EvalHeadData = NARROW[head.data];
ERROR EvaluationFailed[expr: evalHeadData.expr, msg: msg];
};
HelpWrongType: BBEval.HelpWrongType -- [head: EvalHead, parent: Tree, value: TV, target: Type, msg: ROPE] RETURNS [correct: RopeOrTV] -- = {
OPEN AMTypes;
evalHeadData: REF EvalHeadData = NARROW[head.data];
outRopeStream: STREAM = ROS[];
flag: BOOLEAN;
shouldBeTV: TV;
[flag, shouldBeTV] ← SupplyCorrectValue[exec: evalHeadData.exec, targetType: target, valueSupplied: value]; -- e.g. convert value to REF value, convert ROPE to Long String, REF TEXT, REF READONLY TEXT, or Rope.Text
IF flag THEN RETURN[[tv[shouldBeTV]]];
SELECT TypeClass[target] FROM
procedure => {
IF value = NIL THEN -- check for inline
{
i: INT;
firstToken: ROPE;
exec: UserExec.ExecHandle = evalHeadData.exec;
outRopeStream.Reset[];
outRopeStream.Put[refAny[parent]];
firstToken ← IO.GetToken[RIS[GetOutputStreamRope[outRopeStream]]]; -- GetToken used, rather than GetMesaToken, so that Foo.Fie will be returned
msg ← "Can't call an INLINE yet";
i ← Rope.Find[firstToken, "."];
IF i # -1 THEN
{r: ROPE;
outRopeStream.Reset[];
outRopeStream.Put[rope[firstToken], text[": "]];
[] ← UserExecPrivate.PrintDeclFromSource[target: Rope.Substr[firstToken, i + 1], file: Rope.Concat[Rope.Substr[base: firstToken, len: i], ".mesa"], exec: exec];
r ← GetOutputStreamRope[outRopeStream];
IF Rope.Find[r, "INLINE"] # -1 THEN msg ← Rope.Concat[r, msg];
};
RETURN[[fail[msg]]];
};
};
ENDCASE;
outRopeStream.Reset[];
{
ENABLE ANY =>
{UserExec.GetStreams[evalHeadData.exec].out.PutRope[outRopeStream.GetOutputStreamRope[]]; -- if error occurs while printing, print how far you got.
outRopeStream.Reset[]; -- so wont get printed twice if more than one signal
};
outRopeStream.PutF["*n********Wrong Type: %g\nshould be of type: %g\nis of type: %g", tv[value], type[target], type[TVType[value]]];
};
RETURN[[fail[outRopeStream.GetOutputStreamRope[]]]];
};
HelpId: BBEval.HelpId -- [head: EvalHead, parent: Tree, id: ROPE, context: Type, target: Type, msg: ROPE] RETURNS [correct: RopeOrTV] -- = {
evalHeadData: REF EvalHeadData = NARROW[head.data];
exec: UserExec.ExecHandle = evalHeadData.exec;
viewer: Viewer = evalHeadData.viewer;
expr: Expression = evalHeadData.expr;
shouldBe: ROPE;
shouldBeTV: TV;
success: BOOLFALSE;
{ -- to establish scope for exits
ENABLE IO.UserAborted =>
{UserExec.ResetUserAbort[exec]; -- so guy up above will be able to print the message without himself being aborted.
msg ← Rope.Concat["aborted\n", msg];
GOTO Fail;
};
IF Rope.Equal[msg, "undefined"] THEN
{
underType: Type ← AMTypes.UnderType[target];
class: AMTypes.Class = AMTypes.TypeClass[underType];
ground: Type;
flag: BOOLEAN;
WOULD BE NICE IF COULD DISTINGUISH LISTT.APPEND FROM LISTT SO THAT COULD TRY MODULE CORRECTION FIRST ON FORMER.
msg ← Rope.Cat[id, " is ", msg];
IF (expr # NIL AND expr.dontCorrect) THEN GOTO Fail;
IF Rope.Fetch[id, 0] = '& THEN { -- user types &A3 to refer to 3rd & var in workarea A
stream: STREAM = IO.RIS[id];
execId: ROPE;
exec: UserExec.ExecHandle;
private: REF UserExecPrivate.ExecPrivateRecord;
untilNumber: IO.BreakProc = {
RETURN[IF char IN ['0..'9] THEN break ELSE other];
};
[] ← stream.GetChar[]; -- the &
execId ← stream.GetToken[untilNumber];
IF (exec ← UserExec.GetExecHandle[id: execId]) = NIL THEN GOTO Fail;
private ← UserExecPrivate.GetPrivateStuff[exec];
[success, shouldBeTV] ← SymTab.Fetch[private.evalHead.specials, Rope.Concat["&", stream.GetToken[]]];
IF success THEN GOTO Success;
GOTO Fail;
};
IF class = enumerated THEN
{
enumTypeGenerator: Spell.SpellingGenerator ← GetGenerators[evalHeadData].enumTypeGenerator;
enumGenState: REF EnumGenState = NARROW[enumTypeGenerator.clientData];
enumGenState^ ← [tv: AMTypes.First[underType]];
shouldBe ← UserExec.GetTheOne[unknown: id, generator: enumTypeGenerator, event: NIL, exec: exec, viewer: viewer];
IF shouldBe # NIL THEN GOTO Success;
}
ELSE IF class = subrange AND AMTypes.TypeClass[ground ← AMTypes.GroundStar[underType]] = enumerated THEN
{
i: INT ← 0;
i ← AMTypes.NameToIndex[ground, id ! AMTypes.Error => IF reason = badName THEN CONTINUE];
IF i # 0 THEN -- WORK AROUND means id was in the enumerated type to begin with
{tv: TV = AMTypes.Value[ground, i]; -- gets the corresponding tv.
shouldBeTV ← NIL;
shouldBeTV ← AMTypes.Coerce[tv: tv, targetType: target !
AMTypes.Error => IF reason = typeFault OR reason = rangeFault THEN CONTINUE;
];
IF shouldBeTV # NIL THEN GOTO Success; -- was correct all along
msg ← "out of range";
}
ELSE
{subrangeGenerator: Spell.SpellingGenerator ← GetGenerators[evalHeadData].subrangeGenerator;
subrangeGenState: REF SubrangeGenState = NARROW[subrangeGenerator.clientData];
subrangeGenState^ ← [tv: AMTypes.First[underType], last: AMTypes.Last[underType], ground: ground];
shouldBe ← UserExec.GetTheOne[unknown: id, generator: subrangeGenerator, event: NIL, exec: exec, viewer: viewer];
IF shouldBe # NIL THEN GOTO Success;
};
};
still inside of Undefined
{
IF RTMiniModel.AcquireIRType[defsName: id ! AMTypes.Error => CONTINUE] # RTBasic.nullType THEN TRUSTED {
shouldBeTV ← AMBridge.TVForReferent[NEW[BasicUserExec.Interface ← NEW[BasicUserExec.InterfaceRec ← [id]]]];
GOTO Success;
};
name: ATOM = Atom.MakeAtom[id]; -- work around.
FOR l: LIST OF ATOM ← interfaceList, l.rest UNTIL l = NIL DO
IF l.first = name THEN TRUSTED {
shouldBeTV ← AMBridge.TVForReferent[NEW[BasicUserExec.Interface ← NEW[ROPE ← id]]];
GOTO Success;
};
ENDLOOP;
};
IF evalHeadData.defaultInterface # NIL THEN {
ENABLE EvaluationFailed => CONTINUE;
expr: UserExec.Expression = UserExec.CreateExpr[rope: Rope.Cat[evalHeadData.defaultInterface, ".", id]];
save: TV = SymTab.Fetch[x: head.specials, key: "&"].val;
shouldBeTV ← UserExec.EvalExpr[expr: expr, exec: evalHeadData.exec, viewer: evalHeadData.viewer].value;
[] ← SymTab.Store[x: head.specials, key: "&", val: save];
IF expr.correctionMade THEN shouldBe ← Rope.Substr[base: expr.rope, start: Rope.Find[expr.rope, "."] + 1];
GOTO Success;
};
{ -- search default global context
gf: TV = BBContext.GetContents[head.globalContext].gf;
IF gf # NIL AND (shouldBe ← FixSpellFromType[evalHeadData: evalHeadData, unknown: id, type: AMTypes.TVType[AMTypes.Globals[gf]]]) # NIL THEN GOTO Success;
};
{ -- search local frames (still inside of Rope.Equal[msg, "undefined"])
gf: TV;
SearchFrame: BBContext.FrameAction -- [lf: TV] RETURNS [ActionContinuation ← continue] -- = {
ptv: TV;
type: Type;
IF (count ← count + 1) > howMany AND NOT Rope.Equal[AMTypes.TVToName[gf], AMTypes.TVToName[AMTypes.GlobalParent[lf]]] THEN RETURN[quit]; -- says once you go beyond howMany, you can keep going so long as you are in the same global frame. Heuristic is to prevent searching off into space somewhere, but to allow climbing stack inside of procedure calls in same module.
gf ← AMTypes.GlobalParent[lf];
ptv ← AMTypes.Procedure[lf ! AMTypes.Error => CONTINUE];
IF ptv = NIL THEN ptv ← AMTypes.Signal[lf ! AMTypes.Error => CONTINUE];
DO
IF (shouldBe ← FixSpellFromType[evalHeadData: evalHeadData, unknown: id, type: AMTypes.TVType[AMTypes.Locals[lf]]]) # NIL THEN RETURN[quit];
IF (lf ← AMTypes.EnclosingBody[lf]) = NIL THEN EXIT;
ENDLOOP;
IF ptv # NIL AND (type ← AMTypes.TVType[ptv]) # RTBasic.nullType THEN
{IF (shouldBe ← FixSpellFromType[evalHeadData: evalHeadData, unknown: id, type: AMTypes.UnderType[AMTypes.Domain[type]]]) # NIL THEN RETURN[quit];
};
IF (shouldBe ← FixSpellFromType[evalHeadData: evalHeadData, unknown: id, type: AMTypes.TVType[AMTypes.Globals[gf]]]) # NIL THEN RETURN[quit];
}; -- end of SearchFrame
count: INT ← 0;
howMany: INT = 3;
IF head.context # NIL THEN BBContext.EnumerateFramesInContext[context: head.context, action: SearchFrame];
IF shouldBe # NIL THEN GOTO Success;
};

IF
target = RTBasic.nullType THEN
{
line: ROPE = expr.rope;
i: INT ← Rope.Find[line, id];
char: CHARACTER;
IF i = -1 OR (i ← i + Rope.Length[id]) = Rope.Length[line] OR (char ← Rope.Fetch[line, i]) # '[ THEN -- id[ can't possibly be frame name. cuts down a lot of cases.
{
frameGenerator: Spell.SpellingGenerator = GetGenerators[evalHeadData].frameGenerator;
frameGenState: REF FrameGenState = NARROW[frameGenerator.clientData];
frameGenState.startsWith ← Rope.Upper[Rope.Fetch[id, 0]];
IF frameGenState.startsWith IN ['A..'Z] AND (frameGenState.frameList ← frameCache[frameGenState.startsWith]) # NIL THEN shouldBe ← UserExec.GetTheOne[unknown: id, generator: frameGenerator, event: NIL, exec: exec, viewer: viewer];
IF shouldBe # NIL THEN GOTO Success;
};
};
[flag, shouldBeTV] ← CorrectUndefinedId[exec: exec, targetType: target, id: id];
IF flag THEN GOTO Success;
}
ELSE IF Rope.Equal[msg, "invalid selector"] THEN
{
IF expr # NIL AND expr.dontCorrect THEN GOTO Fail;
shouldBe ← FixSpellFromType[evalHeadData: evalHeadData, unknown: id, type: context];
IF shouldBe # NIL THEN GOTO Success;
};
GOTO Fail;
EXITS
Success => success ← TRUE; -- necessary because a plausible successful return is with shouldBeTV = NIL
Fail => NULL;
};
IF evalHeadData.confirmMenuUp THEN
{UserExec.FinishAskUser[evalHeadData.viewer];
evalHeadData.confirmMenuUp ← FALSE;
};
IF NOT success THEN RETURN[[fail[msg]]]
ELSE IF shouldBe # NIL THEN
{expr.correctionMade ← TRUE;
IF shouldBeTV # NIL THEN RETURN[[both[rope: shouldBe, tv: shouldBeTV]]]
ELSE RETURN[[rope[shouldBe]]];
}
ELSE RETURN[[tv[shouldBeTV]]];
};
HelpSelector: BBEval.HelpSelector -- [head: EvalHead, parent: Tree, id: ROPE, context: TV, target: Type, msg: ROPE] RETURNS [correct: RopeOrTV] -- = {
OPEN AMTypes;
evalHeadData: REF EvalHeadData = NARROW[head.data];
errorMsg: ROPE ← Rope.Cat[msg, " on ", id];
shouldBe: ROPE;
type: Type = UnderType[TVType[context]];
class: Class ← TypeClass[type];
{
ENABLE IO.UserAborted =>
{UserExec.ResetUserAbort[evalHeadData.exec];
msg ← Rope.Concat["aborted\n", msg];
GOTO Fail;
};
IF evalHeadData.expr.dontCorrect THEN GOTO Fail;
SELECT class FROM
globalFrame =>
{globalsType: Type = TVType[Globals[context]];
shouldBe ← FixSpellFromType[evalHeadData: evalHeadData, unknown: id, type: globalsType];
IF shouldBe # NIL THEN GOTO Success;
};
ref, list => BEGIN
typ: Type ← UnderType[TVType[context]];
class ← TypeClass[typ];
SELECT class FROM
ref, list, pointer, longPointer =>
{referentTV: TV ← Referent[context ! ANY => GOTO Fail];
typ ← UnderType[TVType[referentTV]];
class ← TypeClass[typ];
};
ENDCASE;
SELECT class FROM
record, structure =>
{shouldBe ← FixSpellFromType[evalHeadData: evalHeadData, unknown: id, type: typ];
IF shouldBe # NIL THEN GOTO Success;
};
ENDCASE;
END;
record =>
{shouldBe ← FixSpellFromType[evalHeadData: evalHeadData, unknown: id, type: type];
IF shouldBe # NIL THEN GOTO Success;
};
ENDCASE;
GOTO Fail;
EXITS
Success => NULL;
Fail => NULL;
};
IF evalHeadData.confirmMenuUp THEN
{UserExec.FinishAskUser[evalHeadData.viewer];
evalHeadData.confirmMenuUp ← FALSE;
};
IF shouldBe # NIL THEN RETURN[[rope[shouldBe]]]
ELSE RETURN[[fail[errorMsg]]];
};
HelpDefault: BBEval.HelpDefault -- [head: EvalHead, parent: Tree, type: Type, index: CARDINAL, msg: ROPE] RETURNS [correct: RopeOrTV] -- = {
OPEN AMTypes;
evalHeadData: REF EvalHeadData = NARROW[head.data];
outRopeStream: STREAM = ROS[];
flag: BOOLEAN;
shouldBe: TV;
argType: Type ← IndexToType[type, index];
argName: ROPE;
[flag, shouldBe] ← SupplyMissingArgument[argType]; -- until IndexToDefaultInitialValue works for all cases. e.g. Docs uses this to default REALS to notSpecifiedreal, etc.
IF flag THEN RETURN[[tv[shouldBe]]];
{
ENABLE ANY =>
{exec: UserExec.ExecHandle = evalHeadData.exec;
UserExec.GetStreams[exec].out.PutRope[outRopeStream.GetOutputStreamRope[]]; -- if error occurs while printing, print how far you got.
};
outRopeStream.PutF["\n******Missing Arguments: "];
argName ← IndexToName[type, index];
IF argName # NIL THEN outRopeStream.Put[rope[argName]]
ELSE {PrintOrdinal[outRopeStream, index]; outRopeStream.PutRope[" argument"]};
outRopeStream.Put[rope[": "], IO.type[argType]];
};
RETURN[[fail[outRopeStream.GetOutputStreamRope[]]]];
};
spelling correction
FixSpellFromType: PROC [evalHeadData: REF EvalHeadData, unknown: ROPE, type: Type, signal: BOOLEANTRUE] RETURNS[shouldBe: ROPE] = {
typeElementGenerator: Spell.SpellingGenerator = GetGenerators[evalHeadData].typeElementGenerator;
typeGenState: REF TypeGenState = NARROW[typeElementGenerator.clientData];
typeGenState^ ← [type: type, last: AMTypes.NComponents[type], index: 1];
IF NOT evalHeadData.confirmMenuUp AND evalHeadData.viewer # NIL THEN
{UserExec.SetupAskUser[evalHeadData.viewer];
evalHeadData.confirmMenuUp ← TRUE;
};
shouldBe ← UserExec.GetTheOne[unknown: unknown, generator: typeElementGenerator, event: NIL, exec: evalHeadData.exec, viewer: evalHeadData.viewer];
IF shouldBe # NIL AND signal THEN
{expr: Expression = evalHeadData.expr;
IF expr # NIL THEN expr.correctionMade ← TRUE;
};
};
generators used by dwim are stored in the private.generators field. They are initialized first time they are used.
GetGenerators: PROC [evalHeadData: REF EvalHeadData] RETURNS[REF GeneratorsRecord] = {
IF evalHeadData.generators = NIL THEN evalHeadData.generators ← UserExecPrivate.Zone.NEW[GeneratorsRecord ←
[
typeElementGenerator: Spell.GeneratorFromProcs[generate: TypeGenerate, clientData: UserExecPrivate.Zone.NEW[TypeGenState ← []]],
enumTypeGenerator: Spell.GeneratorFromProcs[generate: EnumGenerate, clientData: UserExecPrivate.Zone.NEW[EnumGenState ← []]],
subrangeGenerator: Spell.GeneratorFromProcs[generate: SubrangeGenerate, clientData: UserExecPrivate.Zone.NEW[SubrangeGenState ← []]],
frameGenerator: Spell.GeneratorFromProcs[generate: FrameGenerate, clientData: UserExecPrivate.Zone.NEW[FrameGenState ← []]]
]
];
RETURN[evalHeadData.generators];
};
TypeGenState: TYPE = RECORD[type: Type ← NULL, last: CARDINALNULL, index: CARDINALNULL];
TypeGenerate: PROCEDURE [self: Spell.SpellingGenerator] RETURNS [object: ROPE] = {
r: ROPE;
typeGenState: REF TypeGenState = NARROW[self.clientData];
IF typeGenState.index > typeGenState.last THEN RETURN[NIL];
r ← AMTypes.IndexToName[typeGenState.type, typeGenState.index];
typeGenState.index ← typeGenState.index + 1;
RETURN[r]
};
EnumGenState: TYPE = RECORD[tv: TVNIL];
EnumGenerate: PROCEDURE [self: Spell.SpellingGenerator] RETURNS [object: ROPE] = {
enumGenState: REF EnumGenState = NARROW[self.clientData];
tv: TV = enumGenState.tv;
r: ROPE;
IF tv = NIL THEN RETURN[NIL];
r ← AMTypes.TVToName[tv];
enumGenState.tv ← AMTypes.Next[tv];
RETURN[r]
};
SubrangeGenState: TYPE = RECORD[tv, last: TVNIL, ground: Type ← NULL];
SubrangeGenerate: PROCEDURE [self: Spell.SpellingGenerator] RETURNS [object: ROPE] = {
subrangeGenState: REF SubrangeGenState = NARROW[self.clientData];
tv: TV = subrangeGenState.tv;
r: ROPE;
IF tv = NIL THEN RETURN[NIL]; -- HOW TEST FOR LAST?
r ← AMTypes.TVToName[AMTypes.Coerce[tv, subrangeGenState.ground]];
subrangeGenState.tv ← AMTypes.Next[tv];
RETURN[r]
};
FrameGenState: TYPE = RECORD[startsWith: CHARACTERIO.NUL, frameList: LIST OF ROPE ← NIL];
FrameGenerate: PROCEDURE [self: Spell.SpellingGenerator] RETURNS [object: ROPE] = {
frameGenState: REF FrameGenState ← NARROW[self.clientData];
object ← frameGenState.frameList.first;
IF Rope.Upper[Rope.Fetch[object, 0]] > frameGenState.startsWith THEN RETURN[NIL];
frameGenState.frameList ← frameGenState.frameList.rest;
}; -- of FrameGenerate
frame cache
frameCache: REF ARRAY CHARACTER['A..'Z] OF LIST OF ROPENIL; -- contains a pointer into frameList starting at first frame which begins with given character
frameList: LIST OF ROPENIL;
interfaceList: LIST OF ATOMNIL; -- workaround so I can tell if something is the name of an interface
previouslySeen: ROPENIL;
BuildFrameCache: ENTRY PROCEDURE = {
FindAction: BBContext.FindAction -- [gf: TV, name: ROPE] RETURNS [ActionContinuation ← continue] -- = {
IF name # NIL THEN {
IF previouslySeen = NIL THEN previouslySeen ← name;
frameList ← CONS[name, frameList];
};
RETURN[continue];
};
compare: List.CompareProc = {
RETURN[Rope.Compare[NARROW[ref1, Rope.ROPE], NARROW[ref2, Rope.ROPE], FALSE]]
};
lastChar: CHARACTERIO.NUL;
interfaceList ← RTMiniModel.GetLoadstateDefsNames[];
FOR l: LIST OF ATOM ← RTMiniModel.GetLoadstateDefsNames[], l.rest UNTIL l = NIL DO
frameList ← CONS[Atom.GetPName[l.first], frameList];
ENDLOOP;
TRUSTED {BBContext.FindMatchingGlobalFrames[world: WorldVM.LocalWorld[], pattern: "*", action: FindAction]};
TRUSTED {frameList ← LOOPHOLE[List.Sort[list: LOOPHOLE[frameList, LIST OF REF ANY], compareProc: compare], LIST OF ROPE]};
frameCache ← NEW[ARRAY CHARACTER['A..'Z] OF LIST OF ROPEALL[NIL]];
FOR l: LIST OF ROPE ← frameList, l.rest UNTIL l = NIL DO
thisChar: CHARACTER = Rope.Upper[Rope.Fetch[l.first, 0]];
IF thisChar # lastChar THEN
{lastChar ← thisChar;
frameCache[thisChar] ← l;
};
ENDLOOP;
};
UpdateFrameCache: PUBLIC ENTRY PROCEDURE = {
FindAction: BBContext.FindAction -- [gf: TV, name: ROPE] RETURNS [ActionContinuation ← continue] -- = {
IF name # NIL THEN
{IF Rope.Equal[name, previouslySeen] THEN RETURN[quit]; -- now entering that part of the enumeration already seen.
IF firstSeen = NIL THEN firstSeen ← name;
newFramesList ← CONS[name, newFramesList];
};
RETURN[continue];
};
newFramesList: LIST OF ROPE;
firstSeen: ROPENIL;
IF previouslySeen = NIL THEN RETURN; -- cache is not yet built, e.g. user runs a bcd from his profile.
TRUSTED {BBContext.FindMatchingGlobalFrames[world: WorldVM.LocalWorld[], pattern: "*", action: FindAction]};
FOR l: LIST OF ROPE ← newFramesList, l.rest UNTIL l = NIL DO
thisChar: CHARACTER = Rope.Upper[Rope.Fetch[l.first, 0]];
IF frameCache[thisChar] = NIL THEN frameCache[thisChar] ← LIST[l.first]
ELSE FOR l1: LIST OF ROPE ← frameCache[thisChar], l1.rest UNTIL l1 = NIL DO
IF Rope.Compare[l.first, l1.first] = less THEN
{l1.rest ← CONS[l1.first, l1.rest];
l1.first ← l.first;
EXIT;
};
ENDLOOP;
ENDLOOP;
IF firstSeen # NIL THEN previouslySeen ← firstSeen;
};
eval head
GetEvalHead: PUBLIC PROC [exec: UserExec.ExecHandle, expr: Expression, viewer: Viewer ← NIL] RETURNS [evalHead: BBEval.EvalHead] = {
evalHeadData: REF EvalHeadData;
MakeNewEvalHead: PROC RETURNS[BBEval.EvalHead] = {
RETURN[BBEval.NewEvalHead[
context: NIL,
data: NEW[EvalHeadData ← [
exec: exec,
viewer: IF viewer # NIL THEN viewer ELSE IF exec # NIL THEN exec.viewer ELSE NIL
]],
helpFatal: HelpFatal,
helpWrongType: HelpWrongType,
helpId: HelpId,
helpSelector: HelpSelector,
helpDefault: HelpDefault,
specials: SymTab.Create[],
abortProc: Abort
]]
};
private: REF ExecPrivateRecord;
IF exec # NIL THEN
{private ← exec.privateStuff;
IF (evalHead ← private.evalHead) = NIL THEN
{evalHead ← MakeNewEvalHead[];
private.evalHead ← evalHead;
};
}
ELSE IF viewer # NIL THEN
{exec: UserExec.ExecHandle = UserExec.GetExecHandle[viewer: viewer];
evalHead ← MakeNewEvalHead[]; -- might also consider saving (caching) evalHead on viewer's property list, in situation where evaluation did not come from exec (e.g. ^E expansion)
IF exec # NIL THEN
{private: REF ExecPrivateRecord = exec.privateStuff;
IF private.evalHead # NIL THEN evalHead.specials ← private.evalHead.specials; -- exec not specified, so don't pass it in for use with Abort and Confirm, but if viewer corresponds to an exec, use its context and evalHead, e.g. for ^E expansion in exec.
};
ViewerAbort.ResetUserAbort[viewer];
}
ELSE evalHead ← MakeNewEvalHead[];
evalHeadData ← NARROW[evalHead.data];
evalHeadData.expr ← expr;
};
Abort: BBEval.AbortProc -- PROC [data: REF] RETURNS [abort: BOOL] -- = {
evalHeadData: REF EvalHeadData = NARROW[data];
RETURN[
IF evalHeadData.exec # NIL AND UserExec.UserAbort[evalHeadData.exec] THEN TRUE
ELSE IF evalHeadData.viewer # NIL AND ViewerAbort.UserAbort[evalHeadData.viewer] THEN TRUE
ELSE FALSE];
};
miscellaneous
AttachDefaultArgVal, AttachTypeCorrectionProc are in BasicUserExecImpl
Currently the following defaultArgVals are defined:
targetType; PROC ANY, default: NIL
Currently the following type correction procs are defined for the indicated target types
ATOM, undefined id goes to corresponding atom, e.g. foo -> $foo
REF ANY, undefined id goes to corresponding atom, e.g. foo -> $foo,
something of type T goes to REF T
LONG STRING, object of type ROPE goes to correspoding long string
REF TEXT, object of type ROPE goes to corresponding REF TEXT
REF READONLY TEXT, object of type ROPE goes to corresponding REF READONLY TEXT
SupplyCorrectValue: PROC [exec: ExecHandle, targetType: Type, valueSupplied: TV] RETURNS[flag: BOOLEANFALSE, shouldBe: TVNIL] = {
OPEN AMTypes;
ref: REF ANY ← Atom.TypeGetProp[type: targetType, prop: $WrongTypeProc];
IF ref # NIL THEN
{proc: BasicUserExec.CorrectionProc ← (NARROW[ref, REF BasicUserExec.CorrectionProc])^;
[flag, shouldBe] ← proc[targetType: targetType, wrongValue: valueSupplied, exec: exec]
};
}; -- of SupplyCorrectValue
CorrectUndefinedId: PROC [exec: ExecHandle, targetType: Type, id: ROPE] RETURNS[flag: BOOLEANFALSE, shouldBe: TVNIL] = {
OPEN AMTypes;
ref: REF ANY ← Atom.TypeGetProp[type: targetType, prop: $WrongTypeProc];
IF ref # NIL THEN {
proc: BasicUserExec.CorrectionProc ← (NARROW[ref, REF BasicUserExec.CorrectionProc])^;
[flag, shouldBe] ← proc[targetType: targetType, undefinedId: id, exec: exec]
};
}; -- of CorrectUndefinedId
SupplyMissingArgument: PROC [argType: Type] RETURNS[flag: BOOLEAN, shouldBe: TV] = {
shouldBe ← Atom.TypeGetProp[type: argType, prop: $DefaultValue];
IF shouldBe # NIL THEN RETURN[TRUE, shouldBe];
BEGIN OPEN AMTypes;
underType: Class ← TypeClass[UnderType[argType]];
SELECT underType FROM
ref, list, procedure, unspecified, pointer => RETURN[TRUE, NIL]; -- for case of procedure, gets converted to a REF short NIL in CheckArgType
ENDCASE;
END;
RETURN[FALSE, NIL];
}; -- of SupplyMissingArgument
PrintOrdinal: PROC [handle: STREAM, n: NAT] = {
SELECT n FROM
1 => handle.Put[text["first"]];
2 => handle.Put[text["second"]];
3 => handle.Put[text["third"]];
4 => handle.Put[text["fourth"]];
ENDCASE => handle.Put[card[n], text["th"]];
}; -- of PrintOrdinal
Sum: PROC [list: LIST OF INT] RETURNS [sum: INT] = {
sum ← 0;
FOR l: LIST OF INT ← list, l.rest UNTIL l = NIL DO
sum ← sum + l.first;
ENDLOOP;
};
RefSum: PROC [list: LIST OF REF INT] RETURNS [sum: INT] = {
sum ← 0;
FOR l: LIST OF REF INT ← list, l.rest UNTIL l = NIL DO
sum ← sum + l.first^;
ENDLOOP;
};
TRUSTED {Process.Detach[FORK BuildFrameCache[]]}; -- to get it started, so don't have a long wait after everything else is up but before messagewindow says ready.
-- EndOps.Register[UpdateFrameCache];
END. -- of DwimImpl
Edited on March 25, 1983 5:32 pm, by Teitelman
UserExecInterpImpl no longer saves & variables in global table. Installed fix so that things like &A3 would work by looking up the exec, etc.
changes to: DIRECTORY, HelpId, untilNumber (local of HelpId), HelpId, untilNumber (local of HelpId)
Edited on April 8, 1983 1:35 pm, by Teitelman
changes to: HelpId, DIRECTORY
Edited on April 19, 1983 12:14 pm, by Teitelman
changes to: DIRECTORY, IMPORTS