c2nim: rewritten symbol exporting code
This commit is contained in:
parent
e6bc065cc5
commit
704663738a
4 changed files with 44 additions and 90 deletions
|
|
@ -13,7 +13,7 @@
|
||||||
|
|
||||||
# XXX standalone structs and unions!
|
# XXX standalone structs and unions!
|
||||||
# XXX header pragma for struct and union fields!
|
# XXX header pragma for struct and union fields!
|
||||||
# XXX rewrite symbol export handling!
|
# XXX "cast" bug!
|
||||||
|
|
||||||
import
|
import
|
||||||
os, llstream, rnimsyn, clex, idents, strutils, pegs, ast, astalgo, msgs,
|
os, llstream, rnimsyn, clex, idents, strutils, pegs, ast, astalgo, msgs,
|
||||||
|
|
@ -74,8 +74,6 @@ proc ParseUnit*(p: var TParser): PNode
|
||||||
proc openParser*(p: var TParser, filename: string, inputStream: PLLStream,
|
proc openParser*(p: var TParser, filename: string, inputStream: PLLStream,
|
||||||
options = newParserOptions())
|
options = newParserOptions())
|
||||||
proc closeParser*(p: var TParser)
|
proc closeParser*(p: var TParser)
|
||||||
proc exSymbol*(n: var PNode)
|
|
||||||
proc fixRecordDef*(n: var PNode)
|
|
||||||
|
|
||||||
# implementation
|
# implementation
|
||||||
|
|
||||||
|
|
@ -134,8 +132,8 @@ proc parseMacroArguments(p: var TParser): seq[seq[ref TToken]] =
|
||||||
result[L].add(p.tok)
|
result[L].add(p.tok)
|
||||||
of pxParRi:
|
of pxParRi:
|
||||||
# end of arguments?
|
# end of arguments?
|
||||||
if i[pxParLe] == 0: break
|
if i[pxParLe] == 0 and i[pxBracketLe] == 0 and i[pxCurlyLe] == 0: break
|
||||||
dec(i[pxParLe])
|
if i[pxParLe] > 0: dec(i[pxParLe])
|
||||||
result[L].add(p.tok)
|
result[L].add(p.tok)
|
||||||
of pxBracketRi, pxCurlyRi:
|
of pxBracketRi, pxCurlyRi:
|
||||||
kind = pred(kind, 3)
|
kind = pred(kind, 3)
|
||||||
|
|
@ -144,7 +142,6 @@ proc parseMacroArguments(p: var TParser): seq[seq[ref TToken]] =
|
||||||
of pxComma:
|
of pxComma:
|
||||||
if i[pxParLe] == 0 and i[pxBracketLe] == 0 and i[pxCurlyLe] == 0:
|
if i[pxParLe] == 0 and i[pxBracketLe] == 0 and i[pxCurlyLe] == 0:
|
||||||
# next argument: comma is not part of the argument
|
# next argument: comma is not part of the argument
|
||||||
rawGetTok(p)
|
|
||||||
result.add(@[])
|
result.add(@[])
|
||||||
inc(L)
|
inc(L)
|
||||||
else:
|
else:
|
||||||
|
|
@ -298,8 +295,16 @@ proc addImportToPragma(pragmas: PNode, ident: string, p: TParser) =
|
||||||
else:
|
else:
|
||||||
addSon(pragmas, newIdentStrLitPair("header", p.options.header, p))
|
addSon(pragmas, newIdentStrLitPair("header", p.options.header, p))
|
||||||
|
|
||||||
proc mangledIdentAndImport(ident: string, p: TParser): PNode =
|
proc exportSym(p: TParser, i: PNode): PNode =
|
||||||
result = mangledIdent(ident, p)
|
assert i.kind == nkIdent
|
||||||
|
if p.scopeCounter == 0:
|
||||||
|
result = newNodeI(nkPostfix, i.info)
|
||||||
|
addSon(result, newIdentNode(getIdent("*"), i.info), i)
|
||||||
|
else:
|
||||||
|
result = i
|
||||||
|
|
||||||
|
proc varIdent(ident: string, p: TParser): PNode =
|
||||||
|
result = exportSym(p, mangledIdent(ident, p))
|
||||||
if p.scopeCounter > 0: return
|
if p.scopeCounter > 0: return
|
||||||
if p.options.dynlibSym.len > 0 or p.options.header.len > 0:
|
if p.options.dynlibSym.len > 0 or p.options.header.len > 0:
|
||||||
var a = result
|
var a = result
|
||||||
|
|
@ -319,43 +324,15 @@ proc newBinary(opr: string, a, b: PNode, p: TParser): PNode =
|
||||||
addSon(result, a)
|
addSon(result, a)
|
||||||
addSon(result, b)
|
addSon(result, b)
|
||||||
|
|
||||||
# --------------- symbol exporter --------------------------------------------
|
proc skipIdent(p: var TParser): PNode =
|
||||||
|
expectIdent(p)
|
||||||
|
result = mangledIdent(p.tok.s, p)
|
||||||
|
getTok(p, result)
|
||||||
|
|
||||||
proc identVis(p: var TParser): PNode =
|
proc skipIdentExport(p: var TParser): PNode =
|
||||||
# identifier with visability
|
expectIdent(p)
|
||||||
var a = mangledIdent(p.tok.s, p)
|
result = exportSym(p, mangledIdent(p.tok.s, p))
|
||||||
result = newNodeP(nkPostfix, p)
|
getTok(p, result)
|
||||||
addSon(result, newIdentNodeP("*", p))
|
|
||||||
addSon(result, a)
|
|
||||||
getTok(p)
|
|
||||||
|
|
||||||
proc exSymbol(n: var PNode) =
|
|
||||||
case n.kind
|
|
||||||
of nkPostfix:
|
|
||||||
nil
|
|
||||||
of nkPragmaExpr:
|
|
||||||
exSymbol(n.sons[0])
|
|
||||||
of nkIdent, nkAccQuoted:
|
|
||||||
var a = newNodeI(nkPostFix, n.info)
|
|
||||||
addSon(a, newIdentNode(getIdent("*"), n.info))
|
|
||||||
addSon(a, n)
|
|
||||||
n = a
|
|
||||||
else: internalError(n.info, "exSymbol(): " & $n.kind)
|
|
||||||
|
|
||||||
proc fixRecordDef(n: var PNode) =
|
|
||||||
if n == nil: return
|
|
||||||
case n.kind
|
|
||||||
of nkRecCase:
|
|
||||||
fixRecordDef(n.sons[0])
|
|
||||||
for i in countup(1, sonsLen(n) - 1):
|
|
||||||
var length = sonsLen(n.sons[i])
|
|
||||||
fixRecordDef(n.sons[i].sons[length - 1])
|
|
||||||
of nkRecList, nkRecWhen, nkElse, nkOfBranch, nkElifBranch, nkObjectTy:
|
|
||||||
for i in countup(0, sonsLen(n) - 1): fixRecordDef(n.sons[i])
|
|
||||||
of nkIdentDefs:
|
|
||||||
for i in countup(0, sonsLen(n) - 3): exSymbol(n.sons[i])
|
|
||||||
of nkNilLit: nil
|
|
||||||
else: internalError(n.info, "fixRecordDef(): " & $n.kind)
|
|
||||||
|
|
||||||
proc addPragmaToIdent(ident: var PNode, pragma: PNode) =
|
proc addPragmaToIdent(ident: var PNode, pragma: PNode) =
|
||||||
var pragmasNode: PNode
|
var pragmasNode: PNode
|
||||||
|
|
@ -371,25 +348,6 @@ proc addPragmaToIdent(ident: var PNode, pragma: PNode) =
|
||||||
InternalError(ident.info, "addPragmaToIdent")
|
InternalError(ident.info, "addPragmaToIdent")
|
||||||
addSon(pragmasNode, pragma)
|
addSon(pragmasNode, pragma)
|
||||||
|
|
||||||
proc exSymbols(n: PNode) =
|
|
||||||
if n == nil: return
|
|
||||||
case n.kind
|
|
||||||
of nkEmpty..nkNilLit: nil
|
|
||||||
of nkProcDef..nkIteratorDef: exSymbol(n.sons[namePos])
|
|
||||||
of nkWhenStmt:
|
|
||||||
for i in countup(0, sonsLen(n) - 1): exSymbols(lastSon(n.sons[i]))
|
|
||||||
of nkStmtList:
|
|
||||||
for i in countup(0, sonsLen(n) - 1): exSymbols(n.sons[i])
|
|
||||||
of nkVarSection, nkConstSection:
|
|
||||||
for i in countup(0, sonsLen(n) - 1): exSymbol(n.sons[i].sons[0])
|
|
||||||
of nkTypeSection:
|
|
||||||
for i in countup(0, sonsLen(n) - 1):
|
|
||||||
exSymbol(n.sons[i].sons[0])
|
|
||||||
if (n.sons[i].sons[2] != nil) and
|
|
||||||
(n.sons[i].sons[2].kind == nkObjectTy):
|
|
||||||
fixRecordDef(n.sons[i].sons[2])
|
|
||||||
else: nil
|
|
||||||
|
|
||||||
# --------------- parser -----------------------------------------------------
|
# --------------- parser -----------------------------------------------------
|
||||||
# We use this parsing rule: If it looks like a declaration, it is one. This
|
# We use this parsing rule: If it looks like a declaration, it is one. This
|
||||||
# avoids to build a symbol table, which can't be done reliably anyway for our
|
# avoids to build a symbol table, which can't be done reliably anyway for our
|
||||||
|
|
@ -418,11 +376,6 @@ proc stmtKeyword(s: string): bool =
|
||||||
|
|
||||||
# ------------------- type desc -----------------------------------------------
|
# ------------------- type desc -----------------------------------------------
|
||||||
|
|
||||||
proc skipIdent(p: var TParser): PNode =
|
|
||||||
expectIdent(p)
|
|
||||||
result = mangledIdent(p.tok.s, p)
|
|
||||||
getTok(p, result)
|
|
||||||
|
|
||||||
proc isIntType(s: string): bool =
|
proc isIntType(s: string): bool =
|
||||||
case s
|
case s
|
||||||
of "short", "int", "long", "float", "double", "signed", "unsigned":
|
of "short", "int", "long", "float", "double", "signed", "unsigned":
|
||||||
|
|
@ -519,7 +472,7 @@ proc parseStructBody(p: var TParser): PNode =
|
||||||
while true:
|
while true:
|
||||||
var def = newNodeP(nkIdentDefs, p)
|
var def = newNodeP(nkIdentDefs, p)
|
||||||
var t = pointer(p, baseTyp)
|
var t = pointer(p, baseTyp)
|
||||||
var i = skipIdent(p)
|
var i = skipIdentExport(p)
|
||||||
t = parseTypeSuffix(p, t)
|
t = parseTypeSuffix(p, t)
|
||||||
addSon(def, i, t, nil)
|
addSon(def, i, t, nil)
|
||||||
addSon(result, def)
|
addSon(result, def)
|
||||||
|
|
@ -615,7 +568,7 @@ proc parseFunctionPointerDecl(p: var TParser, rettyp: PNode): PNode =
|
||||||
parseCallConv(p, pragmas)
|
parseCallConv(p, pragmas)
|
||||||
if p.tok.xkind == pxStar: getTok(p, params)
|
if p.tok.xkind == pxStar: getTok(p, params)
|
||||||
else: parMessage(p, errTokenExpected, "*")
|
else: parMessage(p, errTokenExpected, "*")
|
||||||
var name = skipIdent(p)
|
var name = skipIdentExport(p)
|
||||||
eat(p, pxParRi, name)
|
eat(p, pxParRi, name)
|
||||||
parseFormalParams(p, params, pragmas)
|
parseFormalParams(p, params, pragmas)
|
||||||
addSon(procType, params)
|
addSon(procType, params)
|
||||||
|
|
@ -651,11 +604,11 @@ proc otherTypeDef(p: var TParser, section, typ: PNode) =
|
||||||
of pxStar:
|
of pxStar:
|
||||||
# typedef typ *b;
|
# typedef typ *b;
|
||||||
t = pointer(p, typ)
|
t = pointer(p, typ)
|
||||||
name = skipIdent(p)
|
name = skipIdentExport(p)
|
||||||
else:
|
else:
|
||||||
# typedef typ name;
|
# typedef typ name;
|
||||||
t = typ
|
t = typ
|
||||||
name = skipIdent(p)
|
name = skipIdentExport(p)
|
||||||
t = parseTypeSuffix(p, t)
|
t = parseTypeSuffix(p, t)
|
||||||
addTypeDef(section, name, t)
|
addTypeDef(section, name, t)
|
||||||
|
|
||||||
|
|
@ -663,7 +616,7 @@ proc parseTrailingDefinedTypes(p: var TParser, section, typ: PNode) =
|
||||||
while p.tok.xkind == pxComma:
|
while p.tok.xkind == pxComma:
|
||||||
getTok(p, nil)
|
getTok(p, nil)
|
||||||
var newTyp = pointer(p, typ)
|
var newTyp = pointer(p, typ)
|
||||||
var newName = skipIdent(p)
|
var newName = skipIdentExport(p)
|
||||||
newTyp = parseTypeSuffix(p, newTyp)
|
newTyp = parseTypeSuffix(p, newTyp)
|
||||||
addTypeDef(section, newName, newTyp)
|
addTypeDef(section, newName, newTyp)
|
||||||
|
|
||||||
|
|
@ -697,7 +650,7 @@ proc parseTypeDef(p: var TParser): PNode =
|
||||||
if p.tok.xkind == pxCurlyLe:
|
if p.tok.xkind == pxCurlyLe:
|
||||||
var t = parseStruct(p)
|
var t = parseStruct(p)
|
||||||
var name = skipIdent(p)
|
var name = skipIdent(p)
|
||||||
addTypeDef(result, structPragmas(p, name), t)
|
addTypeDef(result, structPragmas(p, exportSym(p, name)), t)
|
||||||
parseTrailingDefinedTypes(p, result, name)
|
parseTrailingDefinedTypes(p, result, name)
|
||||||
elif p.tok.xkind == pxSymbol:
|
elif p.tok.xkind == pxSymbol:
|
||||||
# name to be defined or type "struct a", we don't know yet:
|
# name to be defined or type "struct a", we don't know yet:
|
||||||
|
|
@ -709,10 +662,10 @@ proc parseTypeDef(p: var TParser): PNode =
|
||||||
# typedef struct tagABC {} abc, *pabc;
|
# typedef struct tagABC {} abc, *pabc;
|
||||||
# --> abc is a better type name than tagABC!
|
# --> abc is a better type name than tagABC!
|
||||||
var name = skipIdent(p)
|
var name = skipIdent(p)
|
||||||
addTypeDef(result, structPragmas(p, name), t)
|
addTypeDef(result, structPragmas(p, exportSym(p, name)), t)
|
||||||
parseTrailingDefinedTypes(p, result, name)
|
parseTrailingDefinedTypes(p, result, name)
|
||||||
else:
|
else:
|
||||||
addTypeDef(result, structPragmas(p, nameOrType), t)
|
addTypeDef(result, structPragmas(p, exportSym(p, nameOrType)), t)
|
||||||
of pxSymbol:
|
of pxSymbol:
|
||||||
# typedef struct a a?
|
# typedef struct a a?
|
||||||
if mangleName(p.tok.s, p) == nameOrType.ident.s:
|
if mangleName(p.tok.s, p) == nameOrType.ident.s:
|
||||||
|
|
@ -732,7 +685,7 @@ proc parseTypeDef(p: var TParser): PNode =
|
||||||
var t = enumFields(p)
|
var t = enumFields(p)
|
||||||
eat(p, pxCurlyRi, t)
|
eat(p, pxCurlyRi, t)
|
||||||
var name = skipIdent(p)
|
var name = skipIdent(p)
|
||||||
addTypeDef(result, enumPragmas(p, name), t)
|
addTypeDef(result, enumPragmas(p, exportSym(p, name)), t)
|
||||||
parseTrailingDefinedTypes(p, result, name)
|
parseTrailingDefinedTypes(p, result, name)
|
||||||
elif p.tok.xkind == pxSymbol:
|
elif p.tok.xkind == pxSymbol:
|
||||||
# name to be defined or type "enum a", we don't know yet:
|
# name to be defined or type "enum a", we don't know yet:
|
||||||
|
|
@ -746,10 +699,10 @@ proc parseTypeDef(p: var TParser): PNode =
|
||||||
# typedef enum tagABC {} abc, *pabc;
|
# typedef enum tagABC {} abc, *pabc;
|
||||||
# --> abc is a better type name than tagABC!
|
# --> abc is a better type name than tagABC!
|
||||||
var name = skipIdent(p)
|
var name = skipIdent(p)
|
||||||
addTypeDef(result, enumPragmas(p, name), t)
|
addTypeDef(result, enumPragmas(p, exportSym(p, name)), t)
|
||||||
parseTrailingDefinedTypes(p, result, name)
|
parseTrailingDefinedTypes(p, result, name)
|
||||||
else:
|
else:
|
||||||
addTypeDef(result, enumPragmas(p, nameOrType), t)
|
addTypeDef(result, enumPragmas(p, exportSym(p, nameOrType)), t)
|
||||||
of pxSymbol:
|
of pxSymbol:
|
||||||
# typedef enum a a?
|
# typedef enum a a?
|
||||||
if mangleName(p.tok.s, p) == nameOrType.ident.s:
|
if mangleName(p.tok.s, p) == nameOrType.ident.s:
|
||||||
|
|
@ -798,7 +751,7 @@ proc parseVarDecl(p: var TParser, baseTyp, typ: PNode,
|
||||||
origName: string): PNode =
|
origName: string): PNode =
|
||||||
result = newNodeP(nkVarSection, p)
|
result = newNodeP(nkVarSection, p)
|
||||||
var def = newNodeP(nkIdentDefs, p)
|
var def = newNodeP(nkIdentDefs, p)
|
||||||
addSon(def, mangledIdentAndImport(origName, p))
|
addSon(def, varIdent(origName, p))
|
||||||
addSon(def, parseTypeSuffix(p, typ))
|
addSon(def, parseTypeSuffix(p, typ))
|
||||||
addInitializer(p, def)
|
addInitializer(p, def)
|
||||||
addSon(result, def)
|
addSon(result, def)
|
||||||
|
|
@ -808,7 +761,7 @@ proc parseVarDecl(p: var TParser, baseTyp, typ: PNode,
|
||||||
var t = pointer(p, baseTyp)
|
var t = pointer(p, baseTyp)
|
||||||
expectIdent(p)
|
expectIdent(p)
|
||||||
def = newNodeP(nkIdentDefs, p)
|
def = newNodeP(nkIdentDefs, p)
|
||||||
addSon(def, mangledIdentAndImport(p.tok.s, p))
|
addSon(def, varIdent(p.tok.s, p))
|
||||||
getTok(p, def)
|
getTok(p, def)
|
||||||
addSon(def, parseTypeSuffix(p, t))
|
addSon(def, parseTypeSuffix(p, t))
|
||||||
addInitializer(p, def)
|
addInitializer(p, def)
|
||||||
|
|
@ -851,7 +804,7 @@ proc declaration(p: var TParser): PNode =
|
||||||
addSon(pragmas, newIdentNodeP("cdecl", p))
|
addSon(pragmas, newIdentNodeP("cdecl", p))
|
||||||
elif pfStdcall in p.options.flags:
|
elif pfStdcall in p.options.flags:
|
||||||
addSon(pragmas, newIdentNodeP("stdcall", p))
|
addSon(pragmas, newIdentNodeP("stdcall", p))
|
||||||
addSon(result, name)
|
addSon(result, exportSym(p, name))
|
||||||
addSon(result, nil) # no generics
|
addSon(result, nil) # no generics
|
||||||
addSon(result, params)
|
addSon(result, params)
|
||||||
addSon(result, pragmas)
|
addSon(result, pragmas)
|
||||||
|
|
@ -883,7 +836,7 @@ proc enumSpecifier(p: var TParser): PNode =
|
||||||
getTok(p, result)
|
getTok(p, result)
|
||||||
var i = 0
|
var i = 0
|
||||||
while true:
|
while true:
|
||||||
var name = skipIdent(p)
|
var name = skipIdentExport(p)
|
||||||
var val: PNode
|
var val: PNode
|
||||||
if p.tok.xkind == pxAsgn:
|
if p.tok.xkind == pxAsgn:
|
||||||
getTok(p, name)
|
getTok(p, name)
|
||||||
|
|
@ -908,7 +861,7 @@ proc enumSpecifier(p: var TParser): PNode =
|
||||||
var t = newNodeP(nkTypeDef, p)
|
var t = newNodeP(nkTypeDef, p)
|
||||||
getTok(p, t)
|
getTok(p, t)
|
||||||
var e = enumFields(p)
|
var e = enumFields(p)
|
||||||
addSon(t, name, nil, e) # nil for generic params
|
addSon(t, exportSym(p, name), nil, e) # nil for generic params
|
||||||
addSon(result, t)
|
addSon(result, t)
|
||||||
else:
|
else:
|
||||||
parMessage(p, errTokenExpected, "{")
|
parMessage(p, errTokenExpected, "{")
|
||||||
|
|
@ -1537,5 +1490,4 @@ proc parseUnit(p: var TParser): PNode =
|
||||||
while p.tok.xkind != pxEof:
|
while p.tok.xkind != pxEof:
|
||||||
var s = statement(p)
|
var s = statement(p)
|
||||||
if s != nil: embedStmts(result, s)
|
if s != nil: embedStmts(result, s)
|
||||||
exSymbols(result)
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -40,7 +40,7 @@ proc parseDefine(p: var TParser): PNode =
|
||||||
# a macro with parameters:
|
# a macro with parameters:
|
||||||
result = newNodeP(nkTemplateDef, p)
|
result = newNodeP(nkTemplateDef, p)
|
||||||
getTok(p)
|
getTok(p)
|
||||||
addSon(result, skipIdent(p))
|
addSon(result, skipIdentExport(p))
|
||||||
eat(p, pxParLe)
|
eat(p, pxParLe)
|
||||||
var params = newNodeP(nkFormalParams, p)
|
var params = newNodeP(nkFormalParams, p)
|
||||||
# return type; not known yet:
|
# return type; not known yet:
|
||||||
|
|
@ -68,7 +68,7 @@ proc parseDefine(p: var TParser): PNode =
|
||||||
while p.tok.xkind == pxDirective and p.tok.s == "define":
|
while p.tok.xkind == pxDirective and p.tok.s == "define":
|
||||||
getTok(p) # skip #define
|
getTok(p) # skip #define
|
||||||
var c = newNodeP(nkConstDef, p)
|
var c = newNodeP(nkConstDef, p)
|
||||||
addSon(c, skipIdent(p))
|
addSon(c, skipIdentExport(p))
|
||||||
addSon(c, nil)
|
addSon(c, nil)
|
||||||
skipStarCom(p, c)
|
skipStarCom(p, c)
|
||||||
if p.tok.xkind in {pxLineComment, pxNewLine, pxEof}:
|
if p.tok.xkind in {pxLineComment, pxNewLine, pxEof}:
|
||||||
|
|
|
||||||
|
|
@ -234,8 +234,6 @@ Limitations
|
||||||
|
|
||||||
* C's ``,`` operator (comma operator) is not supported.
|
* C's ``,`` operator (comma operator) is not supported.
|
||||||
* C's ``union`` has no equivalent in Nimrod.
|
* C's ``union`` has no equivalent in Nimrod.
|
||||||
* Standalone ``struct x {}`` declarations are not implemented. Put them into
|
|
||||||
a ``typedef``.
|
|
||||||
* The condition in a ``do while(condition)`` statement must be ``0``.
|
* The condition in a ``do while(condition)`` statement must be ``0``.
|
||||||
* Lots of other small issues...
|
* Lots of other small issues...
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -130,6 +130,7 @@ int IupConvertXYToPos(PIhandle ih, int x, int y);
|
||||||
|
|
||||||
#ifdef C2NIM
|
#ifdef C2NIM
|
||||||
# def EXTERN(x) static x
|
# def EXTERN(x) static x
|
||||||
|
# def TWO_ARGS(x, y) x* y
|
||||||
#endif
|
#endif
|
||||||
// parses now!
|
// parses now!
|
||||||
EXTERN(int) f(void);
|
EXTERN(int) f(void);
|
||||||
|
|
@ -141,6 +142,9 @@ int IupConvertXYToPos(PIhandle ih, int x, int y);
|
||||||
EXPORT int f(void);
|
EXPORT int f(void);
|
||||||
EXPORT int g(void);
|
EXPORT int g(void);
|
||||||
|
|
||||||
|
static TWO_ARGS(int, x) = TWO_ARGS(56, 45);
|
||||||
|
|
||||||
|
|
||||||
# define abc 34
|
# define abc 34
|
||||||
# define xyz 42
|
# define xyz 42
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue