Projekt arbejde til kurset Advanced Models and Programs, SASP-AMP 2008 Poul Brønnum, IT Universitetet i København, Maj 2008. Indholdsfortegnelse



Relaterede dokumenter
Åben uddannelse, Efterår 1996, Oversættere og køretidsomgivelser

Software Construction 1. semester (SWC) januar 2014 Spørgsmål 1

Software Construction 1 semester (SWC) Spørgsmål 1

Online kursus: Programming with ANSI C

Programmering i C. Lektion december 2008

DM507 Algoritmer og datastrukturer

Programmering i C. Lektion september 2009

DM507 Algoritmer og datastrukturer

Abstrakte datatyper C#-version

DM507 Algoritmer og datastrukturer

Skriftlig eksamen, Programmer som Data januar 2014

Pointers. Programmering i C. 11. december Lektion 5. Eksempel: denne plads. Getting the address of a variable:

Klasse 1.4 Michael Jokil

Kernealphaerne Indhold af G1

DM507 Algoritmer og datastrukturer

LØKKER METODER C S HISTORIE. Grundlæggende programmering Lektion 4

Polymorfi. Arv (inheritance) Abstrakte klasser, substitutionsprincippet, overriding, statisk og dynamisk type. Coercion

METODER ARV KLASSER. Grundlæggende programmering Lektion 5

Skriftlig eksamen, Programmer som Data Onsdag 6. januar Spørgsmål 1 (20 %): Regulære udtryk og automater

Kursusarbejde 3 Grundlæggende Programmering

SWC eksamens-spørgsmål. Oversigt

Planen for idag. Indhold af G1. Kernealphaerne. Alpha erne. Datalogi 1F Forår 2003

Eksamens spørgsmål Software Construction. Objekter. Spørgsmål 1: Januar Giv en beskrivelse af Objekt-begrebet og deres brug

Undervisningsbeskrivelse

Programmering i C. Kursusintroduktion. Lektion september Målgruppe 2 Indhold 3 Form 4 Materiale. Målgruppe Indhold Form Materiale

Python programmering. Per Tøfting. MacFest

Programmering i C. Lektion september 2009

Sproget Rascal (v. 2)

DM507 Algoritmer og datastrukturer

Lektion 4. Grundlæggende programmering i VR

Forelæsning Uge 4 Mandag

Sproget Six. Til brug i rapportopgaven på kurset Oversættere. Vinter Abstract

Kapitel 4 Løkker i C#

Yderligere udvidelser af oversætter for Minimal

LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam) { int wmid, wmevent; programmering med

Divide-and-Conquer algoritmer

Noter til C# Programmering Selektion

Datalogi OB, Efterår 2002 OH er, forelæsning 10/ Klasser og nedarvning

Projekt - Visual Basic for Applications N på stribe

Målet for disse slides er at diskutere nogle metoder til at gemme og hente data effektivt.

Forelæsning Uge 4 Torsdag

Kontrol-strukturer i PHP

Studiepraktik. Thomas Bøgholm Mikkel Hansen Jacob Elefsen

Forelæsning Uge 4 Torsdag

Recollections about the Development of Pascal. Niklaus Wirth ACM, 1993

DM507 Algoritmer og datastrukturer

Kapitel 3 Betinget logik i C#

DM507 Algoritmer og datastrukturer

DM502. Peter Schneider-Kamp

Undervisningsbeskrivelse

Divide-and-Conquer algoritmer

Forelæsning Uge 4 Mandag

BlogReader Af Jonas F. Jensen.

Modern Concurrency Abstractions for C#

Introduktion til funktioner, moduler og scopes i Python

Michael Jokil

Web of Science Vejledning

Programmering i C. Lektion oktober 2008

Sortering af information er en fundamental og central opgave.

Sortering. Eksempel: De n tal i sorteret orden

Lageradministration Paging og segmentering

Crash Course i Programmering. HumTek, RUC

Programmering i C Intro og grundlæggende C 5. marts 2007

SPSS introduktion Om at komme igang 1

Skriftlig eksamen i Datalogi

Generel projektbeskrivelse

Forelæsning Uge 4 Mandag

Divide-and-Conquer algoritmer

Kursusgang 11. Oversigt: Sidste kursusgang Værktøjer til udvikling og implementering af HCI-design Oversigt over Java Swing

University of Southern Denmark Syddansk Universitet. DM502 Forelæsning 4

Forelæsning Uge 2 Torsdag

Undervisningsbeskrivelse

DM507 Algoritmer og datastrukturer

Undervisningsbeskrivelse

AAU, Programmering i Java Intern skriftlig prøve 18. maj 2007

vil jeg blive mindet om det af VBA allerede mens jeg skriver koden, da der er tale om en såkaldt kompileringsfejl:

Udvikling af DOTNET applikationer til MicroStation i C#

Under 'Microsoft Block Editor', klik 'New project' for at åbne block editor-værktøjet.

DM507 Algoritmer og datastrukturer

DM507 Algoritmer og datastrukturer

Exceptions i Delphi. Try except

Forelæsning Uge 2 Torsdag

Programmering C Eksamensprojekt. Lavet af Suayb Köse & Nikolaj Egholk Jakobsen

Programmering. Det rent og skært nødvendige, det elementært nødvendige! Morten Dam Jørgensen

DM13-3. Obligatorisk opgave E.05 Håndoptimering af SPARC assembler-kode

4 Basal Objekt-orienteret Programmering I.

Skriftlig eksamen i Datalogi

DM536. Rapport og debug

Grafer og graf-gennemløb

Divide-and-Conquer algoritmer

Lektion 6. Grundlæggende programmering i VR

Sortering. Eksempel: De n tal i sorteret orden

Sider og segmenter. dopsys 1

Brugermanual til Assignment hand in

Grådige algoritmer. Et generelt algoritme-konstruktionsprincip ( paradigme ) for optimeringsproblemer.

Udarbejdet af CFU Absalon

Ghostbusters. Introduktion. Scratch. Du skal lave et fangelegsspil med spøgelser! Arbejdsliste. Test dit Projekt. Gem dit Projekt

Forelæsning Uge 3 Mandag

Mircobit Kursus Lektion 3 (Du skal her vælge Lets Code Og nederst Microsoft Block Editor.)

Test af It-komponent

Transkript:

Udvidelse af MicroC compileren til at kunne håndtere struct- og union typer samt switch-, break- og continue statement. Yderligere er ovenstående konstruktioner anvendt til at indføre malloc og free kald i MicroC. Projekt arbejde til kurset Advanced Models and Programs, SASP-AMP 2008 Poul Brønnum, IT Universitetet i København, Maj 2008 Indholdsfortegnelse 1. Forord og indledning...2 2. Baggrund og problemstilling...2 2.1 Continue statement....2 2.2 Switch statement...2 2.3 Break statement...3 2.4 Struct og Union typer....3 2.5 malloc og free kald...3 3. Problemanalyse og teknisk beskrivelse...4 3.1 Continue...4 3.2 Switch...4 3.3 Break...5 3.4 Struct og Union Typer...6 3.5 malloc og free...7 4. Afprøvning af programmet....8 5. Konklusion...8 6. Litteratur...9 7. Bilag...9

2 1. Forord og indledning Rapporten er udarbejdet maj 2008 som afslutning på kurset Advanced Models and Programs på IT universitetet i København, under vejledning af Peter Sestoft som jeg takker for god hjælp. Udover at berøre og praktisere anvendelsen af en række programmeringssprog og analyseteknikker, indeholdt kurset en fire lektioners gennemgang af compileres konstruktion og omgivelser. Teorien knyttet til denne del af kurset blev anskueliggjort og praktiseret gennem øvelser med og tilretninger af MicroC compileren, samt parameterfiles for Coco parseren til C. Betegnelser som på kurset udelukkende blev anvendt i engelsk version af alle deltagere, er adopteret i denne rapport, og introduceres så vidt muligt i anførselstegn. Da den udførte program-kode udvider compiler og parserparametre, er der lagt vægt på at bibeholde den eksisterende grundstruktur i disse. Der er således f.eks. bygget videre på compilerens Abstract Syntax Tree (AST) samt klasser for typer, expressions og statements, således at denne afprøvede base kan genbruges. The C Programming Language, second edition af Kerninghan og Ritchie [1] er benyttet som reference for kravene til et korrekt C sprog. Denne rapport med tilhørende dokumenter, filer og kildekode kan downloades fra http://www.itu.dk/people/pbr/microc/ 2. Baggrund og problemstilling I det følgende beskrives de krav, der stilles til udvidelserne af MicroC. 2.1 Continue statement. Dette statement skal indlede næste gennemløb af nærmeste for eller while loop, og kan således betragtes som et kontrolleret goto. Loops er allerede implementeret i MicroC. I for statement skal programmet fortsætte ved loopets increment-step. Det er ikke muligt at anvende statementet uden for loops. 2.2 Switch statement Dette statement skal kunne udføre en forgrening i programmet, bestemt af hvorvidt et udtryk finder match i en liste af én eller flere konstante værdier. Forgreningspunkter bærer labels af formen case: konstant svarende til startpunkter for denne fordeling. En mulig default: label kan opfange ikke-matchede udtryk. Break statement (2.3) benyttes til at afslutte en sådan forgrening inden næste label, men er ikke krævet, dvs. fall through kan opstå i C.

3 2.3 Break statement Dette statement skal muliggøre, at enten en loop konstruktion eller et switch statement (se 2.2) kan forlades uden at gennemføre indledende test af variable. Statementet forårsager at inderste loop eller switch forlades (afbrydes) straks, og fortsætter i næste niveau. I den sammenhæng skal nævnes, at en continue inde i en switch sætning derimod vil afbryde alle omgivende switch statements indtil nærmeste loop statement. Der er ikke muligt at anvende break udenfor switch eller loops. 2.4 Struct og Union typer. Disse typer skal kunne referere til en gruppe af én eller flere variable, således at denne gruppe kan behandles som én enhed i stedet for som enkeltstående variable. Struct skal organisere disse variable sekventielt, medens union har sine variable placeret overlappende med samme startposition. I det følgende anvendes fællesbetegnelsen record for de to typer. Til at deklarere variable af disse typer, skal anvendes keywords struct eller union. Til en deklaration kan knyttes et typenavn svarende til int eller bool for primitive typer. Når en type er defineret, kan variable efterfølgende defineres ved at referere til dette typenavn. Variable i en record kaldes members og skal kunne adresseres ved at sammenknytte members med operatorerne. og ->. Sidstnævnte benyttes til at adressere via pointere. Syntaks er således (struct union) [typenavn] [{ type navn;*}] [variabelnavn]; Eksempel: struct header { struct header *ptr; int size; } base; Struct header freehdr; Eksemplet viser samtidig, at members kan have samme type som den deklarerende record, blot denne selv-henvisning foregår i pointerform for at undgå cyklisk reference. Alle eksisterende typer skal kunne benyttes som member variabel. Således også pointere og arrays. 2.5 malloc og free kald Disse kald skal på kørselstidspunkt kunne allokere og frigive memory til et MicroC program, således at et behov for lager som ikke kendes statisk, kan tilfredsstilles dynamisk fra en memorypool. Alle typer kan tildeles memory via malloc. Da C sproget ikke har garbage collection, ønskes computerens memory anvendt så optimalt som muligt. Derfor skal f.eks. free muliggøre, at memory hentet via malloc og ikke længere er anvendt, kan genbruges.

4 3. Problemanalyse og teknisk beskrivelse I de følgende analyser af opgaveløsninger refererer check til compilerens gennemløb af AST med henblik på type check medens compile tilsvarende henviser til gennemløbet hvor der dannes mellemliggende kode. Environment er datastrukturer og funktioner tilknyttet respektive gennemløb. Under krav formuleret til check er underforstået, at der kastes en compiler exception såfremt pågældende krav ikke opfyldes. 3.1 Continue Denne funktionalitet løses med god hjælp fra eksisterende compiler og parser. En ny statement klasse ContStatement afledt af Statement introduceres, og parseren danner som node i AST et objekt heraf, når det mødes hvor statements i øvrigt kan optræde. Når AST gennemløbes for type check, kontrolleres ved hjælp af en simpel loop tæller - (whilelevel) indført til formålet i While statement - at et omgivende loop er i gang. Til at understøtte Compile indføres i while statement en continue label (contlab) som henviser til eksisterende start label eller increment label afhængig af loop type. Disse labels pushes i environment i særlig ActiveWhiles stack ved indledning af nyt loop. Herefter kan Continue compileres som en simpel goto til den øverste label i denne stack. 3.2 Switch Den ønskede funktionalitet a) Først at evaluere tilknyttet switch expression b) Dernæst at finde tilhørende case værdi i en liste af mulige forgreningspunkter (under hensyntagen til en eventuel default label) c) Endelig at fortsætte udførelse ved statement tilknyttet den fundne case værdi og tage højde for et eventuelt møde med et break statement løses ved hjælp af følgende tilføjelser og hjælpekonstruktioner: - parseren indstilles til at møde keyword switch på statement niveau, hvilket vil indlede dannelsen af et objekt i en ny klasse Switch afledt af Statement. Samtidig danner parseren et dictionary (casedict) som er element i en stack (switchstack) der vedligeholdes i environment. Formålet er, at der allerede på parserniveau sikres, at case numre ikke kan optræde duplikerede og at case labels ikke kan mødes uden for en omgivende switch. - Til type check kontrolleres ved hjælp af en simpel loop tæller - (switchlevel) i Switch statement - at en omgivende switch er i gang.

5 - Der dannes et objekt af en ny undertype, LabelStatement, som sørger for at skabe og lagre en unik label for et tilhørende statement. Denne klasse kontrollerer endvidere, at label nr. er af type Constant. - Endelig styres compilering af nestede switch blokke ved hjælp af en stack over igangværende Switch objekter (ActiveSwitches) som vedligeholdes i compile environment. Switch objektet er konstrueret med to elementer: Et switch expression og et Block statement (composite Statement, en gruppe af et eller flere andre statements). Herefter kan den ønskede funktionalitet konstrueres, der henvises til indledningen af dette punkt: - Check er forholdsvist simpelt. Først kontrolleres expression for at være integer type. Dernæst steppes switchlevel, hvilket vil legalisere at der efterfølgende mødes break statements. Endelig udføres check af Block statementet, og switchlevel reduceres igen. - Compilering er knap så enkel. Funktionen skal indledes af en test, og dernæst skal der fortsættes et eller andet sted i switch blokken, som gennemløbet endnu ikke har mødt i AST og dermed compileret til kendte labels. Derfor benyttes følgende konstruktion, inspireret af Compilers, Principles techniques and tools [2]: Først springes uden om koden til en label (beginlab) som indleder testen. Herefter kan blok statementet (som jo i øvrigt rekursivt kan indeholde nestede switch statements) compileres. Endelig compileres testen baseret på switch-expression denne test fordeler sig til nu kendte labels, som ovenfor beskrevne LabelStatement i mellemtiden har tilføjet det aktuelle Switch objekt gennem ActiveSwitches stack i environment. Eller laver fall through hvis der ikke findes en default label. Skelet for kodegenerering kan sammenfattes således: * GOTO beginlab; * ([caselabel n: default:] statement)*; * GOTO endlab; * beginlab: * <test>; * endlab: Generering af test kode foregår ved simpel if expression equals label constant goto case label. En statisk optimering havde været mulig, ved f.eks. at anvende tabelopslag i tilfælde hvor konstanterne ligger tilstrækkelig tæt. Eller en effektiv hash-lookup kan inlines eller kaldes gennem systemkald som anskueliggjort senere under malloc/free. Dette kan være relevant ved tunge loops. 3.3 Break En break skal afbryde et igangværende switch statement eller loop, afhængig af hvad der er nærmest i scope. Til dette formål vedligeholder Switch og While objekter en stack i environment (ActiveLabels) som indeholder end-label for switch henholdsvis loop statements.

6 Herefter kan break håndteres yderst enkelt: Test foretages ved at sikre sig at der findes switch eller loop i gang (whilelevel > 0 eller switchlevel > 0). Compile udføres ved at emitte en goto til den øverste label i ActiveLabels stack. 3.4 Struct og Union Typer Disse to typer har flere ligheder end forskelle, og fælles-behandles i det følgende under betegnelsen records, med mindre de varierer. Det er centralt for denne del af analysen, at records muliggør indførelse af composite typer på individuelt program niveau. Typer kan nu ikke længere findes frem gennem færdige og kendte typeklasser, men skal dannes og compileres, samt identificeres ved et eventuelt typenavn. Samtidig er deklaration af en recordtype variabel mere kompleks, idet variablen kan deklareres i umiddelbar forlængelse af type deklarationen, i hvilket tilfælde typen ikke behøver at blive navngivet. Eller en record variabel kan deklareres gennem refererence til et allerede deklareret typenavn. Endelig kan et typenavn deklareres uden nødvendigvis samtidig at deklarere variable. Analysen skal yderligere håndtere den vinkel, at disse typer da de ikke er kendte når parseren starter skal lagres og fremfindes dynamisk under hensyntagen til scope og environment. På mange måder den samme håndtering som foregår for almindelige variable. Men en type og en variabel i samme scope kan godt have samme navn. Record typer aspirerer således til at være composite subtyper af Type klassen, på samme måde som Block statements rummer andre Statement typer. Og disse typer aspirerer endvidere til at have en væsentlig del af sin struktur fælles med almindelige variable. På denne baggrund er funktionaliteten indført som beskrevet i det følgende: Der oprettes to ny Operator klasser til at håndtere sammenstilling af member variable: varmember og ptrmember for henholdsvis. og -> operator. Samtidig indføres en ny subtype til Access typen, som kan rumme 2 andre objekter af Access type, samt en memberoperator. Denne type kaldes MemberAccess og har således de samme basis egenskaber som Access mht. bl.a. lvalue or rvalue. Endvidere kan den rumme et ubegrænset antal members, idet højre ben i objektets node i AST kan være et nyt MemberAccess objekt. Jeg har haft en række forskellige overvejelser vedrørende organisation af disse typer, og har valgt en løsning, hvor navngivne typer lagres sammen med andre variable, idet navnet prefixes tegnet som er illegalt i et almindeligt variabelnavn og derfor vil blive afvist af parseren. På den ene side undgås en masse duplikering af kode og data (håndtering i scope stacks, lookup lokalt / globalt, forskellig behandling i forskellig type environment etc.). På den anden side kan dette valg indebære en fare for urent design ved at lagre to forskellige slags emner i samme struktur. Men overordnet vil f.eks. check af en alm. variabel skulle fremfinde variablens type ud fra dens navn, på samme måde som et type navn skal. Og håndtering af tegnet kan foregå centralt i Record-typen. På type niveau indføres en Record type som er en abstract class. Heraf afledes Struct og Union klasserne som f.eks. ved polymorphic dispatch beregner member offset og size på hver sin måde. Parseren danner i første omgang en ikke-komplet Record type, som bl.a. indeholder en member List. Denne færdiggøres løbende, og senest ved deklaration af type og variabel, hvor bl.a.

7 referencer til allerede definerede typer løses, og member List færdigbehandles med hensyn til offset og kontrol af duplicates. Endelig har jeg fundet det relevant at indføre tre delegates (FindType, AddType og StartRecord) som kaldes fra en type og udfører environment funktioner der har samme formål men er implementeret forskelligt i henholdsvis check og compile environment. På den både kan den eksisterende adskillelse mellem de to environments bibeholdes, samtidig med at beslægtet funktionalitet kan udføres og kontrolleres i de respektive environments. (Og et objekt orienteret design bibeholdes uden eksplicit test på typer). Herefter står tilbage at udføre check og compile af et MemberAccess objekt. Venstre del af et member par kontrolleres således, at pointer operatorer kun er tilladt for members med PointerAccess. Samtidig vedligeholdes en stack i environment over aktuel Record type, således at members kan behandles og kontrolleres rekursivt. Compile af et MemberAccess foretages ved at første element placerer sin adresse på normal vis vha. sin VariableAccess. Herefter udfører members rekursivt ADD af sit offset til denne adresse. 3.5 malloc og free For dynamisk at kunne allokere og tilgå memory i samme adresserum som øvrige variable, er den eksisterende stack (1000 integers) opdelt i to halvdele, hvoraf den nederste fortsætter med uændret formål. Jeg har valgt at tilrette den algoritme som er beskrevet i The C Programming Language, 2. ed. [1], idet den netop udviklede funktionalitet for Record typer derved kan komme i spil. Den samlede kode er vist i bilag 2. Koden vil kunne opdeles så en compilering vil starte med at danne en arbejdsfil som består af indledende system deklarationer efterfulgt at det program som skal compileres og afsluttet med selve malloc / free koden. Eksemplet viser den totale kode. Da MicroC (endnu) ikke har mulighed for casts eller void datatyper, har jeg måttet liberalisere typecheck for assignments, således at der kan assignes mellem int og pointer typer. Dette bør ved første lejlighed strammes igen, men er nødvendig for at muliggøre denne implementering. Malloc fungerer gennem et system af header records, som dels beskriver et umiddelbart følgende dataareal gennem et size member, og dels peger på næste fri blok. Funktionen henter memory fra systemet når det er nødvendigt, og holder selv regnskab med en liste af fri datablokke, som er linket sammen i en cirkulær liste. Når en bruger anmoder om memory af en bestemt størrelse starter et gennemløb, som stopper ved første fri blok der kan rumme det ønskede. Hvis denne blok har den nøjagtige størrelse frigøres den, og returneres til bruger med den adresse som umiddelbart følger blokkens header. Bruger kan herefter assigne denne adresse til en pointer, og arbejde videre med den på normal vis. Hvis den mødte blok indeholder mere end nødvendigt, omdannes den sidste del til en blok der returneres til bruger, medens den første del fortsætter sit liv som fri blok med en reduceret længde. Hvis der ikke er nogle blokke til rådighed af ønsket størrelse forsøger funktionen at hente mere memory fra systemet, der linkes ind i ringen af fri blokke. Hvis systemet afviser anmodningen, returnerer malloc værdien 0 til bruger.

8 free virker omvendt, dvs. bruger afleverer en blok til funktionen. Denne linkes ind i den eksisterende ring af fri blokke. Hvis nabo blokke er fri, smeltes de sammen med den frigjorte blok. Jeg har forsøgt så vidt muligt at debugge koden i bilag 2, og har fundet at den opfører sig som beskrevet. Det viste applikationskald demonstrerer (set udefra) kun, at memory et eller andet sted kan skrives og læses. 4. Afprøvning af programmet. Bilag 1 viser en række eksempler og test af break, continue og switch. Bilag 2 viser som beskrevet i forrige afsnit en intens brug af records samt implementering af malloc og free. 5. Konklusion. Formålet med at udføre denne rapport har især været at forøge min forståelse af og kendskab til compileres virkemåde. I den sammenhæng har arbejdet været meget lærerigt, og vist hvor meget der kan laves med få instruktioner når den nødvendige infrastruktur er til stede. Samtidig har arbejdet også forøget forståelsen for at ensartede principper følges, så overblikket bevares selv om programmet er langt inde i f.eks. en nestet member-deklaration af et array af pointervariable. Med hensyn til det konkrete resultat, så mener jeg at det er lykkedes at skabe brugbare tilføjelser til de omhandlede emner, uden at indføre unødvendige begrænsninger. Malloc kræver naturligvis mere memory til rådighed i praksis. Selv om afprøvningen har vist korrekte resultater, ville jeg i en fremtidig version bruge yderligere tid på test og atter test. Som en af de første tilføjelser til en ny release, ville jeg indføre void pointere, så der kan udføres mere rene assignments. Og en funktions pointer kunne åbne en række muligheder. Efter en mere grundig gennemgang af stackens anvendelse, kan det muligvis være en fordel at lægge stacken sidst i memory, således at der kastes en exception ved stack overflow som ved tidligere version - i stedet for at fortsætte ind i malloc område.

9 6. Litteratur 1. Brian W. Kernighan, Dennis M. Ritchie: The C Programming Language, 2. edition, Prentice Hall (1988) 2. Aho A., Lam M., Sethi R., Ullman J.D.: Compilers. Principles, Techniques & Tools, 2. edition, Addison Wesley (2006) 3. Appel, A.: Modern compiler implementation in java, 2. edition, Cambridge University Press (2002) 7. Bilag Bilag 1. Bilag 2. Bilag 3. Bilag 4. Demonstration og test af break, continue og switch statements Implementation af malloc og free, samt demonstration af kald Programtekst for MicroC compiler med farvemarkering af ændringer Tilsvarende liste for Coco ATG parameter file