lycoris v2.0.1
Blink Programming Language
Blink is a tiny programming language whose sole purpose is to learn how to design and implement a programming language. Blink is an interpreted class-based, object-oriented programming language featuring a strong static type system. Its syntax and semantics are inspired by the programming languages Scala, Clojure, Kotlin and Rust.
This repository contains the tokenizer, the parser, the type checker, the interpreter and the standard library.
Getting starting
How to setup
Blink is implemented in ES6 and the interpreter requires Node.js >= 4.2.2 to run.
With Node.js installed, type the following commands in a prompt to setup Blink.
$ git clone https://github.com/andela-ftchirou/blink.git$ cd blink$ npm install
How to build
Run npm run build to build.
How to run
Run npm run repl to start an interactive shell for writing Blink programs. Just type your expressions in the interpreter to have them evaluated.
First steps with Blink
How to use the Blink interpreter
$ npm run repl
Welcome to Blink 0.0.1
Type in expressions to have them evaluated.
Type :quit to quit.
blink> Type an expression e.g 1 + 2 and hit Enter
blink> 1 + 2
The interpreter should respond with
res0: Int = 3
res0is a name you can use to refer to this result in later expressionsIntis the type of the result3is the value of the result.
Multiline expressions
Expressions can span multiple lines in the interpreter. When pressing Enter, if Blink detects that the expression typed is not yet complete, it will allow you to continue the expression on the next line. Once a complete expression is detected, Blink will automatically evaluate it.
Welcome to Blink 0.0.1
Type in expressions to have them evaluated.
Type :quit to quit.
blink> 1 +
| 2 +
| 3 +
| 4 +
| 5
res1: Int = 15Cancel the current expression
Type Enter twice (consecutively) to cancel the current expression and start a new one.
blink> 1 +
|
|
Two blank lines typed. Starting a new expression.Load external files
You can define your code in externals files and load them into the interpreter using the :load command.
blink> :load <absoluteFilePath1> [ <absoluteFilePath2>, ...]What to do in the interpreter
In Blink, everything (a part from global variables, functions and classes which are definitions) is an expression. You can type expressions in the interpreter to have them evaluated.
Evaluate literals
Numbers, booleans and strings are all literal expressions and they evaluate to themselves in the interpreter.
blink> 42
res0: Int = 42
blink> 3.14
res1: Double = 3.14
blink> true
res2: Bool = true
blink> "Hello, World!"
res3: String = "Hello, World!"Compute
You can use the interpreter as a calculator and compute mathematical expressions involving the following operators +, -, *, / and %.
blink> 7 - 4 + 2
res1: Int = 5Use Console.println() to print something in the interpreter.
blink> Console.println("Hello, World!")
Hello, World!Declare a local variable with let
Block-scoped variables can be defined using a let expression
blink> let message: String = "Hello, World!" in {
| Console.println(message)
| }
Hello, World!In this example, we defined a variable named message of type String initialized to "Hello, World!. The part of the expression after the in keyword is the body of the let expression. The variable message is accessible only inside the body of the let.
A let expression evaluates to the value of the last expression in its body.
Omit the type of the variable
If a variable is initialized at its declaration, then its type can be omitted. Blink is able to infer the correct type of a variable according to its value.
blink> let message = "Hello, World!" in {
| Console.println(message)
| }
Hello, World!Multiple variables
To declare multiple variables at once, separate them with commas.
blink> let a = 2, b = 3 in {
| a + b
| }
res1: Int = 5The curly braces around the body can be omitted if the body contains only one expression
blink> let a = 2, b = 3 in a + b
res3: Int = 5Decide with if
You can use an if expression to execute one or other expression according to a condition. The condition must evaluate to a Bool value.
blink> if (true) {
| "true"
| } else {
| "false"
| }
res7: String = "true"If the body of the if or else branch is made of only one expression, the curly braces can be omitted.
blink> if (true) "true" else "false"
res8: String = "true"Loop with while
A while expression is used to execute one or more expressions as long as a condition holds true.
blink> let i = 1 in {
| while (i <= 10) {
| Console.println(i)
| i += 1
| }
| }
1
2
3
4
5
6
7
8
9
10Define a global variable
Global variables are defined using the var keyword and are accessible in all expressions in the interpreter.
blink> var message: String = "Hello, World!"
message: String = Hello, World!As with let expressions, indicating the type of the variable is optional when the variable is initialized at its definition.
blink> var message = "Hello, World!"
message: String = Hello, World!Define a function
Functions are declared using the func keyword.
blink> func add(a: Int, b: Int): Int = {
| a + b
| }
add(a: Int, b: Int): IntAfter the func keyword, comes the name of the function, the list of parameters separated by commas and enclosed in parentheses (the type of each parameter must be explicitely provided, preceded by a :), a :, the return type of the function, an = and the body of the function which is a list of expressions enclosed in curly braces.
Once a function is defined, you can call it in the traditional way.
blink> add(2, 3)
res4: Int = 5Unit functions
If a function does not return any value, its return type must be Unit. However, the Unit return type declaration is optional, you can then write methods like
blink> func greet() = {
| Console.println("Hello, World!")
| }
greet()Single-Expression functions
If the body of the function is made up of only one expression, the curly braces can be omitted.
blink> func add(a: Int, b: Int): Int = a + b
add(a: Int, b: Int): IntNo return keyword
There is no return keyword in Blink. The last expression of the body of a function is the return value of the function.
Define a class
Classes in Blink are declared using the class keyword.
blink> class Person {
| }
defined class PersonConstructor
A class in Blink can only have one constructor which is part of the class header. To define a constructor, add a list of parameters enclosed in parentheses to the name of the class.
blink> class Person(firstname: String, lastname: String) {
| }
defined class PersonObjects are then created using the new keyword
blink> new Person("John", "Doe")
res6: Person = Person@8Properties
Class properties are declared with the var keyword
blink> class Person {
| var firstname: String
|
| var lastname: String
|
| var age: Int
| }
defined class PersonProperties can be initialized at declaration. The initialization expression of a property will be evaluated when the object is being created.
Properties are private
Properties in Blink are private and they cannot be made public. If you need to access a property outside of a class, you will need to create a getter and/or a setter for it.
Functions
A class can have functions. Functions are declared as normal functions with the func keyword.
blink> class Person(firstname: String, lastname: String) {
| var age: Int = 0
|
| func firstname(): String = {
| firstname
| }
|
| func setFirstname(name: String) = {
| firstname = name
| }
|
| // ...
| }
defined class PersonFunctions can then be called on an object using the . operator.
blink> var person = new Person("John", "Doe")
person: Person = Person@this
blink> person.firstname()
res7: String = "John"Functions are public by default
Functions in Blink are public by default. To make a function private, add the private modifier to its declaration.
blink> class Person {
| private func age(): Int = ...
|}Inheritance
A class can inherit another class with the extends keyword.
blink> class Employee(firstname: String, lastname: String, company: String) extends Person(firstname, lastname) {
| func company(): String = company
|
| func setCompany(c: String) = company = c
| }
defined class Employee
blink> var employee = new Employee("John", "Doe", "ACME")
employee: Employee = Employee@this
blink> employee.firstname()
res8: String = "John"
blink> employee.company()
res9: String = "ACME"When specifying the superclass, you must pass in the parameters required by the constructor of the superclass.
Object class
By default, all classes inherit from the Object class.
Override functions
To override a superclass function, use the override modifier.
blink> class Person(firstname: String, lastname: String) {
| override func toString(): String = "Person(" + firstname + ", " + lastname + ")"
| }
defined class Person
blink> new Person("John", "Doe")
res9: Person = Person(John, Doe)toString()
The interpreter uses toString() to display values in the REPL. So, it's always a good idea to override toString() in your classes to have a more friendly and accurate representation of your values instead of the default
<className>@<address> .
blink> new Person("John", "Doe")
res9: Person = Person(John, Doe)instead of
blink> new Person("John", "Doe")
res10: Person = Person@12Next steps
Have a look in the samples directory to learn more about the inner details of writing Blink programs.
Domo!
6 years ago