Pascar: A Programming Language that has Pascal-like Syntaxes
Pascar is yet another statically typed programming language. Pascar has:
- Powerful Type System
- based on Hindley-Milner type system
- support object system based on row polymorphism
- Lexically-scoped Variables
- First-class Functions
- String Interpolation
- found in Ruby, Scala, Kotlin, etc.
- Loop Expression
- Space-sensitive and Line-sensitive Syntax
- list literals
- map literals
- set literals
- , etc.
Pascar's syntax is heavily inspired by Pascal.
Instalattion and Quick Start
It requires Java 8 or later.
You can download the binary distribution (executable jar) from the release page. Put the file on an directory and execute the pascar.jar by java -jar
command:
$ java -jar pascar.jar
Usage: java -jar pascar.jar (-f <fileName> | -e <expression>)
<fileName> : read a program from <fileName> and execute it
-e <expression> : evaluate <expression>
Write the folowing lines and save it to hello.pascar
.
writeln('Hello, World!')
And run the interpreter by java -jar pascar.jar hello.pascar
:
$ java -jar pascar.jar hello.pascar
Hello, World!
Syntax
Variable Declaration
var one = 1
Declare variable one
and one
is bound to 1
. You can omit
semicolon(;
) at the last of the declaration:
var name = expression [;]
Constant Declaration
const name = expression [;]
Declared constant 'one_immutable' is bound to '2'. You can omit
semicolo (:
) at the last of the declarat ion:
You can use type annotaion as you like:
var name: String = 'FOO'
If Expression
The syntax of if expression is like Pascal:
if i < 3 then
writeln('i < 3')
else
writeln('i >= 3')
end
While Expression
Thke syntax of while expression is like Pascal:
var i = 0
while i < 10 do
begin
i := i + 1
writeln(i)
end
Function Literal
var add = (x, y) => x + y
Declare variable add
and add
is bounded to the function literal that
calculates x + y
. If an anonymous function has block body, you can write as
the following:
var printAndAdd = (x, y) => {
writeln(x)
writeln(y)
x + y
}
Note that semicolon at the end of each expression of block can be omitted.
Named Function
If you want to define recursive functions, anonymous function literal cannot be used. Instead, you can use the notation for recursive functions:
function fact(n)
begin
if n < 2 then
1
else
n * fact(n - 1)
end
end
fact(0) // 1
fact(1) // 1
fact(2) // 2
fact(3) // 6
fact(4) // 24
fact(5) // 120
// The result of type inference of fact is : Int => Int
Function Invocation
const add = (x, y) => x + y
writeln(add(1, 2))
A function can be invoked as the form fun(p1, p2, ..., pn)
. The evaluation
result of fun
must be a function object.
List Literal
const list = [1, 2, 3, 4, 5]
writeln(list)
A list literal can be expressed as the form [e1, e2, ...,en]
. Note that
separator characters have also line feeds and spaces in Klassic unlike other programming languages.
const list = [
1
2
3
4
5
]
writeln(list)
var list = [[1 2 3]
[4 5 6]
[7 8 9]]
The type of list literal is a instance of special type constructor List<'a>
.
Map Literal
const map = %['A': 1, 'B': 2]
map Map#get 'A' // => 1
map Map#get 'B' // => 2
map Map#get 'C' // => null
A map literal can be expressed as the form %[k1:v1, ..., kn:vn]
(kn
and vn
are expressions). Note that
separator characters also include line feeds and spaces in Klassic unlike other programmign languages:
const map2 = %[
'A' : 1
'b' : 2
]
The type of map literal is a instance of special type constructor Map<'k, 'v>
.
Set Literal
A map literal can be expressed as the form %(v1, ..., vn)
(vn
are expressions). Note that
separator characters also include line feeds and spaces in Klassic unlike other programmign languages:
const set1 = %(1, 2, 3)
const set2 = %(1 2 3) // space is omitted
const set3 = %(
1
2
3
)
The type of set literal is a instance of special type constructor Set<'a>
.
Numeric Literal
Pascar supports various literal. The followings are explanations:
Int
writeln(100)
writeln(200)
writeln(300)
The max value of Int literals is Int.MaxValue
in Scala and the min value of integer literals is
Int.MinValue
in Scala.
Byte
The suffix of byte literal is BY
. The max value of long literals is Byte.MaxValue
in Scala and
the min value of long literals is Byte.MinValue
in Scala.
writeln(127BY)
writeln(-127BY)
writeln(100BY)
Short
The suffix of short literal is S
. The max value of long literals is Short.MaxValue
in Scala and
the min value of long literals is Short.MinValue
in Scala.
writeln(100S)
writeln(200S)
writeln(300S)
Long
writeln(100L)
writeln(200L)
writeln(300L)
The suffix of long literal is L
. The max value of long literals is Long.MaxValue
in Scala and
the min value of long literals is Long.MinValue
in Scala.
Double
writeln(1.0)
writeln(1.5)
The max value of double literal is Double.MaxValue
in Scala and the min value of double literal is Double.MinValue
in Scala.
Float
writeln(1.0F)
writeln(1.5F)
The max value of float literal is Float.MaxValue
in Scala and the min value of float literal is Float.MinValue
in Scala.
Comment
Pascar provides two kinds of comment
(Nestable) Block Comment
1 + (* nested
(* comment */ here *) 2 // => 3
Line comment
1 + // comment
2 // => 3
Type System
Pascar is a statically-typed programming language.
Hindley-Milner Type Inference
Pascar's type inference is based on HM. It means that type annotations is not required in many cases:
function fold_left(list)
begin
(z) => (f) => {
If isEmpty(list) Then z Else fold_left(tail(list))(f(z, head(list)))(f) End If
}
end
// The result of type inference: List<'a> => 'b => (('b, 'a) => 'b) => 'b
Row Polymorphism
Pascar has simple object system based on row polymorphism. For example,
function add(o)
begin
o.x + o.y
end
the type of above program is inferred:
add: { x: Int; y: Int; ... }
It means that add
function accepts any object that has field x
and field y
.
Although it is not subtyping strictly, many situations that need subtyping are
covered.
Type Cast
In some cases, escape hatches from type system are required. In such cases, user can insert cast explicitly.
var s: * = (100 :> Double) // 100 is casted to Double type
Built-in Functions
Pascar supports some kind of built-in functions.
Standard output Functions
writeln: (param:Any) => Any
display theparam
into the standard output.writeln('Hello, World!')
String Functions
-
substring: (s:String, begin:Int, end:Int) => String
Returns a substring of the Strings
. The substring begins at the indexbegin
and ends at the indexend
- 1.substring('FOO', 0, 1) // => 'F'
-
at: (s:String, index:Int) => String
Returns a String with a character value at the indexindex
of the Strings
.at('BAR', 2) // => 'R'
-
matches: (s:String, regex:String) => Boolean
Returns true if the Strings
matches the regular expressionregex
, false otherwise.val pattern = '[0-9]+' matches('199', pattern) // => true matches('a', pattern) // => false
Numeric Functions
-
sqrt: (value:Double) => Double
Returns the square root of the Doublevalue
.sqrt(2.0) // => 1.4142135623730951 sqrt(9.0) // => 3.0
-
int: (vaue:Double) => Int
Returns the Doublevalue
as the Int value.int(3.14159265359) // => 3
-
double: (value:Int) => Double
Returns the Intvalue
as the Double value.double(10) // => 10.0
-
floor: (value:Double) => Int
Returns the truncated Doublevalue
as the Int value.floor(1.5) // => 1 floor(-1.5) // => -1
-
ceil: (value:Double) => Int
Returns the rounded-up Doublevalue
as the Int value.ceil(4.4) // => 5 ceil(4.5) // => 5 ceil(-4.4) // => -4 ceil(-4.5) // => -4
-
abs: (value:Double) => Double
Returns the absolute value of the Doublevalue
.abs(10.5) // => 10.5 abs(-10.5) // => 10.5
List Functions
-
map: (list:List<'a>) => (fun:('a) => 'b) => List<'b>
Returns a new List consisting of the results of applying the given functionfun
to the elements of the given Listlist
.map([1 2 3])((x) => x + 1) // => [2 3 4] map([2 3 4]){x => x + 1} // => [3 4 5]
-
head: (list:List<'a>) => List<'a>
Returns the first element of the Listlist
.head([1 2 3 4]) // => 1
-
tail: (list:List<'a>) => List<'a>
Returns a new List consisting of the elements of the given Listlist
except for the first element.tail([1 2 3 4]) // => [2 3 4]
-
cons: (value:'a) => (list:List<'a>) => List<'a>
Creates a new List, the head of which isvalue
and the tail of which islist
.cons(1)([2 3 4]) // => [1 2 3 4]
-
size: (list:List<'a>) => Int
Returns the size of the Listlist
.size([1 2 3 4 5]) // => 5
-
isEmpty: (list:List<'a>) => Boolean
Returns true if the Listlist
is empty, false otherwise.isEmpty([]) // => true isEmpty([1 2 3]) // => false
-
foldLeft: (list:List<'a>) => (acc:'b) => (fun:('b, 'a) => 'b) => 'b
Applies a functionfun
to a start valueacc
and all elements of the Listlist
, going left to right.foldLeft([1 2 3 4])(0)((x, y) => x + y) // => 10 foldLeft([1.0 2.0 3.0 4.0])(0.0){x, y => x + y} // => 10.0 foldLeft([1.0 2.0 3.0 4.0])(1.0){x, y => x * y} // => 24.0
Thread Functions
-
thread: (fun:() => Unit) => Unit
Creates a new thread and starts runnng the passed argument functionfun
asynchronously.thread(() => { sleep(1000) writeln('Hello from another thread.') }) writeln('Hello from main thread.') // => 'Hello from main thread.' // => 'Hello from another thread.'
-
sleep: (millis:Int) => Unit
Causes the current thread to sleep for themillis
milliseconds.sleep(1000)
Utility Functions
-
stopwatch: (fun:() => Unit) => Int
Returns the time in milliseconds taken to evaluate the passed argument functionfun
.val time = stopwatch( => { sleep(1000) writeln('1') }) writeln('it took #{time} milli seconds')
-
ToDo: () => Unit
Throwspascar.runtime.NotImplementedError
when evaluated.ToDo() // => throw NotImplementedError
Assertion Functions
-
assert: (condition:Boolean) => Unit
Asserts that thecondtion
should be true, and throwspascar.runtime.AssertionError
if thecondition
is false.assert(2 == 1 + 1) // => OK assert(3 > 5) // => NG: AssertionError
-
assertResult: (expected:Any)(actual:Any) => Unit
Asserts that theactual
value should be equal to theexpected
value, and throwspascar.runtime.AssertionError
if theactual
value is not equal to theexpected
value.val add = (x, y) => { x + y } assertResult(5)(add(2, 3)) // => OK assertResult(2)(add(1, 2)) // => NG: AssertionError
Interoperating Functions
-
url: (value:String) => java.net.URL
Creates newjava.net.URL
object from a Stringvalue
.url('https://github.com/pascar/pascar')
-
uri: (value:String) => java.net.URI
Creates newjava.net.URI
object from a Stringvalue
.uri('https://github.com/pascar/pascar')
-
desktop: () => java.awt.Desktop
Returns the Desktop instance of the current browser context via Java Desktop API.desktop()->browse(uri('https://github.com/pascar/pascar'))