A NesC alkalmazás
Egy nesC alkalmazás három részből áll:
- C deklarációkból és definíciókból
- Interfész típusok egy halmazából
- Komponensek halmazából
A nesC alkalmazások névadási környezete a következőképpen épül fel:
- Egy legkülső, globális hatáskör, amely három névteret tartalmaz:
- Egy névteret, amely a C-s változókat tartalmazza
- Egy névteret a C-s definíciók és deklarációk részére
- Egy névteret, amely a nesC interfészeket és komponenseket tartalmazza
- A C deklarációk és definíciók ugyancsak be tudnak vezetni saját hatókört a globális hatókörön belül (pl. függvény definíciók)
- Minden interfész bevezet egy hatókört, amely a parancsokat és az eseményeket tartalmazza. Ez a hatókör a globális hatókörbe van helyezve, ezért a parancsok és események el tudják érni a C típusokat és változókat.
- Minden komponens két újabb hatókört vezet be:
- A specifikációs hatókör a globális hatókörbe van helyezve. Ez tartalmaz egy változó-névteret, amely a komponens specifikációs elemeit tartalmazza.
- Az implementációs hatókör a specifikációs hatókörbe van helyezve. Ez két névteret tartalmaz:
- Egy változó-névteret (variable namespace): ez tartalmazza azokat a neveket, amelyekkel a komponens a beszerkesztett (include) komponensekre hivatkozik.
- Egy tag-névteret (ezt nem tudtam lefordítani rendesen, angolul: tag-namespace)
Moduloknál az implementációs hatókör tartalmazza a taszkokat, a C deklarációkat és definíciókat.
Ezek a deklarációk bevezethetnek saját hatókört az implementációs hatókörbe.
Ez a struktúra lehetővé teszi, hogy a modulok elérhessék a globális deklarációkat, de ne tekinthessenek bele a többi komponensbe.
Egy nesC program fordításakor csak a legkülső komponenst kell megadnunk (nevezzük ezt K-nak).
A többit a fordító magától ki tud találni. Ezt a nesC programok szerkezetének köszönhetjük.
A fordító a főkomponenst (K-t) ellenőrizve igény szerint haladva be tudja tölteni az egész programot.
A nesC fordító először egy tos C fájlt tölt be, majd pedig a K komponenst.
Az alkalmazás kódja az a kód lesz, amely ennek a két dolog betöltése által indított betöltési folyamat által betöltődtek.
A nesC fordító feltételezi, minden olyan függvényhíváshoz, amely nincs ellátva spontaneous attribútummal, létezik hívás a kódban (azaz nincs „láthatatlan” hívás a nem-spontán függvényekre).
Az X C fájl beolvasása
Ha az X már betöltődött, akkor semmi sem történik.
Különben az X.h fájlt megkeresi a fordító és elő feldolgozza.
A C makrók (#define és #undef) által kiváltott változások láthatóak lesznek az összes többi elő feldolgozott fájlban.
A fájlban található deklarációk és definíciók a globális hatókörbe kerülnek,
ezért láthatóak lesznek az összes többi előfeldolgozott C fájl számára, valamint az interfészek és a komponensek számára.
A K komponens beolvasása
Ha K már betöltődött, akkor semmi sem történik. Különben elő feldolgozódik a K.nc.
A C makrók (#define és #undef) által kiváltott változások nem játszanak szerepet.
Az elő feldolgozott fájl többek között a következő nyelvtani szabályok szerint fordítódik:
nesC-file:
includes-listopt interface
includes-listopt module
includes-listopt configuration
includes-list:
includes
includes-list includes
includes:
includes identifier-list ;
Ha az X.nc fájl nem tartalmaz K modult vagy konfigurációt, akkor fordítási idejű hiba keletkezik.
Egyébként a következő lépések hajtódnak végre:
- Minden C fájl , ami fel van sorolva az includes-list -ben betöltődik.
- Minden a specifikációs részben felsorolt interfész típus betöltődik.
- Feldolgozódik a specifikációs rész. Ha K egy konfiguráció, akkor minden itt deklarált specifikációs komponens betöltődik.
- Feldolgozódik a K implementációs része.
Az I interfész típus betöltése
Ha az I már be van töltve, akkor nem történik semmi. Különben előfeldolgozódik az X.nc fájl.
A C makrók (#define és #undef) által kiváltott változások nem játszanak szerepet.
Ha az X.nc fájl nem tartalmazza az I interfészt, akkor egy fordítási idejű hiba keletkezik.
Egyébként:
• Az összes fájl betöltődik az includes-list –ből.
• Feldolgozódik az interfész definíciója
Példa:
Bar.nc:
includes BarTypes;
interface Bar {
command result_t bar(BarType arg1);
}
BarTypes.h:
typedef struct {
int x;
double y;
} BarType;
Ebben a példában a Bar interfész használja a BarType típust, ezért először a BarTypes.h fog betöltődni,
ezt követi a Bar interfész betöltése, majd csak ezután következnek a Bar interfészt használó komponensek betöltése.