In this section we'll walk through creating simple programs in Dana, from command-line programs to graphical applications. We suggest that you read through the descriptions of each program in-order, since we don't repeat all of the explanations for each example.
This program will use standard output, command-line input, and the file system.
First, create a new file MyApp.dn and open it in a text editor. The .dn file extension is used for all Dana source code. In this file we write the component that this source file will represent, declaring the interfaces that the component provides and requires, and writing implementation functions for provided interfaces:
component provides App requires io.Output out, io.Input in, io.File {
int App:main(AppParam params[])
{
out.print("Enter some text: ")
char input[] = in.readln()
File f = new File("out.txt", File.CREATE)
f.write(input)
f.close()
return 0
}
}
Provided interfaces define the functionality that this component implements (i.e., offers to other components). Required interfaces define the functionality that this component depends upon from other components; these is a little bit like 'import'-style statements in other languages, but with additional semantics. Every component must have at least one provided interface to make its functionality accessible. Components that represent executable programs will always implement the App interface.
The App interface has one function called main. You can see the definition of this function in the API docs here. Each provided interface function is implemented by writing a function of the same name but with the interface's type name, followed by a colon, preceding that function name.
Our example program asks the user for some input, reads that input from the command-line, and writes that input to a text file. To make this work, we declare three required interfaces io.Output, io.Input, and io.File. You can find details of these interfaces in Dana's standard library documentation. These three required interfaces are wired to the provided interfaces of suitable other components when the program is executed. These wirings can be adjusted live, to update or adapt a program's operation while it's running.
For convenience, two of our required interfaces here (io.Output and io.Input) were given names. When we do this, Dana will automatically instantiate these required interfaces and allow those instances to be globally accessed within this component using the given name. This notation is shorthand for a global variable which is automatically instantiated; this shorthand can only be used with interfaces that do not declare a constructor.
The other required interface, io.File, is instantiated within the main method using the new operator. The io.File interface declares a constructor function and so must be instantiated in this way. You can see the interface definition in the standard library here.
Dana is a compiled language, so to run this program we first need to compile it. Dana's compiler is a program called dnc. Open a command-prompt in the directory containing your file MyApp.dn and type:
dnc MyApp.dn
And press enter. This will compile your component.
We run the program using Dana's interpreter, a program called dana. In the same command-prompt window, type:
dana MyApp
And press enter. You should be prompted for some input and the program should then create a text file in this directory containing whatever you choose as input.
The full list of interfaces that you can use in requires directives can be viewed at the Dana API pages along with documentation of each one.
Below is the source code for a simple GUI program, using our event-driven desktop application framework.
component provides App requires ui.IOLayer, ui.Window, ui.Button, ui.Label, data.IntUtil iu {
IOLayer coreui
Window window
Button button
Label label
int clicks
eventsink AppEvents(EventData ed)
{
if (ed.type == Button.[click] && ed.source === button)
{
clicks ++
label.setText("Number of clicks: $(clicks)")
}
}
eventsink SysEvents(EventData ed)
{
if (ed.source === coreui && ed.type == IOLayer.[ready])
{
startApp()
}
else if (ed.source === window && ed.type == Window.[close])
{
window.close()
coreui.shutdown()
}
}
void startApp()
{
window = new Window("MyWindow")
window.setSize(250, 100)
window.setVisible(true)
sinkevent SysEvents(window)
button = new Button("Click on me!")
label = new Label("Number of clicks: 0")
button.setPosition(10, 30)
label.setPosition(button.getPosition().x + button.getPreferredSize().width + 10, 30)
window.addObject(button)
window.addObject(label)
sinkevent AppEvents(button)
}
int App:main(AppParam params[])
{
//initialise the system-level UI framework
coreui = new IOLayer()
//listen for startup events from the system
sinkevent SysEvents(coreui)
//run UI system loop, which blocks until last window closed
coreui.run()
return 0
}
}
As you can see in this code, the Dana core language features an event model which allows one object to register event handlers for events that are generated by other objects, with those event handlers being notified when such events are emitted.
Copy and paste this code into a text file, for example called MyGUI.dn, then compile it using the command:
dnc MyGUI.dn
Then run the program with the command:
dana MyGUI
You can read more about the GUI app framework by going to the Dana API package-level documentation and clicking on the doc button next to the ui package.
Below is the source code for a graphical direct-render-loop program, a style often used to make things like media players or video games. It allows you direct control over the rendering of each frame and the associated frame rate.
component provides App requires ui.FlowRender, ui.FlowCanvas, ui.FlowFont, time.Timer timer, io.Output out {
bool processEvents(FlowEvent events[])
{
bool quit = false
for (int i = 0; i < events.arrayLength; i++)
{
if (events[i].type == FlowEvent.T_QUIT)
{
quit = true
}
}
return quit
}
int App:main(AppParam params[])
{
FlowRender window = new FlowRender(25)
window.setSize(400, 200)
FlowCanvas canvas = new FlowCanvas(window)
window.setTitle("Direct Render Loop")
window.setVisible(true)
Ellipse2D ball = new Ellipse2D(10, 100, 80, 80, new Color(100, 100, 200, 255))
bool quit = false
int x = 100
bool right = true
while (!quit)
{
FlowEvent events[] = window.getEvents()
window.renderBegin()
ball.x = x
canvas.ellipse(ball)
window.renderEnd()
quit = processEvents(events)
window.wait()
if (right)
{
x += 2
if (x >= 320) right = false
}
else
{
x -= 2
if (x == 80) right = true
}
}
return 0
}
}
Copy and paste this code into a text file, for example called MyRender.dn, then compile it using the command:
dnc MyRender.dn
Then run the program with the command:
dana MyRender
You can read more about the UI framework by going to the Dana API package-level documentation and clicking on the doc button next to the ui package.
Dana's standard library includes video decoders and encoders, audio decoders, and a range of other media APIs.