aboutsummaryrefslogtreecommitdiff
path: root/plugin/syntaxes/cie-pseudocode.tmLanguage.json
blob: cf1957f06391ab7b3e7fcd1143ecd82b275bc5ee (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126

{
  "name": "CIE Pseudocode",
  "scopeName": "source.cie-pseudocode",
  "patterns": [
    {
      "include": "#line_comment"
    },
    {
      "include": "#strings"
    },
    {
      "match": "(?<!\\w)(?:IF|THEN|ELSE|ENDIF|WHILE|DO|ENDWHILE|REPEAT|UNTIL|FOR|TO|STEP|NEXT|RETURN|CALL)(?!\\w)",
      "name": "keyword.control.cie-pseudocode"
    },
    {
      "match": "(?<!\\w)(?:DECLARE|TYPE|ENDTYPE|FUNCTION|ENDFUNCTION|PROCEDURE|ENDPROCEDURE|RETURNS|BYREF|BYVAL|OF)(?!\\w)",
      "name": "storage.modifier.cie-pseudocode"
    },
    {
      "match": "(?<!\\w)(?:INTEGER|REAL|BOOLEAN|STRING|CHAR|ARRAY)(?!\\w)",
      "name": "storage.type.cie-pseudocode"
    },
    {
      "match": "(?<!\\w)(?:TRUE|FALSE)(?!\\w)",
      "name": "constant.language.cie-pseudocode"
    },
    {
      "match": "(?<!\\w)(?:INPUT|OUTPUT|OPENFILE|READFILE|WRITEFILE|CLOSEFILE|READ|WRITE|APPEND)(?!\\w)",
      "name": "keyword.other.cie-pseudocode"
    },
    {
      "match": "(?<!\\w)(?:LENGTH|MID|RIGHT|LEFT|LCASE|UCASE|ASC|CHR|IS_NUM|NUM_TO_STR|STR_TO_NUM|EOF)(?!\\w)(?=\\s*\\()",
      "name": "entity.name.function.cie-pseudocode"
    },
    {
      "match": "(?<!\\w)(?:DIV|MOD|AND|OR|NOT)(?!\\w)",
      "name": "keyword.operator.word.cie-pseudocode"
    },
    {
      "match": "<-",
      "name": "keyword.operator.assignment.cpp"
    },
    {
      "match": "<>|<=|>=|[+*/=&^<>-]",
      "name": "keyword.operator.cie-pseudocode"
    },
    {
      "match": "(?<!\\w)\\d+(?:\\.\\d*)?(?!\\w)",
      "name": "constant.numeric.cie-pseudocode"
    },
    {
      "match": "\\b[A-Za-z][A-Za-z0-9_]*(?=\\s*\\()",
      "name": "entity.name.function.cie-pseudocode"
    },
    {
      "match": "[()]",
      "name": "punctuation.section.parens.cie-pseudocode"
    },
    {
      "match": "[\\[\\]]",
      "name": "punctuation.section.brackets.cie-pseudocode"
    },
    {
      "match": ",",
      "name": "punctuation.separator.delimiter.comma.cie-pseudocode"
    },
    {
      "match": "[:.]",
      "name": "punctuation.separator.cie-pseudocode"
    }
  ],
  "repository": {
    "line_comment": {
      "begin": "(//)",
      "end": "$",
      "beginCaptures": {
        "1": {
          "name": "punctuation.definition.comment.cie-pseudocode"
        }
      },
      "endCaptures": {},
      "name": "comment.line.double-slash.cie-pseudocode",
      "patterns": []
    },
    "strings": {
      "patterns": [
        {
          "match": "'''",
          "name": "string.quoted.single.cie-pseudocode"
        },
        {
          "begin": "\"",
          "end": "\"|$",
          "beginCaptures": {
            "0": {
              "name": "punctuation.definition.string.begin.cie-pseudocode"
            }
          },
          "endCaptures": {
            "0": {
              "name": "punctuation.definition.string.end.cie-pseudocode"
            }
          },
          "name": "string.quoted.double.cie-pseudocode",
          "patterns": []
        },
        {
          "begin": "'",
          "end": "'|$",
          "beginCaptures": {
            "0": {
              "name": "punctuation.definition.string.begin.cie-pseudocode"
            }
          },
          "endCaptures": {
            "0": {
              "name": "punctuation.definition.string.end.cie-pseudocode"
            }
          },
          "name": "string.quoted.single.cie-pseudocode",
          "patterns": []
        }
      ]
    }
  }
}
Value *Source = Builder->CreateStructGEP(Type->LLVMType, Address, Field.Index); Value *Value = containsString(FieldType) ? copyClassValue(FieldType, Source, Fields) : Builder->CreateLoad(FieldType->LLVMType, Source); if (!Value) return nullptr; Result = Builder->CreateInsertValue(Result, Value, {Field.Index}); } auto *Storage = materializeOperatorReceiver(Result, Type); transferObjects(Fields); if (typeNeedsDestruction(Type->Name)) Transfers.push_back(StackObjects.size() - 1); return Builder->CreateLoad(Type->LLVMType, Storage); } if (!checkOperatorAccess(*Copy)) return nullptr; auto *Storage = CreateEntryBlockAlloca(Builder->GetInsertBlock()->getParent(), Type->LLVMType, "copy_object"); size_t Lifetime = registerStackObject(Storage, Type->Name, DiagnosticLine); if (Lifetime != size_t(-1)) StackObjects[Lifetime].Temporary = true; Builder->CreateStore(Types->getZeroValue(Type->Name), Storage); if (!emitFieldDefaults(Type, Storage, DiagnosticLine)) return nullptr; if (!FuncGen->emitCall(*Copy, {Storage, Address})) return nullptr; activateStackObject(Lifetime); Transfers.push_back(Lifetime); return Builder->CreateLoad(Type->LLVMType, Storage, "copied_value"); } void CodeGen::cleanupTemporaries(size_t Start) { if (!Builder->GetInsertBlock() || Builder->GetInsertBlock()->hasTerminator()) return; // Destructor emission can itself register temporaries. Copy each record before emitting. for (size_t I = StackObjects.size(); I > Start; --I) { auto Object = StackObjects[I - 1]; if (Object.Temporary) emitStackObjectCleanup(Object); } } bool CodeGen::typeNeedsConstruction(const std::string &TypeName) const { if (findMethod(TypeName, "NEW")) return true; const TypeInfo *Info = resolveType(TypeName); if (Info && Info->asRecord()) { for (const auto &[Name, Field] : Info->Record.Fields) if (typeNeedsConstruction(Field.TypeName)) return true; } return false; } bool CodeGen::emitConstruct(const std::string &TypeName, Value *Addr, const std::vector<std::unique_ptr<ExprAST>> &Args, int Line) { SaveAndRestore<int> Location(DiagnosticLine, Line); const TypeInfo *Info = resolveType(TypeName); if (!Info) return false; if (Info->asRecord() && !emitFieldDefaults(Info, Addr, Line)) return false; if (findMethod(TypeName, "NEW")) return emitMethodCall(TypeName, Addr, "NEW", Args, Line) != nullptr; if (!Args.empty()) { reportError("Type %s has no constructor NEW but arguments were supplied", TypeName.c_str()); return false; } return true; } bool CodeGen::emitDefaultConstruct(const std::string &TypeName, Value *Addr, int Line) { return emitConstruct(TypeName, Addr, {}, Line); } // Default-constructs, in declaration order, each field of the record at Addr // that needs construction. bool CodeGen::emitFieldDefaults(const TypeInfo *Type, Value *Addr, int Line) { for (const auto &[Name, Field] : Type->Record.Fields) { if (!typeNeedsConstruction(Field.TypeName)) continue; Value *FieldAddr = Builder->CreateStructGEP(Type->LLVMType, Addr, Field.Index); if (!emitDefaultConstruct(Field.TypeName, FieldAddr, Line)) return false; } return true; } Value *CodeGen::symbolAddress(const SymbolInfo &Info) { if (!Info.IsReference) return Info.Storage; return Builder->CreateLoad(PointerType::getUnqual(*TheContext), Info.Storage, "object_address"); } void CodeGen::emitAssignment(const LValueInfo &Target, ExprAST *Expr, const SymbolInfo *Symbol) { const TypeInfo *Info = resolveType(Target.TypeName); if (auto *New = dynamic_cast<NewExprAST*>(Expr)) { SaveAndRestore<int> Location(DiagnosticLine, New->getLine()); const TypeInfo *Class = resolveType(New->getClassName()); if (!Class || !Class->IsClass) { reportError("NEW requires a class type: %s", New->getClassName().c_str()); return; } if (!Info || !Info->sameType(Class)) { reportError("NEW target must have type %s", Class->Name.c_str()); return; } Value *Size = Builder->CreateIntCast(Types->getSizeOfConstant(Class->LLVMType), Type::getIntNTy(*TheContext, sizeof(std::size_t) * CHAR_BIT), false); // Method-only classes have zero-sized LLVM structs but still need // a non-null object address (MALLOC(0) deliberately returns null). Size = Builder->CreateSelect(Builder->CreateICmpEQ(Size, ConstantInt::get(Size->getType(), 0)), ConstantInt::get(Size->getType(), 1), Size); Value *Object = Memory->emitAllocate(Size, New->getLine()); Builder->CreateStore(Types->getZeroValue(Class->Name), Object); if (!emitConstruct(Class->Name, Object, New->getArgs(), New->getLine())) return; if (Symbol && Symbol->IsReference) { Builder->CreateStore(Object, Symbol->Storage); } else { Value *Val = Builder->CreateLoad(Class->LLVMType, Object); Builder->CreateStore(Val, Target.Addr); } return; } if (Info && Info->IsClass) { auto Resolution = resolveOperator(tok_assign, Info, getExprTypeInfo(Expr)); if (Resolution.Ambiguous) { reportError("Ambiguous assignment operator"); return; } if (const FuncSig *Sig = Resolution.Signature) { if (!checkOperatorAccess(*Sig)) return; std::vector<Value *> Args = {Target.Addr}; if (marshalCallArgs(*Sig, std::vector<ExprAST *>{Expr}, DiagnosticLine, Args)) FuncGen->emitCall(*Sig, Args); return; } } if (Info && containsString(Info)) { Value *Val = emitCoercedExpr(Expr, Info); if (!Val) return; std::vector<size_t> Transfers; bool CopySource = isObjectLValue(Expr); if (!CopySource) { materializeOperatorReceiver(Val, Info); if (typeNeedsDestruction(Info->Name)) Transfers.push_back(StackObjects.size() - 1); } // Default aggregate assignment replaces fields without reconstructing // the enclosing class or calling its DELETE method. std::function<Value *(const TypeInfo *, Value *)> Clone = [&](const TypeInfo *Type, Value *Value) -> llvm::Value * { if (Type->isString()) { auto *Source = CreateEntryBlockAlloca(Builder->GetInsertBlock()->getParent(), Type->LLVMType, "assignment_source"); Builder->CreateStore(Value, Source); return copyClassValue(Type, Source, Transfers); } for (const auto &[Name, Field] : Type->Record.Fields) { const auto *FieldType = resolveType(Field.TypeName); if (!containsString(FieldType)) continue; auto *Copy = Clone(FieldType, Builder->CreateExtractValue(Value, {Field.Index})); if (!Copy) return nullptr; Value = Builder->CreateInsertValue(Value, Copy, {Field.Index}); } return Value; }; if (CopySource) Val = Clone(Info, Val); if (!Val) return; std::function<bool(const TypeInfo *, Value *)> Release = [&](const TypeInfo *Type, Value *Address) { if (Type->isString()) return emitDestroy(Type->Name, Address, DiagnosticLine); for (const auto &[Name, Field] : Type->Record.Fields) { const auto *FieldType = resolveType(Field.TypeName); if (containsString(FieldType) && !Release(FieldType, Builder->CreateStructGEP(Type->LLVMType, Address, Field.Index))) return false; } return true; }; if (!Release(Info, Target.Addr)) return; Builder->CreateStore(Val, Target.Addr); transferObjects(Transfers); return; } Value *Val = emitCoercedExpr(Expr, Info); if (Val) Builder->CreateStore(Val, Target.Addr); } bool CodeGen::typeNeedsDestruction(const std::string &TypeName) const { if (findMethod(TypeName, "DELETE")) return true; const TypeInfo *Info = resolveType(TypeName); if (Info && Info->asRecord()) { for (const auto &[Name, Field] : Info->Record.Fields) if (typeNeedsDestruction(Field.TypeName)) return true; } return false; } bool CodeGen::emitDestroy(const std::string &TypeName, Value *Addr, int Line) { SaveAndRestore<int> Location(DiagnosticLine, Line); const TypeInfo *Info = resolveType(TypeName); if (!Info) return false; if (findMethod(TypeName, "DELETE") && !emitMethodCall(TypeName, Addr, "DELETE", {}, Line)) return false; if (Info->asRecord()) { for (auto It = Info->Record.Fields.rbegin(); It != Info->Record.Fields.rend(); ++It) { const auto &Field = It->second; if (!typeNeedsDestruction(Field.TypeName)) continue; Value *FieldAddr = Builder->CreateStructGEP(Info->LLVMType, Addr, Field.Index); if (!emitDestroy(Field.TypeName, FieldAddr, Line)) return false; } } return true; } size_t CodeGen::registerStackObject(Value *Addr, const std::string &TypeName, int Line, bool IsArray) { if (!typeNeedsDestruction(TypeName)) return size_t(-1); auto *F = Builder->GetInsertBlock()->getParent(); IRBuilder<> Entry(&F->getEntryBlock(), F->getEntryBlock().begin()); auto *Alive = Entry.CreateAlloca(Entry.getInt1Ty(), nullptr, "object_alive"); Entry.CreateStore(Entry.getFalse(), Alive); AllocaInst *Count = nullptr; if (IsArray) { Count = Entry.CreateAlloca(Entry.getInt64Ty(), nullptr, "object_count"); Entry.CreateStore(Entry.getInt64(0), Count); } StackObjects.push_back({Addr, TypeName, Alive, Count, Line}); // This DECLARE can execute repeatedly, including after a backward loop edge. emitStackObjectCleanup(StackObjects.back()); return StackObjects.size() - 1; } void CodeGen::activateStackObject(size_t Index, Value *Count) { if (Index == size_t(-1)) return; const auto &Object = StackObjects[Index]; if (Object.Count) Builder->CreateStore(Count, Object.Count); Builder->CreateStore(Builder->getTrue(), Object.Alive); } void CodeGen::emitStackObjectCleanup(const StackObject &Object) { auto *F = Builder->GetInsertBlock()->getParent(); auto *Destroy = BasicBlock::Create(*TheContext, "destroy_object", F); auto *Done = BasicBlock::Create(*TheContext, "destroyed_object", F); Builder->CreateCondBr(Builder->CreateLoad(Builder->getInt1Ty(), Object.Alive), Destroy, Done); Builder->SetInsertPoint(Destroy); // Clear before the user method: a throwing destructor must never run twice. Builder->CreateStore(Builder->getFalse(), Object.Alive); if (!Object.Count) { emitDestroy(Object.TypeName, Object.Addr, Object.Line); } else { Value *Base = Builder->CreateLoad(PointerType::getUnqual(*TheContext), Object.Addr); Value *Count = Builder->CreateLoad(Builder->getInt64Ty(), Object.Count); auto *Preheader = Builder->GetInsertBlock(); auto *Loop = BasicBlock::Create(*TheContext, "destroy_elements", F); auto *Body = BasicBlock::Create(*TheContext, "destroy_element", F); Builder->CreateBr(Loop); Builder->SetInsertPoint(Loop); auto *Index = Builder->CreatePHI(Builder->getInt64Ty(), 2); Index->addIncoming(Count, Preheader); Builder->CreateCondBr(Builder->CreateICmpNE(Index, Builder->getInt64(0)), Body, Done); Builder->SetInsertPoint(Body); Value *Previous = Builder->CreateSub(Index, Builder->getInt64(1)); Value *Element = Builder->CreateGEP(resolveType(Object.TypeName)->LLVMType, Base, Previous); emitDestroy(Object.TypeName, Element, Object.Line); Index->addIncoming(Previous, Builder->GetInsertBlock()); Builder->CreateBr(Loop); Builder->SetInsertPoint(Done); return; } Builder->CreateBr(Done); Builder->SetInsertPoint(Done); } void CodeGen::finishStackObjects(Function *F) { Exceptions->fillCleanups(F, [this](bool TemporariesOnly) { for (auto It = StackObjects.rbegin(); It != StackObjects.rend(); ++It) if (!TemporariesOnly || It->Temporary) emitStackObjectCleanup(*It); }); }