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.
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:
- add ANLR into a maven project,
- create simple "Hello Word!" grammar,
- add error handling into it,
- add custom catch clause to parser rules,
- add new methods and fields to generated classes,
- override generated methods.
Second part contains:
- lexer basics,
- parser basics,
- boolean expression language,
- rewrite rules - how to specify abstract syntax tree structure,
- better boolean expression language.
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 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
leads to following abstract syntax tree:
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 ruleTOKEN
. - 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:
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.
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!;
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 is any valid expression in whatever programming language is used. You can reference labels using usual
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
If there are no additional arguments, e.g. the list
If it is not empty, create usual flat tree:
This is final rule:
Abstract syntax tree generated from single expression
and abstract syntax tree generated from
{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.
388 comments:
«Oldest ‹Older 1 – 200 of 388 Newer› Newest»This was very informative. But how do you get around the "greedy" token matching in a grammar like:
grammar Option ;
//
// any number of optionstacks
//
option : OPTIONSTACK_GROUP + ;
//
// At least one option_line per stack
//
OPTIONSTACK_GROUP : OPTIONSTACK_LINE
OPTION_LINE + ;
OPTIONSTACK_LINE : WS 'OPTIONSTACK' ':'
optionstackname=STRING { System.out.println ("optionstackname:" + optionstackname); }
WS;
OPTION_LINE : WS 'OPTION' ':'
optionname=STRING { System.out.println ("optionname:" + optionname); }
':' optionvalue=STRING { System.out.println ("actionvalue:" + optionvalue); }
WS ;
//
// any number (including zero) of spaces or tabs and returns
//
WS : ( '\t' | ' ' ) * ( ( '\r' ? '\n') * | 'uffff' ) ;
//
// we need the slash for filenames eventually
//
STRING : ( 'a' .. 'z' | 'A' .. 'Z' | '0' .. '9' | '.' | '_' | '/' ) * ;
======
which fails on the second OPTIONSTACK in this data file:
======
OPTIONSTACK:PARSER
OPTION:TRACE:1
OPTION:VERBOSE:1
OPTIONSTACK:ANALYZER
OPTION:TRACE:1
OPTION:VERBOSE:1
OPTIONSTACK:OPTIMIZER
OPTION:TRACE:1
OPTION:VERBOSE:1
OPTIONSTACK:GENERATOR
OPTION:TRACE:1
OPTION:VERBOSE:1
This was very informative. But how do you get around the "greedy" token matching in a grammar like:
grammar Option ;
//
// any number of optionstacks
//
option : OPTIONSTACK_GROUP + ;
//
// At least one option_line per stack
//
OPTIONSTACK_GROUP : OPTIONSTACK_LINE
OPTION_LINE + ;
OPTIONSTACK_LINE : WS 'OPTIONSTACK' ':'
optionstackname=STRING { System.out.println ("optionstackname:" + optionstackname); }
WS;
OPTION_LINE : WS 'OPTION' ':'
optionname=STRING { System.out.println ("optionname:" + optionname); }
':' optionvalue=STRING { System.out.println ("actionvalue:" + optionvalue); }
WS ;
//
// any number (including zero) of spaces or tabs and returns
//
WS : ( '\t' | ' ' ) * ( ( '\r' ? '\n') * | 'uffff' ) ;
//
// we need the slash for filenames eventually
//
STRING : ( 'a' .. 'z' | 'A' .. 'Z' | '0' .. '9' | '.' | '_' | '/' ) * ;
======
which fails on the second OPTIONSTACK in this data file:
======
OPTIONSTACK:PARSER
OPTION:TRACE:1
OPTION:VERBOSE:1
OPTIONSTACK:ANALYZER
OPTION:TRACE:1
OPTION:VERBOSE:1
OPTIONSTACK:OPTIMIZER
OPTION:TRACE:1
OPTION:VERBOSE:1
OPTIONSTACK:GENERATOR
OPTION:TRACE:1
OPTION:VERBOSE:1
Hi Anonymous,
nice exercise you got :). That grammar file has three problems:
1.) You are making whitespaces mandatory, I would rather ignore them.
2.) Lexer rule may reference only fragmented lexer rule. OPTIONSTACK_GROUP in that grammar references both OPTIONSTACK_LINE and OPTION_LINE lexer rules.
3.) Each lexer rule corresponds to one token. As a result, even if the grammar would work, it would create one big token from:
OPTIONSTACK:PARSER
OPTION:TRACE:1
OPTION:VERBOSE:1
another big token from
OPTIONSTACK:ANALYZER
OPTION:TRACE:1
OPTION:VERBOSE:1
and so on. Basically, parser would have nothing to do. You are trying to put all functionality into lexer and it is not build for that.
The solution to this is simple: change OPTIONSTACK_GROUP, OPTIONSTACK_LINE and OPTION_LINE lexer rules into optionstack_group, optionstack_line, option_line parser rules.
Hope that helped. The grammar is in a next comment.
grammar Option ;
//
// any number of optionstacks
//
option : optionstack_group + ;
//
// At least one option_line per stack
//
optionstack_group : optionstack_line
option_line + ;
optionstack_line : 'OPTIONSTACK' ':'
optionstackname=STRING { System.out.println ("optionstackname:" + optionstackname); };
option_line : 'OPTION' ':'
optionname=STRING { System.out.println ("optionname:" + optionname); }
':' optionvalue=STRING { System.out.println ("actionvalue:" + optionvalue); }
;
//
// any number (including zero) of spaces or tabs and returns
//
WS : ( '\t' | ' ' ) * ( ( '\r' ? '\n') * | 'uffff' ) {$channel=HIDDEN;} ;
//
// we need the slash for filenames eventually
//
STRING : ( 'a' .. 'z' | 'A' .. 'Z' | '0' .. '9' | '.' | '_' | '/' ) * ;
*****************************************************
OUTPUT ON OPTIONS FILE
*****************************************************
optionstackname:[@2,12:17='PARSER',<4>,1:12]
optionname:[@6,26:30='TRACE',<4>,2:7]
actionvalue:[@8,32:32='1',<4>,2:13]
optionname:[@12,41:47='VERBOSE',<4>,3:7]
actionvalue:[@14,49:49='1',<4>,3:15]
optionstackname:[@18,64:71='ANALYZER',<4>,5:12]
optionname:[@22,80:84='TRACE',<4>,6:7]
actionvalue:[@24,86:86='1',<4>,6:13]
optionname:[@28,95:101='VERBOSE',<4>,7:7]
actionvalue:[@30,103:103='1',<4>,7:15]
optionstackname:[@34,118:126='OPTIMIZER',<4>,9:12]
optionname:[@38,135:139='TRACE',<4>,10:7]
actionvalue:[@40,141:141='1',<4>,10:13]
optionname:[@44,150:156='VERBOSE',<4>,11:7]
actionvalue:[@46,158:158='1',<4>,11:15]
optionstackname:[@50,173:181='GENERATOR',<4>,13:12]
optionname:[@54,190:194='TRACE',<4>,14:7]
actionvalue:[@56,196:196='1',<4>,14:13]
optionname:[@60,205:211='VERBOSE',<4>,15:7]
actionvalue:[@62,213:213='1',<4>,15:15]
Very good article, thank you for this very valuable information.
Thank you for posting this, specifically the conditional re-write rules, it was exactly what I was searching for.
Thanks for the 'ANTLR lexer uses standard regular expressions grammar.'
Just what the doctor ordered!
@Anonymous Happy to hear that :).
This is really nice
Thank you very much for this blog post. It's much better than the official documentation and examples.
Very informative, very concise, very well explained. Thank you for sharing in this post
This was very very good! Very well explained. Easy to digest and just very nicely structured. Thanks you so much.
hi Meri nice tutorial you got there.can you help me with my school project which i explained below thanks :)
You will design a lexical and syntax analyzer for your own programming language.
You will describe some rules for your programming language and call it as MPL (My
Programming Language).
For example, your rules may be as;
- All programs must start with “basla” and end with “bitir”
- All commands must be ended by “:”
- All variables must be declared just after “basla” in the following format;
int; i num:
real; rl:
- Three types exist; “int”, “real”, “char”
- Variables names may be any alphanumeric string, but they must start with a letter
- Statements between “basla” and “bitir” can be either a variable declaration or an
assignment
- An assignment includes four type of operators; +,-,*, /.
- The number of variables or constants in an expression is unlimited
- The precedence of operators given as..............
- ........
- .etc
The assignment will be submitted in 3 steps:
Step 1. Your PL should include a well- defined regular grammar for variable names,
rules for variable declarations including their type, at least 4 arithmetic operators
with their precedence rules, 2 logical operators (&, ||) with their precedence, 4 relational
operators (<, <=, >, >=) and indefinite number of assignments with expressions having
unlimited number of operands.
After writing BNF rules for your language design and implement a syntax analyzer for it
using ANTLR. In each step:
Make a demo with example source codes written in your language, e.g
myprog1.mpl, myprog2.mpl to demonstrate that your analyzer can detect syntax
error in your source codes.
I'm using ANTLRWorks2 and the AST-shaping operators (!, ^) do not seem to be recognized by it. Is this tutorial outdated or is there an explanation for this?
Hi. You did a great job I liked your website and your posts. Wish you all good luck.
Seo Services Company In Delhi
I believe there are many more pleasurable opportunities ahead for individuals that looked at your site.
java training in chennai
java training in bangalore
java online training
java training in pune
java training in chennai
java training in bangalore
java training in tambaram
Great post! I am actually getting ready to across this information, 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.
automation anywhere training in chennai
automation anywhere training in bangalore
automation anywhere training in pune
automation anywhere online training
blueprism online training
rpa Training in sholinganallur
rpa Training in annanagar
iot-training-in-chennai
blueprism-training-in-pune
automation-anywhere-training-in-pune
This blog is the general information for the feature. You got a good work for these blog.We have a developing our creative content of this mind.Thank you for this blog. This for very interesting and useful.
rpa Training in annanagar
blue prism Training in annanagar
automation anywhere Training in annanagar
iot Training in annanagar
rpa Training in marathahalli
blue prism Training in marathahalli
automation anywhere Training in marathahalli
blue prism training in jayanagar
automation anywhere training in jayanagar
It's interesting that many of the bloggers to helped clarify a few things for me as well as giving.Most of ideas can be nice content.The people to give them a good shake to get your point and across the command
rpa Training in tambaram
blueprism Training in tambaram
automation anywhere training in tambaram
iot Training in tambaram
rpa training in sholinganallur
blue prism training in sholinganallur
automation anywhere training in sholinganallur
iot training in sholinganallur
Thanks Admin for sharing such a useful post, I hope it’s useful to many individuals for developing their skill to get good career.
Data Science Training in Chennai
Data science training in bangalore
Data science online training
Data science training in pune
Data science training in kalyan nagar
selenium training in chennai
I really like the dear information you offer in your articles. I’m able to bookmark your site and show the kids check out up here generally. Im fairly positive theyre likely to be informed a great deal of new stuff here than anyone
java training in marathahalli | java training in btm layout
java training in jayanagar | java training in electronic city
java training in chennai | java training in USA
selenium training in chennai
Wow 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 blog.
java training in tambaram | java training in velachery
java training in omr | oracle training in chennai
java training in annanagar | java training in chennai
You blog post is just completely quality and informative. Many new facts and information which I have not heard about before. Keep sharing more blog posts.
python training in pune
python online training
python training in OMR
Thank you a lot for providing individuals with a very spectacular possibility to read critical reviews from this site.
Blueprism training in Chennai
Blueprism online training
Blue Prism Training in Pune
Thanks for your informative article, Your post helped me to understand the future and career prospects & Keep on updating your blog with such awesome article.
Blueprism training in Chennai
Blueprism online training
Blue Prism Training in Pune
Resources like the one you mentioned here will be very useful to me ! I will post a link to this page on my blog. I am sure my visitors will find that very useful
Blueprism training in Chennai
Blueprism online training
Blue Prism Training in Pune
Outstanding blog post, I have marked your site so ideally I’ll see much more on this subject in the foreseeable future.
angularjs Training in bangalore
angularjs Training in bangalore
angularjs Training in btm
angularjs Training in electronic-city
angularjs online Training
Thank you for benefiting from time to focus on this kind of, I feel firmly about it and also really like comprehending far more with this particular subject matter. In case doable, when you get know-how, is it possible to thoughts modernizing your site together with far more details? It’s extremely useful to me.
angularjs-Training in chennai
angularjs Training in chennai
angularjs-Training in tambaram
angularjs-Training in sholinganallur
angularjs-Training in velachery
This is good site and nice point of view.I learnt lots of useful information.
angularjs-Training in chennai
angularjs Training in chennai
angularjs-Training in tambaram
angularjs-Training in sholinganallur
angularjs-Training in velachery
I like your blog, I read this blog please update more content on hacking, further check it once at python online course
Finally I have found something which helped me. Many thanks!
Learn iOS App Development Bangalore
App Development Course in Bangalore
iOS Training in Bangalore
iOS Swift Training in Bangalore
iOS Dvelopment training in Bangalore Marathahalli
iOS Classes In Marathahalli
ios app development training center Marathahalli
Best IOS Institute Marathahalli
iOS Coaching Marathahalli
Thanks for the marvelous posting! I genuinely enjooyed reading it, I want to encourage that you continue your great writing, have a nice day!
Advanced java Training in Bangalore
Advanced java Courses in Bangalore
Advanced java Training Center in Bangalore
Advanced java Classes Bangalore
Best Advanced java Training in Bangalore
Best Advanced java Training in Marathahalli
Advanced java Institute in Marathahalli
Advanced java Courses in Bangalore Marathahalli
Hello there! I just want to offer you a big thumbs up for your great info
you have right here on this post. I'll be coming back to your web site for more soon.
Java Training in Bangalore
java/j2ee classes Bangalore
java Training Center Bangalore
Best Core Java Training in Bangalore
java Course in Bangalore
Best Java Training Institute Bangalore Marathahalli
Core and Advanced Java Institute Marathahalli
Spot on with this write-up, I truly believe that this amazing site needs
much more attention.
Java Training in Bangalore
Java Training in Bangalore
Java Training in Bangalore
Java Course in Bangalore
Java Training in Bangalore
Java Training in Bangalore
Java Training in Bangalore
Java Training in Bangalore
Java Training in Bangalore
Java Technologies for web applications
Thanks for the marvelous posting! I genuinely enjooyed reading it, I want to encourage that you continue your great writing, have a nice day!
Oracle Goldengate Training From India
Oracle Dba 12C Training From India
Hello there! I just want to offer you a big thumbs up for your great info
you have right here on this post. I'll be coming back to your web site for more soon.
Selenium Classes
sharepoint Classes
This is very good content you share on this blog. it's very informative and provide me future related information.
Best Training Insittue institute
Abinitio Training Insittue
Application Packaging Training Insittue
Nice post. Thank you for the post.
Digital marketing course in Chennai
Nice article with excellent way of approach. Your post was really helpful.Thanks for Sharing this nice info.
rpa training chennai | rpa training in velachery | rpa fees in chennai
Such a wonderful article on Blueprism , full of information and it seems you have great knowledge for Blueprism . Keep updating and keep sharing.
Thanks and regards,
Blueprism training in Chennai
Blueprism training cost in Chennai
Blueprism certification in Chennai
I am really happy with your blog because your article is very unique and powerful for new reader.
Click here:
selenium training in chennai
selenium training in bangalore
selenium training in Pune
selenium training in pune
Selenium Online Training
https://saga-androidapplication.blogspot.com/2011/04/click-button-and-open-image-gallery.html
Thanks for taking time to share this valuable information admin. Really informative, keep sharing more like this.
Blue Prism Training in Chennai
Blue Prism Training Chennai
Blue Prism Training Institute in Chennai
Angularjs Training in Chennai
AWS Training in Chennai
DevOps Training in Chennai
Such an informative article with great content
Selenium Training in Chennai
selenium Classes in chennai
iOS Training in Chennai
Digital Marketing Training in Chennai
.Net coaching centre in chennai
Best PHP training in chennai
PHP Training
It is a great post. Keep sharing such kind of useful information.
smarthrsolution
Article submission sites
One of the best blogs that I have read till now. Thanks for your contribution in sharing such a useful information. Waiting for your further updates.
Spoken English Classes in Bangalore
Spoken English Class in Bangalore
Spoken English Training in Bangalore
Spoken English Course near me
Spoken English Classes in Chennai
Spoken English Class in Chennai
Spoken English in Chennai
I am obliged to you for sharing this piece of information here and updating us with your resourceful guidance. Hope this might benefit many learners. Keep sharing this gainful articles and continue updating us.
Web Designing Course in chennai
Java Training in Chennai
Web Designing Institute in Chennai
Web Designing Training Institutes in Chennai
Java Training Institute in Chennai
Best Java Training Institute in Chennai
Thanks a million and please keep up the effective work.
R Programming institutes in Chennai | R Programming Training in Chennai | R Programming Course Fees | R Language training in Chennai
It's really a nice experience to read your post. Thank you for sharing this useful information. If you are looking for more about Trending Software Technologies in 2018 | Hadoop Training in Chennai | big data Hadoop training and certification in Chennai | Roles and reponsibilities of hadoop developer |
This blog is more effective and it is very much useful for me.
we need more information please keep update more.
Salesforce courses in Anna Nagar
Salesforce Course in Anna Nagar
Salesforce Certification Training in T nagar
Salesforce Courses in T nagar
The blog which you are shared is very much helpful for us to knew about the web designing. thanks for your information.
Web Designing Institute
Best Web Design Courses
Web Design Training Courses
Learn Website Design
Best Way to Learn Web Design
Your blog has valuable information. I appreciate work in your blog.
Linux Training in Chennai
Linux training
Linux Certification Courses in Chennai
Linux Training in Adyar
Linux Course in Velachery
Best Linux Training Institute in Tambaram
Great informative bog. Thanks for sharing such a valuable information with us.
karnatakapucresult
Education
good work done and keep update more.i like your information's and
that is very much useful for readers.
AngularJS Certification Training in T nagar
AngularJS courses in Anna Nagar
angularjs training center in bangalore
Angularjs course in Bangalore
Thank you for sharing this useful information. If you are looking for more about machine learning training in chennai
artificial intelligence and machine learning course in chennai
machine learning classroom training in chennai
Hey Nice Blog!! Thanks For Sharing!!!Wonderful blog & good post.Its really helpful for me, waiting for a more new post. Keep Blogging!
dot net course training in coimbatore
IT security training in coimbatore
Amazing Blog. The liked your way of writing. It is easy to understand. Waiting for your next post.
Node JS Training in Chennai
Node JS Course in Chennai
Node JS Advanced Training
Node JS Training Institute in chennai
Node JS Training Institutes in chennai
Node JS Course
Thanks for the wonderful work. It is really superbb...
best selenium testing training in chennai
Selenium Courses in Chennai
iOS Training in Chennai
French Classes in Chennai
Loadrunner training institute in Chennai
hp loadrunner training in chennai
Nice blog,Thank you for sharing. Best Python Online Training || Learn Python Course
Thanks for sharing this information admin, it helps me to learn new things
bizzway
Article submission sites
Great stuff! Thanks for sharing this informative post. Looking forward for more.
Microsoft Dynamics CRM Training in Chennai | Microsoft Dynamics Training in Chennai | Microsoft Dynamics CRM Training | Microsoft Dynamics CRM Training institutes in Chennai | Microsoft Dynamics Training | Microsoft CRM Training | Microsoft Dynamics CRM Training Courses | CRM Training in Chennai
Thanks for sharing useful information article to us keep sharing this info,
Mobile application development company in chennai.
Such a great piece of information! Thanks for sharing.
Oracle Training in Chennai | Oracle Training institute in chennai | Oracle course in Chennai | Oracle Training | Oracle Certification in Chennai | Best oracle training institute in Chennai | Best oracle training in Chennai | Oracle training center in Chennai | Oracle institute in Chennai | Oracle Training near me
After seeing your article I want to say that the presentation is very good and also a well-written article with some very good information which is very useful for the readers....thanks for sharing it and do share more posts like this.
python Training in Bangalore | Python Training institute in Bangalore
Data Science training in Chennai | Data Science Training Institute in Chennai
It's really a nice experience to read your post. Thank you for sharing this useful information. If you are looking for more about
machine learning course fees in chennai
machine learning training center in chennai
top institutes for machine learning in chennai
Android training in chennai
PMP training in chennai
I'm glad that I came across such a wonderful post! Regards, keep posting.
Placement Training in Chennai
Placement Training institutes in Chennai
Best Placement Training institutes in Chennai
Training and Job Placement in Chennai
Spark Course in Chennai
C++ Programming Course
HealRun is a health news blog we provide the latest news about health, Drugs and latest Diseases and conditions. We update our users with health tips and health products reviews. If you want to know any information about health or health product (Side Effects & Benefits) Feel Free To ask HealRun Support Team.
Supplements For Fitness Guarana, also called Brazilian cacao, is made from the seeds of the Brazilian plant Paullinia cupana and contains between 3 and 5 percent of caffeine, which is the main ingredient known to promote the benefits of weight loss.
one of the best blog keep posting
Machine learning training in chennai
Wonderful Post. The content is very much thought provoking. Thanks for sharing.
Ethical Hacking Course in Chennai
Hacking Course in Chennai
Ethical Hacking Course in OMR
Ethical Hacking Course in Anna Nagar
IELTS coaching in Chennai
IELTS Training in Chennai
Spoken English Classes in Chennai
Best Spoken English Classes in Chennai
I am really happy to say it’s an interesting post to read . I learn new information from your article , you are doing a great job . Keep it up.
Blue Prism Training in Pune
blueprism training
Vital Keto : N'est-ce pas exactement la fonction de la perte de poids aujourd'hui? Cela a changé ma vie pour toujours. C'était un peu extrême. Il s'agit d'un scénario courant et prend habituellement environ 24 heures afin que je suis vraiment impatient de perdre du poids.
Visitez-nous : Vital Keto
Vous pouvez également visiter : bit.ly/2QNfWny
Wow useful post thanks for sharing
php training institute in chennai
Viral RX : That ought to facilitate cut back your troubles. I've been doing detailed analysis and that i know what I'm talking about. I thought I ought to share this with you this morning.I am gratified when counterparts scan what I have to say in respect to, Testosterone booster and you have to present it high priority.You will make friends with an example. It would possibly appear like I'm a small amount overactive. Didn't you see that on Big Brother?
Visit Here : http://www.supplementmegamart.org/viral-rx/
Renewal Derm: I figured that almost all folks can be looking out for anti wrinkle because there is forever a risk. This sort of anti aging cream is superb if you are in the slightest degree serious as this respects skin care. You know that you must do it deep down.
https://beautysecretanswers.com/renewal-derm-skin/
Genodrive : That has been a jaw dropping result. It's simply the honest truth of it. The risk of finding a top quality doing this is often anticipated by several. I simply finished writing a story in reference to Testosterone booster. This helped me gain authority. I'm as worried as a cat near water. There are basically no opinions on that lengthy topic. Maybe I may be absolutely correct as this relates to that. Bear this in mind: I am worthless.
BUY NOW HERE : https://www.nutrifitweb.com/genodrive/
Read More : https://www.smore.com/q9b6t-viralis-rx
Whole Greens CBD Oil: By the way, "Don't make a mountain out of a molehill." I, absolutely, do not prize it. This is how to prevent being disquieted what plain old citizens think. Chances are good that they will carry a number of anti aging cream materials. It is always better to go for an affordable anti aging cream. That is where it gets interesting. Let's up the ante. Sometimes we forget that which is actually important. I actually don't get a phenomenon.
Visit us : https://bit.ly/2RyAKEI
I want to thank for sharing this blog, really great and informative. Share more stuff like this.
Data Science Course in Chennai
Data Science Training in Chennai
Data Analytics Courses in Chennai
Big Data Analytics Courses in Chennai
Nice posting thanks for sharing
IOT training course in chennai
Very good to read i will follow your post
blue prism training class in chennai
Keto Burn Even though protein drinks and bars are in style nowadays, they're nothing a lot of than fuel for your workouts. As the intensity increases, additional of carbohydrates are used, as they can be burned faster. You can do other activities than just your walks. Therefore, allot some weekends for cluster activities like hiking and bicycling.
https://supplementsbook.org/keto-burn-diet/
Useful post for the Graduates keep on posting
matlab training institute in chennai
Impressive to read this blog thanks for sharing
best data science training in chennai
Enjoy to read this post thanks for sharing
Digital marketing certification training chennai
whatsapp group join
download lucky patcher android app
Vazcon.com
Pubg names
whatsapp dp
flosshype.com
great post very useful information
Blockchain training institute in chennai
Nice one.
selenium training in Bangalore
web development training in Bangalore
selenium training in Marathahalli
selenium training institute in Bangalore
best web development training in Bangalore
You are doing a great job. I would like to appreciate your work for good accuracy
Regards,
PHP Training in Chennai | PHP Course in Chennai | Best PHP Training Institute in Chennai
I would assume that we use more than the eyes to gauge a person's feelings. Mouth. Body language. Even voice. You could at least have given us a face in this test.
Microsoft Azure online training
Selenium online training
Java online training
Python online training
uipath online training
C C
++ Classes in Bhopal
Nodejs Training in Bhopal
Big Data Hadoop Training in Bhopal
FullStack Training in Bhopal
AngularJs Training in Bhopal
Cloud Computing Training in Bhopal
PHP Training in Bhopal
Graphic designing training in bhopal
Python Training in Bhopal
nice post.it course in chennai
it training course in chennai
I like your blog, I read this blog please update more content on hacking,Nice post
Excellent Blog , I appreciate your hard work ,it is useful
Tableau online Training
Android app development Course
Data Science online Course
Visual studio training
iOS online courses
Informatica Online Training
Thanks for sharing valuable information AWS Online Training
Thanks for sharing in detail.
Really nice blog post.provided a helpful information.I hope that you will post more updates like this
Tableau online Training
Indian WhatsApp Group Links: Join Indian WhatsApp Groups. indian whatsapp groups 2019 We have 1000+ Latest WhatsApp Indian Groups Links List.
kenya whatsapp chat groups are very much in kenya whatsapp groups list demand so thats why we have to add the article about Kenya whatsapp chat groups links
Thanks for sharing fabulous information.
"Visit PicsArt happy birthday background banner Marathi बर्थडे बैनर बैकग्राउंड"
Thank you for this post.This is very interesting information for me.
i found this amazing cashify offers
I think things like this are really interesting. I absolutely love to find unique places like this. It really looks super creepy though!!
Check out : big data hadoop training cost in chennai | hadoop training in Chennai | best bigdata hadoop training in chennai | best hadoop certification in Chennai
Thanks for sharing valuable information.It will help everyone.keep Post.
nagaland state lottery
g.co
lampungservice.com
bateraitanam.blogspot.com
bimbellampung.blogspot.com
pinterest.com
iphongthuynet
iphongthuynet
iphongthuynet
iphongthuynet
iphongthuynet
iphongthuynet
iphongthuynet
iphongthuynet
iphongthuynet
QuickBooks Payroll Support Phone Number This can be an excellent software. You can actually manage your finances here. That is right after your accounts software. You'll be able to manage staffs with ease.
Great efforts put it to find the list of articles which is very useful to know, Definitely will share the same to other forums.
hadoop training in chennai cost
hadoop certification training in chennai
big data hadoop course in chennai with placement
big data certification in chennai
Virtually every Small And Medium Company Is Currently Using QuickBooks Enterprise Support Phone Number And As A Consequence Errors And Problems Related To It Could Be Often Seen. These Problems And Troubleshooted By The Expert And Technical Team Of QuickBooks Support Number Can Be Used To Contact.
There are many features that produce QuickBooks Support Phone Number Premier standout such as it offers bank security that aids anyone to go with IT maintenance smoothly. You might also add amount of users that may access company files at multiple locations.
Very useful information about language, good article, thanks for sharing
Data Science
This becomes one of several primary known reasons for poor cashflow management in large amount of businesses. It's going to be enough time for QuickBooks Helpline Number. The traders can’t make money. But, we have been here to aid a forecast.
Easily, the article is actually the best topic on this registry related issue. I fit in with your conclusions and will eagerly look forward to your next updates.data analytics certification courses in Bangalore
ExcelR Data science courses in Bangalore
compromise utilising the safety of your customers. You’ll be able to give us a call any moment for the moment support we tend to are accessible QuickBooks Support Number for you 24*7. Our talented team of professionals is invariably in a position to assist you whatever needs doing.
As QuickBooks Support Phone Number provide a day customer care at , your issues are resolved at any instance of that time from technically skilled professionals at minimal price.
Your customers. You’ll be able to give us a call any moment for the moment support we tend to are accessible for you 24*7. QuickBooks Tech Support Number Our talented team of professionals is invariably in a position to assist you whatever needs doing.
Actually I read it yesterday but I had some thoughts about it and today I wanted to read it again because it is very well written.
Data Science Course
More often than not when folks are protesting about incorrect calculation and defaults paychecks results. Similarly fixing QuickBooks structure of account can typically be a confusing attempt to do and hard to handle a large number of for an everyday user.
VISIT : https://www.247techsupportnumber.com/quickbooks-payroll-support-number/
Attend The Python training in bangalore From ExcelR. Practical Python training in bangalore Sessions With Assured Placement Support From Experienced Faculty. ExcelR Offers The Python training in bangalore.
python training in bangalore
Whilst you should be realizing that QuickBooks has made bookkeeping a simple task, you can find times when you may possibly face a couple of errors that will bog throughout the performance when it comes to business. QuickBooks Support telephone number may be the better location to find instant help for almost any QuickBooks related trouble.
VISIT : https://www.247supportphonenumber.com/
Are you currently scratching your head and stuck along with your QuickBooks related issues, you will be just one single click away from our expert tech support team for your QuickBooks related issues. We QuickBooks Technical Support Phone Number, are leading tech support team provider for all your QuickBooks related issues. Either it is day or night, we provide hassle-free tech support team for QuickBooks as well as its associated software in minimum possible time. Our dedicated technical team can be acquired to help you 24X7, 365 days per year to make certain comprehensive support and services at any hour. We assure you the fastest solution of all of the your QuickBooks software related issues.
QuickBooks Setup and Installation Process for Windows and Mac
By discussing a great deal QuickBooks Support Phone Number about this prodigious application, you might also be wondering that you can carry forward the setup and the installation process for different operating systems.
Many thanks. For me it was useful.
For such kind of information, be always in contact with us through our blogs. To locate the reliable way to obtain assist to create customer checklist in QB desktop, QuickBooks Payroll Tech Support Number online and intuit online payroll? Our QuickBooks Payroll Support service may help you better.
So now you have grown to be well tuned in to advantages of QuickBooks online payroll in your company accounting but since this premium software contains advanced functions that can help you along with your accounting task to accomplish, so you might face some technical errors while using the QuickBooks payroll solution. If that's so, Quickbooks Support Number provides 24/7 make it possible to our customer.
how to use paytm postpaid
technologytipsraja.com
IndiaYojna.in
Our experts team at QuickBooks Payroll Tech Support Number is going to make you realize its advanced functions and assists anyone to lift up your business growth.
Yet another thing to think about would be to host the program on the cloud for convenience to manage business efficiently. To be able to provide equal some time QuickBooks Payroll Support to any or all our clients round the clock,
QuickBooks, a credit card applicatoin solution which is developed in such a way that you can manage payroll, inventory, sales and each other need of small businesses. Each QuickBooks software option would be developed predicated on different industries and their demands in order to seamlessly manage all your business finance whenever you want plus in one go. Do not need to worry if you are stuck with QuickBooks issue in midnight as our technical specialists at QuickBooks Support is present twenty-four hours every single day to serve you along with the best optimal solution very quickly.
Thanks for sharing useful article… Keep on updating such useful information. TN EPayslip | Manidhaneyam IAS Academy Entrance Exam
QuickBooks Error Code 3371 might annoy you as it won’t let you open your QuickBooks file and a warning message will pop up which states QuickBooks could not load the license data. This can be caused due to missing or damaged files.
For such types of information, be always in contact with us through our blogs. Seeking for the reliable way to obtain help to create customer checklist in QB desktop, QuickBooks Payroll technical support number online and intuit online payroll?
Very interesting blog. Alot of blogs I see these days don't really provide anything that I'm interested in, but I'm most definately interested in this one. Just thought that I would post and let you know.
data science course malaysia
Hey,you just share the main information that really needed thanks keep writing good work quantum manifestation code pdf
my back pain coach review
the vertigo and dizziness program pdf
15 MINUTE MANIFESTATION EDDIE SERGEY
the lost ways book pdf
lost book of remedies pdf
QuickBooks Support has got plenty of
alternatives for most of us. Significant quantity of features from the end are there any to guide both both you and contribute towards
All this is performed without compromising with the quality of services because nothing seems good in the event that work is not done.
Our customer care team is enthusiastic and makes best use of its experience. QuickBooks Payroll Technical Support Number They simply do not let go any issue regardless of if it’s fairly complex.
In May 2002 Intuit thrown QuickBooks Enterprise Solutions for medium-sized businesses. QuickBooks Enterprise Support here to create tech support team to users. In September 2005, QuickBooks acquired 74% share associated with market in america. A June 19, 2008 Intuit Press Announcement said that during the time of March 2008, QuickBooks’ share of retail units within the industry accounting group touched 94.2 percent, according to NPD Group.
amazing post
Top 10 cars under 5 lakhs
Top 10 cars under 6 lakhs
top 5 light weight scooty
Very Useful blog.... Thanks for sharing with us...
Python Course in Bangalore
Python Training in Coimbatore
Python Course in Coimbatore
Python Classes in Coimbatore
Python Training Institute in Coimbatore
Selenium Training in Coimbatore
Tally Training Coimbatore
SEO Training in Coimbatore
QuickBooks encounter a number of undesirable and annoying errors which keep persisting in the long run if you don't resolved instantly. One of such QuickBooks issue is Printer issue which mainly arises as a result of a number of hardware and software problems in QuickBooks, printer or drivers. You are able to resolve this error by following the below troubleshooting steps or you can simply contact our QuickBooks Support Number offered by our toll-free.You should run QuickBooks print and pdf repair tool to recognize and fix the errors in printer settings before starting the troubleshooting. Proceed with the below steps in order to scrutinize the error
Real time the assistance of smart QuickBooks Enterprise Tech Support Number technicians Result-oriented solution each time Full satisfaction guaranteed On-demand customer priority.
QuickBooks offers a quantity of features to trace your startup business. Day by day it is getting well liked among the businessmen and entrepreneurs. However with the increasing popularity, QuickBooks is meeting a lot of technical glitches. And here we show up with our smartest solutions. Check out the problem list and when you face any one of them just call QuickBooks Tech Support Number for the assistance. We shall help you with…
The different industry specific versions add cherry concerning the cake. For such adaptive QuickBooks Enterprise Technical Support software, it really is totally acceptable to throw some issues at some instances.
Our support also also incorporates handling those errors that always occur when your form of QuickBooks Enterprise Support Phone Number has been infected by a malicious program like a virus or a spyware, which may have deleted system files, or damaged registry entries.
QuickBooks is an accounting software program generated by Intuit. In QuickBooks Payroll Support Phone Number, can help you all the billing, bookkeeping, and invoicing at one place. You can track sales and expenses, accept payments etc.
ok tốt lắm
lưới chống chuột
cửa lưới dạng xếp
cửa lưới tự cuốn
cửa lưới chống muỗi
Thanks for a nice share you have given to us with such an large collection of information.
Great work you have done by sharing them to all. for more info
simply superb.PGDCA class in bhopal
autocad in bhopal
3ds max classes in bhopal
CPCT Coaching in Bhopal
java coaching in bhopal
Autocad classes in bhopal
Catia coaching in bhopal
Attend The Python training in bangalore From ExcelR. Practical Python training in bangalore Sessions With Assured Placement Support From Experienced Faculty. ExcelR Offers The Python training in bangalore.
python training in bangalore
Attend The Python training in bangalore From ExcelR. Practical Python training in bangalore Sessions With Assured Placement Support From Experienced Faculty. ExcelR Offers The Python training in bangalore.
python training in bangalore
QB Enterprise occurs due to QuickBooks Enterprise Support Number company file. Should your software encounters the issue, If that's the case, data corruption issue in you can expect to start You can easily avoid this issue from occurring.
This printer then they HP Printer Support Phone Number should on the double relate with client administration During the point when the regarded client of HP Printer observes any entanglement while utilizing to cope with the issue went up against. You need to call HP Printer Tech Support Number as they are known for.
Let’s have a clearer picture of QuickBooks POS Tech Support Phone Number. This form of QuickBooks has contributed a lot in bringing fame and popularity to QuickBooks, the brand. This has instigated features that are quick and useful.
You might encounter QuickBooks Error Code 6000-301 when attempting to access/troubleshoot/open the organization file in your QuickBooks. Your workflow gets hindered with a mistake message that says– “QuickBooks Desktop tried to gain access to company file. Please try again.”
Some of the users facing errors while using QuickBooks, one particular error is QuickBooks Error -6000, -304. In this website, you’ll learn steps to fix this error. If you are not enthusiastic about doing its very own, you can take services from our Support For QuickBooks Error team. You are able to pose a question to your queries by dialing 24/7 available toll-free help desk +1-888-477-0210.
We understand that your growing business needs your precious time which explains why we offer the most effective to the customers. Our technically skilled professionals are well regarded for smart technical assistance QuickBooks Support
Healthy GNC - In usa is a wide variety of health,wellness and Male health performance products.which has include protein,male health performance and weight Loss management supplements.This product is really made to help improve your health, whether you are at the beginning of your fitness.Healthy GNC,gnc,weight loss,bodybuilding,vitamins,energy,fitness,strength,healthfulness, stamina, Wellness.
For more info - http://www.healthygnc.com/
Dial Contact QuickBooks Technical Support to have the matchless technical assistance in QB
re we intend to update you the way you'll be able to obtain QuickBooks Enterprise Tech Phone Support or simple recommendations for connecting QuickBooks enterprise customer support contact number. QuickBooks is financial software that will assist small enterprise, large business along side home users.
QuickBooks has introduced its version called as QuickBooks POS Support Number This software focuses exclusively on sales, customer relationship management and various other necessary aspects that altogether make a business successful. Let’s have a clearer picture of QuickBooks POS.
I am really enjoying reading your well written articles. It looks like you spend a lot of effort and time on your blog. I have bookmarked it and I am looking forward to reading new articles. Keep up the good work.
www.technewworld.in
How to Start A blog 2019
Eid AL ADHA
I really enjoy simply reading all of your weblogs. Simply wanted to inform you that you have people like me who appreciate your work. Definitely a great post. Hats off to you! The information that you have provided is very helpful.
www.technewworld.in
How to Start A blog 2019
Eid AL ADHA
list optimally, QuickBooks Payroll Tech Support Phone Number then browse the description ahead. Here, you obtain the determination of numerous variety of information everything you’ve close by for assisting the setup process with comfort.
Enhance Mind IQ is a nootropic dietary supplement that enhances brain performances and energy. With this particular supplement, you can experience improved mental focus and an enhanced memory regardless of age or gender. Kindly Visit on http://www.rushyourtrial.com/coupon/enhance-mind-iq-increase-brain-power-naturally-free-trial-pack/
Purefit KETO is non-GMO and made with all-natural ingredients. Purefit KETO contains magnesium BHB, calcium BHB and sodium BHB in a proprietary blend totaling 800 mg per capsule. Kindly Visit on http://www.choosetolose.net/purefit-keto-advanced-weight-loss-supplement/
Great Blog!!! Thanks for sharing with us...
Big Data Course in Coimbatore
DevOps Training in Coimbatore
Best DevOps Training in Coimbatore
German Classes in Coimbatore
Hacking Course in Coimbatore
IELTS Coaching in Coimbatore
Java Training in Coimbatore
Selenium Training in Coimbatore
If you need the help or even the information about it, our company has arrived now to do business with you with complete guidance combined with demo. Connect to us anytime anywhere. Only just contact us at QuickBooks Payroll Tech Support Phone Number . Our experts professional have provided a lot of the required and resolve all type of issues related to payroll.
Maka, anda bisa menjalani permainan poker online dengan kualitas terjamin, serta keuntungan yang menjanjikan yang akan diberikan pihak agen tersebut. Oleh karena itu, pastikan anda sudah bergabung di tempat yang tepat
asikqq
http://dewaqqq.club/
http://sumoqq.today/
interqq
pionpoker
bandar ceme terbaik
betgratis
paito warna terlengkap
forum prediksi
You might comment on the order system of the blog. You should chat it's splendid. Your blog audit would swell up your visitors. I was very pleased to find this site.I wanted to thank you for this great read!!
Data Science Courses
Amazing article. Your blog helped me to improve myself in many ways thanks for sharing this kind of wonderful informative blogs in live.
javascript training in chennai | javascript training institute in chennai | javascript course in chennai | javascript certification in chennai | best javascript training in chennai
It is brilliant substance. I for the most part visit numerous locales however your site has something unique highlights. I for the most part visit on your site. Best Seo Tips
Contact us- https://myseokhazana.com
The QuickBooks Payroll Service Phone Number
team at site name is held accountable for removing the errors that pop up in this desirable software. We take care of not letting any issue can be found in between your work and trouble you in undergoing your tasks. Most of us resolves all of the QuickBooks Payroll issue this type of a fashion you will yourself believe that your issue is resolved without you wasting the full time into it. We take toll on every issue making use of our highly trained customer care
If you are facing every other issue besides QuickBook Error Code 111. Don’t hesitate to obtain in contact with us, because we have been a certified team of QuickBooks ProAdvisors offered by all times to help and make suggestions.
tax return online
Central Office Support is a company that was founded with 4 pillars of office work in mind. These are Accounting, Taxation, Financing, and Consulting. We believe that all of these fields are easily outsourced and that’s where we come in. Our team of professionals have years of experience in this field and are capable of helping out any company, be it the smallest or the largest.
thanks for sharing valuable information..
AngularJS interview questions and answers/angularjs interview questions/angularjs 6 interview questions and answers/mindtree angular 2 interview questions/jquery angularjs interview questions/angular interview questions/angularjs 6 interview questions
sugar balance pills is a chromium-based formula providing important nutrients needed for the metabolism of sugar, and for energy production. People diagnosed with high blood sugar, individuals living with diabetes, and anyone with blood sugar concerns may benefit from Sugar Balance pill, an innovative nutritional supplement from The Hall Center. Visit On http://www.healthenrich.org/sugar-balance-herbal-supplement-to-control-blood-sugar/
Absolute Keto is the latest and brand new naturally developed ketogenic weight loss supplement that could help you to lose weight. There is no supplement in market like this right now. The first question that comes in your mind when you would like to take any supplement or any medicine.This supplement is helpful in reducing fat. When you take a dose of these pills, it passes through metabolic process. Then it enters in the circulatory system and starts working at the effective place in a short period of time.Kindly Visit on Absolute Keto
Nice post. It was an informative article. Keep sharing. Hydraulic lifts | home elevators
Supreme Vigor Testosterone is a supplement that improves the Quantities of Testosterone in the body. Most people would ask why this needs to be carried out, because testosterone is naturally produced by the body.These degrees drop as the individual begins to age. This results from the reality that the body begins to age as well as all body organs and also systems In the body beginning functioning extra slowly and also inefficiently. This Is the Reason aid from External resources in required to make sure that the testosterone degrees in the body Are maintained a max to give the body its desired benefits. Visit On http://www.theapexme.com/supreme-vigor-testosterone-booster-male-enhancement-capsules/
You can actually resolve this error by using the below troubleshooting steps you are able to simply contact our QuickBooks Support Phone Number available at.You should run QuickBooks Support Phone Number print and pdf repair tool to ascertain and fix the errors in printer settings prior to starting the troubleshooting.
Get prominent QuickBooks Tech Support Number by reaching out to the technical team who are highly knowledgeable in providing valid solutions related to QuickBooks related queries.
Brother Printer Support Number +1-888-600-5222.is high Quality device best Features available
for Printer Help Anytime Printers can show error any time while setting it up or during a printing task. These technical glitches are unpredictable and it affects your work entirely,Brother Printer Support Phone Number. In the world of printers, Brother is one of the most well-known brands. Along with printers, Brother also manufactures lots of electrical types of equipment such as a desktop computer, fax machine, typewriters, etc.brother Printer Tech Support Number
brother helpline Number
brother Printer Customer Support Number
download clash of null apk free
facebook auto liker
Why you ought to choose Quickbooks Support PhoneNumberThe principal intent behind QuickBooks Support number is to supply the technical help 24*7 so as in order to avoid wasting your productivity hours.
Visit Here: https://www.dialsupportphonenumber.com/quickbooks-online-banking-error-9999/
Thank you for providing the valuable information …
If you want to connect with AI (Artificial Intelligence) World
as like
Python Training
ML(Machine Learning)
Course related more information then meet on EmergenTeck Training Institute .
Thank you.!
Thank you for providing the valuable information …
If you want to connect with AI (Artificial Intelligence) World
as like
Python Training
ML(Machine Learning)
Course related more information then meet on EmergenTeck Training Institute .
Thank you.!
Now you can get a sum of benefits with QuickBooks Tech Support Number. Proper analyses are done first. The experts find out of the nature associated with trouble. You will definately get a complete knowledge as well. The support specialist will identify the problem.
The team of genius are highly experienced and certified in solving QuickBooks error 1904 technical issues and also nontechnical issues. The QuickBooks customer care executives is likely to be available 24/7 only for their users. So get quick help by contacting the QuickBooks Support Number.
hindi status
Life
We realize that getting errors like 9999 in online banking is frustrating and it also might interrupt critical business tasks, you will get in contact with us at our QuickBooks Online Banking Error 9999 for help related to online banking errors 24/7.
Great…!! that’s great advice, I read and also saw your every post, nice artical very usefull your post Thank you so much for sharing this and the information provide.
pqwl status means in hindi
acne treatment skin care
New Intuit's Data Shield service, at QuickBooks 2018 Automatically backs up your QuickBooks information (or all your valuable files, as much as 100 gigabytes) every single day into some Web-based storage place. The details Shield service differs into the elderly QuickBooks Premier Support into the backup process runs regardless if your computer data file can be obtained, and every daily backup is stored for 45 days.
Nice Blog....Waiting for next update..
SAS Training in Chennai
sas training fees
sas course fees
sas training in Thiruvanmiyur
SAS Training in Tambaram
clinical sas training in chennai
Mobile Testing Training in Chennai
QTP Training in Chennai
Hibernate Training in Chennai
DOT NET Training in Chennai
you’ll additionally visit our web site to induce to grasp additional concerning our code as well as its upgrades. you’ll scan in-depth articles concerning most of the errors and also the best way to resolve them. Rectifying errors desires in-depth information regarding the device as well as its intricacies. Our web site can be a go-to supply for everything related to QuickBooks Tech Support Phone Number.
QuickBooks Enterprise Support Number Is Here to greatly help ease Your Accounting Struggle QuickBooks Enterprise provides end-to end business accounting experience. With feature packed tools and features, this application is effective at managing custom reporting, inventory, business reports etc. all at one place. Are QuickBooks Enterprise errors troubling you? Are you currently fed up with freezing of QuickBooks? If yes, you then have browsed off to the right place. QuickBooks Enterprise Support telephone number is successfully delivering the entire world class technical assistance for QuickBooks Enterprise at comfort of your house. We understand your growing business need and that is the reason why we provide just the best. We make sure to give worth of each and every penny by providing the consumer friendly technical support services that include.
its really awesome blog post thanks for information.
Bollywood
Bollywood Comedy
Home Salon
QuickBooks is accounting software which helps the businesses in managing their inventory, payrolls, and employees, reporting, invoicing, and other valuable aspects of business. It is captivated by its capability, scalability, and flexibility, which helps you in working in different business situations and environments. It is intuitive and simple to understand. Users can install this accounting software by dialing QuickBooks Enterprise Tech Suppport Number anytime and our customer support will get back to you immediately with instant results.
Post a Comment