4101 Final

Ace your homework & exams now with Quizwiz!

What are the key steps in a compiler/ interpreter?

- Compiler Process 1. Lexical Analysis 2. Syntax Analysis 3. Semantic Analysis 4. Intermediate code generation 5.. Code Optimization 6. Code Gen 7. Linking - Interpreter 1. Reading Code 2. Lexical/ Syntax Analysis 3. Execution

What are the key differences between NFA and DFA?

- NFAs can have more than one transition leaving a state on the same symbol. - DFAs allow one transition per symbol. - Transition function must be a valid function. DFA is a special case of NFA - NFAs may have transitions with empty string labels. May move to new state without consuming character - DFA transition must be labeled with symbol -DFA is a special kind of NFA

parse tree

- a data structure that shows how a statement in a language is derived from - the context-free grammar of the language; it may be annotated with additional information, e.g. for compilation purposes. - Shows how a string is produced by a grammar.

Start State

- state with incoming transition from no other state. - Can have only one start state

What are the rules of reference in Rust?

1. At any given time, you can have either but not both of - One mutable reference- Any number of immutable references 2. References must always be valid- A reference must never out live its referent

What are the differences between Parse Tree and AST?

A parse tree and AST are similar, but not the same. The former describes parsing, the latter is a result of it

Compiler

A program that translates code in a high-level language into machine language, binary code, or a intermediate language

type system

A type system is a series of rules that ascribe types to expressions.

What is the output? use std::collections::HashMap; fn main(){ let mut h = HashMap::new(); h.insert("Alice", "1"); h.insert("Bob", "2"); match h.get(&"Alice") { Some(&id) => println!("Alice:{}",id), _ => println!("Not Found"), } } A. Alice:1 B. Not Found C. Error

A. Alice:1

What is the output? #[derive(Debug)] enum Auth { Enabled(i32), Disabled(i32) } fn main() { let yes = Auth::Enabled(1); let no = Auth::Disabled(0); println!("{:?}", yes); } A. Enabled(1) B. Disabled(0) C. 1 D. 0

A. Enabled(1)

What is the output? #[derive(Debug)] struct Point { x: i32, y: i32, } impl Point { fn m(&mut self){ self.x += 1 self.y += 1 }} fn main() {let mut p = Point{ x: 0, y: 0 }; p.m(); println!("{:?}",p); } A. Point{x:1, y:1} B: Point{x:0, y:} C: Point{0,0} D. Point {1,1 }

A. Point{x:1, y:1}

When is the type of a variable determined in a statically typed language? A. When the program is compiled B. At run-time, when the variable is used C. At run-time, when that variable is first assigned to D. At run-time, when the variable is last assigned to

A. When the program is compiled

What is the output? let mut s1 = String::from("Hello"); let s2 = " World"; s1.push_str(s2);print!("{}",s2); A. World B. Hello World C. Error because s2 transferred the ownership

A. World push_str() function does not take the ownership of the parameter

What does this evaluate to? let mut x = 1; for i in 1..6 { let x = x + 1; } x A.1 B. 6 C.0 D. error

A.1

What is the output? let s = String::from("Rust is fun!"); let h = &s[0..4];println!("{}",h); A. RustB. isC. fun!D. Type Error

A.Rust

Which string is NOT accepted by this NFA? A. ab B. abaa C. abab D. abaab

B. abaa

Consider the grammar S → bS | T T → aT | U U → cU | ε • Which of the following regular expressions accepts the same language as this grammar? A. (a|b|c)* B. b*a*c* C. (b|ba|bac)* D. bac*

B. b*a*c*

What is printed? fn foo(s: &mut String) -> usize{ s.push_str("Bob"); } s.len() fn main() {let mut s1 = String::from("Alice"); println!("{}",foo(&mut s1)) } A.0 B. 8 C. Error D.5

B.8

Which string is NOT in L3? -L1 ={a,ab,c,d,ε} where Σ={a,b,c,d} -L2 ={d} -L3 =L1 ∪L2 A. a B. ad C. ε D. d

B.ad

Consider the grammar S → bS | T T → aT | U U → cU | ε Which of the following strings is generated by this grammar? A. aba B. ccc C. bab D. ca

B.ccc

Which string is not in L3 • L1 ={a,ab,c,d,ε} • L2 = {d}• L3 = L1(L2*) A. a B. abd C. adad D. abdd

C. adad

Output of following code enum Number { Zero, One, Two, } use Number::Zero; let t = Number::One; match t { Zero => println!("0"),} Number::One => println!("1"), A.0 B.1 C.Compile Error

C.Compile Error Pattern `Two` not covered

What does this evaluate to? A. "Hello!" B. "Hello! World!" C. Error D. "Hello!World!" { let mut s1 = String::from("Hello!"); {let s2 = &s1; s2.push_str("World!"); } println!("{}", s2) }

C.Error : s2 is not mut

Static vs. Dynamic Type Checking Claim 1

Claim 1: Dynamic is more convenient • Dynamic typing lets you build a heterogeneous list or return a "number or a string" without workarounds Claim 1: Static is more convenient • Can assume data has the expected type without cluttering code with dynamic checks or having errors far from the logical mistake

Static vs. Dynamic Type Checking Claim 2

Claim 2: Static catches bugs earlier • Static typing catches many simple bugs as soon as "compiled". Claim 2: Static catches only easy bugs • But static often catches only "easy" bugs, so you still have to test your functions, which should find the "easy" bugs too

Static vs. Dynamic Type Checking Claim 3

Claim 3: Static typing is faster. • Language implementation: Claim 3: Dynamic typing is not too much slower

Static vs. Dynamic Type Checking Claim 4

Claim 4: Code reuse easier with dynamic • Without a restrictive type system, more code can just be reused with data of different types Claim 4: Code reuse easier with static • The type system serves as "checked documentation," making the "contract" with others' code easier to understand and use correctly

When is the type of a variable determined in a dynamically typed language? A. When the program is compiled B. At run-time, when the variable is used C. At run-time, when that variable is first assigned to D. At run-time, when the variable is last assigned to

D. At run-time, when the variable is last assigned to

What does this evaluate to? { let x = 6; let y = "hi";if x == 5 { y} else { 5 }; 7 } A.6 B. 7 C.5 D. Error

D. Error - if and else have incompatible types

What is printed? { let mut s = &String::from("dog"); {let y = String::from("hi"); } s = &y; println!("s: {}",s); } A. dog B. hi C. Error - y is immutable D. Error - y dropped while still borrowed

D. Error - y dropped while still borrowed

What is the output? let s1 = String::from("CSC"); let s3; //deferred init{ let s2 = String::from("4101"); s3 = s1+&s2; } print!("{}",s3); print!("{}",s1); A. CSC4101 B. CSC C. CSC4101CSC D. Error

D. Error s1 lost ownership

What does this evaluate to? { let x = 6; let y = 4; y = x; x == y } A.6 B. true C. false D. error

D.error - y is immutable

What are the benefits of static type system?

Error Detection at Compile Time: Performance Optimization: Improved Code Readability and Maintainability: Better Tooling Support: Documentation and Design Clarity: . Easier Refactoring: Safer Code Evolution: Early Feedback and Rapid Development:

All NFAs are DFAs, but not all DFAs are NFAs (T/F)

False

Ocaml has dynamic type system since it does not require type annotations(T/F)

False

Select (TRUE/FALSE) if the following finite automaton will accept "accabc", where ∑ = {a,b,c}

False

You can have two mutable references to the same object in Rust(T/F)

False

LetL1 ={a,b},L2 ={1,2,3}. What is L1L2 ? WhatisL1 ∪L2 ? WhatisL1*?

L1L2: { a1, a2, a3, b1, b2, b3 } L1 ∪L2: { a, b, 1, 2, 3 } L1 *: { ε, a, b, aa, bb, ab, ba, aaa, aab, bba, bbb, aba, abb, baa, bab, ... }

Non-Deterministic Automaton

May have many sequences of steps for each string. Accepts if path ends in final state at end of string. More compact than DFA. More Expensive to test whether a string matches

Point out at least two errors? fn add_elems(arr : &[i32]) -> i32{ let sum = 0; for I in arr.iter() { sum += arr[I];} sum; }

Mutable Variable Issue: The variable sum is declared as immutable, but it is intended to be used for accumulation. In Rust, variables are immutable by default. To modify sum within the loop, it should be declared mutable using the mut keyword. Incorrect Loop Variable Usage: In the loop, for I in arr.iter(), I is an iterator over references to the elements of arr, not an index. Therefore, using arr[I] is incorrect. Instead, you should directly use I, which is a reference to each element of the array.

Final State

State with double circle. - Can have 0 or more final states - Any state including the start state can be final

What are the alternatives to OCaml records and variants in Rust?

Structs and Enums

CFGs subsume DFA, NFA, and RE(T/F)

True

Declarations are immutable by default in Rust(T/F)

True

Dynamic analysis requires execution(T/F)

True

Ocaml and Ruby use garbage collection(T/F)

True

deterministic finite automaton

a finite automaton that has at most one transition from a state for each input symbol and no empty transitions. Abbreviated DFA. Exactly one sequence of steps for each string. Easy to implement acceptance check

context-free grammar

a grammar in which the left-hand side of each production consists of a single nonterminal symbol. A way of describing set of strings (= languages) - Write L(G) the language of strings defined by grammar - Grammar is the same as regex (0|1)*

sound analysis

cannot give false negatives

Complete analysis

cannot give false-positives

Let two languages, L1 = {a,b,c}, L2 = {1,2,3}, What is L1L2 (concatenation operation)? a. {a1,a2,a3,b1,b2,b3} b. {a, b,1,2,3} c. {a,b,c,1,2,3} d. {a1,a2,a3,b1,b2,b3, c1,2,3}

d. {a1,a2,a3,b1,b2,b3, c1,2,3}

Interpreter

directly execute instructions written in a programming language or scripting language without previously converting them into object or machine code.

Abstract Syntax Tree

is a data structure that represents a parsed input - used by the compiler or interpreter

Finite Automata (FA)

is a machine for recognizing a language. Elements: State S(start, final) Alphabet, Transition Edges.

Can the following compile, if not what is the error, otherwise write the output. fn main(){ let a = 3; let b = a; let &mut c = b; *b = 5; println! (" ", a + c) ; }

let &mut c = b; is an attempt to create a mutable reference to b, but the syntax is incorrect. Moreover, b itself is not mutable. *b = 5; is trying to dereference b and assign a new value to it, which is not valid because b is not a pointer or a reference; it's an integer. The println! macro is not being used correctly. The string literal should contain placeholders for the variables' values, and there should not be a space before the macro name

What are the key advantages of dynamic type systems?

lexibility and Ease of Use: Less Boilerplate Code: Easier Code Reuse: Dynamic Behaviors: Ease of Interactive Development: Late Binding: Duck Typing: Rapid Prototyping and Iterative Development:

Rust

• Key properties: Type safety, and no data races, despite use of concurrency and manual memory management

Soundness and Completeness

• Type safety is a soundness property- • Static type systems are rarely complete - Dynamic type systems are often complete


Related study sets

Introduction to Entrepreneurship

View Set

Chapter 17 The Endocrine System Review Questions

View Set

Leadership and Management Quiz 5,6,11,15

View Set