Functional Pls Introduction to Haskell
Total Page:16
File Type:pdf, Size:1020Kb
Thoughts on Assignment 4 PL Category: Functional PLs Introduction to Haskell CS F331 Programming Languages CSCE A331 Programming Language Concepts Lecture Slides Friday, February 22, 2019 Glenn G. Chappell Department of Computer Science University of Alaska Fairbanks [email protected] © 2017–2019 Glenn G. Chappell Thoughts on Assignment 4 Introduction In Assignment 4 you will be writing a Recursive-Descent parser, in the form of Lua module parseit. It will be similar to module rdparser4 (written in class), but it will involve a different grammar & AST specification. In Assignment 6 you will write an interpreter that takes, as input, an AST of the form your parser returns. The result will be an implementation of a programming language called Jerboa. Module parseit is to parse a complete programming language, while module rdparser4 only parses expressions. Nonetheless, Jerboa has expressions, and they work in much the same way as those handled by rdparser4. I suggest using file rdparser4.lua as a starting point for Assignment 4. 22 Feb 2019 CS F331 / CSCE A331 Spring 2019 2 Thoughts on Assignment 4 The Goal Here is a sample Jerboa program, again. # Function fibo # Main Program # Given k, return F(k), where # F(n) = nth Fibonacci no. # Get number of Fibos to output def fibo() write("How many Fibos to print? ") a = 0 # Consecutive Fibos n = readnum() b = 1 write(cr) i = 0 # Loop counter while i < k # Print requested number of Fibos c = a+b # Advance j = 0 # Loop counter a = b while j < n b = c k = j i = i+1 # ++counter write("F(",j,") = ",fibo(),cr) end j = j+1 # ++counter return a # Result end end 22 Feb 2019 CS F331 / CSCE A331 Spring 2019 3 Thoughts on Assignment 4 The Grammar (1) program → stmt_list (2) stmt_list → { statement } (3) statement → ‘write’ ‘(’ write_arg { ‘,’ write_arg } ‘)’ (4) | ‘def’ ID ‘(’ ‘)’ stmt_list ‘end’ (5) | ‘if’ expr stmt_list { ‘elseif’ expr stmt_list } [ ‘else’ stmt_list ] ‘end’ (6) | ‘while’ expr stmt_list ‘end’ (7) | ‘return’ expr (8) | ID ( ‘(’ ‘)’ | [ ‘[’ expr ‘]’ ] ‘=’ expr ) (9) write_arg → ‘cr’ (10) | STRLIT (11) | expr (12) expr → comp_expr { ( ‘&&’ | ‘||’ ) comp_expr } (13) comp_expr → ‘!’ comp_expr (14) | arith_expr { ( ‘==’ | ‘!=’ | ‘<’ | ‘<=’ | ‘>’ | ‘>=’ ) arith_expr } (15) arith_expr → term { ( ‘+’ | ‘-’ ) term } (16) term → factor { ( ‘*’ | ‘/’ | ‘%’ ) factor } (17) factor → ‘(’ expr ‘)’ (18) | ( ‘+’ | ‘-’ ) factor (19) | NUMLIT (20) | ( ‘true’ | ‘false’ ) (21) | ‘readnum’ ‘(’ ‘)’ (22) | ID [ ‘(’ ‘)’ | ‘[’ expr ‘]’ ] 22 Feb 2019 CS F331 / CSCE A331 Spring 2019 4 Thoughts on Assignment 4 Common Mistakes The most common mistake I made when writing module parseit was to return only one value from a parsing function. § A parsing function will always return two values: boolean & AST. § If the boolean is true, then the AST needs to be in the proper form. § If the boolean is false, then the AST can be anything (it might as well be nil). A mistake I have seen students make on an assignment of this kind involves failing to “trust” their parsing functions in some way. § If you need something parsed, then call the appropriate parsing function, and trust that function to do it right. § Do not try to do a function’s work for it. § Do not rewrite something that has already been written. 22 Feb 2019 CS F331 / CSCE A331 Spring 2019 5 Thoughts on Assignment 4 Parsing end Three kinds of Jerboa statements are terminated with an end keyword: § Function definitions: def ID() … end § If-statements: if … elseif … else … end § While-loops: while … end Other kinds of statements have no end. This means, for example, that you generally do not want to exit the parsing function early if you think the if-statement is over. So code like the following is not good. if not matchString("else") then return true, ast end 22 Feb 2019 CS F331 / CSCE A331 Spring 2019 6 Thoughts on Assignment 4 Some Code I have posted a file containing a portion of my version of parseit.lua. See assn4_code.txt. 22 Feb 2019 CS F331 / CSCE A331 Spring 2019 7 Review Overview of Lexing & Parsing Parsing Lexer Parser Character Lexeme AST Stream Stream or Error cout << ff(12.6); cout << ff(12.6); expr id op id lit punct op op binOp: << Two phases: expr expr § Lexical analysis (lexing) id: cout funcCall § Syntax analysis (parsing) id: ff expr The output of a parser is often an abstract numLit: 12.6 syntax tree (AST). Specifications of these can vary. 22 Feb 2019 CS F331 / CSCE A331 Spring 2019 8 Review The Basics of Syntax Analysis — Categories of Parsers Parsing methods can be divided into two broad categories. Top-Down § Go through derivation from top to bottom, expanding nonterminals. § Important subcategory: LL parsers (read input Left-to-right, produce Leftmost derivation). § Often hand-coded—but not always. § Method we look at: Predictive Recursive Descent. Bottom-Up § Go through the derivation from bottom to top, reducing substrings to nonterminals. § Important subcategory: LR parsers (read input Left-to-right, produce Rightmost derivation). § Almost always automatically generated. § Method we look at: Shift-Reduce. 22 Feb 2019 CS F331 / CSCE A331 Spring 2019 9 Review Shift-Reduce Parsing [1/2] We looed at a class of table-based bottom-up parsing algorithms. Tables are produced before execution. Parser execution uses a state machine with a stack, called a Shift- Reduce parser. The name comes from two operations: § Shift. Advance to the next input symbol. § Reduce. Apply a production: replace substring with nonterminal. In the form we presented it, Shift-Reduce parsing is an LR parsing method that can handle all LR(1) grammars. 22 Feb 2019 CS F331 / CSCE A331 Spring 2019 10 Review Shift-Reduce Parsing [2/2] Generating Shift-Reduce parsing tables in a straightforward way tends to result in far too many states. Various ways of generating smaller tables are known, but these do not work for all LR(1) grammars. Probably the most common way of generating smaller parsing tables is Lookahead LR (LALR) [F. DeRemer 1969]. During parsing-table generation, a kind of hypothetical lookahead is done, with the results used to collapse multiple states into a single state. The LR(k) grammars for which this gives correct results are the LALR(k) grammars. The Yacc (“Yet Another Compiler Compiler”) parser generator and its descendants (GNU Bison, etc.), use LALR. 22 Feb 2019 CS F331 / CSCE A331 Spring 2019 11 Review Parsing Wrap-Up — Efficiency of Parsing Counting the input size as the number of lexemes/characters: Practical parsers (and lexers) run in linear time. This includes Predictive Recursive-Descent parsers and Shift- Reduce parsers. Various parsing methods are known that can handle all CFLs (Ealey, CYK, GLR); these generally run in cubic time. However, some of them achieve linear time for some grammars and can thus be considered practical. An oddity is Valiant’s Algorithm, which can parse any CFL, using matrix multiplication. Fast matrix multiplication algorithms allow it to be, roughly, Θ(n2.372864). However, this speed-up only matters for extremely large input. 22 Feb 2019 CS F331 / CSCE A331 Spring 2019 12 Review Parsing Wrap-Up — Parsing in Practice In my experience, the parser used in a production compiler is generally one of the following two kinds. § A hand-coded top-down parser using Recursive Descent—or something similar. § An automatically generated bottom-up parser that runs as a table- based Shift-Reduce parser—or something similar. Producing a parser is a very practical skill, because: Parsing is making sense of input. And that is something that computer programs need to do a lot. 22 Feb 2019 CS F331 / CSCE A331 Spring 2019 13 Review PL Feature: Type System The following type-related terms will be particularly important in the upcoming material. § Type § Static typing § Manifest typing vs. implicit typing § Type inference § Type annotation § First-class functions § Sound type system See A Primer on Type Systems for full information on this topic. 22 Feb 2019 CS F331 / CSCE A331 Spring 2019 14 PL Category: Functional PLs Background [1/4] It is likely that your programming experience has largely involved imperative programming: writing code to tell a computer what to do. Running a program is thus saying to a computer, “Follow these instructions.” This is the predominant paradigm in PLs like Java, C++, and Lua. An alternative is declarative programming: writing code to tell a computer what is true. Running a program might be thought of in terms of asking a question. The most important declarative programming style is functional programming. Later in the semester, we will look at logic programming, another declarative style. 22 Feb 2019 CS F331 / CSCE A331 Spring 2019 15 PL Category: Functional PLs Background [2/4] Functional programming (FP) is a programming style that generally has the following characteristics. § Computation is considered primarily in terms of the evaluation of functions—as opposed to execution of tasks. § Functions are a primary concern. Rather than mere repositories for code, functions are values to be constructed and manipulated. § Side effects* are avoided. A function’s only job is to return a value. *A function has a side effect when it makes a change, other than returning a value, that is visible outside the function. 22 Feb 2019 CS F331 / CSCE A331 Spring 2019 16 PL Category: Functional PLs Background [3/4] One can do functional programming, in some sense, in just about any PL. However, some PLs support it better than others. A functional programming language is a PL designed to support FP well. This is thus a somewhat vague term. § No one calls C a functional PL. § Opinions vary about JavaScript. § Everyone agrees that Haskell is a functional PL. PLs generally agreed to be functional include Haskell, the ML family (ML, OCaml, F#), R, and Miranda. In addition, the Lisp family of PLs (Common Lisp, Emacs Lisp, Scheme, Clojure, Racket, Logo, Arc) generally offers excellent support for FP, but is usually considered as a separate category.