Skip to content

Commit efd04e4

Browse files
committed
Nested functions & "is not" operator
1 parent 0187e9b commit efd04e4

File tree

12 files changed

+219
-140
lines changed

12 files changed

+219
-140
lines changed

README.md

Lines changed: 1 addition & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,18 +4,8 @@
44
It features a Pascal-inspired syntax, iwth features taken from a number of newer languages.
55

66
The aim is a programming language that will be a direct conduit from a developer's mind to the machine,
7-
not a maze of mandatory abstractions, unsafe patterns, or verbose boilerplate.
7+
not a maze of mandatory abstractions, or symbolic verbose boilerplate.
88

9-
The philosophy is simple:
10-
11-
- Pragmatic: Express inherits the strong safety guarantees of Pascal but sheds its verbosity.
12-
It offers the low-level, but unlike C-family languages it's cleaner, less symbol-heavy syntax.
13-
The result is a language that feels intuitive and modern, choosing proven concepts over novelty.
14-
- Clear by design: The syntax is minimal and consistent. There is no hidden "fallthrough" in switch statements and no mandatory semicolons. Type inference, lightweight record types, and universal extension methods reduce boilerplate and allow the code to clearly express the programmer's intent.
15-
- Write Fast, Refine for Safety. Express is designed to support a rapid development workflow. The principle is simplicity first; safety is a tool for refinement, not a barrier to entry.
16-
You can declare variables with type inference (var x := 10) to get your ideas working quickly. Later, you can add explicit types (var x: Int64 = 10) to make the code more robust.
17-
Similarly, method overrides are implicit by default, simplifying OOP. In the future, an optional `@override` attribute will allow you to ask the compiler to verify that you are correctly overriding a parent method, adding a layer of safety when you need it.
18-
This "progressive enhancement" approach is central to the language, a language that lets you write code quickly, and then helps you make it correct.
199

2010
---
2111

@@ -231,7 +221,6 @@ Enums and Sets: For more expressive and safe code.
231221

232222
- Operator Overloading: Allowing user-defined types to work with standard operators.
233223
- Properties: Class and record fields with custom getter/setter logic.
234-
- Nested Functions: To enable more powerful functional patterns.
235224
- Default function parameters with assign by name
236225
- Strings are currently limited to Ansistring.
237226

compiler/xpr.compilercontext.pas

Lines changed: 41 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,13 @@ TXprVar = record // might be best to make this a class, records are clumpsy he
6363
function InCurrentScope(ctx: TCompilerContext): Boolean;
6464
end;
6565

66+
TNamedVar = record
67+
Name: string;
68+
XprVar: TXprVar;
69+
end;
70+
6671
TXprVarList = specialize TArrayList<TXprVar>;
72+
TVarList = specialize TArrayList<TNamedVar>;
6773
TStringToObject = specialize TDictionary<string, XType>;
6874
TXprTypeList = specialize TArrayList<XType>;
6975

@@ -248,6 +254,8 @@ TCompilerContext = class(TObject)
248254
function EmitUpcastIfNeeded(VarToCast: TXprVar; TargetType: XType; DerefIfUpcast:Boolean): TXprVar;
249255
procedure VarToDefault(TargetVar: TXprVar);
250256
function GetManagedDeclarations(): TXprVarList;
257+
function GetClosureVariables(): TVarList;
258+
251259
function GenerateIntrinsics(Name: string; Arguments: array of XType; SelfType: XType; CompileAs: string = ''): XTree_Node;
252260
function ResolveMethod(Name: string; Arguments: XTypeArray; SelfType, RetType: XType; DocPos:TDocPos): TXprVar;
253261
procedure ResolveToFinalType(var PseudoType: XType);
@@ -560,7 +568,7 @@ procedure TCompilerContext.DecScope();
560568

561569
function TCompilerContext.GetStackPos(): SizeInt;
562570
begin
563-
Result := StackPosArr[Scope]{+SizeOf(Pointer)};
571+
Result := StackPosArr[Scope];
564572
end;
565573

566574
procedure TCompilerContext.IncStackPos(Size:Int32=STACK_ITEM_ALIGN);
@@ -697,6 +705,10 @@ function TCompilerContext.GetVar(Name: string; Pos:TDocPos): TXprVar;
697705
// Search scopes from the inside out (current scope -> parent -> ... -> global)
698706
for i := Self.Scope downto GLOBAL_SCOPE do
699707
begin
708+
// XXX: new design, only do local and global here
709+
if (i <> self.Scope) and (i <> GLOBAL_SCOPE) then
710+
continue;
711+
700712
// prefer local w/ namespace
701713
if CurrentNamespace <> '' then
702714
begin
@@ -984,7 +996,7 @@ function TCompilerContext.RegVar(Name: string; var Value: TXprVar; DocPos: TDocP
984996

985997
exists := self.VarDecl[varScope].Get(XprCase(PrefixedName), declList);
986998
if exists then
987-
begin
999+
begin //XXX: Make it all just Add - verify integrity of it
9881000
if (Value.VarType.BaseType in [xtMethod, xtExternalMethod]) then
9891001
declList.Add(Result)
9901002
else
@@ -1026,7 +1038,7 @@ function TCompilerContext.RegGlobalVar(Name: string; var Value: TXprVar; DocPos:
10261038

10271039
function TCompilerContext.RegMethod(Name: string; var Value: TXprVar; DocPos: TDocPos): Int32;
10281040
begin
1029-
Result := Self.RegVar(XprCase(Name), Value, DocPos, True);
1041+
Result := Self.RegVar(XprCase(Name), Value, DocPos, True);
10301042
end;
10311043

10321044

@@ -1381,6 +1393,27 @@ function TCompilerContext.GetManagedDeclarations(): TXprVarList;
13811393
end;
13821394
end;
13831395

1396+
function TCompilerContext.GetClosureVariables(): TVarList;
1397+
var
1398+
i,j,k: Int32;
1399+
xprVar: TNamedVar;
1400+
begin
1401+
Result.Init([]);
1402+
// looks crazy, but it's not that bad
1403+
1404+
with Self.VarDecl[scope] do
1405+
for i:=0 to RealSize-1 do
1406+
for j:=0 to High(Items[i]) do
1407+
begin
1408+
k := Items[i][j].val.High();
1409+
if k < 0 then continue;
1410+
1411+
xprVar.XprVar := Self.Variables.Data[Items[i][j].val.data[k]];
1412+
xprVar.Name := Items[i][j].key;
1413+
Result.Add(xprVar);
1414+
end;
1415+
end;
1416+
13841417

13851418

13861419
function GetConversionCost(FromType, ToType: XType): Integer;
@@ -1520,13 +1553,14 @@ function TCompilerContext.ResolveMethod(Name: string; Arguments: XTypeArray; Sel
15201553
Ambiguous := False;
15211554
for i := 0 to CandidateList.High do
15221555
begin
1556+
if name = 'recurse' then WriteLn(Name,': ',CandidateList.Data[i].NestingLevel);
1557+
15231558
CandidateVar := CandidateList.Data[i];
15241559
if not (CandidateVar.VarType is XType_Method) then Continue;
15251560
FType := XType_Method(CandidateVar.VarType);
15261561

15271562
if (SelfType <> nil) and not FType.TypeMethod then Continue;
1528-
if Length(FType.Params) <> Length(EffectiveArgs) then Continue;
1529-
1563+
if FType.RealParamcount <> Length(EffectiveArgs) then Continue;
15301564
TotalScore := (CandidateVar.NestingLevel * SCOPE_PENALTY);
15311565

15321566

@@ -1602,6 +1636,7 @@ function TCompilerContext.ResolveMethod(Name: string; Arguments: XTypeArray; Sel
16021636
CURRENT_SCOPE: Int32;
16031637
begin
16041638
Result := Resolve(Self.GetVarList(Name));
1639+
if name = 'recurse' then WriteLn(Name,': ',Result.NestingLevel);
16051640

16061641
if (Result = NullVar) then
16071642
begin
@@ -1855,6 +1890,7 @@ function XType.Equals(Other: XType): Boolean;
18551890

18561891
function XType.IsManagedType(ctx: TCompilerContext): Boolean;
18571892
begin
1893+
Assert(Self <> nil, 'It should be impossible');
18581894
Result := ((Self.BaseType in XprRefcountedTypes)) or
18591895
((Self is XType_Record) and ctx.IsManagedRecord(Self));
18601896
end;

compiler/xpr.interpreter.pas

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -804,16 +804,6 @@ procedure TInterpreter.Run(var BC: TBytecode);
804804
bcCOPY_GLOBAL:
805805
Pointer(Pointer(BasePtr + pc^.Args[0].Data.Addr)^) := Pointer(Global(pc^.Args[1].Data.Addr)^);
806806

807-
bcLOAD_NONLOCAL: //static link walk
808-
begin
809-
raise Exception.Create('NONLOCAL: NOT IMPLEMENTED');
810-
//ParentFramePtr := PPointer(BasePtr + SizeOf(Pointer))^; //statick link is stored here
811-
//for linkwalk := 1 to pc^.Args[3].Data.i32 - 1 do
812-
// ParentFramePtr := Pointer(Pointer(ParentFramePtr + SizeOf(Pointer))^);
813-
814-
//Pointer(Pointer(BasePtr + pc^.Args[0].Data.Addr)^) := Pointer(ParentFramePtr + pc^.Args[1].Data.Addr);
815-
end;
816-
817807
bcNEWFRAME:
818808
begin
819809
// This might save us from a lot of bullshit:

compiler/xpr.langdef.pas

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ TOperatorDef = record
2222
end;
2323

2424
const
25-
BinPrecedence: array [0..36] of TOperatorDef = (
25+
BinPrecedence: array [0..37] of TOperatorDef = (
2626
// Level 0: Assignments
2727
(OP:tkASGN; Prec:0; Assoc:-1),
2828
(OP:tkPLUS_ASGN; Prec:0; Assoc:-1),
@@ -47,6 +47,7 @@ TOperatorDef = record
4747
(OP:tkEQ; Prec:5; Assoc:1),
4848
(OP:tkNE; Prec:5; Assoc:1),
4949
(OP:tkKW_IS; Prec:5; Assoc:1),
50+
(OP:tkKW_ISNOT; Prec:5; Assoc:1),
5051

5152
// Level 6: Relational Operators
5253
(OP:tkLT; Prec:6; Assoc:1),

compiler/xpr.parser.pas

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1371,6 +1371,11 @@ function TParser.RHSExpr(Left:XTree_Node; leftPrecedence:Int8=0): XTree_Node;
13711371
Result := XTree_DynCast.Create(Left, Right, FContext, DocPos)
13721372
else if (OP = op_IS) and (Right is XTree_Identifier) then
13731373
Result := XTree_TypeIs.Create(Left, Right, FContext, DocPos)
1374+
else if (OP = op_ISNOT) and (Right is XTree_Identifier) then
1375+
begin
1376+
WriteLn('ISNOT');
1377+
Result := XTree_UnaryOp.Create(op_not, XTree_TypeIs.Create(Left, Right, FContext, DocPos), FContext, DocPos)
1378+
end
13741379
else
13751380
Result := XTree_BinaryOp.Create(op, Left, Right, FContext, DocPos);
13761381
end;

compiler/xpr.tokenizer.pas

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ interface
4040
tkKW_IN,
4141
tkKW_INHERITED,
4242
tkKW_IS,
43+
tkKW_ISNOT,
4344
tkKW_NEW,
4445
tkKW_NIL,
4546
tkKW_OF,
@@ -241,6 +242,7 @@ function isNoDocPos(Pos: TDocPos): Boolean; inline;
241242
'in',
242243
'inherited',
243244
'is',
245+
'is not',
244246
'new',
245247
'nil',
246248
'of',
@@ -400,7 +402,13 @@ procedure TTokenizer.AddIdent();
400402
while Current in ['a'..'z','A'..'Z','_','0'..'9'] do Inc(pos);
401403
tmp := Copy(data, i, pos-i);
402404
tok := KeywordMap.GetDef(XprCase(tmp), tkIdent);
403-
self.Append(tok, tmp);
405+
406+
if (FArrHigh > 0) and (tok = tkNOT) and (self.Tokens[FArrHigh-1].Token = tkKW_IS) then
407+
begin
408+
self.Tokens[FArrHigh-1].Token := tkKW_ISNOT;
409+
self.Tokens[FArrHigh-1].Value := 'is not';
410+
end else
411+
self.Append(tok, tmp);
404412
end;
405413

406414

0 commit comments

Comments
 (0)