SableCC jest generatorem parserów dla Javy. Więcej informacji możecie znaleźć TUTAJ. W swoim artykule chciałam skupić się na konfiguracji IntelliJ, skompilowaniu prostej gramatyki napisanej właśnie w SableCC i przetestowaniu jej z wykorzystaniem własnego interpretera napisanego w Javie.
Zacznijmy od pobrania programu. TUTAJ znajdziecie najnowszą stabilną wersję sableCC, którą należy pobrać i rozpakować. Do poprawnego działania potrzebne będzie nam JDK w odpowiedniej wersji. Teraz przystąpmy do ustawień. Otwórzmy okno Project Settings (CTRL+ALT+SHIFT+S) -> zakładka Modules -> karta Dependencies. Klikając na plusa (ATL+INSERT) dodajmy plik .JAR z folderu lib w lokalizacji, w której rozpakowaliśmy paczkę z sableCC.
Teraz stwórzmy plik z gramatyką (*.sable) i dodajmy go do naszego projektu. Dla przykładu stworzyłam gramatykę (grammar.sable) bardzo prostego kalkulatora.
Package calculator ;
Helpers
digit = [‘0’ .. ‘9’] ;
add = ‘+’;
sub = ‘-‘;
div = ‘/‘;
multi = ‘*‘;
Tokens
integer = digit+;
operation = add sub div multi;
Productions
program = [left]:integer operation [right]:integer;
Co tak naprawdę znajduje się w naszej gramatyce:
- package: nazwa pakietu, w którym mają nam się tworzyć parsery i leksery po skompilowaniu gramatyki,
- helpers: pomocnicze znaki, które wykorzystujemy do tworzenia tokenów,
- tokens: podstawowe jednostki leksykalne,
- productions: reguły naszej gramatyki.
Następnie stworzymy narzędzie do kompilowania naszej gramatyki. Wejdźmy w ustawienia (CTRL+ALT+S) -> Tools -> External Tools. Klikając na plusa (ALT+INSERT) dodajmy nowe narzędzie.
Teraz ustawmy wszystkie potrzebne pola:
- Name: nazwa naszego narzędzia, np. “SableCC Compiler”,
- Program: ścieżka do pliku javaw.exe z folderu JDK,
- Parameters: -classpath <ścieżka do pliku sablecc.jar> org.sablecc.sablecc.SableCC <ścieżka do naszego pliku z gramatyką>
Mamy już wszystko, co potrzebne do skompilowania gramatyki, więc do dzieła. Wyszukujemy nasz plik z gramatyką w strukturze projektu, a następnie prawym przyciskiem myszy wybieramy ExternalTool i nasze narzędzie (SableCC Compiler).
Po skompilowaniu powinny nam się pojawić 4 nowe pakiety (analysis, lexer, parser, node) w pakiecie, który podaliśmy w pliku z gramatyką.
Ostatnim krokiem zostało napisanie kompilatora, który odczyta naszą gramatykę i zwróci odpowiedni wynik. Dodajmy nową klasę Interpreter:
package calculator.interpreter;
import calculator.analysis.DepthFirstAdapter;
import calculator.node.AProgram;
/**
* Created by CodeCouple
*/
public class Interpreter extends DepthFirstAdapter {
public void caseAProgram(AProgram node) {
String l = node.getLeft().getText().trim();
String r = node.getRight().getText().trim();
String sign = node.getOperation().getText().trim();
double left = new Double(l).doubleValue();
double right = new Double(r).doubleValue();
double result = 0;
switch(sign) {
case ("+"):
result = left + right; break;
case ("-"):
result = left - right; break;
case ("\*"):
result = left \* right; break;
case ("/"):
result = left / right; break;
}
System.out.println(left + " " + sign + " " + right + " = " + result);
}
}
Oraz klasę startową Main:
package calculator.runner;
import calculator.interpreter.Interpreter;
import calculator.lexer.Lexer;
import calculator.node.Start;
import calculator.parser.Parser;
import java.io.* ;
/**
* Created by CodeCouple
*/
public class Main {
public static void main(String[] args) {
if (args.length > 0) {
try {
Lexer lexer = new Lexer (new PushbackReader(new FileReader(args[0]), 1024));
Parser parser = new Parser(lexer);
Start ast = parser.parse() ;
Interpreter interpreter = new Interpreter () ;
ast.apply(interpreter) ;
}
catch (Exception e) {
System.out.println (e) ;
}
} else {
System.exit(1);
}
}
}
Teraz dodajmy do programu plik tekstowy expression.txt, w którym będziemy podawać wyrażenia do obliczenia:
2+2
Ustawmy parametry programu _Run->Edit Configurations…->Program arguments: <_ścieżka do naszego pliku z wyrażeniem>:
Teraz wystarczy skompilować i uruchomić program. W konsoli powinniśmy otrzymać następujący wynik: 