1 // Written in the D programming language.
2 /**
3  * Copyright: Copyright (c) 2015, Joakim Brännström. All rights reserved.
4  * Author: Joakim Brännström (joakim.brannstrom@gmx.com)
5  *
6  * Only kept utility functionality and comments of the original implementation.
7  * The rest is synchronized with Token.d in DStep.
8  *
9  * Copyright: Copyright (c) 2016 Wojciech Szęszoł. All rights reserved.
10  * Authors: Wojciech Szęszoł
11  * Version: Initial created: Feb 14, 2016
12  * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0)
13  */
14 module clang.Token;
15 
16 import std.conv : to;
17 import std.typecons;
18 
19 import clang.c.Index;
20 
21 import clang.Cursor;
22 import clang.SourceLocation;
23 import clang.SourceRange;
24 import clang.TranslationUnit;
25 import clang.Util;
26 import clang.Visitor;
27 
28 @property auto toString(Token tok) {
29     import std.format : format;
30 
31     return format("%s [%s %s]", tok.spelling, tok.kind, to!string(tok.cursor.cx),);
32 }
33 
34 @property auto toString(ref TokenRange toks) {
35     import std..string;
36 
37     string s;
38 
39     foreach (t; toks) {
40         s ~= t.spelling ~ " ";
41     }
42 
43     return s.strip;
44 }
45 
46 /** Represents a single token from the preprocessor.
47  *
48  * Tokens are effectively segments of source code. Source code is first parsed
49  * into tokens before being converted into the AST and Cursors.
50  *
51  * Tokens are obtained from parsed TranslationUnit instances. You currently
52  * can't create tokens manually.
53  */
54 struct Token {
55     private struct Container {
56         CXTranslationUnit translationUnit;
57         CXToken* tokens;
58         ulong numTokens;
59 
60         ~this() {
61             if (tokens != null) {
62                 clang_disposeTokens(translationUnit, tokens, to!uint(numTokens));
63             }
64         }
65     }
66 
67     private const RefCounted!Container container;
68     size_t index;
69 
70     @property private Container* containerPtr() return const {
71         return cast(Container*)&(container.refCountedPayload());
72     }
73 
74     @property static Cursor empty() {
75         //TODO why is this function needed, remove?
76         auto r = clang_getNullCursor();
77         return Cursor(r);
78     }
79 
80     /// Obtain the TokenKind of the current token.
81     @property CXTokenKind kind() const {
82         return clang_getTokenKind(containerPtr.tokens[index]);
83     }
84 
85     /** The spelling of this token.
86      *
87      * This is the textual representation of the token in source.
88      */
89     @property string spelling() const {
90         auto r = clang_getTokenSpelling(containerPtr.translationUnit, containerPtr.tokens[index]);
91         return toD(r);
92     }
93 
94     /// The SourceLocation this Token occurs at.
95     @property SourceLocation location() const {
96         auto r = clang_getTokenLocation(containerPtr.translationUnit, containerPtr.tokens[index]);
97         return SourceLocation(r);
98     }
99 
100     /// The SourceRange this Token occupies.
101     @property SourceRange extent() const {
102         auto r = clang_getTokenExtent(containerPtr.translationUnit, containerPtr.tokens[index]);
103         return SourceRange(r);
104     }
105 
106     /// The Cursor this Token corresponds to.
107     @property Cursor cursor() {
108         Cursor c = empty;
109         clang_annotateTokens(containerPtr.translationUnit, &containerPtr.tokens[index], 1, &c.cx);
110 
111         return c;
112     }
113 }
114 
115 struct TokenRange {
116     private const RefCounted!(Token.Container) container;
117     private size_t begin;
118     private size_t end;
119 
120     private static RefCounted!(Token.Container) makeContainer(
121             CXTranslationUnit translationUnit, CXToken* tokens, ulong numTokens) {
122         RefCounted!(Token.Container) result;
123         result.translationUnit = translationUnit;
124         result.tokens = tokens;
125         result.numTokens = numTokens;
126         return result;
127     }
128 
129     private this(const RefCounted!(Token.Container) container, size_t begin, size_t end) {
130         this.container = container;
131         this.begin = begin;
132         this.end = end;
133     }
134 
135     this(CXTranslationUnit translationUnit, CXToken* tokens, ulong numTokens) {
136         container = makeContainer(translationUnit, tokens, numTokens);
137         begin = 0;
138         end = numTokens;
139     }
140 
141     @property bool empty() const {
142         return begin >= end;
143     }
144 
145     @property Token front() const {
146         return Token(container, begin);
147     }
148 
149     @property Token back() const {
150         return Token(container, end - 1);
151     }
152 
153     @property void popFront() {
154         ++begin;
155     }
156 
157     @property void popBack() {
158         --end;
159     }
160 
161     @property TokenRange save() const {
162         return this;
163     }
164 
165     @property size_t length() const {
166         return end - begin;
167     }
168 
169     Token opIndex(size_t index) const {
170         return Token(container, begin + index);
171     }
172 
173     TokenRange opSlice(size_t begin, size_t end) const {
174         return TokenRange(container, this.begin + begin, this.begin + end);
175     }
176 
177     size_t opDollar() const {
178         return length;
179     }
180 }