/RhoLP

RhoLang Lexer/Parser, copiler/interpreter frontend with excellent error messages.

Primary LanguageScalaGNU General Public License v2.0GPL-2.0

RhoLP

RhoLang Lexer/Parser, compiler/interpreter frontend with excellent error messages.

Demo

import net.golovach.rholp.lexer.RhoLexer;
import net.golovach.rholp.log.*;
import net.golovach.rholp.log.impl.*;

public class Demo {
    public static void main(String[] args) {
        String content =
                "type T = Functor[({ type λ[α] = Map[Int, α] })#λ]";
        DiagnosticListener listener = new GroupedPrinter(System.out);
        RhoLexer lexer = new RhoLexer(content, listener);

        lexer.readAll();
    }
}
NOTE
  Error code: lexer.note.identifier.like-absent-keyword
  Message:    identifier 'type' like absent keyword/type
  Line/Column: [1, 1], [1, 21], [1, 33]
  ----------
  type T = Functor[({ type λ[α] = Map[Int, α] })#λ]
  ^^^^                ^^^^        ^^^              
ERROR
  Error code: lexer.err.codepoint.illegal.unicode-1-char
  Message:    illegal 1-char unicode symbol: ''{0}'', hex = {1}, codepoint = {2}
  Line/Column: [1, 26], [1, 28], [1, 42], [1, 48]
  ----------
  type T = Functor[({ type λ[α] = Map[Int, α] })#λ]
                           ^ ^             ^     ^ 
ERROR
  Error code: lexer.err.operator.absent
  Message:    there is no '#' operator
  Line/Column: [1, 47]
  ----------
  type T = Functor[({ type λ[α] = Map[Int, α] })#λ]
                                                ^

Main classes

RhoTokenType, RhoToken, RhoLexer

public enum RhoTokenType {
    IF, ELSE, MATCH, EOF, ERROR, IDENT, LITERAL_INT, LITERAL_STRING, LITERAL_URI, ...
    
    public final RhoToken T;
    public RhoToken T(String chars) {...}
}
public class RhoToken {
    public final RhoTokenType type;
    public final String val;
    ...
}
public class RhoLexer {
    public RhoLexer(String content) {...}
    public RhoLexer(String content, DiagnosticListener listener) {...}
    public RhoLexer(String content, DiagnosticListener listener, Properties messages) {...}
    
    public RhoToken readToken() {...}
    public List<RhoToken> readAll() {...}
}

Example

List<RhoToken> tokens = asList(
        NEW.T, IDENT.T("chan"), LBRACE.T, NIL.T, RBRACE.T, EOF.T);

Is the same as

RhoLexer lexer = new RhoLexer("new chan in { Nil }");
List<RhoToken> tokens = lexer.readAll();

Diagnostic, DiagnosticListener

Diagnostic - plain ADT with error/warn/note info

public class Diagnostic {
    public static enum Kind { NOTE, WARN, ERROR }
    
    public final Kind kind;
    public final String code;
    public final String msg;
    public final String msgTemplate;
    public final List<String> msgArgs;
    //
    public final String line;
    public final int col;
    public final int len;
    //
    public final int row;
    public final int offset;
    ...
}

DiagnosticListener - interface for receiving diagnostics from tools

public interface DiagnosticListener {
    void report(Diagnostic diagnostic);
    void eof();
}

Error types

Numbers

import net.golovach.rholp.lexer.RhoLexer;
import net.golovach.rholp.log.*;
import net.golovach.rholp.log.impl.*;

public class Demo {
    public static void main(String[] args) {
        DiagnosticListener listener = new GroupedPrinter(System.out);
        RhoLexer lexer = new RhoLexer("[42L, 9999999999999999999, 1.1e-1f]", listener);
        lexer.readAll();
    }
}
ERROR
  Error code: lexer.err.literal.absent.int-L-suffix
  Message:    there is no '''l''' or '''L''' at the ent of Int literals
  Line/Column: [1, 2]
  ----------
  [42L, 9999999999999999999, 1.1e-1f]
   ^^^                               
ERROR
  Error code: lexer.err.literal.int-too-big
  Message:    too big Int literal, should be in range [-9223372036854775808, 9223372036854775807]
  Line/Column: [1, 7]
  ----------
  [42L, 9999999999999999999, 1.1e-1f]
        ^^^^^^^^^^^^^^^^^^^
ERROR
  Error code: lexer.err.literal.absent.floating-point
  Message:    there are no floating-point types
  Line/Column: [1, 28]
  ----------
  [42L, 9999999999999999999, 1.1e-1f]
                             ^^^^^^^         

String-like

import net.golovach.rholp.lexer.RhoLexer;
import net.golovach.rholp.log.*;
import net.golovach.rholp.log.impl.*;

public class Demo {
    public static void main(String[] args) {
        DiagnosticListener listener = new GroupedPrinter(System.out);
        RhoLexer lexer = new RhoLexer("['A', α, `rho:io:stdout, ]", listener);

        lexer.readAll();
    }
}
ERROR
  Error code: lexer.err.literal.absent.single-quote
  Message:    there is no 'A' single-quote literal (char) only string ("...") and uri (`...`)
  Line/Column: [1, 2]
  ----------
  ['A', α, `rho:io:stdout, ]
   ^^^    
ERROR
  Error code: lexer.err.codepoint.illegal.unicode-1-char
  Message:    illegal 1-char unicode symbol: 'α'
  Line/Column: [1, 7]
  ----------
  ['A', α, `rho:io:stdout, ]
        ^                   
ERROR
  Error code: lexer.err.literal.uri.unclosed
  Message:    unclosed uri literal
  Line/Column: [1, 10]
  ----------
  ['A', α, `rho:io:stdout, ]
           ^^^^^^^^^^^^^^^^^

Absent operators/keywords, misspelled keywords

import net.golovach.rholp.lexer.RhoLexer;
import net.golovach.rholp.log.*;
import net.golovach.rholp.log.impl.*;

public class Demo {
    public static void main(String[] args) {
        DiagnosticListener listener = new GroupedPrinter(System.out);
        RhoLexer lexer = new RhoLexer("type ~> null", listener);

        lexer.readAll();
    }
}
NOTE
  Error code: lexer.note.identifier.like-absent-keyword
  Message:    identifier 'type' like absent keyword/type
  Line/Column: [1, 1]
  ----------
  type ~> null
  ^^^^        
WARN
  Error code: lexer.warn.identifier.like-existing-keyword
  Message:    identifier 'null' like existing keyword/type 'Nil', maybe typo?
  Line/Column: [1, 9]
  ----------
  type ~> null
          ^^^^
ERROR
  Error code: lexer.err.operator.absent.arrow
  Message:    there is no arrow operator '~>' only ('<-', '<=', '=>')
  Line/Column: [1, 6]
  ----------
  type ~> null
       ^^