My favorites | Sign in
Project Logo
                
Search
for
Updated Oct 13, 2008 by st...@riverbed.demon.co.uk
UsingJelan  
Quick guide to using JELAN

How to use JELAN

The following is a quick guide to using JELAN. It assumes that the containing class static imports the LexMethods and RuleMethods classes to make the language specification succinct.

1. define the constant tokens to be generated by the lexer and analyzed by the parser:

public static final SpecialToken
    EQ              = new SpecialToken("="),
    DOT             = new SpecialToken("."),
    OPEN            = new SpecialToken("("),
    CLOSE           = new SpecialToken(")"),
    PLUS            = new SpecialToken("+"),
    MINUS           = new SpecialToken("-"),
    STAR            = new SpecialToken("*"),
    DIV             = new SpecialToken("/"),
    COMMA           = new SpecialToken(","),
    MOD             = new SpecialToken("%"),
    EXP             = new SpecialToken("^");

2. Create CharSet instances for required character sets and the graph of Lex instances defining the grammar for identifying tokens and whitespace:

private static final LexGrammar lexGrammar; 
static {
    CharSetFactory csf = new CharSetFactory();
    CharSet digit = csf.createCharSet("digit").addRange('0','9');
    CharSet idStart = csf.createCharSet("idStart").addRange('a','z').addRange('A', 'Z');
    CharSet idOther = csf.createCharSet("idOther").add(idStart).add(digit);
    CharSet whiteSpace = csf.createCharSet("whitespace").add(" \t\n\r");
    
    TokenFactory keywordFactory = KeywordFactory.keywordOrUnknown(MIN, MAX);
    Lex digits = repeat(aChar(digit),1); 
    Lex sign = choice(aChar('+'),aChar('-'));
    Lex signedInteger = seq(opt(sign), digits);
    Lex exponentIndicator = choice(aChar('e'),aChar('E'));
    Lex exponent =  seq(exponentIndicator, signedInteger);
    Lex integerPart = digits;
    Lex fractionPart = digits;
    Lex significand = choice(
            seq(integerPart, opt(seq(aChar('.'),opt(fractionPart)))),
            seq(aChar('.'), fractionPart));
    Lex bigDecimal = make(seq(significand, opt(exponent)),NumberToken.FACTORY);
    Lex keyword = make(seq(aChar(idStart),repeat(aChar(idOther))), keywordFactory);
    lexGrammar = new LexGrammar(seq(repeat(aChar(whiteSpace)),token(
            mapChoice(
                    keyword,
                    bigDecimal,
                    make(aChar('('), OPEN),
                    make(aChar(')'), CLOSE),
                    make(aChar('+'), PLUS),
                    make(aChar('-'), MINUS),
                    make(aChar('*'), STAR),
                    make(aChar('^'), EXP),
                    make(aChar('/'), DIV),
                    make(aChar('%'), MOD),
                    make(aChar(','), COMMA)
            ))));
}

3. Create the graph of Rule instances defining the grammar for analyzing the language structure from the sequence of tokens:

private static ParseGrammar parseGrammar;

static {
    // Expression grammar
    SeqRule expression = new SeqRule("expression");  // forward creation to allow recursion
    Rule signedNumber = choice(
            seq(MINUS, save(NEG_NUMBER, NumberToken.TYPE_RULE)),
            seq(opt(PLUS), save(NUMBER, NumberToken.TYPE_RULE)));
    Rule function = call(save(OPERATOR, seq(choice(MAX,MIN), OPEN, expression, COMMA, expression, CLOSE)));
    Rule primary = choice("primary", signedNumber, function, seq(OPEN, expression, CLOSE)); 
    Rule factor  = seq("factor", primary, repeat(call(seq(save(OPERATOR, EXP),primary))));
    Rule term  = seq("term", factor, repeat(call(seq(save(OPERATOR,choice(STAR, DIV, MOD)), factor))));
    expression.seq(term, repeat(call(seq(save(OPERATOR, choice(PLUS,MINUS)), term))));
    parseGrammar = new ParseGrammar(seq("grammar", start(), expression, end()));
}

4. Create a Lexer and Parser instance with the input text to analyse and call the parse method on the parser to carry out the analysis.:

public static String analyze(String text, ParseHandler handler) {
    CharSource cs = new StringCharSource(text);
    Lexer lexer = new SoftLexer(lexGrammar, cs);
    Parser parser = new SoftParser(parseGrammar, lexer, handler);
        
        try {
            parser.parse();
        }
        catch (LexException e) {
            return "Lex Error: " + e.getMessage();
        }
        catch (ParseException e) {
            return "Parse Error: " + e.getMessage();
        }
        
    }
}

The product of the analysis is delivered in the form of calls to the handler, capturing interesting tokens and structures identified during analysis. The items of interest are defined by call and save rules embedded in the grammar.

By default, if an error is found during lexing or parsing an exception is thrown. Alternatively a ParseErrorHandler can be passed to the parser which receives an indication of any error detected, and basic error recovery is attempted.

For further information on JELAN usage, javadoc or the source code is currently your best bet. For a complete simple example, see the test class riverbed.jelan.test.ExpressionEvaluator from which the above code was taken.


Sign in to add a comment
Hosted by Google Code