Fastnat i tankarna
Fastnat i tankarna
Jag har ju en funktion som gör att jag programledes i en µC kan "känna igen" ord och förstå kommandon i klar text.
Det fungerar skitbra - förutom idag då jag krånglade med det och mina uträkningar sket sig.
Såhär gör jag: (litet exempel)
const BYTE Comm_Text_Get[] = "Get";
const BYTE Comm_Text_Set[] = "Set";
const BYTE Comm_Text_Time[] = "Time";
const BYTE Comm_Text_Date[] = "Date";
Detta är de ord som kommandon kan styckas ihop av.
Sedan ska jag kunde indexera i dessa så att rutinen kan scanna igenom kommandosträngen och jämföra med dessa kommandoord. Alltså måste jag göra en tabell och för att få den till att bli en förutsägbar storlek blir den en tabell med BYTE*:
const BYTE* Comm_Texts[] = {Comm_Text_Get, Comm_Text_Set, Comm_Text_Time, Comm_Text_Date};
Jag behöver veta storleken på tabellen:
#define Comm_Texts_Size (sizeof(Comm_Texts) / sizeof(BYTE*))
Och nu kommer kruxet som ställer till det i mina tankar. Kommandon som ska kännas igen ska ju definieras. Och då kommer problemet:
enum {Index_Get, Index_Set, Index_Time, Index_Date};
#define Command_Get_Time ((Index_Get * Comm_Texts_Size) + Index_Time)
#define Command_Set_Time ((Index_Set * Comm_Texts_Size) + Index_Time)
#define Command_Get_Date ((Index_Get * Comm_Texts_Size) + Index_Date)
#define Command_Set_Date ((Index_Set * Comm_Texts_Size) + Index_Date)
Min rutin som känner igen kommandoorden tar dom steg för steg och räknar på samma sätt vilket betyder att när jag har de värden som motsvarar giltiga kommandon kommer en switch() att ta hand om dom.
Men problemet som kan uppstå när man börjar få lite olika ord är att enum{Index_???} och Comm_Texts[] kommer i otakt och då passar väldigt få värden.
Så är det någon som har en bra idé om hur jag kan "bygga upp" Index_??? utan att ha en separat tabell som ska vara synkroniserat med en enum-listan ville jag bli glad.
Denna gången löste jag det vid att ta data i Comm_Texts[] och kopiera det in i enum{} och göra en snabb search-&-replace - men detta sätt är ju FEL! (Läs "En man som heter Ove" för att förstå).
Det fungerar skitbra - förutom idag då jag krånglade med det och mina uträkningar sket sig.
Såhär gör jag: (litet exempel)
const BYTE Comm_Text_Get[] = "Get";
const BYTE Comm_Text_Set[] = "Set";
const BYTE Comm_Text_Time[] = "Time";
const BYTE Comm_Text_Date[] = "Date";
Detta är de ord som kommandon kan styckas ihop av.
Sedan ska jag kunde indexera i dessa så att rutinen kan scanna igenom kommandosträngen och jämföra med dessa kommandoord. Alltså måste jag göra en tabell och för att få den till att bli en förutsägbar storlek blir den en tabell med BYTE*:
const BYTE* Comm_Texts[] = {Comm_Text_Get, Comm_Text_Set, Comm_Text_Time, Comm_Text_Date};
Jag behöver veta storleken på tabellen:
#define Comm_Texts_Size (sizeof(Comm_Texts) / sizeof(BYTE*))
Och nu kommer kruxet som ställer till det i mina tankar. Kommandon som ska kännas igen ska ju definieras. Och då kommer problemet:
enum {Index_Get, Index_Set, Index_Time, Index_Date};
#define Command_Get_Time ((Index_Get * Comm_Texts_Size) + Index_Time)
#define Command_Set_Time ((Index_Set * Comm_Texts_Size) + Index_Time)
#define Command_Get_Date ((Index_Get * Comm_Texts_Size) + Index_Date)
#define Command_Set_Date ((Index_Set * Comm_Texts_Size) + Index_Date)
Min rutin som känner igen kommandoorden tar dom steg för steg och räknar på samma sätt vilket betyder att när jag har de värden som motsvarar giltiga kommandon kommer en switch() att ta hand om dom.
Men problemet som kan uppstå när man börjar få lite olika ord är att enum{Index_???} och Comm_Texts[] kommer i otakt och då passar väldigt få värden.
Så är det någon som har en bra idé om hur jag kan "bygga upp" Index_??? utan att ha en separat tabell som ska vara synkroniserat med en enum-listan ville jag bli glad.
Denna gången löste jag det vid att ta data i Comm_Texts[] och kopiera det in i enum{} och göra en snabb search-&-replace - men detta sätt är ju FEL! (Läs "En man som heter Ove" för att förstå).
Re: Fastnat i tankarna
Så här skulle jag göra:
Reserverar mig för syntaxfel;
Kod: Markera allt
struct {
const BYTE *s;
const BYTE tag;
} kom_struct;
kom_struct tabell[] = {
"Get", 1,
"Set", 2,
};
Re: Fastnat i tankarna
baron3d: Definitivt ett sätt - men om man manuellt klantar sig med fortlöpande numrering är man ju lika klippt för det. Men jag kan hålla med om att det är enklare att överskåda!
- Jan Almqvist
- Inlägg: 1581
- Blev medlem: 1 oktober 2013, 20:48:26
- Ort: Orust
Re: Fastnat i tankarna
Jag skulle föreslå en riktig lexikalanalysator som tolkar dina "Get", "Set" och övriga indata plus en enklare parser.
Lätt att göra och mer generellt.
Lätt att göra och mer generellt.
Re: Fastnat i tankarna
Jan Almqvist: Nu är jag osäker på vad du menar med "lexikalanalysator" men jag HAR en rutin som hittar kommandon ord för ord så att jag kan stycka ihop kommandon som jag vill. I detta fall har jag 29 olika ord som jag kombinerar till 52 olika kommandosträngar (och det blir nog fler eftersom).
En googling ger att en "lexikalanalysator" bara kontrollerar att texten uppfyller en viss struktur, typ XML eller liknande och jag ser faktisk inte vad det skulle ge av funktion. Kommandon ger jag via ett terminalprogram och tangentbordet och att börja med en struktur där är bara att glömma.
Om jag skriver "set intensity low xxx" (där xxx är ett värde) dekoder min rutin "Set", "Intensity" och "Low". När den kombination uppnår rätt värde exekveras <case Command_Set_Intensity_Low:> och värdet "xxx" kollas för tillåtna värden innan det används.
Och det fungerar perfekt - fram till det blir osynkroniserat mellan index och faktisk räkföljd.
Så det svåra är att se till att autonumreringen passar med verkligheten.
En googling ger att en "lexikalanalysator" bara kontrollerar att texten uppfyller en viss struktur, typ XML eller liknande och jag ser faktisk inte vad det skulle ge av funktion. Kommandon ger jag via ett terminalprogram och tangentbordet och att börja med en struktur där är bara att glömma.
Om jag skriver "set intensity low xxx" (där xxx är ett värde) dekoder min rutin "Set", "Intensity" och "Low". När den kombination uppnår rätt värde exekveras <case Command_Set_Intensity_Low:> och värdet "xxx" kollas för tillåtna värden innan det används.
Och det fungerar perfekt - fram till det blir osynkroniserat mellan index och faktisk räkföljd.
Så det svåra är att se till att autonumreringen passar med verkligheten.
- Jan Almqvist
- Inlägg: 1581
- Blev medlem: 1 oktober 2013, 20:48:26
- Ort: Orust
Re: Fastnat i tankarna
om du bygger upp det med en lexikalanalysator och en parser så kommer lexikalanalysatorn att returnera olika "lexeme" eller "token" när den anropas från parsern, lämpligen ett lexeme eller token i taget.
för dina 29 ord behövs lika många lexemes/tokens samt ett lexeme/token som betyder numeriskt värde. kanske också ett lexeme eller token för radslut. ditt lexeme/token för värde måste innehålla värdet också.
för dina 29 ord behövs lika många lexemes/tokens samt ett lexeme/token som betyder numeriskt värde. kanske också ett lexeme eller token för radslut. ditt lexeme/token för värde måste innehålla värdet också.
Re: Fastnat i tankarna
Jag hade nog byggt ett länkat träd. Fyll trädet med alla giltiga kombinationer. Sen när du ska parsea är det bara att vandra ner i trädet, är det en giltig kombination så hittar du ett löv. Det kan peka på den funktion/case som ska köras för det kommandot.
Re: Fastnat i tankarna
Kolla in MAP-makrot, https://github.com/swansontec/map-macro
Med det skulle du kunna skriva nåt sånthär:
Och, kompilerat med GCC -E får jag följande preprocessade .c-fil:
/johan
Med det skulle du kunna skriva nåt sånthär:
Kod: Markera allt
#include "map.h"
#define STRING(x) #x,
#define INDEX(x) Index_##x,
#define COMMANDS(...) \
enum { MAP(INDEX, __VA_ARGS__) }; \
const char* Comm_Texts[]={ MAP(STRING, __VA_ARGS__) };
COMMANDS(Get,Set,Date,Time,Interval,Poll,Status,Machine,Effort)
int main(int argc, char**argv)
{
return 0;
}
Kod: Markera allt
enum { Index_Get, Index_Set, Index_Date, Index_Time, Index_Interval, Index_Poll, Index_Status, Index_Machine, Index_Effort, };
const char* Comm_Texts[]={ "Get", "Set", "Date", "Time", "Interval", "Poll", "Status", "Machine", "Effort", };
int main(int argc, char**argv)
{
return 0;
}
Re: Fastnat i tankarna
En liknande variant som jag använder för flerspråksstöd:
Kod: Markera allt
#define TEXT_SKVENS \
TEXTSELECT( TEXT_PA, "på" , "on")\
TEXTSELECT( TEXT_AV, "av" , "off")\
TEXTSELECT( TEXT_AUTO, "auto", "auto")\
#undef TEXTSELECT
#define TEXTSELECT(symbol, svstr, enstr, ...) #svstr,
const BYTE *texter_se_arr[] = { TEXT_SKVENS };
#undef TEXTSELECT
#define TEXTSELECT(symbol, svstr, enstr, ...) #enstr,
const BYTE *texter_en_arr[] = { TEXT_SKVENS };
Re: Fastnat i tankarna
Och ytterligare en variant på baron3d's variant
/johan
Kod: Markera allt
#define STRING(x) #x,
#define INDEX(x) Index_##x,
#define ITEMS(f) \
f(Get) \
f(Set) \
f(Date) \
f(Time) \
enum { ITEMS(INDEX) };
const char* Comm_Texts[]={ ITEMS(STRING) };
Re: Fastnat i tankarna
johano: sedär - det var något att gå vidare med! Den ska jag suga lite på när jag får tid.
Det verkar vara en allmän uppfattning att det svåra är att dekoda vilka kommandoord som finns - men det är totalt avklarat redan! Att prata om kommandosyntax, parser osv är att missa målet!
Det finns ett par sätt man kan göra detta på. Om ett kommando kan bestå av ett ord är det bara att ha en index så att resultatet av funktioner i grunden är: Det finns en tabell över orden och är det ord #3 ska funktion #3 anropas.
Men om det finns fler ord i ett kommando (t.ex. "set intensity", "get intensity") kan man välja att antingen ha en tabell över indexen. Alltså:
Ord #1 har index X.
Ord #2 har index Y.
osv.
Sedan kan man sålla ner med t.ex. switch() efter switch() - eller göra som jag gör: Räkna ihop till en enda variabel som man sedan kör igenom en switch(). Jag anser att mitt sätt är enklast att läsa i koden sedan utan en massa kommentarer i koden.
Det verkar vara en allmän uppfattning att det svåra är att dekoda vilka kommandoord som finns - men det är totalt avklarat redan! Att prata om kommandosyntax, parser osv är att missa målet!
Det finns ett par sätt man kan göra detta på. Om ett kommando kan bestå av ett ord är det bara att ha en index så att resultatet av funktioner i grunden är: Det finns en tabell över orden och är det ord #3 ska funktion #3 anropas.
Men om det finns fler ord i ett kommando (t.ex. "set intensity", "get intensity") kan man välja att antingen ha en tabell över indexen. Alltså:
Ord #1 har index X.
Ord #2 har index Y.
osv.
Sedan kan man sålla ner med t.ex. switch() efter switch() - eller göra som jag gör: Räkna ihop till en enda variabel som man sedan kör igenom en switch(). Jag anser att mitt sätt är enklast att läsa i koden sedan utan en massa kommentarer i koden.
Re: Fastnat i tankarna
Exakt, men bygger du ett länkat träd istället för tabell så behöver du inte hålla koll på nån tabell själv som kan spåra ur. Bara mata in i trädet med en add-funktion. Alla giltiga kombinationer läggs in i init av programmet.
Add(Set intensity, funcX)
Add(Get intensity) funcY)
Sen en get function som letar i trädet och returnerar/kör functionen den hittar.
Add(Set intensity, funcX)
Add(Get intensity) funcY)
Sen en get function som letar i trädet och returnerar/kör functionen den hittar.
- Jan Almqvist
- Inlägg: 1581
- Blev medlem: 1 oktober 2013, 20:48:26
- Ort: Orust
Re: Fastnat i tankarna
hur ska du ge dina ord index så att summan blir "rätt"?
29 ord kan kombineras på ett oändligt antal sätt.
( 29 + 29 * 29 + 29 * 29 * 29 + 29 * 29 * 29 * 29 etc )
även vi begränsar oss till vad som kan tänkas rymmas i en inmatningsbuffert t.ex. 80 eller 256 tecken så blir det miljarders miljarder olika kombinationer.
29 ord kan kombineras på ett oändligt antal sätt.
( 29 + 29 * 29 + 29 * 29 * 29 + 29 * 29 * 29 * 29 etc )
även vi begränsar oss till vad som kan tänkas rymmas i en inmatningsbuffert t.ex. 80 eller 256 tecken så blir det miljarders miljarder olika kombinationer.
Re: Fastnat i tankarna
Orden har ett index i en tabell. I detta fall går de mellan 0 och 28.
Om ett giltigt kommando består av index 3 följd av index 6 blir uträkningen:
(3 * 29) + 6 = 93.
Så om jag "fångar" värdet 93 kan jag fastställa att de två ord har kommit i följd.
Självklart betyder det att om jag har två olika kommandon där det ena är på två ord och det nästa är på tre ord och de två första ord är de samma i båda fall blir det fel, då fångar rutinen de första två ord. Men det kan jag överleva.
Jag har funderat på att kunde "gå tillbaka" så att den finner alla 3 ord i ovanstående exempel och först när de 3 ord är hittat kan rutinen avgöra att kommandot rent faktisk är på 3 ord.
Om ett giltigt kommando består av index 3 följd av index 6 blir uträkningen:
(3 * 29) + 6 = 93.
Så om jag "fångar" värdet 93 kan jag fastställa att de två ord har kommit i följd.
Självklart betyder det att om jag har två olika kommandon där det ena är på två ord och det nästa är på tre ord och de två första ord är de samma i båda fall blir det fel, då fångar rutinen de första två ord. Men det kan jag överleva.
Jag har funderat på att kunde "gå tillbaka" så att den finner alla 3 ord i ovanstående exempel och först när de 3 ord är hittat kan rutinen avgöra att kommandot rent faktisk är på 3 ord.
Re: Fastnat i tankarna
För skojs skull så gjorde jag en högst hypotetisk implementation i Python bara för att labba lite kvickt. Tog ca 30 minuter och helt utan tester *fy skäms*.
Försökt att hålla mig till c-liknande struktur om man nu skull vilja porta.
Jag har bara täckt lite generik i denna implementation men tanken/idéen jag hade var att det man ska behöva deklarera är endast följande:
Som listar vilka kommandon som ska tolkas.
som listar vilka funktioner som ska kallas samt hur många parametrar som behövs som indata
som listar vilka sekvenser av tolkade kommandon som betyder något och vilken action som den mappas mot.
dvs. (0,3) ==> GET DATE, mappas till action 1 ==> (getdate, 0) som följdaktligen kallar på funktionen getdate utan parametrar.
jag har kapslat datat som funktionerna opererar på i en "struct" ... men går lika bra med globala variabler etc...
Som sagt, endast för skojs skull en koncept-lösning. Kanske kul för någon att leka med.
Försökt att hålla mig till c-liknande struktur om man nu skull vilja porta.
Jag har bara täckt lite generik i denna implementation men tanken/idéen jag hade var att det man ska behöva deklarera är endast följande:
Kod: Markera allt
commands = ["GET", "SET", "TIME", "DATE"]
Kod: Markera allt
actions = [(gettime, 0), (getdate, 0), (settime, 1), (setdate, 1), (setdatetime, 2), (settimedate, 2)]
Kod: Markera allt
commandsequences = [(0, 2), (0, 3), (1, 2), (1, 3), (1, 3, 2), (1, 2, 3)]
commandactions = [0,1,2,3,4,5]
dvs. (0,3) ==> GET DATE, mappas till action 1 ==> (getdate, 0) som följdaktligen kallar på funktionen getdate utan parametrar.
jag har kapslat datat som funktionerna opererar på i en "struct" ... men går lika bra med globala variabler etc...
Som sagt, endast för skojs skull en koncept-lösning. Kanske kul för någon att leka med.
Kod: Markera allt
class operator:
def __init__(self):
self.time = 0
self.date = 0
def gettime(obj):
print("** GET TIME: ", obj.time)
return obj.time
def getdate(obj):
print("** GET DATE: ", obj.date)
return obj.date
def settime(obj, time):
print("** SET TIME: ", obj.time, "to", time)
obj.time = time
def setdate(obj, date):
print("** SET DATE: ", obj.date, "to", date)
obj.date = date
def setdatetime(obj, date, time):
print("** SET DATE and TIME:", obj.date, obj.time, "to:", date, time)
def settimedate(obj, time, date):
print("** SET TIME and DATE:", obj.time, obj.date, "to:", time, date)
# list of commands
commands = ["GET", "SET", "TIME", "DATE"]
# list of function pointers and how many parameters are needed
actions = [(gettime, 0), (getdate, 0), (settime, 1), (setdate, 1), (setdatetime, 2), (settimedate, 2)]
# list of valid sequences of commands
commandsequences = [(0, 2),
(0, 3),
(1, 2),
(1, 3),
(1, 3, 2),
(1, 2, 3)]
commandactions = [0,1,2,3,4,5]
# will be used as upper bound for our search
longest_cmd = 0
for c in commandsequences:
if len(c) > longest_cmd:
longest_cmd = len(c)
# instance of object to operate on
obj = operator()
# sequence of commands that we get from the user
inputsequence = "UNKNOWN 112 SET GET GET SET GET DATE GET TIME SET TIME SET TIME 123 GET TIME SET DATE 312 GET DATE SET TIME DATE 1337 31337 GET TIME GET DATE SET DATE TIME 123 321 GET TIME GET DATE"
command_seq = inputsequence.split()
cmd_parsed = []
is_param = []
for c in command_seq:
if c in commands:
is_param.append(False)
cmd_parsed.append(commands.index(c))
else:
is_param.append(True)
cmd_parsed.append(c)
cmd_parsed = tuple(cmd_parsed)
# rude parsing without parameter validation
cpos = 0
while cpos < len(cmd_parsed):
action_called=False
print("Trying to parse commands from", cpos)
while is_param[cpos]:
print("Can not start with argument, skip")
cpos += 1
testlen = longest_cmd
while testlen > 0:
if action_called:
cpos -= 1
break
print("Testing commands of length:", testlen, "from pos:", cpos, "RAW:", command_seq[cpos:cpos+testlen])
inseq = cmd_parsed[cpos:cpos+testlen]
if inseq in commandsequences:
print("Found Command...")
cpos += testlen
idx = commandsequences.index(inseq)
action, nparams = actions[commandactions[idx]]
if nparams > 0:
print("...with parameters expected...")
pfound = 0
params = []
while pfound < nparams:
print("Checking if:", cmd_parsed[cpos], "at:", cpos ,"is a parameter")
if is_param[cpos]:
params.append(cmd_parsed[cpos])
cpos += 1
pfound += 1
else:
# not enough parameters to call command
# break out of this nicely
print("Not enough parameters for this command")
break
if pfound < nparams:
continue
print("Calling command with parameters")
action(obj, *params)
action_called = True
continue
else:
print("Calling command without parameters")
action(obj)
action_called = True
continue
testlen -= 1
cpos += 1
Senast redigerad av mounte 19 mars 2015, 22:30:32, redigerad totalt 1 gång.