Nano
the
nanny of
pico
user
manual
v0.2
Naam : Van Belle
Werner
e-mail : we47091@is2.vub.ac.be
programmeerproject
1e licentie Computerwetenschappen
Projectbegeleider: Wolgang De
Meuter
Alle schade die voorkomt door het gebruik van dit programma wordt volledig genegeerd door de auteur van dit programma.
Een pc die draait moet voldoende zijn om nano te draaien. Merk op dat het geheugengebruik vergroot naarmate de parse-tree groter wordt. Nano kan maximaal 32 MB aanspreken.
1. Zorg eerst dat de compiler goed geïnstalleerd is. (dus ook 'set PATH=c:\java\bin;%path%')
2. Maak dan ergens een subdirectory op uw harddisk en kopieer alle files naartoe. Stel dat de subdirectory '\nano\' heet. (Merk verder op dat de directory PicoRuntime wel degenlijk 'PicoRuntime' moet heten)
3. Voeg aan de
dos-environment 'CLASSPATH=\nano' toe. Als volgt: set CLASSPATH=\nano
4. chdir naar '\nano\PicoRuntime'. Type hier 'namethem'. (een batchfile) Hierdoor krijgen al de korte filenames hun lange juiste filename.
5. typ 'javac *.java'. Hiermee wordt normaal de pico-runtime library gecompileerd. Deze mag geen fouten geven.
Dit programma is gemaakt om van een gegeven pico-source file automatisch een -file te genereren. Deze file zou na compileren hetzelfde resultaat moeten geven als de geïnterpreteerde pico-source.
Om een vergelijking te kunnen maken met de oorspronkelijke pico is er een pico-interpreter toegevoegd (pico.exe).
nano
inputfile [outputclasse]
Nogal simpel:
inputfile: wat is de invoerfile... (typ de extentie er bij)
outputfile: wat is de naam van de output-classe (ZONDER extentie)
Tussen de installatie-files steekt een file 'showdown.pic'. Laat ons deze pico-source eens omzetten naar ...
Typ 'nano tests\showdown.pic'. De uitvoer is te zien op het volgende blad.
Nu is er een file 'RunMe.jav' gecreeerd. Rename deze file naar 'RunMe.java'. Ik slaag er namelijk niet in vanuit dos de langere bestandsnamen van windows '95 te acceseren.
Deze file moet nu gecompileerd worden naar de juiste class-files. Doe dit als volgt: 'javac RunMe.java'. Deze staat een tijdje te denken en zal dan terugkeren naar de prompt. Nu zijn er class-files gegenereerd.
Het runnen van de showdown gebeurt nu als volgt: 'java RunMe'
(om eventueel een vergelijking te maken met de oorspronkelijke pico kan je 'pico tests\showdown.pic' starten.)
De
ingevoerde pico heeft
een aantal beperkingen die die oorspronkelijke pico niet heeft.
Bijvoorbeeld:
de functionele formele parameters die zelf parameters hebben worden
niet
aanvaard. Samengevat:
begin(
a(b(x)):b(12),
display(a(x*x)))
zal 144 printen terwijl nano zal klagen dat x
niet
gekend is.
De function-assignment uit pico wordt ook niet ondersteund. Ik heb eigenlijk zelfs nog niet uitgevist wat hij betekend.
De uitgeveorde code is uitermate efficiënt, hoewel het blijkbaar toch nog steeds zo is dat de uitgevoerde code trager blijkt te gaan dan de geïnterpreteerde pico. Waarschijnlijk ligt dit aan de sloomheid
van de -interpreter en/of compiler.
De truc die ik gebruik om code te optimiseren bestaat er uit actuele parameters direct uit te rekenen en mee te geven indien dat mogelijk is. Merk op dat in pico de callee bepaalt onder welke vorm de actuele parameters
binnen moeten komen.
De intermediate code generator genereert variabelen als kosten ze niets. Het weghalen van redundante variabelen beschouw ik als een taak voor de -compiler.
Dereferencing variable who's value is never used (%s) : de gegeven identifier wordt opgevraagd maar het resultaat wordt volledig genegeerd.
Er wordt mij gevraagd een nutteloos nummer te creeeren : er moet een nummer gemaakt worden dat nadien gewoon niet meer gebruikt wordt.
Nutteloze table-referentie : in dezelfde trend voortgaand wordt hier de inhoud van een table opgevraagd terwijl deze ook direct vergeten wordt. Rather nutteloos nietwaar ?
Er wordt mij gevraagd een doelloze string te genereren : hoeft geen uitleg meer dacht ik zo...
Couldn't open 'natives.dat' file : de beschrijving van de natives kon niet gevonden worden...
wrong parametercount : het aantal parameters dat meegegeven wordt aan een functie is fout. Merk op dat deze error niet altijd gegeven kan worden.
Failed assertions are considered fatal internal errors. Please report them.
Internal compiler error : bug in het programma, waarschijnlijk.
Undefined variabele : ongedefiniëerde variabele. De gebruikte scoping is de gewone lexicale scoop.
invalid argument list; invalid argument list2 : in de argumentenlijst van een functiedefinitie zit een constructie die niet katholiek is.
unkown character; unkown char : de mapping van pico identifiers naar unieke identifiers gebeurt met een soort escape characters. U hebt zonet een karakter gevonden dat nog niet gecovered is door de escape karakters.
dictionary full : u gebruikt te veel identifiers.
out of memory : het geugen is op.
can't open file %s : de gegeven filename kan niet geopend worden. Hoogst waarschijnlijk bestaat hij niet.
can't open outputfile %s : de outputfile met de gegeven filename kan niet aangemaakt worden. Disk full ?
Parametercount in natives.dat moet <80: nano kan maar 80 parameters uitlezen.
Parametercount missing in natives.dat : er ontbreekt een parametercount.
Writeout name missing in natives.dat (pico-name = %s) : de write-out constructor ontbreekt.
corrupted 'natives.dat' file: er zit een onverwacht token in natives.dat (btw: rems zijn niet toegelaten in deze file)
Omdat de picoruntime nog lang niet af is heb ik een mogelijkheid toegevoegd waarmee zelf natives toegevoegd kunnen worden.
De native function beschrijvingen verblijven in een file 'natives.dat'. Elke native wordt beschreven door 1 lijn waarbij eerst de pico-name wordt gegeven, nadien de java-allocatie-methode en daarachter een beschrijving van de parameters. De beschrijving van de parameters bestaat eerst uit het aantal parameters en dan voor elke parameter een 1 of een 0. Een 1 duidt aan dat die parameter normal moet gepassed worden. Een parameter-table (de @ operator) wordt meegegeven als een count die -1 is.
Een paar voorbeeldjes:
`//'
`new nat_div()'
2 0 0
`\\'
`new nat_mod()'
-1
`display' `new
nat_display()' -1
`lazy_and'
`new nat_lazy_and()' 2 0 1
De volgende functies kunnen niet geherdefiniëerd worden door eigen natives omdat de compiler ze vanzelf zal optimizeren, dit wil zeggen : inline coderen (de begin zal bijvoorbeeld gewoon niet gecalled worden !):
begin
if
until
while
hoewel ze wel at runtime een andere toekenning kunnen krijgen...
De interface tot de runtime kan duidelijk afgelezen worden in de .java sourcefiles die verblijven in PicoRuntime. Als conventie stel ik voor elke native function te laten voorafgaan door 'nat_'. Daardoor worden name-conflicten vermijden.
Mail naar we47091@vub.ac.be of contacteer me persoonlijk.
Nog geen troubles ontdekt... (ook nog niet deftig gebeta-test)
Ik heb het 'pico-manifest' toegevoegd zodat de gebruiker toch al weet (kan weten) wat pico is. Indien er vragen zijn omtrent pico zouden deze aan Prof. Theo D'Hondt gesteld moeten worden.
De taal is volledig gedocumenteerd en kan gevonden worden te http://java.sun.com/...
You may freely distribute this product as long as no payment is involved and you keep my name in it.
Pico : computer programming in 10 hours
Theo D’Hondt
Programming Technology Lab — V.U.B.
June 1995
Aims:
The fundamental aim of Pico is to introduce the essentials of computer
programming to
undergraduate students in the basic sciences other than computer science[1]. In conceiving Pico
we were strongly inspired by the approach used in Abelson and
Sussman’s book “Structure and Interpretation of Computer Programs”[2] and equally strongly repulsed
by the
various standards efforts to teach computer programming at the
high-school and
academic level. Pico can actually be
viewed as an effort to render Scheme
palatable and even enjoyable to people unable or unwilling to make the
intellectual effort necessary to grasp its elegance and power. We do so
by
adapting Scheme’s syntax
(significantly) and semantics (subtly) in order to use what (little)
understanding the novice science student has of (specification)
languages.
The word Pico
should be interpreted as synonymous with very small (according to
Webster’s). The idea was indeed to have a very small language with a
very
general impact. The 10 hours in the
title refers to the fact
that in a 30 hour course we limit to one third the time spent on teaching computer programming. The other
two thirds are spent on using
computer programming in various interesting ways.
We were also strongly driven by
the
ambition to return to the original attraction exerted by computer
programming
on young people; we strongly deplore the current situation where most
of these
regard programming as a mindnumbing chore. Pico
is essentially the result of sugarcoating the hard essentials of
computer
programming in such a way that students cannot help but enjoy it.
Strategy:
The basic rules for establishing
Pico as a programming language result
from the following concerns:
o the
semantics of Pico
should be as simple as possible even if this means that a number of Scheme features become inaccessible
o the
syntax of Pico should
diverge as little as possible from the syntax used in elementary
calculus
o Pico must be presented in
as attractive a way as possible, sacrificing any performance constraint
that
stands in the way of ease–of–use, portability, interactivity and other
similar
concerns
o Pico as a language should
coincide with Pico as a tutoring
system; the boundaries between programming and learning must be totally
removed
As mentioned in the
introduction, we have
briefly considered using Scheme for
this purpose. On the basis of our (extensive) experience with using Scheme in the Computer Science
curriculum we chose not to do so.
Although extremely well–suited for the more computer science oriented
student, Scheme’s cryptic notation makes it
unsuitable for our purposes.
Basic syntax of Pico
In Pico
the basic syntactical entities are —not unexpectedly— numbers, symbols,
variable definitions, variable references, function definitions and
function
applications. Symbols are specified as quote-delimited character
strings while
numbers follow the generally accepted rules. Variables are
alphanumerical
strings starting with a letter. Every isolated instance of the name of
a
variable constitutes a reference to it; defining a variable is done by
associating it with an initial value:
Pi:
3.14159
Function applications are
formulated in
elementary calculus style:
min(f(x),0)
i.e. a name followed by a
parenthesis–delimited, comma-separated argument list. Functions are
defined by
associating an implementation with a function application prototype:
min(x,y):
if(smaller(x,y),x,y)
Function names are extended to
cover
operators; unary or binary operators may be invoked in prefix or infix
notation, again to reflect elementary calculus. For instance
x<<n:
x*2^n
255<<4
illustrates the definition and
application
of a shift operator. Precedence among operators is determined by each
operator’s initial character and is hardwired into the syntax.
Operators are nothing more than
syntactic
sugar and do not have any impact on Pico’s
semantics.
Basic semantics of Pico
In the spirit of Scheme,
we have opted for a functional, dynamically typed,
statically scoped language. There are, however, two major differences
at the
level of the basic semantics.
In the first place, we impose
universal naming of functions:
comb(p,q):
fac(p)/(fac(q)*fac(p-q))
defines the expression to the
right of the
colon as the implementation of a function called comb
taking p and q for
arguments. Compared to Scheme this
slightly reduces the expressiveness of Pico
but eliminates most of the confusing recursion-related issues in Scheme such as the let, let* and letrec
variants of block structures.
In the second place we extend
the Scheme-like argument binding rule to
include formal functional parameters. For instance:
and(p,q()):
if(p,q(),false)
defines a lazy Boolean and. Evaluating the expression:
and(a>0,a<10)
will result in the value of a>0 being bound to p and q to be bound to reflect the definition of a
parameterless function:
q():
a<10
Laziness results from q being called only when needed.
Formal functional parameters
allow us to
bypass the need for Scheme’s special forms and to have totally uniform function call
semantics. It is
for instance very straightforward to introduce a l-calculus
styled Boolean system:
true(then(),
else()): then()
false(then(),
else()): else()
and(left,
right()): left(right(), false)
or(left,
right()):
left(true, right())
not(pred):
pred(false, true)
if(cond,
then(),
else()): cond(then(), else())
The (informal) semantics of Pico are extremely simple:
EVAL(nbr,env)
_ nbr
EVAL(sym,env)
_ sym
EVAL(var:ini,env)
_ DEFINE(var,EVAL(ini,env),env)
EVAL(var,env)
_ LOOKUP(var,env)
EVAL(fun(arg…):bod ,env) _ DEFINE(fun,FUNCTION(arg…,bod,env),env)
EVAL(fun(arg…),env) _ APPLY(LOOKUP(fun,env),arg…,env)
In the above the DEFINE
and LOOKUP functions act upon the
environment in the
usual way; FUNCTION and APPLY
are identical to their Scheme
equivalent, apart from the argument binding behaviour:
BIND(frm,act,env,callenv)
_ DEFINE(var,EVAL(act,callenv),env)
if frm _ var is a
variable
reference
_ DEFINE(fun,FUNCTION(arg…,act,callenv),env)
if frm _ fun(arg…) is a
function
application
The names frm
and act denote respectively a formal
and actual parameter; env represents the (extended)
static environment and callenv represents the dynamic environment. This
part of the
(informal) semantics constitutes the major difference with Scheme;
it is also the basis for the uniform function call
semantics of Pico.
Data structures in Pico
With Pico’s
target audience in mind, we have taken a pragmatic approach to data
structures.
We opted for simple arrays called tables,
which again according to Webster’s is a systematic
arrangement of data. A Pico table is represented
using an
additional syntactical notation which again as closely as possible
reflects
convention. A table is an indexable structure of fixed size; for
instance:
primeNumbers[N]:
true
defines a table called primeNumbers of size N and with all members
initialised to the value of true. Indexing is e.g. performed as follows:
primeNumbers[2*k+1]
with the index expression 2*k+1 dynamically checked to range from 1 through N.
Members of a table are arbitrary
combinations of valid Pico values,
i.e. numbers, symbols, functions or tables. This closely reflects the
data
structure called vector from Scheme.
In order to complete this
picture, we also introduce destructive operations:
primeNumbers[4]:=
false
rebinds the fourth member of primeNumbers to the value of false.
In order to maintain consistency, we have therefore also included
destructive
operations for variables and functions:
Pi:=3.14159265358979
comb(p,q):=
(fac(p)/fac(p-q))/(fac(q)
The apply
operator in Pico
A Pico
interpreter is understood to operate upon an abstract grammar
representation of
a Pico program. In the spirit of
Scheme, this abstract grammar is expressed as Pico tables;
this makes it possible for us to define Pico in a
metacircular way. A
side-effect of this approach is that function argument lists are also
represented as tables, which enables us to introduce an apply
operator at no extra cost. Representing this operator by the
symbol @, we can for instance define:
begin@list:
list[size(list)]
i.e. a function begin
taking at least one argument and returning the value of the last
argument.
Since non-functional formal parameter binding is eager in Pico,
the begin function defines the equivalent
of a Scheme expression sequence and
consequently:
loop(value,
boolean): boolean(loop(body(), cond()), value)
defines an iterator. Using
functional
formal parameters we easily get:
while(cond(),body()):
loop(void, cond())
Another useful application of @ is the following function:
tab@list:
list
which enables us to define
tables in an
alternative way:
primeNumbers:tab(true,true,true,false,true,false,true,false,false)
In analogy to Scheme,
the apply
operator can also be used to dynamically assemble function calls:
cell(start):
begin(put(command,value):start:=value,
get(command):
start,
dispatch@message:
begin(op:if(message[1]='put',put,get),
op@message))
shows that Scheme’s
higher–order function features have been retained in Pico.
Examples
The elementary examples of
recursion are
obviously very straightforward:
fac(n):
if(n>1,
n*fac(n-1), 1)
but Pico
is also very easy to express more sophisticated algorithms in, such as
for
instance the quicksort:
QuickSort(V,Low,High):
begin(Left:Low,
Right:High,
Pivot:V[(Left+Right)//2],
until(Left>Right,
begin(while(V[Left]<Pivot,Left:=Left+1),
while(V[Right]>Pivot,Right:=Right-1),
if(not(Left>Right),
begin(Save:V[Left],
V[Left]:=V[Right],
V[Right]:=Save,
Left:=Left+1,
Right:=Right-1),
false))),
if(Low<Right,QuickSort(V,Low,Right),false),
if(High>Left,QuickSort(V,Left,High),false))