Thursday, September 29, 2011

ANTLR Tutorial - Expression Language

ANTLR tool is useful any time you need to create compiler, interpreter or parser of your own language. It takes so called grammar file as an input and generates two classes: lexer and parser.

This post explains what is lexer, what is parser and how to write grammar files to generate them. In the end of the post, you will be able to create a compiler into abstract syntax tree for any simple programming language.

Our example project, a boolean expression language, is written in Java and available on Github. Besides that, everything explained in this post is language independent. Grammar files are the same in all languages.

This is our second post on the topic. First post showed how to use ANTLR in a maven project, how to add error handling to it and how to create a 'Hello World' language parser in Java. Posts are independent - you do not have to read the first one to understand the second one.

Table of Contents

First part shows how to:

Second part contains:

Overview

The overview is taken from the previous article. Skip it if you wish so. ANTLR generates two classes from grammar file: lexer and parser.

Lexer runs first and splits input into pieces called tokens. Each token represents more or less meaningful piece of input. The stream of tokes is passed to parser which does all necessary work. It is the parser who builds abstract syntax tree, interprets the code or translate it into some other form.

Grammar file contains everything ANTLR needs to generate correct lexer and parser. Whether it should generate java or python classes, whether parser generates abstract syntax tree, assembler code, directly interprets code and so on. As this tutorial shows how to build abstract syntax tree, we will ignore other options in following explanations.

Most importantly, grammar file describes how to split input into tokens and how to build tree from tokens. In other words, grammar file contains lexer rules and parser rules.

Lexer - Basics

Lexer takes characters stream as an input and splits it into stream of tokens. Lexer rules define tokens, each rule represents one token. Lexer rule always starts with a token name. Token name must start with an uppercase letter and is followed by regular expression that describes it:
TokenName: regular expression;   

Regular expression grammar is described in the first part of this chapter. Second part explains how lexer works. We wont go into details, this chapter contains only necessary minimum needed to understand and use ANTLR. Finally, we show how to reference lexer rules within other rules and how to get rid of white spaces.

Regular Expressions
ANTLR lexer uses standard regular expressions grammar. Character sequence must be enclosed in '' quotes. For example, this lexer rule matches salutation 'Hello':
SALUTATION: 'Hello';   

Use '|' to express alternatives. Lexer rule matching either salutation 'Hello' or 'Bye':
HELLO_OR_BYE: 'Hello' | 'Bye';   

The dot '.' represents any character:
ANY_CHARACTER: .;   

Use parenthesis whenever you feel like. Following expressions are equivalent to previous ones:
SALUTATION: ('Hello');
HELLO_OR_BYE: (('Hello') | ('Bye'));
ANY_CHARACTER: (.); 

Use '..' to express interval. Lexer rule matching either 0 or 1 or 2 or 3 or 4 or 5 or 6 or 7 or 8 or 9:
NUMBER: '0'..'9';   

Use '+' to express repeated one or more times. Lexer rule matching any integer:
NUMBER: ('0'..'9')+;   

Of course, you can combine expressions. Lexer rule matching any integer without leading zeros:
NUMBER_NO_LEADING_ZEROS: ('0'..'9') | (('1'..'9')('0'..'9')+);   
Read it as: either one digit number or at least two digits number that does not start with a 0.

Use '*' to express repeated zero or more times. Lexer rule matching any sequence of characters (e.g. create token from whole input):
EVERYTHING: .*;   

Use '?' to express repeated zero or one times. Lexer rule matching one or two digits:
ONE_OR_TWO_DIGITS: ('0'..'9')('0'..'9')?;   

Use '~' to express inverse of a set. Lexer rule matching anything except a digit:
NOT_A_DIGIT: ~('0'..'9');   

How Lexer Works
If we ignore details, lexers algorithm is very simple. First, lexer selects the rule to be used. Second, lexer matches the selected rule with the input and outputs corresponding token. The beginning of the character stream is chopped off and lexer continues with a first step on the rest of the input.

Few things are important:
  • Lexer looks for the first longest matching rule.
  • The matching is greedy - selected token uses as many characters as possible.
  • Lexer never changes its previous decision.

First Longest Matching Rule
Lexer looks for the first longest matching rule. In other words, if multiple rules match the input, the longest one is selected. For example, both SHORTTOKEN and LONGTOKEN in the grammar
SHORTTOKEN: 'abc';
LONGTOKEN: 'abcabc';
match the beginning of the input stream abcabc. As the LONGTOKEN matches six characters and SHORTTOKEN matches only three characters, the lexer selects longer LONGTOKEN token.

If multiple rules match the same length of input, the first one is selected. The stream abc and grammar:
SHORTTOKEN: 'a';
FIRSTTOKEN: 'abc';
SAMELENGTHTOKEN: 'ab.';
would result in FIRSTTOKEN output stream.

Greedy Token Matching
The second step matches selected token with the input beginning. The matching is greedy - selected token uses as many characters as possible. As a result, following token always matches whole input:
WHOLE_INPUT: .*;

Greediness may lead to unexpected errors. Consider the grammar:
ENDING: 'bc';
REPEAT_ABC: ('abc')* 'a';
and stream abcabcabc. The beginning clearly matches the token REPEAT_ABC. The greedy algorithm matches all abc blocks available, including the last one. As the token REPEAT_ABC ends with a letter a, lexer reports an error:
line 1:9 mismatched character '<EOF>' expecting 'a'

Decision Finality
Once the lexer matched the token, it puts it to the output stream and moves forward in the input stream. It never changes previous decision. Theoretically, the grammar:
SHORT: 'aaa';
LONG: 'aaaa';
could split the input aaaaaa into token stream SHORT SHORT. However, the lexer will choose and match the longest token LONG. As the remaining input aa does not match any token, the lexer reports two errors:
line 1:4 no viable alternative at character 'a'
line 1:5 no viable alternative at character 'a'

Common Errors
The lexer chooses next token without fully matching it to the input. Once it eliminated all shorter rules, it stops looking ahead and chooses remaining longest token. If selected token does not match the input, second step reports an error.

For example, the input abcabQ and grammar:
SHORTTOKEN: 'abc';
LONGTOKEN: 'abcabc';

The token SHORTTOKEN matches 3 letters while the token LONGTOKEN matches more than 4 letters. Therefore, the first step will choose the token LONGTOKEN. Unfortunately, LONGTOKEN does not match the input:
line 1:5 mismatched character 'Q' expecting 'c'

If no token matches the input, an error is reported:
line 1:0 no viable alternative at character 'Q'

Fragments
Once you start to create real lexer rules, you may find out that some of them tend to be complicated and difficult to read.

To solve the problem, ANTLR provides a way to create helper rules. Helper rules do not represent tokens, they exist only to be referenced by other rules. All helper rules are marked with keyword fragment.

For example, we may rewrite previous:
NUMBER_NO_LEADING_ZEROS: ('0'..'9') | (('1'..'9')('0'..'9')+);   

to cleaner:
fragment DIGIT: ('0'..'9');
fragment DIGIT_NOT_ZERO: ('1'..'9');
NUMBER_NO_LEADING_ZEROS: DIGIT | (DIGIT_NOT_ZERO DIGIT+);   

Important: you can reference only fragment rules. E.g. following is wrong:
//non-fragment rule
SALUTATION: 'Hello';   
//Incorrect rule - ANTLR fails. It references non-fragment rule.
HELLO_OR_BYE: SALUTATION | 'Bye';   

White spaces
Most languages ignores all spaces, tabulators, end of line and similar characters. If you wish to do the same, add following into your grammar:
WS : ( ' ' | '\t' | '\r' | '\n' )+ { $channel = HIDDEN; }


Parser - Basics

On the surface, parser rules seems to be very similar to the lexer rules - they use the same regular expression grammar. Under the surface, they are very different. More importantly, they are used in different way.

While lexer splits input into tokens, parser translates token stream into some other structure. In our case, it will be an abstract syntax tree and each parser rule represents small sub-tree of final tree.

First part of this chapter lists differences between lexer's and parser's expression grammar. Second part explains how parser works. The rest of the chapter shows typical problems you may run into.

Parser Rules
Both lexer and parser use standard regular expression grammar to match the input.
To distinguish between lexer and parser rules in the same file, each parser rule must begin with a lower case letter:
ruleName: parser expression;   

Any Token
As parser works on the token stream, the dot '.' meaning changed. The dot inside parser rule represents any token. Following rule always matches whole token stream:
//match all tokens in the stream
everything: .*;   

Referencing
Rules for what parser rules may reference are different too:
  • Parser rule can never reference helper lexer rule (those start with keyword fragment).
  • Parser rule can reference any non-fragment lexer rules.
  • Parser rule can reference any other parser rule.

Inline Tokens
You can use character sequences inside parser rules, ANTLR automatically creates corresponding tokens:
//ANTLR automatically creates token from the ',' comma
list: LISTNAME LISTMEMBER (',' LISTMEMBER)*;   

Abstract Syntax Tree
Each parser rule corresponds to small part of final abstract syntax tree. By default, each token represents one node and all nodes are connected to the root node. Root node is dummy node - it does not correspond to any token.

For example, input lownumbers 2, 3, 4 parsed with grammar:
LISTNAME: ('a'..'z')+;
LISTMEMBER: ('1'..'9')+;
//remove whitespaces from input
WS :  ( ' ' | '\t' | '\r' | '\n') {$channel=HIDDEN;}  ;
//ANTLR automatically creates token from the ',' comma
list: LISTNAME LISTMEMBER (',' LISTMEMBER)*;   

leads to following abstract syntax tree:
null
-- lownumbers
-- 2
-- ,
-- 3
-- ,
-- 4

To suppress useless tokens in the tree, use '!' symbol after corresponding tokens:
//remove ',' comma from the output
list: LISTNAME LISTMEMBER (','! LISTMEMBER)*;   

Put '^' symbol after the node you wish to be a parent:
//remove ',' comma from the output; LISTNAME is the root
list: LISTNAME^ LISTMEMBER (','! LISTMEMBER)*;   

New abstract syntax tree has much better structure than previous one:
lownumbers
-- 2
-- 3
-- 4

How Parser Works
Parser starts knowing which rule should correspond to the input and then tries to match it to the input stream. Matching always starts from left-most element of the rule and continues to the right.

Parser invoked with a call:
//parse expression out of the token stream
GeneratedParser.expression_return ret = parser.expression();

assumes that the input contains an input matching to the 'expression' rule. Read the call as "parse the input token stream assuming that it represents an expression" or "match the expression rule to the input tone stream".

Unless configured otherwise, parser rules are expected to be unambiguous. No matter what input, parser should not be forced to make decisions or backtrack. There must be only one way how to match the input to the rule.

If the rule starts with a token, parser compares it to the first token from the input token stream. If they are different, an error is reported. Otherwise, a mapping is created and parser continues with the next rule element. For example, the parser for the 'Hello World' grammar:
expression : SALUTATION ENDSYMBOL;
would validate whether the input token stream contains a SALUTATION token followed by an ENDSYMBOL token.

If the rule starts with reference to another parser rule, parser first matches the other rule to the input beginning. The parser for a grammar:
expression : otherexpression TOKEN;
otherexpression: ... something ...
matches otherexpression to the rule beginning first. Once the matching is done, parser validates whether the rest of the input begins with a token TOKEN.

If it encounters a rule with multiple alternatives:
expression : somekindofexpression | differentexpression;
somekindofexpression: SALUTATION NAME WELCOME;
differentexpression: SALUTATION WELCOME;
parser first decides between 'somekindofexpression' and 'differentexpression' rules. To decide which rule should be used, it investigates (look ahead) incoming token stream and decides accordingly. In this case, parser reads first two tokens and depending on the second one decides which alternative to use.

If no alternative matches the input token stream, parser reports an error. An attempt to parse the expression "Tree !" with a previous grammar would lead to following error:
line 1:0 no viable alternative at input 'Tree '

This algorithm has several consequences which are discussed in following chapters.

Independence of Lexer and Parser
It it important to remember, that lexer and parser run independently. First, the lexer splits input into tokens, then parser arrange them into some other form. The lexer has no knowledge of parser rules.

Theoretically, it would be possible to split the input Hello Hello ! with grammar:
ENSYMBOL: '!';
SALUTATION: 'Hello ';
NAME: ('a'..'z'|'A'..'Z')*;
expression : SALUTATION NAME ENSYMBOL;
into parseable token stream SALUTATION NAME ENDSYMBOL where the name is 'Hello '.

However, ANTLR will not do that. Instead, the lexer will split the input into two salutation tokens followed by one ensymbol token. As the token stream SALUTATION SALUTATION ENDSYMBOL makes no sense for the parser, it will report an error:
line 1:6 mismatched input 'Hello ' expecting NAME

Start Rule
Any grammar needs so-called start rule. Start rule is a rule that is not referenced by another rule. If your grammar does not have such rule, ANTLR generator will issue a warning:
no start rule (no rule can obviously be followed by EOF)

To avoid it, add a dummy start rule to your grammar:
start_rule: someOtherRule;   

Multiple Alternatives
Some grammars tend to be unclear:
SALUTATION:'Hello ';   
NAME:('A'..'Z')('a'..'z')*;

startRule: knowperson | unknowperson;
knowperson: SALUTATION NAME*;  
unknowperson: SALUTATION;

The input Hello parsed by previous grammar could match both knownperson and unknown person alternatives. In such case, the build would fail and ANTLR maven plugin would throw an error:
warning(200): Grammair.g:27:10: Decision can match input such as "SALUTATION" using multiple alternatives: 1, 2
As a result, alternative(s) 2 were disabled for that input

error(201): Grammair.g:27:10: The following alternatives can never be matched: 2

The parser warns us, that the rule startRule is unclear. The token stream SALUTATION, e.g. input Hello , could be interpreted in two different ways. As an unknownperson salutation or as a knownperson salutation followed by zero names.

The parser decided to always use the first alternative knowperson. As a result, the alternative unknowperson will not be used.

Ambiguity
Matching between token stream and parser rule should be unambiguous. If some token stream matches the rule, there should be only one way how to match them. This rule is very similar to the previous one.

The rule expression in Hello Word grammar:
SALUTATION:'Hello word';   
ENDSYMBOL:'!';

expression : SALUTATION ENDSYMBOL;
is unambiguous. Only one token stream SALUTATION ENDSYMBOL matches the rule and there is only one way how to map it. The token SALUTATION is mapped to the SALUTATION part of the rule and the token ENDSYMBOL is mapped to the ENDSYMBOL part of the rule.

On the other hand, following grammar is ambiguous:
TOKEN:'a' | 'b';   
expression : TOKEN* TOKEN TOKEN*;

The expression ba leads to the token stream TOKEN TOKEN where the first token corresponds to the letter 'b' and the second one corresponds to the letter 'a'.

The grammar has two possible mappings between the stream and the rule:
  • The first token 'b' could correspond to the first .* part. The second token 'a' then corresponds to the middle part of the rule TOKEN.
  • The first token 'b' could correspond to the middle TOKEN part. The second token 'a' then corresponds to the ending part of the rule .*.

Unambiguity is not a hard requirement. ANTLR is be able to create both parser and lexer from the previous grammar. However, it reports warning to the console:
warning(200): Decision can match input such as "TOKEN TOKEN" using multiple alternatives: 1, 2
As a result, alternative(s) 2 were disabled for that input

You cannot avoid the issue by simple tricks such as:
TOKEN:'a' | 'b';   
expression: trick TOKEN TOKEN*;
trick: TOKEN*;

The grammar is still ambiguous and ANTLR would complain.

Left-Recursion
You have to avoid something called left-recursion. Left recursion happens when the rule references itself on the leftmost part.

Imagine grammar containing lists of letters. Any list is either a letter (e.g. f) or a list of letters followed by a symbol ',' and another letter (e.g. m, f, b). We may use following rule to describe the list:
//ANTLR fails, rule breaks no left-recursion rule
letterslist: LETTER  | letterslist ',' LETTER;   

The above rule defines letterslist ad letterslist followed by something else which breaks no left recursion rule. ANTLR would complain:
error(210):  The following sets of rules are mutually left-recursive [letterslist]

If you would try to trick it and split a rule into two:
//ANTLR fails, rule breaks no left-recursion rule
letterslist: LETTER  | longletterslist;   
longletterslist: letterslist ',' LETTER;  

ANTLR would still complain. The rule still uses itself on the left part, it just uses another rule to do so. Such rules are called "mutually left-recursive":
error(210):  The following sets of rules are mutually left-recursive [letterslist, longletterslist]

The correct solution is to rewrite the rule to another equivalent form:
//finally works
letterslist: LETTER (',' LETTER)*;   

ANTLR web contains a longer article on the topic.

Non-LL(*) Decision Error
We continue with another example of faulty grammar. It should recognize inputs for a simple calculator:
NUMBER: ('0'..'9')*;

startRule: plus;
plus: expression '+' expression | expression;   
expression : NUMBER | '(' plus ')';   

The build fails and console output contains an error:
error(211): ParserExperiments.g:45:5: [fatal] rule plus has non-LL(*) decision due to recursive rule invocations reachable from alts 1,2.  Resolve by left-factoring or using syntactic predicates or using backtrack=true option.

The line 45 contains the offending rule:
plus: expression | expression '+' expression;   

To understand the problem, you have to recall that ANTLR goes from left to right whenever parsing an input. It first decides which alternative it will use. Then it sticks with the decision. The decision is based solely on tokens in upcoming stream.

Try to simulate it on a complicated input (1+(7+(9+(0))+(5+4))+(2)). Does it match an expression alternative, or an expression + expression alternative? The only way to decide is to parse whole input to see how does parenthesis match.

By default, ANTLR will not generate parser that would do that. The error suggests three possible solutions:

Left Factoring
Rewrite plus rule in previous grammar to another form. Take the leftmost part and put it before parenthesis. Offending rule
plus: expression | expression '+' expression;   

is equivalent to the rule
plus: expression (| '+' expression);

The new rule does not have previous problem. ANTLR now knows that the input token stream continues with an expression. Once it finishes parsing it, ANTLR will check the upcoming stream to see whether it contains plus token or nothing. An empty stream would cause ANTLR end and plus token would trigger next expression parsing.

The working grammar looks like this:
NUMBER: ('0'..'9')*;

startRule: plus;
plus: expression (| '+' expression);
expression: NUMBER | '(' plus ')';

As non LL(*) decision rule may be split through multiple other complicated rules, left factoring can be surprisingly difficult. However, you should use it whenever possible.

Syntactic Predicates
Syntactic predicates are parser conditions. Use them to help parser decide which alternative it should take:
rule: (condition_rule1) => alternative1 | (condition_rule2) => alternative2 | ... ;

The parser uses conditions to decide between rule alternatives. It always starts with a left most condition. If the incoming token stream matches condition_rule1, the parser will use alternative1.If the first condition does not match, the parser tries second one.

We can use it to solve problem in our last grammar:
NUMBER: ('0'..'9')*;

startRule: plus;
plus: (expression '+' expression) => expression '+' expression | expression;   
expression : NUMBER | '(' plus ')';   

New parser will always check expression '+' expression alternative first. If it is possible to parse it out of the stream, it will go for it. Otherwise, it will assume expression alternative.

Keep in mind, that this technique may slow down parsing. The parser may be forced to pass through whole input multiple times. Given right circumstances, which are met in our example, it turns fast linear algorithm into a slow exponential one.

Backtracking
As we wrote before, ANTLR will not go back to change what it already parsed unless you explicitly allow it. To allow it, add backtrack=true into your grammar options:
options
{
  ...

  //turn on backtracking
  backtrack=true;
}

ANTLR will try each alternative from left to right on each decision point and use the one that matches. In other words, backtrack=true option automatically adds condition (syntactic predicate) wherever needed. Unfortunately, this may require multiple parse attempts on the whole input multiple times.

As this alternative turns fast linear algorithm into a slow exponential one, it should be used rarely.

More on the topic is available on ANTLR page.

Boolean Expression Language

We are finally ready to create first boolean grammar. It has three operators:
  • && - logical and,
  • || - logical or,
  • ! - logical not.

Any expression can be encapsulated in parentheses ( ). For example, this is correct boolean expression:
bwahaha && (winner || !looser)

First Version
First, we create tokens for each operator and parenthesis:
LPAREN : '(' ;
RPAREN : ')' ;
AND : '&&';
OR : '||';
NOT : '!';

then we need token for names (e.g. bwahaha, winner, looser). Names are composed of letters and digits:
NAME : ('a'..'z' | '0'..'9')*; 

Last lexer rule deals with white spaces:
WS : ( ' ' | '\t' | '\r' | '\n' )+ { $channel = HIDDEN; };

Finally, we create parser rules. Grammar explanations is written in rules comments:
//start rule
expression : andexpression;
//first, we try to match all first level && (e.g. && not included in some sub-expression) 
andexpression : orexpression (AND orexpression)*;
//second, we try to match all first level || (e.g. || not included in some sub-expression) 
orexpression : notexpression (OR notexpression)*;
//third, there may or may not be first level ! in front of an expression
notexpression : NOT atom | atom;
//finally, we found either name, or a sub-expression
atom : NAME | LPAREN andexpression RPAREN;

This is simplest possible grammar that avoids parser rules problems explained in previous chapter.

Final grammar file is available on Github. If you wish to see it in action, compiler class and unit test are available too. Full project is downloadable under 004-S005SimpleBoolean tag.

Basic AST Modifications
While previous grammar parses all needed expressions, the result tree leaves a lot to be desired. For example, bwahaha && (winner || !looser) expression is compiled into following abstract syntax tree:
null
-- bwahaha
-- &&
-- (
-- winner
-- ||
-- !
-- looser
-- )

We do not like three things about it:
  • useless parenthesis nodes,
  • dummy root node,
  • operators (&&, ||, !) should be parents of sub-trees.

Each used parser rule should correspond to a sub-tree with operator on top and sub-expressions as childs. Parenthesis nodes are useless, expression structure should be clear from the tree structure. We put '^' behind tokens we wish to be on top and '!' behind useless nodes:
//start rule
expression : andexpression;
//first, we try to match all first level && (e.g. && not included in some sub-expression) 
andexpression : orexpression (AND^ orexpression)*;
//second, we try to match all first level || (e.g. || not included in some sub-expression) 
orexpression : notexpression (OR^ notexpression)*;
//third, there may or may not be first level ! in front of an expression
notexpression : NOT^ atom | atom;
//finally, we found either name, or a sub-expression
atom : NAME | LPAREN! andexpression RPAREN!;

The above grammar, its compiler and test are available on Github under 005-S006SimpleBoolean tag.

Lets try to parse previous expression bwahaha && (winner || !looser) again:
&&
-- bwahaha
-- ||
---- winner
---- !
------ looser

Add Multi-Parameter Functions
Before we proceed to the next chapter, which explains how to do major changes in the output tree, we add built-in functions to our grammar. New grammar contains four shortcut functions:
  • all(p1, p2, ..., pn) equivalent to p1 && p2 && ... && pn,
  • atleastone(p1, p2, ..., pn) equivalent to p1 || p2 || ... || pn.
  • neither(p1, p2, ..., pn) equivalent to !p1 && !p2 && ... && !pn,
  • notall(p1, p2, ..., pn) equivalent to !p1 || !p2 || ... || !pn.

We have to create new token for each function:
ALL : 'all';
ATLEASTONE : 'atleastone';
NEITHER : 'neither';
NOTALL : 'notall';

Placing is important. We must place new tokens before NAME lexer rule. If they would be after it, lexer would never recognize them:
error(208): S007BooleanWithFunctions.g:59:1: The following token definitions can never be matched because prior tokens match the same input: ALL,ATLEASTONE,NEITHER,NOTALL

The atom is now either a name, an expression or a function:
atom : function | NAME | LPAREN! andexpression RPAREN!;

Any function is composed of its name and arguments in parenthesis. Functions name should be the root and arguments its childs:
function : functionname^ arguments;
functionname : ALL | ATLEASTONE | NEITHER | NOTALL; 

Any expression is allowed as an argument:
arguments : LPAREN! andexpression (','! andexpression)* RPAREN!;

Any interpreter of our new grammar has to deal with four new tokens in abstract syntax tree. For example, the expression all(notall(p1, p2 && p3), neither(p2,p3, atleastone(p2,p4))) compiles into following tree:
all
-- notall
---- p1
---- &&
------ p2
------ p3
-- neither
---- p2
---- p3
---- atleastone
------ p2
------ p4


Those new tokens are only shortcuts, they provide no new functionality. It would be much better, if the parser created an equivalent tree structure containing only &&, ||, ! and NAME tokens.

The grammar, its compiler and test are available on Github under 006-S007BooleanWithFunctions tag.


Rewrite Rules

Default abstract syntax tree generated by ANTLR has very unfriendly structure. It is composed of one dummy root node with all recognized tokens as childs. We have been able to suppress nodes and specify roots of sub-trees with operators '^' and '!'. Those are great, but very limited.

Rewrite rules are last ANTLR feature discussed in the post. They allow major changes in generated abstract syntax trees. The general form is simple:
ruleName: parser expression -> rewrite rule;   

Rewrite rule part behind the -> specifies how exactly should final abstract syntax tree look like. They can customize abstract syntax tree structure, add new tokens into it or suppress some tokens.

Tree Structure
Use ^(...) syntax to specify desired tree structure. The parenthesis contains tokens and parser rules referenced in regular expression before ->.
rule: some TOKEN and other Elements -> ^(TOKEN and other DUMMY Elements);   

Parser takes everything in the parentheses and creates sub-tree from it. The first element in parenthesis one must be a token. It becomes the root of created sub-tree. All following elements are childs and sub-sub-trees:
TOKEN 
-- and 
-- other 
-- DUMMY   
-- Elements  

The ^(...) parenthesis does not have to contain all elements matched in the parser expressions. Ommited elements are left from the result tree. Referenced parser rule some in the above example is missing from final tree.

The ^(...) may contain dummy tokens - tokens missing from the left rule part. The example tree contains a child node with dummy token DUMMY.

Simple Example
The rule for binary && operator:
binaryAnd: expression AND^ expression;   
can be rewritten to equivalent rule with ^(...):
binaryAnd: expression AND expression -> ^(AND expression expression);   

Nested Example
Of course, you can nest ^(...). For example, slightly longer rule:
binaryAnd: expression AND^ expression AND^ expression;   
is equivalent to following rule:
binaryAnd: expression AND expression AND expression -> ^(AND expression ^(AND expression expression));   

ANTLR keeps order of elements. E.g. the first expression element on the left part correspond to first expression element after the ->. Second expression element on the left part correspond to second expression element after the -> and so on.

The input bwahaha && something && else results in following tree:
&&
-- bwahaha
-- &&
---- something  
---- else  

Labels
You can assign labels to the elements on the left and use those labels in the rewrite rule. Labels are particularly usefull if you need to do complicated transformations.

Reordering
The input bwahaha && something && else parsed with following expression:
binaryAnd: a=expression AND b=expression AND c=expression -> ^(AND $c ^(AND $b $a));   
results in reordered tree:
&&
-- else  
-- &&
---- something  
---- bwahaha

Multiple Elements
Use += to assign the same label to multiple elements. The label then contains a list of elements. When referenced, labels are used in the same order as they appeared in the input. Following rules are equivalent:
binaryAnd: expression AND expression AND expression -> ^(AND expression expression expression);   

binaryAnd: a+=expression AND a+=expression AND a+=expression -> ^(AND $a $a $a);  

The input bwahaha && something && else parsed with previous two expressions leads to a flat tree:
&&
-- bwahaha
-- something  
-- else  

Flush List
It is possible to flush out all list elements with operators + and *. Next two rules are equivalent to previous two rules:
binaryAnd: a+=expression AND a+=expression AND a+=expression -> ^(AND $a+);
binaryAnd: a+=expression AND a+=expression AND a+=expression -> ^(AND $a*);

The input bwahaha && something && else is still leads to the same flat tree:
&&
-- bwahaha
-- something  
-- else  

Of course, the label can contain any number of elements. The previous rule is able to handle two ANDs in a row. We can change it to handle at least two ANDs:
//flat tree from at least two expressions
binaryAnd: a+=expression (AND a+=expression)+ -> ^(AND $a*);   

The rule parses any number of AND, but the tree from bwahaha && something && else does not change:
&&
-- bwahaha
-- something  
-- else  

The difference between + and * operators is simple: * is able to handle an empty list while + throws a RewriteEarlyExitException exception in that case.
//incorrect, fails if faced with one member long list 'hello'
list:  b=expression (',' a+=expression)* -> ^($b $a+);      
//correct expression
list:  b=expression (',' a+=expression)* -> ^($b $a*);      

Repeat Structures
Operator plus + and asterisk * do not have to be directly behind the list. It is possible to repeat any structure surrounding repeated list label.

For example, you can use it to add dummy token on top of each sub-tree from previous rule:
binaryAnd: a+=expression (AND a+=expression)+ -> ^(AND ^(DUMMY $a)+);
binaryAnd: a+=expression (AND a+=expression)+ -> ^(AND ^(DUMMY $a)*);

This is resulting tree:
&&
-- DUMMY
---- bwahaha
-- DUMMY
---- something  
-- DUMMY
---- else  

Conditions
Each rule may have multiple rewrite rules. Which one is used depends on {condition}? condition. In general, the rule have the form:
ruleName: parser expression 
  -> {condition1}? rewrite rule 1 
  -> {condition2}? rewrite rule 2 
  -> ...
  -> {conditionn}? rewrite rule n 
  -> falback rewrite rule

Condition is any valid expression in whatever programming language is used. You can reference labels using usual $label syntax.

For example, we can enhance 'flat tree from at least two expressions' to 'flat tree from any number of expressions (including one)'. BinaryAnd will match any number of expressions. First expression is labeled b, the rest goes to the list a:
//flat tree from any number of expressions (including one)
binaryAnd: b=expression (AND a+=expression)* 

If there are no additional arguments, e.g. the list a is empty, binaryAdd is equivalent to its first argument b:
//if the list $a is empty, use only first argument 
  -> {$a==null || $a.isEmpty()}? $b

If it is not empty, create usual flat tree:
//otherwise create flat tree from all arguments
  -> ^(AND $b $a*);

This is final rule:
//flat tree from any number of expressions (including one)
binaryAnd: b=expression (AND a+=expression)* 
  //if the list $a is empty, use only first argument 
  -> {$a==null || $a.isEmpty()}? $b
  //otherwise create flat tree from all arguments
  -> ^(AND $b $a*);

Abstract syntax tree generated from single expression bwahaha is:
bwahaha

and abstract syntax tree generated from bwahaha && winner && looser is:
AND
-- bwahaha
-- winner
-- looser


Multi-Parameter Functions With Rewrite Rules

Finally, we are ready to add some reasonable structure to our multi-parameters functions grammar. Recall, that our grammar has four functions:
  • all(p1, p2, ..., pn) equivalent to p1 && p2 && ... && pn,
  • atleastone(p1, p2, ..., pn) equivalent to p1 || p2 || ... || pn.
  • neither(p1, p2, ..., pn) equivalent to !p1 && !p2 && ... && !pn,
  • notall(p1, p2, ..., pn) equivalent to !p1 || !p2 || ... || !pn

and its abstract syntax tree should hide existence of all four new keywords.

First, the original general function:
//original functions definition with complicated tree
function : functionname^ arguments;
functionname : ALL | ATLEASTONE | NEITHER | NOTALL; 
arguments : LPAREN! andexpression (','! andexpression)* RPAREN!;

is split into multiple concrete functions:
//function definition
function : allFunction | ateastoneFunction | neitherFunction | notallFunction;
//concrete functions
allFunction : ...;
ateastoneFunction : ...;
neitherFunction : ...;
notallFunction: ...;

The function all may have either one or multiple arguments. We use rewrite rules conditions to generate different trees in each case:
//the function all(p1, p2, ..., pn) is equivalent to p1 && p2 && ... && pn
allFunction : ALL LPAREN b=andexpression (',' a+=andexpression)* RPAREN 
  //if the list $a  is empty, use only first argument 
  -> {$a==null || $a.isEmpty()}? $b
  //otherwise create a flat tree 
  -> ^(AND $b $a*);

Abstract syntax tree generated from all(bwahaha) is:
bwahaha

and abstract syntax tree generated from all(bwahaha, winner, looser) is:
AND
-- bwahaha
-- winner
-- looser

The function atleastone is almost the same as the previous all function. Simply replace && operator with || operator:
//the function ateastone(p1, p2, ..., pn) is equivalent to p1 || p2 || ... || pn
atleastoneFunction : ATLEASTONE LPAREN b=andexpression (',' a+=andexpression)* RPAREN 
  //if the list $a is empty, use only first argument 
  -> {$a==null || $a.isEmpty()}? $b
  //otherwise create a flat tree 
  -> ^(OR $b $a*);

The function neither copies all function, except that there is a dummy root node NOT ! in front of each argument:
//the function neither(p1, p2, ..., pn) is equivalent to !p1 && !p2 && ... && !pn
neitherFunction : NEITHER LPAREN b=andexpression (',' a+=andexpression)* RPAREN 
  //if the list $a is empty, use only first argument 
  -> {$a==null || $a.isEmpty()}? ^(NOT $b)
  //otherwise create a flat tree 
  -> ^(AND ^(NOT $b) ^(NOT $a)*);

Abstract syntax tree generated from neither(bwahaha) is:
NOT
-- bwahaha

and abstract syntax tree generated from neither(bwahaha, winner, looser) is:
AND
-- NOT
---- bwahaha
-- NOT
---- winner
-- NOT
---- looser

Finally, the function notall copies atleastone function. Of course, there is a dummy root node NOT ! in front of each argument:
//the function notall(p1, p2, ..., pn) is equivalent to !p1 || !p2 || ... || !pn
notallFunction : NOTALL LPAREN b=andexpression (',' a+=andexpression)* RPAREN 
  //if the list $a is empty, use only first argument 
  -> {$a==null || $a.isEmpty()}? ^(NOT $b)
  //otherwise create a flat tree 
  -> ^(OR ^(NOT $b) ^(NOT $a)*);

Abstract syntax tree generated from notall(bwahaha) is:
NOT
-- bwahaha

and abstract syntax tree generated from notall(bwahaha, winner, looser) is:
OR
-- NOT
---- bwahaha
-- NOT
---- winner
-- NOT
---- looser

As usually, complete grammar including compiler and test case are available on Github under 007-S008FunctionsNiceTree tag.

End

Depending on the language you try to create, parser construction may be very easy or difficult. As the topic is very broad, we covered only basics needed to create an abstract syntax tree for an expression in a simple expression language.

Little knowledge of antlr may turn some time-consuming tasks into easy and fast exercices. We hope that this post gave you basic idea of what is possible and how to achive it.

389 comments:

«Oldest   ‹Older   201 – 389 of 389   Newer›   Newest»
rdsraftaar said...

Evolution may be the mantra of bringing your company into the pinnacle of success. As time passes it is always required to bring new and positive changes in one’s business in order not to let your company methodology get rusted. QuickBooks 2019 has introduced newer and more effective and amazingly functional features in every its versions and along with this our QuickBooks Upgrade Support Phone Number has furnished aid to all or any the QuickBooks 2019 users.

Durai Moorthy said...

A very nice post. Thanks for sharing such a piece of valuable information...
AWS Training in Marathahalli
AWS Training in Bangalore
RPA Training in Kalyan Nagar
Data Science with Python Training Bangalore
AWS Training in Kalyan Nagar
RPA Training in bellandur
Data Science Training in bellandur
Data Science Training in Kalyan Nagar

JimGray said...

Speak to us at Quickbooks contact number support whenever you feel the requirement for in-depth technical assistance into the software. Our technical backing team can perform resolving your QuickBooks POS issues in a few minutes. Keep your retail business more organized with team offered by QuickBooks POS Support contact number. If you are the main one trying to hire anyone to manage your QuickBooks POS software for you then our QuickBooks Point Of Sale Support will be your one-stop destination. Yes, by dialling, you could get your QuickBooks POS issues resolved, files repaired and queries answered by experts.

QuickBooks Payroll Support Phone Number said...

Their pre-preparedness helps them extend their hundred percent support to any or all the entrepreneurs along with individual users of QuickBooks.
As tech support executives for QuickBooks Support Numberwe assure our twenty-four hours a day availability at our technical contact number.

Shubh said...

Download Sketch 2018 Hindi Dubbed Full Movie

askmetraveller said...

Thanks for posting useful information.You have provided a nice article, Thank you very much for this one. And I hope this will be useful for many people.. and I am waiting for your next post keep on updating these kinds of knowledgeable things...Really it was an awesome article...very interesting to read..please sharing this information......I think your suggestion would be helpful for me. I will let you know if its work for me too. Thanks and keep post such an informative blogs.

Bryan Willson said...

Any errors connected with QuickBooks software may damage your computer data you could fix them through QuickBooks tech support team Phone Number and QuickBooks Desktop Support Number will always be there to greatly help. We solve, manage, which help you to definitely cope with any technical problems and guarantees that this program is effective.

QuickBooks Payroll Support said...

DIAL QuickBooks Payroll Support Number QuickBooks is the supreme software of accounting for managing the financial health of the business. The trend of the marketplace has modified with its introduction. It provides you the entire image of business insights in order that you’ll strategize for future prospective consequently for your business.

Blogsilly said...

Our research team at QuickBooks Canada Tech Support Number is dependable for most other reasons as well. We have customer care executives which are exceptionally supportive and pay complete awareness of the demand of technical assistance made by QuickBooks users. Our research team is always prepared beforehand because of the most appropriate solutions which are of great help much less time consuming. Their pre-preparedness helps them extend their hundred percent support to any or all the entrepreneurs along with individual users of QuickBooks.As tech support executives for QuickBooks, we assure our twenty-four hours a day availability at our technical contact number.

jaanu said...

It should be noted that whilst ordering papers for sale at paper writing service, you can get unkind attitude. In case you feel that the bureau is trying to cheat you, don't buy term paper from it.
digital marketing course

jameswill11 said...

QuickBooks Support Phone Number has completely transformed the way people used to operate their business earlier. To get familiar with it, you ought to welcome this positive change. Supervisors at QuickBooks Support Phone Number have trained all of their executives to combat the issues in this software.

tom wick said...

This sort of bookkeeper services accepts the accountability with their activities. But, you won’t find any flaws in their services! Good and reliable bookkeeper builds quality relationship together with customers. Friendly relationships will enable business people to inquire of the professional for possible ways to develop their businesses. for any help dial QuickBooks Technical Support Number

QuickBooks Payroll Support Phone Number said...

QuickBooks Payroll Support Phone Number will be the toll-free level of where our skilled, experienced and responsible team can be obtained 24*7 at your service. There are a selection of errors that pop up in QuickBooks Payroll which are taken care of by our highly knowledgeable and dedicated customer support executives. There are several regularly occurring Payroll errors of the software which may be of a little assist to you.

Benish said...

Its really awesome post..Thanks for sharing..
Python training in Chennai/
Python training in OMR/
Python training in Velachery/
Python certification training in Chennai/
Python training fees in Chennai/
Python training with placement in Chennai/
Python training in Chennai with Placement/
Python course in Chennai/
Python Certification course in Chennai/
Python online training in Chennai/
Python training in Chennai Quora/
Best Python Training in Chennai/
Best Python training in OMR/
Best Python training in Velachery/
Best Python course in Chennai/

Mathew said...

And along with support for QuickBooks, it is much simpler to undertake all the tools of QuickBooks in a hassle-free manner. Below is a listing of several Intuit QuickBooks Tech Support Number errors that one may meet with when you're using it. Have a glimpse at it quickly.

jim carter said...

Let’s talk about our QuickBooks Enterprise tech support that will be quite exciting for you all. The advanced QuickBooks Desktop App for QuickBooks Enterprise Support Phone Number can now work as an ERP system beneficial for medium scale businesses. QuickBooks Desktop Enterprise is not alike to pro, premier & online versions. Capacity and capability can be the reason behind this.

James077 said...

you ought to additionally get active support services for your code that square measure obtainable 24/7. If for those who run into any QuickBooks errors or problems or would really like any facilitate, you’ll dial the direct line variety to succeed in the QuickBooks 24/7 Customer Service Number specialists.

Jamess said...

Quickbooks online payroll support number provides 24/7 make it possible to our customer. Only you need to do is make an individual call at our toll-free QuickBooks Payroll Tech Support Phone Number. You could get resolve all the major issues include installations problem, data access issue, printing related issue, software setup, server not responding error etc with this QuickBooks payroll support team.

Blogsilly said...

Our research team at QuickBooks Support Number is dependable for most other reasons as well. We have customer care executives which are exceptionally supportive and pay complete awareness of the demand of technical assistance made by QuickBooks users. Our research team is always prepared beforehand because of the most appropriate solutions which are of great help much less time consuming. Their pre-preparedness helps them extend their hundred percent support to any or all the entrepreneurs along with individual users of QuickBooks.As tech support executives for QuickBooks, we assure our twenty-four hours a day availability at our technical contact number.

QuickBooks Payroll Support Phone Number said...

While creating checks while processing payment in QuickBooks Basic Payroll Support Number online, a couple of that you have a successful record of previous payrolls & tax rates. That is required since it isn’t an easy task to create adjustments in qbo in comparison to the desktop version. The users who are able to be using QuickBooks very first time, then online version is a superb option.

JimGray said...

Quickbooks payroll software really helps to meet with the challenges like decreasing the operational cost, provides customisable support according to the payroll needs and easy option of most of the employees. Quickbooks payroll software helps in providing automation of payroll processing thereby highly helps in elimination regarding the need of manual calculations and save times. Quick books helps the users to eliminate payroll redundancies and inaccuracies. If you face any problem , errors , issue using the Quickbooks Enhanced Payroll call the QuickBooks Payroll Support

Blogsilly said...

Our QuickBooks Technical Support is obtainable for 24*7: Call @ QuickBooks Technical Support contact number any time.Take delight in with an array of outshined customer service services for QuickBooks via QuickBooks Technical Support Phone Number at any time and from anywhere.It signifies that one can access our tech support for QuickBooks at any moment. Our backing team is dedicated enough to bestow you with end-to-end QuickBooks solutions when you desire to procure them for every single QuickBooks query.

Mathew said...

QuickBooks Enterprise Technical Support Number Is A Software Platform On Which A Person Can Manage Different Financial Needs Of An Enterprise Such As For Instance Payroll Management, Accounts Management, Inventory And Many Other Things.

Robert Lee said...

Awesome post, Thanks for sharing useful information article keep sharing helpful info.this is a very knowledgeable blog post for us, thank for sharing your knowledge.
Keto BodyTone
Keto BodyTone
Keto BodyTone
Keto BodyTone
Keto BodyTone

kevin2 said...

Payroll functions: A business must notice salaries, wages, incentives, commissions, etc., QuickBooks Customer Support Phone Number has paid towards the employees in a period period. Above all may be the tax calculations needs to be correct and based on the federal and state law.

Bryan Willson said...

It’s extraordinary for organizations which report using one basis & record assesses an extra.
Search towards the chart of accounts is simple to manage with added search bar right in the chart of accounts. For better information, you could call at QuickBooks Enterprise Support Phone Number.

kevin32 said...

QuickBooks Support Number Self-Employed is created specifically for Independent Contractors. It enables you to track miles, your revenue & expense, record invoices and accept payments, and run reports.

QuickBooks Support Phone Number said...

As well as using this, many QuickBooks users encounter some heavy and unexpected errors such as QuickBooks Support Phone Number and so many more. So in such form of tough situations, you can rely on on-to the best QuickBooks customer support that you must subscribe to enable you to use our all amazing support services when you face error or any type of issue.

QuickBooks Payroll Support said...

QuickBooks Pro has made it easy for a business person to create an invoice, track expense and automatically backup data to avoid losing them at any cost. QuickBooks Tech Support Number is available 24/7 to provide much-needed integration related support. This particular QuickBooks product can be installed up to three computers and can be simultaneously accessed to efficiently maintain the accounts.

steffan said...


The QuickBooks Support Number could be reached all through night and day additionally the technicians are very skilled to deal with the glitches that are bugging your accounting process.

Blogsilly said...

We have a team of professionals that have extensive QB expertise and knowledge on how to tailor this software to any industry. Having performed many QB data conversions and other QB engagements, we have the experience that you can rely on. To get our help, just dial the QuickBooks Support Phone Number to receive consultation or order our services. We will help you streamline and simplify the accounting, reporting and tracking so that managing your company’s finances would be much easier. Also, we guarantee to maintain anonymity and high level of security while handling issues related to QB software use. Our QuickBooks customer service is available to you at any time. Get in touch with us by using a phone number or email indicated on the Quickbooks Support site. We will be happy to assist you with any question about QuickBooks you might have.

Thomas said...

Single diversion tickets can be acquired by means of Ticketmaster utilizing Visa or Mastercard. While saving your ticket on the web, you can pick your favored seat through the intuitive outline. Ticket holders can sign in to record to see and oversee same.
https://www.vodafone-customer-care-number.in/maharashtra-goa/
customer care number vodafone

kevin32 said...

Our instantly QuickBooks Support team is perfect in taking down every QuickBooks error. We could assure you this with a guarantee. Call our QuickBooks Support contact number. Our QuickBooks Tecnical Support Number team will attend you.

QuickBooks Payroll Support said...

QuickBooks Premier is a popular product from QuickBooks known for letting the business people easily monitor their business-related expenses; track inventory at their convenience, track the status of an invoice and optimize the data files without deleting the data.QuickBooks Support is available 24/7 to provide much-needed integration related support. While integrating this particular product with other Windows software like MS Word or Excel, certain errors might happen and interrupt the file from opening up.

Blogsilly said...

We have a team of professionals that have extensive QB expertise and knowledge on how to tailor this software to any industry. Having performed many QB data conversions and other QB engagements, we have the experience that you can rely on. To get our help, just dial the QuickBooks Technical Support Number to receive consultation or order our services. We will help you streamline and simplify the accounting, reporting and tracking so that managing your company’s finances would be much easier. Also, we guarantee to maintain anonymity and high level of security while handling issues related to QB software use. Our QuickBooks customer service is available to you at any time. Get in touch with us by using a phone number or email indicated on the Quickbooks Support site. We will be happy to assist you with any question about QuickBooks you might have.

QuickBooks Payroll Support said...


Manage all your valuable financial requirements on your own fingertips with QuickBooks enterprise. Maintain your accounts, payroll, inventory and more in an organized way. The QuickBooks Technical Support Number is toll-free and also the professional technicians handling your support call will come up with an immediate solution that may permanently solve the glitches. Get loaded with some of the most brilliant accounting features such as for example creating a company plan, easy remote access, inventory tracking and much more.

Unknown said...

Earn Paytm Cash Online

gautham said...

i have learn a lot through this course on cyber security online training hyderabad

Rajesh Anbu said...

I like your post.It is very useful to us...

Data Analytics with R Training in Bangalore
Hadoop training center in bangalore
AWS training in bangalore
AWS training in marathahalli
Python training in marathahalli
Hadoop training in marathahalli
Python training in bangalore

gautham said...

hacking tutorial hacking course online

Archana said...

Thank you so much for sharing this excellent information. Your article is amazing. Good to discover your post. We are the Best

salesforce Training in Bangalore
uipath Training in Bangalore
blueprism Training in Bangalore

gautham said...

Looks like amazing sql online training

prince said...

Thanks for the informative article. This is one of the best resources I have found in quite some time. Nicely written and great info. I really cannot thank you enough for sharing.
Also read: clash of clans mod apk

ExcelR Solutions said...

nice blog very helpfull. keep going. Machine Learning Course Bangalore

아리 said...

Now downloading of any Tamil movie is simple through isaimini Tamil rockers it will really help you a lot.

Admin said...

Thank you for sharing valuable information. Thanks for provide great informatic blog, really nice required information & the things i never imagined. Thanks you once agian Download CamScanner Pro Apk

dezayno said...

At Dezayno, we understand that it is the responsibility of each of us, as individuals and together, to protect the environment, and provide safe, environmentally friendly products for the world. This is why all of our Premium Limited Edition T-Shirts are made on Non-GMO 100% Certified Organic Cotton that is grown on organic farms. Our Natural Organic ringspun cotton t-shirts are not only Eco friendly, they are extremely comfortable and designed to last. Our textile and manufacturing facilities have been carefully selected to help Dezayno source the world's best all-natural, organic materials for our premium apparel brand. Organic Clothing

gautham said...

Nice stuff azure certification

gautham said...

Thanks for sharing amazing post blockchain course

Anonymous said...


Thanks for Sharing This Article.It is very so much valuable content. I hope these Commenting lists will help to my website
top angular js online training

MovieNews said...

Hello, friends, I am movies lover and write reviews on the latest Movies Online on hdmovieswap.com

Raj Sharma said...

Awesome post. Good Post. I like your blog. You Post is very informative. Thanks for Sharing.
Machine Learning Training in Noida
Data Science Training in Noida
Ethical Hacking Training in Noida
Digital Marketing course in Noida
Web Designing Training in Noida

gautham said...

this is the awesome post
blockchain training

jefrin said...

Great Blog post very useful info thanks for this post ....
power bi training chennai |Power bi training class chennai

Training for IT and Software Courses said...

Wow it is really wonderful and awesome thus it is veWow it is really wonderful and awesome thus it is very much useful for me to understand many concepts and helped me a lot. it is really explainable very well and i got more information from your site.ry much useful for me to understand many concepts and helped me a lot. it is really explainable very well and i got more information from your site.python training in bangalore

salman chattels said...

Great Share!
warehouse for rent in chennai
Godowns for rent in chennai
Factory shed for rent in chennai

Vibha said...

Thank you for such a nice article keep posting, I am a Regular Visitor of your website.
lucent gk pdf

nowfirstviral said...

Thank you for this post. i found this amazing 먹튀보증업체

nowfirstviral said...

very nice website 먹튀보증업체

Ritu said...

I found your article on Google when I was surfing, it is written very nicely and is optimized .Thank you I visit your website regularly.
Lucent GK

Training for IT and Software Courses said...

Congratulations This is the great things. Thanks to giving the time to share such a nice information.aws training in bangalore

Ritu said...

Nice information, you write very nice articles, I visit your website for regular updates.
haircut for girls

Kevin said...

Great content.
Inspirational Status on life

nowfirstviral said...

thank you very much for share this wonderful article 토토사이트

Real Time Experts said...

Enjoyed reading the article above, really explains everything in detail, the article is very interesting and effective. Thank you and good luck.

Best SAP EWM Training in Bangalore - Learn from best Real Time Experts Institutes in Bangalore with certified experts & get 100% assistance.

svrtechnologies said...

thanks for sharing such an informative stuff...

apex tutorial

Softgen Infotech said...

I am happy for sharing on this blog its awesome blog I really impressed. thanks for sharing.

Get Best SAP MM Training in Bangalore from Real Time Industry Experts with 100% Placement Assistance in MNC Companies. Book your Free Demo with Softgen Infotech.

Jack sparrow said...

This post is really nice and informative. The explanation given is really comprehensive and informative . Thanks for sharing such a great information..Its really nice and informative . Hope more artcles from you. I want to share about the best java video tutorials with free bundle videos provided and java training .

eTechno Soft Solutions said...

Thank you for sharing such a nice post!

Looking for SAP S4 HANA Simple Finance Training in Bangalore , learn from eTechno Soft Solutions SAP S4 HANA Simple Finance Training on online training and classroom training. Join today!

Vinay kumar Nevatia said...

Manoj Malviya kolkata ips


Manoj Malviya kolalata ips


Vinay kumar Nevatia said...

Dharmaraj Meena - NPA, Hyderabad - Assistant Superintendent of Police, Bhopal

Manoj Malviya kolalata ips

shree said...

very nice...
inplant training in chennai
inplant training in chennai for it.php
namibia web hosting
norway web hosting
rwanda web hosting
spain hosting
turkey web hosting
venezuela hosting
vietnam shared web hosting


Super Fast Keto Boost reviews said...

Super Fast Keto Boost is an advanced weight loss formula that can help you burn fat more effectively than ever. It is formulated in such a way that it can help your body reach a state of ketosis faster. The active component in Super Fast Keto Boost is a fat-burning ketone called BHB (beta-hydroxybutyrate). This ketone is one of the first substrates that helps your body to enter a state of ketosis faster. Visit On http://www.choosetolose.net/super-fast-keto-boost-reviews-benefits-and-side-effects/

Anonymous said...

Thanks for Sharing This Article.It is very so much valuable content. I hope these Commenting lists will help to my website
best workday online training
workday studio online training
top workday studio online training

tejaswini said...

You have done a amazing job with you websitebig data malaysia
data scientist certification malaysia
data analytics courses

Ravi Kumar said...


It Was Nice Experience To Visit To Your WebsiteWhatsApp Group Links

svrtechnologies said...

thanks for the informative stuff...

Tableau Online Training

Gurvinder sir said...

Thank you for this post.This is very interesting information for me.

ccc online test 2020

Geometry Dash said...

A befuddling web diary I visit this blog, it's incredibly grand. Strangely, in this present blog's substance made motivation behind fact and sensible. The substance of information is instructive

Geometry Dash MOD APK
CCleaner Pro APK
AVG Cleaner Pro
AVG Cleaner Pro
AVG Cleaner Pro

Bale said...

Path of Exile is completely free of game content. In Path of Exile, the player was driven out of his home and came to the corrupt and deeply hostile war continent. The POE currency is the core of the economic system. It contains a variety of unique POE Currency, which is the “money” used by players in the Path of Exile! Each POE Currency allows for a specific role in the reorganization of the character's passive ability tree in the case of a device that creates and enhances the character or in the case of a regrettable orb. Path of Exile Currency may be identified as a drop from a monster or box, purchased directly from various suppliers in the town, or by using a supplier recipe system to trade a specific configuration of the item to any town supplier.

POE Currency, the purchase path for orbs and items. Delivered immediately, at a low price, and supports POE. Add your POE Items along with MMOAH. They has a large number of common and special currencies, and their inventory is always large. For example, they have worship, chaos, the jeweler's orb, the fusion of the orb and the regret of the orb. You can also get a discount pack that can save you about 5-20%, these packs usually contain worship, chaos and sacred orbs. Come and buy it!

tejaswini said...

I have express a few of the articles on your website now, and I really like your style of blogging. I added it to my favorite’s blog site list and will be checking back soon…big data in malaysia
data scientist course malaysia
data analytics courses
360DigiTMG

Techtrick said...

i found this amazing. 9xmovies

khatrimaza

movierulz

tamilrockers

Admin said...

<a href="http://biggbossmalayalamvote.site/'>big boss malayalam</a>

tejaswini said...

This is my first time i visit here. I found so many entertaining stuff in your blog, especially its discussion. From the tons of comments on your articles, I guess I am not the only one having all the leisure here! Keep up the good work. I have been meaning to write something like this on my website and you have given me an idea.big data analytics malaysia
data science course malaysia
data analytics courses
360DigiTMG

sravs said...

Very good post...

aws training hyderabad

.
Machine learning course

datasciencecourse said...

I am looking for and I love to post a comment that "The content of your post is awesome" Great work!

digital marketing course
For more info :

ExcelR - Data Science, Data Analytics, Business Analytics Course Training in Mumbai

304, 3rd Floor, Pratibha Building. Three Petrol pump, Opposite Manas Tower, LBS Rd, Pakhdi, Thane West, Thane, Maharashtra 400602
18002122120

sravanthi said...

very good post....

AWS course

click here for more info

svrtechnologies said...

Pretty article! I found some useful information in your blog, it was awesome to read, thanks for sharing this great content to my vision, keep sharing.... abinitio online training

nowfirstviral said...

두 번째 베팅 라운드는 플레이어가 딜러의 왼쪽에 앉아 시작합니다. 사람들은 작은 내기를 기준으로 금액을 인상해야합니다. 이 라운드에서도 3 번 베팅 할 수 있습니다. 사람들은 또한 베팅 가능성을 거절해야하지만 게임에 남아있는 체크인을 선택할 수 있습니다 우리카지노.

Techno Blogging said...

hey as we know that the snaptube is most famous for downloding the videos and movies. Let know more about Snaptube Apk latest

svrtechnologies said...

I am inspired with your post writing style & how continuously you describe this topic. After reading your post, thanks for taking the time to discuss this, I feel happy about it and I love learning more about this topic.... erp tutorial

gadgetsdora said...

This excellent website definitely has all of the info I needed concerning this subject and didn’t know who to ask.
news

AlisonKat said...

Nice information, you write very nice articles, I visit your website for regular updates.
rajnandgaon

info said...

It’s nearly impossible to find well-informed people in this particular topic, however, you seem latest like you know what you’re talking about! Thanks

svrtechnologies said...

Thanks for sharing such a great information..
azure online training
Its really nice and informative..

nowfirstviral said...

i like so much your content please keep it up 바카라사이트

Unknown said...

im fully satisfied with this artcle. its really amazing.
https://chserialkey.com/draftsight-crack/

Unknown said...

such a great article. its amazing
https://cracksmad.com/spyhunter-5-crack/

Unknown said...

i appreciate this article. its really fantastic
https://letcracks.com/smadav-pro-crack-full-keygen/

Unknown said...

wow, great work
https://shehrozpc.com/4k-video-downloader-crack/

Unknown said...

cool site, i like it
https://zsactivationkey.com/roguekiller-crack/

Aruna said...

Thanks for the informative article About Angular Js. This is one of the best resources I have found in quite some time. Nicely written and great info. I really cannot thank you enough for sharing.

Java training in chennai | Java training in annanagar | Java training in omr | Java training in porur | Java training in tambaram | Java training in velachery

Indhu said...

Thanks for sharing this informations.
ios training in coimbatore

amazon web services training in coimbatore

aws training in coimbatore

big data training in coimbatore

hadoop training in coimbatore

C and C++ training in coimbatore

RPA Course in coimbatore

zoe said...

Thank you for this great article i learn a lot from your article keep it up.
baby shark pillow case
baby yoda pillow case
burlap sofa pillow

david said...

Great Post. really awesome to read.

Data Science Training Course In Chennai | Data Science Training Course In Anna Nagar | Data Science Training Course In OMR | Data Science Training Course In Porur | Data Science Training Course In Tambaram | Data Science Training Course In Velachery


kevin32 said...


A few of the users facing errors when using QuickBooksone particular error is QuickBooks Error -6000. In this blogyou’ll learn steps to repair this error. If you're not interested in doing its ownyou can easily take services from our Support For QuickBooks Error team.
Visit: https://www.dialsupportnumber.com/quickbooks-6000-series-error/

Angela said...

Thank you for such a nice article keep posting, I am a RegularVisitor of your website.
indiapostgdsonline phase 4 reg combinednew

Angela said...

Nice information, you write very nice articles, I visit your website for regular updates.
hindi grammar pdf

subha said...


I like your post very much. It is very useful for my research. I hope you can share more info about this. Keep posting something realetd to this.
Ai & Artificial Intelligence Course in Chennai
PHP Training in Chennai
Ethical Hacking Course in Chennai Blue Prism Training in Chennai
UiPath Training in Chennai

Sudhir said...

Thanks for provide great informatic and looking beautiful blog, really nice required information & the things i never imagined and i would request, wright more blog and blog post like that for us. Thanks you once agian

birth certificate in delhi
birth certificate in bangalore
birth certificate in gurgaon
birth certificate correction
birth certificate in noida
birth certificate online
birth certificate in ghaziabad
birth certificate in india
birth certificate apply online
birth certificate in bengaluru

Joyal said...

Oracle Training | Online Course | Certification in chennai | Oracle Training | Online Course | Certification in bangalore | Oracle Training | Online Course | Certification in hyderabad | Oracle Training | Online Course | Certification in pune | Oracle Training | Online Course | Certification in coimbatore

Fuel Digital Marketing said...

thanks for sharing with us.As the most reputed website designers in Chennai, our work is always elegant & has a visual story to it. Our team comprises the best website designers in India.


digital marketing agencies in chennai | best web developers and designers in chennai | best website designing companies in chennai | | Website designers in chennai | Best logo designers in chennai

snk creation said...

thanks for sharing amazing offer to Buy Instagram Followers India

sudhan said...

marvelous offers ,its an very pleasent.


Robotic Process Automation (RPA) Training in Chennai | Robotic Process Automation (RPA) Training in anna nagar | Robotic Process Automation (RPA) Training in omr | Robotic Process Automation (RPA) Training in porur | Robotic Process Automation (RPA) Training in tambaram | Robotic Process Automation (RPA) Training in velachery







IT Technology Updates said...

You have done a phenomenal work in this blog..Too good..Keep writing.

data science course fees in chennai
data science course in chennai
best data science training institute in chennai
data science course in velachery
data science training in omr
data science training in anna nagar
data science training with placement in chennai
data science course with placement in chennai
data science training center in chennai
data science training institutes in chennai
data science training in porur
data science course syllabus

radhika said...

It’s very helpful for this blog. .Also great with all of the valuable information you have keep up the good work you are doing well.
AWS training in Chennai

AWS Online Training in Chennai

AWS training in Bangalore

AWS training in Hyderabad

AWS training in Coimbatore

AWS training

AWS online training

Unknown said...

This is excellent information. It is amazing and wonderful to visit your site.Thanks for sharing this information,this is useful to me.

Microsoft Azure Online Training

Microsoft Azure Classes Online

Microsoft Azure Training Online

Online Microsoft Azure Course

Microsoft Azure Course Online

Dinesh G said...

Thank you so much for sharing this wonderful information. It was a really awesome content. Looking forward to read more articles from you.

Digital Marketing Agency in Coimbatore
SEO Company in Coimbatore
Creative Design Services

Aishu said...

You have shared a nice article here. After reading your article I got very much information and It resolved many of my doubts. Thanks for sharing this article here.
IELTS Coaching in chennai

German Classes in Chennai

GRE Coaching Classes in Chennai

TOEFL Coaching in Chennai

spoken english classes in chennai | Communication training

praveen said...

Nice article with informative,
Thanks to share with us valuable content,

oracle training in chennai

oracle training in porur

oracle dba training in chennai

oracle dba training in porur

ccna training in chennai

ccna training in porur

seo training in chennai

seo training in porur

Jayalakshmi said...

Amazing information to know and understand this article thanks to update in this website.
sap training in chennai

sap training in tambaram

azure training in chennai

azure training in tambaram

cyber security course in chennai

cyber security course in tambaram

ethical hacking course in chennai

ethical hacking course in tambaram

harinijegan80 said...

hi

shiny said...

Thank you for your informative article, I have been doing research on this subject, and for three days I keep entering sites that are supposed to have what I am searching for, only to be discouraged with the lack of what I needed. Thank you again.

data science training in chennai

data science training in annanagar

android training in chennai

android training in annanagar

devops training in chennai

devops training in annanagar

artificial intelligence training in chennai

artificial intelligence training in annanagar



Science Technology said...

Nice Blog..Thanks for sharing..

artificial intelligence training in chennai | artificial intelligence training in anna nagar | artificial intelligence training in ramapuram | artificial intelligence course in chennai | artificial intelligence training in omr | artificial intelligence training in velachery | Ai & artificial intelligence course in chennai

rocky said...

Thanks for sharing the required infos with the clear update and required points. To appreciate this I like to share some useful information.
python training in chennai

python course in chennai

python online training in chennai

python training in bangalore

python training in hyderabad

python online training

python training

python flask training

python flask online training

python training in coimbatore

lavanya said...

Nice! you are sharing such helpful and easy to understandable blog in decoration. i have no words for say i just say thanks because it is helpful for me.Nice post. Thanks for sharing! I want people to know just how good this information is in your article. It’s interesting content and Great work.Java training in Chennai

Java Online training in Chennai

Java Course in Chennai

Best JAVA Training Institutes in Chennai

Java training in Bangalore

Java training in Hyderabad

Java Training in Coimbatore

Java Training

Java Online Training

jeni said...

I just loved your article on the beginners guide to starting a blog.If somebody take this blog article seriously in their life, he/she can earn his living by doing blogging.thank you for this article.
sap training in chennai

sap training in velachery

azure training in chennai

azure training in velachery

cyber security course in chennai

cyber security course in velachery

ethical hacking course in chennai

ethical hacking course in velachery

Anonymous said...

wonderful article.River Group of Salon and spa, T.Nagar, provide a wide range of spa treatments, like body massage, scrub, wrap and beauty parlour services. We ensure unique care and quality service.

massage in T.Nagar | body massage T.Nagar | massage spa in T.Nagar | body massage center in T.Nagar | massage centre in chennai | body massage in chennai | massage spa in chennai | body massage centre in chennai | full body massage in T.Nagar

Admin said...

bigg boss website
bigg boss voting website
bigg boss telugu voting website
bigg boss tamil voting website
bigg boss hindi voting website

surya said...

Very good article, thank you for this very valuable information.

angular js training in chennai

angular training in chennai

angular js online training in chennai

angular js training in bangalore

angular js training in hyderabad

angular js training in coimbatore

angular js training

angular js online training

KITS Technologies said...

Looking forward to reading more. Great blog article. Great.
Python online training
Python training
R programming online training
R programming training
Salesforce admin online training
Salesforce admin training
salesforce development online training
salesforce development training

Revathi said...

These contents are very valuable to all readers and I gain my knowledge after visiting your post..thanks lot!!

Android Training in Chennai

Android Online Training in Chennai

Android Training in Bangalore

Android Training in Hyderabad

Android Training in Coimbatore

Android Training

Android Online Training

vivekvedha said...

Thanks Admin for sharing such a useful post, I hope it’s useful to many individuals for developing their skill to get good career.
acte reviews

acte velachery reviews

acte tambaram reviews

acte anna nagar reviews

acte porur reviews

acte omr reviews

acte chennai reviews

acte student reviews

Best laptops Under 30000 said...

Best 3 burner gas stove
gaming laptops under 30000
http://www.bestlaptopsunder30000.com/best-foldable-keyboards-buy

free-recharge-tricks said...

https://earn2you.com/free-recharge-tricks-in-multi-product-online-service

Anonymous said...

Thanks for provide great informatic and looking beautiful blog.
Python Interview Question And Answers
Web Api Interview Questions
OSPF Interview Questions And Answers

shiva said...

Simply wish to say your article is as astonishing,,,, thanks a lot
Cyber Security Training Course in Chennai | Certification | Cyber Security Online Training Course | Ethical Hacking Training Course in Chennai | Certification | Ethical Hacking Online Training Course |
CCNA Training Course in Chennai | Certification | CCNA Online Training Course | RPA Robotic Process Automation Training Course in Chennai | Certification | RPA Training Course Chennai | SEO Training in Chennai | Certification | SEO Online Training Course

vivekvedha said...

Thank you so much for your information,its very useful and helpful to me.
acte chennai

acte complaints

acte reviews

acte trainer complaints

acte trainer reviews

acte velachery reviews complaints

acte tambaram reviews complaints

acte anna nagar reviews complaints

acte porur reviews complaints

acte omr reviews complaints

nikhil reddy said...

Good Post! Thank you so much for sharing this pretty post, it was so good to read and useful to improve my knowledge as updated one, keep blogging

Data Science Training in Hyderabad

kevin32 said...

Sometimes as a result of expired Windows security certificate, QuickBooks Error 15243 take place in the machine.

Blogsilly said...

QuickBooks Support Phone Number
QuickBooks Technical Support Number
QuickBooks Tech Support Number
QuickBooks Payroll Support Number

Kousalya said...

YOU CAN HAVE BEST DEALS EVER

BEST APPSUMO DEALS EVER LIFETIME DEALS
BEST 33 APPSUMO LIFETIME DEALS
APPSUMO DELAS 2020-2021

Dinesh Karthik said...

Excellent Blog. Thank you so much for sharing.

buy crackers at low price?

Buy sivakasi crackers online from bijili.co

Unknown said...

https://zulqarnainbharwana.com/alex-morgan/

kevin32 said...

What exactly is a QuickBooks Pro Advisor? The professionals who take certification from Intuit for QuickBooks accounting software are known as QuickBooks Pro Advisors. They help business people and QuickBooks users to aid in any Support problems and issues they could be facing with all the QB software need any help dial QuickBooks help.

Unknown said...

https://zulqarnainbharwana.com/leeds/

Unknown said...

This article is very helpful for me. Visit This Website formore solutions
JASHABHSOFT

kirankumarpaita said...

software testing company in India
software testing company in Hyderabad
Very useful information.
Thanks for sharing such a great info with us.
keep sharing.

KP Webtech said...



Wordpress Development Company in Chennai

KP Webtech, among the top Wordpress development companies in Chennai, has competent developers in the Wordpress domain who can capably bend the software to deliver inspiring custom corporate websites, CMS solutions and e-commerce portals...
Wordpress has for long been a favourite among programmers due to its multiple possibilities and availability of numerous plugins. Statistics show that over 60 million people (and growing) use Wordpress for website development.
As the best Wordpress development company in Chennai, KP Webtech empowers you with agile and scalable solutions to satisfy all your needs right from content management to marketing with our Wordpress solutions. Secure, user-centric, elegant and scalable, Wordpress ticks all the boxes to be the platform of choice for web development. While the Wordpress website development cost in Chennai is variable, we can offer flexible packages to suit your needs and ensure absolute satisfaction at every step.

richard bryan said...

At SynergisticIT we offer the best java cerfication training

Cubestech said...

Thanks for the lucid content. Your knowledge on the subject and dedication is of prime importance.

"Best mobile app development service in chennai
Best ERP software solutions in chennai
Digital Marketing Agency in Chennai
Best web development company in chennai"

savas said...

Wonderful post, very informative. I wonder why the other experts of this sector do not notice this. You must continue your writing.
Thanks for sharing excellent informations.
Please visit my sites Also.
You can get a lot of information.

1) http://yc.kugeu.org/participation/humor.php?pagenumber=1&at=view&idx=184038
2) http://www.tlclogis.com/bbs/board.php?bo_table=tlc_qna&wr_id=528
3) http://www.japanpr.com/?listStyle=webzine&mid=kumamoto_board&document_srl=12467605
4) http://suncheon.alf-pet.com/bbs/board.php?bo_table=alf_notice&wr_id=319
5) http://www.hk-fiber.com/bbs/board.php?bo_table=board_products10&wr_id=7685

sachink said...

I have read your First post and know that how to use ANTLR in a maven project and this time i really didn't know about lexer.so your tutorial help me to know about it.thank youFirst Copy Ladies Watches Online

Training Program and Placement said...

Great content, thanks for sharing, i also recommend to see the blogs on
java full stack developer course, java training in bangalore, best java training institutes in bangalore

saketh321 said...


I really thank you for the valuable info on this great subject and look forward to more great posts ExcelR Data Analytics Courses

arshiya fouzia said...

Thanks for sharing good content.
Digital marketing course in chennai

arshiya fouzia said...

Wonder post,Thanks for the blog.
Oracle non-technical interview

Taj Kaif said...

I want you to thank for your time of this wonderful read!!!
I definately enjoy every little bit of it and I have you bookmarked to check out new stuff of your blog a must read blog!
you can visite my website.

मेरी फसल मेरा ब्यौरा

shivam said...

Digibrom is the Best Digital Marketing
Training & Services In Bhopal
Digibrom is the Best Digital Marketing Training & Services in Bhopal, providing complete digital growth for your business. We are one of the leading Digital Marketing company in Bhopal, and we are experts in digital marketing & web design. The market penetration along with the use of digital technology is our power. We serve businesses according to the need and requirements of the customers and deliver quality work in time because Digibrom is the best digital marketing training institute in Bhopal and service provider. We create likable content that increases the time spent by the customer on the internet.Digital Marketing is one of the greatest opportunities for a great career. Including lots of opportunities within the field and attractive salaries, it’s the best time to connect a digital marketing Training. These days Digibrom is the Best Digital Marketing Training In Bhopal. it has become a demand for professions to have a solid online presence and for that, people need to get help from digital marketing companies to improve their online appearance. Digibrom is the best digital marketing Training & Services company in Bhopal.
Digital marketing training in bhopal

Best digital marketing company in bhopal

hont said...

blockchain technology company in usa
data science and analytics services in usa
predictive analytics services in usa
augmented reality and virtual reality in usa
machine learning in usa

vé máy bay từ Nhật Bản về Việt Nam said...

Đặt vé máy bay tại Aivivu, tham khảo

Lịch bay từ Hàn Quốc về Việt Nam hôm nay

giá vé máy bay huế- tp hồ chí minh

giá vé máy bay pacific airlines sài gòn hà nội

giá vé máy bay đà nẵng nha trang

lịch bay hà nội đà lạt

Softwaresmods said...

I just like the helpful information you provide on your articles.
I’ll bookmark your blog and test again here frequently.
I am reasonably sure I will be told many new stuff proper right here!
Good luck for the next!

नरेगा जॉब कार्ड

Shala Darpan

Govt.Schemes

Fantastic Game

Village Talkies said...

This post is really amazing
Village Talkies a top-quality professional corporate video production company in Bangalore and also best explainer video company in Bangalore & animation video makers in Bangalore, Chennai, India & Maryland, Baltimore, USA provides Corporate & Brand films, Promotional, Marketing videos & Training videos, Product demo videos, Employee videos, Product video explainers, eLearning videos, 2d Animation, 3d Animation, Motion Graphics, Whiteboard Explainer videos Client Testimonial Videos, Video Presentation and more for all start-ups, industries, and corporate companies. From scripting to corporate video production services, explainer & 3d, 2d animation video production , our solutions are customized to your budget, timeline, and to meet the company goals and objectives.
As a best video production company in Bangalore, we produce quality and creative videos to our clients.

Keerthi55 said...

Appreciate you sharing, great article.Much thanks again. Really Cool.
data science training
python training
angular js training
selenium trainings
java training

Devi said...

Learn Hadoop Training in Chennai for making your career towards a sky-high with Infycle Technologies. Infycle Technologies offers the best Big Data Hadoop training in Chennai, providing courses for Big Data in 200% hands-on practical training with professional trainers in the domain. Apart from the coaching, the placement interviews will be arranged for the students, so that they can set their career without any struggle. Of all that, 100% placement assurance will be given here. To have the best career, call 7502633633 to Infycle Technologies and grab a free demo to know more.Big Data Hadoop Training in Chennai | Infycle Technologies

Mr Windscreen Repairs said...

My deep gratitude for the information provide…. These are amazing for windscreen repairs melbourne

Unique Academy said...

There is an amazing offer for you guys during this lockdown. To enhance your skills our institution is conducting CS executive classes and offering a free CSEET class and many more things to explore. To know more about this contact us or visit our website
https://uniqueacademyforcommerce.com/

Vé máy bay từ Canada về việt nam said...

Đặt vé tại phòng vé Aivivu, tham khảo

vé máy bay đi Mỹ hạng thương gia

vé máy bay từ mỹ về việt nam giá rẻ

vé máy bay nhật việt

vé máy bay từ đức về việt nam

thông tin chuyến bay từ canada về việt nam

vé máy bay từ hàn quốc sang việt nam

khách sạn cách ly hà nội

Rajendra Cholan said...

PYTHON TRAINIING INSTITUTE IN CHENNAI | INFYCLE TECHNOLOGIES:


Reach out to Infycle Technologies, the leading software training school in Chennai, to get python training in Chennai and enter the software sector with well-defined capabilities. Infycle Technologies is one of Chennai's fastest growing software training and placement centres, with a reputation for providing high-quality hands-on training with a 100% assured job placement.

Python course inn Chennai

kishor said...

nices information thanku so mcuh
Kishorsasemahal
free classified submission sites list

Tamil novels said...

Thank you for sharing such a valuable information with us.
Tamil novel writers
Ramanichandran novels PDF
srikala novels PDF
Mallika manivannan novels PDF
muthulakshmi raghavan novels PDF
Infaa Alocious Novels PDF
N Seethalakshmi Novels PDF
Sashi Murali Tamil Novels PDF Download







Rajendra Cholan said...

Don’t miss this Infycle Education feast!! Special menu like updated Java, Python, Big Data, Oracle, AWS, and more than 20 software-related courses. Just get Data Science from the best Data Science Training Institute in Chennai, Infycle Technologies, which helps to recreate your life. It can help to change your boring job into a pep-up energetic job because, in this feast, you can top-up your knowledge. To enjoy this Data Science training in Chennai, just make a call to 7502633633.
Best software training in chennai

wethink said...

thanku so much this information
valueking
hotels-in-dubai-for-your-holidays

Keerthi55 said...

Great, thanks for sharing this post.Much thanks again. Awesome.
java online training
java training

Anonymous said...

Now you will be able to know all this and much more thanks to vst4crack.








wethink said...

thanku somuch this information thanks lot
Wethinksolution
search-engine-optimization-services

Health Insurance in UAE said...

HI THANKU SO MUCH THIS INFORMATION THANKU GRATEFUL INFORMATION
vattalks

Elena James said...

How Do You Lose Currency Trading In au-usd

BK-25 said...

MSBI Certification Training in Chennai
Informatica Certification Training in Chennai
Android Application Training In Chennai
Ios Application Training In Chennai
Best Software training institute
Best Docker Training in Chennai
Xamarin Training in Chennai
Power-Bi Certification Training

Peter Schiff said...

Do You Know AximTrade Review Is A Global Financial Firm That Offers A Wide Range Of Financial Services, Including Fx, Cfd, No Deposit Bonus And More Sign Up With AximTrade Login Account And Trade In Forex.

hotpcsoft said...

Good Post. This is my first time i visit here and I found so many interesting stuff in your blog especially it's discussion, thank you.
CCleaner Pro Crack torrent

Unknown said...

nice blog..
net coure
net training

ani321india said...


hindi blog



youtube to mp3 conconver

login360 said...

Digital marketing is the effective and faster Technique to reach our target audience with than standard traditional methods of marketing.
the online market plays a vital role compared to the offline market in today's business.
Purchases are done online today within a few minutes using mobile screens.
Technology has gone this far and businesses are making use of Digital Marketing to grow their markets.
But, to optimise their business with advancements, various businesses including startups, small businesses, corporates, and institutions are in need of qualified professional digital marketers.
The traditional methods of all marketing have being changed because of advanced digital marketing techniques Digital marketers always come up with many ways to interact and connect with their audience read more….

Techno Blogging said...

download the best consoles Free Roms Download and Roms free Download for wii u, ps2, psp, etc for free for your pc and android.

priya said...

The software environment and programming language for statistical computing and graphics is called R. For both data scientists and statisticians, and it is one of the most important languages to know. To learn more about R Programming, join R Programming Training in Chennai at FITA Academy.


R Programming Training in Chennai

Anonymous said...

I really admired the clarity of your blog explanation. It delves into seven practical tips for achieving healthy and radiant skin, offering reasonable approaches to skincare maintenance. Additionally, it acknowledges alternative treatments like hydrafacial, microneedling, and facial extractions, which further enhance skin rejuvenation, imparting an extra glow to the face

SEO Agency in Karachi said...

Great insights in this blog post! I love how you've broken down the topic in such a clear and engaging way. By the way, if anyone's looking to boost their online visibility, I highly recommend checking out a reliable SEO Agency in Karachi. They can really help optimize your strategy and drive traffic to your site

Paid Connections said...

Excellent read! The actionable advice and clear explanations make this blog truly stand out—thank you for sharing! Customized phone advice services

«Oldest ‹Older   201 – 389 of 389   Newer› Newest»

Post a Comment