COS 320 Compilers David Walker. last time context free grammars (Appel 3.1) –terminals, non-terminals, rules –derivations & parse trees –ambiguous grammars.

Slides:



Advertisements
Similar presentations
Parsing V: Bottom-up Parsing
Advertisements

Parsing 4 Dr William Harrison Fall 2008
A question from last class: construct the predictive parsing table for this grammar: S->i E t S e S | i E t S | a E -> B.
Chap. 5, Top-Down Parsing J. H. Wang Mar. 29, 2011.
Mooly Sagiv and Roman Manevich School of Computer Science
6/12/2015Prof. Hilfinger CS164 Lecture 111 Bottom-Up Parsing Lecture (From slides by G. Necula & R. Bodik)
Top-Down Parsing.
By Neng-Fa Zhou Syntax Analysis lexical analyzer syntax analyzer semantic analyzer source program tokens parse tree parser tree.
ML-YACC David Walker COS 320. Outline Last Week –Introduction to Lexing, CFGs, and Parsing Today: –More parsing: automatic parser generation via ML-Yacc.
Parsing Compiler Baojian Hua Front End source code abstract syntax tree lexical analyzer parser tokens IR semantic analyzer.
CS Summer 2005 Top-down and Bottom-up Parsing - a whirlwind tour June 20, 2005 Slide acknowledgment: Radu Rugina, CS 412.
COS 320 Compilers David Walker.
Parsing VI The LR(1) Table Construction. LR(k) items The LR(1) table construction algorithm uses LR(1) items to represent valid configurations of an LR(1)
COS 320 Compilers David Walker. last time context free grammars (Appel 3.1) –terminals, non-terminals, rules –derivations & parse trees –ambiguous grammars.
ISBN Chapter 4 Lexical and Syntax Analysis The Parsing Problem Recursive-Descent Parsing.
Parsing — Part II (Ambiguity, Top-down parsing, Left-recursion Removal)
Prof. Fateman CS 164 Lecture 91 Bottom-Up Parsing Lecture 9.
CS 330 Programming Languages 09 / 23 / 2008 Instructor: Michael Eckmann.
Professor Yihjia Tsai Tamkang University
Table-driven parsing Parsing performed by a finite state machine. Parsing algorithm is language-independent. FSM driven by table (s) generated automatically.
1 CIS 461 Compiler Design & Construction Fall 2012 slides derived from Tevfik Bultan, Keith Cooper, and Linda Torczon Lecture-Module #12 Parsing 4.
1 Bottom-up parsing Goal of parser : build a derivation –top-down parser : build a derivation by working from the start symbol towards the input. builds.
Bottom-up parsing Goal of parser : build a derivation
Lexical and syntax analysis
CPSC 388 – Compiler Design and Construction
CSC3315 (Spring 2009)1 CSC 3315 Lexical and Syntax Analysis Hamid Harroud School of Science and Engineering, Akhawayn University
Syntax Analysis – Part II Quick Look at Using Bison Top-Down Parsers EECS 483 – Lecture 5 University of Michigan Wednesday, September 20, 2006.
Parsing IV Bottom-up Parsing Copyright 2003, Keith D. Cooper, Ken Kennedy & Linda Torczon, all rights reserved. Students enrolled in Comp 412 at Rice University.
Syntax and Semantics Structure of programming languages.
Parsing. Goals of Parsing Check the input for syntactic accuracy Return appropriate error messages Recover if possible Produce, or at least traverse,
LR Parsing Compiler Baojian Hua
Top-Down Parsing - recursive descent - predictive parsing
4 4 (c) parsing. Parsing A grammar describes the strings of tokens that are syntactically legal in a PL A recogniser simply accepts or rejects strings.
4 4 (c) parsing. Parsing A grammar describes syntactically legal strings in a language A recogniser simply accepts or rejects strings A generator produces.
Profs. Necula CS 164 Lecture Top-Down Parsing ICOM 4036 Lecture 5.
1 Compiler Construction Syntax Analysis Top-down parsing.
Review 1.Lexical Analysis 2.Syntax Analysis 3.Semantic Analysis 4.Code Generation 5.Code Optimization.
CS 153 A little bit about LR Parsing. Background We’ve seen three ways to write parsers:  By hand, typically recursive descent  Using parsing combinators.
CMSC 331, Some material © 1998 by Addison Wesley Longman, Inc. 1 Chapter 4 Chapter 4 Bottom Up Parsing.
Syntactic Analysis Natawut Nupairoj, Ph.D. Department of Computer Engineering Chulalongkorn University.
Syntax and Semantics Structure of programming languages.
4 4 (c) parsing. Parsing A grammar describes syntactically legal strings in a language A recogniser simply accepts or rejects strings A generator produces.
Exercise 1 A ::= B EOF B ::=  | B B | (B) Tokens: EOF, (, ) Generate constraints and compute nullable and first for this grammar. Check whether first.
COS 320 Compilers David Walker. The Front End Lexical Analysis: Create sequence of tokens from characters (Chap 2) Syntax Analysis: Create abstract syntax.
COP4020 Programming Languages Parsing Prof. Xin Yuan.
LL(k) Parsing Compiler Baojian Hua
Prof. Necula CS 164 Lecture 8-91 Bottom-Up Parsing LR Parsing. Parser Generators. Lecture 6.
Notes on First and Follow Written by David Walker Edited by Phil Sweany.
Top-Down Parsing CS 671 January 29, CS 671 – Spring Where Are We? Source code: if (b==0) a = “Hi”; Token Stream: if (b == 0) a = “Hi”; Abstract.
Top-Down Parsing.
CS 330 Programming Languages 09 / 25 / 2007 Instructor: Michael Eckmann.
Bernd Fischer RW713: Compiler and Software Language Engineering.
UMBC  CSEE   1 Chapter 4 Chapter 4 (b) parsing.
Bottom Up Parsing CS 671 January 31, CS 671 – Spring Where Are We? Finished Top-Down Parsing Starting Bottom-Up Parsing Lexical Analysis.
COMP 3438 – Part II-Lecture 6 Syntax Analysis III Dr. Zili Shao Department of Computing The Hong Kong Polytechnic Univ.
Top-down parsing 1. Last Time CYK – Step 1: get a grammar in Chomsky Normal Form – Step 2: Build all possible parse trees bottom-up Start with runs of.
1 Topic 3: Parsing and Yaccing COS 320 Compiling Techniques Princeton University Spring 2016 Lennart Beringer.
CMSC 330: Organization of Programming Languages Pushdown Automata Parsing.
Syntax and Semantics Structure of programming languages.
Parsing Bottom Up CMPS 450 J. Moloney CMPS 450.
Programming Languages Translator
Lexical and Syntax Analysis
Table-driven parsing Parsing performed by a finite state machine.
4 (c) parsing.
Top-Down Parsing CS 671 January 29, 2008.
Parsing #2 Leonidas Fegaras.
Compilers Principles, Techniques, & Tools Taught by Jing Zhang
Compiler Design 7. Top-Down Table-Driven Parsing
Parsing #2 Leonidas Fegaras.
Kanat Bolazar February 16, 2010
Presentation transcript:

COS 320 Compilers David Walker

last time context free grammars (Appel 3.1) –terminals, non-terminals, rules –derivations & parse trees –ambiguous grammars recursive descent parsers (Appel 3.2) –parse LL(k) grammars –easy to write as ML programs –algorithms for automatic construction from a CFG

1. S ::= IF E THEN S ELSE S 2. | BEGIN S L 3. | PRINT E 4. L ::= END 5. | ; S L 6. E ::= NUM = NUM non-terminals: S, E, L terminals: NUM, IF, THEN, ELSE, BEGIN, END, PRINT, ;, = rules: fun S () = case !tok of IF => eat IF; E (); eat THEN; S (); eat ELSE; S () | BEGIN => eat BEGIN; S (); L () | PRINT => eat PRINT; E () and L () = case !tok of END => eat END | SEMI => eat SEMI; S (); L () and E () = eat NUM; eat EQ; eat NUM val tok = ref (getToken ()) fun advance () = tok := getToken () fun eat t = if (! tok = t) then advance () else error () datatype token = NUM | IF | THEN | ELSE | BEGIN | END | PRINT | SEMI | EQ

Constructing RD Parsers To construct an RD parser, we need to know what rule to apply when –we have seen a non terminal X –we see the next terminal a in input We apply rule X ::= s when –a is the first symbol that can be generated by string s, OR –s reduces to the empty string (is nullable) and a is the first symbol in any string that can follow X

Computing Nullable Sets Non-terminal X is Nullable only if the following constraints are satisfied (computed using iterative analysis) –base case: if (X := ) then X is Nullable –inductive case: if (X := ABC...) and A, B, C,... are all Nullable then X is Nullable

Computing First Sets First(X) is computed iteratively –base case: if T is a terminal symbol then First (T) = {T} –inductive case: if X is a non-terminal and (X:= ABC...) then –First (X) = First (X) U First (ABC...) where First(ABC...) = F1 U F2 U F3 U... and »F1 = First (A) »F2 = First (B), if A is Nullable »F3 = First (C), if A is Nullable & B is Nullable »...

Computing Follow Sets Follow(X) is computed iteratively –base case: initially, we assume nothing in particular follows X –(Follow (X) is initially { }) –inductive case: if (Y := s1 X s2) for any strings s1, s2 then –Follow (X) = First (s2) U Follow (X) if (Y := s1 X s2) for any strings s1, s2 then –Follow (X) = Follow(Y) U Follow (X), if s2 is Nullable

building a predictive parser Z ::= X Y Z Z ::= d Y ::= c Y ::= X ::= a X ::= b Y e nullablefirstfollow Z Y X

building a predictive parser Z ::= X Y Z Z ::= d Y ::= c Y ::= X ::= a X ::= b Y e nullablefirstfollow Zno Yyes Xno base case

building a predictive parser Z ::= X Y Z Z ::= d Y ::= c Y ::= X ::= a X ::= b Y e nullablefirstfollow Zno Yyes Xno after one round of induction, we realize we have reached a fixed point

building a predictive parser Z ::= X Y Z Z ::= d Y ::= c Y ::= X ::= a X ::= b Y e nullablefirstfollow Znod Yyesc Xnoa,b base case

building a predictive parser Z ::= X Y Z Z ::= d Y ::= c Y ::= X ::= a X ::= b Y e nullablefirstfollow Znod,a,b Yyesc Xnoa,b after one round of induction, no fixed point

building a predictive parser Z ::= X Y Z Z ::= d Y ::= c Y ::= X ::= a X ::= b Y e nullablefirstfollow Znod,a,b Yyesc Xnoa,b after two rounds of induction, no more changes ==> fixed point

building a predictive parser Z ::= X Y Z Z ::= d Y ::= c Y ::= X ::= a X ::= b Y e nullablefirstfollow Znod,a,b{ } Yyesc{ } Xnoa,b{ } base case

building a predictive parser Z ::= X Y Z Z ::= d Y ::= c Y ::= X ::= a X ::= b Y e nullablefirstfollow Znod,a,b{ } Yyesce,d,a,b Xnoa,bc,d,a,b after one round of induction, no fixed point

building a predictive parser Z ::= X Y Z Z ::= d Y ::= c Y ::= X ::= a X ::= b Y e nullablefirstfollow Znod,a,b{ } Yyesce,d,a,b Xnoa,bc,d,a,b after two rounds of induction, fixed point (but notice, computing Follow(X) before Follow (Y) would have required 3 rd round)

Z ::= X Y Z Z ::= d Y ::= c Y ::= X ::= a X ::= b Y e nullablefirstfollow Znod,a,b{ } Yyesce,d,a,b Xnoa,bc,d,a,b Grammar: Computed Sets: Build parsing table where row X, col T tells parser which clause to execute in function X with next-token T: abcde Z Y X if T  First(s) then enter (X ::= s) in row X, col T if s is Nullable and T  Follow(X) enter (X ::= s) in row X, col T

Z ::= X Y Z Z ::= d Y ::= c Y ::= X ::= a X ::= b Y e nullablefirstfollow Znod,a,b{ } Yyesce,d,a,b Xnoa,bc,e,d,a,b Grammar: Computed Sets: Build parsing table where row X, col T tells parser which clause to execute in function X with next-token T: abcde ZZ ::= XYZ Y X if T  First(s) then enter (X ::= s) in row X, col T if s is Nullable and T  Follow(X) enter (X ::= s) in row X, col T

Z ::= X Y Z Z ::= d Y ::= c Y ::= X ::= a X ::= b Y e nullablefirstfollow Znod,a,b{ } Yyesce,d,a,b Xnoa,bc,e,d,a,b Grammar: Computed Sets: Build parsing table where row X, col T tells parser which clause to execute in function X with next-token T: abcde ZZ ::= XYZ Z ::= d Y X if T  First(s) then enter (X ::= s) in row X, col T if s is Nullable and T  Follow(X) enter (X ::= s) in row X, col T

Z ::= X Y Z Z ::= d Y ::= c Y ::= X ::= a X ::= b Y e nullablefirstfollow Znod,a,b{ } Yyesce,d,a,b Xnoa,bc,e,d,a,b Grammar: Computed Sets: Build parsing table where row X, col T tells parser which clause to execute in function X with next-token T: abcde ZZ ::= XYZ Z ::= d YY ::= c X if T  First(s) then enter (X ::= s) in row X, col T if s is Nullable and T  Follow(X) enter (X ::= s) in row X, col T

Z ::= X Y Z Z ::= d Y ::= c Y ::= X ::= a X ::= b Y e nullablefirstfollow Znod,a,b{ } Yyesce,d,a,b Xnoa,bc,e,d,a,b Grammar: Computed Sets: Build parsing table where row X, col T tells parser which clause to execute in function X with next-token T: abcde ZZ ::= XYZ Z ::= d YY ::= Y ::= cY ::= X if T  First(s) then enter (X ::= s) in row X, col T if s is Nullable and T  Follow(X) enter (X ::= s) in row X, col T

Z ::= X Y Z Z ::= d Y ::= c Y ::= X ::= a X ::= b Y e nullablefirstfollow Znod,a,b{ } Yyesce,d,a,b Xnoa,bc,e,d,a,b Grammar: Computed Sets: Build parsing table where row X, col T tells parser which clause to execute in function X with next-token T: abcde ZZ ::= XYZ Z ::= d YY ::= Y ::= cY ::= XX ::= aX ::= b Y e if T  First(s) then enter (X ::= s) in row X, col T if s is Nullable and T  Follow(X) enter (X ::= s) in row X, col T

Z ::= X Y Z Z ::= d Y ::= c Y ::= X ::= a X ::= b Y e nullablefirstfollow Znod,a,b{ } Yyesce,d,a,b Xnoa,bc,e,d,a,b Grammar: Computed Sets: What are the blanks? abcde ZZ ::= XYZ Z ::= d YY ::= Y ::= cY ::= XX ::= aX ::= b Y e

Z ::= X Y Z Z ::= d Y ::= c Y ::= X ::= a X ::= b Y e nullablefirstfollow Znod,a,b{ } Yyesce,d,a,b Xnoa,bc,e,d,a,b Grammar: Computed Sets: What are the blanks? --> syntax errors abcde ZZ ::= XYZ Z ::= d YY ::= Y ::= cY ::= XX ::= aX ::= b Y e

Z ::= X Y Z Z ::= d Y ::= c Y ::= X ::= a X ::= b Y e nullablefirstfollow Znod,a,b{ } Yyesce,d,a,b Xnoa,bc,e,d,a,b Grammar: Computed Sets: Is it possible to put 2 grammar rules in the same box? abcde ZZ ::= XYZ Z ::= d YY ::= Y ::= cY ::= XX ::= aX ::= b Y e

Z ::= X Y Z Z ::= d Z ::= d e Y ::= c Y ::= X ::= a X ::= b Y e nullablefirstfollow Znod,a,b{ } Yyesce,d,a,b Xnoa,bc,e,d,a,b Grammar: Computed Sets: Is it possible to put 2 grammar rules in the same box? abcde ZZ ::= XYZ Z ::= d Z ::= d e YY ::= Y ::= cY ::= XX ::= aX ::= b Y e

predictive parsing tables if a predictive parsing table constructed this way contains no duplicate entries, the grammar is called LL(1) –Left-to-right parse, Left-most derivation, 1 symbol lookahead if not, of the grammar is not LL(1) in LL(k) parsing table, columns include every k- length sequence of terminals: aaabbabbacca...

another trick Previously, we saw that grammars with left-recursion were problematic, but could be transformed into LL(1) in some cases the example non-LL(1) grammar we just saw: how do we fix it? Z ::= X Y Z Z ::= d Z ::= d e Y ::= c Y ::= X ::= a X ::= b Y e

another trick Previously, we saw that grammars with left-recursion were problematic, but could be transformed into LL(1) in some cases the example non-LL(1) grammar we just saw: solution here is left-factoring: Z ::= X Y Z Z ::= d Z ::= d e Y ::= c Y ::= X ::= a X ::= b Y e Z ::= X Y Z Z ::= d W Y ::= c Y ::= X ::= a X ::= b Y e W ::= W ::= e

summary of RD parsing CFGs are good at specifying programming language structure parsing general CFGs is expensive so we define parsers for simpler classes of CFG –LL(k), LR(k) we can build a recursive descent parser for LL(k) grammars by: –computing nullable, first and follow sets –constructing a parse table from the sets –checking for duplicate entries, which indicates failure –creating an ML program from the parse table if parser construction fails we can –rewrite the grammar (left factoring, eliminating left recursion) and try again –try to build a parser using some other method

summary of RD parsing CFGs are good at specifying programming language structure parsing general CFGs is expensive so we define parsers for simpler classes of CFG –LL(k), LR(k) we can build a recursive descent parser for LL(k) grammars by: –computing nullable, first and follow sets –constructing a parse table from the sets –checking for duplicate entries, which indicates failure –creating an ML program from the parse table if parser construction fails we can –rewrite the grammar (left factoring, eliminating left recursion) and try again –try to build a parser using some other method...such as using a bottom- up parsing technique

Bottom-up (Shift-Reduce) Parsing

shift-reduce parsing –aka: bottom-up parsing –aka: LR(k) Left-to-right parse, Rightmost derivation, k-token lookahead more powerful than LL(k) parsers LALR variant: –the basis for parsers for most modern programming languages –implemented in tools such as ML-Yacc

shift-reduce parsing example A ::= S EOFL ::= L ; S L ::= S Grammar: S ::= ( L ) S ::= id = num Parsing Table

State of parse so far: ( id = num ; id = num ) EOF shift-reduce parsing example A ::= S EOFL ::= L ; S L ::= S Grammar: S ::= ( L ) S ::= id = num Input from lexer: yet to read Parsing Table

State of parse so far: ( id = num ; id = num ) EOF shift-reduce parsing example A ::= S EOFL ::= L ; S L ::= S Grammar: S ::= ( L ) S ::= id = num Input from lexer: ( yet to read Parsing Table SHIFT

State of parse so far: ( id = num ; id = num ) EOF shift-reduce parsing example A ::= S EOFL ::= L ; S L ::= S Grammar: S ::= ( L ) S ::= id = num Input from lexer: ( id yet to read Parsing Table SHIFT

State of parse so far: ( id = num ; id = num ) EOF shift-reduce parsing example A ::= S EOFL ::= L ; S L ::= S Grammar: S ::= ( L ) S ::= id = num Input from lexer: ( id = yet to read Parsing Table SHIFT

State of parse so far: ( id = num ; id = num ) EOF shift-reduce parsing example A ::= S EOFL ::= L ; S L ::= S Grammar: S ::= ( L ) S ::= id = num Input from lexer: ( id = num yet to read Parsing Table SHIFT

State of parse so far: ( id = num ; id = num ) EOF shift-reduce parsing example A ::= S EOFL ::= L ; S L ::= S Grammar: S ::= ( L ) S ::= id = num Input from lexer: ( S yet to read Parsing Table REDUCE S ::= id = num

State of parse so far: ( id = num ; id = num ) EOF shift-reduce parsing example A ::= S EOFL ::= L ; S L ::= S Grammar: S ::= ( L ) S ::= id = num Input from lexer: ( L yet to read Parsing Table REDUCE L ::= S

State of parse so far: ( id = num ; id = num ) EOF shift-reduce parsing example A ::= S EOFL ::= L ; S L ::= S Grammar: S ::= ( L ) S ::= id = num Input from lexer: ( L ; yet to read Parsing Table SHIFT

State of parse so far: ( id = num ; id = num ) EOF shift-reduce parsing example A ::= S EOFL ::= L ; S L ::= S Grammar: S ::= ( L ) S ::= id = num Input from lexer: ( L ; id = num yet to read Parsing Table SHIFT

State of parse so far: ( id = num ; id = num ) EOF shift-reduce parsing example A ::= S EOFL ::= L ; S L ::= S Grammar: S ::= ( L ) S ::= id = num Input from lexer: ( L ; S yet to read Parsing Table REDUCE S ::= id = num

State of parse so far: ( id = num ; id = num ) EOF shift-reduce parsing example A ::= S EOFL ::= L ; S L ::= S Grammar: S ::= ( L ) S ::= id = num Input from lexer: ( L yet to read Parsing Table REDUCE S ::= L ; S

State of parse so far: ( id = num ; id = num ) EOF shift-reduce parsing example A ::= S EOFL ::= L ; S L ::= S Grammar: S ::= ( L ) S ::= id = num Input from lexer: ( L ) yet to read Parsing Table SHIFT

State of parse so far: ( id = num ; id = num ) EOF shift-reduce parsing example A ::= S EOFL ::= L ; S L ::= S Grammar: S ::= ( L ) S ::= id = num Input from lexer: S yet to read Parsing Table REDUCE S ::= ( L )

State of parse so far: ( id = num ; id = num ) EOF shift-reduce parsing example A ::= S EOFL ::= L ; S L ::= S Grammar: S ::= ( L ) S ::= id = num Input from lexer: A Parsing Table SHIFT REDUCE A ::= S EOF ACCEPT

State of parse so far: ( id = num ; id = num ) EOF shift-reduce parsing example A ::= S EOFL ::= L ; S L ::= S Grammar: S ::= ( L ) S ::= id = num Input from lexer: A Parsing Table A successful parse! Is this grammar LL(1)?

Shift-reduce algorithm Parser keeps track of –position in current input (what input to read next) –a stack of terminal & non-terminal symbols representing the “parse so far” Based on next input symbol & stack, parser table indicates –shift: push next input on to top of stack –reduce R: top of stack should match RHS of rule replace top of stack with LHS of rule –error –accept (we shift EOF & can reduce what remains on stack to start symbol)

Shift-reduce algorithm (a detail) The parser summarizes the current “parse state” using an integer –the integer is actually a state in a finite automaton –the current parse state can be computed by running the automaton over the current parse stack Revised algorithm: Based on next input symbol & the parse state (as opposed to the entire stack), parser table indicates –shift s: push next input on to top of stack and move automaton into state s –reduce R & goto s: top of stack should match RHS of rule replace top of stack with LHS of rule move automaton into state s –error –accept

State of parse so far: ???? ???? EOF shift-reduce parsing Grammar: Input from lexer: ???? Like LL parsing, shift-reduce parsing does not always work. What sort of grammar rules make shift-reduce parsing impossible? ????

State of parse so far: ???? ???? EOF shift-reduce parsing Grammar: Input from lexer: ???? Like LL parsing, shift-reduce parsing does not always work. Shift-Reduce errors: can’t decide whether to Shift or Reduce Reduce-Reduce errors: can’t decide whether to Reduce by R1 or R2 ????

State of parse so far: shift-reduce errors A ::= S EOF Grammar: S ::= S + S S ::= S * S S ::= id Input from lexer: ???? ???? EOF ????

State of parse so far: id + id * id EOF shift-reduce errors A ::= S EOF Grammar: S ::= S + S S ::= S * S S ::= id Input from lexer: S + S reduce by rule (S ::= S + S) or shift the * ??? notice, this is an ambiguous grammar – we are always going to need some mechanism for resolving the outstanding ambiguity before parsing

State of parse so far: shift-reduce errors A ::= S id EOF Grammar: E ::= E ; E E ::= id Input from lexer: id ; id EOF E ; S ::= E ; reduce by rule (S ::= E ;) or shift the id id ; id ; id EOF input might be this, making shifting correct some unambiguous grammars can’t be parsed by LR(1) parsers either

State of parse so far: ( id ) EOF reduce-reduce errors A ::= S EOF Grammar: S ::= ( E ) S ::= E Input from lexer: ( E ) reduce by rule ( S ::= ( E ) ) or reduce by rule ( E ::= ( E ) ) E ::= ( E ) E ::= E + E E ::= id

Summary Top-down Parsing –simple to understand and implement –you can code it yourself using nullable, first, follow sets –excellent for quick & dirty parsing jobs Bottom-up Parsing –more complex: uses stack & table –more powerful –Bonus: tools do the work for you ==> ML-Yacc but you need to understand how shift-reduce & reduce- reduce errors can arise