13 Nisan 2015 Pazartesi

Java Language Specification Chapter 3: Lexical Structure

3.1. Unicode karakter seti

Java programları Unicode karakter seti ile yazılır.
Bir java release'de kullanılan Unicode versiyonunu Character sınıfının dokümantasyonundan öğrenebiliriz.
Örneğin Java SE 8 Unicode 6.2'yi kullanırken, Java SE 7 Unicode 6.0 kullanıyordu.
Unicode standardı 16-bitlik birimler halinde geliştirilmişti. Şu anda UTF-16 sayesinde 16bitten fazlasını gerektiren karakterleri de temsil edebiliyor.
Comment, identifier, character ve String literal haricinde bir programdaki bütün input elementleri sadece ascii karakterlerinden oluşur (unicode escape karakterleri de olabiliyor fakat onlar da ascii karakterlerine dönüşüyorlar).
Not: Unicode UTF-16 encoding'in ilk 128 karakteri ascii karakterleridir.

3.2. Lexical translation yani çeviriler

Bir Unicode karakter akışı lexical çeviriyle bir token sıralamasına dönüşür.
Bu çeviri 3 aşamalıdır:
1. Unicode escape karakterleri belirlenir.
2. İlk aşamadan çıkan Unicode akışı input karakterleri ve lien terminatorlar şeklinde bir akışa dönüşür.
3. İkinci aşamadan gelen input karakterleri ve line terminatorları akışı input elementleri sıralamasına dönüşür.
Bunların içinden de white space ve commentler çıkarıldığında geriye sadece gramerdeki terminal sembooller olan token'lar kalır.

Lexical translation sayesinde sadece ascii karakterleri kullanarak unicode escape karakterlerden de yararlanabiliyoruz.

Bu çeviri sonucunda elde edilen karakterler, bir input elementleri dizisini oluşturur.
Bu input elementleri: white space, commentler, ve token'lardır.

Token'lar ise syntactic gramerdeki şu elemanlardır :
  1. identifier
  2. keyword
  3. literal
  4. separator
  5. operator

3.3. Unicode escape karakterleri

Java compiler inputta ilk olarak Unicode escape karakterleri seçer.
Backslash sayısı tek ise escape karakteri tanınır. Çift ise tanınmaz.

For example, the raw input "\\u2122=\u2122" results in the eleven characters  " \ \ u 2 1 2 2 = ™ " (\u2122 is the Unicode encoding of the character ™)

3.4. Line Terminator'ları

Çeşitli sistemlerdeki satır sonu ifadelerini desteklemek için line terminatorlar kullanılır.
Java compiler Unicode escapeleri tanımladıktan sonra, line terminatorları bularak metni satırlara böler. Line terminator dışındaki her karakter bir input karakteridir.

Line Terminatorlar:
  1. ascii LF : newline, line feed, LF
  2. ascii CR : return, carriage return, CR
  3. ascii CR ve ardından ascii LF : CR LF (arka arkaya geldiklerinde tek bir satır sonu demektir)
Not: Line terminator // şeklindeki commentin bittiğini de ifade eder.


3.5. Input elementleri ve token'lar

Tokenization işlemindeki ilk iki aşama yani escape processing ve input line recognition aşamalarından sonra geriye input elementlerinden oluşan satırlar kalır.
Input elementleri: white space, comment ve token'lardır.
Yani bunun anlamı boşluk ve commentler haricindeki her şey bir token'dır.
Bu token'lar: identifier, keyword, literal, separator, ve operator'lardır.


3.6. White space yani boşluk karakterleri

Boşluk karakterleri:
  1. ascii SP : space, ascii boşluk karakteri
  2. ascii HT: horizontal tab karakteri
  3. ascii FF : form feed karakteri
  4. line terminator karakteridir.

3.7. Comment'ler

İki çeşit comment vardır:
  1. /* text */ : /* ve */ ascii karakterleri arasındaki bütün karakterler gözardı edilir
  2. // text : // ascii karakterlerinden satır sonuna kadarki tüm karakterler gözardı edilir
Not: lexical gramere göre karakter ya da string literalleri içerisinde comment bulunamaz.

3.8. Identifier'lar

Bir identifier mutlaka bir harf ile başlayan ve sınırsız uzunluktaki bir harf ve sayı sıralamasıdır.
Harfler unicode karakter seti içindeki faklı dil ve alfabelere has karakterler olabilir.

Burada harf olarak:  "_" altçizgi, ve $ dolar simgesi kullanılabilir.
Yalnız $ simgesi sadece mekanik olarak üretilen kodlarda ya da legacy sistemlerde kullanılmalıdır.
The "Java letters" include uppercase and lowercase ASCII Latin letters A-Z (\u0041-
\u005a), and a-z (\u0061-\u007a), and, for historical reasons, the ASCII underscore (_,
or \u005f) and dollar sign ($, or \u0024). The $ sign should be used only in mechanically
generated source code or, rarely, to access pre-existing names on legacy systems.
The "Java digits" include the ASCII digits 0-9 (\u0030-\u0039)
Bir identifier herhangi bir keyword, boolean literal(true, false), ya da null literal(null) ile aynı şekilde yazılamaz.

İki identifierın aynı sayılması için içindeki her harf ya da sayının aynı Unicode karakter ile ifade edilmiş olması gereklidir.

3.9. Keyword'ler

50 tane ascii karakter sıralaması rezerve edilmiş olduğundan identifier olarak kullanılamaz. Bunlar:

abstract continue for        new       switch
assert   default  if         package   synchronized
boolean  do       goto       private   this
break    double   implements protected throw
byte     else     import     public    throws
case     enum     instanceof return    transient
catch    extends  int        short     try
char     final    interface  static    void
class    finally  long       strictfp  volatile
const    float    native     super     while


Bu listedeki const ve goto kelimeleri kullanılmasa da rezerve edilmiştir. Eğer bunlar yanlışlıkla kullanılırsa bu sayede compiler doğru hata mesajları ile uyarabilmektedir.

Not: true ve false kelimeleri keyword gibi görünse de teknik olarak boolean literalleridir. Aynı şekilde null kelimesi de keyword gibi görünmesine rağmen aslında teknik olarak null literalidir.


3.10. Literaller

Literal, bir primitif tipin değerinin (ya da String ya da null tipi değerinin) source code'daki temsilidir.


Integer literalleri
Bir integer literal 10, 16, 8 ya da 2'li sistemde olabilir.
  • Decimal: base 10
  • Hexadecimal: base 16
  • Octal: base 8
  • Binary: base 2
Bir integer literalin başında ascii L ya da l karakteri varsa long, yoksa int tipindedir.
Genelde küçük l harfi bir rakamına benzediğinden büyük "L" karakteri tercih edilir.
Bir integerin haneleri arasına "_" karakteri ayırıcı olarak konabilir.(başta ya da sonda olamaz)

Not: Hexa ya da binary literallerde 0x ya da 0b karakterlerini takip eden sayılar değeri belirtir. Bu yüzden bunlardan hemen sonra "_" olamaz.

En büyük int decimal literali 2^31 dir (2 milyardan biraz büyük).
Bundan büyük bir int literali compile-time hatasına yol açar.
Hexa,octal, binary ya da int literalleri 32 biti aştığı anda compile-time hatasına yol açar.

En büyük long literali 2^63 tür.
long tipinde bir decimal literali 2^63L den büyükse compile-time hatasına yol açar.
Hexa, octal ya da binary long literalleri 64 biti aştığında compile time hatasına yol açar.

Floating-point literaller
Decimal (10luk) ya da hexa(16lık) olabilir.
Ortasında "." ya da "e" karakteri bulunur.
Sonunda F ya da f karakteri varsa float tipinde, yoksa double tipindedir.
Double tipinin başına opsiyonel olarak D ya da d karakteri gelebilir.
float tipi 32-bit, double tipi ise 64-bit ile temsil edilir.
java.lang paketindeki Float.valueOf() ve Double.valueOf() metodları ile String değerinden kendi değerine çeviri yapılabilir.

Boolean literaller
boolean tipinin iki değeri vardır. Bunlar true ve false literalleridir. Tipleri daima booleandır.

Character literaller
ascii tek tırnakların içinde bulunan bir karakter ya da escape sequence dır.
Tipi daima char dır.
bir karakter literalin (ya da escape sequence in) hemen ardından tek tırnak gelmezse compile-time hatasına yol açar.
Tek tırnak açıldıktan sonra ve kapanmadan önce line terminator varsa compile-time hatasına yol açar.
(Çünkü CR ve LF input karakteri değildir.)
Not: satır sonu koymak için LFnin temsili olan '\u000a' yazmak yanlıştır çünkü unicode escape karakterleri erkenden işlenir ve bu ifade bir line terminatora dönüşür. Line terminator da input karakteri değildir, bu yüzden karakter literali geçersiz olur.
Bunun yerine '\n' escape sequence'i kullanılmalıdır.
Aynı şekilde return yazmak için de '\u000d' yerine '\r' kullanılmalıdır.

String literaller
Sıfır ya da daha fazla karakterin çift tırnak içerisine konmasından oluşur.
Daima tipi String dir.
Escape sequence barındırabilir.
Tırnaklar arasında line terminator varsa compile-time hatasına yol açar.

Uzun bir string literali + operatorü ile parçalanıp parantezlerde gruplanabilir.
Karakterde olduğu gibi string literallerde de LF ve CR nin unicode karşılıkları yerine "\n" ve "\r" kullanılmalıdır.

Bir string literali, String sınıfının bir instance'ına verilmiş bir referanstır.
Ve bir string literali daima aynı instance'a referans verir.
Çünkü string literalleri String.intern metoduyla unique instance'ları paylaşacak şekilde içselleştirilirler.

Örnekte görelim:

package testPackage;
class Test {
 public static void main(String[] args) {


  String hello = "Hello", lo = "lo";


  System.out.print((hello == "Hello") + " ");
  System.out.print((Other.hello == hello) + " ");
  System.out.print((other.Other.hello == hello) + " ");
  System.out.print((hello == ("Hel"+"lo")) + " ");
  System.out.print((hello == ("Hel"+lo)) + " ");
  System.out.println(hello == ("Hel"+lo).intern());

 }
}
class Other { static String hello = "Hello"; }


package other;

public class Other { public static String hello = "Hello"; }


Şu sonuç alınır:
true true true true false true

Buradaki her ifade bir konuya açıklık getiriyor. Şöyle ki:
1. Aynı class ve package içindeki string literalleri aynı String nesnesine referans verirler.
System.out.print((hello == "Hello") + " "); //true

2. Aynı package da farklı classlara ait string literaller yine aynı String nesnesine referans verirler.
System.out.print((Other.hello == hello) + " "); //true

3. Farklı package ve classlarda bulunan string literalleri yine aynı String nesnesine referans verirler.
System.out.print((other.Other.hello == hello) + " "); //true

4. constant expressionlarla hesaplanan stringler compile timeda hesaplanır ve literalmiş gibi davranılırlar.
System.out.print((hello == ("Hel"+"lo")) + " "); //true

5. runtime sırasında birleştirilerek hesaplanan string literali yeni yaratılır, o yüzden farklıdır.
System.out.print((hello == ("Hel"+lo)) + " "); //false

6. intern() metoduyla explicit olarak içselleştirilirse, aynı içeriğe sahip bir string literal ile aynı olur.
System.out.println(hello == ("Hel"+lo).intern()); //true

Character ve String literalleri için kullanılan Escape Sequenceları

\ b (backspace BS, Unicode \u0008)\ t (horizontal tab HT, Unicode \u0009)\ n (linefeed LF, Unicode \u000a)\ f (form feed FF, Unicode \u000c)\ r (carriage return CR, Unicode \u000d)\ " (double quote ", Unicode \u0022)\ ' (single quote ' , Unicode \u0027)
\ \ (backslash \, Unicode \u005c)


Eğer bir escape sequence ta kesme işaretini takip eden karakter şunlardan biri değilse compile-time hatasına yol açar:
b, t, n, f, r, ", ' , \, 0, 1, 2, 3, 4, 5, 6, 7

null literal
null tipinin tek değeri vardır, o da null dır.
null literali daima null tipindedir.


3.11. Separator / ayırıcılar

12 tokendan oluşurlar:
(   )   {   }   [   ]   ;   ,   .   ...   @   ::

3.12. Operatorler

38 tokendan ibarettir:
=   >   <   !   ~   ?   :   ->
==  >=  <=  !=  &&  ||  ++  --
+   -   *   /   &   |   ^   %   <<   >>   >>>
+=  -=  *=  /=  &=  |=  ^=  %=  <<=  >>=  >>>=



Hiç yorum yok:

Yorum Gönder